为OmniRoute打分
给出您宝贵的评分:
手机端可长按上方图片保存到相册,或点击「下载/分享」分享到微信
使用 OmniRoute,你可以:
免费AI中转网关,单一接口对接231+大模型服务商,含50余种免费模型,适配主流编码智能体;双层压缩最高节省95%令牌,具备自动故障降级、MCP/A2A、多模态接口,支持桌面与PWA。
用户评论 (0)
2026年08月10日
2026年07月21日
2026年08月03日
2026年08月03日
2026年07月25日
2026年07月01日
2026年08月25日
2026年09月09日
2026年09月08日
2026年09月08日
2026年09月07日
2026年09月07日
2026年09月06日
v3.8.50
2026年08月27日
📊 Release by the numbers
| 👥 People who contributed | 248 |
| 📝 Commits in the cycle | 1,714 |
| 🔀 Pull requests referenced | 1,666 |
| 📋 Changelog entries | 1,182 |
| 🙌 Contributors credited in entries | 256 |
| 🤖 Automated dependency commits | 22 |
Entries by type
| Type | Count |
|---|---|
| 🐛 Fixes | 779 |
| ✨ Features | 169 |
| 📚 Docs | 29 |
| 🧹 Chore | 27 |
| 🧪 Tests | 15 |
| ♻️ Refactor | 5 |
| ⚡ Performance | 3 |
| providers | 2 |
| 🔒 Security | 2 |
| ⚙️ CI | 2 |
| deps | 2 |
| maint | 2 |
🏆 Top 25 contributors this cycle
By commits in ed2db6cb19..v3.8.50, author identities consolidated via .mailmap. Bots excluded.
| # | Contributor | Commits |
|---|---|---|
| 🥇 | diegosouzapw | 738 |
| 🥈 | backryun | 88 |
| 🥉 | Dizzle | 66 |
| 4 | Ravi Tharuma | 52 |
| 5 | Markus Hartung | 48 |
| 6 | Bob.Hou | 42 |
| 7 | Rouzbeh† | 38 |
| 8 | Xiangzhe | 31 |
| 9 | Paco Cartones | 28 |
| 10 | Nguyen Thanh Dat | 23 |
| 11 | Aman | 22 |
| 12 | Will Gordon | 19 |
| 13 | 小妍儿 ✨ | 17 |
| 14 | adevwithpurpose | 16 |
| 15 | Andrew B. | 10 |
| 16 | NOXX - Commiter | 10 |
| 17 | ignamiranda | 10 |
| 18 | Jonathan Bailey | 9 |
| 19 | Ke Jin | 9 |
| 20 | Austin Liu | 8 |
| 21 | Chewji | 8 |
| 22 | Prudhvi Vuda | 7 |
| 23 | benzntech | 7 |
| 24 | rinseaid | 7 |
| 25 | stanley | 7 |
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.
✨ New Features
-
feat(search): first-class X Search provider (
x-search) on… #10985 -
feat(core): add Layer A capability filter at router #5696
-
feat(providers): add DeepAI as paid API-key image provider #6671
-
feat(providers): add Naga.ac and… #6674 — thanks @chirag127
-
feat(api): add response content encoding verification —… #6736
-
feat(api): add plugins marketplace install endpoint with… #6752
-
feat(chatgpt-web): harden prompt-emulated… #7679 — thanks @horacecar
-
docs: add management authentication terminology guide #7786
-
feat(a2a): Conductor bridge — long-lived SSE consumer that… #8080
-
feat(a2a): the Agent Card (
/.well-known/agent.json) now… #8119 -
feat(dashboard): "Conductor" panel — OmniConductor fleet… #8221
-
feat(dashboard): Faro chat with voice on the Conductor panel —… #8222
-
feat(a2a): inbound delegation to the OmniConductor fleet — `POST… #8223
-
docs: add low-memory/small VPS optimization guide #8237
-
feat(providers): add connection-level… #8369 — thanks @Benson-mk
-
feat(copilot): add approval gate for runOmniRouteCli commands #8461
-
feat(ci): add windows-latest leg to test-bun-sqlite job #8468
-
feat(electron): Desktop app can now attach… #8799 — thanks @soulhakr
-
Database The
node:sqlitefallback now… #8870 — thanks @artickc -
Providers expands the Novita AI catalog… #8913 — thanks @jax-novita
-
feat(providers): native xAI Agent Tools passthrough on… #8964
-
feat(providers): add UnoRouter provider UnoRouter is an… #8978
-
feat(sse): deprecated the legacy
gemini-cli**upstream… #7034 #8980 -
Add a default-off connection setting for Codex, OpenAI, and…
-
Omit opaque encrypted reasoning values from persisted call logs… #9000
-
feat(providers): add Regolo AI OpenAI-compatible provider #9031
-
feat(db): add provider-scoped model aliases that survive… #9068
-
feat(cursor): surface a dismissible dashboard banner suggesting… #9173
-
feat(cursor): proactively renew Cursor sessions before their ~24h… #9173
-
feat(codex): accept parenthesized… #9208 — thanks @seakleangnhak
-
feat(usage): surface Claude thinking token… #9214 — thanks @luoyide
-
feat(ollama): add Ollama Local embedding… #9225 — thanks @HaoNgo232
-
feat(images): execute full combo strategy + fallback in… #9239
Adds open-sse/services/imageCombo.ts that expands combo targets, filters to images-capable, executes the priority strategy with handleImageGeneration per target, and returns the first success or last failure. Route patches detect combo names before model resolution and divert to the new execution path.
-
feat: make forwarded upstream response-header budget configurable… #9243
-
feat(providers): filter provider detail… #9247 — thanks @RobertsXML
-
feat(providers): make video_url… #9248 — thanks @HellFiveOsborn
-
feat(gemini): recursive type:object injection in schema… #9268
-
feat(dashboard): render a conditional "Get API key" link on the… #9270
-
feat(providers): accept JSON cookie… #9284 — thanks @AIB1TAL0S
-
feat(providers): support max reasoning effort for opencode-zen… #9318
-
feat(providers): expanded the NanoGPT (
nano-gpt.com) upstream… #9322 -
feat(sse): combo
system_messagesupports server-side… #5501 -
feat(sse): template expansion… #5501 #9414 — thanks @maxmad64bis
-
feat(sse): New-API/One-API/Sub2API aggregator balance detection… #9415
-
feat(catalog): added opt-in settings
hideAutoCombosand… #9418 -
feat(opencode-plugin): added… #9473 — thanks @omniroute
-
feat(providers): add native DeepSeek V4 Flash and Pro… #9485
-
feat(opencode-plugin): warm catalog startup from disk snapshot +… #9490
The config-shim hook now reads the last disk snapshot before fetching, so the provider registers immediately with the last-known-good catalog (~1-2s vs ~30s on a warm gateway). All six fetchers run concurrently via Promise.allSettled instead of sequentially. A failed refresh keeps the snapshot (no overwrite). An in-flight guard prevents concurrent refreshes for the same cache key. The features.diskCache: false opt-out disables the warm read entirely.
-
feat(models): Test All's "Auto-hide failed models" no longer… #9511
-
Add an advisory forgotten-sibling-tests report to pull-request… #9530
-
feat(providers): add Muse Code CLI provider preset #9544
-
feat(plugins): expose client request headers in plugin… #9570
-
feat(plugins): add onStreamComplete built-in event exposing… #9571
Adds a new
onStreamCompleteplugin event that fires after an SSE stream is fully
consumed, carrying usage token counts and timing metrics (latency, TTFT). Built-in
events now includeonStreamCompleteas a fire-and-forget lifecycle hook.Payload:
status,usage(prompt_tokens, completion_tokens, reasoning_tokens,
cache_read_input_tokens, cache_creation_input_tokens),timing(latencyMs, ttft),
model,provider,errorCode.Non-breaking — existing
onResponsehooks with{ streamed: true }remain unchanged. -
feat(audio): Soniox STT + TTS provider (
sx) — async… #9579 -
Show cache-read and cache-write token counts in request log rows and…
report them. (#9620) -
feat(memory): support custom OpenAI-compatible endpoints for… #9622
-
feat(resilience): add an opt-in watchdog for persistently slow… #9709
-
Onboarding: add an explicit, reviewable one-click setup for eligible…
with per-provider caution links, selectable confirmation, idempotent creation, and safe partial
retries. Existing provider connections are never changed and setup completion never enables
providers silently. (#9752) -
feat(settings): add a dedicated Modality Bridge settings page… #9782
-
feat(modality bridge): Transcribe chat audio for text-only… #9807
-
feat(memory):
PROVIDERS_SYSTEM_MUST_BE_FIRST(the… #6135 #7293 #9924 -
feat(api): API keys can disable prompt… #10001 — thanks @shixi-li
-
Add cliproxy provider exposure controls and manifest injection #7329 — thanks @KooshaPari
-
feat(infra): add a systemd autostart unit for Linux #8635
-
feat(db): add node sqlite adapter parity #8871 — thanks @epsilonode
-
feat(alibaba): free-tier routing with live quota sync #8893 — thanks @AndrianBalanescu
-
feat(oauth): add Raycast Pro provider with local auto-import #8895 — thanks @AndrianBalanescu
-
feat(executors): add isolated Claude Code bridge over Devin ACP #8914 — thanks @McLuck
-
feat: improve provider quota layouts #8916 — thanks @apoapostolov
-
feat(mcp): add omniroute_create_combo tool #8925 — thanks @lucasmellos
-
feat(ci): gate the publish on clean-install AND upgrade-over-previous #8953
-
feat(providers): add Conol (conol.ai) web session provider #8974 — thanks @artickc
-
Feat/combo provider wise model test #9011 — thanks @JoshimOfficial
-
feat(model-alias): add runtime Model Alias Resolver middleware #9020 — thanks @Egorich-print
-
feat(i18n): complete zh-CN localization for compression engines and dashboard UI #9038 — thanks @qianze0628
-
feat: Cheaper Inference provider (chat + native Responses + images, sponsor rail 2nd) #9043
-
feat(providers): add comprehensive support for self-hosted Firecrawl via FIRECRAWL_BASE_URL and custom base URLs #9052 — thanks @mad-gooze
-
feat(dahl): add manual API key option alongside auto-generated token #9077 — thanks @pizzav-xyz
-
feat(ci): G0 — reforça o trilho PR→release/ ** #9108
-
feat(providers): native xAI Agent Tools passthrough for /v1/responses #9111 — thanks @VXNCXNX
-
feat(.50): completa itens restantes — G13, G14, gap34, docs, R0.2 #9126
-
feat(g1): rewrite combo-strategy check to runtime-import approach #9131
-
feat(test:scoped): TIA-based local test runner (#8084 D1) #9143
-
feat(docker): publish next from active release branches #9181 — thanks @Zartharas
-
feat(usage): show Grok Build billing limits #9205 — thanks @xz-dev
-
feat(models): functional gateway mirrors + fix synced-substitution #9217
-
feat(admission): add adaptive overload protection for LLM routes #9262 — thanks @xz-dev
-
feat(i18n): update italian translations #9280 — thanks @Gecky2102
-
feat(dashboard): persist provider screen filters to URL for bookmarking #9307 — thanks @swingtempo
-
feat(api-manager): add provider-level model permissions #9313 — thanks @xz-dev
-
feat(warmup): proactive Claude warmup scheduler (#8848) #9449 — thanks @HouMinXi
-
feat(infra): add systemd autostart unit for Linux (#8635) #9466
-
feat(lib): make MODELS_DEV_SYNC_ENABLED actually control the sync #9483 — thanks @HouMinXi
-
feat(radar): flag-gated signed free-model catalog overlay #9515
-
feat(compression): add Russian language pack #9581 — thanks @vinogradovnet
-
feat(providers): integrate wave4 free-tier gateways #9584
-
feat(providers): add Zylo UnoRouter and Poolside registries #9585
-
feat(providers): add FastRouter AnyAPI and ElectronHub registries #9586
-
feat(providers): add LLMGateway and LLM Kiwi registries #9587
-
feat(providers): add FreeInference registry #9594
-
feat(radar): contributor + supporter claim buttons on the activation screen (F4/T7) #9710
-
feat(radar): paste-key input on the activation screen (F4) #9758
-
feat(guardrails): modality bridge core — vision mode/task-aware/cache/input_image + modalityBridge settings #9759
-
feat(radar): referrals from standalone /v1/referrals feed (no 30-day delay) #9762
-
feat: generic OpenAI-compatible video custom provider #9844 #9818 — thanks @oyi77
-
feat(logging): make the chat-log truncation limit configurable, bumped default 128x #9863 #9738 — thanks @hartmark
-
feat(logging): bump CHAT_LOG_ARRAY_TAIL_ITEMS default 24 -> 128 #9864 #9735 — thanks @hartmark
-
feat(oauth): add Openference OAuth and API key provider integration #9869 #9722 — thanks @AnhLead
-
feat(src): proxy-pool-toolbar-minor-improvements #9870 #9718 — thanks @AgnesRiber
-
feat(resilience): expose providerQuotaOverrides via /api/resilience #9871 #9714 — thanks @herjarsa
-
feat(responses): add encrypted reasoning replay opt-in #9876 #9601 — thanks @jackjinke
-
feat(resilience): add per-account resilience connections view (API + dashboard) #9880 #9510 — thanks @HouMinXi
-
feat(db): add a job registry for scheduled background work #9886 #9631 — thanks @HouMinXi
-
feat(telegram): Mini App chat bridge — initData auth, update webhook, chat proxy #9907 #9812 — thanks @benzntech
-
feat(cursor): exclusive live listing + verbatim AgentRun model ids #9911 — thanks @yansigit
-
feat(usage): add Command Code quota tracking #9921 — thanks @yansigit
-
feat(combo): add quota-only priority fallback #9983 — thanks @xz-dev
-
feat(onboarding): add one-click free provider setup #10014
-
feat(admission) — direct pushes: adaptive overload/pressure…
-
feat(agentrouter) — direct pushes: support Claude and Codex…
-
feat(providers) — direct pushes: ChatGPT Web session credential…
-
feat(sse): honor provider-rule lock scope for agentrouter… #10419
-
feat(ocr): Vertex AI DeepSeek-OCR provider #10398
-
feat(providers): derive imageToText from the OCR registry +… #10400
-
feat(ocr): multi-provider /v1/ocr with transformation layer… #10283
-
feat(providers): declare imageToText serviceKind on major… #10275
-
feat(bridge): native-vision skip guard + configurable describe… #10289
-
feat(bridge): normalize images to 2048px long edge before… #10287
-
feat(sse): restate agentrouter quota 403/400 as retryable 429… #10335
-
feat(sse): add i-have-adhd output style to compression catalog #10271
-
feat(i18n): complete Portuguese… #10250 — thanks @DarkEsteves
-
feat(providers): publish Poolside's… #10216 — thanks @pacocartones
-
feat(crof): advertise reasoning… #10062 — thanks @excessivechaos
-
feat(open-sse): expose… #10040 #10046 — thanks @tiangao88
-
feat(dashboard): Kimi 15% first-top-up campaign — dedicated… #10240
-
feat(providers): integrate audited free-tier gateways #9210
- feat(radar): OmniRoute Radar — the signed free-model… #10826
- feat(video): Video Bridge gained a full… #10483 — thanks @backryun
- feat(volcengine): Volcengine **Ark plan… #11333 — thanks @rengaryang
- feat(providers): #10722 #10184 #10174 — thanks @hgaib @megamen32 @MeRezaRezaei
- feat(providers): #9909 #10804 — thanks @yansigit @tuandinh0801
- feat(providers): tool calling for… #10948 — thanks @acc0mplish
- feat(providers): per-connection… #10885 — thanks @maxmad64bis
- feat(providers): #10226 #10195 #10942 — thanks @backryun @oyi77
- feat(providers): the web-session credential… — thanks @benzntech
- feat(sse): explicit
glm-5.3-max… #11415 — thanks @phuongddx - feat(sse): discover Anthropic partner models… #11279 — thanks @maci0
- feat(sse): opt-in… #10965 — thanks @mymusicmyspace
- feat(sse): Kimi Web token lifecycle… #10944 — thanks @MeRezaRezaei
- feat(sse): Cursor plan images through the Agent CLI… #10842 #10425
- feat(api): #10819 #10977 #10771 — thanks @RaviTharuma @maxmad64bis
- feat(api): #10568 #11076 — thanks @RaviTharuma @ntdatt812
- feat(audio): native… #11312 #11315 #10822 — thanks @RaviTharuma
- feat(gemini-web): image generation… #10494 — thanks @Abhishek4512009
- feat(cli): native Bun backend… #11039 #11168 — thanks @rqzbeh
- feat(cli): tray… #11230 #10830 #11372 — thanks @tuandinh0801 @ziuus
- feat(cli): relay-like CLI closure — a target… — thanks @backryun
- feat(dashboard): #11228 #11227 #11224 #11215 #11195 #11206 — thanks @ignamiranda
- feat(dashboard): #10263 #10900 #10520 #11196 #11329 #10354 — thanks @hartmark @swingtempo
- feat(routing): #10126 #10362 #10881 #10927 — thanks @benzntech @KaspaPulse @Egorich-print
- feat(combo): an opaque… #10730 — thanks @stanleytejakusuma
- feat(admission): #11268 #10814 #10437 — thanks @RaviTharuma @xz-dev
- feat(compression): adopts… #10647 #11318 — thanks @RaviTharuma
- feat(models): #11252 #10883 #10884 — thanks @maxmad64bis @excessivechaos
- feat(search):
context7added as a… #11140 — thanks @HouMinXi - feat(mcp): dynamic runtime tool-schema… #11155 — thanks @rqzbeh
- feat(a2a): A2A v1.0 client compatibility —… #10839 — thanks @wpec
- feat(server): native systemd… #10662 — thanks @maxmad64bis
- feat(redis): configurable key… #11042 — thanks @MeRezaRezaei
- feat(docker): hardened Linux VPS… #10623 — thanks @freudantunes
- feat(proxy): #10876 #10342 — thanks @maxmad64bis @Gi99lin
- feat(services): sanitized CLIProxyAPI… #11314 — thanks @RaviTharuma
- feat(db): the DB health check now… #10652 — thanks @maxmad64bis
- feat(codex): sync with Codex v178 identity… #10716 — thanks @xz-dev
- feat(usage): Kimi Coding "Extra Usage" is… #10712 — thanks @xz-dev
- feat(oauth):
gemini-3.7-flashmodels… #10305 — thanks @Chewji9875 - feat(cli-tools): Prime Agent added to… #11166 — thanks @arminanton
- feat(guardrails): a focused video-analysis mode for the vision…
- feat(ops): canary deploy path with a provenance gate, a… #10446 #10444
- feat(api): list embeddings… #11249 #11213 — thanks @rafacpti23
- feat(responses):
previous_response_id… #10262 — thanks @hartmark
🐛 Bug Fixes
- fix(build): every route no longer answers HTTP… #11343 #6344 #10060
- security(search) block SSRF via
/v1/search… — thanks @zmf963 - providers honor
PATCH /api/providers/[id]so `omniroute… #10366 - cli route provider test commands through configured connection… #10570
- executors fix internal timeout misclassified as client… #8197
- test(combo): guard auto/best-free never leaks the combo name as a… #7754
- fix(vision-bridge): describe-model no longer returns unreachable… #8430
- fix(vision-bridge): validate fixedModel against usable credentials… #8430
- fix(vision-bridge): in the combo describe path, replace raw images… #8430
- fix(quality): add base-relative file-size check so inherited drift… #8522
- fix(ci): aggregate all fast-gates into non-fail-fast loop so one… #8542
- fix(tests): make machineId tests macOS-compatible by stubbing… #8577
- fix(scripts): replace bash 4+ readarray with compatible while-read… #8577
- fix(cli): enable systray2 on Windows for Norton-friendly tray #8609
- fix(executor): guard claude/anthropic buildHeaders against empty… #8653
- fix(providers): gate premium opencode-zen/opencode-go models… #8681
- fix(api): make
/v1/modelsstale refresh response-safe… #8728 #8697 - fix(yuanbao-web): accept
contentfield in SSE text events… #8739 - fix(build): remove misleading open-sse/package.json facade and add… #8781
- fix(errorClassifier): classify ChatGPT Web SENTINEL_BLOCKED 403 as… #8813
- fix(cli): fall back to node:sqlite when better-sqlite3 constructor… #8826
- fix(opencode): prefix provider id with "opencode-" for auth login… #8830
- fix(opencode-zen): add current free-tier models to registry to… #8841
- fix(api): Let image and video providers… #8843 — thanks @artickc
- fix(build): include better-sqlite3 prebuilds in standalone bun… #8847
- fix(proxy-health): include credentials in proxy health check URLs #8853
- fix(build):
prepublishno… #8858 — thanks @omniroute @maisdesign - fix(opencode): generate… #8869 — thanks @xiaoyaner0201
- fix(cli): default omitted Codex CLI… #8876 — thanks @xiaoyaner0201
- fix(proxy): isolate new proxy… #8883 — thanks @xiaoyaner0201
- fix(db): invalidate stale LKGP pins when provider connections are… #8887
- fix(tests): update stale nightly compat fixtures and goldens to… #8901
- fix(quota): Deleting a quota pool now… #8906 — thanks @xiaoyaner0201
- fix(executors): Vertex AI now routes… #8909 — thanks @wgordon17
- fix(providers): expose both OAuth… #8921 — thanks @Llliao1113
- fix(compression): drop orphan… #8946
- fix(auth): setting first dashboard login password no longer fails… #8950
- fix(github): add targetFormat to GPT-5.6 Sol/Terra/Luna models #8951
- fix(auto-update): skip synthetic Next.js standalone package.json… #8956
- fix(opencode): propagate vision capability from live catalog into… #8960
- fix(providers): switch Antigravity quota RPCs to iterate… #8965
- fix(oauth): GHE Copilot OAuth lifecycle — connecting an account…
- fix(health-check): the access-token-only… #8970 — thanks @hppsc1215
- fix(providers): copilot-m365-web enterprise turns send… #8971
- fix(ci): the reconciliation helper no longer bounds its scan… #8985
- fix(ci): fixed a live auto-update defect where **Intel Macs… #8988
- fix(sse):
stripResponsesLifecycleEchono longer strips… #8990 - fix(vertex): route Claude models to native rawPredict endpoint and… #8994
- fix(proxies): resolveProxyForConnection now returns the proxy… #8995
- fix(translator): the Responses-to-Chat promotion path called… #8997
- fix(sse): Claude reasoning-effort suffix ids…
correctly on any provider serving a real Claude model, not just the direct Anthropic provider
(#9006) - fix(sse): the no-thinking (
no-think/) catalog variant's…
made it unusable outside the direct provider, both in the discovery catalog and the dashboard
playground — is fixed (#9006) - fix(sse): a single unrecognized model id on a Vertex connection no…
other model on that connection for 2 minutes — Vertex 404s are now scoped to a per-model
lockout viapassthroughModelsinstead of a connection-wide cooldown
(#9006) - fix(sse): Vertex
PERMISSION_DENIED403s are now disambiguated…
documented error format — a genuinely connection-wide cause (API disabled, project-level IAM
denial) still cools the whole connection, while a model-specific denial locks out only that
model (#9006) - fix(sse): error-only streams now preserve… #9022 — thanks @shixi-li
- fix(cursor): preserve tool context across multi-turn conversations… #9029
- fix(sse): move Antigravity client system content to first user… #9030
- fix(auth): IP blacklist now blocks on direct connections via… #9033
- fix(api): use configured prefix instead of raw node UUID for… #9034
- fix(resilience): Detect and reset idle-capacity rate-limit… #9041
- fix(db): stream DB backup export instead of buffering entire file… #9045
- fix(ui): normalize Free Pool API response payload to read from… #9046
- fix(translator): pass… #9053 — thanks @ikelvingo
- fix(api/analytics): stop charging :free models at arbitrary… #9054
- fix(api): auto/* routing aliases bypass API-key… #9057
- fix(providers): anthropic strips code-execution/skills beta flag,… #9064
- fix(batches):
GET /v1/batchesnow validates thelimitquery… #9073 - fix(a2a): the A2A JSON-RPC router now compares the bearer token… #9083
- fix(api/skills): the
/api/skills/**routes now run caught… #9088 - fix(providers): admit audio-speech/audio-transcriptions apiType in… #9096
- fix(providers): modal.com validation returns clear error when Base… #9102
- fix(providers): GitHub Copilot no longer re-imports or routes… #9103
- fix(providers): resolve combo names in audio transcriptions route… #9134
- fix(vscode): allow built-in auto-routing models in VS Code model… #9140
- fix(background): detect Anthropic top-level system prompts for… #9142
- fix(dashboard): the provider "Auto Sync" toggle now applies to… #9149
- fix(cli): use process.execPath for macOS launchd autostart #9156
- fix(management): authorize mcp:connect-only keys on loopback/LAN… #9159
- fix(model-discovery): ingest capabilities.effort_tiers for synced… #9160
- fix(translator): Honor configured Chat… #9161 — thanks @Zartharas
- fix(translator): buffer and normalize upstream tool-call argument… #9168
- fix(cursor): the manual "Refresh" button on Cursor connections now… #9173
- fix(translator): avoid double-normalizing tool names in… #9177
- fix(dashboard): the "Default Model" of an OpenAI-compatible… #9179
- fix(db): honor the
ENABLE_REQUEST_LOGS… #9187 — thanks @RobertsXML - fix(model): normalize client… #9193 — thanks @b1nhm1nh
- fix(catalog): repair dead guard and synced-first ordering for… #9195
- fix(routing): consult customModels supportsVision flag in Combo… #9195
- fix(models): Preserve published model… #9199 — thanks @xz-dev
- fix(models): Reuse one build-local… #9199 — thanks @xz-dev
- fix(models): Resolve token limits and model… #9199 — thanks @xz-dev
- fix(models): Read and parse models.dev… #9199 — thanks @xz-dev
- fix(compression): honor the global… #9200 — thanks @joachimBrindeau
- fix(web-search): bind each search provider attempt to its… #9201
- fix(auth): make antigravity and agy equivalent in credential… #9204
- fix(cli): prefer IPv4 DNS for spawned… #9209 — thanks @dsitmilis
- fix(pricing): stop billing reasoning… #9212 — thanks @yidecode
- fix(models):
/v1/modelsnow… #4164 #9215 — thanks @nguyenha935 - fix(combo): the Combo "Add model"… #9218 — thanks @szzhoujiarui
- fix(codex): normalize additional_tools… #9219 — thanks @SalyyS1
- fix(codex): preserve quota window… #9222 — thanks @HectorBernstorff
- fix(azure): normalize GPT-5 chat… #9223 — thanks @royanrosyad85
- fix(codex): strip orphaned tool outputs… #9228 — thanks @raflyazf
- fix(i18n): completed the French UI… #9235 — thanks @alex-jordan547
- fix(nvidia): normalize tool names and… #9236 — thanks @minhnhat166
- fix(lmarena): emit Uint8Array SSE chunks instead of strings to… #9237
- fix(db):
validateRegisteredKeyno longer rejects the first… #9241 - fix(i18n): localized hardcoded web… #9245 — thanks @alex-jordan547
- fix(db): deleting a provider connection… #9246 — thanks @maxmad64bis
- fix(cli-tools): keep Apply enabled… #9250 — thanks @lazysaltyfish
- fix(translator): harden Claude format… #9253 — thanks @ervareza
- fix(minimax): add the required empty… #9256 — thanks @rixzkiye
- fix(resilience): Enforce RPM limits with rolling leases and… #9259
- fix(classify429): add missing
have exhausted their quota… #9269 - fix(claude): remove unconditional "always" return in… #9276
- fix(qoder): include actionable CLI_QODER_BIN hint in connection… #9277
- fix(providers): the web search fallback detector in… #9279
- fix(docker): the bundled Redis sidecar no longer publishes on… #9286
- fix(credential-health): scheduler never retries failed connections… #9289
- fix(db): persist the account egress IP… #9291 — thanks @maxmad64bis
- fix(api): specialty model catalog ignores hidden OpenRouter model… #9293
- fix(mcp): break circular import between googApiKeyAuth.ts and… #9297
- fix(catalog): cache getModelsDevPricing to prevent OOM at startup #9300
- fix(providers): bump qwen-web SPA version header from 0.2.66 to… #9304
- fix(sse): broaden OMNIROUTE_SSE_COMMENTS to accept… #9305
- fix(lmarena): encode SSE stream chunks as Uint8Array to prevent… #9306
- fix(claude): reconcile compacted tool… #9308 — thanks @ryanngit
- fix(kiro): validate completed nested… #9314 — thanks @SemonCat
- fix(backend): use accumulated responseBody for provider payload in… #9315
- fix(qoder): surface qodercli stderr in error message instead of… #9319
- fix(security): require auth for /v1/models when management auth is… #9320
- fix(claude): normalize nested Claude… #9332 — thanks @AlanSyue
- fix(providers): map kimi-web/K3 to K2D5 scenario instead of OK… #9338
- fix(security): require explicit tool envelope to prevent bare JSON… #9343
- fix(api): consult LiteLLM pricing_synced layer in… #9364
- fix(providers): treat claude-web 429 as unhealthy and forward… #9406
- fix(providers): treat muse-spark-web 429 as unhealthy #9406
- fix(providers): detect expired gemini-web sessions via… #9407
- fix(providers): add tool_use block handling to claude-web stream… #9408
- fix(api): fall back to slugified provider name when prefix is… #9416
- fix(providers): Codex GPT-5.6 model metadata reports the 1M… #9431
- fix(oauth): Kiro import token endpoint no longer overwrites… #9435
- fix(sse): hoisting a mid-conversation
system/developer… #9436 - fix(backend): force system MITM CA cert to 0644 on Linux… #9442
- fix(routing): a Codex-native bare model id (
gpt-5.5, the… #9447 - fix(docker): ship MITM
_internal/shims andselfsignedpackage… #9451 - fix(cli): probe PATH for claude.exe/codex.exe on Windows before… #9454
- fix(cli): stop the supervisor before the child so omniroute stop… #9455
- fix(cli): route claude-code OAuth to the Anthropic
claude… #9474 - fix(cli): re-verify running binary version after `omniroute… #9475
- fix(providers): classify 400 out of extra usage as quota_exhausted… #9486
- fix(auth): redirect active sessions from… #9491 — thanks @DaDecky
- fix(api): make the 800-message chat history cap opt-in so long… #9494
- fix(translator): preserve authentic K3 Responses reasoning by… #9496
- fix(translator): join reasoning summary segments with newline… #9500
- fix(muse-spark-web): document the ecto1: WS auth token requirement… #9502
- fix(sse): stop force-injecting advanced-tool-use beta via the… #9505
- fix(sse): stop the reasoning-token buffer from enlarging a… #9507
- fix(ci): include combo-matrix tests in test-integration job #9531
- fix(ci): tighten unit suite ceiling from 100min to 80min as a… #9532
- fix(ratelimit): added queue-wait timeout tests and… #9533
- fix(test): prevent flaky modelsDevSync timer assertions by… #9534
- fix(backend): map cache tokens in OpenAI-to-Claude non-streaming… #9536
- fix(db): add transient-error retry to corruption probe to prevent… #9541
- fix(search): mark searxng-search as fallbackOnly to prevent… #9543
- fix(providers): strip provider prefix in getModelTargetFormat to… #9545
- fix(model): add "aq" alias for amazon-q provider so parseModel… #9550
- fix(proxy): NO_PROXY now bypasses context-level proxy in… #9551
- fix(build):
npm run build:cli… #8858 #9553 — thanks @omniroute - fix(quality): #8522 #9024 #9324 #9329 #9193 #9332 #9228 #9260 #8934 #9196 #9163 #9554
- fix(build): exec native esbuild binary directly in prepublish —… #9558
- fix(mcp): the 3
audit.test.tsshutdown/fallback tests… #8959 #9559 - fix(build): lazy-resolve module-level fs paths to avoid Turbopack… #9560
- fix(sse): replace timer-based waits with polling to fix flaky… #9567
- fix(translator): restore original tool name casing in… #9568
- fix(translator): add case-insensitive fallback for upstream tool… #9575
- fix(standalone): multipart uploads (`POST… #9580
- fix(docker): standalone co-location now… #9615 — thanks @atjsh
- fix(resilience): failed connection test now sets a short cooldown… #9623
- fix(db): wire telemetry cleanup scheduler in Next.js startup path #9624
- fix(db): align domain_cost_history cleanup cutoff with millisecond… #9625
- fix(playground): surface provider model loading errors and offer… #9626
- fix(combo): distinguish pre-dispatch skips from genuine failures… #9630
- fix(build): add build-next-isolated.mjs sibling imports to… #9633
- fix(providers): new per-provider… #9675
- fix(docker):
--build-arg OMNIROUTE_USE_TURBOPACK=0now… #9695 - fix(translator): restore TitleCase tool names on the Claude →… #9713
- fix(db): clear stale combo connection pins when provider… #9719
- fix(compression): persist
enableRenderersthrough… #9730 - Fixed
GET/PUT/DELETE /api/memory/[id]always failing with a… #9737 - Replaced hand-rolled body type checks with Zod validation in… #7 #9737
- Restore Vietnamese locale parity after the entity-normalization… #9737
- fix(radar): refresh signed catalog/referral caches when… #9776
- Translator keep the Responses namespace… #9783 — thanks @VXNCXNX
- fix(api): Model catalogs no longer expose… #9788 — thanks @xz-dev
- fix(executors): preserve Command Code… #9826 — thanks @MrShitFox
- fix(executors): prevent intermittent Codex… #9828
- fix(cursor): SelectedImage uses… #9834 — thanks @yansigit
- fix(test): reconcile test expectations that drifted from the… #9874
- fix(search): nest Exa contents options (text/highlights) for… #9914
- fix(i18n): re-escape CC discovery-alias… #8747 #9917 — thanks @yansigit
- fix(encryption): name failing credential + recovery path in… #9927
- fix(executors): preserve non-strict function-tool semantics… #9931
- fix(migrations): don't abort on fresh install with only the 001… #9934
- fix(admission): per-connection… #9654 #9940 — thanks @branben
- Repair release-sweep regressions in locale and environment… #9945
- Let the release-green validator finish the test-masking gate on… #9964
- fix(chat): don't misclassify content-less thinking/redacted Claude… #9971
- fix(images): normalize terminal upstream errors via… #9981
- fix(quality): green release/v3.8.50 base-reds — sync 4 env vars… #9985
- fix(sse): replay Gemini
thought_signatureon the direct Claude→Gemini path #3440 #2504 — thanks @csoftware-arigpt - fix(security): bump adm-zip >=0.6.0 + exact host matching in the mitm DNS test #7733
- fix(i18n): polish zh-CN/zh-TW translations and fix over-translation of proper nouns #8872 — thanks @ikelvingo
- fix(build): support npm v11 allowScripts for optional native deps #8877 — thanks @configurowebmax
- fix(combo): exclude hidden leaves from catalog and dispatch #8878 — thanks @ahmet-cetinkaya
- fix(antigravity): add onboardUser fallback for accounts missing Cloud Code project #8886 — thanks @HouMinXi
- fix(sse): brand-neutral keepalive frames #8888 — thanks @AndrianBalanescu
- fix(deepseek-web): enable toolCalling on all models #8889 — thanks @AndrianBalanescu
- fix(combo): fail-fast concurrency gate and execute-mode overflow #8890 — thanks @AndrianBalanescu
- fix(antigravity): quota-aware account selection and projectId persistence #8891 — thanks @AndrianBalanescu
- fix(usage): aggregate provider window costs in SQL #8892 — thanks @AndrianBalanescu
- fix(combo): least-used quota strategy and wildcard UI preservation #8894 — thanks @AndrianBalanescu
- fix(test): stop autostart tests from disabling the developer's real systemd service #8900 — thanks @nosolosoft
- fix(combos): include id column in getCombos query #8905 — thanks @HouMinXi
- fix: prevent false 'Failed to save connection' error when adding providers #8912 — thanks @ziuus
- fix: add Termux/Android support for playwright-core and better-sqlite3 #8922 — thanks @Kaedo17
- fix(i18n): localize SubscriptionTab UI strings instead of hardcoded Chinese #8930 — thanks @Hdiaktoros
- fix(kiro): keep interleaved tool results grouped without dropping assistant text (#8903) #8931 — thanks @xiaoyaner0201
- Fix custom tool output pairing during context compression #8933 — thanks @JxnLexn
- fix(routing): account for active OAuth sessions #8940 — thanks @JxnLexn
- fix(vision): prevent bridge streaming and normalize OMP effort #8945 — thanks @rinseaid
- fix(ci): stop one failing platform from taking the whole desktop channel down #8957
- fix(api): alias-backed models leak raw node UUID prefix in /v1/models (#8958) #8961 — thanks @Rahulsharma0810
- fix(sse): default OpenAI Chat Completions to non-stream when stream omitted #8976 — thanks @HouMinXi
- fix(cache): add latency marker + per-key bypass for semantic cache #8984 — thanks @HouMinXi
- fix(perplexity-web): update catalog to use 'copilot' mode and fix model IDs #8989
- fix(docker): move entrypoint script to /app to avoid tmpfs masking #8999 — thanks @yutuknown
- fix(executors): backfill missing tool message names for Kimi K3 and strict BYOK providers #9005 — thanks @Zenlyte
- fix(command-code): enable vision flags for CC models and fix vision-bridge reroute #9007 — thanks @Stazyu
- fix(sse): route Poe API-key traffic through DefaultExecutor (#8969) #9014 — thanks @Prudhvivuda
- fix(sse): preserve Gemini thought_signature on Claude Desktop tool turns #9015 — thanks @Prudhvivuda
- fix(sse): preserve Claude Code tool-name casing via Gemini/Antigravity #9016 — thanks @Prudhvivuda
- fix(dashboard): make quota providers expandable #9025 — thanks @jktan0504
- fix(chat): resolve stored combo names before image-model validation (#8986) #9027 — thanks @xiaoyaner0201
- fix(kiro): read usage from the frames Kiro actually sends #9035 — thanks @ddarkr
- fix(kiro): keep relocated tool documentation on multi-turn requests #9036 — thanks @ddarkr
- fix(vision): preserve images for text-only routes #9037 — thanks @rinseaid
- fix(db): bundle and verify the sql.js fallback #9044 — thanks @nguyenha935
- fix(open-sse): route GitHub Copilot gpt-5.6 sol/terra/luna to /responses #9050 — thanks @marchlhw
- fix: skills & memory — tool-name encoding, schema normalization, warm-cache, combo id, Ponytail catalog #9058 — thanks @Egorich-print
- fix(classify): recognize Modal 'usage limit reached' as quota exhausted #9079 — thanks @HouMinXi
- Fix/issue #8656 #9095 — thanks @infinit-X
- fix(adobe-firefly): open browser sign-in and resolve provider slug in /login #9097 — thanks @artickc
- fix(providers): make model Check/Test honor the node apiType, and show upstream model names #9099 — thanks @zhiru
- fix(audio): let the audio routes use audio-typed provider nodes, and gate remote ones behind a default-off flag #9101 — thanks @zhiru
- fix(antigravity): alias gemini-3.1-pro-high to gemini-pro-agent #9106 — thanks @HouMinXi
- fix(api): flatten single-row embedding vectors to OpenAI shape #9148 — thanks @aniketshukla1
- fix(proxy): restore connection pooling on proxy/relay paths (#9100) #9158 — thanks @oyi77
- fix(rate-limit): separate queue wait from execution timeout #9164 — thanks @Zartharas
- fix(translator): translate Codex agent messages for Chat #9171 — thanks @Gioxaa
- fix(settings): allow hidePaidModels updates #9182 — thanks @Zartharas
- fix(routing): evict affinity after terminal stream EOF #9184 — thanks @Zartharas
- fix(docker): bundle LLMLingua optional dependencies #9185 — thanks @Zartharas
- fix(health): skip disabled provider connections #9186 — thanks @Zartharas
- fix(claude): preserve standalone whitespace deltas #9189 — thanks @Zartharas
- fix(sse): evict a principal's own CCR blocks before another principal's (#9146) #9191 — thanks @fajarhide
- fix(responses): normalize terminal usage for Codex #9192
- fix(sse): back the CCR block store with a durable tier (#9061) #9198 — thanks @fajarhide
- fix(combo): recover provider circuit breaker from HALF_OPEN on success #9207 — thanks @HouMinXi
- fix(vision-bridge): improve compatibility with Anthropic image blocks and self-loop describe requests #9226 — thanks @Stazyu
- fix(images): refresh OAuth credentials and rotate accounts after 401 #9231 — thanks @Bl0ck154
- fix(ci): merge-queue tolerance for Build (advisory), drops paid-tier batching #9233 — thanks @wgordon17
- fix(token-refresh): exempt transient errors from exponential backoff #9242 — thanks @HouMinXi
- fix(resilience): count STREAM_EARLY_EOF as a provider failure in combo routing #9251 — thanks @TechNickAI
- fix(command-code): preserve literal max effort for command-code provider #9257 — thanks @Chewji9875
- fix(dashboard): open webhook wizard in edit mode #9272 — thanks @khoazero123
- fix(mcp): remove non-standard x-provider field from omniroute_test_combo body #9274 — thanks @Sam280903
- fix(routing): bare model ids route to codex first; validate synced candidates #9275
- fix(mcp): stop DB init logging from corrupting the stdio JSON-RPC stream #9281 — thanks @Sam280903
- fix(reasoning): forward Ollama Cloud thinking #9290 — thanks @xz-dev
- fix(models): reconcile active live model catalogs #9294 — thanks @Zartharas
- fix: update Baichuan website URL to baichuan-ai.com #9312 — thanks @zabrodschiipavel-sketch
- fix(agentrouter): retry on 400 content-blocked + burst guard #9323
- fix:nanogpt model discovery #9326 — thanks @TheFrenchGhosty
- fix(rate-limit): patch Bottleneck doExpire capacity leak #9328 — thanks @HouMinXi
- fix(auth): let an agy request find the connection it authorized #9340 — thanks @HouMinXi
- fix(combo): network errors must not trip provider circuit breaker #9342 — thanks @HouMinXi
- fix(antigravity): propagate switchAuth signal from 429 engine to retry guard #9351 — thanks @HouMinXi
- fix(routing): correct reset-window strategy prioritization (#9330) #9353 — thanks @Iammilansoni
- fix(sse): drop the localDb barrel imports from chat and auth #9380 — thanks @HouMinXi
- fix(providers): enforce gemini-web reasoning and tool constraints (#9356) #9397 — thanks @Iammilansoni
- fix(combo): complete #8400 — preserve full fallback order + per-model account affinity for deterministic combos #9420 — thanks @Chewji9875
- fix(translator): normalize streamed optional tool arguments #9423 — thanks @KittisakT
- fix(providers): correct Codex GPT-5.6 context limits #9432 — thanks @PixmaNts
- fix(usage): stop double-counting cache-read tokens in Command Code executor #9438 — thanks @Stazyu
- fix(deps): bumps transitive deps for 8 CVEs surfaced by vuln-ratchet #9441 — thanks @wgordon17
- fix(ui): preserve request log position #9452 — thanks @xiaoyaner0201
- fix(sse): preserve client cache boundaries when hoisting system roles #9457 — thanks @LeonG606
- fix(providers): switch minimax from claude to openai format so images work #9463
- fix(sse): take the Antigravity output ceiling from the model, not a constant #9482 — thanks @HouMinXi
- fix(docs): add required MDX frontmatter to AGENTROUTER_WAF.md #9503
- fix(quality): prune a stale entry from the ESLint suppressions baseline #9509 — thanks @HouMinXi
- fix(classify): honor upstream retry windows on Gemini free-tier 429s #9513 — thanks @shixi-li
- fix(quality): 2 production bugs + 24 unit base-reds + measured gate ceilings #9529
- fix(providers): support data URL icons #9555 — thanks @xz-dev
- fix(sse): shrink chat.ts back under the frozen file-size cap (base-red drain) #9598
- fix(ci): clear base-reds on release/v3.8.50 (migration collision + 4 masked gates) #9600
- fix(resilience): enforce RPM with rolling leases #9604
- fix(backend): stop reasoning replay placeholder from self-poisoning #9610 — thanks @stanleytejakusuma
- fix(providers): mint a Zed LLM token for zed-hosted model discovery #9628 — thanks @ARC345
- fix(test): reconcile base-drifted test expectations on release/v3.8.50 #9634 — thanks @HouMinXi
- fix(openrouter): scope model failures per-model instead of poisoning the whole connection #9635 — thanks @hartmark
- fix(radar): close the audit gaps — auth, merged feed fields, opt-in state, sidebar gate, size cap + daily scheduler #9686
- fix(ci): clear the NEW base-reds from the 08-06 merge batch (migration collision #2 + broken import) #9688
- fix(codebuddy-cn): replace agent system prompts to bypass Tencent content filter #9723 — thanks @zuckdorsey
- fix(db): resolve migration version 135 numbering collision #9745 — thanks @hartmark
- fix(ci): clear the 08-08 base-red layers — dead-code, prod crash in chat.ts, Responses payload regression, born-red stdio test, gate drifts #9757
- fix(bun): make server child and outbound fetch Bun-safe #9761 — thanks @Arul-
- fix(sse): preserve original body for semantic cache signature — fixes 0% hit rate #9775
- fix(api): validate request bodies with Zod in 4 routes — restores the t06 gate (#9737) #9779
- fix(ci): restore current release test integrity #9819
- fix(ci): close remaining release-green gaps #9835
- fix(proxy): isolate wreq TLS sessions by account #9837 — thanks @agisota
- fix(cursor): SelectedImage blobIdWithData + JPEG soft-cap prep #9840 #9834 — thanks @yansigit
- fix(executors): strip redundant oneOf matching sibling enum #9841 #9828 — thanks @larin-vas
- fix(executors): preserve Command Code usage in Responses streams #9842 #9826 — thanks @MrShitFox
- fix(responses-api): tool call after a text message collided on the same output_index #9843 #9822 — thanks @hartmark
- fix(admission): queue heavyweight chat requests before 503 busy #9845 #9816 — thanks @herjarsa
- [TS7] fix(types): accept synced catalog model rows #9846 #9798 — thanks @backryun
- [TS7] fix(types): normalize DuckDuckGo request messages #9847 #9797 — thanks @backryun
- [TS7] fix(types): expose SQLite transaction state #9848 #9796 — thanks @backryun
- [TS7] fix(types): validate default executor pool config #9849 #9795 — thanks @backryun
- [TS7] fix(types): normalize Gemini Business credentials #9851 #9792 — thanks @backryun
- [TS7] fix(types): preserve Claude thinking body contracts #9852 #9791 — thanks @backryun
- fix(response): strip internal reasoning placeholder from all reasoning fields #9853 #9790 — thanks @adevwithpurpose
- fix(api): enforce model permissions on gateway mirrors #9854 #9788 — thanks @xz-dev
- fix(sse): apply Azure param rules on azure-ai and clamp gpt-4o-mini output tokens #9855 #9787 — thanks @Michael-Rocco-Goldmann
- fix(sse): route claude discovery aliases for catalog-only providers #9856 #9777 — thanks @Michael-Rocco-Goldmann
- fix(i18n): translate validation model keys in 34 locales #9857 #9773 — thanks @Michael-Rocco-Goldmann
- [TS7] fix(skills): normalize web fetch credentials #9859 #9755 — thanks @backryun
- [TS7] fix(types): narrow DeepSeek tool calls #9860 #9751 — thanks @backryun
- fix(pricing): memoize getSyncedPricing() across catalog cache versions #9861 #9746 — thanks @chloeassistant
- fix(logging): use configurable max-depth when bounding logged tool_calls #9865 #9734 — thanks @hartmark
- fix(executors): repair DuckDuckGo AI Chat challenge solver (418 ERR_CHALLENGE) #9866 #9733 — thanks @Mynacol
- fix(compression): persist RTK renderer configuration #9867 #9730 — thanks @isaaclb98
- fix(dashboard): unregister leftover service workers in dev mode #9868 #9727 — thanks @hartmark
- fix(docker): make the webpack build-arg escape hatch actually work #9872 #9695 — thanks @HouMinXi
- fix(providers): per-provider opt-out for anonymous no-auth fallback (opencode-go/zen 401s) #9873 #9675 — thanks @chloeassistant
- fix(providers): reject the dashboard password as a connection API key #9877 #9572 — thanks @HouMinXi
- fix(settings): use provider prefixes in model overrides #9878 #9569 — thanks @xz-dev
- fix(translator): preserve Kimi K3 Responses reasoning #9879 #9556 — thanks @jackjinke
- fix(adobe-firefly): harden CDP warm, risk session, and browser sign-in #9881 #9549 — thanks @artickc
- fix: pass max reasoning effort through by default, add global model registry fallback #9883 #9612 — thanks @Momen4444
- fix(db): resolve CCR migration version collision #9884 #9618 — thanks @fenix007
- fix(compression): add Lite tool truncation toggle #9885 #9629 — thanks @xz-dev
- fix(web-tools): anchor tool contract at prompt tail + user-turn reminder for large prompts #9887 #9693 — thanks @ryan-brosas
- fix(sse): persist per-tool-call JSON escape state across SSE delta chunks #9889 #9704 — thanks @hartmark
- fix(db,combo): renumber ccr_blocks 134→139 + restore antigravity pool filter #9890 #9707 — thanks @matiasbaglieri
- fix(sse): grace period before finalizing a client disconnect as 499 #9891 #9711 — thanks @hartmark
- fix(build): standalone bundle misses LLMLingua dist + onnxruntime native binaries #9892 #9712 — thanks @hartmark
- fix(responses-api): sync reasoning-cache write index with the fixed read side #9895 #9741 — thanks @hartmark
- fix(ci): repair release lint test regressions #9896 #9813 — thanks @alex-jordan547
- fix(command-code): include tool call arguments #9897 #9821 — thanks @Chewji9875
- fix(providers): remove retired NVIDIA NIM catalog entries #9898 #9825 — thanks @Zartharas
- fix(nvidia): keep 410 failures model-scoped #9899 #9833 — thanks @Zartharas
- fix: retry CodeBuddy large-tool requests in compact form #9900 #9542 — thanks @mvanhorn
- fix(quality): clears two release/v3.8.50 base-red gates #9901 #9619 — thanks @wgordon17
- fix: resolve hollow external package directory crashes and implement … #9913 — thanks @SupremeNexas
- fix(i18n): restore Vietnamese locale parity #9925
- fix(combo): classify Cloudflare 1010 fingerprint rejection as non-auth #9929 — thanks @HouMinXi
- fix(image): return Fal images as base64 by default #9932 — thanks @rinseaid
- fix(image): support Fal reference-image edits #9933 — thanks @rinseaid
- fix(db): invalidate LKGP pins on provider connection delete #9936 — thanks @Zartharas
- fix(services): stop embedded-service supervisor retry loop when binary cannot spawn #9937 — thanks @herjarsa
- fix(backend): retain streaming usage for providers with choices:[{delta:{}}] final chunk #9938 — thanks @sadSanta-07
- fix(antigravity): ban-safety hardening — bounded onboarding retries, gate thought-signature bypass sentinel #9939 — thanks @benzntech
- fix(guardrails): vision bridge reroute/pool/self-loop fixes (auto/best-vision, claude-wire base64) #9946 — thanks @herjarsa
- fix(mcp): stop omniroute_get_health silently discarding real data #9959 — thanks @tald26
- fix(build): bump @huggingface/transformers to 4.2.0 + onnxruntime-node 1.24.3 #9962 — thanks @witt3rd
- fix(cleanup): prune mcp_tool_audit/a2a_task_events by created_at column #9963 — thanks @witt3rd
- fix(db): avoid skipping pending job registry migration 146 #9965 — thanks @Zartharas
- fix(video): support Fal-hosted Grok Imagine Video #9969 — thanks @rinseaid
- fix(i18n): escape angle brackets in denoRelayOrgDomainHint across all 43 locales #9976 — thanks @AgnesRiber
- fix(media): support Gemini Omni Flash video #9982 — thanks @rinseaid
- fix(copilot-web): restore browser authentication #9984 — thanks @backryun
- fix(opencode): fallback unsupported DeepSeek json schema output #9992 — thanks @Zartharas
- fix(translator): restore TitleCase tool names on the Claude to Gemini path #9993 — thanks @engmarcosjr
- fix(providers): scope model-level targetFormat to declaring provider catalog #9994 — thanks @Chewji9875
- fix(kimi): apply K3 effort policy to aliases #10005 — thanks @jackjinke
- fix(adobe-firefly) — direct pushes: harden credential parsing and…
- fix(combo/sse) — direct pushes: ignore benign empty error fields…
- fix(logging) — direct pushes: make stream-chunk capture and…
- fix(i18n) — direct pushes: restore/unescape HTML entities in UI…
- fix(providers) — direct pushes: repair the DeepAI registry import…
- fix(deps) — direct pushes: CVE-driven bumps (nanoid, dompurify,…
- fix(ci): pin Build (advisory) to a hosted runner with memory… #10408
- fix(providers): refresh the translate-path golden for the… #10410
- fix(sse): surface Qwen/Alibaba personal Token Plan quota in… #10290
- fix(deps): pin next to an exact version so a fresh upstream… #10340
- fix(types): restore custom model output… #10339 — thanks @backryun
- fix(sse): stop the executor-contract guard from hot-looping… #10373
- fix(types): validate nonstreaming JSON… #10258 — thanks @backryun
- fix(types): narrow refresh token rotation… #10257 — thanks @backryun
- fix(types): normalize executor result… #10256 — thanks @backryun
- fix(types): align Responses stream options #10255 — thanks @backryun
- fix(types): narrow combo credential… #10254 — thanks @backryun
- fix(compression): cap… #10118 — thanks @adevwithpurpose
- fix(ci): clear base-reds on release/v3.8.50 (round 4 #10260
- fix(sse): extract perplexity-web… #10259 — thanks @jeyhunfaslanov
- fix(mcp): persist and re-attach Gemini… #9448 — thanks @Sam280903
- fix(opencode-plugin): respect… #8982 #9316 — thanks @xiaoyaner0201
- fix(providers): raise default provider… #9283 — thanks @Sam280903
- fix(opencode-plugin): stop… #8983 #9042 — thanks @xiaoyaner0201
- fix(opencode): force CLI… #10222 — thanks @adevwithpurpose
- fix(deepseek-web): classify business… #10218 — thanks @Zartharas
- fix(combo): make failoverBeforeRetry… #10217 — thanks @hartmark
- fix(responses): preserve case-insensitive… #10177 — thanks @ddarkr
- fix(discovery): parse reasoning… #10138 — thanks @excessivechaos
- fix(combo): isolate session stickiness… #10137 — thanks @hydraxman
- fix(combo): default chaos SSE to… #10128 — thanks @herjarsa
- fix(kimi): normalize MFJS tool schemas #10079 — thanks @xz-dev
- fix(mcp): move pack validation out of… #10065 — thanks @yansigit
- fix(zed-hosted): send the provider wire… #10051 — thanks @ARC345
- fix(ci): repair and wire the two… #10050 — thanks @ARC345
- fix(reasoning): preserve and replay… #10045 — thanks @jackjinke
- fix(types): tighten chatCore helper… #10175 — thanks @backryun
- fix(cli): read the full provider… #10097 — thanks @amartinawi
- fix(cli): stop swallowing non-2xx… #10092 — thanks @amartinawi
- fix(cli): openapi… #10091 — thanks @amartinawi
- fix(cli): doctor detects prebuilt… #10090 — thanks @amartinawi
- fix(providers): kilo-gateway authType… #10086 — thanks @TengSivtean
- fix(cli): strip inline comments when… #10101 — thanks @amartinawi
- fix(logging): document… #10038 — thanks @hartmark
- fix(dashboard): expose OpenAI Responses… #10121 — thanks @hartmark
- fix(combo): clear LKGP pin when its… #10034 — thanks @hartmark
- fix(sse): provider-response summary… #10037 — thanks @hartmark
- fix(responses-api): tool call after… #10025 — thanks @hartmark
- fix(responses-api): explicit… #10041 — thanks @hartmark
- fix(kimi): recupera limite temporario… #10058 — thanks @bortolidiego
- fix(translator): preserve Responses… #10114 — thanks @mtb-ninja
- fix(providers): xai-oauth… #10165 #10170 — thanks @nordz0r
- fix(ollama-cloud): map xhigh reasoning… #10160 — thanks @Chewji9875
- fix(translator): strip Codex encrypted… #10053 — thanks @XDayonline
- fix(combo): preserve OpenCode Free oc/… #10180 — thanks @AStupidBear
- fix(sse): apply free-tier filter to… #10199 — thanks @ggdayup
- fix(providers): default missing… #10221 — thanks @jeff-alves
- fix(ci): clear base-reds on release/v3.8.50 (round 3 #10213
- fix(security): correct XML double-unescape and non-CSPRNG… #10154
- fix(docker): eliminate npm-bundled CVEs from the published… #10182
- fix(security): resolve open CodeQL alerts #10188
- fix(dashboard): retarget Kimi promo CTA to the API platform… #10200
- fix(build): repair broken production build, red lint gate and… #10198
- fix(cli): repair hollow externalized package dirs in the nested… #7346
- Electron packaged smoke test: add a cold-restart mode… #7592
- fix(usage): keep session/weekly/monthly quota windows in… #7764
- fix(images): retry Codex image generation on a sibling ChatGPT… #8307
- fix(dashboard): treat UncloseAI as a no-auth provider so the… #8864
- fix(dashboard): model-level allowed/blocked param edits now… #9013
- fix(ssrf): make
getProviderOutboundGuard(used for… #9123 - fix(compression): preserve unfenced raw code (e.g. Copilot #file… #9144
- fix(api): yield the event loop during catalog builds and bulk-load… #9147
- fix(combo): recovery hint for all_targets_skipped now points at… #9303
- fix(providers): strip uniqueItems from Gemini tool schemas (Gemini… #9617
- fix(translator): convert OpenAI
image_urlblocks nested in… #9692 - fix(resilience): retry a retryable Codex pre-output… #9708
- fix(ratelimit): respect operator
minTimeBetweenRequestsMs… #9763 - fix(test): remove live
npm packfrom MCP files unit… #3578 #3821 - fix(dashboard): media mini-playgrounds authenticate via session… #9935
- fix(sse): exclude search providers from credential-health… #9970
- Passthrough streaming: stop leaking upstream SSE control lines… #10017
- fix(cli): stop diagnosing every Next.js instrumentation-hook… #10028
- fix(build): stop the native… #10060 — thanks @latest
- fix(providers): the five g4f.space… #10071 — thanks @chirag127
- fix(chatgpt-web): Preserve native
max… #10077 — thanks @zannen7 - Fix: wire AgentRouter's existing console balance fetcher into the… #10078
- Fix: AgentRouter's dollar balance now renders as a… #10078
- fix(sse): bridge generic openai-compatible/anthropic-compatible… #10085
- fix(domain): stop treating an unreported Antigravity quota… #10095
- fix(dashboard): remap unified Kimi Code card API-key save to the… #10096
- fix(antigravity): strip trailing model turn for native Gemini… #10104
- fix(admission): stop the adaptive latency-gradient collapse… #10111
- fix(sse): downgrade client-supplied
thinking:{type:"adaptive"}… #10119 - fix(logging): move call-log artifact serialization and… #10123
- perf(logging): bound each scheduled call-log rotation pass to… #10125
- fix(streaming): start early SSE heartbeats when Responses or… #10127
- fix(combo): scope session-stickiness bindings to their owning… #10136
- fix(translator): resolve the Claude thinking output cap with… #10139
- fix(providers): correct the conol-web registry fallback-models import…
non-existentopen-sse/config/services/and made any suite loading the provider registry fail to
resolve (#10140) - fix(oauth): Claude connections created via… #10144 #10143
- fix(sse): Responses-passthrough
response.completedsnapshots… #10156 - fix(proxy-subscriptions): allow local/loopback proxy-subscription… #10158
- fix(routing): keep approximate Combo… #10162 — thanks @xz-dev
- docs(settings): document Thinking Budget modes (passthrough vs… #10169
- fix(cli): guarantee a non-empty
[STARTUP] Fatal:log line for… #10171 - fix(sse): gate structural chat admission shedding on real… #10183 #10268
- fix(guardrails): Vision Bridge handles… #10202 — thanks @Zartharas
- fix(cursor): Stop truncating pending tool calls on… #10215
- fix(responses): repair corrupted SSE deltas for non-ASCII… #10223
- fix(combo): defer the known-context-overflow hard rejection… #10225
- fix(api): deleting a… #3199 #3782 #10228 — thanks @Neuron-Mr-White
- Audio Bridge: fix production transcription self-loop uploads… #10229
- fix(api): DeepSeek V4's native… #10230 — thanks @Neuron-Mr-White
- fix(providers): FreeAIAPIKey now targets… #10233
- fix(providers): MonsterAPI's deprecation now actually applies… #10234
- fix(cliproxy): read platform/arch at runtime via… #10244
- fix(providers): compatible/custom providers… #10247 — thanks @xz-dev
- fix(models): custom model metadata and… #10248 — thanks @jackjinke
- fix(open-sse): stop concurrent requests colliding on the same… #10249
- fix(translator): Text-format tool calls emitted inline by some… #10251
- fix(dashboard): make provider card warning indicators expose the… #10261
- fix(command-code): route chat to the documented… #10265
- fix(providers): preserve validator HTTP… #10272 — thanks @Zartharas
- fix(sse): tiny-budget reasoning… #10281 — thanks @harkaranbrar7
- fix(video): stop advertising the googleflow (Veo) video provider… #10285
- fix(build): stop Turbopack from dead-code-eliminating the… #10293
- fix(ops): Docker HEALTHCHECK defaults to the lightweight… #10311
- fix(api): hash the API key before using it as the model-catalog… #10313
- fix(resilience): keep combo quality and auth failure reasons… #10314
- fix(dashboard): send periodic WS heartbeat pings so live… #10319
- fix(chat-body-admission): restore a single process-wide… #10110
- fix(providers): validate Z.ai web Local… #10329 — thanks @Zartharas
- fix(opencode-plugin): publish bare combo model ids without the… #10345
- fix(backend): log
auto/<family> matched no connected models… #10346 - fix(backend): redact client IPs and account prefixes from default… #10348
- fix(github): proactive credential… #10352 — thanks @RaviTharuma
- fix(docker): warn at boot when
OMNIROUTE_MEMORY_MBdisagrees… #10353 - fix(providers): GitLab Duo falls back to the public Code… #10365
- fix(db):
getSettingsdefaults… #10372 — thanks @lamchun1110 - fix(translator): Consolidate tool-name casing… #7926 #8979 #10374
- fix(responses): preserve native tool definitions for custom… #10374
- fix(dashboard): Free Tier 'used this month' now includes live… #10381
- fix(executors): OpencodeExecutor and MimocodeExecutor now… #10393
- fix(sse): the header-budget drop… #10397 — thanks @lamchun1110
- fix(sse): fail over combo streaming responses that reach… #10404
- fix(guardrails): Vision Bridge now reroutes… #10415 — thanks @rqzbeh
- fix(antigravity): geo-blocked egress… #10420 — thanks @rqzbeh
- fix(antigravity): strip competing-agent… #10420 — thanks @rqzbeh
- fix(antigravity): accounts with an empty… #10424 — thanks @rqzbeh
- fix(antigravity): Google deprecated… #10424 — thanks @rqzbeh
- fix(usage): read Gemini
usageMetadata… #59 #10430 — thanks @rqzbeh - fix(usage): surface Gemini… #10430 #10465 — thanks @rqzbeh
- fix(antigravity): automatically… #10424 #10470 — thanks @rqzbeh
- fix(mitm): forward passthrough traffic to the actual requested… #10479
- fix(docker): point the bifrost sidecar at the real… #10482
- fix(sse): stop ZWJ-obfuscating the substring "hermes" in user… #10484
- fix(memory): auto-check Qdrant health on mount and stop the… #10489
- test(compression): align source-contract tests with the merged… #10489
- fix(cli): use 127.0.0.1 for the readiness health-check poll… #10508
- fix(providers): zed-hosted OAuth now… #10517 — thanks @phatchau036
- fix(providers): allow token-backed web… #10518 — thanks @Zartharas
- fix(providers): test token-backed web… #10519 — thanks @Zartharas
- fix(compliance): redact additional… #10521 — thanks @Zartharas
- fix(providers): register a real Firefly auth probe under both the… #10522
- fix(services): isolate probeBeforeSpawn adoption tests on… #10523
- fix(sse): auto-replay a bounded multi-turn trajectory in the… #10527
- fix(network): direct (no-proxy) egress now bounds each… #10214
- fix(models): align Codex GPT-5.6 context limits with the Codex… #10530
- fix(deps): upgrade… #10536 — thanks @atjsh @tensorflow @huggingface
- fix(deepseek): Advertise
none,low,… #10540 — thanks @jackjinke - fix(a2a): use a constant-time bearer compare in… #10544
- Preserve portable plaintext reasoning by default across… #10550 #10959
- fix(dashboard): show the real model count on the "List Models"… #10553
- fix(cli): ignore the operating… #10557 #10492 — thanks @redzrush101
- fix(providers): OpenCode
x-opencode-sessionnow derives a… #10571 - fix(mcp): make GitHub skill tools discoverable through…
- fix(providers): remove 10 retired model ids from the crof seed… #10577
- fix(audio): when a prefix-matched STT provider has no… #10583
- fix(sse): resolve the short provider-alias prefix (e.g.
el/)… #10586 - fix(sse): map OpenAI-compat voice names to real ElevenLabs… #10589
- fix(dashboard): route the Playground's ChatTab "Send" through the… #10592
- fix(providers): Magnific Mystic is now the canonical provider… #10594
- fix(sse): Include the redacted upstream error body in the… #10597
- fix(xai): trim Chat Completions
messagesand Responses… #10601 - fix(cli): derive the machine-id token correctly under plain… #10612
- fix(cli):
omniroute setup --add-provider --api-key <key>no… #10613 - fix(dashboard): make /api/models agree with /v1/models on… #10615
- Combo routing: await each connection's token limit before… #10686
- fix(guardrails): resolve the public provider alias before… #10702
- fix(dashboard): filter the Modality Bridge Vision model picker to… #10703
- fix(usage): repair provider-reported input_tokens: 0 on… #10705
- fix(cli): distinguish a CLI-probe timeout from a genuinely… #10710 #10711
- fix(cli): pass --allow-scripts for the runtime's own… #10713
- fix(db): filter
getProviderMetricsto providers with a live… #10714 - fix(proxy): keep password-only proxy credentials instead of… #10720
- fix(executors): the Meta AI (muse-spark-web) WebSocket… #10727
- fix(providers): copilot-m365-web chat… #10732 — thanks @acc0mplish
- fix(catalog): stop counting
getTokenLimit's generic 128k… #10734 - fix(search): name
/v1/search502s with provider id and… #10735 - fix(db): pause call-log rotation and record SQLITE_CORRUPT on… #10736
- fix(compression): skip the expensive
createCompressionStats… #10765 - fix(api):
/api/cache/statsreported the… #9446 — thanks @Poid-ZA - fix(logging): the app log is filterable and readable again. Entries…
- fix(analytics): Claude Code… #10774 — thanks @electrumguy
- fix(db): periodically run
wal_checkpoint(TRUNCATE)so the… #10781 - fix(sse): replace LiveWS's application-only liveness check with a… #10782
- fix(open-sse): declare
supportedThinkingEfforts… #10788 - fix(resilience): scope the same-account transport retry out of… #9708
- fix(opencode-plugin): respect log level… #10798 — thanks @tientien17
- fix(providers): Keep NVIDIA timeout… #10799 — thanks @Zartharas
- fix(db): disambiguate
createProviderConnection's OAuth email… #10815 - fix(oauth): stop treating the Kiro profile ARN as an account… #10815
- fix(images): register OpenAI
dall-e-3in the image registry… #10832 - fix(security): Outbound URL guard now… #10843 — thanks @ntdat812
- fix(config): exclude cookie-auth image bridges (chatgpt-web,… #10848
- fix(api): POST /v1/search now replies with a named `Unknown… #10849
- fix(api): alias
GET/HEAD/readyzto/healthzso… #10850 - Document the conditional management authentication and 401/403…
- fix(i18n): The "Disabled" status… #10812 #10853 — thanks @ntdat812
- fix(skills): Marketplace-installed… #10854 — thanks @kriptoburak
- fix(catalog):
/v1/modelsno… #10831 #10857 — thanks @ntdat812 - fix(context): Base64 file payloads… #10840 #10858 — thanks @ntdat812
- fix(mcp): MCP tool calls that wait on a model provider no longer…
- fix(providers): importing models with an expired API key… #5460 #5465
- fix(api): reject a combo update that removes every model, and… #10866
- fix(proxy): proxy "Test connection" no longer reports an… #1255
- fix(cli): warn when a .env line never takes effect, and stop… #10870
- fix(db): Remove stale MiMoCode provider… #10873 — thanks @Zartharas
- fix(sse):
getResetAwareProviderand the auto-combo quota… #10877 - fix(provider-health): Keep unsupported… #10878 — thanks @Zartharas
- fix(antigravity): map Gemini 3.7… #10882 — thanks @adevwithpurpose
- fix(memory): enable agent memory… #10887 — thanks @Egorich-print
- fix(perplexity-web): make the… #10902 #8634 — thanks @danscMax
- fix(providers): the loopback readiness gate no longer… #10903
- fix(relay): the Cloudflare… #4643 #1360 #6149 — thanks @evil
- fix(build): the
nextDocker… #10936 #10933 — thanks @arminanton - fix(cli): always emit limit.output in generated OpenCode config… #10940
- fix(relay): the private/loopback guard the three proxy-relay… #6149
- Account rotation: make
fallbackStrategy: "least-used"… #10945 - Desktop auto-update (Windows): stop the in-app updater 404ing… #10947
- Preserve explicit plaintext reasoning when a Responses… #10949 #10959
- fix(catalog): preserve provider-declared… #10953 — thanks @xz-dev
- fix(cli): combo create accepts --models and no longer creates… #10954
- fix(cli): resolve $ref path params and add PATCH combos… #10955
- fix(sse): default single-target incompatible reasoning to drop… #10959
- fix(sse): combo diagnostics no longer truncate… #10967
- fix(sse): combo terminal failures caused entirely by… #10966
- fix(search): skip catalog-default SearXNG… #10976
- fix(command-code): surface reasoning-only output as content when… #10986
- fix(ci): clear inherited
release/v3.8.50quality-gate reds… #10988 - fix(ci): clear remaining
release/v3.8.50… #9147 #10501 #10988 - Static model catalog for v0-vercel-web: seed a static catalog… #10990
- fix(providers): mark the blackbox provider deprecated —… #10997
- fix(providers): validate Dify keys against its native… #11002
- fix(accounts):
markCooldownnow… #11008 — thanks @maxmad64bis - fix(providers): route terminal… #11009 — thanks @maxmad64bis
- fix(codex): drop non-standard… #11014 — thanks @RaviTharuma
- fix(resilience): count heavyweight… #11015 — thanks @RaviTharuma
- fix(startup): log `Credential health… #11016 — thanks @RaviTharuma
- docs(api-keys): document that… #2289 #11017 — thanks @RaviTharuma
- fix(webhooks): remove 3 declared-but-never-emitted events…
- fix(providers): filter Perplexity model import to the Sonar… #11060
- fix(claude): restore canonical tool names… #11085 — thanks @linhdmn
- fix(resilience): filter chat connection selection by each… #11089
- fix(install): make the ONNX dependency chain optional so… #11095
- fix(providers): Reject silent validation degradation on… #11101
- Autopilot suggestion counter: the combo health autopilot… #11102
- Config audit persistence: persist the configuration audit… #11103
- fix(sse): resume mid-stream recovery after a completed tool… #11109
- fix(providers):
reasoning_effortnow… #11116 — thanks @maxmad64bis - fix(sse): parallel
function_callitems in a Responses API… #11144 - fix(analytics):
opencode-gois now… #11149 — thanks @electrumguy - fix(dashboard): keep
open-sse/config/providerRegistry.ts… #11122 #11154 - Combo create: creating a routing combo without any model is now…
- fix(resilience): a missing-model… #11165 — thanks @yourspraveen
- fix(routing): a custom… #11180 — thanks @marcs7
- fix(routing): the Routing tab's "last known good provider"… #11181
- fix(ollama): Ollama Local models are… #11271 — thanks @yourspraveen
- fix(providers): Antigravity OAuth marks connects with no Cloud… #11284
- fix(translator): preserve omitted… #11297 — thanks @ofonseca-pyming
- fix(db): group model patterns escape regex metacharacters, so… #11311
- fix(db): the upstream proxy URL check judges the host by… #11319
- fix(i18n): three
ptstrings had dropped their placeholders… #11325 - fix(kie): map the remaining
google-imagen/*KIE… #11225 #11326 - fix(security):
proxy-authorizationandproxy-authenticate… #11328 - fix(video-bridge): fall back to the deterministic… #11344
- fix(translator): Codex Responses tool calls translated for… #11347
- fix(video-bridge): burn high-contrast timestamps into every… #11350
- fix(video): fingerprint protected Video Bridge bytes, coalesce… #11362
- fix(catalog): keep large
/v1/modelsbuilds responsive by… #11367 - fix(video): apply the caption-frame cap after bounded visual… #11382
- Live dashboard: honour the WebSocket port reported by… #11331
- fix(dashboard): Model Database sync… #11394 — thanks @An0nym0us92
- fix(api-manager): Allowed Combos can now be restricted to zero…
- fix(build): tolerate a same-realpath symlink or stale-typed dest in the…
- fix(auto): rate-limit
auto/<family> matched no connected models… - fix(cli): drop the orphaned
resolveOpencodeConfigDir… #10246 #9985 - fix(ci): make
Build (advisory)produce a signal again — pinned to a… - fix(api): hash API keys in the
/v1/modelscatalog cache Map key so… - fix(providers): register live OpenRouter… — thanks @RaviTharuma
- fix(translator): merge consecutive same-role contents in direct…
- fix(cli):
omniroute updatenow finds npm… #52554 #5379 #5542 #11335 - fix(cline): Preserve client-supplied Cline task IDs and omit the…
- Hardened the Codex app-server transport after the post-merge… #11205
- fix(codex): prefer
max_context_windowover thecontext_window… - fix(catalog): derive combo reasoning-effort tiers from the exact…
- fix(combo): evict in-memory session-stickiness bindings when a combo…
- fix(combo): resolve effort-suffixed command-code variants (e.g.…
- fix(db): the
compression_run_telemetryretention sweep… #6848 #9625 - fix(compression): use
pathToFileURLincompressionWorkerPoolso… - fix(db): database settings API no longer returns HTTP 500 on SQLite…
- fix(discovery): parse upstream reasoning tiers nested under…
- fix(ops): Docker HEALTHCHECK probes lightweight
/healthzinstead… - fix(api):
/v1/embeddings400s for native… — thanks @RaviTharuma - fix(sse): keep Codex/Anthropic quota headers under the upstream…
- fix(usage): z.ai/GLM coding-plan subscription keys now render their…
- fix(auth): a connection's
lastErrornow names the real upstream… - fix(live-ws): the Live dashboard socket can now be pointed at… #11331
- fix(sse): MiniMax music models now generate audio instead of failing…
- fix(models): honor
MODELS_DEV_SYNC_ENABLED=0as a hard kill switch… - fix(providers): when
OPENCODE_SYNTHESIZE_CLI_HEADERS=true, a… #5997 - OpenCode config merge: stop
mergeOpenCodeConfigsplaying a… - fix(models): a model synced from a provider's own
/models… - fix(providers): Claude Code /… — thanks @jeff-alves
- fix(electron): desktop window stays hidden on Windows because the…
- fix(executors): OpencodeExecutor rotates (or retries once on a…
- fix(cli): recognize native… #10227 — thanks @tito13kfm
- fix(i18n): complete Vietnamese translations for recently added UI… #9985
- fix(api): repair broken
@/lib/db/connectionsimport in the… #10939 - chore(docs): regenerate PROVIDER_REFERENCE and refresh README diagram…
- chore(lint): prune ESLint suppressions orphaned on the release branch
- fix(build): #9011 #9982 #9969 #9199 #8728 #8736 #10087 #8974 #7682 #9173
- fix(security): harden three secret-leak paths surfaced by an audit…
- fix(db): the sql.js fallback now publishes the database atomically…
- fix(sse): #10441 #11152 #11151 #10475 #10980 #10805 — thanks @geek007git @maxmad64bis @HouMinXi @linhdmn @minhlongs
- fix(sse): #11043 #11110 #11129 #10330 #10806 — thanks @maxmad64bis @hartmark @xz-dev
- fix(sse): #11209 #11214 #10803 #10457 #11113 — thanks @linhdmn @Kizuno18 @HouMinXi @ggdayup
- fix(sse): #11194 #10445 #11047 #10435 #10632 #10650 #10450 #10434 #10691 #10674 — thanks @jonlwheat2-gif @killmonger2317-coder @maxmad64bis @octo-patch
- fix(sse): #11177 #10545 #10684 #10978 #10306 #10488 — thanks @rqzbeh @NahuSaruf @cryptiklemur @maxmad64bis @RaviTharuma
- fix(sse): the Adobe Firefly… #11387 #11386 — thanks @HouMinXi
- fix(providers): #11375 #11123 #11262 #11117 #11055 #10356 #10566 #10634 — thanks @ggdayup @rqzbeh @rizxfrog @backryun @RaviTharuma
- fix(providers): reasoning-effort… #11274 #11305 — thanks @linhdmn
- fix(providers): #11309 #11302 #10862 #10971 #10072 #10471 #11049 #10973 #10974 — thanks @ntdat812 @RaviTharuma @xz-dev @sha367 @maxmad64bis
- fix(resilience): #11355 #11310 #10534 #11078 #11267 #11075 #10506 #11020 — thanks @sprintberlin @SnCr90 @rqzbeh @yourspraveen @hartmark @RaviTharuma
- fix(quota): #11353 #11378 — thanks @sprintberlin @Neuron-Mr-White
- fix(combo): #11360 #10463 #10456 #11036 #10907 #10846 #10016 — thanks @sprintberlin @herjarsa @asorourx @excessivechaos @HouMinXi @fenix007
- fix(auto): #10820 #10344 #11400 #11399 — thanks @RaviTharuma @jacobsparts
- fix(routing): #11107 #11198 #11193 #10124 — thanks @SCys @pacocartones @benzntech
- fix(models): #10957 #10898 #11397 #11307 #10533 #10299 #10055 — thanks @Neuron-Mr-White @rqzbeh @marcelokarval @ekinnee @jackjinke @RaviTharuma
- fix(catalog): #10963 #10723 #11337 — thanks @xz-dev @stanleytejakusuma
- fix(pricing): #10635 #10636 #11210 — thanks @stanleytejakusuma @xyzs996
- fix(api): #10657 #10654 #10769 #10663 #10253 #10772 #10607 #10565 #11162 #11081 — thanks @maxmad64bis @HouMinXi @dpozimski @sadSanta-07 @pucedoteth @RaviTharuma @AndrianBalanescu
- fix(security): #11429 #11418 #11261 #10888 #11293 #11040 #11028 #10738 #10739 #10380 #11189 — thanks @HouMinXi @rqzbeh
- fix(authz): exact public routes are matched exactly instead of… #11417
- fix(auth): #11376 #11175 #10899 #10614 — thanks @ntdat812 @rqzbeh @MeRezaRezaei
- fix(oauth): #11141 #10725 #10620 — thanks @HouMinXi @MichaelYcJo @krishna3554
- fix(cli): #11374 #11332 #11238 #10835 #11263 — thanks @ntdat812 @pacocartones @adevwithpurpose
- fix(cli): #11173 #11079 #11054 #10572 #10468 #10447 #11264 — thanks @rqzbeh @hydraxman @xiaoyaner0201
- fix(dashboard): #11407 #11402 #11067 #11056 #10872 #10452 #10448 #10449 — thanks @jacobsparts @rqzbeh @Rahulsharma0810
- fix(live-ws): the… #11377 #11269 — thanks @ntdat812 @Minamaged18
- fix(db): #10979 #10709 #10552 #10278 #10423 #10432 — thanks @RaviTharuma @jonlwheat2-gif @excessivechaos @maxmad64bis
- fix(build): #11159 #10836 #10776 #11266 #10695 — thanks @aliyosufi @adevwithpurpose
- fix(docker): #11419 #10818 #10288 — thanks @RaviTharuma @anudeepadi
- fix(electron): the embedded server is… #10717 — thanks @echoriver89
- fix(compression): #11364 #11084 #10807 #10660 #10834 #10498 #10655 — thanks @TheDemonTuan @HouMinXi @blackwell-systems @stanleytejakusuma @adevwithpurpose @abhijeetnardele24-hash
- fix(translator): #11365 #10658 #10392 — thanks @Siva010 @giauphan
- fix(gemini): a missing
itemsschema… #10605 — thanks @sadSanta-07 - fix(codex): #11179 #11041 #10608 #10573 #10838 #10946 — thanks @excessivechaos @jackjinke @JxnLexn @HouMinXi @YunyunZhai
- fix(opencode): #10874 #11004 #11385 #11133 #10821 #11199 — thanks @zoser69 @ntdat812 @AStupidBear @maxmad64bis @RaviTharuma @pacocartones
- fix(executors): #11158 #10402 #10694 — thanks @maxmad64bis @excessivechaos
- fix(search): #10901 #11125 #10756 #10981 #11097 — thanks @rqzbeh @RaviTharuma @Egorich-print
- fix(mcp): #11139 #10860 #10575 #10209 — thanks @HouMinXi @ntdat812 @branben @sadSanta-07
- fix(memory): a mid-conversation… #11303 #11114 — thanks @ggdayup
- fix(embeddings): an account is… #10529 #11260 — thanks @HouMinXi
- fix(video-bridge): structural segment sampling is validated, contact…
- fix(images): #10847 #10363 #10554 — thanks @RaviTharuma @tiangao88 @rinseaid
- fix(fusion): the vision-compatibility filter is applied to… #10737
- fix(proxy): #11182 #10868 #10664 #10416 — thanks @rqzbeh @ntdat812 @maxmad64bis
- fix(relay): one… #10941 #10935 #10797 — thanks @ntdat812
- fix(logging): #10770 #10331 — thanks @maxmad64bis @hartmark @benzntech
- fix(logs): #11082 #10685 — thanks @AndrianBalanescu @cryptiklemur
- fix(webhooks): three… #11050 #11130 — thanks @maxmad64bis
- fix(antigravity): #10422 #10376 #10436 — thanks @JxnLexn @Chewji9875
- fix(adobe-firefly): sessions renew through… #9255 — thanks @artickc
- fix(cline): #10279 #10706 #11132 — thanks @arafatkatze @rqzbeh
- fix(agentrouter): the protocol is inferred from the client endpoint,…
- fix(services): the… #10371 #10459 — thanks @tkgo11 @aron-intframe
- fix(monitoring): #10370 #10307 #10827 #10699 — thanks @tkgo11 @RaviTharuma
- fix(github): GitHub access tokens are… #11320 — thanks @RaviTharuma
- fix(i18n): #11339 #11322 #10546 #9721 #11208 — thanks @pacocartones @rizxfrog @dionjoshualobo
- fix(chat): the… #11304 #10394 #10438 — thanks @azzaouiomar19-sketch
- fix(providers): #11026 #10733 #10810 #10458 #11045 #10116 #10637 #11088 #10904 #10584 #10186 #11207 #10894 — thanks @sanforex24h @InkshadeWoods @Hsia97 @benzntech @MeRezaRezaei @Chewji9875 @backryun @yourspraveen @RaviTharuma @Tushar49 @rafacpti23 @rqzbeh
- fix(radar): feature availability… #10487 #10464 — thanks @backryun
- fix(usage): quota windows are ordered… #11241 — thanks @pacocartones
- fix(files): the list
limit… #10673 #11059 — thanks @pacocartones - fix(onboarding): the setup wizard… #10855 — thanks @krishna3554
- fix(settings): #10890 #10525 — thanks @rqzbeh @HouMinXi
- fix(conversations): the reconnect… #10800 — thanks @adevwithpurpose
- fix(reasoning): compatible response… #10574 — thanks @jackjinke
- fix(agent-bridge): the regenerate-cert… #10715 — thanks @ntdatt812
- fix(api-manager): empty combo restrictions… #10066 — thanks @xz-dev
- fix(db): the Database settings page no… #10558 — thanks @TechNickAI
- fix(models): health-check-excluded… #10026 — thanks @ritheshcn25
- fix(skills): the CLI skills left stale by the quota… #10698
- fix(mcp): CLI MCP call protocol issues… #10960 — thanks @YunyunZhai
- fix(dashboard): expert mode in the Combo Builder can type… #8875 #8285
- fix(sse): a combo step pinned to an explicit connection (or a… #8875
- fix(cli): the local CLI sees the full
/api/monitoring/health… #11040 - fix(search): a configured search connection (Serper, Brave,… #11524
- fix(api):
/v1/modelsno longer blocks the stale… #11551 #10198 #8728 - fix(sse): a universal handoff whose summary comes back… #11552
📝 Maintenance
- refactor(providers): removed the Puter provider (id
puter, alias… - fix(types): preserve the client response format contract while… #8484
- Preserve the Responses API transform options contract under TypeScript…
- fix(types): preserved the known first-failure record while… #8484
- fix(types): reuse the validation failure predicate when… #8484
- fix(types): preserve the literal `capabilities.vision:… #9121 #8484
- chore(quality): add an RTL layout ratchet… #3541 #8828 — thanks @lukiod
- chore(sse): dropped the leftover
iflowentry from the… #8966 - chore(ci): removed two fork-owned image-publish workflows that… #8967
- test(ci): fixed the intermittent
spawnSync bash EPIPEfailure… #8977 - refactor(providers): removed the retired GitHub Models provider… #9023
- test(sse): added the first test suite for…
module was hotspot #7 in the coverage plan at 11.28% lines with no dedicated test; the 14 pure
detectors are now pinned edge-to-edge (token ladders, per-domain max-not-sum scoring, and the
min/max clamps), taking the file to ~100% line coverage. The suite also documents two quirks
left unchanged:detectReasoningDepthscores above 0 on marker-free input via its
always-applied message-depth bonus, anddetectErrorContextreturns non-integer scores because
it never rounds (#9063) - fix(types): preserved the Veo polling delay promise result as… #9104
- fix(types): imported compression analytics statistics from… #9105
- chore(types): align semantic cache signature inputs with the… #9117
- fix(types): narrowed non-streaming chat response metadata… #9118
- docs(readme): replace Roo Code branding with… #9229 — thanks @taltas
- chore(ci): stopped dependabot from grouping
ioredismajors… #9425 - chore(tests): cleared two… #9064 #9488 — thanks @typescript-eslint
- test(cli): OpenCode plugin suite realigned to the… #9178 #9175 #9614
- Removed the unused
RadarReferralstype export left by the… #9697 #9738 - Reconcile the final v3.8.50 bundle-size and file-size ratchets… #9839
- chore(quality): expand all file-size baselines by +30% ahead of… #9950
- fix(quality): tighten eslintWarnings baseline 5000->0 to match the…
- [v3.8.50] feat: add RTL layout compatibility CSS (fixes #7680) #7987 — thanks @Dingding-leo
- [v3.8.50] feat(devin-desktop): replace public Windsurf provider #8228 — thanks @backryun
- [v3.8.50] feat(ci): extend i18n glossary-consistency gate to ko #8244 — thanks @MichaelYcJo
- [v3.8.50] test(tail): realign 3 stale base-red guards + note 2 env-only false positives (slice 6) #8263
- [v3.8.50] feat(ui): add global model search to Combo builder #8285 — thanks @corefusiion
- [v3.8.50] feat: extract CloakBrowser/browser-pool into optional plugin package #8299 — thanks @oyi77
- [v3.8.50] fix(i18n): clean up and naturalize Spanish translations #8339 — thanks @Dragost
- [v3.8.50] fix(compression): bound session-dedup suffix-block scan to prevent OOM #8438 — thanks @adrianojiu
- [v3.8.50] Fix Z.ai web browser transport and model capabilities #8451 — thanks @backryun
- [v3.8.50] feat(services): add Dario as a 5th embedded service (Claude Code toggle/failover) #8523 — thanks @seanford
- [v3.8.50] fix(adobe-firefly): live x-arp-session-id / Arkose wire (stop HTTP 408) #8571 — thanks @artickc
- [v3.8.50] fix(adobe-firefly): durable session, Chrome recovery, browser sign-in #8578 — thanks @artickc
- [v3.8.50] fix: passthrough non-standard cache token fields for DeepSeek / MiniMax / Bedrock across streaming, non-streaming, and Dashboard paths #8591 — thanks @ikelvingo
- [v3.8.50] fix: treat zero-reset Antigravity 429s as transient #8626 — thanks @costaeder
- [v3.8.50] fix: enforce OpenAI model lifecycle without silent reroutes #8627 — thanks @backryun
- [v3.8.50] fix(claude): preserve signed thinking turns during obfuscation #8629 — thanks @costaeder
- [v3.8.50] fix(antigravity): lock full quota per exact model #8630 — thanks @costaeder
- [v3.8.50] fix(errorConfig): add status 499 metadata mapping (fixes #8535) #8640 — thanks @Dingding-leo
- [v3.8.50] fix(auth): accept x-api-key without anthropic-version for claude-code user-agent (fixes #8655) #8678 — thanks @Dingding-leo
- [v3.8.50] fix(open-sse): add 'has been exhausted' to CREDITS_EXHAUSTED_SIGNALS (fixes #8631) #8704 — thanks @Dingding-leo
- [v3.8.50] fix(github): honor per-model targetFormat override for Copilot custom models #8713 — thanks @Witroch4
- [v3.8.50] fix(test): revive orphaned vitest tests and fix CI routing #8718 — thanks @MohitRawat017
- [v3.8.50] feat(providers): add support for TinyCMS Web #8736 — thanks @jhordanjw123
- [v3.8.50] feat(memory): MemoryBackend provider pattern with generic HTTP connector #8752 — thanks @oyi77
- [v3.8.50] refactor(db): add combo repository boundary #8757 — thanks @xiaoyaner0201
- [v3.8.50] fix(test): revive orphaned open-sse vitest tests #8772 — thanks @MohitRawat017
- [v3.8.50] fix(open-sse): filter non-numeric values in comboTargetLimits before min calculation #8774 — thanks @Dingding-leo
- [v3.8.50] feat(compression): add Italian (it) Caveman rule pack #8776 — thanks @Anjielon
- [v3.8.50] feat(combo): add maxContextWindow to contextRequirements (fixes #8777) #8790 — thanks @Dingding-leo
- [v3.8.50] feat(images): add POST /v1/images/upscale (Adobe Firefly Topaz + Stability + Topaz Labs) #8791 — thanks @artickc
- [v3.8.50] fix(providers): drop dead Cloudflare Workers AI free catalog IDs (#8717) #8804 — thanks @DinonowDev
- [v3.8.50] fix(sse): stop fabricating encrypted Codex reasoning summary text #8807 — thanks @Prudhvivuda
- [v3.8.50] fix(backend): update Cloudflare Workers AI model catalog & remove dead model IDs (#8717) #8808 — thanks @Dingding-leo
- [TS7] [v3.8.50] refactor(db): preserve normalized combo model types #8809 — thanks @backryun
- [v3.8.50] fix(db/apiKeys): respect provider parameter in group model permission checks (fixes #8803) #8817 — thanks @Dingding-leo
- [TS7] [v3.8.50] fix(types): preserve browser abort handling #8818 — thanks @backryun
- [v3.8.50] feat(cli): deliver the Antigravity credential straight to the remote install #8834
- [v3.8.50] docs: slim AGENTS.md #8839 — thanks @MumuTW
- docs(guides): add Antigravity (Google One AI) onboarding guide #8904 — thanks @HouMinXi
- [v3.8.50] fix(usage): reject impossible provider token counts #8927 — thanks @artickc
- Treat context metadata as a routing hint #8944 — thanks @JxnLexn
- docs(db): specify MySQL conformance semantics #8947 — thanks @rushsinging
- Add native ChatGPT Web provider for Codex clients #8949 — thanks @JxnLexn
- i18n(ru): complete Russian locale — 100% coverage #9001 — thanks @Egorich-print
- docs(troubleshooting): document the chat_admission_busy 503 and how to tune heavyweight chat concurrency #9021 — thanks @xiaoyaner0201
- Security: Update Redis to fix critical vunerability #9065 — thanks @tuxmonteiro
- [TS7] fix(types): validate chat context estimation inputs #9084 — thanks @backryun
- [TS7] fix(types): narrow stream response output #9086 — thanks @backryun
- docs: clarify free-provider model refresh outcomes #9087 — thanks @AbdullahFageeh
- [TS7] [v3.8.50] fix(types): preserve SSE tool call function shape #9090 — thanks @backryun
- [TS7] fix(types): narrow CCR store rejections #9091 — thanks @backryun
- [TS7] fix(types): preserve array-buffer response bodies #9092 — thanks @backryun
- [TS7] fix(types): narrow media generation failures #9093 — thanks @backryun
- [TS7] fix(types): preserve thinking signature recovery failure #9114 — thanks @backryun
- [TS7] fix(types): preserve Responses transform options #9119 — thanks @backryun
- [TS7] fix(types): preserve validation failure narrowing #9120 — thanks @backryun
- [TS7] fix(types): preserve client usage format contract #9122 — thanks @backryun
- [TS7] fix(types): preserve request rule input contracts #9135 — thanks @backryun
- [TS7] fix(types): simplify Codex service tier narrowing #9136 — thanks @backryun
- [TS7] fix(types): tighten extracted chatCore contracts #9137 — thanks @backryun
- [TS7] fix(types): align web executor event and model contracts #9138 — thanks @backryun
- [TS7] fix(types): align web provider support contracts #9139 — thanks @backryun
- [TS7] fix(types): type media provider request payloads #9141 — thanks @backryun
- test(dashboard): drop stale next-intl mock breaking ProviderDetailPageClient smoke #9150 — thanks @maxmad64bis
- test(mcp): guard Node 24 bundled MCP startup #9162 — thanks @Gioxaa
- test(compression): lock in stacked RTK+Caveman savings on redundant tool_result content #9278 — thanks @Sam280903
- chore: bump better-sqlite3 and add provider DB query scripts #9325 — thanks @jowimila
- test(quota): wait for the hot-path consumption instead of sleeping #9365 — thanks @HouMinXi
- refactor(sse): move the thinking-budget helpers out of base.ts #9381 — thanks @HouMinXi
- test(sse): expect the trailing period in the no-credentials message #9392 — thanks @HouMinXi
- chore(db): raise sqlite cache_size/mmap_size defaults #9467 — thanks @Poid-ZA
- [TS7] fix(types): align stream failure callback contracts #9561 — thanks @backryun
- [TS7] fix(types): narrow chatCore local contracts #9562 — thanks @backryun
- [TS7] fix(types): validate Azure OpenAI base URLs #9563 — thanks @backryun
- [TS7] fix(types): preserve sanitized tool array contracts #9564 — thanks @backryun
- [TS7] fix(types): validate Vision Bridge combo names #9565 — thanks @backryun
- [TS7] fix(types): preserve streaming PII choice keys #9566 — thanks @backryun
- [TS7] test(types): use Vitest expectations in tier resolver #9742 — thanks @backryun
- [TS7] fix(translator): preserve video URL override contracts #9747 — thanks @backryun
- [TS7] fix(codex): preserve narrowed input arrays #9748 — thanks @backryun
- [TS7] fix(kiro): complete cache-only usage totals #9753 — thanks @backryun
- chore(repo): ignore Electron build output unpacked into repo root #9858 #9770 — thanks @Michael-Rocco-Goldmann
- test(integration): add general live-test tool for the real "default" combo + rootless wire capture #9862 #9744 — thanks @hartmark
- ci(test): route orphaned Vitest tests through blocking CI #9875 #9605 — thanks @MohitRawat017
- docs(proposals): Telegram Mini App integration feasibility analysis #9906 #9810 — thanks @benzntech
- chore: ignore docker-compose.override.yml #9919 — thanks @lucasalx
- [TS7] fix(types): stabilize skill token extraction #9920 — thanks @backryun
- docs: add quickstart code examples for Python, Node.js, PHP and cURL #9922 — thanks @Hariprajwal
- [TS7] fix(types): narrow combo model collections #9972 — thanks @backryun
- [TS7] fix(types): align Claude message contracts #9973 — thanks @backryun
- [TS7] fix(types): type Copilot WebSocket construction #9974 — thanks @backryun
- [TS7] chore(types): remove orphan combo manifest metrics #9975 — thanks @backryun
- [TS7] fix(types): normalize stream usage before cost calculation #9977 — thanks @backryun
- [TS7] fix(stream): collect synthesized Responses tool events #9978 — thanks @backryun
- [TS7] fix(types): complete Responses stream failure contract #9979 — thanks @backryun
- [TS7] fix(types): narrow chat dispatch contracts #9986 — thanks @backryun
- [TS7] fix(types): narrow chatCore local contracts #9987 — thanks @backryun
- [TS7] fix(types): preserve GHE Copilot executor configuration #9988 — thanks @backryun
- [TS7] fix(types): validate Fal video result URLs #9989 — thanks @backryun
- [TS7] fix(types): narrow Claude stream deltas #9990 — thanks @backryun
- provider(agnes):refresh model catalog #9998 — thanks @backryun
- docs: fix duplicated word in MCP server audit logging section #10000 — thanks @TengSivtean
- docs: fix stale tool count (105 -> 104) in MCP server docs #10002 — thanks @TengSivtean
- Document default behavior for ToS-flagged free-tier providers #10013 — thanks @yulinlina
- [TS7] fix(tinycms): align executor and signer contracts #10087 — thanks @backryun
- [TS7] fix(types): restore provider breaker predicate import #10088 — thanks @backryun
- [TS7] ci: block new TypeScript 7 diagnostics #10134 — thanks @backryun
- chore(repo): remove tracked local artifacts #10178 — thanks @backryun
- maintenance — direct pushes (rollup): release-gate and base-red…
- deps (rollup): #9081 #9082 #9427 #9458 #9459 #9461 #9462 #9472
- docs/chore (rollup): #8954 #8991 #9059 #9194 #9258 #9508
- main-branch plumbing (rollup): #7168 #7179 #7216 #7220 #7225 #8941 #7559 #6634 #7341 #7347 #8067 #8070 #8317 #7076
- deps: bump the development group across 1… #10043 — thanks @app
- deps: bump electron from 43.2.0 to 43.3.0 in… #10042 — thanks @app
- maint(release): 45 direct pushes to the release branch with no PR…
- maint(repo): #10187 #10189 #10190 #10193 #10196 #10203 #10204 #10205 #10207 #10210 #10236 #10318
- docs(auth): distinguish dashboard sessions,
oma_live_…… #7786 - docs(ops): document Kubernetes probe… #10297 — thanks @RaviTharuma
- docs(docker): spell out that
:latesttracks the highest… #10317 - docs(backend): document that memory extraction, skills… #10349
- docs(docker): document default SQLite as single-replica /… #10350
- docs(backend): document that pre-write SQLite backups… #10351
- fix(tests): drain three base-reds on the release branch… #10539 #10704
- chore(security): remove the unused
enforceSecretsduplicate of… #10775 - fix(quality): register GrokBuildToolCard.tsx… #10778
- docs: Custom combos are only invoked… #10779 — thanks @maxmad64bis
- chore(startup): remove
src/server-init.ts(183 lines, never… #10780 - fix(quality): rebaseline file-size for 's own… #10859
- docs(openapi): document the
GETandPUToperations on… #10875 - fix(quality): bump EXPECTED_FEATURE_FLAG_COUNT to 52 for 's own… #10889
- test(db): replace three empty
test.skipplaceholders in the… #10906 - docs(docker): document runtime RAM for coding-agent… #10982
- docs(database): align the SQLite cache guide with the 65,536… #11018
- docs(docker): document N independent… #11024 — thanks @RaviTharuma
- fix(quality): rebaseline file-size for modelCapabilities.ts… #11034
- fix(quality): register… #11053
- chore(quality): drain two
release/v3.8.50base-reds — refresh… #11160 - chore(lint): ratchet… #7879 #11247 — thanks @typescript-eslint
- fix(deps): prevent pnpm from auto-installing the… — thanks @lobehub
@lobehub/icons, keeping six unneeded packages with incompatible or unverifiable license
metadata out of production installs (#11342). - ci(changelog): replace the broad removal bypass with an exact,… #11345
- docs(readme): reconcile live v3.8.50 provider, free-tier, CLI,…
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). - docs(openapi): document the conditionally… #11363
- fix(video-bridge): make opt-in segment-aware sampling use one… #11381
- docs: add an embeddings client runbook with… — thanks @RaviTharuma
- chore(ci): ignore ad-hoc
BOT_TOKEN/BOT_URLin env-doc-sync… - test(kimi): the Kimi background health sweep no longer draws… #11361
- chore(test): regenerate the provider/translate-path golden… #10531
- chore(release): resync the v3.8.50 provider and CLI catalogs,…
- fix(ci): route…
download-failure message throughsanitizeErrorMessage()instead of embedding a raw
err.message, clearing thecheck:error-helperbase-red onrelease/v3.8.50(#9985). - fix(ci): drain three more base-reds on
release/v3.8.50.… #9985
219 errors locally (vs. 25 in the last CI run) — all fromreact-hooks/set-state-in-effect,
react-hooks/preserve-manual-memoization,react-hooks/immutability,
react-hooks/static-components,react-hooks/refsandreact-hooks/purity, six React
Compiler lint rules thateslint-plugin-react-hooksv7 turns on by default and that were
never frozen inconfig/quality/eslint-suppressions.jsonafter the dependency bump. Froze
the pre-existing violations for those six rules via ESLint's native
--suppress-rule/--suppressions-locationmechanism (the same pattern already used for
@next/next/no-location-assign-relative-destination) — no application code changed, no rule
disabled, only genuinely-new violations stay blocking.check:dead-codewas at 418 against a
415 baseline: removed the unusedsrc/lib/quota/providerCapabilities.tsfile and the unused
ProviderQuotaMonitorinterface inproviderQuotaTelemetry.ts(both dead since PR #10148,
2026-08-18, confirmed viagrep/knip cross-reference), landing at 416; the residual +1 could
not be attributed to a single recent commit after checking every dead-list entry touched
since the 2026-08-14 baseline measurement, so it is rebaselined with the investigation
recorded inquality-baseline.json.tests/unit/autoCombo/tieredRotation.test.ts's
"rotates across all 43 Cerebras connection IDs" case was hitting vitest's 5000ms default
timeout on a 200-iteration synchronousselectProvider()loop under shared-devbox
contention (load average 40-60+ observed) — widened its explicit timeout to 20000ms; the
assertion itself is unchanged. - fix(tests): drain two base-reds on the release branch —… #10491
- fix(tests): realign the two
stream-utilspassthrough… #10017 #10473 - fix(tests): drain several base-reds on
release/v3.8.50that… #9985
of the same pattern — a legitimate product change landed without updating the test that
asserted the old behavior:tests/unit/glm-provider-model-import-route.test.ts(12 tests)
andtests/unit/model-sync-route.test.ts(2 tests) predate #10603's "upstream model sync is
opt-in and manual overrides are preserved" change;tests/unit/antigravity-model-aliases.test.ts
predated #10537 retiring the collapsedgemini-3.7-flashalias in favor of its three tiered
ids. Also fixes a real data drift inopen-sse/config/freeModelCatalog.data.ts(theqwen-web
free-catalog entry still pointed at the retiredqwen3.8-max-previewid instead of the
currentqwen3.8-max), corrects the zh-TWproviders.autoFetchModelsTooltipstring to the
glossary-canonical 快取 instead of 緩存, and removes an unused default export from
src/lib/oauth/providers/zed-hosted.ts(the named export already covers every consumer) to
shave one symbol off thecheck:dead-coderatchet regression. - chore(release): synchronize migration-count documentation and…
- fix(i18n): translate the 14
providers.harImport*keys into… #11069
- fix(release): #11038 #10534 #10964 #11259 #11338 — thanks @backryun @adevwithpurpose @hartmark @pacocartones
- fix(quality): #11438 #11321 — thanks @pacocartones @hartmark @adevwithpurpose @wgordon17 @backryun
- fix(tests): #11306 #10726 #11161 #11341 #11327 #11240 #11380 #10683 #10682 #10689 #11135 #10700 #10451 #11040 #11147 — thanks @MichaelYcJo @rqzbeh @hartmark @wgordon17
- fix(ci): Windows… #10453 #11301 #10828 #10325 — thanks @backryun
- perf(electron): #10390 #10367 #10382 #10359 #10327 #10328 #10324 — thanks @backryun
- perf(providers): provider schema validation is lazy and… #11334 #11220
- refactor(sse): #10633 #10910 #8367 #10201 #11051 — thanks @oyi77 @xz-dev
- refactor(dashboard): custom provider quota keys are… #11188 #11156
- chore(security): the… #10952 #10411 — thanks @arminanton @blarovse
- chore(deps): #10931 #10932 #10625 #10626 #10403 #10622 #10405 #10406 #10407 #10928 #10929 #10930
- fix(deps): #10610 #10543 — thanks @atjsh @tensorflow @jonlwheat2-gif @huggingface @dcox79
- docs: #10648 #10512 #10569 #10112 #10649 #10816 #10817 #10823 #10824 #10825 #10983 #11031 #11299 #11204 #11157 #10433
- docs(i18n): #11254 #11237 #11246 #10777 #10490 #10731 — thanks @farshidrezaei @realize000 @pandaaaa1990
- chore(repo): repository hygiene — the self-referential
_tasks… - chore(release): localized
llm.txtmirrors and the v3.8.50 base… - fix(ci): the pack-artifact provenance gate now checks… #8875 #10427
- test(dashboard): the proxy-registry e2e smoke flow opens… #8875 #9870
- test(api): the
/v1/modelse2e check accepts the auth… #9320 #8875 - chore(quality): refreshed the combos-page ESLint… #8875 #8285
🙌 Contributors
Thanks to everyone whose work landed in v3.8.50:
| Contributor | PRs / Issues |
|---|---|
| @AbdullahFageeh | #9087 |
| @abhijeetnardele24-hash | #10498 |
| @Abhishek4512009 | #10494 |
| @acc0mplish | #10732, #10948 |
| @adevwithpurpose | #9790, #10118, #10222, #10800, #10834, #10835, #10836, #10882 |
| @adrianojiu | #8438 |
| @agisota | #9837 |
| @AgnesRiber | #9718, #9976 |
| @ahmet-cetinkaya | #8878 |
| @AIB1TAL0S | #9284 |
| @AlanSyue | direct commit / report |
| @alex-jordan547 | #9235, #9245, #9813 |
| @aliyosufi | #11159 |
| @amartinawi | #10090, #10091, #10092, #10097, #10101 |
| @An0nym0us92 | #11394 |
| @AndrianBalanescu | #8888, #8889, #8890, #8891, #8892, #8893, #8894, #8895, #11081, #11082 |
| @AnhLead | #9722 |
| @aniketshukla1 | #9148 |
| @Anjielon | #8776 |
| @anudeepadi | #10288 |
| @apoapostolov | #8916 |
| @app | #10042, #10043 |
| @arafatkatze | #10706 |
| @ARC345 | #9628, #10050, #10051 |
| @arminanton | #10933, #10952, #11166 |
| @aron-intframe | #10459 |
| @artickc | #8571, #8578, #8791, #8843, #8870, #8927, #8974, #9097, #9255, #9549 |
| @Arul- | #9761 |
| @asorourx | #11036 |
| @AStupidBear | #10180, #11385 |
| @azzaouiomar19-sketch | #10394 |
| @b1nhm1nh | direct commit / report |
| @backryun | #8228, #8451, #8627, #8809, #8818, #9084, #9086, #9090, #9091, #9092, #9093, #9114, #9119, #9120, #9122, #9135, #9136, #9137, #9138, #9139, #9141, #9561, #9562, #9563, #9564, #9565, #9566, #9742, #9747, #9748, #9751, #9753, #9755, #9791, #9792, #9793, #9795, #9796, #9797, #9798, #9920, #9972, #9973, #9974, #9975, #9977, #9978, #9979, #9984, #9986, #9987, #9988, #9989, #9990, #9998, #10087, #10088, #10134, #10175, #10178, #10195, #10254, #10255, #10256, #10257, #10258, #10324, #10339, #10453, #10464, #10483, #10637, #10964 |
| @Benson-mk | #8369 |
| @benzntech | #9810, #9812, #9939, #10124, #10126, #10458 |
| @Bl0ck154 | #9231 |
| @blackwell-systems | #10807 |
| @blarovse | #10411 |
| @bortolidiego | #10058 |
| @branben | #9940, #10575 |
| @Chewji9875 | #9257, #9420, #9821, #9994, #10116, #10160, #10305, #10376 |
| @chirag127 | #6674, #10071 |
| @chloeassistant | #9675, #9746 |
| @configurowebmax | #8877 |
| @corefusiion | #8285 |
| @costaeder | #8626, #8629, #8630 |
| @cryptiklemur | #10684, #10685 |
| @csoftware-arigpt | #3440 |
| @DaDecky | direct commit / report |
| @danscMax | #8634 |
| @DarkEsteves | #10250 |
| @dcox79 | #10543 |
| @ddarkr | #9035, #9036, #10177 |
| @Dingding-leo | #7987, #8640, #8678, #8704, #8774, #8790, #8808, #8817 |
| @DinonowDev | #8804 |
| @dionjoshualobo | #9721 |
| @dpozimski | #10253 |
| @Dragost | #8339 |
| @dsitmilis | direct commit / report |
| @echoriver89 | #10717 |
| @Egorich-print | #9001, #9020, #9058, #10881, #10887, #11097 |
| @ekinnee | #11307 |
| @electrumguy | #10774, #11149 |
| @engmarcosjr | #9993 |
| @epsilonode | #8871 |
| @ervareza | direct commit / report |
| @excessivechaos | #10062, #10138, #10552, #10883, #10884, #10907, #11179 |
| @fajarhide | #9191, #9198 |
| @farshidrezaei | #10777 |
| @fenix007 | #9618, #10016 |
| @freudantunes | #10623 |
| @Gecky2102 | #9280 |
| @geek007git | #10441 |
| @ggdayup | #10199, #11113, #11114, #11375 |
| @Gi99lin | #10342 |
| @giauphan | #10392 |
| @Gioxaa | #9162, #9171 |
| @HaoNgo232 | direct commit / report |
| @Hariprajwal | #9922 |
| @harkaranbrar7 | #10281 |
| @hartmark | #9635, #9704, #9711, #9712, #9727, #9734, #9735, #9738, #9741, #9744, #9745, #9822, #10025, #10034, #10037, #10038, #10041, #10121, #10217, #10262, #10263, #10330, #10331, #11259 |
| @Hdiaktoros | #8930 |
| @HectorBernstorff | direct commit / report |
| @HellFiveOsborn | #9248 |
| @herjarsa | #9714, #9816, #9937, #9946, #10128, #10456 |
| @hgaib | #10722 |
| @horacecar | #7679 |
| @HouMinXi | #8886, #8904, #8905, #8976, #8984, #9079, #9106, #9207, #9242, #9328, #9340, #9342, #9351, #9365, #9380, #9381, #9392, #9449, #9482, #9483, #9509, #9510, #9572, #9631, #9634, #9695, #9929, #10457, #10475, #10525, #10529, #10573, #10663, #10846, #11084, #11139, #11140, #11141, #11386, #11418 |
| @hppsc1215 | #8970 |
| @Hsia97 | #10810 |
| @hydraxman | #10137, #10572 |
| @Iammilansoni | #9353, #9397 |
| @ignamiranda | #11206 |
| @ikelvingo | #8591, #8872, #9053 |
| @infinit-X | #9095 |
| @InkshadeWoods | #10733 |
| @isaaclb98 | #9730 |
| @jackjinke | #9556, #9601, #10005, #10045, #10248, #10533, #10540, #10574, #11041 |
| @jacobsparts | #11399, #11400, #11402 |
| @jax-novita | #8913 |
| @jeff-alves | #10221 |
| @jeyhunfaslanov | #10259 |
| @jhordanjw123 | #8736 |
| @jktan0504 | #9025 |
| @joachimBrindeau | #9200 |
| @jonlwheat2-gif | #10610, #10709, #11194 |
| @JoshimOfficial | #9011 |
| @jowimila | #9325 |
| @JxnLexn | #8933, #8940, #8944, #8949, #10422, #10608 |
| @Kaedo17 | #8922 |
| @KaspaPulse | #10362 |
| @khoazero123 | #9272 |
| @killmonger2317-coder | #10445 |
| @KittisakT | #9423 |
| @Kizuno18 | #10803 |
| @KooshaPari | #7329 |
| @kriptoburak | #10854 |
| @krishna3554 | #10620, #10855 |
| @lamchun1110 | #10372, #10397 |
| @larin-vas | #9828 |
| @lazysaltyfish | direct commit / report |
| @LeonG606 | #9457 |
| @linhdmn | #10980, #11085, #11214, #11274 |
| @Llliao1113 | #8921 |
| @lucasalx | #9919 |
| @lucasmellos | #8925 |
| @lukiod | #8828 |
| @luoyide | direct commit / report |
| @maci0 | #11279 |
| @mad-gooze | #9052 |
| @maisdesign | #8858 |
| @marcelokarval | #11397 |
| @marchlhw | #9050 |
| @marcs7 | #11180 |
| @matiasbaglieri | #9707 |
| @maxmad64bis | #9150, #9246, #9291, #9414, #10278, #10402, #10652, #10662, #10664, #10694, #10769, #10770, #10779, #10876, #10885, #10974, #10978, #11008, #11009, #11047, #11116, #11129, #11130, #11133, #11151, #11162, #11252 |
| @McLuck | #8914 |
| @megamen32 | #10184 |
| @MeRezaRezaei | #10174, #10614, #10944, #11042, #11045 |
| @Michael-Rocco-Goldmann | #9770, #9773, #9777, #9787 |
| @MichaelYcJo | #8244, #10725, #10726 |
| @Minamaged18 | #11269 |
| @minhlongs | #10805 |
| @minhnhat166 | direct commit / report |
| @MohitRawat017 | #8718, #8772, #9605 |
| @Momen4444 | #9612 |
| @MrShitFox | #9826 |
| @mtb-ninja | #10114 |
| @MumuTW | #8839 |
| @mvanhorn | #9542 |
| @mymusicmyspace | #10965 |
| @Mynacol | #9733 |
| @NahuSaruf | #10545 |
| @Neuron-Mr-White | #10228, #10230, #10957, #11378 |
| @nguyenha935 | #9044, #9215 |
| @nordz0r | #10170 |
| @nosolosoft | #8900 |
| @ntdat812 | #10843, #10853, #10857, #10858, #10860, #10862, #10868, #10935, #11004, #11374, #11376, #11377 |
| @ntdatt812 | #10715, #11076 |
| @octo-patch | #10650 |
| @ofonseca-pyming | #11297 |
| @oyi77 | #8299, #8752, #9158, #9818, #10910, #10942 |
| @pacocartones | #10216, #10673, #11059, #11193, #11199, #11238, #11241, #11321, #11322, #11332, #11338 |
| @pandaaaa1990 | #10731 |
| @phatchau036 | #10517 |
| @phuongddx | #11415 |
| @PixmaNts | #9432 |
| @pizzav-xyz | #9077 |
| @Poid-ZA | #9467 |
| @Prudhvivuda | #8807, #9014, #9015, #9016 |
| @pucedoteth | #10607 |
| @qianze0628 | #9038 |
| @rafacpti23 | #11207, #11213 |
| @raflyazf | direct commit / report |
| @Rahulsharma0810 | #8961, #10872 |
| @RaviTharuma | #10055, #10297, #10307, #10344, #10352, #10488, #10565, #10566, #10568, #10584, #10771, #10814, #10818, #10820, #10821, #10822, #10827, #10847, #10971, #10979, #10981, #11014, #11015, #11016, #11017, #11020, #11024, #11314, #11318, #11320 |
| @realize000 | #10490 |
| @redzrush101 | #10492 |
| @rengaryang | #11333 |
| @rinseaid | #8945, #9037, #9932, #9933, #9969, #9982, #10554 |
| @ritheshcn25 | #10026 |
| @rixzkiye | direct commit / report |
| @rizxfrog | #10356, #10546 |
| @RobertsXML | direct commit / report |
| @royanrosyad85 | direct commit / report |
| @rqzbeh | #10415, #10420, #10424, #10430, #10465, #10470, #10890, #10894, #10898, #10899, #10901, #11039, #11054, #11055, #11056, #11078, #11117, #11123, #11125, #11132, #11155, #11161, #11177, #11182, #11189, #11262 |
| @rushsinging | #8947 |
| @ryan-brosas | #9693 |
| @ryanngit | direct commit / report |
| @sadSanta-07 | #9938, #10209, #10605, #10772 |
| @SalyyS1 | direct commit / report |
| @Sam280903 | #9274, #9278, #9281, #9283, #9448 |
| @sanforex24h | #11026 |
| @SCys | #11107 |
| @seakleangnhak | direct commit / report |
| @seanford | #8523 |
| @SemonCat | direct commit / report |
| @sha367 | #10471 |
| @shixi-li | #9022, #9513, #10001 |
| @Siva010 | #10658 |
| @SnCr90 | #10534 |
| @soulhakr | #8799 |
| @sprintberlin | #11353, #11355, #11360 |
| @stanleytejakusuma | #9610, #10636, #10660, #10730, #11337 |
| @Stazyu | #9007, #9226, #9438 |
| @SupremeNexas | #9913 |
| @swingtempo | #9307, #10354 |
| @szzhoujiarui | #9218 |
| @tald26 | #9959 |
| @taltas | direct commit / report |
| @TechNickAI | #9251, #10558 |
| @TengSivtean | #10000, #10002, #10086 |
| @TheDemonTuan | #11364 |
| @TheFrenchGhosty | #9326 |
| @tiangao88 | #10046, #10363 |
| @tientien17 | #10798 |
| @tito13kfm | #10227 |
| @tkgo11 | #10370, #10371 |
| @tuandinh0801 | #10804, #10830, #11230 |
| @Tushar49 | #10186 |
| @tuxmonteiro | #9065 |
| @vinogradovnet | #9581 |
| @VXNCXNX | #9111, #9783 |
| @wgordon17 | #8909, #9233, #9441, #9619 |
| @Witroch4 | #8713 |
| @witt3rd | #9962, #9963 |
| @wpec | #10839 |
| @XDayonline | #10053 |
| @xiaoyaner0201 | #8757, #8869, #8876, #8883, #8906, #8931, #9021, #9027, #9042, #9316, #9452, #10468 |
| @xyzs996 | #11210 |
| @xz-dev | #8367, #8908, #9199, #9205, #9262, #9290, #9313, #9555, #9569, #9629, #9788, #9983, #10066, #10072, #10079, #10162, #10243, #10247, #10437, #10712, #10716, #10723, #10806, #10953 |
| @yansigit | #9834, #9909, #9911, #9917, #9921, #10065 |
| @yidecode | direct commit / report |
| @yourspraveen | #11075, #11088, #11165, #11271 |
| @yulinlina | #10013 |
| @YunyunZhai | #10946, #10960 |
| @yutuknown | #8999 |
| @zabrodschiipavel-sketch | #9312 |
| @zannen7 | #10077 |
| @Zartharas | #9161, #9164, #9181, #9182, #9184, #9185, #9186, #9189, #9294, #9825, #9833, #9936, #9965, #9992, #10202, #10218, #10272, #10329, #10518, #10519, #10521, #10799, #10873, #10878 |
| @Zenlyte | #9005 |
| @zhiru | #9099, #9101 |
| @ziuus | #8912, #11372 |
| @zmf963 | direct commit / report |
| @zoser69 | #10874 |
| @zuckdorsey | #9723 |
| @diegosouzapw | maintainer |
📖 Full changelog with complete descriptions: CHANGELOG.md
详细ChangeLogradar-export-latest
2026年08月20日
Export estável do catálogo OmniRoute para o Radar. Atualizado automaticamente; NÃO é um release de versão do produto.
详细ChangeLogv3.8.49
2026年07月30日
All 1383 entries from this cycle are listed below, one line each — descriptions are
trimmed to fit GitHub's 125,000-character release body. Full wording, context and links:
CHANGELOG.md.
Living section — regenerated 2026-07-19 from all 306 cycle commits (bump 2c62333 → tip). Bullets carry the merged PR and its author; direct pushes listed separately. Finalized at the v3.8.49 release.
✨ New Features
-
feat: generalize ensureThinkingBudget to all providers +… (#6979) — @rafaumeu
-
feat(6922): effort-tier aliases for glm-5.2 & mimo-v2.5 on… (#6987) — @rafaumeu
-
feat(providers): curated OpenRouter embeddings catalog + specialty merge… (#6994)
-
feat(quota): opt-in auto-ping to keep Codex quota windows warm (#6995)
-
feat(providers): add Agnes AI native provider support (#7035) — @HouMinXi
-
feat(sse): allow disabling
:comment heartbeats via… (#7036) — @xier2012 -
feat(perf): add performance.mark/measure to SSE pipeline +… (#7045) — @oyi77
-
feat(providers): add Dahl free inference provider (#7062) — @growab
-
feat(ci): boot-smoke the packed npm tarball (check:pack-boot,… (#7086)
-
feat(ci): hotfix fast-lane + tests-only E2E skip (WS3.1) (#7088)
-
feat(ci): continuous release-green — on-push quick gate + 3x/day… (#7089)
-
feat(ci): duration-balanced E2E shards via LPT bin-packing (WS4.1) (#7090)
-
feat(ci): TypeScript 7 native shadow for typecheck:core (WS4.2,… (#7091)
-
feat(release): npm staged publishing + pre-publish boot-smoke (WS1.3) (#7092)
-
feat(release): post-publish verifier — clean-container install + boot… (#7109)
-
feat(ci): Mergify merge queue + manual-train fallback runbook… (#7112)
-
feat(ci): Windows leg for Electron prepare smoke (WS1.5) (#7113)
-
feat(ci): Codecov patch coverage (informational) + fix missing… (#7114)
-
feat(sidecar): support conditional provider manifest refresh (#7130) — @KooshaPari
-
feat(homolog): real-environment E2E homologation suite (npm run… (#7133)
-
feat(usage): add Codex reset credit picker (#7154) — @JxnLexn
-
feat(ci): Trunk Flaky Tests uploads for vitest + Playwright E2E… (#7175)
-
feat(ci): Trunk Flaky Tests upload on the fast-path vitest job… (#7205)
-
feat(kiro): register GPT-5.6 Sol/Terra/Luna model family (#7209)
-
feat(dashboard): show Codex plan label in provider and quota views (#7210)
-
feat(dashboard): add reorder connections by availability button (#7211)
-
feat(dashboard): add 180D and 365D usage/cost analytics periods (#7213)
-
feat(api): add Vary: Accept-Encoding to token-authenticated /v1*… (#7217)
-
feat(api): expose GET /api/usage/model-latency-stats (#7218)
-
feat(dashboard): add compression-mode selector to Context & Cache combos… (#7219)
-
feat(sse): route GitHub Copilot Claude models through native… (#7223)
-
feat(mitm): add Antigravity reasoning-effort overrides (#7228)
-
feat: replace free-text model inputs with hidePaid-aware… (#7229)
-
feat: editable ComfyUI base-URL field + per-connection… (#7232)
-
feat(sse): add optional-enum null-omission idiom for codex… (#7233)
-
feat(sse): preserve tools/tool_choice for tool-bearing requests… (#7235)
-
feat(api): accept x-goog-api-key header for client-facing auth (#7236)
-
feat(sse): add native xAI Grok Imagine video generation provider (#7238)
-
feat: add Type filter and easiest-first sort to Free Provider… (#7240)
-
feat(cli): add Grok Build CLI tool setup (~/.grok/config.toml) (#7241)
-
feat(provider): add Chenzk API OpenAI-compatible gateway (#7246)
-
feat(providers): let custom connections opt into prompt-cache capability (#7257)
-
feat(db): include xp_audit_log in automatic retention/prune (#7260)
-
feat(api): structured X-Routing-Fallback-Reason header for relay… (#7262)
-
feat(compression): support RTK TOML schema v1 filters (#7281) — @JxnLexn
-
feat: add principal-scoped CCR MCP lifecycle (#7282) — @JxnLexn
-
feat(issue-agent): surface RecordedTriageTimeoutError as 504 (#7315) — @KooshaPari
-
feat(incident-response): structured incident response templates (#7334) — @KooshaPari
-
feat(providers): add xAI OAuth PKCE provider (#7399) — @fenix007
-
feat(models): advertise Claude reasoning-effort variants in /v1/models (#7497) — @thepigdestroyer
-
feat(kimi): sync Code, Web, and Moonshot providers (#7531) — @backryun
-
feat(resilience): guard OmniRoute peer routing loops (#7555) — @isiahw1
-
feat: add Mixedbread AI as embeddings provider (#7595)
-
feat(providers): add Rev AI speech-to-text provider (#7596)
-
feat: add Freepik (Magnific Mystic) image generation provider (#7597)
-
feat(sse): add DeepInfra as a video-generation provider (#7598)
-
feat(providers): add Felo chat-aggregator provider (#7599)
-
feat(sse): add Notion AI Web (Unofficial/Experimental) provider (#7600)
-
feat: add FreeTheAi as OpenAI-compatible gateway provider (#7602)
-
feat: add Gladia as an async speech-to-text provider (#7603)
-
feat: add EdgeTTS audio-tts provider (#7605)
-
feat(video): add Novita AI as video-generation provider (#7606)
-
feat: add Segmind image+video provider (#7608)
-
feat: add Microsoft Designer as image provider (#7609)
-
feat: per-model default reasoning_effort + no-think none on… (#7631)
-
feat(sse): per-model upstream header-response timeout override (#7632)
-
feat(dashboard): in-product guidance for prompt compression engines (#7634)
-
feat(usage): add TTFT/E2E-latency/tokens-per-second to model latency… (#7635)
-
feat: import providers from CSV/JSON file (#7636)
-
feat: confirm before removing a single connection (#7640)
-
feat(sse): honor excluded models in no-auth auto-combo candidate… (#7646)
-
feat(providers): add g4f.space no-key gateway… (#7647)
-
feat: rate-limit queue admission control (maxQueueDepth + 15s… (#7649)
-
feat(sse): generalize session affinity TTL to all providers (#7650)
-
feat: OpenRouter quota tracking (key/credits + free-window… (#7651)
-
feat(sse): quota tracking for AgentRouter, v0 (Vercel), FreeModel… (#7653)
-
feat(providers): Speechmatics STT, gTTS, VibeProxy preset (#6659, #6667,… (#7655)
-
feat(api): route Google AI Studio Imagen through… (#7656) — @danscMax
-
feat(auth): OIDC as optional dashboard admin login gate (password… (#6973) — @mikolaj92
-
feat(api): add pagination params to 8 DB modules + recharts… (#7046) — @oyi77
-
feat(proxy): operator-level proxy subscriptions (Karing-style) —… (#7299) — @xier2012
-
feat(grok-cli): align with official Grok Build client (#7358) — @backryun
-
feat(providers): Complete GHE Copilot OAuth provider implementation (#7546) — @hppsc1215
-
feat(guardrails): add CredentialMaskerGuardrail for API key/secret… (#7683) — @Securiteru
-
feat(perplexity): refresh provider integrations (#7687) — @backryun
-
feat(providers): notion-web live model discovery via getAvailableModels (#7696) — @artickc
-
feat(providers): add proactive cf_clearance/User-Agent hint to grok-web… (#7713)
-
feat: add live gRPC-web quota fetcher for grok-cli (#7714)
-
feat(api): add opt-in auto-sync scheduler for free-proxy sources (#7716)
-
feat(dashboard): show proxy name in badge, sort saved-proxy picker,… (#7720)
-
feat(cli): add auth export command for decrypted provider… (#7724)
-
feat(oauth): accept full ChatGPT session JSON for Codex manual import (#7725)
-
feat(sse): add nvidia NIM local RPM budget + concurrency cap (#7726)
-
feat(gemini-web): emulate OpenAI tool calling via the webTools prompt shim (#7727)
-
feat(services): introduce pluggable service-provider contract, migrate… (#7730)
-
feat(mitm): root-CA + per-host leaf certs for AgentBridge static… (#7731)
-
feat(providers): add hailuo-web (MiniMax web) chat provider (#7734)
-
feat: browser login for Grok Build provider (#7735)
-
feat(routing): wire interceptFetch tool interception into the chat… (#7736)
-
feat(sse): add X-OmniRoute-Decision routing trace header (#7765)
-
feat(providers): zai-web live model discovery with local-catalog fallback (#7766)
-
feat(api): sync upstream reasoning.supported_efforts into… (#7767)
-
feat(dashboard): pin Kimi providers first in category + official… (#7775)
-
feat(chaos+ponytail): parallel chaos-mode dispatch + ponytail output … (#7781) — @Moseyuh333
-
feat(perf): IC2 — cache provider connections by ID + lazy-decrypt… (#7787) — @oyi77
-
feat(quality): gate the free-tier headline so it can never silently… (#7798)
-
feat(providers): expose an explicit tier override for any provider… (#7838)
-
feat(routing): read-only auto/* candidate transparency + per-API-key… (#7839)
-
feat(catalog): map unmapped free tiers, add navy + aihorde, surface… (#7840)
-
feat(providers): add OpenRouter speech-to-text (audio transcription)… (#7861) — @Tasogarre
-
feat(qwen): add Qwen3.8 Max Preview catalogs [Part 2/3] (#7874) — @backryun
-
feat(providers): add 5 free-tier providers (ainative, aion, sealion,… (#7887)
-
feat(vnc-session): persistent noVNC browser login for web-cookie providers (#7892) — @Capslockb
-
feat(sse): add PromptQL playground provider (unofficial) (#7911) — @artickc
-
feat(cline): align ClinePass catalog and request protocol (#7914) — @backryun
-
feat: narrow mcp:connect scope + per-key HTTP tool-scope… (#7967)
-
feat: provider tab account search + mirrored top pagination (#7968)
-
feat: canonical numeric helpers + tier-1 (analytics) migration (#7969)
-
feat(sse): add HyperAgent (hyperagent.com) unofficial web provider (#7994) — @artickc
-
feat: copilot-m365-web tone-selected model variants (#7997)
-
feat(media): Adobe Firefly image + video generation provider (#8006) — @artickc
-
feat(compression): select model-aware tokenizers (#8009) — @JxnLexn
-
feat(compression): add Responses tool-output engine (#8010) — @JxnLexn
-
feat(dashboard): Kimi sponsor banner, Kimi Coding preset, official… (#8039)
-
feat(dashboard): make Codex quota card windows reflect reality (#8054) — @insoln
-
feat(compression): teach the model the CCR retrieve protocol on first… (#8063)
-
feat(compression): per-model/endpoint compression exclusion filter (#8064)
-
feat(providers): add CLOVA Studio, InternLM and Ant Ling API-key… (#8077) — @alvaretto
-
feat(codex): support reference image edits (#8122) — @xiaoyaner0201
-
feat(providers): add weekly quota tracking for grok-web (#8127) — @apoapostolov
-
feat(providers): add Sarvam AI, Writer Palmyra and PLaMo API-key… (#8161) — @alvaretto
-
feat: native Fish Audio TTS provider on /v1/audio/speech (#8164)
-
feat: zh-CN terminology glossary + consistency gate +… (#8166)
-
feat(providers): add Typhoon (Thailand) and Inception Mercury diffusion… (#8170) — @alvaretto
-
feat(sse): restrict auto-combo no-auth pool to allowlist… (#8183)
-
feat(sre): add tcp-close-analyzer.py for debugging… (#8208) — @hartmark
-
feat(settings): configurable model catalog cache TTL (#8219) — @oyi77
-
feat(github-models): refresh catalog and compatibility (#8225) — @backryun
-
feat(github): refresh Copilot model catalog (#8226) — @backryun
-
feat: classify grok-web Cloudflare anti-bot blocks + gated… (#8241)
-
feat(providers): weekly quota for xAI OAuth (Grok) (
xai-oauth/xao)… (#8471) — @allanvb -
feat(sse): every completion response now carries an… — @chirag127
-
feat(ws): the live-dashboard WebSocket server now auto-starts… — @ianriizky
-
feat(sse): configurable per-model upstream… (#6354)
-
feat(dashboard): Replace free-text model inputs in the Routing (web… (#6540)
-
feat(compression): new omniglyph engine (context-as-image) — renders… (#6556 #6661)
-
feat(sse): rate-limit request queue admission control —… — @chirag127
-
feat(sandbox): the skill sandbox gained a container-provider… — @KooshaPari
-
feat(oauth): accept the full ChatGPT session JSON (not… (#6636)
-
feat(sse): add 5 no-key g4f.space gateway providers —… — @chirag127
-
feat(sse): add DeepInfra as a video-generation provider… (#6653)
-
feat(providers): add Freepik (Magnific Mystic) API-key… (#6654)
-
feat(providers): add Rev AI speech-to-text provider… (#6655)
-
feat(providers): add Segmind as an image + video generation provider… (#6656)
-
feat(providers): add Gladia as an async speech-to-text… (#6657)
-
feat(video): add Novita AI as a video-generation… (#6658)
-
feat(providers): add Speechmatics as an STT provider — async batch… (#6659)
-
feat(providers): add Mixedbread AI as an embeddings… (#6660)
-
feat(providers): add Felo (felo.ai) as a free, no-signup, no-API-key… (#6666)
-
feat(providers): add gTTS (Google Translate TTS) as a free, no-signup… (#6667)
-
feat(sse): add EdgeTTS (Microsoft Edge "Read Aloud") as a free,… (#6668)
-
feat(providers): add FreeTheAi as an OpenAI-compatible… (#6670)
-
feat(sse): add Microsoft Designer as an unofficial… (#6672)
-
feat(providers): add Hailuo Web (
hailuo-web) — a free,_token-based… (#6673) -
feat(cli): new
omniroute auth exportcommand dumps DECRYPTED… (#6683) -
feat(mitm): the AgentBridge static MITM server (
server.cjs) can… (#6684) -
fix(sse): skip
thinkingConfigfor Gemma models on the… — @chy1211 -
feat(xai): route xAI clients to Grok's native
/v1/responses… — @ryanngit -
feat(models): add a Settings → AI "Model Overrides" UI plus… — @xz-dev
-
feat(api): add
Vary: Accept-Encodingto token-authenticated… — @chirag127 -
feat(sse): add Notion AI Web (Unofficial/Experimental)… (#6758)
-
feat(dashboard): add per-routing-combo compression-mode override to the… (#6760)
-
feat(resilience): operator-configurable account rotation policy — a new… — @artickc
-
feat(sse): preserve
tools/tool_choicefor tool-bearing… — @chirag127 -
chore(cursor): add Grok 4.5 effort/fast model IDs: (#6774 ). — @andrewmunsell
-
feat(codex): Codex provider model discovery now fetches the live… — @JxnLexn
-
feat(cursor): register the Opus 4.8, Fable 5, and Sonnet 5 model… — @andrewmunsell
-
Changelog fragments (
changelog.d/): PRs now add their changelog entry as a new fragment… -
feat(proxy): add a latency-optimized proxy rotation strategy that… — @iamraydoan
-
feat(db): include
xp_audit_login the automatic… (#6801) -
feat(fusion): the fusion judge may now draw on its own knowledge and… — @chirag127
-
feat(dashboard): search box on the Playground's raw model
<select>—… (#6811) -
feat(usage): Antigravity/agy quota widget now surfaces the… (#4017 #6818)
-
feat(codex): Codex CLI compatibility shim — the Responses API… (#3697 #6820)
-
Z.ai Web (free web-session provider): new
zai-webweb-cookie provider drives the free… (#6823) -
feat(dashboard): import multiple, possibly different… (#6836)
-
feat(compression): update the vendored GCF codec behind the… (#6837)
-
feat(sse): OpenRouter quota tracking — a dedicated fetcher polls… (#6842)
-
feat(sse): live gRPC-web quota fetcher for Grok Build (
grok-cli)… (#6844) -
feat(sse): add dual-window quota tracking for the… (#6845)
-
feat(sse): add a static local RPM budget (default… (#6846)
-
feat(sse): add quota tracking for the
agentrouter… (#6850) -
feat(providers): Add GPT-5.6 support across OpenAI API, Codex, and… (#6862) — @backryun
-
chore(providers): Align emitted Claude Code identity headers, bridge… (#6862) — @backryun
-
feat(api): add a structured
X-Routing-Fallback-Reason… (#6872) -
feat(api): new GET /api/usage/model-latency-stats management… (#6873)
-
feat(providers): add a
vibeproxy-openaiprovider-node preset to `POST… -
feat(usage): add… (#6875)
-
feat(sse): per-model default
reasoning_effort… (#6879) -
feat(providers): let a custom/openai-compatible connection opt into… — @andrea-kingautomation
-
feat(dashboard): add a Type filter (No Signup / OAuth Login / API Key)… (#6915)
-
feat(providers): expose an editable base-URL field on the ComfyUI… (#6928)
-
feat(providers): refresh the curated OpenRouter embeddings catalog… (#6976)
-
feat(quota): Add opt-in auto-ping to keep Codex quota windows warm —… (#6977)
-
feat(oauth): Add a one-click browser (PKCE) login for Grok Build… (#7013)
-
feat(sse): Add optional-enum
null-omission idiom for… (#7023) -
feat(auth): accept the
x-goog-api-keyheader for client-facing… — @QRcode1337 -
feat(sse): add a local dual-window (5h + 7d,…
-
feat(api): opt-in scheduled auto-sync for free-proxy sources… — @chirag127
-
feat(kiro): register the GPT-5.6 Sol/Terra/Luna model family (272k… — @SemonCat
-
feat(dashboard): show the Codex subscription plan label in provider… — @CarmeloCampos
-
feat(dashboard): add a "Reorder" button to provider connections that… — @fzrilsh
-
feat(dashboard): add 180D and 365D periods to the Cost Explorer range… (#7213)
-
feat(sse): GitHub Copilot Claude models now route through… — @yidecode
-
feat(mitm): Antigravity MITM model mappings now support an optional… — @trfi
-
feat(sse): add native xAI Grok Imagine video generation provider —… — @anndev-69
-
feat(cli): add Grok Build CLI tool setup — writes a… — @rixzkiye
-
feat(provider): add Chenzk API OpenAI-compatible gateway. (thanks… — @CahyokPutraDev99
-
feat(dashboard): add a per-operator "hide this quota row" toggle to the… — @nguyenha935
-
feat(sse): session affinity (
X-Session-Id/x-codex-session-id… — @tenshiak -
feat(sse):
gemini-webnow honors the OpenAItoolsarray by… (#7286) -
feat(eval): added a router-eval harness (
npm run eval:router,… — @KooshaPari -
feat(services): introduce a
ServiceProviderPlugincontract… (#7333) -
feat(routing): wire
interceptFetchinto the chat… -
feat(dashboard): confirm before removing a single… (#7361)
-
feat(providers): Add a first-class xAI OAuth PKCE provider for… (#7399) — @fenix007
-
feat(dashboard): surface in-product guidance for the Settings → Prompt… (#7530)
-
feat(providers): Complete GHE Copilot OAuth provider implementation with… (#7546)
-
feat(providers): grok-web's add-connection dialog now… (#7567)
-
feat(providers): register
lmstudioin the embedding provider registry… (#7601) — @ekinnee -
feat(sse): honor a no-auth provider connection's… (#7622)
-
feat(dashboard): proxy-assignment UX quality-of-life fixes — the… — @tenshiak
-
feat(providers):
zai-web(chat.z.ai) now wires live… — @andrea-kingautomation -
feat(perplexity): Refresh Perplexity Web model mappings and Search API… (#7687) — @backryun
-
Generic OpenAI-compatible model sync now captures a… (#6879)
-
feat(dashboard): Kimi (Moonshot AI) official-partnership highlight on… (#7775)
-
feat(providers): let any provider connection — built-in… (#7818)
-
feat(routing): add a read-only `GET… (#7819)
-
feat(providers): copilot-m365-web tone-selected model… (#7872)
-
feat(shared): Add canonical numeric coercion helpers (
toNumber,… (#7879) -
feat(mcp): add a narrow
mcp:connectAPI-key scope for… (#7895) -
feat(dashboard): provider tab account search… (#7937)
-
feat(sse): classify grok-web Cloudflare anti-bot blocks… (#8019)
-
Teach the model the CCR retrieve protocol (marker →… (#8033)
-
Add a per-model/endpoint compression exclusion filter:… (#8034)
-
feat(i18n): zh-CN terminology glossary + consistency gate + 提供商→提供者… (#8038)
-
feat(dashboard): The Codex quota card no longer shows latent, never-used… (#8051)
-
feat(providers): native Fish Audio TTS provider on… (#8099)
-
feat(codex): add bounded multi-reference PNG/JPEG/WebP image editing… (#8122) — @xiaoyaner0201
-
refactor(sse): classify SSE critical-path empty catches… (#8142)
-
feat(db): persist caller session tag into call_logs for… (#8249)
-
feat(api): quota-aware fallback routing for web-fetch… (#8297)
-
feat(providers): map upstream reasoning-level metadata… (#8347)
-
feat(providers): weekly quota for xAI OAuth (Grok) (
xai-oauth/xao)… (#8471) — @allanvb -
feat(jobs):
backup auto enableschedule is now… (#8513) -
feat(ci): automate ratchet shrink-banking so file-size/complexity… (#8612) — @MumuTW
-
feat(providers): live monthly credit quota for Firecrawl (`GET… (#8759) — @allanvb
-
feat(search): add Firecrawl to
POST /v1/searchsupported providers… (#8814) — @allanvb -
feat(adobe-firefly): reference-image attach for generate + OpenAI…
-
Homologation suite: new
npm run homologruns the full… -
feat(mcp): add a read-only Local Corpus context source with…
-
feat(providers): notion-web live model discovery via…
-
Provider connections can now be shown or hidden…
-
docs(i18n): rewrite Russian README as a full native…
-
Providers: adds the Xiaomi MiMo Token Plan provider and a… (#8861)
-
feat(opencode-plugin): auto-discover models while running + force sync (#8101) — @RaviTharuma
-
feat(combo): enforce provider and model family invariants (#8304) — @RaviTharuma
-
feat(cli): replace ANTHROPIC_SMALL_FAST_MODEL with Fable default (#8343) — @leszek3737
-
feat: read INITIAL_PASSWORD env var during setup (#8439) — @linhdmn
-
feat(providers): add missing opencode-go reasoning effort variants (#8441) — @Prudhvivuda
-
feat(combos): add select all / unselect all in Browse Catalog (#8526) — @JoshimOfficial
-
feat(cli-tools): add all Hermes Agent auxiliary model roles (#8543) — @leszek3737
-
feat(ci): block stale UI translations when an English value is… (#8574)
-
feat(providers): flatten multi-turn history for gemini-web (#8607) — @Prudhvivuda
-
feat(sse): relay upstream 4xx error bodies verbatim on the… (#8622)
-
feat: Claude Code discovery aliases (surface non-Claude… (#8666)
-
feat(dashboard): copy-paste settings.json block for Claude Code discovery (#8722)
-
feat(quality): temporary relax of complexity/file-size ratchets for… (#8767)
-
feat(db): let the migration runner scan extra namespaced… (#8770)
-
feat(api): prompt-cache health summary endpoint and analytics tab (#8827)
⚡ Performance
-
perf(db): project columns + composite index in… (#6918) — @oyi77
-
perf(db): add jitter to stagger due-on-restart connections (#6919) — @oyi77
-
perf(startup): warm model catalog cache at module init (#6920) — @oyi77
-
perf(db): add temp_store=MEMORY pragma to SQLite init (#6921) — @oyi77
-
perf(db): cap modelLockouts eviction at 1000 entries (#6923) — @oyi77
-
perf: wrap ComboCard, HeroSection in React.memo (#7070) — @oyi77
-
perf: Date.now hoist, hasActiveDeltaValue hoist, buffer.split… (#7066) — @oyi77
-
perf(memory): mitigate event-loop starvation under 3000+ provider… (#7719) — @oyi77
-
perf: reduce long-context request copies (#7862) — @RaviTharuma
-
perf: lazy provider init, P2C quota cache, structuredClone… (#7893) — @oyi77
-
perf(api): singleflight version lookups (#8301) — @RaviTharuma
-
perf(api): skip the full catalog build for quota-exclusive keys (#8771)
🐛 Bug Fixes
-
fix: add re-entrancy guard to token health check sweep (#6917) — @oyi77
-
fix(grok): strip reasoningEffort for grok cli models (#6938) — @CitrusIce
-
fix(6954,6953): preserve system role + strip empty-signature thinking… (#6982) — @rafaumeu
-
fix(6980): classify Cloudflare AI neuron exhaustion as… (#6983) — @rafaumeu
-
fix(dashboard): hide disabled provider connections from combo builder (#6984)
-
fix(providers): cap grok-cli tools at 200 for cli-chat-proxy (#6986)
-
fix(6848): auto-cleanup for telemetry tables causing OOM (#6988) — @rafaumeu
-
fix(models): preserve direct-model combo metadata (#6993) — @JxnLexn
-
fix: DDG circuit breaker + null content validation (#7001) — @rafaumeu
-
fix(models): preserve chat-capable image model rows (#7004) — @xz-dev
-
fix(codex): preserve GPT-5.6 reasoning contract (#7012) — @xz-dev
-
fix(base-red): align least-used combo tests with executionKey usage… (#7015)
-
fix: infer bare models from active synced catalogs (#7028) — @guanbear
-
fix(auggie): update model registry to match v0.32.0 CLI model IDs (#7032) — @oyi77
-
fix(sse): register ollama-cloud in USAGE_FETCHER_PROVIDERS (#7041) — @alltomatos
-
fix(quality): read cognitiveComplexity= machine line in… (#7042) — @alltomatos
-
fix(providers): sanitize Claude native output_config.effort (#7050) — @xier2012
-
fix(combo): treat maxInputTokens as an input-only cap in the… (#7052) — @xier2012
-
fix(antigravity): collect native part.functionCall into tool calls (#7053) — @xier2012
-
fix(responses): map mid-conversation system turns to developer role (#7056) — @xier2012
-
fix(combo): least-used sorts by per-account executionKey (#7059) — @xier2012
-
fix(providers): AgentRouter model import applies Claude Code wire image… (#7060) — @xier2012
-
fix(translator): preserve thinking.budget_tokens: 0 in Claude->Gemini (#7061) — @xier2012
-
fix(cloudflare-relay): avoid invalid regex syntax in generated worker (#7063) — @SeaXen
-
fix(dashboard): strip browser-extension attrs before hydration (#7073) — @MrFadiAi
-
fix(relay): bound Bifrost stream lifetime (#7093) — @KooshaPari
-
fix(sse): recognize xiaomi-tokenplan mimo as a thinking-mode model (#7098)
-
fix(codex): strip regex lookaround from tool schema patterns (#7100)
-
fix(openai): strip reasoning_effort when GPT-5.x models carry… (#7101)
-
fix(compression): Headroom SmartCrusher skips developer-role messages… (#7102)
-
fix(providers): surface a warning on 404 model_not_found in… (#7103)
-
fix(executors): forward X-Session-ID/X-Title agent metadata headers (#7104)
-
fix(cli): verify better-sqlite3 native binary is actually loadable (#7105)
-
fix(sse): sanitize non-ok Antigravity streaming error body (port… (#7106)
-
fix(providers): add MiniMax image-generation provider (#7108)
-
fix(sse): handle space-separated arg name/value in Composer tool… (#7116)
-
fix(cli): remove MITM DNS spoof entries before killing server… (#7117)
-
fix(dashboard): include never-tested connections in combo builder… (#7118)
-
fix(api): check Vercel SSO-protection PATCH response on relay… (#7119)
-
fix(combos): reject oversized fusion panels before fan-out (port… (#7120)
-
fix(combo): detect empty content_block in streaming SSE peek (#7121)
-
fix(oauth): resolve Kiro AWS SSO cache client credentials by… (#7122)
-
fix(tests): vitest UI suite back to green (69 fails triaged — WS6.1) (#7127)
-
fix(auto): use p95 fallback in speed factors (#7128) — @KooshaPari
-
fix(models): update Anthropic model contextLength to 1M (#7129) — @HouMinXi
-
fix(ci): raise dast-smoke timeout 12->25min (build alone eats up… (#7139)
-
fix(compression): lazy-load typescript in RTK codeStripper so prod-lean… (#7164) — @alltomatos
-
fix(providers): accept m365.cloud.microsoft for copilot-m365-web token (#7166) — @xier2012
-
fix(executors): disable parallel tools for Codex Responses Lite (#7171) — @fenix007
-
fix(tests+providers): env-dependent tests exposed by GH-hosted runners (#6634… (#7174)
-
fix(combo): reject known context overflow without exhausting… (#7177) — @JxnLexn
-
fix: add static.cloudflareinsights.com to CSP script-src (#7178) — @oyi77
-
fix: extend turbopack ignoreIssue suppression to compression… (#7180)
-
fix: recognize Ollama Cloud session usage-limit 429 as… (#7181)
-
fix: preserve relayAuth for pool-referenced relay proxies (#7182)
-
fix: wire adaptive context-budget dial into settings schema… (#7183)
-
fix(providers): DuckDuckGo VQD 429 misclassified as 503 (#7185)
-
fix(db): cap OOM probe-failure cycle in getDbInstance() (#7186)
-
fix: stop opencode-go quota lookup defaulting to Z.AI… (#7187)
-
fix(providers): refresh OpenCode (oc) free-tier model catalog (#7188)
-
fix: include proxyId when testing a saved registry proxy (#7189)
-
fix: sanitize non-Latin1 chars in combo diagnostic headers (#7190)
-
fix: raise main server keepAliveTimeout/headersTimeout above… (#7191)
-
fix: route zai-web (and other registry-entry web-cookie… (#7192)
-
fix(providers): reject chat requests for cloud-agent-only jules provider (#7193)
-
fix: restore mobile grid-cols-1 fallback on quota page card… (#7194)
-
fix: wire modelAliases fetch into HermesAgentToolCard (#7195)
-
fix: surface real claude-web error body for non-SSE 400s (#7196)
-
fix(dashboard): agent bridge dns toggle uses POST, not PUT (#7197)
-
fix: stop duplicating text in Gemini Web streamed responses (#7198)
-
fix: filter hidden custom models out of legacy combo model… (#7199)
-
fix(dashboard): implement missing handleToggleSource on Free Pool tab (#7200)
-
fix: honor combo-level proxy assignments from the registry (#7201)
-
fix(ci): run quality gates on Mergify merge-queue draft PRs… (#7202)
-
fix: add dashboard-scoped typecheck gate covering… (#7203)
-
fix(guardrails/chat): stop Vision Bridge hijacking credentialed models to… (#7204) — @artickc
-
fix(translator): preserve Gemini thought parts as reasoning_content on… (#7206)
-
fix(translator): register openai response projection for gemini clients (#7207)
-
fix(cli): fast-path --version to skip full CLI bootstrap (#7208)
-
fix: honor PROVIDER_LIMITS_SYNC_SPACING_MS for local/API-key… (#7214)
-
fix(api): bulk-add API keys no longer overwrite existing… (#7234)
-
fix(sse): route the public OpenAI GPT-5.6 family through the… (#7242)
-
fix(providers): honor configured proxy on Grok Build egress (#7244)
-
fix(nvidia): expand NIM chat model catalog (#7247)
-
fix(sse): reconstruct Claude-format content in synthetic bypass… (#7248)
-
fix(build): isolate Windows HOME/AppData during next build (#7249)
-
fix(cli): omniroute dashboard respects PORT env when --port is… (#7252)
-
fix(sse): project non-streaming JSON back to the… (#7255)
-
fix(combo): fall back on Responses SSE failures (#7256) — @rushsinging
-
fix(routing): resolve nested combo-ref panel members in fusion… (#7259)
-
fix(usage): reset logs and show provider names in analytics (#7264) — @SeaXen
-
fix(sse): silence noisy proxy-failure log on caller-initiated… (#7266)
-
fix(codex): normalize nested Responses output content (#7269) — @JxnLexn
-
fix(combo): derive session stickiness key from Responses API… (#7277) — @alltomatos
-
fix(antigravity): wrap Pro fallback chain in try/catch for timeout… (#7290) — @HouMinXi
-
fix(logs): show saved provider names in request/provider log views (#7294) — @SeaXen
-
fix(stream): reconcile encrypted Codex reasoning visibility without… (#7304)
-
fix(build): packed tarball boot crash — server-ws timeout import… (#7308)
-
fix(skills): register cli-skill-collector in the agent-skills… (#7310)
-
fix(db): tolerate unavailable virtual table modules in stats (#7313) — @megamen32
-
fix(router-eval): retained-optimization gate cleanup (#7318) — @KooshaPari
-
fix(ci): Coverage job timeout 10->20min (lcov reporter pushed it… (#7342)
-
fix(electron): normalize hashed standalone externals (#7353) — @tianrking
-
fix(db): stop a 'latest' path segment from disabling backups and… (#7359) — @danscMax
-
fix(branding): regenerate raster favicons — white mark was shipped… (#7390) — @vzts
-
fix(test): skip real DNS writes in MITM dynamic-import test (#7398) — @HouMinXi
-
fix(antigravity): streaming passthrough for non-streaming clients (#7408) — @HouMinXi
-
fix(build): align engines.node with SUPPORTED_NODE_RANGE (#7490) — @alltomatos
-
fix(api): await params in Agent Bridge DNS route (Next.js 16) (#7492) — @alltomatos
-
fix(dashboard): show Obsidian context source card (#7500) — @DKotsyuba
-
fix(ci): fetch full base history in pr-test-policy (shallow… (#7501)
-
fix(sse): preserve chat quota across mixed windows (#7504) — @webmasterarbez
-
fix(oauth): surface sanitized device-code error instead of a… (#7511) — @danscMax
-
fix(oauth): repair qwen + codebuddy-cn device-code endpoints (#7517) — @danscMax
-
fix(mitm): strip trailing assistant prefill to prevent upstream… (#7520) — @chirag127
-
fix(codex): Test probe uses a ChatGPT-account-supported model (#7524)
-
fix(codex): validate refresh_token on import before persisting (#7525)
-
fix(codex): non-stream chat 502 'Response body is already used'… (#7526)
-
fix(oauth): surface tunnel hint when Codex OAuth runs on a remote… (#7527)
-
fix(sse): preserve custom tool output images (#7540) — @loulanyue
-
fix(combo): failover when upstream SSE is truncated mid-lifecycle (#7545) — @Chewji9875
-
fix(dashboard): prefer public endpoint URLs (#7547) — @nguyenha935
-
fix(cli): refresh runtime detection accurately (#7552) — @nguyenha935
-
fix(ui): improve React Flow dark theme (#7553) — @nguyenha935
-
fix(i18n): treat MISSING sync placeholders as absent in EN… (#7556)
-
fix(cli): Windows cert check/uninstall key off the real CA… (#7557)
-
fix(sse): feed compression pipeline the authoritative vision… (#7560)
-
fix(dashboard): providers model-name filter matches live/synced catalog (#7561)
-
fix(db): pre-init sql.js WASM ahead of any getDbInstance()… (#7562)
-
fix(dashboard): resolve costs page 500 from out-of-scope t() in… (#7564)
-
fix(dashboard): surface rate-limit warning on 429 chat-probe (#7565)
-
fix(sse): lazy-load playwright in claudeTurnstileSolver (#7566)
-
fix(sse): combo failover for OpenAI streams truncated without… (#7568)
-
fix(cli): reuse win32-aware locateCommand in tool-detector (#7569)
-
fix(codex): #7536 check content-type before touching response.body… (#7570)
-
fix(sse): stop dropping tool_search and leaking OpenAI-only… (#7571)
-
fix(api): resolve provider display name and dedup byModel on… (#7573)
-
fix(mitm): route Claude Code standalone MITM traffic (#7574) — @dongwook-chan
-
fix(sse): stop per-byte enumeration of binary image bytes in log… (#7576)
-
fix(sse): split effort/reasoning suffix off pinned cursor model… (#7577)
-
fix(chatgpt-web): recognize update_content.messages[] celsius WS frames (#7578)
-
fix(sse): 401 model-not-supported lockout + sticky… (#7580)
-
fix(antigravity): allow cloudcode envelope through messages guard (#7582) — @dongwook-chan
-
fix(sse): sanitize empty-signature thinking blocks + hoist… (#7583)
-
fix(sse): honor per-model targetFormat override for… (#7584)
-
fix(sse): clamp glm-4.6v max_tokens to the 32768 ceiling (#7585)
-
fix(cli): log Codex Responses WebSocket history/usage per logical… (#7588)
-
fix(providers): derive static model catalogs for search providers from… (#7589)
-
fix(stream-readiness): bump timeout for heavy Claude-format reasoning replicas (#7612) — @herjarsa
-
fix(translator): synthesize tool call chunks from response.completed… (#7613) — @ekinnee
-
fix(embeddings): add lmstudio to embedding provider registry (#7614) — @ekinnee
-
fix(combo): auto-clear stale session pins and emit recovery hints… (#7625) — @herjarsa
-
fix(providers): unify connection and routing flows (#7629) — @nguyenha935
-
fix(db): dedupe bulk-imported proxies by full credential tuple (#7644) — @alltomatos
-
fix(api): allow text-to-image on dual-modality models + revive… (#7648) — @danscMax
-
fix(stryker): add Microsoft Designer test to tap.testFiles (#7659)
-
fix(dashboard): cut UI import chain from connection persist module (CI… (#7677)
-
fix(perplexity-web): stop empty-content responses from live schematized SSE (#6955) — @artickc
-
fix(dashboard): make quota cards container responsive (#7027) — @xz-dev
-
fix(nvidia): restore GLM-5.2 reasoning on NIM (#7296) — @backryun
-
fix(i18n): complete Vietnamese dashboard localization and runtime… (#7493) — @nguyenha935
-
fix(providers): migrate muse-spark-web from GraphQL to WebSocket… (#7528) — @Ajeesh25353646
-
fix(combo): expose computed context_length via /api/combos for… (#7633) — @herjarsa
-
fix(api): enumerate tiered auto combo endpoints in… (#7662) — @ekinnee
-
fix(dashboard): topology reflects connection health + clears finished… (#7672) — @danscMax
-
fix(kimi-coding): capture and replay reasoning for thinking-mode turns (#7673) — @xz-dev
-
fix(ci): merge-train --fast mirrors test:unit subdir allowlist (#7688)
-
fix(sse): start credential-health sweep at boot so stale web… (#7689) — @danscMax
-
fix(cursor): discover models via official CLI command (#7692) — @makcimbx
-
fix(quota): fix antigravity/agy multi-model quota skipping in combos (#7695) — @irvandikky
-
fix(usage): preserve account identity history (#7700) — @xz-dev
-
fix(db): update proxies on password rotation (#7707) — @floze-the-genius
-
fix(providers): correct Chutes registry baseUrl (#7708)
-
fix(routing): strip prompt_cache_key for NVIDIA NIM (#7709)
-
fix(providers): degrade Arena (lmarena) cookie validation redirect to… (#7710)
-
fix(claude-web): unify Turnstile/executor/fast-path User-Agents behind… (#7711)
-
fix(sse): authenticate CLIProxyAPI fallback/passthrough legs with… (#7712)
-
fix(sse): proactively refresh Grok Build OAuth token before… (#7715)
-
fix(providers): classify ambiguous Mistral 401 instead of hard auth… (#7718)
-
fix(security): bump adm-zip >=0.6.0 + exact host matching in mitm DNS… (#7732)
-
fix(icons): fall back to Stepfun Mono when Color component is… (#7743) — @Dan-ex-hub
-
fix(stream): suppress
</think>close marker for Responses API… (#7747) — @xz-dev -
fix(sse): wire settings.wildcardAliases into model resolution (#7748)
-
fix(authz): classify forge/jcode CLI settings routes as LOCAL_ONLY (#7749)
-
fix(routing): honor eye-icon hidden models for no-auth providers in… (#7750)
-
fix(sse): persist rotated Gemini web-session cookies via… (#7751)
-
fix(docs): heal release-green docs drift + eslint any-suppression… (#7755)
-
fix(mcp): copy undici into dist/node_modules to prevent… (#7756)
-
fix(packaging): move fumadocs-mdx to devDependencies (#7757)
-
fix(ci): build API-only smoke workflows backend-only to fix… (#7758)
-
fix(cli): load DATA_DIR/server.env as fallback for .env on… (#7759)
-
fix(cli): split outboundUrlGuard's DB helpers so setup-opencode… (#7760)
-
fix(notion-web): production-ready labels, multi-workspace, inference,… (#7768) — @artickc
-
fix(kimi): expose K3 reasoning effort levels (#7776) — @xz-dev
-
fix(compression): apply compression combo assignments to routing combos (#7779) — @ekinnee
-
fix(i18n): regenerate Polish UI locale from English (#7782) — @leszek3737
-
fix(combo): retry transient errors in pipeline strategy (#7794) — @AndrianBalanescu
-
fix(quality): register nvidia-quota-phase1 and… (#7796)
-
fix(stream): synthesize terminal finish_reason chunk when upstream… (#7804) — @AndrianBalanescu
-
fix(plugins): 5 bugs on the plugin path (3 Windows-only, 2… (#7806) — @tmone
-
fix(cli): register ESM alias resolver for @/ paths under global… (#7808) — @rafaumeu
-
fix(auth): gate invalid-key check on isRequireApiKeyEnabled for… (#7810) — @AndrianBalanescu
-
fix(ci): repair release regressions exposed by clean runs (#7812) — @backryun
-
fix(rerank): add voyage format adapter for request/response… (#7813) — @AndrianBalanescu
-
fix(antigravity): attempt onboarding when projectId is empty (#5193… (#7815) — @rafaumeu
-
fix(stream): emit terminal SSE frames on mid-stream upstream failure (#7816) — @AndrianBalanescu
-
fix(sse): strip orphaned tool_use before antigravity/Vertex… (#7822)
-
fix(translator): sanitize tool_result.tool_use_id symmetrically with… (#7823)
-
fix(oauth): require chatgptUserId agreement for Codex account dedup (#7825)
-
fix(db): purge in-memory key-health state when a provider… (#7826)
-
fix(compression): keep a retrievable preamble instead of a bare CCR marker (#7827)
-
fix(db): log fatal boot-time SQLite driver-cascade failure… (#7828)
-
fix(docker): repair tls-client-node native binary after… (#7829)
-
fix(auth): restore TICK_MS in tokenHealthCheck (ReferenceError on… (#7830)
-
fix(cli): fix Windows CLI detection false negatives (#7753, #7774) (#7831)
-
fix(docs): document CREDENTIAL_REDACTION_ENABLED and… (#7833)
-
fix(dashboard): fix collapsed quota card session/weekly order (#7834)
-
fix(dashboard): mirror connection-row action-icon spacing under RTL (#7835)
-
fix: avoid cmd.exe spawn on Windows by using os.hostname()… (#7841) — @tientien17
-
fix(usage): harden account identity reconciliation (#7843) — @xz-dev
-
fix(cli): use rundll32 instead of cmd.exe for Windows browser… (#7844) — @tientien17
-
fix: add native lifecycle-aware health endpoint (#7852) — @RaviTharuma
-
fix: reserve chat admission before body parsing (#7853) — @RaviTharuma
-
fix: bound quadratic session-dedup memory growth (#7855) — @RaviTharuma
-
fix(compression): enable OmniGlyph for Claude Fable 5 (#7863) — @enjoyer-hub
-
fix(notion-web): add browser fingerprint headers to reduce Cloudflare… (#7864) — @HassiyYT
-
fix(mitm): gate Agent Bridge Repair on sudo password (#7865) — @skutanjir
-
fix(rerank): honor the connection's pinned proxy on rerank calls (#7867)
-
fix(compression): skip CCR on tool outputs to preserve agent loop (#7869) — @herjarsa
-
fix(vision-bridge): reroute auto/ prefix to vision model when images present (#7871) — @herjarsa
-
fix(opencode-plugin): support separate management read token (#7885) — @RaviTharuma
-
fix(sse): CC bridge loses OpenAI-format image input… (#7888)
-
fix(combo): strip boolean reasoning field for opencode-go providers (#7891) — @AndrianBalanescu
-
fix(notion-web): accept OpenAI content-parts arrays in transcript (#7896) — @artickc
-
fix(notion-web): reuse threadId across OpenAI multi-turn (no new chat… (#7900) — @artickc
-
fix(gemini): strip OpenAI "strict" tool-schema keyword for… (#7901) — @Witroch4
-
fix(antigravity): collect native functionCall parts in SSE collector (#7902) — @Witroch4
-
fix(sse): recover invalid Anthropic thinking signatures once (#7906) — @insoln
-
fix(resilience): don't cool down accounts or trip the breaker on client… (#7908) — @insoln
-
fix(dashboard): preserve quota cutoff drafts (#7909) — @hydraxman
-
fix(providers): treat public-host 302 as valid in Gemini Web connection… (#7917)
-
fix(providers): read reasoning_text in Claude-format response translator (#7919)
-
fix(dashboard): safely render structured error objects in Request Logs… (#7920)
-
fix(dashboard): repair monaco deep import broken by 0.56 exports map (#7922)
-
fix(autostart): adopt 9Router VBS startup to suppress console flash on… (#7925) — @tientien17
-
fix(translators): normalize TitleCase tool names for non-Anthropic models (#7926) — @nramabad
-
fix(api): resolve local provider models via dashboard catalog… (#7927) — @ekinnee
-
fix(auto): pool accounts by provider model (#7928) — @adrianaryaputra
-
fix(perplexity-web): multi-step empty content + advanced-quota cooldown (#7930) — @artickc
-
fix(ccr): resolve principal via OMNIROUTE_API_KEY env var on… (#7932) — @ekinnee
-
fix(combo): context-aware fallback ignores model_context_override (#7933) — @tmone
-
fix(i18n): preserve remaining Vietnamese localization (#7935) — @nguyenha935
-
fix(mitm): gate Agent Bridge DNS and Trust Cert on sudo password (#7939) — @skutanjir
-
fix(providers): route iflytek/sparkdesk to Spark's OpenAI-compatible… (#7942) — @FenjuFu
-
fix(sse): preserve parallel_tool_calls for GPT-5.6 delegation… (#7957)
-
fix(providers): copilot-m365-web fails loudly on empty turns +… (#7958)
-
fix(providers): treat unreliable web-cookie /models probe status as… (#7959)
-
fix(api): add amazon-q to the static model catalog (#7960)
-
fix: parse Gemini 429 RetryInfo.retryDelay for model lockout (#7961)
-
fix(quality): tolerate ESLint's trailing unpruned-suppressions text… (#7962)
-
fix(cli): translate missing sqlite bindings error into actionable… (#7963)
-
fix(cli): spawn opencode.cmd shim with shell:true on win32 (#7964)
-
fix(dashboard): correct block-extra-Claude-usage toggle copy to match… (#7965)
-
fix(api): classify /api/acp/agents as loopback-only (#7966)
-
fix(auth): restore configurable HEALTHCHECK_BATCH_SIZE dropped by… (#7970)
-
fix(test): widen ratelimit-admission pollUntil deadline to 10s (#7971)
-
fix(pricing): clarify disabled automatic sync status (#7972) — @RaviTharuma
-
fix(combo): exempt content_filter from empty-content detection (#7973) — @HouMinXi
-
fix(embeddings): support secure multimodal inputs (#7978) — @RaviTharuma
-
fix(resilience): cap exactCooldownMs against maxCooldownMs (#7980) — @ekinnee
-
fix(electron): derive macOS Helper name from execPath to remove 2nd… (#8002)
-
fix(chatcore): report string-reason client aborts as 499, not 502 (#8011) — @Long-Feeds
-
fix(models): drop generic catalog siblings of specialty surfaces (#8021) — @RaviTharuma
-
fix(models): stop inventing chat capabilities for specialty surfaces (#8022) — @RaviTharuma
-
fix(capabilities): resolve models.dev specialty rows across provider keys (#8023) — @RaviTharuma
-
fix(models): attach models.dev pricing to GET /v1/models entries (#8025) — @RaviTharuma
-
fix(grok-cli): require full auth.json on OAuth paste import (#8027) — @RaviTharuma
-
fix(grok-cli): sanitize function_call_output before Grok Build dispatch (#8030) — @RaviTharuma
-
fix(sse): bound forwarded response headers (#8041) — @insoln
-
fix(sse): replace spoofable .includes() PromptQL issuer check… (#8042)
-
fix(sse): bound Codex SSE peek read with per-read timeout (#8043)
-
fix(cli): stop double-prefixing combo model ids in opencode… (#8047)
-
fix(antigravity): scope 404 model-not-found lockout to exact model +… (#8050) — @AndrianBalanescu
-
fix: repair pre-existing red gates on the release/v3.8.49 tip (#8055)
-
fix(oauth): honor connectionId on token refresh so email-less… (#8062) — @insoln
-
fix: classify Google quota exhaustion responses (#8071) — @rafaumeu
-
fix(providers): refresh duckduckgo-web catalog to current Duck.ai wire… (#8079)
-
fix(security): decouple request PII redaction from injection mode (#8102) — @RaviTharuma
-
fix(providers): discover live AGY models (#8123) — @adevwithpurpose
-
fix(guardrails): align INPUT_SANITIZER request masking gate (#8124) — @RaviTharuma
-
fix(providers): refresh Baidu ERNIE and Qianfan website URLs (#8128) — @TrackCrewGalore
-
fix(stream): add logging to empty catch blocks in stream error… (#8143) — @chirag127
-
fix(sse): stop Codex/Responses sanitizer turning system image_url… (#8147)
-
fix(db): register SIGHUP handler and stop force-killing server… (#8148)
-
fix(providers): add missing poe registry baseUrl entry (#8149)
-
fix(routing): anchor quota cache on globalThis for cross-chunk… (#8150)
-
fix(responses): close namespace round-trip for Responses-Chat… (#8151) — @RCrushMe
-
fix(oauth): warn instead of silently opening unreachable localhost… (#8152)
-
fix(db): stop closing the sql.js singleton in getDbInstance()… (#8153)
-
fix(sse): run compression pipeline per turn in Codex Responses WS… (#8154)
-
fix(cli): merge node bin dir into CLI healthcheck PATH for codex… (#8156)
-
fix(sse): anonymous fingerprint fallback for keyless Pollinations… (#8157)
-
fix(cli): surface the real spawn error in process supervisor (#8158)
-
fix: strip internal reasoning placeholder from user-visible… (#8162) — @Dingding-leo
-
fix(combos): expose synced reasoning-effort variants in Combo… (#8165) — @Dingding-leo
-
fix(windows): add windowsHide to all child process spawns (#8167) — @Dingding-leo
-
fix(cursor): bridge native tools to client calls (#8171) — @makcimbx
-
fix(security): bound JWT-extraction regexes to prevent polynomial… (#8173)
-
fix: log pending request counter decrement failures (#8179) — @rafaumeu
-
fix: suppress sql.js build warning via non-analyzable… (#8184) — @rafaumeu
-
fix: align INPUT_SANITIZER_ENABLED default to true across… (#8185) — @rafaumeu
-
fix(combo): skip remaining same-provider targets on 401/403 auth… (#8195) — @rafaumeu
-
fix(compression): make memo key model-independent for non-vision engines (#8196) — @rafaumeu
-
fix(resilience): add max/step to NumberField for provider cooldown inputs (#8203) — @rafaumeu
-
fix(providers): fix Azure AI Foundry multi-model discovery and… (#8206) — @not-knope
-
fix(logs): stop the async-EPIPE log-flood loop at its ignition… (#8207) — @Tasogarre
-
fix(sse): surface OpenRouter mid-stream error chunks instead of a… (#8210) — @hartmark
-
fix(sse): Gemini malformed function-call handling + tool_choice… (#8211) — @hartmark
-
fix(sse): tool-incapable provider handling (AI Horde + Responses… (#8212) — @hartmark
-
fix(sse): Gemini TPM/RPD quota classification + combo… (#8213) — @hartmark
-
fix(services): resolve and record a real pid when adopting a service (#8218) — @seanford
-
fix(memory): resolve remote embedding dimensions for reindex (#8220) — @Prudhvivuda
-
fix(dashboard): correct machine-translated Korean UI strings in ko.json (#8224) — @MichaelYcJo
-
fix(devin-cli): refresh shared model catalog (#8227) — @backryun
-
fix(claude-web): align session transport and fallback (#8230) — @backryun
-
fix: restore OAuth auto-refresh for gemini-cli connections (#8232) — @seanford
-
fix: normalize Codex URLs and dashboard regressions (#8233) — @nguyenha935
-
fix(api): narrow claudeClassifierCompat auto trigger so… (#8236)
-
fix(gemini): drop HARM_CATEGORY_CIVIC_INTEGRITY from the default… (#8238)
-
fix(backend): word-boundary-safe tool-result truncation in lite… (#8239)
-
fix(providers): filter unsupported family-fallback candidates against… (#8240)
-
fix(translator): synthesize tool call chunks from
response.completed… (#180 #3980) -
fix(api): Vercel Relay deploy now checks the Deployment… (#1037) — @ricatix
-
fix(oauth): resolve Kiro AWS SSO cache client credentials by… — @xcrag
-
fix(combo): streaming Claude responses whose content block opens… (#1382) — @heishen6
-
fix(codex): strip regex
patternlookaround (lookahead/lookbehind)… (#7100) — @evinjohnn -
fix(cli):
stopMitm()now removes /etc/hosts DNS-spoof entries… — @dionisius95 -
fix(sse): Cursor Composer/Auto tool calls that separate the arg… — @way-art
-
fix(dashboard): the "Custom Models" add/edit form now has a "Vision… — @nguyenphi37
-
fix(combos): fusion combos now reject an oversized panel (>40 models… — @fontvu
-
fix(providers): the OpenAI-compatible "Check" validation flow now… — @advane204f
-
fix(dashboard): include never-tested custom provider connections in the… — @fajarbossit
-
fix(executors): forward agent-supplied
X-Session-ID/X-Title… (#7104) — @chitholian -
fix(sse): Antigravity streaming requests that hit a non-ok… — @Duongkhanhtool
-
fix(providers): MiniMax Text-to-Image now works — a
minimax… — @felipeleite -
fix(cli): the runtime self-heal now verifies a cached… (#2493) — @mrprohack
-
fix(openai): strip
reasoning_effort/reasoningfor GPT-5.x models… — @techsolutionmta -
fix(providers): preserve relayAuth for… (#5716)
-
fix(providers): wire the Devin cloud-agent provider… (#6142)
-
fix(providers): refresh Baidu ERNIE + Qianfan provider website URLs to… (#6271) — @TrackCrewGalore
-
fix(providers): honor a provider-level proxy assigned… (#6272)
-
fix(api): merge tool_call continuation deltas that… (#6276)
-
fix(providers): modernize the
lmarenaprovider for the Arena.ai… (#6280) — @backryun -
fix(providers): web-provider model discovery updated — qwen-web uses… — @janeza2
-
fix(logs): the request-log detail modal no longer reopens by… — @xz-dev
-
fix(providers): update SenseNova Token Plan support — register the… — @xz-dev
-
fix(providers): give v0-vercel-web its own alias so its… (#6343)
-
fix(providers): route AgentRouter key validation… (#6377)
-
fix(db): stop legacy log-archive migration from…
-
fix(docs): document Turbopack build memory tradeoff and… (#6409)
-
fix(compression): surface silently-dropped…
-
fix(providers): honor the
max_tokencapability… (#6524) -
fix(dashboard): the onboarding tier-flow diagram rendered broken — its… — @ianriizky
-
fix(routing): the
autocombo's no-auth candidate pool now honors a… (#6557) -
fix(sse): server-tool literal names (e.g.
web_search) are… — @MikeTuev -
fix(sse): sanitize non-Latin1 characters before… (#6612)
-
fix(api): recognize OpenRouter… (#6623)
-
fix(db): share one in-flight sql.js load across… (#6628)
-
fix(db): unwrap lone named-parameter objects before… (#6802)
-
fix(db): break probe-failed/restore loop on large storage.sqlite: (#6632 ). — @KooshaPari
-
fix(ci): exclude check-test-masking.test.ts's own… (#6634)
-
fix(routing): recognize Kimi-style "exceeded model… (#6637)
-
fix(cli): Claude Code installed via WinGet is now detected on… — @enjoyer-hub
-
fix(providers): removed obsolete/defunct providers from the catalog… — @backryun
-
fix(sse): requests rejected before
handleChatCore… (#6698) -
fix(providers): reject chat-completions requests for… (#6699)
-
fix(sse): unwrap bare
{function:{…}}tools so OpenAI-shape… — @samir-abis -
fix(oauth): stop merging distinct Codex OAuth logins that share an… — @lucasjustinudin
-
fix(codex): detect "model at capacity"/overloaded errors embedded… — @ryanngit
-
fix(volcengine): clamp
max_tokensto the VolcEngine Ark endpoint cap… — @whale9820 -
fix(antigravity): surface aborted/malformed Gemini tool calls (e.g.… — @anhdiepmmk
-
fix(routing): the reasoning-token headroom buffer clamps to the… (#6714) — @xz-dev
-
fix(api):
omniroute health(andhealth components/`health… (#6677 #6717) -
fix(startup): webpack build broke on case-insensitive filesystems… (#6718)
-
fix(build): Turbopack production build emitted an "Overly broad… (#6582 #6720)
-
fix(providers): Codex Desktop requests to
gpt-5.3-codex-sparkfailed… (#6651 #6721) — @alltomatos -
fix(providers): the provider quota card's weekly/session bars re-sorted… (#6687 #6722)
-
fix(i18n): pt-BR was missing 194 UI keys present in
en.json— a… (#6695 #6723) -
fix(startup):
omniroute --mcpcrashed at Node ESM link time with… (#6559 #6725) -
fix(providers): Kiro sent the adaptive-thinking… (#6576 #6726)
-
fix(translator): Cursor's local Subagent tool call is no longer… — @like3213934360-lab
-
fix(translator): GLM 5.2 (and other OpenAI-compatible upstreams that… — @itiwant
-
fix(resilience): OmniRoute didn't respect an exhausted Ollama Cloud (or… (#6638 #6731)
-
fix(resilience): a combo step "pinned" to one fingerprint account… (#6696 #6732)
-
fix(api): Responses passthrough emitted event-only SSE frames (no… (#6561 #6735)
-
fix(compression):
/api/compression/preview's top-level… (#6488 #6741) -
fix(resilience): account selection could pick an account already out of… (#6686 #6742)
-
fix(api):
reasoning_content(extended-thinking text) was… (#6662 #6743) -
fix(api): the compression config PUT schema now accepts… — @alltomatos
-
fix(api): raised the provider
apiKeylength cap for… — @alltomatos -
fix(routing): fusion combos no longer silently drop
combo-refpanel… (#6764) -
fix(ci): publish electron-updater
latest*.yml… (#6766) -
fix(i18n): translate hardcoded Portuguese dashboard strings to English (#6761, #6768): (#6769 ). — @chirag127
-
fix(providers): strip redundant node prefix when… (#6772)
-
fix(providers): scope nvidia NIM 404s to the single… (#6773)
-
fix(codex): bump the default Codex CLI client identity from… (#6780) — @quanturbo
-
fix(ci): the blocking "Impacted unit tests (TIA)" step… (#6788)
-
fix(translator): read PDF/video
file_dataattachments on the… — @Witroch4 -
fix(providers): ensure DeepSeek Web SSE emits [DONE] after FINISHED: (#6791 ). — @Pitchfork-and-Torch
-
fix(api): the compression config
PUTschema… — @Pitchfork-and-Torch -
fix(electron): materialize Turbopack hashed-module symlinks during packaging (#6724, #6594): (#6794 ). — @huohua-dev
-
fix(cursor): send the Agent CLI build id as… — @andrewmunsell
-
fix(cli): waitForServer() no longer reports ready from… (#6800)
-
fix(sse): de-flake timing-sensitive combo… (#6803)
-
fix(codex): strip include from compact responses requests: (#6805 ). — @yinaoxiong
-
fix(dashboard): surface Claude extraUsage credits in… (#6806)
-
Request count by provider & date: Dashboard → Analytics now shows a dedicated table of… (#6812) — @tjengbudi
-
fix(resilience): an Ollama Cloud (or any apikey-category provider)… (#3709 #6817)
-
fix(providers): an explicit
thinking.budget_tokens: 0is now honored… — @alltomatos -
fix(bootstrap): filter empty
process.envvalues before spawning… — @AndrianBalanescu -
fix(providers): classify upstream
404responses asMODEL_NOT_FOUND… — @AndrianBalanescu -
fix(db): cap the sql.js OOM-during-probe path in… (#6632 #6835)
-
fix(mcp): de-duplicate
TOTAL_MCP_TOOL_COUNTby tool… (#6854) -
fix(usage): xAI's exact provider-reported
cost_in_usd_ticksno… (#6856) — @KooshaPari -
fix(plugin): the
@omniroute/opencode-plugindynamic provider hook… (#6859) -
fix(sse): apply cliproxyapiModelMapping at CLIProxyAPI… (#6876)
-
fix(sse): defer
response.completeduntil a trailing… (#6906) -
fix(cli): ship
head-response-guard.cjsinto the standalone… (#6908) -
fix(sse): wire the shared quota-fetch throttle into… (#6911)
-
fix(sse): rename client-sent
max_completion_tokensto… (#6912) -
fix(providers):
PROVIDER_LIMITS_SYNC_SPACING_MSnow… (#6916) -
fix(sse): classify LAN embeddings providers (10/8,… (#6925)
-
fix(sse): Qwen Web executor no longer sends `[object… (#6927)
-
perf(api): relay chat-completions routes now thread the… (#6930)
-
fix(sse):
normalizeCodexMessageContentPartnow… (#6932) -
fix(sse): omit removed
attachmentsfield from Muse… (#6935) -
fix(dashboard): label audio/embeddings/image compatible… (#6936)
-
fix(api): model-list discovery for LAN-local… (#6939)
-
fix(providers):
openai->geminitransform now maps… (#4170) — @rafaumeu -
fix(sse): escape backslash before brackets in ChatGPT-web… (#6944) — @brick30llc-ctrl
-
fix(oauth): tokenHealthCheck now lowercase-normalizes… (#6947)
-
fix(sse): make Codex Responses tool-arg normalization… (#6951)
-
fix(sse): drop internal commentary-phase Responses… (#6952)
-
fix(sse): stop forwarding empty-signature thinking… (#6953)
-
fix(sse):
perplexity-web's defaultpplx-auto/pplx-sonar… — @artickc -
fix(combos): embeddings-only and rerank-only models (e.g. JinaAI,… (#6975)
-
fix(combos): when 2+ distinct model ids from the same provider would… (#6957)
-
fix(dashboard): the combos builder now hides provider connections the… — @attid
-
fix(providers): cap grok-cli tools at 200 per request, matching xAI's… — @gitcommit90
-
fix(providers): DuckDuckGo AI Chat executor propagates… (#6996)
-
fix(providers): refresh OpenCode (
oc) free-tier model… (#6998) -
fix(api): raise the main server's… (#7003)
-
fix(compression): wire the adaptive context-budget… (#7005)
-
fix(usage): stop opencode-go quota lookup from… (#7022)
-
fix(providers): Auggie (Augment CLI) model registry updated to the real… — @oyi77
-
fix(ci): add a dashboard-scoped typecheck gate covering… (#7033)
-
fix(cli):
omniroute dashboard(no--portflag) now respects… — @kaon0388v1 -
fix(build): extend the Turbopack
ignoreIssue… (#7051) -
fix(providers): web-cookie… (#7058)
-
fix(resilience): recognize Ollama Cloud's 5-hour… (#7071)
-
fix(dashboard): restore mobile single-column fallback… (#7072)
-
fix(dashboard): include proxyId when testing a saved… (#7080)
-
fix(sse): xiaomi-tokenplan
mimomodels (e.g.mimo-v2.5-pro)… (#7098) — @xxue-z -
fix(dashboard): align onboarding tier descriptions and localize the… (#7125) — @Wibias
-
fix(sse): claude-web now surfaces the real upstream error body… (#7134)
-
fix(db): honor combo-level proxy assignments from the… (#7149)
-
fix(dashboard): wire modelAliases fetch into… (#7151)
-
fix(dashboard): filter hidden custom models out of the… (#7156)
-
fix(dashboard): Agent Bridge DNS toggle now sends POST… (#7157)
-
fix(dashboard): implement missing
handleToggleSource… (#7161) -
fix(sse): stop duplicating text in Gemini Web streamed… (#7163)
-
fix(executors): Codex Responses Lite requests force serial tool calls… (#7171) — @fenix007
-
fix(translator): preserve Gemini thinking-mode
thought:trueparts as… — @warelik -
fix(translator): register the missing OpenAI→Gemini response projection… — @warelik
-
fix(cli):
omniroute --versionnow fast-paths before the tsx/esm… — @Jordannst -
fix(ci): build dast-smoke and nightly API-only smoke… (#7226)
-
api: bulk-add API keys no longer overwrite existing provider… — @asynx6
-
fix(sse): feed the compression pipeline the… (#7237)
-
fix(sse): route the public OpenAI GPT-5.6 family (
gpt-5.6,… — @Jordannst -
fix(providers): honor a configured proxy on Grok Build egress — the… — @ryanngit
-
fix(nvidia): expand NIM chat model catalog with newly-observed… — @spacesky-cell
-
fix(sse): synthetic bypass responses for Claude-format clients no… — @KunN-21
-
fix(build): isolate Windows HOME/AppData during next build. (thanks… — @KunN-21
-
fix(dashboard): providers model-name filter now matches… (#7250)
-
fix(docs): correct stale
/api/versionand… (#7253) -
fix(sse): project non-streaming JSON responses back to… (#7255) — @warelik
-
fix(i18n): treat
__MISSING__:sync-script… (#7258) -
fix(authz): classify /api/cli-tools/forge-settings and… (#7263)
-
fix(sse): lazy-load playwright in claudeTurnstileSolver… (#7265)
-
fix(sse): stop logging a caller-initiated request abort/timeout… — @TuyulSpam
-
fix(sse): classify 401 "model X is not supported" as… (#7268)
-
fix(dashboard): resolve `ReferenceError: t is not… (#7272)
-
fix(cli): Windows MITM root-CA check/uninstall keyed… (#7275)
-
fix(cli): reuse cliRuntime's win32-aware… (#7279)
-
fix(dashboard): connection Test surfaces a rate-limit… (#7284)
-
fix(sse): combo failover now detects OpenAI-shape… (#7285)
-
fix(db):
getDbInstance()now guarantees sql.js WASM has… -
fix(sse): split effort/reasoning suffix off pinned… (#7289)
-
fix(sse): hoist client-injected
systemmessages to… (#7293) -
fix(sse): treat Uint8Array/Buffer as opaque binary in… (#7297)
-
fix(cli): load DATA_DIR/server.env as a fallback for… (#7302)
-
fix(rerank): Honor the connection's pinned proxy on rerank calls, so… (#7350) — @kamenkadmitry
-
fix(electron): Normalize hashed Turbopack external imports in packaged… (#7353) — @tianrking
-
fix(chatgpt-web): recognize
update_content.messages[]… (#7357) -
fix(sse): clamp glm-4.6v max_tokens to the 32768… (#7364)
-
fix(sse): honor per-model targetFormat override for… (#7364)
-
fix(sse): combo session stickiness now releases a… (#7387)
-
fix(cli): log Codex Responses WebSocket history/usage… (#7388)
-
fix(embeddings): remove the non-existent… — @kamenkadmitry
-
fix(providers): expose a base-URL override for… (#7447)
-
fix(build): align
engines.nodesupported range… (#7446) -
fix(i18n): replace machine-bulk-filled Vietnamese UI… (#7493)
-
fix(db): stop closing the single sql.js singleton… (#7494)
-
fix(sse): stop stream readiness from treating a… (#7503)
-
Fixed the Codex connection Test button always… (#7521)
-
The Codex account import (`POST… (#7522)
-
The PKCE OAuth start… (#7523)
-
fix(providers): search providers now expose a static… (#7529)
-
fix(sse): map
tool_searchto a Chat function tool… (#7532) -
fix(sse): gate
verbosity/prompt_cache_keyon OpenAI… (#7533) -
fix(api): Usage page "by provider" table now shows the… (#7534)
-
fix(api): Usage page "model usage" table no longer… (#7535)
-
fix(codex): non-stream Codex (ChatGPT-account) chat no… (#7536)
-
fix(sse): preserve
input_imageparts in array-valued Codex… (#7540) — @loulanyue -
fix(providers): stop reporting Arena (lmarena) cookie… (#7542)
-
fix(combo): streaming combo failover now fails over when an… (#7545) — @Chewji9875
-
fix(dashboard): Public and managed tunnel endpoints now take precedence… (#7547) — @nguyenha935
-
fix(claude-web): unify Turnstile solver, executor and… (#7548)
-
fix(cli): fall back to the node:sqlite driver cascade… (#7586)
-
fix(api): expose Responses-API-format (OpenAI/Codex)… (#7587)
-
grok-cli: OAuth paste-import now requires the full… (#7610)
-
fix(sse): proactively refresh Grok Build's expiring… (#7610)
-
grok-cli: sanitize Responses
function_call_output.outputvalues… (#7611) -
fix(translator): guard against double-emission when
response.completed… -
fix(routing): strip
prompt_cache_keyfor NVIDIA NIM —… (#7617) -
fix(routing): honor eye-icon hidden models for no-auth… (#7620)
-
fix(providers): correct Chutes registry baseUrl from… (#7621)
-
fix(providers): unify connection status across… (#7629)
-
fix(providers): classify Mistral ambiguous 401 (quota… (#7638)
-
fix(sse): route CLIProxyAPI fallback/passthrough legs… (#7645)
-
fix(packaging): move fumadocs-mdx from dependencies to… (#7661)
-
GET /api/combos/auto now enumerates template variants…
-
fix(sse): persist rotated Gemini web-session cookies… (#7676)
-
fix(dashboard): mirror connection-row action-icon… (#7680)
-
fix(cli): split
outboundUrlGuard.ts's DB/feature-flag… (#7682) -
fix(sse): wire settings.wildcardAliases into model… (#7693)
-
fix(mcp): copy undici into dist/node_modules to prevent… (#7701)
-
fix(translator): sanitize
tool_result.tool_use_idsymmetrically with… (#7705) -
fix(db): bulk proxy imports now update an existing proxy when… (#7707) — @floze-the-genius
-
fix(oauth): stop Codex OAuth completion from collapsing… (#7737)
-
fix(db): purge in-memory API-key health/rotation state… (#7740)
-
perf(db): the 5 provider-connection/node call sites 's IC2… (#7787 #7744) — @oyi77
-
fix(compression): CCR no longer collapses a whole… (#7746)
-
fix(sse): strip orphaned tool_use blocks before… (#7752)
-
fix(cli): resolve Windows CLI detection false negatives…
-
fix(dashboard): keep session/weekly quota rows in a… (#7764)
-
fix(db): log the fatal boot-time SQLite driver-cascade… (#7773)
-
fix(sse): Claude-Code-compatible bridge (AgentRouter and any CC… (#7777) — @beingshafin
-
perf(db):
getProviderConnectionsnow reads from a shared… (#7787) — @oyi77 -
fix(docs): document CREDENTIAL_REDACTION_ENABLED and… (#7793)
-
fix(combo): retry intermediate pipeline-strategy steps… — @AndrianBalanescu
-
fix(docker): repair tls-client-node native binary after… (#7802)
-
fix(runtime): Apply refreshed request-queue settings, restore… (#7812)
-
fix(oauth): attempt Antigravity onboarding inline when… (#7814)
-
fix(api): add amazon-q to the static model catalog so… (#7820)
-
fix(sse): preserve parallel_tool_calls for GPT-5.6… (#7821)
-
fix(quality): tolerate ESLint's trailing… (#7837)
-
fix(test): widen ratelimit-admission pollUntil deadline… (#7842)
-
fix(cli): Windows
omniroute dashboardbrowser fallback uses… (#7844) -
fix(dashboard): Request Logs detail modal no longer… (#7845)
-
fix(backend): stop
buildClientRawRequestdeep-cloning… (#7847) -
fix(sse): copy the combo attempt body shallowly instead… (#7847)
-
fix(sse): estimate the combo fallback-compression… (#7847)
-
fix(providers): Copilot reasoning_text now surfaces in… (#7856)
-
fix(providers): treat a non-401/403 response from the… (#7857)
-
fix(providers): copilot-m365-web now fails loudly on a…
-
fix(providers): Gemini Web connection test now accepts… (#7859)
-
fix(cli): translate missing better-sqlite3 native… (#7868)
-
fix(auth): restore
HEALTHCHECK_BATCH_SIZEenv-var… (#7875) -
fix(opencode-plugin): the official
@omniroute/opencode-pluginnow accepts… (#7884) — @RaviTharuma -
fix(dashboard): Provider Quota cutoff inputs preserve unsaved values… (#7889)
-
fix(sse): Anthropic requests that reject a completed historical… (#7906)
-
Chat Core: client aborts that reject the upstream fetch with a raw…
-
fix(resilience): Client-aborted requests are now treated as client… (#7907)
-
fix(cli): spawn the win32
opencode.cmdshim with… (#7913) -
fix(dashboard): correct "Block extra Claude usage"… (#7918)
-
fix(autostart): Windows auto-start no longer flashes a console window —… (#7925)
-
fix(providers): perplexity-web now detects the… (#7930) — @artickc
-
fix(sse): parse Gemini 429 RetryInfo.retryDelay /… (#7940)
-
fix(electron): resolve macOS Helper by binary name to… (#7941)
-
fix(providers): iFlytek Spark (
iflytek,sparkdesk) now target the… (#7942) — @FenjuFu -
fix(api): classify
/api/acp/agentsas loopback-only… (#7948) -
fix(cli): stop double-prefixing combo model ids in the… (#7976)
-
fix(providers): route noauth opencode-zen connections… (#7993)
-
fix(providers): refresh duckduckgo-web free model… (#8000)
-
fix(providers): repoint the zai-web executor at… (#8014)
-
fix(sse): bound the Codex SSE peek/passthrough body… (#8020)
-
fix(sse): replace spoofable
.includes()PromptQL… (#8029) -
fix(providers): path-shaped multimodal model ids (e.g.… (#8032) — @Prudhvivuda
-
fix(cli): merge Node's own bin dir into the CLI… (#8036)
-
fix(sse): Bound forwarded upstream response headers so large… (#8041) — @insoln
-
fix(db): register SIGHUP handler and stop force-killing… (#8045)
-
fix(oauth): warn instead of silently opening an… (#8046)
-
fix(sse): run the prompt-compression pipeline per turn in the… (#8052)
-
fix(oauth): Refreshing the token on an email-less OAuth connection… (#8059)
-
fix(routing): anchor quota routing cache on globalThis… (#8065)
-
fix(providers): add the missing
poeREGISTRY entry so built-in Poe… (#8082) -
fix(translator): set
status: "completed"on… (#8083) -
fix(sse): give keyless Pollinations image requests the… (#8085)
-
fix(sse): stop Codex/Responses sanitizer from turning… (#8089)
-
fix(cli): surface the real spawn error… (#8091)
-
fix(antigravity): preserve request/tool fidelity, make credits modes… (#8098) — @nguyenha935
-
fix(dashboard): clamp provider-cooldown min/max ms to… (#8107)
-
fix(providers): filter unsupported family-fallback… (#8134)
-
fix(providers): classify HTTP 400 model-unavailable as… (#8136)
-
fix(backend): word-boundary-safe tool-result truncation… (#8169)
-
fix(api): narrow claudeClassifierCompat auto trigger so… (#8189)
-
fix(providers): carve cookie-auth providers out of… (#8200)
-
fix(gemini): drop HARM_CATEGORY_CIVIC_INTEGRITY from… (#8231)
-
fix(dashboard): Normalize Codex client URLs, restore the analytics… (#8233) — @nguyenha935
-
fix(providers): classify per-model-quota 403 and…
-
fix(providers): reconcile Kimi K3 vision metadata when synced… (#8250) — @Prudhvivuda
-
fix(providers): the Providers page Learn more button now opens the… (#8284) — @KaynXu
-
fix(api): fold namespace into the flattened Chat tool… (#8295)
-
fix(runtime): sanitize Muse Spark fetch failures before logging and… (#8298) — @backryun
-
fix(sse): Combo middleware preserves OpenAI Responses request… (#8310) — @ridho9
-
fix(api): accept the current compatible-provider… (#8326)
-
fix(api): stop leaking the internal provider UUID in… (#8327)
-
fix(dashboard): show custom provider_nodes providers in… (#8328)
-
fix(api): stop the 2000-token safety buffer from… (#8331)
-
fix(backend): keep combo routing from dispatching image… (#8332)
-
fix(sse): strip third-party-agent signals from the… (#8350)
-
fix(i18n): Restore brand/model proper nouns (Claude, OpenAI,… — @ikelvingo
-
fix(api): estimate inline base64 image tokens instead… (#8368)
-
fix(backend): stop prompt-cache affinity from silently… (#8370)
-
fix(api): accept a missing status query-param on GET… (#8374)
-
fix(resilience): treat an unreachable-proxy… (#8376)
-
fix(backend): make disabling the global per-key proxy… (#8385)
-
fix(dashboard): persist compression engine detail… (#8388)
-
fix(plugins): fire registered+active plugin hooks… (#8395)
-
fix(resilience): cap the connection cooldown after a… (#8396)
-
fix(api): reach synced model_capabilities rows for… (#8429)
-
fix(providers): stop marking a multi-quota-window… (#8431)
-
fix(backend): compute AgentBridge diagnose… (#8466)
-
fix(providers): OpencodeExecutor honors Extra API Keys rotation via… (#8467) — @Prudhvivuda
-
fix(sse): stop combo's "all targets failed" response… (#8486)
-
fix(backend): capability filters fail closed when every combo target… (#8488) — @Prudhvivuda
-
fix(providers): persist a runtime-discovered… (#8491)
-
fix(cli):
backup create/backup auto enable—… (#8512) -
fix(dashboard): Endpoint/API base URL display honors… (#8514) — @rqzbeh
-
fix(client): Absolute
fetch("/api/...")and… (#8515) — @rqzbeh -
fix(cli): create
~/.cache(and setXDG_CACHE_HOME)… (#8519) -
fix(api): surface a non-blocking warning + startup scan… (#8530)
-
fix(kiro): support profileless Builder ID quota, preserve CLI auth… (#8565) — @nguyenha935
-
fix(test): isolate
context-managerunit tests on a… (#8568) -
fix(quality):
check:file-size --updatenow removes baseline entries… (#8589) — @MumuTW -
fix(sse): restore Task-Aware Routing config from… (#8604) — @MumuTW
-
fix(sse): task-aware routing defaults use
auto/*intents… (#8605) — @MumuTW -
fix(providers): keep registered OpenCode Go effort aliases in the… (#8610)
-
fix(resilience): report local request-queue expirations as… (#8613)
-
fix(docker): Honor
OMNIROUTE_BASE_PATHbehind reverse-proxy… (#8615) — @DinonowDev -
fix(resilience): recover a wedged local request limiter after an… (#8616)
-
fix(providers): Kimi Coding billing-cycle quota errors now recover… (#8632) — @glazec
-
fix(oauth): show GitLab Duo OAuth app / env setup instructions in… (#8688)
-
fix(sse): combo compression-limit resolution falls back to… (#8716) — @DinonowDev
-
fix(db): fall back to Node's built-in SQLite driver when… (#8724) — @epsilonode
-
fix(resilience): keep missing request resources such as Files API ids… (#8756) — @wilsonicdev
-
fix(api):
GET /v1/modelsno longer rebuilds the whole catalog… (#6408 #8833) -
GHE Copilot: OpenAI-native models now route to
<gheUrl>/responses… (#8835) — @hppsc1215 -
CLI:
omniroute launchno longer truncates pass-through… (#8837) — @sumanxg -
Providers: Codex GPT-5.6 Sol/Terra/Luna report the current… (#8838) — @TitoTFP
-
Antigravity: a token refresh now recovers a missing
projectId… (#8842) — @HouMinXi -
CI:
check:tracked-artifactsraises thegit ls-files… (#8844) -
OAuth:
ANTIGRAVITY_OAUTH_CLIENT_TYPE=weblets a remote… (#8845) — @HouMinXi -
fix(executors): Vertex AI now routes current-generation Claude models… (#8852) — @wgordon17
-
CLI:
omniroute launch-codexno longer corrupts the codex… (#8856) — @sumanxg -
Providers: AGY model sync now discovers newly announced chat…
-
fix(security): bound JWT-extraction regexes in…
-
Fixed every non-streaming Codex (ChatGPT-account) chat…
-
Logging: a broken stdout/stderr pipe no longer drives a… (#8181)
-
docs: correct three stale references caught by the…
-
Fix the Turbopack
next buildbreaking with "Module… -
Fix
mitm-dnsConfigunit tests failing when the suite… (#6122) -
muse-spark-web: the WebSocket 401 auth-failure message…
-
Harden the Notion web provider thread-session cache…
-
fix(security): OIDC login gate now requires…
-
Build: the packed tarball boots again — #7191's…
-
Packaging: new
check:pack-bootgate packs the real npm tarball,… -
Packaging: pack-artifact closure tests now cover EVERY npm-shipped…
-
fix(cli): CLI detection now refreshes stale cached results,…
-
fix(docker): Podman deployment guidance now distinguishes local… (#8497)
-
fix(ui): Theme React Flow controls correctly in dark mode,…
-
fix(plugins): the generated plugin host script is now deleted…
-
fix(compression): the Headroom SmartCrusher tabular-compaction guard now… — @SingCJ
-
Skills: register
cli-skill-collectorin the agent-skills… -
fix(responses): Make Responses API to Chat Completions downgrades…
-
fix(stream): Responses API clients (
/v1/responses) no longer… (#4633) -
fix(security): bump adm-zip to ^0.6.0 (dev transitive…
-
chore(deps): bump fast-uri≥3.1.3, hono≥4.12.27,…
-
chore(deps): bump dompurify≥3.4.12,…
-
fix(quality): register the two covering unit tests…
-
fix(auth): restore the TICK_MS constant dropped by…
-
fix(guardrails/chat): do not whole-request-reroute…
-
Providers: yuanbao-web no longer forwards a foreign single cookie…
-
Antigravity: the shared token-refresh service now recovers a missing… (#8860) — @HouMinXi
-
Adobe Firefly:
gpt-imagerequests now defaultdetailLevelto… (#8863) — @artickc -
Combos: deleting a provider connection now clears the combo… (#8865) — @HouMinXi
-
Auto routing:
auto/<family>combos (auto/glm,auto/gemini,… (#8866) — @rafaeldrincon -
fix(oauth): wire Test Connection for xAI OAuth (
xai-oauth/… (#8862) — @allanvb -
Logs: a failed
auto/<family>request no longer writes every… (#8867) — @rafaeldrincon -
fix(usage): correct token/request counting for 30D/90D/YTD/ALL… (#7300) — @growab
-
fix(notion-web): use Chrome TLS impersonation for runInferenceTranscript (#8159) — @artickc
-
fix(bifrost): send v-prefixed transport version, not bare semver (#8194) — @seanford
-
fix(resilience,translator): three release/v3.8.49 base-red regressions + eslint… (#8254)
-
fix(sse): export PROVIDER_BREAKER_FAILURE_STATUSES — fix… (#8258)
-
fix(providers): limit Gemini CLI to legacy OAuth refresh (#8275) — @backryun
-
fix(cli): resolve claude.cmd on Windows in omniroute launch (#8283) — @Dingding-leo
-
fix(resilience): skip terminal connections in token health check sweep (#8286) — @Dingding-leo
-
fix(sanitizer): strip zero-width chars from Anthropic-native streaming… (#8287) — @Dingding-leo
-
fix(i18n): re-sync and complete es-ES translations with latest… (#8289) — @Dragost
-
fix(antigravity): add missing gemini-3.6-flash pricing rows to ag OAuth… (#8290) — @HouMinXi
-
fix(dashboard): stop sidebar RSC prefetch storms (#8292) — @RaviTharuma
-
fix(backend): add structure-aware chat admission (#8296) — @RaviTharuma
-
fix(providers): adapt Kimi nonstream requests internally (#8302) — @RaviTharuma
-
fix(mcp): keep POST SSE responses uncompressed (#8303) — @RaviTharuma
-
fix(api): enforce image generation API key auth (#8306) — @fenix007
-
fix(cpa): isolate credential-pool failures (#8308) — @RaviTharuma
-
fix(sse): suppress by default on Chat Completions (#8309) — @Prudhvivuda
-
fix(translator): cap thinking budget on explicit budget_tokens path (#8312) — @HouMinXi
-
fix(providers): classify HTTP 400 model-unavailable as MODEL_NOT_FOUND… (#8319)
-
fix(providers): carve cookie-auth providers out of terminal 401… (#8321)
-
fix(api): fold namespace into the flattened Chat tool name so… (#8322)
-
fix(providers): route noauth opencode-zen connections through their… (#8324)
-
fix(security): remove quadratic trailing-slash trim in Alibaba… (#8333)
-
fix(memory): self-heal upsertVector/deleteVector from a raced… (#8337) — @hartmark
-
fix(sse): stop stripInternalReasoningPlaceholder from eating… (#8341) — @hartmark
-
fix(compression): rank codex-responses in adaptive-ladder maps (#8381)
-
fix(sse): gate reasoning-placeholder strip to chunks that contain… (#8382)
-
fix(resilience): terminal-skip spares the recoverable GitHub Copilot… (#8389)
-
fix(sse): re-export PROVIDER_BREAKER_FAILURE_STATUSES for the… (#8390)
-
fix(sse): family auto-combos include any backend that serves the… (#8391)
-
fix(sse): cap exact cooldowns only when synthetic — verified… (#8393)
-
fix(devin-cli): update ACP JSON-RPC protocol for Devin CLI 3000.2.x… (#8425) — @MumuTW
-
fix(auth): exclude local CLI providers from tokenHealthCheck… (#8426) — @MumuTW
-
fix(oauth): add devin-cli and agy entries to OAUTH_TEST_CONFIG (#8427) — @MumuTW
-
fix(dashboard): preserve connection health visual on last routed… (#8428) — @MumuTW
-
fix(cursor): bridge native TodoWrite completions (#8432) — @makcimbx
-
fix(guardrails): Vision Bridge describe-fallback ignores unreachable… (#8433) — @guhcostan
-
fix(sse): record tool calls into shared state for… (#8462) — @hartmark
-
fix(oauth): actionable guidance for LAN-origin loopback mismatches… (#8463)
-
fix(hyperagent): sticky thread for agentic tool loops (Claude Code) (#8470) — @artickc
-
fix: combo input-bound, Responses->Chat image strip,… (#8476) — @herjarsa
-
fix(sse): call the real abort-signal helper in the Gemini… (#8485) — @backryun
-
fix(providers): honor Extra API Keys rotation in OpencodeExecutor (#8493) — @Prudhvivuda
-
fix(backend): fail closed when capability filters empty the combo pool (#8494) — @Prudhvivuda
-
fix(providers): resolve native vision for path-shaped multimodal model… (#8495) — @Prudhvivuda
-
fix(hyperagent): default 1M context for fable/opus/sonnet (#8496) — @artickc
-
fix(notion-web): mint fresh thread for new OpenAI sessions with same… (#8511) — @artickc
-
fix(db): classify compressionDetailNormalizers as db-internal in… (#8534) — @MumuTW
-
fix(sse): label HTTP 499 disconnects as client_disconnected (#8552) — @DinonowDev
-
fix(resilience): honor comboCooldownWait for every combo strategy (#8559) — @DinonowDev
-
fix(backend): add error.code to synthOpenAIErrorChunk for guard… (#8570) — @marceli1404
-
fix(video): validate Veo AI Free artifacts before success (#8581) — @brunnolouzada
-
fix(routing): exclude locked-out models from auto-combo candidates (#8586) — @Prudhvivuda
-
fix(api): prevent silent lost updates on concurrent settings… (#8587) — @Prudhvivuda
-
fix(auto-combo): short-circuit expandAutoComboCandidatePool when… (#8598) — @mikeiagents
-
fix(auth): tag internal/loopback-origin failed logins in the audit… (#8606) — @Prudhvivuda
-
fix(providers): declare explicit OpenCode plugin feature-flag defaults (#8608) — @Prudhvivuda
-
fix(ci): use webpack fallback for Node 26 compat build to stop… (#8611) — @Prudhvivuda
-
fix(claude): classify native subscription quota 429 (#8628) — @costaeder
-
fix(windows): request shell when spawning bare qoder binary name on… (#8633) — @Dingding-leo
-
fix(middleware): declare withInjectionGuard's context parameter optional (#8644) — @backryun
-
fix(providers): handle space-separated search queries via… (#8660) — @maxmad64bis
-
fix(dashboard): broken icon allignment in
SegmentedControl.tsx(#8679) — @0x2f0 -
fix(providers): deprecate Monster API provider (fixes #8676) (#8691) — @Dingding-leo
-
fix(quality): register the #8494 covering test in stryker… (#8692)
-
fix(sse): clamp max_tokens to the model output cap on every path (#8698)
-
fix(executors): pass TimeoutError reason to controller.abort() in 7… (#8699)
-
fix(api): serve /v1/models stale-first and sanitize its error… (#8703)
-
fix(sse): stop thrashing the provider prompt cache for… (#8705)
-
fix: repair five base-red failures on release/v3.8.49 (#8706)
-
fix(oauth): show GitLab Duo setup before authorize error (#8710) — @DinonowDev
-
fix: hydration mismatch, duplicate React keys, and missing… (#8723) — @Mananz90
-
fix(sse): hide synthetic OpenAI startup reasoning (#8729) — @rinseaid
-
fix(guardrails): check feature flag helper in PIIMasker to honor DB… (#8730) — @Dingding-leo
-
fix(sse): report a stream that completes without any content (#8732) — @backryun
-
fix(plugins): delete the generated host script synchronously so… (#8749) — @MumuTW
-
fix(sse): update ANTHROPIC_PING heartbeat data payload to… (#8762) — @Dingding-leo
-
fix(cloudflare-ai): sync missing free catalog models (fixes #8725) (#8763) — @Dingding-leo
-
fix(bedrock): preserve additionalModelRequestFields in converse… (#8764) — @Dingding-leo
-
fix(docs): use correct kebab-case CLI flags in OpenCode guide… (#8766) — @swingtempo
-
fix(db): repair the extra-migration-dirs test and env contract… (#8773)
-
fix(electron): use NEXT_DIST_DIR when stripping stale native modules (#8794) — @NBN-N3
-
fix(resilience): add UND_ERR_SOCKET to PROXY_UNREACHABLE_ERROR_CODES (#8795) — @Dingding-leo
-
fix(combo): fail open when strict context filter empties… (#8798) — @DinonowDev
-
fix(resilience): honor Cloudflare 1010 retryable:false — skip… (#8800) — @DinonowDev
-
fix(dashboard): restore Usage Model Breakdown column sorting (#8802) — @DinonowDev
-
fix(api): treat cx/* and codex/* as equivalent API-key model… (#8805) — @Prudhvivuda
-
fix(sse): pass real response payload into plugin onResponse hooks (#8806) — @Prudhvivuda
-
fix(types): export web executor types from their module (#8810) — @backryun
-
fix(types): preserve reasoning policy record shapes (#8811) — @backryun
-
fix(types): preserve compression detail config shapes (#8812) — @backryun
-
fix(types): declare virtual chaos combo config (#8815) — @backryun
-
fix(types): type memory skills injection logger (#8816) — @backryun
-
fix(types): align web fallback contracts (#8819) — @backryun
-
fix(types): declare idempotency input contracts (#8820) — @backryun
-
fix(reasoning): sanitize streamed K3 think tags (#8821) — @rinseaid
-
fix(types): normalize Kie transcription results (#8824) — @backryun
-
fix(dashboard): surface quota pool delete failures instead of failing… (#8829)
-
fix(autoCombo): normalize -free suffix in task fitness lookups (#8636) — @Dingding-leo
-
fix(sse): split the reasoning-decision guards so the error arm… (#8641) — @backryun
-
fix(openai-to-claude): skip empty reasoning_content string deltas (#8642) — @Dingding-leo
-
fix(autoCombo): match longest pattern first in lookupStaticFitnessTable (#8651) — @Dingding-leo
-
fix(providers): sync search query to URL query param (#8652) — @Dingding-leo
-
fix(maritalk): refresh supported model catalog (#8663) — @backryun
-
fix(agentSkills): scope command slice before nested subcommands to… (#8667) — @Dingding-leo
-
fix(guardrails): ensure request PII masking respects DB feature flag… (#8715) — @Dingding-leo
-
fix(reasoning): sanitize Kimi Code K3 think tags (#8721) — @rinseaid
-
fix(sse): clamp out-of-range Gemini thinking_budget and learn the… (#8726) — @fuko2935
-
fix(providers): support multi-token search in provider filter (#8731) — @Dingding-leo
-
fix(api): stop JSON.stringify-ing messages before estimateTokens… (#8740)
-
fix(quality): let --update remove baseline entries that already fit… (#8741) — @MumuTW
-
fix: escape angle brackets in i18n messages to prevent… (#8747) — @SteeleHu
-
fix(dashboard): the /home quick-start cards no longer prefetch — #8292…
-
fix(dashboard): Request Logs detail no longer crashes on a structured…
-
fix(dashboard): the logs detail modal stops reopening on first close…
📚 Docs
-
docs(quality): codify retry policy per runner + release-level drift… (#7107)
-
docs(troubleshooting): document Avast/AVG README.md false positive (#7295)
-
docs(perf): add per-endpoint p50/p95/p99 latency + cost budget… (#7336) — @KooshaPari
-
docs: refresh revoked Discord invite + WhatsApp Brasil link (#7604)
-
docs(readme): animated SVG for the 4-tier auto-fallback cascade (#7615)
-
docs: sync provider count to 259 (unblocks docs-counts strict… (#7616)
-
docs(readme): animate pool + combo ASCII blocks as SMIL SVG diagrams (#7626)
-
docs(readme): animate CLI command list + compression flow as SMIL SVGs (#7637)
-
docs(readme): replace free-tier budget mockup with animated SMIL card (#7665)
-
docs(readme): standardize all README tables to full content width (#7666)
-
docs: fix three stale references failing the fabricated-docs… (#7728)
-
docs(readme): unified animated card system — audited v3.8.49 numbers,… (#7769)
-
docs(readme): add Kimi (Moonshot AI) official supporter section (#7770)
-
docs(getting-started): reorder Verify It Works before IDE/CLI setup + add… (#7790) — @swingtempo
-
docs(readme): audit every number against the live code + refresh… (#7795)
-
docs(readme): evolve supporter section into sub2api-style Sponsors… (#7799)
-
docs(readme): contributors 360+ -> 350+ (audited) (#7803)
-
docs(i18n): refresh Polish README and fix relative links (#7807) — @leszek3737
-
docs: add general Web Cookie provider setup guide (#7881) — @arpit-jaiswal-dev
-
docs(guides): document Kaspersky PDM behavioral false positive on the… (#7923)
-
docs: document npm install ERESOLVE/peer/deprecated warnings… (#7988) — @Dingding-leo
-
docs: fix Docker IPv6 connection reset with -p 127.0.0.1 bind… (#7989) — @Dingding-leo
-
docs(readme): Kimi partner tracking links (aff=omniroute) +… (#8028)
-
docs: add AgentRouter multi-provider routing troubleshooting (#8049) — @leninejunior
-
docs(security): correct prompt-injection severity table +… (#8113) — @rafaumeu
-
docs(ops): publish public branching and release model (#8129) — @c4usal
-
docs(i18n): full Russian README rewrite (#8217) — @MonteNegroX
-
docs(readme): re-audit numbers, fix table scroll, refresh contributors (#8243)
-
docs(db): propose pluggable persistence boundary (#8261) — @xiaoyaner0201
-
docs(db): add reproducible SQLite coupling inventory (#8262) — @xiaoyaner0201
-
docs: add public ROADMAP (3.8.5x rail -> 3.9.0 LTS -> 4.0… (#8348)
-
docs: sync env-var contract (chaos panel, notion TLS, grok… (#8362)
-
docs: one golden path across PR template, CONTRIBUTING,… (#8380)
-
docs(claude-code): document unprefixed model IDs and the Ambiguous model… (#8410) — @dvirarad
-
docs(podman): clarify Podman Machine deployment (#8569) — @shixi-li
-
docs(env): document NEXT_PUBLIC_OMNIROUTE_BASE_PATH and… (#8690) — @MumuTW
-
docs(codex): document session affinity and stream idle for long tasks (#8709) — @DinonowDev
-
docs: replace outdated Polish docs with translation from… (#8823) — @leszek3737
-
docs: restore the Polish API_REFERENCE removed by #8823 (#8831)
🧪 Tests & Quality
-
test(build): derive pack-artifact closures for all npm-shipped… (#7081)
-
test(dashboard): dedicated regression guard for #6815 density guarantee (#7291)
-
test(ci): make #6634 selfref guard hermetic — read file from… (#7327)
-
test(ci): mock route bridge surfaces error message, not raw stack (#7354)
-
test(ci): static body in codex e2e mock route bridge (CodeQL #737) (#7558)
-
test(ci): exact-line assert in grok-build config test (CodeQL… (#7628)
-
test(codex): cover image tool output replay (#7704) — @dongwook-chan
-
test(security): exact SAN-entry match in mitm leaf-cert test (CodeQL… (#7824)
-
test: verify keepalive interval cleanup on disconnect,… (#8190) — @rafaumeu
-
test: realign catalog snapshot tests to current deliberate… (#8386)
-
test: hermetic notion thread-session mocks + drop duplicated… (#8392)
-
test(e2e): contract test for the full provider journey (#8444) — @HoneyTyagii
-
test(sse): repair two base-red gates on release/v3.8.49 (#8490) — @backryun
-
test(context): isolate context-manager suite from local DATA_DIR (#8596) — @DinonowDev
🔧 Chores / CI
-
chore(release): gate the sync-back push on release-green --quick (WS0.3) (#7083)
-
chore(ci): gate hygiene — secrets baseline 0, semgrep drop,… (#7099)
-
chore(ops): runner-box janitor + operations runbook (WS3.3) (#7115)
-
chore(ci): promote test:vitest:ui to blocking (suite green after… (#7147)
-
chore(ci): stop dependabot proposing typescript majors —… (#7306)
-
chore(release): script the 0a.0b PR re-home with a verified read-back (#7312)
-
chore(quality): tighten the coverage ratchet to the CI's real numbers (#7326)
-
chore(ci): make the Electron Windows leg advisory with bash stderr… (#7340)
-
chore(deps): bump actions/setup-node from 6 to 7 (#7348)
-
chore(deps): bump codecov/codecov-action (#7350)
-
ci(release-green): add a main-green arm to detect when main goes red (#7355)
-
chore(deps): bump github/codeql-action/analyze from 4.37.0 to 4.37.1 (#7641)
-
chore(deps): bump github/codeql-action/init from 4.37.0 to 4.37.1 (#7642)
-
chore(quality): register #6672 test in stryker tap.testFiles (base-red… (#7652)
-
chore(release): merge-train box-speed suite + --fast mode (#7670)
-
chore: [defer] fix embedded CLIProxyAPI config handling (#6877) — @professional-ALFIE
-
chore: [defer] fix(grok): align responses tool-call shape for… (#6937) — @CitrusIce
-
chore: [needs-vps] feat(dashboard): add per-operator quota row… (#7251)
-
chore: [defer] feat(combo): universal cooldown-aware retry &… (#7301) — @ViFigueiredo
-
chore: Completing Arabic language (#7686) — @mustafa-phd
-
chore: IC2: Cache provider connections by ID + provider nodes (#7744) — @oyi77
-
chore: [Emergency Fix] fix(build): repair release build… (#7772) — @backryun
-
chore: [Part 1/3]refactor(qwen): replace legacy Qwen Code and… (#7866) — @backryun
-
chore: [Part 3/3] feat(qwen): add regional Alibaba and Qwen… (#7882) — @backryun
-
chore: Preserve supported Responses behavior in Chat… (#7894) — @JxnLexn
-
chore: Restore Responses API custom tool calls (#7905) — @JxnLexn
-
chore: Hide internal reasoning replay placeholders (#7912) — @JxnLexn
-
chore(deps): bump js-yaml, brace-expansion, shell-quote, tar… (#7915)
-
chore: i18n(zh-TW): complete Traditional Chinese (Taiwan)… (#8024) — @lunkerchen
-
chore: i18n: bring 40 locales to full parity with en.json (#8031) — @nguyenha935
-
chore(deps): resolve 7 open Dependabot alerts via npm overrides (#8066)
-
chore(deps): resolve 3 more Dependabot alerts (dompurify,… (#8069)
-
chore(dashboard): reframe Kimi partnership as "Open Source Friends" (#8117)
-
chore(quality): fix 2 pre-existing lint/suppression drift issues (#8209) — @hartmark
-
chore: add K3banner-1.png banner asset (#8242)
-
chore(quality): rebaseline accountFallback+combo for #8252 own-growth… (#8252) — @RaviTharuma
-
optimize(chaos+ponytail): i18n ponytail, dedupl dispatch, provider diversity (#8264) — @Moseyuh333
-
chore(deps): bump next to 16.2.11 (9 security advisories) (#8265)
-
chore: Add Alibaba-family media model support (#8266) — @backryun
-
chore: Clicking a provider card hitting back loses scroll… (#8349) — @swingtempo
-
chore: feat(log): Added new visual scrolling log page (#8354) — @hartmark
-
chore: [BUG] enforceOutputTokenBudget ignores combo-resolved… (#8378) — @RCrushMe
-
chore(ci): cancel superseded runs, skip DAST on docs-only PRs,… (#8379)
-
chore(quality): drop stale muse-spark-web allowlist entry + sync… (#8383)
-
chore: i18n(zh-TW): translate missing Reasoning Routing strings (#8423) — @MumuTW
-
chore(deps): bump codecov/codecov-action from 5.5.5 to 7.0.0 (#8452)
-
chore(deps): bump github/codeql-action/analyze from 4.37.1 to 4.37.3 (#8453)
-
chore(deps): bump ossf/scorecard-action from 2.4.3 to 2.4.4 (#8454)
-
chore(deps): bump github/codeql-action/init from 4.37.1 to 4.37.3 (#8455)
-
chore(deps): bump actions/setup-python from 6 to 7 (#8456)
-
chore(sse): drop deprecated baseUrl from open-sse tsconfig for TS… (#8473) — @backryun
-
refactor(sse): resolve open-sse utils/translator type diagnostics for… (#8483) — @backryun
-
refactor(sse): declare the executor execute() result contract (#8489) — @backryun
-
refactor(sse): stop three executors shadowing BaseExecutor.buildHeaders (#8498) — @backryun
-
refactor(sse): narrow three result unions via type predicates (#8499) — @backryun
-
refactor(sse): restore three executor types the runtime already relied… (#8520) — @backryun
-
refactor(dashboard): clear file-size base-red by extracting provider-card… (#8524) — @MumuTW
-
refactor(guardrails): narrow the documented void return at the dispatch site (#8525) — @backryun
-
refactor(sse): type the rerank response adapter's options parameter (#8528) — @backryun
-
refactor(sse): retag the designer-web result unions with string… (#8531) — @backryun
-
refactor(sse): declare the multipart/gRPC frame bodies as… (#8533) — @backryun
-
chore(ci): resync stale no-explicit-any suppression count for… (#8544) — @MumuTW
-
chore(usage): decompose services/usage.ts into per-provider usage/*… (#8545) — @MumuTW
-
chore(validation): decompose providers/validation.ts into validation/*… (#8546) — @MumuTW
-
chore(token-refresh): decompose services/tokenRefresh.ts into tokenRefresh/*… (#8547) — @MumuTW
-
chore(combo): extract pure error predicates and quota status helpers… (#8548) — @MumuTW
-
chore(i18n): normalize zh-TW terminology and sync stale README… (#8554) — @MumuTW
-
refactor(sse): stop isClaudeEventPayload claiming a narrowing it never… (#8557) — @backryun
-
chore(quality): rebaseline file-size for inherited base growth (#8561) — @fenix007
-
chore(deps): patch js-yaml + postcss for 2 high Dependabot alerts (#8572)
-
refactor(tls): update TLSClient instantiation to use… (#8583) — @DinonowDev
-
refactor(sse): peel the bare Response off handleChatCore's union in… (#8647) — @backryun
-
refactor(sse): guard the KIE task-id and callback-url reads at their… (#8661) — @backryun
-
refactor(sse): declare the ArrayBuffer backing on media byte producers (#8665) — @backryun
-
merge: resolve conflicts for #7904 local corpus context… (#8685) — @terrafirmbot-source
-
chore(quality): update baselines after v3.8.49 merge-train (#8686)
-
chore: Dependabot updates (#8695) — @justdoGIT
-
chore: Create AMIT (#8727) — @amitgolan60-coder
-
chore(ci): add release PR build gate (#8735) — @ekinnee
-
chore: Stabilize Notion web sessions and JSON output (#8751) — @0xheycat
-
deps: bump electron from 43.1.1 to 43.2.0 in /electron (#8782)
-
deps: bump the production group across 1 directory with 18… (#8793)
-
refactor(sse): retag the Adobe Firefly IMS token-check union (#8638) — @backryun
-
refactor(db): let the provider-nodes cache keep the row type it… (#8639) — @backryun
-
refactor(sse): use the shared ApiKeyMetadata in reasoningRouting… (#8643) — @backryun
-
refactor(sse): narrow three media-generation result unions (#8645) — @backryun
-
refactor(sse): declare the semantic-cache read path's parameters (#8646) — @backryun
🔀 Other
-
[needs-vps] fix(electron): materialize Turbopack… (#6794) — @huohua-dev
-
[codex] Keep mode-pack weights consistent in auto… (#7008) — @KooshaPari
-
Explain effective auto-combo scoring weights (#7087) — @KooshaPari
-
[needs-vps] fix(dashboard): add vision-capability… (#7124)
-
[needs-vps] fix(dashboard): align onboarding tier… (#7125) — @Wibias
-
Use OpenAI chunks for early chat keepalives (#7136) — @KooshaPari
-
Fix Codex Responses compression analytics (#7273) — @JxnLexn
-
Add cache-aligned Live Zone compression (#7280) — @JxnLexn
-
Fix routed target request parameters (#7323) — @JxnLexn
-
deps: bump electron from 43.1.0 to 43.1.1 in /electron (#7349)
-
deps: bump the development group with 8 updates (#7351)
-
deps: bump the production group across 1 directory with… (#7352)
-
Add per-connection Provider Quota visibility (#7360) — @JxnLexn
-
Stream model health probes for slow providers (#7377) — @JxnLexn
-
Refresh NVIDIA free metadata and detect catalog drift (#7378) — @JxnLexn
-
Reject invalid output token budgets (#7379) — @JxnLexn
-
Restore proxy navigation and sidebar accordion state (#7381) — @JxnLexn
-
Expose proxy controls for no-auth providers (#7419) — @JxnLexn
-
Add reasoning-based model and effort routing (#7607) — @JxnLexn
-
refactor(sse): extract per-provider token-refresh functions from… (#7817)
-
deps: bump the production group with 8 updates [bot] (#7897) — @dependabot
-
deps: bump the development group with 5 updates [bot] (#7898) — @dependabot
-
refactor(antigravity): align official clients and callable catalog (#8013) — @backryun
-
refactor(compression): extract resolveHeadroomDetail to keep… (#8058)
-
deps: bump next from 16.2.10 to 16.2.11 [bot] (#8235) — @dependabot
🩹 Direct release-branch fixes (no PR — authorized base-red sweep, 2026-07-18)
- fix(base-red): full-suite realignment after the 102-PR merge campaign:…
- chore(release-branch): cycle maintenance pushed directly to
release/v3.8.49… - test(ui): the
vitest:uisuite was red across the whole cycle…
📝 Maintenance
- Merge-train script: (
scripts/release/merge-train.sh): batch-validates N… (#6784) - release:
list-uncovered-commits.mjsnow unions the CHANGELOG… (#6857 #6878) - chore(ci): stop dependabot from proposing
typescriptmajors —… - maintenance(quality): re-baseline
file-sizefor two legitimately-grown… - fix(i18n): backfill the four
usage.*quota-visibility keys #7251… - docs(codex): document session affinity (
sessionAffinityTtlMs) and… (#7287) - test(dashboard): restore dedicated regression coverage for #6815's… (#7291)
- Antivirus false-positive note: (
docs/guides/TROUBLESHOOTING.md): documents why… - chore(release): add
scripts/release/rehome-open-prs.mjs— the Phase… (#7307) - Add an advisory production build to the PR-to-release…
- docs: Add
docs/INCIDENT_RESPONSE.md— a non-security… (#7334) — @KooshaPari - docs: Add
docs/PERF_BUDGETS.md— per-endpoint p50/p95/p99… (#7336) — @KooshaPari - README tier-cascade diagram animated: (
docs/diagrams/tier-cascade.svg): the ASCII 4-tier… (#7615) - Docs provider-count sync: (
README.md,AGENTS.md,CLAUDE.md): provider-count… (#7616) - README animated diagrams: (
docs/diagrams/pool-fair-share.svg,… (#7615) - docs(ops): publish public branching/release model (
release/*=… (#7627) — @c4usal - README animated diagrams, round 3: (
docs/diagrams/cli-terminal.svg,… - README: replaced the free-tier budget preview mockup… (#7665)
- README: standardized all 28 tables to the same full… (#7666)
- Realigned 13 test files that had drifted red on the… (#7690)
- README: unified animated card system — numbers audited… (#7769)
- chore(quality): fix two release-tip base-reds blocking every fresh PR…
- fix(i18n): backfill the six
providers.tierOverride*keys #7838… - chore(quality): regenerate the provider translate-path golden snapshot…
- chore(quality): prune a stale ESLint suppression for…
- Extract
resolveHeadroomDetail()from the… - chore(quality): rebaseline complexity (2130→2183) and… (#3501 #8566) — @MisileLab
- refactor(sse): extract combo dispatch prelude… (#8582) — @MumuTW
- chore(quality): bank completed file-size shrinks into… (#8585) — @MumuTW
- refactor(sse): extract combo target resolution into… (#8592) — @MumuTW
- Source-scanner negative guards: (
tests/unit/source-scanner-guards.test.ts): hard gate… (#8619) — @MumuTW - chore(skills): regenerate
skills/cli-backup-sync/SKILL.mdso it… (#8657) — @MumuTW - chore(quality): extract pure provider input parsers to…
- Realign the Antigravity non-streaming 429-retry test…
- chore(quality): restore no-explicit-any severity to…
- chore(base): pt-BR/en i18n keys for #6909…
- chore(base): re-export relayProbeStats from localDb…
- fix(tests): realign the disabled-compression combo…
- CI:
pr-test-policyfetches the base branch with full… - ci: unit fast-path sharding doubled 2→4 (halves the… (#6781 #6691 #6693)
- CI: Codecov patch-coverage on every PR diff (informational…
- Classify upstream dispatches by host instead of URL…
- chore(combo): extract 8 pure error predicates and quota status…
- CI: raise the Coverage job timeout 10→20min — the lcov… (#7114)
- CI: raise the dast-smoke job timeout 12→25min — the CLI…
- chore(validation): decompose…
- chore(token-refresh): decompose…
- chore(usage): decompose
open-sse/services/usage.ts… - docs: routing-strategy count reconciled to 18 across… (#6663) — @chirag127
- CI: E2E matrix shards are now duration-balanced (LPT…
- CI: the new Electron Windows prepare-bundle leg (WS1.5) is…
- CI: the Electron Package Smoke job now runs a Windows leg…
- Quality gates hygiene (WS6/D3 + WS1.7): gitleaks baseline zeroed — the 3 frozen…
- fix(oauth): register
ghe-copilotin the OAuth… - fix(dashboard): add the ghe-copilot entry to the…
- chore(perf): add
npm run bench:heap-body— a… - CI: hotfix fast-lane — PRs labeled
hotfix(owner-applied,… - chore(ci): resync the stale
no-explicit-any… - chore(release): merge-train runs the box-tuned…
- chore(release): merge-train
--fastnow classifies changed tests with… - CI: quality.yml draft guards now also match Mergify…
- Merge queue (D5): reviewed PRs now merge through the Mergify queue…
- Release: npm publishing is now STAGED by default — the workflow…
- chore(release): v3.8.47 pre-flight — relocate #6943…
- Docs:
QUALITY_GATES.mdnow codifies the per-runner test… - chore(quality): owner-approved ratchet rebaseline for…
- docs(readme): fix stale counts — 18 routing strategies (adds the…
- Rebaseline the cyclomatic-complexity ceiling (2059 →…
- CI: release-green validation is now continuous — every code…
- chore(ci): fix two shared base-reds on the release tip that…
- docs: refresh
llm.txtto the current project state (248… - Strengthen the
compareRouterEvalRunsregression test… - Ops:
scripts/ops/runner-janitor.sh+… - chore(security): scrub hardcoded live-instance credentials (API key +…
- Tests: the #6634 self-reference test now fetches
origin/main… - Register 5 covering unit tests (account-fallback…
- chore(quality): register microsoft-designer-web-6672…
- Release tooling:
sync-next-cycle.mjsnow runs `validate-release-green… - Rebaseline
testFrozenfor… - chore(quality): tighten the coverage ratchet to the CI's real numbers…
- chore(quality): split
open-sse/services/tokenRefresh.ts(2249 lines,… - chore(quality): re-pin the file-size ceilings touched…
- chore(quality): re-pin the
chatCore.tsfile-size… - chore(quality): align the pack-artifact expected-path…
- chore(quality): re-pin the
apiKeys.tsand… - CI: Playwright E2E and both vitest suites now emit JUnit…
- CI: the fast-path Vitest job (every PR) now also emits…
- CI: TypeScript 7 (native compiler, GA 2026-07-08) now runs…
- maintenance(quality): clear v3.8.49 owner-PR-campaign base-reds on…
- Release tooling: new
scripts/release/verify-published.mjs <version>—… - CI: promote
test:vitest:uito a blocking gate — the suite… (#7127) - chore(tests): fix all 70 failing
test:vitest:uitests…
🙌 Contributors
| Contributor | PRs / Issues |
|---|---|
| @0x2f0 | #8679 |
| @0xheycat | #8751 |
| @adevwithpurpose | #8123 |
| @adrianaryaputra | #7928 |
| @advane204f | direct commit / report |
| @Ajeesh25353646 | #7528 |
| @allanvb | #8471, #8759, #8814, #8862 |
| @alltomatos | #6703, #6715, #6756, #6757, #6759, #6813, #6819, #6821, #7041, #7042, #7164, #7277, #7490, #7492, #7644 |
| @alvaretto | #8077, #8161, #8170 |
| @amitgolan60-coder | #8727 |
| @andrea-kingautomation | #7678 |
| @andrewmunsell | #6774, #6779, #6795 |
| @AndrianBalanescu | #6828, #6829, #7794, #7804, #7810, #7813, #7816, #7891, #8050 |
| @anhdiepmmk | direct commit / report |
| @anndev-69 | direct commit / report |
| @apoapostolov | #8127 |
| @arpit-jaiswal-dev | #7881 |
| @artickc | #6763, #6955, #7204, #7696, #7768, #7896, #7900, #7911, #7930, #7994, #8006, #8159, #8470, #8496, #8511, #8863 |
| @Arul- | #7878 |
| @asynx6 | direct commit / report |
| @attid | #6984 |
| @backryun | #6280, #6675, #6862, #7296, #7314, #7358, #7531, #7687, #7772, #7812, #7866, #7874, #7882, #7914, #8013, #8225, #8226, #8227, #8230, #8266, #8275, #8298, #8464, #8473, #8483, #8485, #8489, #8490, #8498, #8499, #8520, #8525, #8528, #8531, #8533, #8557, #8638, #8639, #8641, #8643, #8644, #8645, #8646, #8647, #8661, #8663, #8665, #8732, #8810, #8811, #8812, #8815, #8816, #8819, #8820, #8824 |
| @beingshafin | direct commit / report |
| @brick30llc-ctrl | #6944 |
| @brunnolouzada | #8581 |
| @c4usal | #7627, #8129 |
| @CahyokPutraDev99 | direct commit / report |
| @Capslockb | #7892 |
| @CarmeloCampos | direct commit / report |
| @Chewji9875 | #7545 |
| @chirag127 | #6022, #6593, #6643, #6644, #6646, #6650, #6769, #6771, #6804, #7079, #7520, #8143 |
| @chitholian | direct commit / report |
| @chy1211 | direct commit / report |
| @CitrusIce | #6937, #6938 |
| @costaeder | #8628 |
| @Dan-ex-hub | #7743 |
| @danscMax | #7359, #7511, #7517, #7648, #7656, #7672, #7689 |
| @dependabot | #7897, #7898, #8235 |
| @Dingding-leo | #7988, #7989, #8162, #8165, #8167, #8283, #8286, #8287, #8633, #8636, #8642, #8651, #8652, #8667, #8691, #8715, #8730, #8731, #8762, #8763, #8764, #8795 |
| @DinonowDev | #8552, #8559, #8583, #8596, #8615, #8709, #8710, #8716, #8798, #8800, #8802 |
| @dionisius95 | direct commit / report |
| @DKotsyuba | #7500 |
| @dongwook-chan | #7574, #7582, #7704 |
| @Dragost | #8289 |
| @Duongkhanhtool | direct commit / report |
| @dvirarad | #8410 |
| @ekinnee | #7601, #7613, #7614, #7662, #7779, #7927, #7932, #7980, #8735 |
| @enjoyer-hub | #6647, #7863 |
| @epsilonode | #8724 |
| @evinjohnn | direct commit / report |
| @fajarbossit | direct commit / report |
| @felipeleite | direct commit / report |
| @fenix007 | #7171, #7399, #8306, #8561 |
| @FenjuFu | #7942 |
| @floze-the-genius | #7707 |
| @fontvu | direct commit / report |
| @fuko2935 | #8726 |
| @fzrilsh | direct commit / report |
| @gitcommit90 | #6986 |
| @glazec | #8632 |
| @growab | #7062, #7300 |
| @guanbear | #7028 |
| @guhcostan | #8433 |
| @hartmark | #8208, #8209, #8210, #8211, #8212, #8213, #8337, #8341, #8354, #8462 |
| @HassiyYT | #7864 |
| @heishen6 | direct commit / report |
| @herjarsa | #7612, #7625, #7633, #7869, #7871, #8476 |
| @HoneyTyagii | #8444 |
| @HouMinXi | #7035, #7129, #7290, #7398, #7408, #7973, #8290, #8312, #8842, #8845, #8860, #8865 |
| @hppsc1215 | #7546, #8835 |
| @huohua-dev | #6794 |
| @hydraxman | #7909 |
| @iamraydoan | #6798 |
| @ianriizky | #6072, #6538 |
| @ikelvingo | #8355 |
| @insoln | #7906, #7908, #8041, #8054, #8062 |
| @irvandikky | #7695 |
| @isiahw1 | #7555 |
| @itiwant | direct commit / report |
| @janeza2 | #6308 |
| @Jordannst | direct commit / report |
| @JoshimOfficial | #8526 |
| @justdoGIT | #8695 |
| @JxnLexn | #6776, #6993, #7154, #7177, #7269, #7273, #7280, #7281, #7282, #7323, #7360, #7377, #7378, #7379, #7380, #7381, #7419, #7607, #7894, #7905, #7912, #8008, #8009, #8010 |
| @kamenkadmitry | #7350, #7425 |
| @kaon0388v1 | #7049 |
| @KaynXu | #8284 |
| @KooshaPari | #6611, #6632, #6856, #7008, #7087, #7093, #7128, #7130, #7136, #7315, #7318, #7334, #7336 |
| @KunN-21 | direct commit / report |
| @leninejunior | #8049 |
| @leszek3737 | #7782, #7807, #8343, #8543, #8823 |
| @like3213934360-lab | direct commit / report |
| @linhdmn | #8439 |
| @Long-Feeds | #8011 |
| @loulanyue | #7540 |
| @lucasjustinudin | direct commit / report |
| @lunkerchen | #8024 |
| @makcimbx | #7692, #8171, #8432 |
| @Mananz90 | #8723 |
| @marceli1404 | #8570 |
| @maxmad64bis | #8660 |
| @megamen32 | #7313 |
| @MichaelYcJo | #8224 |
| @mikeiagents | #8598 |
| @MikeTuev | #6586 |
| @mikolaj92 | #6973 |
| @MisileLab | #8566 |
| @MonteNegroX | #8217 |
| @Moseyuh333 | #7781, #8264 |
| @MrFadiAi | #7073 |
| @mrprohack | direct commit / report |
| @MumuTW | #8423, #8425, #8426, #8427, #8428, #8524, #8534, #8544, #8545, #8546, #8547, #8548, #8554, #8582, #8585, #8589, #8592, #8604, #8605, #8612, #8619, #8657, #8690, #8741, #8749 |
| @mustafa-phd | #7686 |
| @NBN-N3 | #8794 |
| @nguyenha935 | #7493, #7547, #7552, #7553, #7629, #7935, #8031, #8098, #8233, #8565 |
| @nguyenphi37 | direct commit / report |
| @not-knope | #8206 |
| @nramabad | #7926 |
| @oyi77 | #6917, #6918, #6919, #6920, #6921, #6923, #7032, #7045, #7046, #7066, #7070, #7178, #7719, #7744, #7787, #7893, #8219 |
| @Pitchfork-and-Torch | #6747, #6791 |
| @professional-ALFIE | #6877 |
| @Prudhvivuda | #8032, #8220, #8250, #8309, #8441, #8467, #8488, #8493, #8494, #8495, #8586, #8587, #8606, #8607, #8608, #8611, #8805, #8806 |
| @QRcode1337 | #7034 |
| @quanturbo | #6780 |
| @rafaeldrincon | #8866, #8867 |
| @rafaumeu | #6813, #6979, #6982, #6983, #6987, #6988, #7001, #7808, #7815, #8071, #8113, #8179, #8184, #8185, #8190, #8195, #8196, #8203 |
| @RaviTharuma | #7852, #7853, #7855, #7862, #7885, #7972, #7978, #8021, #8022, #8023, #8025, #8027, #8030, #8101, #8102, #8124, #8252, #8292, #8296, #8301, #8302, #8303, #8304, #8308 |
| @RCrushMe | #8151, #8378 |
| @ricatix | direct commit / report |
| @ridho9 | #8310 |
| @rinseaid | #8721, #8729, #8821 |
| @rixzkiye | direct commit / report |
| @rqzbeh | #8514, #8515 |
| @rushsinging | #7256 |
| @ryanngit | direct commit / report |
| @samir-abis | direct commit / report |
| @seanford | #8194, #8218, #8232 |
| @SeaXen | #7063, #7264, #7294 |
| @Securiteru | #7683 |
| @SemonCat | direct commit / report |
| @shixi-li | #8569 |
| @SingCJ | direct commit / report |
| @skutanjir | #7865, #7939 |
| @spacesky-cell | direct commit / report |
| @SteeleHu | #8747 |
| @sumanxg | #8837, #8856 |
| @swingtempo | #7790, #8349, #8766 |
| @Tasogarre | #7861, #8207 |
| @techsolutionmta | direct commit / report |
| @tenshiak | #7274, #7643 |
| @terrafirmbot-source | #8685 |
| @thepigdestroyer | #7497 |
| @tianrking | #7353 |
| @tientien17 | #7841, #7844, #7925 |
| @TitoTFP | #8838 |
| @tjengbudi | #4009 |
| @tmone | #7806, #7933 |
| @TrackCrewGalore | #6271, #8128 |
| @trfi | direct commit / report |
| @TuyulSpam | direct commit / report |
| @ViFigueiredo | #7301 |
| @vzts | #7390 |
| @warelik | direct commit / report |
| @way-art | direct commit / report |
| @webmasterarbez | #7504 |
| @wgordon17 | #8852 |
| @whale9820 | direct commit / report |
| @Wibias | #7125 |
| @wilsonicdev | direct commit / report |
| @Witroch4 | #6753, #6762, #6790, #7901, #7902 |
| @XCrag | direct commit / report |
| @xiaoyaner0201 | #8122, #8261, #8262 |
| @xier2012 | #7036, #7050, #7052, #7053, #7056, #7059, #7060, #7061, #7166, #7299 |
| @xxue-z | #7098 |
| @xz-dev | #6323, #6330, #6714, #6727, #7004, #7012, #7027, #7673, #7700, #7747, #7776, #7843 |
| @yidecode | direct commit / report |
| @yinaoxiong | #6805 |
| @diegosouzapw | maintainer |
v3.8.48
2026年07月14日
⚠️ Hotfix release. The published npm package for 3.8.47 crashed on every boot (#7065) and was deprecated — 3.8.48 is the first installable release of the v3.8.47 cycle, so everything listed under [3.8.47] below ships here.
🐛 Bug Fixes
- fix(build): ship
dist/head-response-guard.cjsin the npm tarball — the prepublish prune allowlist lacked it, so everyomnirouteboot of the published 3.8.47 crashed withERR_MODULE_NOT_FOUND(3rd occurrence of this class after tls-options/3.8.41); now allowlisted, enforced bycheck:pack-artifact, and guarded by a closure test that derives everyserver-ws.mjssibling import (#7065, #7040) - fix(build): Electron Windows packaging — the better-sqlite3 Electron-ABI rebuild now spawns
npx.cmdthrough a shell (Node's CVE-2024-27980 hardening made the shell-less spawn fail withstatus nullon Windows runners, breaking the v3.8.47 desktop build) - fix(ci): Sonar quality gate zeroed on new code — the coverage lcov now reaches the scanner at
coverage/lcov.info(it read 0% on every scan), the asyncisCloudEnabled()gate in the Kiro auto-import route is awaited (cloud sync ran even when disabled), the deadstructuredClonefallback in the reasoning-split clone is a real JSON fallback, the codex executor handles the asyncreader.cancel()rejection, deterministiclocaleComparesorts, a path-traversal guard inclassify-pr-changes.mjs, and the Docker better-sqlite3 rebuild uses npm's bundled node-gyp instead ofnpx --yes - chore(ci): the Sonar quality gate is informational (
sonar.qualitygate.wait=false) while the org's SonarCloud plan cannot associate the tuned "OmniRoute way" gate (coverage ≥60 aligned with the repo floor)
📦 Everything from the v3.8.47 cycle ships here
The 3.8.47 npm package was never installable (#7065), so 3.8.48 is the release that actually delivers the whole v3.8.47 cycle — full notes below:
- 9router Codex import: the Codex bulk-import endpoint (
POST /api/oauth/codex/import) now accepts 9router's camelCase account export (accessToken/refreshToken/idToken/expiresAt+ nestedproviderSpecificData), not just snake_case —normalizeCodexImportRecordmaps the camelCase aliases onto the existing snake_case keys, filling each only when absent so snake_case/mixed exports keep working unchanged (#6665) — thanks @deadcoder0904. Regression guard:tests/unit/codexBulkImport.test.ts(9router camelCase record, pre-suppliedproviderSpecificDatawithout an id_token, snake_case-not-overridden, and a full{accounts:[...]}flatten).
✨ New Features
-
feat(plugins): Langfuse observability plugin. (#6577 — thanks @chirag127)
-
feat(combo): context requirements config for per-target filtering in combos. (#6907 — thanks @oyi77)
-
feat(providers): icons for 46 providers that were missing images. (#6926 — thanks @oyi77)
-
feat(compression): vendored GCF (Headroom) codec updated to spec v3.2 (nested flattening). (#6838 — thanks @blackwell-systems)
-
feat(proxy): shorthand proxy formats + protocol header mode for bulk import. (#6867 — thanks @growab)
-
feat(provider): OpenVecta AI inference gateway. (#6833 — thanks @hajilok)
-
feat(i18n): Traditional Chinese (zh-TW) localization for frontend and CLI. (#6320 — thanks @lunkerchen)
-
feat(xai): route xAI clients to Grok's native
/v1/responsesendpoint. (#6709 — thanks @diegosouzapw) -
feat(routing): per-model web-search/web-fetch interception rules. (#3384, #6814 — thanks @diegosouzapw)
-
feat(release):
changelog.d/fragments — eliminates the CHANGELOG merge-storm cascade. (#6783 — thanks @diegosouzapw) -
feat(quality):
validate-release-green --full-cireproduces the entire ci.yml static gate set locally. (#6583 — thanks @diegosouzapw) -
feat(dashboard): sidebar quick-filter — a search input at the top of the expanded dashboard sidebar (
src/shared/components/Sidebar.tsx) filters nav sections/groups/items client-side by label as you type, reusing the existingcommon.search/common.noResultsi18n keys (zero new locale edits) and the sharedInputicon="search"pattern; matching sections auto-expand while searching (bypassing the accordion/pin state) and collapse back to normal once the query is cleared. Pure filtering logic extracted intofilterSidebarSectionsByQuery()(src/shared/utils/sidebarSearch.ts) for isolated unit testing. Regression guard:tests/unit/sidebar-search-filter.test.ts,src/shared/components/Sidebar.search.test.tsx. (#4013 — thanks @crochabe-cyber) -
feat(combo):
auto/*combos gain a strict budget-cap fallback policy —X-OmniRoute-Budget-Fallback: strict(or the persistedconfig.budgetFallback: "strict") makes an over-budget request fail fast withHTTP 402instead of the previous silent fallback to the globally cheapest candidate, which could still exceed the cap. The default (cheapest) preserves existing behavior. Builds on the existingX-OmniRoute-Budget/X-OmniRoute-Modeper-request controls (#6023/#6024/#6025), consolidated intoresolveRequestAutoControls(). Regression guard:tests/unit/auto-combo-budget-fallback-3470.test.ts. (#3470) -
Provider/model param filters: config-driven parameter denylist/allowlist per provider/model with auto-learn from upstream 400s (#6649 — thanks @ThongAccount, closes #6625)
-
Per-combo reasoning token buffer toggle: the combo builder now exposes an explicit checkbox for the
#3587reasoning-modelmax_tokensbuffer, defaulting to the existing enabled behavior, so a combo can opt out without hand-editing raw JSON config (#6702 — thanks @xz-dev) -
feat(dashboard): 9router-parity Routing Strategy settings card on Settings → Routing, plus a per-provider account-routing override on the provider detail page (#6678) — surfaces the existing account round-robin / sticky-limit knobs and adds a new combo-level sticky round-robin (
comboStickyRoundRobinLimit, resolved viaresolveComboStickyRoundRobinLimit()— per-combo → global combo sticky → account sticky cascade) so combo targets can batch calls per target the same way account fallback already does. A newproviderStrategiessetting (Zod-validated map,src/shared/validation/settingsSchemas.ts) lets a specific provider override the globalfallbackStrategy/stickyRoundRobinLimitwithout touching the account-wide default, wired intogetProviderCredentials()(src/sse/services/auth.ts) ahead of the global fallback. Regression guard:tests/unit/combo-rr-sticky-9router.test.ts,tests/unit/settings-ui-layout-static.test.ts. (thanks @SeaXen) -
feat(icons): provider logos now resolve local SVG assets first for faster rendering, with a 5-tier fallback chain — local SVG →
@lobehub/iconsReact components →thesvg.orgCDN (external SVG for unknown providers) → local PNG → generic AI icon — replacing the previous LobeHub-first order. Adds dozens of first-party provider SVGs and migrates several bitmap logos (continue/copilot/cursor/deepgram/heroku/openclaw/ovhcloud) from PNG to SVG. Regression guard:tests/unit/ui/ProviderIcon-icon-url.test.tsx. (#6317 — thanks @hamsa0x7) -
Skill Collector CLI detection: new
GET /api/skills/collect/detect+POST /api/skills/collect/install(and thecli-skill-collectoragent skill) detect which coding CLIs (Claude Code, Codex, Cursor, Copilot, Cline, Hermes, OpenCode, etc.) are installed locally viagetCliRuntimeStatus(), match them against GitHub agent-skill repos, and plan an install path per tool — replacing the standalone Skill Collector Python app. Both new routes andGET/POST /api/github-skillsnow require management auth (requireManagementAuth()) and are loopback-gated (LOCAL_ONLY_API_PREFIXES+SPAWN_CAPABLE_PREFIXES) since the detect route spawns a child process per candidate CLI tool (Hard Rules #15 + #17). Theomniroute_github_skills_installMCP tool now reports the honestaction: "planned"instead of"installed", matching the REST route (#6294 — thanks @Moseyuh333) -
ClinePass dual-auth: ClinePass now offers both sign-in methods on its dashboard page — OAuth (reusing the Cline WorkOS flow) as the primary "Connect" path, or a pasted BYOK API key via "Manual API key", instead of only the API-key-only provider shipped in #5942. The registry alias was aligned to
cp(matching theOAUTH_PROVIDERScatalog alias) so<alias>/<modelId>routing resolves correctly, the OAuth refresh dispatch now routesclinepassto the shared Cline refresh flow, and the duplicate API-key-only catalog entry was removed to keep ClinePass listed once. Regression guard:tests/unit/clinepass-provider.test.ts. (#6126 — thanks @hajilok) -
feat(oauth): Kiro/Amazon Q auto-import now supports enterprise External IdP ("Your organization") logins via Microsoft Entra/Okta/Auth0/OneLogin/Ping/Google/Cognito — these org-issued tokens are not AWS SSO tokens (no
aorAAAAAG-prefixed refresh token) and can't refresh through the AWS OIDC/Kiro-social path, sotryAwsSsoCache()now detects them (authMethod/provider === "externalidp") and refreshes via the org IdP's owntokenEndpoint(public-client OAuth2 refresh grant, no client secret), persistingTokenType: EXTERNAL_IDPgating so the runtime executor sends the header the AWS CodeWhisperer API requires for these accounts;tokenEndpointis SSRF-guarded against an HTTPS + known-IdP-host-suffix allowlist. (#6363 — thanks @artickc) -
Kiro long-lived API key auth: new
/api/oauth/kiro/api-keyroute +KiroService.validateApiKeylet a Kiro account be linked with a long-lived AWS CodeWhisperer/Kiro API key instead of the interactive OAuth device flow, with live per-account model discovery (ListAvailableModels, 5-minute cache) layered over the existing static registry fallback (#6587 — thanks @strangersp) -
Chaos Mode: multi-model parallel/collaborative task execution — dispatches a task to every active provider connection at once (parallel) or chains outputs sequentially so each model builds on the previous one's answer (collaborative), configurable via Dashboard → Chaos Mode (
GET/PUT/DELETE /api/chaos/config) and gated per-API-key via a newchaosModeEnabledpermission (opt-in — disabled by default globally and per key).POST /api/chaos/run(dashboard session) andPOST /api/skills/collect/chaos(external Bearer-token) delegate to a sharedexecuteChaosRun()engine (src/lib/chaos/chaosExecutor.ts) that dispatches in-process via the established synthetic-Request/route-handler pattern (no network hop, no hardcoded port), with a concurrency cap (max 10 parallel), configurablemax_tokens(256–128k), a clear error whenstreamis requested, and collaborative-chain info (provider order + input size). Fixes external Bearer-auth bypass and stale config-cache leakage. Regression guard:tests/unit/chaos-config.test.ts,tests/unit/chaos-executor.test.ts,tests/unit/chaos-api-routes.test.ts. (#6728 — thanks @Moseyuh333) -
feat(cli): 2 new CLI tool integrations on Dashboard → CLI Tools — omp (Oh My Pi) and letta — each with binary detection, config apply/reset, and a settings card following the existing tool-card pattern. Both settings routes shell out to
which omp/which lettato detect the local install, so they're loopback-gated (LOCAL_ONLY_API_PREFIXES, Hard Rules #15/#17) in addition to the sharedrequireCliToolsAuth()management-auth guard every cli-tools route requires, and route errors throughsanitizeErrorMessage();src/lib/db/omp.tsisolates theompCLI's own local SQLite reads behind parameterized queries. (Note: the original PR also proposed pi, codewhale, and jcode integrations — those three had already shipped via a separate PR by the time this one was reconciled, so only omp+letta landed here.) Regression guard:tests/unit/db/omp.test.ts,tests/unit/cli-tools-auth-hardening.test.ts,tests/integration/cli-settings-omp.test.ts,tests/integration/cli-settings-letta.test.ts. (#6318 — thanks @hamsa0x7) -
feat(providers): custom models now support a manual Context Window Override so an operator can correct a provider's misreported context length (e.g. reports 1M when the real limit is 128K) instead of the model silently getting dropped from combo routing once the wrong value lands in the catalog (#4125 — thanks @rucciva). Reuses the existing Feature-5004
model_context_overridestable (source: "manual") — already the priority-0 sourcegetModelContextLimit()(the function combo's context-window filter calls) reads ahead of the models.dev/registry/static catalog — so no new resolver logic was needed, only the missing write path:PUT /api/provider-modelsnow accepts an optionalcontextWindowOverride(number to set,nullto clear),GETsurfaces the current value back on each custom-model row, and the provider detail page's custom-model edit form gained a Context Window Override field + badge. Regression guard:tests/unit/provider-models-context-window-override-4125.test.ts. -
fix(providers): register OpenRouter as a rerank provider so
openrouter/cohere/rerank-*models resolve instead of erroringInvalid rerank model(#6574 — thanks @rafpigna) -
fix(api):
HEADrequests no longer hang until client timeout on any route — valid, unknown, authed, or unauthed (#6400), broader follow-up to the route-specific #6517 (/v1/models). Root cause: Next.js 16's App Router route-handler pipeline (next/dist/server/send-response.js) correctly skips piping aResponsebody forHEAD, but its page-rendering pipeline (next/dist/server/pipe-readable.js→pipeToNodeResponse, used for every app-router page/layout render — including thenot-foundboundary any unmatched path falls through to) has no such check and always streams the full rendered body regardless of method; combined with Node's default keep-alive framing this left some clients unsure whether the (implicitly bodyless)HEADresponse had actually finished. A newscripts/dev/head-response-guard.cjs, wired into both the dev/start custom server (scripts/dev/run-next.mjs) and the packaged standalone server (scripts/dev/standalone-server-ws.mjs) at the same tier as the existinghttp-method-guard.cjs/peer-stamp.mjswrappers, discards any body bytes written for aHEADrequest and forcesConnection: closeonce.end()is called — independent of route existence or auth state, satisfying RFC 9110 §9.3.2. Regression guard:tests/unit/head-request-closes-6400.test.ts. -
feat(dashboard): Provider Quota page (
Dashboard → Quota) fills horizontal whitespace before stacking vertically (#3520) —QuotaCardGridpreviously stacked every provider group in a single verticalflex flex-col, and each group's own card grid didn't go multi-column untilmd(grid-cols-1 md:grid-cols-2 xl:grid-cols-3 2xl:grid-cols-4). Provider groups now flow into a 2-column CSS multi-column layout on very wide (2xl) screens instead of an unconditional vertical stack, and each group's card grid starts at 2 columns immediately (grid-cols-2 md:grid-cols-3 xl:grid-cols-4), reaching higher density sooner on narrower-but-not-mobile viewports. Regression guard:tests/unit/quota-card-grid-horizontal-layout.test.ts(thanks @gdevenyi). -
feat(ws): the live-dashboard WebSocket server now auto-starts in-process (via
instrumentation-node.ts) across every deployment mode — dev, production, Docker, Electron — with no separate sidecar script; the default WS port moved from 20129 to 20132 to avoid colliding withAPI_PORTin split-port setups, the deprecatedOMNIROUTE_DISABLE_LIVE_WSenv was consolidated intoOMNIROUTE_ENABLE_LIVE_WS(default enabled), and the WS path is now derived fromNEXT_PUBLIC_LIVE_WS_PUBLIC_URL's pathname (/live-wsfallback) (#6072 — thanks @ianriizky). -
feat(compression): new omniglyph engine (context-as-image) — renders system prompt, tool docs, and dense history as compact PNG pages the model reads instead of text (~10× fewer tokens on the converted block; 59–70% end-to-end measured). Works stacked with RTK/Caveman (
stackPriority: 90) or standalone (mode: omniglyph); restricted to Claude Fable 5 over the direct Anthropic route, fail-closed gates withskip:<reason>techniques, preview (stable: false, off by default) (#6556). Dependency bumped toomniglyph@^1.0.2for upstream ReDoS fixes (#6661). -
feat(sandbox): the skill sandbox gained a container-provider abstraction that auto-detects and uses the best native runtime per host — Apple Container (macOS 26+), WSL container (
wslc.exe), OrbStack, Podman — instead of hardcodingdocker run, removing the Docker Desktop requirement on macOS/Windows (#6611 — thanks @KooshaPari). -
fix(sse): skip
thinkingConfigfor Gemma models on the OpenAI→Gemini path so OpenAI-shape clients no longer get a 400 from Vertex. (thanks @chy1211) -
feat(xai): route xAI clients to Grok's native
/v1/responsesendpoint instead of the chat-completions bridge. (thanks @ryanngit) -
feat(models): add a Settings → AI "Model Overrides" UI plus
/api/model-capability-overridesCRUD and amodel_capability_overridestable, letting operators set a manual max-output-token override per provider/model (#6727 — thanks @xz-dev). -
feat(resilience): operator-configurable account rotation policy — a new
rotationConfiglayer lets operators tune how connections rotate on failure, wired intoaccountFallback(#6763 — thanks @artickc). -
chore(cursor): add Grok 4.5 effort/fast model IDs (#6774 — thanks @andrewmunsell).
-
feat(codex): Codex provider model discovery now fetches the live catalog from
chatgpt.com/backend-api/codex/modelsusing Codex-shaped headers, falling back to a GitHub-hosted model manifest and then to the local static catalog when the live/GitHub sources are unavailable or return an unexpected shape — newsrc/app/api/providers/[id]/models/discovery/codex.ts(normalization, version-gating, merge/enrich against the local catalog) covered bytests/unit/provider-models-discovery-split.test.tsandtests/unit/provider-models-route-codex.test.ts(#6776 — thanks @JxnLexn). -
feat(cursor): register the Opus 4.8, Fable 5, and Sonnet 5 model families for the Cursor Agent provider so the latest Claude/Fable model ids route correctly (#6779 — thanks @andrewmunsell).
-
Changelog fragments (
changelog.d/): PRs now add their changelog entry as a new fragment file (changelog.d/{features|fixes|maintenance}/<PR>-<slug>.md) instead of editingCHANGELOG.md— two PRs never touch the same file, structurally eliminating the CHANGELOG-eat merge conflicts that forced a re-sync push + full CI re-run after every sibling merge (O(N²) CI runs in a merge-storm).scripts/release/aggregate-changelog.mjs(npm run changelog:aggregate) folds fragments into the living section at release reconciliation, andcheck:changelog-integritynow also validates fragment well-formedness. Regression guard:tests/unit/changelog-fragments.test.ts. -
feat(proxy): add a latency-optimized proxy rotation strategy that ranks pool entries by measured round-trip latency, extending the existing round-robin/random/sticky proxy-pool selection (#6798 — thanks @iamraydoan).
-
feat(fusion): the fusion judge may now draw on its own knowledge and override the panel when every panel answer is wrong or incomplete, instead of being restricted to synthesizing only from panel output (#6804 — thanks @chirag127).
-
feat(dashboard): search box on the Playground's raw model
<select>(#4086) — the sharedModelSelectModal(combo builder + CLI-code cards) already had search, but Playground'sStudioConfigPanemodel dropdown stayed a flat unsearchable list, unusable once a provider like OpenRouter contributed 50+ models. Typing now filters the dropdown (Turkish-safe accent/case-insensitive match viamatchesSearch), while the currently selected model always stays pinned in the list even if it no longer matches the query, so typing never silently swaps the active selection. Reuses the existingcommon.searchi18n key (already translated in all 42 locales) — no new translation key needed. Regression guard:tests/unit/playground-model-selection-3731.test.ts(filterModelsByQuery),tests/unit/ui/playground-model-search-4086.test.tsx. -
feat(usage): Antigravity/agy quota widget now surfaces the weekly window alongside the existing per-model 5-hour window (#4017) — the weekly limit isn't part of the per-model
retrieveUserQuotaresponse the fetcher already calls; it only appears in a separate, undocumentedretrieveUserQuotaSummaryRPC that groups models into families ("Gemini Models", "Claude and GPT models") with one weekly bucket per family. A newusage/antigravityWeeklyQuota.tsleaf fetches that RPC (cached, best-effort — a failure or unavailable RPC never breaks the existing per-model quotas) and parses the weekly bucket per group intogemini_weekly/claude_gpt_weeklyquota entries, merged into the samequotasmap the widget already renders generically. Regression guard:tests/unit/antigravity-weekly-quota-4017.test.ts(bucket parsing, the alternatequotaSummary-nested envelope, and end-to-end merge viagetUsageForProvider). -
feat(codex): Codex CLI compatibility shim — the Responses API
response.created/response.in_progress/response.completedpayloads now carry amodelfield (previously absent), and for Codex-CLI-originated requests it echoes the client-requested, effort-suffixed model id (e.g.gpt-5.5-xhigh) instead of the bare upstream id (gpt-5.5), so the Codex CLI status line/model button shows the active reasoning effort (#3697).openaiToOpenAIResponsesResponse(open-sse/translator/response/openai-responses.ts) now threads the upstream model into the Responses event objects; a newisCodexOriginatedHeaders()(open-sse/config/codexIdentity.ts, reusing PR #3481'soriginator/User-Agent detection) makes chatCore's existing opt-inechoRequestedModelName(#1311) model-echo pipeline fire automatically for Codex clients regardless of the setting, detected by request headers so it still applies whencodex/gpt-5.5-xhighis routed through a combo to a non-codex upstream;echoModelInObject/echoModelInSseLine(open-sse/services/responseModelEcho.ts) now also rewrite the nestedresponse.modelfield the Responses API uses./v1/modelsstill returnsmodels: []for Codex (unchanged). Regression guard:tests/unit/codex-effort-model-echo-3697.test.ts. -
Z.ai Web (free web-session provider): new
zai-webweb-cookie provider drives the free chat.z.ai consumer chat UI via a pasted browser session cookie, distinct from the existing API-keyzai/glm/glm-cn/glmtproviders (api.z.ai) — modeled on thedoubao-web/venice-webcookie executors and the pre-existingchatglm-webcredential requirement/token-extraction entries.ZaiWebExecutor(open-sse/executors/zai-web.ts) posts tochat.z.ai/api/chat/completionswith the cookie forwarded both asCookieand asAuthorization: Bearer <token>, and normalizes both z.ai's internaldelta_content/phaseSSE envelope and a pass-through OpenAI-shapedchoices[].deltaframe into standard chat-completion chunks. Registered inWEB_COOKIE_PROVIDERS,WEB_SESSION_CREDENTIAL_REQUIREMENTS, the provider registry (zai-webentry, GLM-4.6/4.5/4.5V models), andtokenExtractionConfig.tsfor in-app cookie capture. Regression guard:tests/unit/executor-zai-web.test.ts(16 tests — token extraction, frame parsing for both SSE shapes, streaming and non-streaming aggregation, error paths). (#4056) -
feat(compression): update the vendored GCF codec behind the Headroom engine to spec v3.2 (nested flattening) (#6837). Homogeneous arrays whose rows carry nested objects/arrays now tabularize via
>-prefixed path fields instead of a low-yield per-row fallback, so nested MCP tool-result rows (meta:{...},tags:[...]) compact like flat rows. On representative shapes the update takes deeply-nested payloads the old codec left near-uncompressed from ~3% to ~32% vs JSON (k8s pods,cl100k_base), with shallow-nested rows seeing a small bump and flat arrays unchanged. Re-vendored from current gcf-typescript (zero runtime deps, MIT, SPDX-marked, generic-profile only); also folds in the[N]:inline-array quoting fix and canonical decimal formatting. Round-trip stays lossless (order-insensitive), and the decoder is hardened against prototype pollution (a__proto__/constructorpath segment never mutatesObject.prototype, and keys shadowing built-ins liketoStringnow round-trip correctly instead of misparsing). Regression guard:tests/unit/compression/headroom-smartcrusher.test.ts(deep-nested + prototype-pollution cases). -
feat(providers): Add GPT-5.6 support across OpenAI API, Codex, and ChatGPT Web, including Codex Max/Ultra efforts, VS Code metadata, Fast-tier credit accounting, curated live discovery, the Codex 0.144.1 client identity, and correct chat routing for models that also support image generation (#6862) - thanks @backryun
-
chore(providers): Align emitted Claude Code identity headers, bridge fingerprints, provider profiles, and documented defaults with claude-cli 2.1.207 (#6862) - thanks @backryun
🐛 Bug Fixes
-
fix(dashboard): the Proxy Registry settings page crashed at runtime (
ReferenceError: poolLoaded/bulkImportOpen is not defined) — the #6625/#6909 hook-extraction refactors deleted 10 state declarations (poolLoaded,poolSaving, and the 8-member bulk-import family) while ~30 usages remained; all restored (caught by the release E2E;typecheck:coredoes not cover dashboard TSX — follow-up #7021). (thanks @diegosouzapw) -
fix(combo):
comboStickyRoundRobinLimitnow defaults to inherit (null) instead of1— the literal default silently shadowed the documented batched round-robin rotation (stickyRoundRobinLimit: 3), flipping every round-robin combo to per-request alternation (#6678 follow-up, caught by the release CI). (thanks @diegosouzapw) -
fix(ws): the standalone LiveWS startup script exited 0 without ever listening — its bootstrapped child re-spawned with the import-suppressor
OMNIROUTE_ENABLE_LIVE_WS=0and then honored it as an operator disable (#6072 follow-up, caught by the release CI). (thanks @diegosouzapw) -
fix(api): compression PUT schema accepts every catalog engine. (#6792 — thanks @Pitchfork-and-Torch)
-
fix(compression): surface fallback reasons in the preview response. (#6461, #6519 — thanks @chirag127)
-
fix(providers): fail fast on an empty auto-combo pool instead of a 15s timeout. (#6458, #6546 — thanks @chirag127)
-
fix(compression): honor UI-toggled engines in the stackedPipeline dispatch + surface substitutions. (#6463, #6534 — thanks @chirag127)
-
fix(api): return 400 for missing/invalid
messagesbefore model resolution. (#6402, #6515 — thanks @chirag127) -
fix(providers): enrich the model_cooldown 429 body with a
retry_afterISO timestamp + credential count. (#6460, #6523 — thanks @chirag127) -
fix(sse): default reasoning summary for effort-only Responses requests. (#6807 — thanks @rushsinging)
-
fix(sse): compression no-op treated as zero-savings, not inflation/silent-drop. (#6883 — thanks @chirag127)
-
fix(oauth): Trae OAuth client_id embedded via
resolvePublicCred()(Hard Rule #11). (#6870 — thanks @chirag127) -
fix(sse): combo path no longer trips the whole-provider breaker on a plain 429. (#6868 — thanks @chirag127)
-
fix(api): malformed JSON bodies now return 400 instead of 500. (#6871 — thanks @chirag127)
-
fix(fusion): judge selected from a surviving panel member when no explicit judge is configured. (#6869 — thanks @chirag127)
-
fix(sse): combo model lockout honors the parsed upstream quota reset. (#6863, #6866 — thanks @AgentKiller45)
-
fix(dashboard): logs detail modal no longer reopens on first close. (#6830 — thanks @MikeTuev)
-
fix(usage): honor xAI provider-reported exact cost. (#6711 — thanks @diegosouzapw)
-
fix(kiro): probe IdC region during profileArn discovery, cross-region (recovers #6099). (#6840 — thanks @diegosouzapw)
-
fix(antigravity): sanitize Cloud Code safety settings. (#6839 — thanks @diegosouzapw)
-
fix(translator): defer
content_block_startuntil GLM streams the tool name. (#6730 — thanks @diegosouzapw) -
fix(translator): strip empty
cloud_base_branchfrom Cursor Subagent tool calls. (#6729 — thanks @diegosouzapw) -
fix(antigravity): surface aborted Gemini tool calls off
end_turn. (#6713 — thanks @diegosouzapw) -
fix(volcengine): clamp Kimi
max_tokensto the Ark endpoint cap. (#6712 — thanks @diegosouzapw) -
fix(codex): surface capacity errors embedded in 200-OK SSE streams. (#6710 — thanks @diegosouzapw)
-
fix(sse): skip
thinkingConfigfor gemma models in openai→gemini translation. (#6708 — thanks @diegosouzapw) -
fix(oauth): avoid bare-email dedup of Codex OAuth logins. (#6706 — thanks @diegosouzapw)
-
fix(sse): unwrap bare
{function:{…}}tools in openai→claude translation. (#6704 — thanks @diegosouzapw) -
fix(db): eliminate a redundant
getApiKeyMetadatacall in the embeddings route. (#6929 — thanks @oyi77) -
fix(db):
authTypefilter support ingetProviderConnections. (#6946 — thanks @oyi77) -
fix(i18n): the provider-detail (
/dashboard/providers/[id]) visibility + free/paid model filter labels (showVisibleOnly,showHiddenOnly,freeFilterAll,freeFilterFreeOnly,freeFilterPaidOnly,hideAllModels, plus the currently-unusedfilterVisible/filterHidden/filterByVisibility) rendered as the literal__MISSING__:<english>sentinel in 15 locales, including pt-BR (#6694) —providerText()(providerPageHelpers.ts) checkst.has(key)before falling back to clean English, andt.has()returnstrueeven when the stored value is the__MISSING__:sentinelscripts/i18n/sync-ui-keys.mjswrites when mirroring keys across locales, so the sentinel rendered verbatim instead of the fallback. Disjoint key set from #6290 (filterAll/filterActive/filterError/filterBanned/filterCreditsExhausted). All 9 keys now carry real translations across the 15 affected locale files (it,ja,ko,mr,ms,nl,no,phi,pl,pt,pt-BR,ro,ru,sk,sv). Regression guard:tests/unit/i18n-provider-visibility-filter-keys-6694.test.ts. -
fix(cli): the dashboard's Claude Code CLI card could report "Not detected"/"Not installed" even when Claude Code was genuinely installed and previously used (#6701) —
getCliRuntimeStatus()(src/shared/services/cliRuntime.ts) determinedinstalledpurely from binary resolution (known install paths + awhere/whichPATH search), with no fallback when that lookup fails for reasons unrelated to whether the CLI is actually installed (stale PATH inherited by a long-running/background process, the binary having moved, an install method not yet catalogued, etc.) — even though~/.claude/settings.jsonon disk proves the tool was installed and used before. Upstream 9router's equivalent route already has this exact fallback. A newwithSettingsFallback()(src/shared/services/cliInstallFallback.ts) restores 9router parity: when the binary lookup's own reason is"not_found"(never for deliberate security rejections like unsafe/relative env overrides or symlink escapes) and the tool's settings file exists on disk,installednow reportstrue. Regression guard:tests/unit/repro-6701-claude-detect-fallback.test.ts. -
fix(cli): per-agent AgentBridge DNS toggle was broken for 8 of the 9 supported agents, and a failed MITM startup step could orphan the spawned proxy child —
addDNSEntry/removeDNSEntry(src/mitm/dns/dnsConfig.ts) always resolved the legacy Antigravity default hosts regardless of which agent's toggle was flipped, so enabling DNS for Cursor/Codex/Claude Code/etc. silently added onlydaily-cloudcode-pa.googleapis.comwhile the DB recordeddns_enabled=truefor the selected agent. Both functions now accept an optionalagentIdand resolve hosts viaALL_TARGETS;POST /api/tools/agent-bridge/agents/[id]/dnspasses the route'sidthrough and now returns 404 for an id that doesn't match a known target instead of silently falling back. Separately,startMitmInternal()(src/mitm/manager.ts) now wrapsgenerateCert()(log + rethrow), theprovisionDnsEntries()call, and the PID-file write in try/catch so a mid-startup failure can't orphan the already-spawned MITM child process. On Windows,addDNSEntries/removeDNSEntriesalso batch every missing/present entry into a single elevated PowerShell invocation instead of one UAC prompt per host line. Regression guard:tests/unit/dns-config-generic.test.ts(agent-specific resolution + batching),tests/unit/agent-bridge-dns-route-validation.test.ts(404 for unknown agent id). (#6338 — thanks @hamsa0x7) -
fix(guardrails): Vision Bridge's individual-model auto-reroute (route an image-bearing request straight to a vision-capable model instead of describe-then-forward) could bypass a policy-restricted API key's model allowlist/budget (#6640) —
VisionBridgeGuardrail.preCall()(src/lib/guardrails/visionBridge.ts) swapsbody.modelto the best available vision-capable model, but that swap happens in the guardrail pipeline AFTERchat.tsalready calledenforceApiKeyPolicy()against the ORIGINAL model, so a key scoped to a narrowallowedModelslist could still execute against an unvetted (and possibly costlier) vision model the reroute picked.chat.tsnow re-validates any guardrail-driven model change against the same per-key allowlist (isModelAllowedForKey) before honoring it, falling back to the original already-approved model when the reroute target is not allowed. The reroute path also now honors an explicitsettings.visionBridgeModeloperator override (previously ignored, unlike the combo/describe path a few lines below it, which already respects it viagetVisionBridgeConfig). Regression guard:tests/unit/guardrails/visionBridge.test.ts(22 tests). (thanks @herjarsa) -
fix(auth): an API key restricted via
allowedModels/allowedComboscould bypass that restriction entirely over the Codex Responses-over-WebSocket bridge (#6564) —prepare()insrc/app/api/internal/codex-responses-ws/route.tsauthenticated the WS bridge's API key (authenticate()/authorizeWebSocketHandshake()) and honoredallowedConnections, but never calledenforceApiKeyPolicy(), the same model/combo policy gate the HTTP/v1/responsespath enforces viahandleChat()— so a key scoped to e.g.combo/model-1.0could still reach a direct Codex model likegpt-5.5through this transport, as long as an eligible Codex OAuth connection existed. The bridge's WS auth token arrives via query params (api_key/token/access_token), not a normalAuthorizationheader, so a newenforceCodexWsApiKeyPolicy()builds an equivalentRequestcarrying an explicitAuthorization: Bearer <apiKey>header and callsenforceApiKeyPolicy()against the CLIENT-requested model, before any Codex-specific model remapping or credential selection. Regression guard:tests/unit/codex-ws-policy-enforcement-6564.test.ts(a model-restricted key is rejected 403 before reaching credential selection; a combo-restricted key is rejected 403 requesting a disallowed combo; a key that DOES allow the requested model still proceeds past policy). (thanks @Squawk7777 for the report and an independent fix via #6565) -
fix(security): loopback-gate
/api/middleware/*so a leaked JWT over a tunnel can't install or trigger a middleware hook — middleware hooks compile + run arbitrary JS vianew vm.Scripton the request hot path (src/lib/middleware/registry.ts), the same RCE class as the already-gated/api/plugins/*;/api/middleware/is now inLOCAL_ONLY_API_PREFIXESso loopback enforcement runs unconditionally before any auth check (Hard Rules #15 + #17). Regression guard:tests/unit/route-guard-middleware-local-only.test.ts. (#6541) — see PR. (thanks @developerjillur) -
fix(startup): AgentBridge's MITM server no longer fails to start with
ROUTER_API_KEY is requiredon a normal install (#6403) —POST /api/tools/agent-bridge/serverresolved the spawned MITM child's router key from only an explicitapiKeybody field (never sent by the AgentBridge UI — the schema has no such field) and theROUTER_API_KEYenv var (unset by default), sostartMitm()always received""and the child hard-exited, even though OmniRoute already had a usable API key in its own DB. A newresolveRouterApiKey()now falls back topickApiKeyForInternalUse()(the same DB-backed selector the combo-health-check / cloud-sync internal probes use), resolving in order: explicit key →ROUTER_API_KEYenv → an existing DB key. Regression guard:tests/unit/agentbridge-mitm-router-key-6403.test.ts. -
fix(providers): deploying a Cloudflare relay Worker from Dashboard → System → Proxy pool → Cloudflare relay failed immediately with
Cloudflare Worker upload failed: Content-Type must be one of: application/javascript, text/javascript, multipart/form-data, even with a valid token/account (#6416) — the Worker-script upload built a nativeFormDataand letfetchderive the multipart Content-Type automatically, but in productionglobalThis.fetchis patched withnode_modules/undici's own fetch (open-sse/utils/proxyFetch.ts), whoseFormData/Requestclasses differ from the runtime's globalFormData(same cross-realm class mismatch already fixed once for image edits in #3273); passing a nativeFormDatainstance through undici's patched fetch made it serialize the body as the literal string"[object FormData]"withContent-Type: text/plain;charset=UTF-8, which Cloudflare rejects outright.buildCloudflareWorkerUploadRequest()(src/lib/proxyRelay/cloudflareWorkerScript.ts) now builds the multipart body as a rawBufferwith an explicit boundary andContent-Type: multipart/form-data; boundary=…header, accepted verbatim by any fetch implementation. Regression guard:tests/unit/cloudflare-worker-upload-content-type-6416.test.ts+ updatedtests/unit/relay-deploy-5128.test.ts. -
fix(security): SSRF-guard the provider-validation probes so they can no longer be used as an open relay to cloud-metadata endpoints —
directHttpsRequest()(web-cookie / NVIDIA / Z.AI validation, all with a caller-controllablebaseUrl) ran withguard:"none"+allowRedirect:true; it now appliesgetProviderValidationGuard()(defaultblock-metadata: LAN/localhost allowed,169.254.169.254/link-local IMDS rejected, opt-out viaOMNIROUTE_ALLOW_PRIVATE_PROVIDER_URLS) andallowRedirect:falseso a provider can't 3xx-redirect the probe to metadata past the initial-URL guard. Regression guard:tests/unit/provider-validation-ssrf-guard.test.ts. (#6542) — see PR. (thanks @developerjillur) -
fix(startup): AgentBridge's MITM proxy served a mismatched cert for 3 of the 4 antigravity/cloudcode-pa hosts it terminates TLS for, breaking interception (#6494) —
src/mitm/server.cjs'sTARGET_HOSTSdecrypts all 4 hosts locally (daily-cloudcode-pa.googleapis.com,cloudcode-pa.googleapis.com,daily-cloudcode-pa.sandbox.googleapis.com,autopush-cloudcode-pa.sandbox.googleapis.com), butsrc/mitm/cert/generate.ts's self-signed cert only carried a SAN entry for the first host — a request to any of the other 3 got served a cert whose CN/SAN didn't match (confirmed viacurl -k https://cloudcode-pa.googleapis.com/showingCN=daily-cloudcode-pa.googleapis.com).generateCert()now sources its host list fromANTIGRAVITY_TARGET.hosts(src/mitm/targets/antigravity.ts, the single authoritative registry already kept in lock-step withserver.cjs/dnsConfig.ts/mitmToolHosts.tsby their own drift tests) and emits a SAN entry for all 4 hosts instead of hard-coding a second, incomplete copy. Regression guard:tests/unit/agentbridge-antigravity-cert-hosts-6494.test.ts(asserts the host list covers all 4 hosts and that the real generated cert's SAN includes each one). -
fix(resilience): a
prioritycombo never fell back when a target masked credit/quota exhaustion behind an HTTP 200 (#6427) —validateResponseQuality()(open-sse/services/combo/validateQuality.ts) only inspected the response body's top-levelerrorfield whenchoiceswas ALSO missing/empty (the narrower #3424 empty-completion case); a masked 200 that echoed a non-empty stubchoicesalongside a structured error object, or a known exhaustion phrase (e.g. "insufficient credits", "quota exceeded") in the error envelope, slipped through as "valid" and the combo kept returning the dead target's response forever instead of failing over. The quality check now inspects the error envelope — a top-level OpenAI-shapeerrorobject, or a bounded, case-insensitive exhaustion-phrase match againsterror.message/error.code/error.type/top-levelmessage/detail— unconditionally, before any shape-specific branch, and regardless of whetherchoices/outputalso look structurally present. The check never inspectschoices[].message.content, so a legitimate completion that merely mentions "quota" or "credits" in assistant prose is not misclassified. Regression guard:tests/unit/masked-200-exhaustion-fallback-6427.test.ts. -
fix(security): fail-closed CORS for the cookie/session-authed cloud-agent management routes —
getCloudAgentCorsHeaders()reflected any caller'sOriginand paired it withAllow-Credentials: true(a CSRF/exfil hole); it now defers to the central allowlist (resolveAllowedOrigin), echoes only an allowlisted origin withVary: Origin, and emitsAllow-Credentialsonly for an explicitly allowlisted origin — never for aCORS_ALLOW_ALLwildcard echo. Regression guard:tests/unit/cloud-agent-cors-failclosed.test.ts. (#6543) — see PR. (thanks @developerjillur) -
fix(compression): adaptive-compression ladder ranked 6 real catalog engines (
ccr,ionizer,relevance,llmlingua,llm,read-lifecycle) as if they didn't exist (#6533) —ladder.ts'sAGGRESSIVENESSandREDUCTION_FACTORmaps only covered the 7 engines wired intoDEFAULT_LADDER(session-dedup/rtk/headroom/lite/caveman/aggressive/ultra); every other engine registered inopen-sse/services/compression/engines/index.ts— includingccr/llmlingua, which the ladder doc comment already says are intentionally addable vialadderOverride— fell through toaggressivenessOf()'s?? 0default (same rank as"off") andexpectedReductionFactor()'s generic?? 0.9fallback, sofloor-mode escalation could not rank or escalate past them once added to a custom ladder. Both maps now carry entries for all 6 missing real engines, rescaled ×10 (off:0…ultra:70) and placed by each engine's documentedstackPriority(ionizerbetweenrtk/headroom,relevancebeforecaveman,llmlingua/llmbetweenaggressiveandultra, etc.);mcpAccessibility— named in the report — is not a registeredCompressionEngine(it's a separate MCP tool-response truncation mechanism) and was correctly left out. Regression guard:tests/unit/ladder-engine-maps-6533.test.ts(asserts every id fromlistCompressionEngines()ranks above"off"with a non-default reduction factor). (thanks @chirag127) -
fix(api): tool-call arguments could render as
[object Object]sequences instead of the real JSON through the/anthropic(Anthropic-shape/messages) routing path (#6459) —appendToolCallArgumentDelta()(open-sse/utils/toolCallArguments.ts), the shared accumulator the streamingopenai-to-clauderesponse translator,openai-responsestranslator, andresponsesTransformerall call to build up a tool call'sarguments/input_json_deltabuffer, treated any non-stringincomingfragment as an empty string. Some upstreams deliver the fulltool_calls[].function.argumentsvalue as an already-parsed JSON object/array instead of the OpenAI-contracted JSON-encoded string; the old code silently discarded that fragment, leavingtool_use.inputempty, and left downstream buffers open to a plain string coercion of the object ([object Object]) once client-side concatenation kicked in.appendToolCallArgumentDelta()nowJSON.stringify()s a non-string, non-null object/array fragment into a valid JSON fragment instead of dropping it, so the assembledpartial_jsonalways parses back into the original structured value. Regression guard:tests/unit/anthropic-toolcall-args-6459.test.ts. (thanks @chirag127) -
fix(providers):
fusioncombo returned the opaque"All fusion panel models failed"503 even when only a minority of panel members were actually cooling down / rate-limited, and a user-suppliedfusionTuning.minPanel=1was silently overridden (#6454) —handleFusionChat()hard-clamped the quorum floor viaMath.min(Math.max(2, cfg.minPanel), panel.length), so an operator-configuredminPanel=1never took effect:collectPanel()'s straggler-grace timer only starts onceok >= minPanel, and with the floor forced to 2 a single fast success plus N slow-failing stragglers never reached quorum, so the panel sat waiting instead of degrading to the survivor. Per-member failure reasons (straggler_dropped/timeout/threw/status_XXX/empty_content/unparseable) were also logged server-side but never surfaced in the 503 body, leaving operators unable to tell a rate-limit fan-fail from a broader outage. Fixed by honoringMath.max(1, cfg.minPanel)and threading afailures: Array<{ model, reason }>collector into the 503 message (model=reasonper entry) — production fix already merged via #6521; this entry backfills the missing CHANGELOG bullet and adds an 11-member,fusion-free-scale regression test matching the original repro shape (a cooling minority must not sink a healthy majority; a genuinely all-failed panel still returns the documented 503). Regression guard:tests/unit/services/fusion-min-panel-and-failure-detail.test.ts+tests/unit/fusion-partial-panel-failure-6454.test.ts. (thanks @chirag127) -
fix(providers):
fusioncombo strategy silently returned a panel member's raw answer instead of the configuredconfig.judgeModelsynthesis (#6455) —handleFusionChat()'s single-survivor "degrade gracefully" path (added for #6454) returned the lone panel answer directly whenever only one panelist succeeded, regardless of whether an explicitjudgeModelwas configured; with the defaultminPanel: 2and a 2-model panel, any single flaky/rate-limited panelist forced this path on every request, so the configured judge (e.g.auto/claude-opus) was never invoked and the client-visible.modelreflected whichever panelist happened to survive. The judge is now still invoked to synthesize a lone surviving answer wheneverjudgeModelis explicitly configured; the cheap direct-answer shortcut is kept only for the implicit case (nojudgeModelset, where the "judge" is justpanel[0]). Regression guard:tests/unit/fusion-judge-model-6455.test.ts+ updatedtests/unit/combo-fusion-strategy.test.ts. (thanks @chirag127) -
feat(combo): sanitized diagnostic trace on an auto-combo terminal failure — instead of an opaque 503, a terminal combo failure now returns a whitelist-projected trace (candidate pool size, attempted count, excluded provider/reason codes, attempt order, and a terminal-reason code) via the new
errorResponseWithComboDiagnostics()/sanitizeComboDiagnostics()inopen-sse/utils/error.ts— provider/model ids and enumerated reason codes only, never keys/tokens/bodies, length- and count-capped. A reasoning-budget-exhausted panel now returns an actionable "increase max_tokens" message rather than a blind retry-limit 503. Regression guard:tests/unit/combo-diagnostics-trace.test.ts. (#6545) — see PR. (thanks @developerjillur) -
fix(providers): image/diffusion models discovered from an upstream catalog (e.g. HuggingFace's live
/v1/models) are no longer advertised as chat models (#6457) — the chat catalog builder defaulted synced models with no modality info toendpoints: ["chat"], sohuggingface/stabilityai/stable-diffusion-xl-base-1.0showed up in the chat/v1/modelslisting and returned400 "not a chat model"when called.catalog.tsnow skips any synced model already registered as an image model for that provider (via the newisRegisteredImageModel()), leavinggetAllImageModels()to list it with the correcttype: "image". Regression guard:tests/unit/image-model-not-in-chat-catalog-6457.test.ts. -
fix(resilience): combo session stickiness never released a pin on a credits-exhausted/banned/expired account, permanently defeating failover for that conversation (#6692) —
applySessionStickiness()(open-sse/services/combo/sessionStickiness.ts) gated the sticky pin only on 5h/weekly usage-percentage headroom, which is orthogonal to account availability: acredits_exhausted/banned/expiredconnection, or one still inside itsrateLimitedUntilcooldown, reports perfectly healthy headroom, so the pin was force-promoted back to the front of the target list on every subsequent turn.clearStickyBinding()also had zero call sites incombo.ts's failure paths, so a quality-validation-rejected 200 (a masked daily-cap refusal) never released the pin either. The gate now also resolves the bound connection's terminal status/cooldown via a new injectable fetcher seam (fail-open on lookup errors, mirroring the existing saturation fetcher), andcombo.ts's two dispatchers (handleComboChat/handleRoundRobinCombo) release the pin immediately at both their connection-exhaustion classification point and their quality-validation-failure branch via the newreleaseStickyPinOnFailure(). Regression guard:tests/unit/repro-6692-sticky-terminal.test.ts+ extendedtests/unit/combo-session-stickiness.test.ts. -
fix(test): replace the bare
expect(true).toBe(true)tautology inplayground-api-tab.test.tsx's SSE test and close thecheck:test-maskinggap that let it slip through for a full cycle (#6404) — a prior pass (#6548) had already swapped the literal toexpect(sendBtn).toBeDefined(), but that stayed just as vacuous: the test's fetch mock returned an empty/v1/modelslist, soApiTab's Send button is alwaysdisabled(!selectedModel) and the SSE branch never runs — the "SSE infra is verified" comment was never true. The test now mocks a real model, drives the model<select>to enable Send, assertssendBtn.disabled === falsebefore clicking, and asserts the streamed SSE delta ("Hello!") actually reached the response editor. Root cause on the detector side:check-test-masking.mjs's tautology subcheck only compares base-vs-HEAD counts within a PR's own diff (headExtTaut > baseExtTaut) and no-ops locally whenGITHUB_BASE_SHA/GITHUB_BASE_REFare unset ("sem base ref — pulando") — so a tautology merged once, or checked with a bare local run, was invisible forever after. Added a new always-on, PR-independent absolute-floor scan (scanBareTautologies+countBareTautologies) over every git-tracked test file for the bareexpect(true).toBe(true)/assert.equal(1,1)/assert.strictEqual(1,1)patterns specifically (deliberately excludingassert.ok(true), which has ~15 pre-existing verified-legitimate try/catch-fallback uses repo-wide and stays governed by the lenient diff-only subcheck) — verified zero pre-existing hits repo-wide once this file was fixed, so the new floor is safe to enforce unconditionally. Regression guard:tests/unit/check-test-masking.test.ts(newscanBareTautologies/countBareTautologiescases) +tests/unit/ui/playground-api-tab.test.tsx. (thanks @chirag127) -
fix(oauth): Codex/ChatGPT (and every other OAuth provider) connection stays stuck showing "Auth Failed" even after a genuinely successful token refresh (#6352) —
updateProviderCredentials()(the sharedonPersistcallback for the manual "Refresh token" route, the reactive per-request refresh inchat.ts, and the Codex/Claude auth-file importers) correctly reused the storedrefresh_token, persisted the newaccess_token, and replaced a rotatedrefresh_token, but never cleared the staletestStatus/lastError*/errorCodefields left over from a prior expired/invalid refresh or upstream 401/403 — only the separate background health-check sweep did that clearing. A successful refresh now resetstestStatusto"active"and clearslastError,lastErrorAt,lastErrorType,lastErrorSource, anderrorCode(an explicittestStatusfrom the caller still wins). Regression guard:tests/unit/codex-oauth-refresh-persist-6352.test.ts. -
perf(health): short-TTL (1s) cache for the frequently-polled
GET /api/monitoring/healthpayload — rebuilding it every request (DB reads + status aggregation across 8 subsystems) was wasteful under rapid polling; the cache stays near-real-time and is invalidated immediately onDELETE(circuit-breaker reset) so a manual reset is reflected at once. Regression guard:tests/integration/monitoring-health-cache.test.ts. (#6553) — see PR. (thanks @developerjillur) -
fix(resilience):
headroomcombo routing did not always select the Codex account with the most free quota (#6379) —orderTargetsByHeadroom(open-sse/services/combo/quotaStrategies.ts) already loaded the per-connection DB snapshot (with decrypted credentials) viaexpandTargetsByQuotaAwareConnections, but discarded it before callinggetSaturation; for Codex,fetchCodexSaturationforwards straight tofetchCodexQuota(connectionId, connection), which needsconnection(or a priorregisterCodexConnection()call, which never happens before headroom ranking runs) to readaccessToken— so it returnednullfor every candidate, saturation failed open to0across the board, and ranking fell back to the original combo order regardless of actual free quota.getSaturation()and the headroomSaturationFetcherseam now accept and thread the loaded connection snapshot through tofetchCodexQuota. Kilo's dup flag vs #5903 was a false positive — that issue is about session-sticky reset-aware/least-used selection, not headroom's Codex saturation lookup. Regression guard:tests/unit/headroom-codex-quota-snapshot-6379.test.ts. (thanks @eidoog) -
fix(providers): custom models a provider actually has are no longer dropped from the Free Provider Rankings when both "Configured only" and "Available only" filters are applied (#6368), follow-up to #6150 —
freeProviderRankings.ts::getProviderModels()only ever walked the staticopen-sse/config/providerRegistry.tscatalog, so a user-added custom model (e.g. a Puterclaude-fable-5model saved as "Claude Fable 5") never entered the candidate model list the ranking scores against, and could never survive the #6150 configured/available filters even when actually configured and available. It now additively merges the provider's custom models (db/models.ts::getCustomModels) into that candidate list via a new pure, de-dupingmergeProviderModels()helper, before scoring/filtering runs — catalog free/paid filtering elsewhere is untouched. Regression guard:tests/unit/free-provider-rankings-custom-models-6368.test.ts. (thanks @shabeer) -
fix(playground): accept a valid dashboard session for
GET/POST /api/playground/presetsunderREQUIRE_API_KEY=true— the Playground page calls this route with a cookie/session and no API key, which previously 401'd the authenticated dashboard;checkAuth()now accepts a management/dashboard session (requireManagementAuth) as an alternative to an API key, while a presented API key must still be valid and the anonymous-allowed default is preserved. Regression guard:tests/integration/presets-dashboard-auth.test.ts. (#6554) — see PR. (thanks @developerjillur) -
fix(providers):
cloudflare-aino longer silently drops image/non-text content parts (#6390) —transformRequest()'sflattenContent()(added for #2539 to satisfy the Workers AI/ai/v1/chat/completionsplain-stringcontentrequirement) mapped any non-text OpenAI content part (e.g.image_url) to""and joined the rest, so a request carrying an image quietly went out as text-only with the attachment gone and no error surfaced. It now throws a clear error on the first non-text part instead of dropping it silently, which the existing top-levelchatCore.tscatch already routes throughbuildErrorBody()/sanitizeErrorMessage()(same pattern asbuildUrl()'s missing-Account-ID error). Regression guard:tests/unit/cloudflare-ai-image-parts-6390.test.ts. -
fix(providers): stop Antigravity connections from falsely reporting all-accounts quota-exhausted (#6295) —
genericQuotaFetcher.ts::percentUsedForQuota()ignored thefractionReportedflag and defaulted an unreported model'sremainingPercentageto 0, which computed as 100% used; sinceconvertUsageToQuotaInfo()takes the worst-case window across a connection, a single model with no reported fraction dragged the whole account intolimitReachedandquotaPreflightskipped it.percentUsedForQuota()now returnsnull(unknown, window ignored) wheneverfractionReported === false, before falling back toremainingPercentage. Regression guard:tests/unit/generic-quota-fetcher.test.ts. -
fix(fusion): the fusion judge no longer replays a panel member's answer via an idempotency-key collision — fusion's panel + judge sub-requests re-enter
chatCoresharing the client's headers, so they derived the sameIdempotency-Key/x-request-idand a panel answer saved under the key was replayed by the judge's check ~1ms later (inside the 5s window), returning a panel member's answer instead of the judge synthesis (observed live onnexa/conversation-fusion).composeIdempotencyKey()now namespaces the key by target provider/model + a digest of the request messages, so sub-requests can't collide while a genuine client retry (same key/model/body) still replays. Regression guard:tests/unit/idempotency-fusion-collision.test.ts. (#6558) — see PR. (thanks @developerjillur) -
fix(providers): grok-cli (Grok Build) now strips
reasoning_effort/reasoningbefore forwarding the request (#6288) — Claude Code sendsreasoning_efforton every request (routing the Opus slot), which Grok Build's upstream chat-proxy endpoint rejects with a 400;transformRequest()'s existingUNSUPPORTEDsampling-param strip list (#5273) never covered it. Regression guard:tests/unit/grok-cli-reasoning-strip-6288.test.ts. -
fix(cli):
omniroute serveno longer hangs silently on a readiness timeout (#6321) — the child server's stdout was piped to"ignore"whenever--log/OMNIROUTE_SHOW_LOGwasn't set (the default), discarding any debug output, andrunWithSupervisor'swaitForServer(...).then((up) => { if (up) {...} })had noelsebranch, so a boot that never became ready produced zero further output after "⏳ Starting server...". Stdout is now buffered alongside stderr (ServerSupervisor.getRecentLog()), and a timeout prints a clear diagnostic plus the buffered output instead of staying silent. Does not by itself explain why boot never completes on a given machine — see the issue for further reproduction. Regression guard:tests/unit/cli-serve-readiness-timeout-6321.test.ts. -
fix(pricing): Pricing Sync dashboard no longer stuck on "Next Sync: Never" / "Synced Models: 0" (#6325) —
pricingSync.tskept sync state (lastSyncTime,lastSyncModelCount) in module-level vars, but the background periodic sync (instrumentation-node.ts) and the dashboard status route (/api/pricing/sync) each import the module from separate Next.js standalone webpack chunks, giving each its own independent state;getSyncStatus()read the (empty) API-route instance's vars. Sync status is now additionally persisted to a newpricing_sync_statuskey_valuenamespace andgetSyncStatus()falls back to it when the local module instance never ran a sync itself. Regression guard:tests/unit/pricing-sync-cross-instance.test.ts. -
fix(api): stop spuriously 403-ing "Invalid request origin" on
POST /api/providers/health-autopilot/actionsfor Docker/LAN dashboard requests (#6277) — the route carried a duplicate per-routevalidateBrowserMutationOrigincheck re-added by the v3.8.42 release squash after PR #5278 centralized origin enforcement in the authz pipeline; the pipeline stripsPEER_IP_HEADERbefore forwarding, so the stale duplicate check could no longer resolve the LAN "direct-local-host" candidate and rejected legitimate same-origin LAN mutations (e.g. clicking "remove cooldown" when accessed via a LAN IP). Removed the duplicate check — origin validation is now solely enforced by the centralized pipeline check, which already handles this case correctly. Regression guard:tests/unit/serial/provider-health-autopilot.test.ts. -
fix(resilience): a bare, unrecognized
403from a no-credential (authType:"none") provider like mimocode or theoldllm no longer permanently bans the connection (#6315, #6345) —classifyProviderError()'s 403 branch only exemptedapikeyproviders from the terminalFORBIDDENclassification, so these free/stateless proxies (no real account/credential to revoke) fell through toFORBIDDENon the first unmatched 403 and gotisActive:false, testStatus:"banned"with no cooldown or retry. The exemption now also coversauthType:"none"providers, returningnull(recoverable) so the existing connection-cooldown/retry layer handles it. Regression guard:tests/unit/errorClassifier-noauth-403-6315.test.ts. -
fix(providers): the Auggie (Augment CLI) executor no longer fails on Windows with
spawn EINVAL(#6304) — the global-npm install exposesauggieas a.cmdshim, which Node'schild_process.spawncannot launch on win32 withoutshell: true. Both spawn sites (streaming + theauggie --versiontest) now go through a sharedbuildAuggieSpawnOptions()that setsshell: process.platform === "win32"; the argv (built bybuildAuggieArgs()with a registry-validatedmodeland a trailing--end-of-options marker) is unchanged, so the argument-injection surface stays closed on non-Windows. Regression guard:tests/unit/auggie-win32-spawn-6304.test.ts. -
fix(api): the dashboard "Test model" action is now a clean connection test (#6240) —
modelTestRunnersent its probe request without an explicit compression override, so whenever the operator's globalcompression.enabledflag was on the test call inherited compression (and any Output-Styles system prompt), polluting the result. The internal test requests now sendX-OmniRoute-Compression: off, andchatCorehonors an explicitoffheader even whencompression.enabledis globally true. Regression guards:tests/unit/model-test-runner-compression-off-6240.test.ts,tests/integration/test-model-compression-off-6240.test.ts. -
fix(startup): an update/restart could crash the whole server at boot with
TypeError: Cannot create property 'message' on string 'Database closed', masking the real failure and 500-ing every request until manually restarted (#6560, plausibly the root cause of #6594's post-upgrade 500) —driverFactory.ts::preInitSqlJs()cached its sql.js WASM adapter per file path in aglobalThis-backed map for idempotency, but never checked whether the cached adapter had since been closed (e.g. bygracefulShutdown/resetDbInstanceracing a reload); reusing that dead handle made the very next query throw sql.js's own bare string"Database closed"(not anError) straight out ofinstrumentation-node.ts's previously-unguardedensureDbInitialized()call, and Next.js's internalregisterInstrumentation()wrapper unconditionally doeserr.message = ...on whateverregister()rejects with — assigning.messageon a primitive string throws in strict mode, so the secondaryTypeErroris what actually crashed the process. Fixed in two parts:preInitSqlJs()now evicts a closed cached adapter and creates a fresh one instead of returning it; a newensureDbReadyForBoot()wraps the DB-init call, normalizes any non-Error throw vianormalizeBootError(), and retries once specifically for a transient "database closed" message (now succeeding against the fresh adapter) before re-throwing anything else as a realError. Regression guard:tests/unit/instrumentation-database-closed-6560.test.ts. -
fix(api):
POST /api/keysno longer hangs 20–90+ seconds on a fresh install, even with valid auth (#6570) —cloudEnableddefaults totrueinsrc/lib/db/settings.ts::getSettings()on any install with no persisted settings row (i.e. every fresh install), so the create-key handler's unconditionalawait syncKeysToCloudIfEnabled()always attempted a real outboundfetch()toCLOUD_URLviasyncToCloud(); when that endpoint is unset/unreachable/slow, the HTTP response blocked until the request settled or timed out — unlike sibling routes (POST /api/keys/:id/regenerate,GET /api/combos), which never touch this side effect at all.src/app/api/keys/route.tsnow dispatchessyncKeysToCloudIfEnabled()fire-and-forget (void) instead of awaiting it; its internal try/catch already logs failures, so cloud sync still runs, it just no longer blocks the response. Regression guard:tests/unit/api-keys-create-no-hang-6570.test.ts(asserts the route resolves in well under 2s even when the Cloud-syncfetch()is stubbed to never settle) + updatedtests/integration/api-keys.test.ts(the pre-existing "triggers cloud sync"/"still succeeds when cloud sync fails" tests, which previously hung indefinitely on this exact path, now await the fire-and-forget sync tick before asserting). -
fix(api): editing any existing OpenAI Codex provider connection in the dashboard returned "Invalid request" and the edit could never be saved (#6562) —
createProviderConnection()(src/lib/db/providers.ts) auto-increments a new connection'sprioritytoMAX(priority)+1per provider with no upper bound, and OAuth-imported connections (Codexcodex-auth/import/import-bulk, up to 50 accounts per call, callable repeatedly — the standard Codex bulk-account-rotation workflow) never pass throughcreateProviderSchema's Zod validation at all, so nothing ever capped that value;EditConnectionModal'shandleSubmitalways resends the connection's currentpriorityunchanged on every save, andupdateProviderConnectionSchemacappedpriority/globalPriorityatmax(100)— a UI-only ceiling the create path never enforced — so the first edit of any connection whose priority had already grown past 100 (routine once a Codex account count exceeds 100) failed validation regardless of which field the user actually changed. Raised the ceiling tomax(100_000)on both fields — still bounded (a genuinely out-of-range value is still rejected), just wide enough to accept priorities the app itself already produces. Regression guard:tests/unit/codex-connection-edit-6562.test.ts(a Codex OAuth connection whose priority already exceeds the old 100 cap now validates + persists on edit; a control payload with a still-genuinely-invalid priority is still rejected with "Invalid request"). -
mimocode: rotate accounts on MiMoCode's rate-limit-style 400s (body-classified) instead of failing on the first account; malformed 400s still fail fast with the real upstream error (#6648 — thanks @pizzav-xyz)
-
fix(cli): compression CLI REST fallback now reads/writes the canonical
defaultModefield (surfaced asstrategy) instead of a nonexistentenginekey, and table output renders nested objects as JSON instead of[object Object](#6571 — thanks @charleszolot) -
fix(providers): web-cookie providers without a
providerRegistry.tsentry (lmarena,gemini-business,poe-web,venice-web,v0-vercel-web) now reportunsupported: trueinstead of silently "OK" (#6309) —validateWebCookieProvider()(src/lib/providers/validation.ts) previously required a registry entry and returned "Provider not found in registry" for these; a fallback toWEB_COOKIE_PROVIDERS[provider].websitewas proposed, but live verification showed the${website}/modelsprobe does not reliably signal session validity for these providers (redirects/SPA 200s regardless of cookie validity — e.g. lmarena's real API isarena.ai, notlmarena.ai; Poe's real endpoint is a GraphQL POST, not a REST/models), so it would report an expired or garbage cookie as valid. Until each provider has a verified, side-effect-free auth probe against its real API host, the fallback now returnsunsupported(no network call) instead of a false positive. Regression guard:tests/unit/web-cookie-validation-fallback.test.ts. (thanks @oyi77) -
fix(api):
POST /api/middleware/hooksandPUT /api/middleware/hooks/[name]no longer leak raw internal error messages in their 500 responses (#6645 — thanks @chirag127) — both catch blocks returnederror?.messagedirectly (Hard Rule #12), which could surface internal SQLite path fragments on a DB failure; both now route throughsanitizeErrorMessage()fromopen-sse/utils/error.ts. Regression guard:tests/unit/middleware-hooks-error-sanitization.test.ts. -
fix(docker): compile better-sqlite3 for the server Docker image (Dokploy/self-hosted builds) via a direct
node-gyp rebuildinsidenode_modules/better-sqlite3, instead ofnpm rebuild better-sqlite3(#6700) — thebuilderstage installs dependencies withnpm ci --ignore-scripts(deliberate: closes the supply-chain surface where a transitive dep's install script runs arbitrary code) and re-enables the native build for the one package that needs it;npm rebuild <pkg>re-runs that indirectly through the package's own install script, which under npm 11 depends on npm's script-allowlist machinery correctly re-enabling it — some self-hosted build environments (e.g. Dokploy) hit a broken/mismatched native binding through that indirection. Invokingnode-gyp rebuilddirectly bypasses npm's script-running layer entirely and is deterministic regardless of npm version. Regression guard:tests/unit/dockerfile-better-sqlite3-node-gyp-6700.test.ts. (thanks @nowhats-br) -
fix(providers): the Cloudflare relay Worker deploy fix in #6416/#6618 still failed uploads in practice — it changed the multipart Content-Type but kept the emitted worker source as an ES module (
export default { fetch(...) }) withmain_modulemetadata; Cloudflare's Workers upload API parses a plainapplication/javascriptscript part as Service Worker syntax regardless of themain_modulemetadata field, andmain_modulerequires the script to actually be an ES module (top-levelexport), so the mismatch still rejected the upload (#6496).buildCloudflareWorkerScript()(src/lib/proxyRelay/cloudflareWorkerScript.ts) now emits Service Worker syntax (addEventListener("fetch", ...), no top-levelexport) and the upload metadata usesbody_partinstead ofmain_module. Regression guard:tests/unit/relay-deploy-5128.test.ts(asserts the emitted script has noexport default, registers afetchlistener, and the upload metadata carriesbody_part/omitsmain_module; also proves the inlinedisPrivateHostname()SSRF guard still rejects bracketed IPv6 loopback/ULA hosts like[::1]/[fd00::1]after the script-body rewrite). (thanks @SeaXen) -
fix(providers): ChatGPT Web (
chatgpt-web) responses rendered raw ChatGPT UI citation markup — private-use marker tokens (e.g.citeturn0search0) andurl…inline-link markers — instead of real Markdown links, since these only ever get resolved client-side by chatgpt.com's own JS usingmessage.metadata.content_references(#6635) —cleanChatGptText()now resolvescontent_references(grouped webpages, footnote sources, inlinewebpage/urlmentions) into[label](url)Markdown links for both the streaming and non-streaming response builders, and for the GPT-5.5 Prostream_handoffpolled-answer path, falling back to stripping any marker that has no resolvable source instead of leaking the raw private-use bytes. The citation parsing/rendering logic was extracted into a new pure sibling module (open-sse/executors/chatgpt-web/citations.ts) to keep the executor under the frozen file-size cap. Regression guard:tests/unit/chatgpt-web-citations.test.ts(non-streaming citation resolution, streaming marker buffering across split SSE chunks, and the Pro-handoff polled-answer path). (thanks @Thinkscape) -
fix(dashboard): the live-dashboard WebSocket descriptor handshake (
GET /api/v1/ws?handshake=1) and the lightweightGET /api/health/pingliveness probe both 401'd for unauthenticated callers, even though both are metadata-only reads intended to be public (#6335) —clientApiPolicyrequired a bearer/dashboard-session before the WS route handler could even return its ownwsAuth/protocol descriptor, and/api/health/pingwas never added toPUBLIC_READONLY_API_ROUTE_PREFIXESdespite its own docstring documenting it as "No auth required."clientApiPolicy.evaluate()now allows an anonymous{kind:"anonymous", id:"ws-handshake"}subject for GET/HEAD/OPTIONS on/api/v1/ws?handshake=1(the route handler still performs its own real wsAuth/dashboard/API-key decision before opening the socket), and/api/health/pingis now inPUBLIC_READONLY_API_ROUTE_PREFIXES. Regression guard:tests/unit/authz/client-api-policy.test.ts(WS handshake allowed, including relative request URLs),tests/unit/public-api-routes.test.ts, andtests/unit/authz/classify.test.ts(/api/health/pingclassifiedPUBLIC). (thanks @JxnLexn) -
fix(providers): wire the Devin cloud-agent provider into the generic provider-page validator and static model catalog, matching the existing
julescloud-agent pattern (#6142) -
fix(providers): honor a provider-level proxy assigned to no-auth providers like MiMoCode Free (#6272)
-
fix(api): merge tool_call continuation deltas that carry only
id(noindex) so tool-call arguments are no longer split/lost in request/response logs (#6276) -
fix(providers): modernize the
lmarenaprovider for the Arena.ai rebrand — route chat througharena.aicreate-evaluation with Chrome TLS impersonation, seed a static Direct-chat Text/Search + Image catalog, and keep thelmarena/lmawire id for back-compat (#6280) — thanks @backryun -
fix(providers): web-provider model discovery updated — qwen-web uses the slash-terminated models endpoint (avoiding a blocked 307 redirect), and kimi-web matches the current request shape (POST with bearer +
kimi-authcookie replay) with its catalog refreshed to the current non-agent models (#6308 — thanks @janeza2). -
fix(logs): the request-log detail modal no longer reopens by itself after being closed — a stale in-flight detail refresh resolved after close and re-triggered the modal open state (#6323 — thanks @xz-dev).
-
fix(providers): update SenseNova Token Plan support — register the token-plan model ids/constants and adjust the SenseNova registry so token-plan accounts route correctly (#6330 — thanks @xz-dev).
-
fix(providers): give v0-vercel-web its own alias so its credentials are detected (#6343)
-
fix(providers): route AgentRouter key validation through the CC wire image so a valid key no longer 403s as "Invalid API key" (#6377)
-
fix(db): stop legacy log-archive migration from deleting the live app-logger directory and crashing startup on a stat/stream race (#6401, #6799)
-
fix(docs): document Turbopack build memory tradeoff and
OMNIROUTE_USE_TURBOPACK=0webpack fallback for RAM-constrained machines (#6409) -
fix(compression): surface silently-dropped stacked-pipeline steps (session-dedup, ccr) and stop the aggregate inflation guard from misfiring on a genuine no-op (#6479, #6480, #6491)
-
fix(providers): honor the
max_tokencapability override in the reasoning-token-buffer output cap (#6524) -
fix(dashboard): the onboarding tier-flow diagram rendered broken — its SVGs lived in the repo-root
images/(not a served path); moved topublic/images/so Next.js serves them (#6538 — thanks @ianriizky). -
fix(routing): the
autocombo's no-auth candidate pool now honors a disabled provider connection's ownisActive=false(the toggle on the main Providers grid card), not just the separate globalblockedProviderssetting — disabling opencode/mimocode/etc. via the grid toggle no longer leaves it in rotation (#6557). -
fix(sse): server-tool literal names (e.g.
web_search) are preserved in message history andtool_choiceinstead of being namespaced/rewritten, so follow-up turns referencing those tools keep working (#6586 — thanks @MikeTuev). -
fix(api): recognize OpenRouter reasoning/reasoning_details in non-streaming OpenAI-to-Claude conversion (#6623)
-
fix(db): share one in-flight sql.js load across concurrent
preInitSqlJs()callers to stop the boot-time thundering-herd re-decode of the whole database file (#6628) -
fix(db): unwrap lone named-parameter objects before
sql.jsstmt.bind()so@/:/$-style named placeholders bind correctly instead of throwing "Wrong API use" (#6802) -
fix(db): break probe-failed/restore loop on large storage.sqlite (#6632 — thanks @KooshaPari).
-
fix(ci): exclude check-test-masking.test.ts's own tautology fixtures from the diff-based test-masking gate and recognize
✗in validate-release-green's failure-line detector (#6634) -
fix(routing): recognize Kimi-style "exceeded model token limit" 400 as context overflow so combo fallback continues to the next target (#6637)
-
fix(cli): Claude Code installed via WinGet is now detected on Windows (the WinGet install path was missing from the binary lookup) (#6647 — thanks @enjoyer-hub).
-
fix(providers): removed obsolete/defunct providers from the catalog (glhf, kluster, cablyai, inclusionai) (#6675 — thanks @backryun).
-
fix(sse): requests rejected before
handleChatCore(circuit-breaker/cooldown gate or combo with all targets exhausted) are now recorded inusage_historytoo, so a key whose traffic was entirely gate-rejected no longer shows "zero requests" in the per-API-key usage counter (#6698). -
fix(sse): unwrap bare
{function:{…}}tools so OpenAI-shape clients no longer have tools silently dropped in Claude translation. (thanks @samir-abis) -
fix(oauth): stop merging distinct Codex OAuth logins that share an email but lack a verifiable account id, preventing silent token overwrite. (thanks @lucasjustinudin)
-
fix(codex): detect "model at capacity"/overloaded errors embedded in a 200-OK SSE stream and surface them as a real error so account fallback rotates, instead of passing them through as a successful response. (thanks @ryanngit)
-
fix(volcengine): clamp
max_tokensto the VolcEngine Ark endpoint cap for the Kimi model so oversized values no longer 400. (thanks @whale9820) -
fix(antigravity): surface aborted/malformed Gemini tool calls (e.g.
MALFORMED_FUNCTION_CALL) as an explicit non-end_turnfinish reason instead of a silent clean completion. (thanks @anhdiepmmk) -
fix(routing): the reasoning-token headroom buffer clamps to the model's explicit output cap instead of inflating past it, and
getExplicitModelOutputCapfalls through to the registry/spec cap when a synced capability row exists without a numericlimit_output(#6714) — thanks @xz-dev -
fix(api):
omniroute health(andhealth components/health watch) returnedError: HTTP 404(#6677) —bin/cli/commands/health.mjscalledapiFetch("/api/health", ...), a route that was moved toGET /api/monitoring/health(src/app/api/monitoring/health/route.ts) without updating the CLI;src/app/api/health/on disk only hasdegradation/route.tsandping/route.ts, no top-level handler.runHealthCommand()/runHealthComponentsCommand()now call/api/monitoring/healthand read its actual payload shape (activeConnections,circuitBreakers: {open, halfOpen, closed},memoryUsage) instead of the old, nonexistentrequests/breakers/cache/memoryfields. Regression guard:tests/unit/cli-health-monitoring-route.test.ts. -
fix(startup): webpack build broke on case-insensitive filesystems (macOS APFS default, Windows) with a casing-collision warning plus "not exported" errors in
StudioConfigPane.tsx/ChatTab.tsx(#6584) —src/app/(dashboard)/dashboard/playground/components/ReasoningControls.tsx(the component) andreasoningControls.ts(the utils module) shared the same lower-cased stem in the same directory, and two importers used the extensionless formfrom "./reasoningControls", the exact resolution path that becomes ambiguous once casing is folded. Renamed the utils module toreasoningControlUtils.ts(no collision) and updated the 3 import sites. Regression guard:tests/unit/case-collision-6584.test.ts(scanssrc//open-sse/for any same-directory, case-only filename collision). (#6584) -
fix(build): Turbopack production build emitted an "Overly broad patterns can lead to build performance issues" warning per entry point importing
src/lib/agentSkills/generator.ts(603 warnings reported on v3.8.46, up from 379 on v3.8.45) (#6582) —generator.ts'soutputBaseis built aspath.isAbsolute(outputDir) ? outputDir : path.join(process.cwd(), outputDir), whereoutputDiris a runtime function parameter, not a compile-time literal, so Turbopack's build-time file-tracing analyzer can't statically narrow the several dynamicreaddirSync/rmSync/readFileSync/writeFileSynccall sites a few lines below and falls back to a project-wide glob; #6366's commit message claimed to "anchor the base path with a literal" but the shipped code never did. Since this fs access is legitimate and bounded (skills/<id>/SKILL.md, ~48 known IDs),next.config.mjs'sturbopack.ignoreIssue(Next.js 16.2+) now suppresses this specific, known-benign diagnostic, mirroring the existingwebpack.ignoreWarnings/isNextIntlExtractorDynamicImportWarningprecedent already in the same file for the webpack path. Regression guard:tests/unit/next-config.test.ts(asserts theturbopack.ignoreIssuerule shape targetingsrc/lib/agentSkills/**). -
fix(providers): Codex Desktop requests to
gpt-5.3-codex-sparkfailed with[400]: Tool 'image_generation' is not supported with gpt-5.3-codex-spark, even on paid-plan accounts (#6651) —CodexExecutor.transformRequest(open-sse/executors/codex.ts) only dropped the Codex Desktop-injectedimage_generationhosted tool whenisCodexFreePlan()matched the account's plan, with no awareness that Spark-scope Codex models rejectimage_generationupstream regardless of plan.dropImageGenerationnow also drops it whengetCodexModelScope(model) === "spark"(the existing Spark classifier fromopen-sse/config/codexQuotaScopes.ts), independent of account plan. Regression guard:tests/unit/codex-spark-image-generation.test.ts(thanks @alltomatos for independently catching and fixing it via #6819). -
fix(providers): the provider quota card's weekly/session bars re-sorted by remaining percentage instead of staying in a fixed, deterministic order (#6687) —
QuotaCardExpanded.tsx'ssortQuotasByRemaining()(added in #5977) was applied unconditionally viauseMemo(() => sortQuotasByRemaining(quotas), [quotas]), undoing the deterministicCODEX_QUOTA_ORDER/GLM_QUOTA_ORDERwindow orderquotaParsing.ts'ssortCodexOrder()/sortGlmOrder()(added in #6336) already established for Codex and the GLM family — since #6336 never touchedQuotaCardExpanded.tsx, the two orderings never composed, so e.g. a Codexsessionwindow with less headroom thanweeklyrendered after it instead of staying first. A newhasFixedQuotaOrder()(quotaParsing.ts) andresolveQuotaDisplayOrder()(QuotaCardExpanded.tsx) now skip the remaining-% re-sort for providers with a fixed window order, threadingproviderIdfromQuotaCard.tsxthrough to the display layer; every other provider still gets the remaining-% sort. Regression guard:tests/unit/quota-card-expanded-fixed-order-6687.test.ts. -
fix(i18n): pt-BR was missing 194 UI keys present in
en.json— a real, silent data-sync gap, not covered by any duplicate/mislabeled #6694 (that issue's 9providers.*keys are disjoint, present-but-untranslated sentinels caused by a separateproviderText()fallback bug) (#6695) —scripts/i18n/sync-ui-keys.mjs(which mirrors newly-addeden.jsonkeys into every locale) wasn't re-run after recenten.jsonadditions, and the CIi18n:check-ui-coveragegate only fails a locale below an 80% threshold, so pt-BR stayed green at 93.8% coverage despite the gap. Backfilled all 194 missing keys intosrc/i18n/messages/pt-BR.json(translated to Brazilian Portuguese, no leftover__MISSING__markers) vianpm run i18n:sync-ui -- --locale=pt-BR+ manual translation. Regression guard:tests/unit/i18n-pt-br.test.ts(new case asserting fullen.json→pt-BR.jsonkey parity, so a future drift fails a fast unit test instead of silently degrading the coverage percentage). -
fix(startup):
omniroute --mcpcrashed at Node ESM link time withERR_MODULE_NOT_FOUNDforioredison installs where the published MCP bundle didn't happen to haveioredisrescued from a parentnode_modules(#6559) —src/shared/utils/rateLimiter.tshad a top-level staticimport Redis from "ioredis"; that module is only ever reached via a lazyawait import(...)several call-sites deep in the MCP tool chain, but esbuild's--packages=externalbundling of the MCP server (scripts/build/prepublish.tsStep 8.5) still hoisted rateLimiter.ts's own static import into a real top-level ESM import in the compileddist/open-sse/mcp-server/server.js, forcing Node to resolveioredisat module-link time — before any--mcpstartup code runs — andioredisis not guaranteed to ship in the MCP-only bundle'snode_modules.getRedisClient()now lazily importsioredison first use (matching the established soft-dependency pattern insrc/lib/quota/redisQuotaStore.ts) while still throwing synchronously when Redis isn't configured. Regression guard:tests/unit/build/mcp-bundle-no-eager-ioredis.test.ts(bundles the real MCP server entrypoint with the exact publish-time esbuild flags and asserts no top-level staticioredisimport remains, while the pre-existing lazyawait import("ioredis")inredisQuotaStore.tsstays intact). -
fix(providers): Kiro sent the adaptive-thinking
additionalModelRequestFieldsenvelope forclaude-sonnet-4.5/claude-haiku-4.5, which Kiro/CodeWhisperer rejects upstream with a raw[400]: additionalModelRequestFields is not supported for this model(#6576) —buildKiroPayload()(open-sse/translator/request/openai-to-kiro.ts) gated the field on the generic Anthropic-APIsupportsReasoning()capability flag, which istruefor both models on Anthropic's direct API but does not reflect what Kiro's CodeWhisperer backend actually accepts; onlyclaude-sonnet-5is confirmed adaptive-thinking-capable there. A new Kiro-specific allowlist (supportsKiroAdaptiveThinking()inopen-sse/translator/request/openai-to-kiro/adaptiveThinking.ts) now gates the envelope instead. Regression guard:tests/unit/repro-6576-kiro-thinking-unsupported-model.test.ts. -
fix(translator): Cursor's local Subagent tool call is no longer rejected with
cloud_base_branch may only be specified when environment equals cloud— the Responses→Chat tool-arg cleanup (stripEmptyOptionalToolArgs) was scoped to Claude Code'sReadtool only, so Cursor'sSubagenttool passed through with the cloud-onlycloud_base_branch: ""(Cursor treats an empty string as "specified" and rejects the call before starting the local subagent). The cleanup now covers an allowlist ofRead+Subagent; arbitrary tools are still left untouched (empty strings/arrays can be valid payloads for them). Regression guard:tests/unit/openai-responses-subagent-strip-2446.test.ts. (thanks @like3213934360-lab) -
fix(translator): GLM 5.2 (and other OpenAI-compatible upstreams that stream a tool call's
idandfunction.namein separate SSE chunks) no longer produce an empty tool name /No such tool available:error through the Claude/messagespath — theopenai-to-claudestreaming translator emittedcontent_block_startimmediately on the id-only chunk with an emptyname, and the Claude SSE protocol cannot patch a block after it is emitted, so the later name-only chunk was silently dropped. It now deferscontent_block_startuntil the tool name arrives (falling back to starting the block when arguments arrive first), so the emittedtool_usealways carries the real name. Regression guard:tests/unit/openai-to-claude-glm-split-tool-name-2077.test.ts. (thanks @itiwant) -
fix(resilience): OmniRoute didn't respect an exhausted Ollama Cloud (or any other apikey-category provider) quota — it retried the account seconds later instead of waiting out the real reset window (#6638) —
shouldPreserveQuotaSignalsFor429()/checkFallbackError()(open-sse/services/accountFallback.ts) only applied body-text quota classification (daily/monthly/weekly quota-exhausted detection) to OAuth-category providers; apikey-category 429s (Ollama Cloud, OpenAI, etc.) always fell through to the generic short rate-limit cooldown regardless of what the error body said, andparseRetryFromErrorText()also had no support for day-granularity reset hints ("Your quota will reset in 3 days.") — only Xh/Ym/Zs combos. An explicit quota-exhausted signal in the body (looksLikeQuotaExhausted()) now overrides the apikey-category default via the newshouldPreserveQuotaSignals()(open-sse/services/quotaResetParsing.ts), andparseDayGranularityResetMs()parses whole-day reset countdowns so the real multi-day window is honored instead of a few seconds of backoff. Regression guard:tests/unit/issue-6638-ollama-quota.test.ts+ 2 alignedtests/unit/account-fallback-service.test.tscases that previously asserted the buggy rate_limit_exceeded/undefined-dailyQuotaExhausted behavior for apikey-provider quota text. -
fix(resilience): a combo step "pinned" to one fingerprint account (mimocode/mcode/opencode multi-account providers) never actually resolved to that account, so it couldn't fail over when the pinned account was depleted (#6696, relates #6612) — the combo builder UI encodes an account pin as a composite connectionId (
${rowId}|fp|${fingerprint},src/lib/combos/builderOptions.ts), butexpandTargetsByFingerprints()(open-sse/services/combo/fingerprintExpansion.ts) looked that composite string up directly inconnectionById(keyed by real DB row ids), gotundefined, and passed the target through unchanged, still carrying the bogus composite id — so downstream credential resolution could never match it either.expandTargetsByFingerprints()now splits the|fp|composite id back into the real connection row id + the pinned fingerprint (newsplitFingerprintPin()helper) before any lookup, resolving the target to the real connectionId (with the pinned fingerprint carried on the newpinnedFingerprintfield) instead of the inert composite string. Regression guard:tests/unit/combo-fingerprint-pin-6696.test.ts. -
fix(api): Responses passthrough emitted event-only SSE frames (no
data:line) for every dropped commentary event, breaking the OpenAI Python SDK'ssse.json()parser (#6561), follow-up to #6199/#6232 — the commentary-dropcontinue;branches inopen-sse/utils/stream.tsskipped thedata:line for a dropped commentary event but never cleared the already-bufferedevent:line for that same frame, so the next blank line flushed the staleevent:line alone. Both drop sites now callclearPendingPassthroughEvent()beforecontinue, discarding the buffered prefix along with the dropped payload; the commentary-drop decision itself was extracted into a newopen-sse/utils/responsesCommentaryDrop.tsso the fix does not grow the frozenstream.ts. Regression guard:tests/unit/responses-commentary-event-frame-6561.test.ts(realisticevent:\ndata:\n\nframes — the existing #6199 test only used baredata:lines and never exercised this path). -
fix(compression):
/api/compression/preview's top-leveloriginalTokens/compressedTokensdiverged fromengineBreakdown[0]'s counts for the same single-engine run (tiktoken outer counts vs theJSON.stringify(...).length/4estimate per engine), worst on small inputs. A newreconcileSingleEngineTokens()overwrites the single-engine breakdown entry with the outer, more accurate figures; multi-step pipeline breakdowns are left untouched (#6488). Regression guard:tests/unit/compression/preview-outer-engine-token-reconcile-6488.test.ts. -
fix(resilience): account selection could pick an account already out of quota upstream on every credentialed route except
chat/codex(#6686) —getProviderCredentials()(src/sse/services/auth.ts) only skips a connection when a local cache already flags it exhausted (isQuotaExhaustedForRequest/src/domain/quotaCache.ts); it never itself calls the registered upstreamQuotaFetcher. OnlygetProviderCredentialsWithQuotaPreflight()performs that live upstream check, and it was wired into exactly 2 call sites (src/sse/handlers/chat.ts,src/app/api/internal/codex-responses-ws/route.ts) — every other credentialed route (rerank,images/generations,images/edits,audio/transcriptions|speech|translations,videos/generations,music/generations,ocr,providers/[provider]/embeddings,providers/[provider]/images/generations,web/fetch,moderations,search) called the plain, cache-only selector, so an account whose cache entry was never populated (e.g. its first request landed on one of these routes) could be selected even at 0% quota remaining. Those 14 call sites now go throughgetProviderCredentialsWithQuotaPreflight()instead, matching chat/codex coverage. Regression guard:tests/unit/issue-6686-quota-preflight-coverage.test.ts(static check that none of the routes call the plain selector anymore + a behavioral check that the preflight-aware selector blocks a 100%-used account). -
fix(api):
reasoning_content(extended-thinking text) was silently dropped from/v1/chat/completionsSSE on theclaude-webandv0-vercel-webexecutors (#6662) — every chunk builder in both adapters hardcodeddelta: { content: ... }with no reasoning path, unlike the established pattern already used bydefault.ts/deepseek-web.ts/bedrock.tsand the real-Anthropic-APIclaude-to-openai.tstranslator (thinking_delta→reasoning_content).v0-vercel-web.tsnow forwards an upstreamdelta.reasoning_contentfield (streaming and non-streaming) the same waydeepseek-web.tsdoes.claude-web.ts'sbuildClaudeStreamingResponsenow maps acontent_block_start(type: "thinking")/content_block_delta(delta.thinking) pair ontodelta.reasoning_content, andclaude-web/payload.ts'stransformToClaude()no longer hardcodesthinking_mode: "off"— a newwantsExtendedThinking()derives it from the request'sreasoning_effort/reasoning.effort/thinking.typesignal, so extended thinking can actually be requested. Regression guard:tests/unit/issue-6662-repro.test.ts(RED→GREEN for both adapters). -
fix(api): the compression config PUT schema now accepts
enableRenderersfor the RTK engine instead of rejecting the documented option (#6703, #6757 — thanks @alltomatos, with an independent duplicate fix from @chirag127 via #6756). -
fix(api): raised the provider
apiKeylength cap for cookie-based web providers, whose session-cookie credentials legitimately exceed the previous limit (#6715, #6759 — thanks @alltomatos). -
fix(ci): publish electron-updater
latest*.ymlmanifests in electron release assets so auto-update can find them (#6766) -
fix(i18n): translate hardcoded Portuguese dashboard strings to English (#6761, #6768) (#6769 — thanks @chirag127).
-
fix(providers): strip redundant node prefix when resolving custom OpenAI/Anthropic-compatible connections by raw connection id, preventing double-namespaced model ids from 400ing upstream (#6772)
-
fix(providers): scope nvidia NIM 404s to the single failing model instead of cooling down the whole connection (#6773)
-
fix(codex): bump the default Codex CLI client identity from
0.142.0to0.144.0for compatibility with newer Codex-backed models (#6780) — thanks @quanturbo -
fix(ci): the blocking "Impacted unit tests (TIA)" step false-redded any PR whose impact graph reached a dashboard component — it ran every selected test under
--import tsx/esm, buttests/unit/dashboard/**requires the--import tsxCJS transform (ESM-only deep imports like@lobehub/icons/es/*), exactly as the canonicaltest:unit:ci:shardalready does per segment. The impacted selection is now split by segment with matching loaders (closes #6787). -
fix(translator): read PDF/video
file_dataattachments on the OpenAI→Gemini/Antigravity and OpenAI→Claude paths so multimodal documents (not just images) reach the upstream — PDFs map todocument/inlineDataand videos keep theirvideo/mp4mime instead of being dropped (#6790 — thanks @Witroch4, with an independent report/fix from @samimozcan via #6762/#6753). -
fix(providers): ensure DeepSeek Web SSE emits [DONE] after FINISHED (#6791 — thanks @Pitchfork-and-Torch).
-
fix(api): the compression config
PUTschema (stackedPipelineStepSchema) now accepts everyENGINE_CATALOGid — the structural enginessession-dedup/ccr/headroom/relevance/llmlingua/omniglyphand theaggressiveultraintensity — so aGET→PUTround-trip of a stacked pipeline no longer 400s on a valid engine the discriminated union had omitted (#6747 — thanks @Pitchfork-and-Torch). -
fix(cursor): send the Agent CLI build id as
x-cursor-client-versionso Cursor upstream accepts requests from the current CLI build instead of a stale hardcoded version (#6795 — thanks @andrewmunsell). -
fix(cli): waitForServer() no longer reports ready from a raw TCP accept alone — requires a fast HTTP rejection or a real health response, so the "OmniRoute is running!" banner no longer fires 30-60s before the server can actually answer requests (#6800)
-
fix(sse): de-flake timing-sensitive combo cooldown/breaker tests + add explicit MCP audit shutdown timeout (#6803)
-
fix(codex): strip include from compact responses requests (#6805 — thanks @yinaoxiong).
-
fix(dashboard): surface Claude extraUsage credits in quota card when quotas is empty (#6806)
-
Request count by provider & date: Dashboard → Analytics now shows a dedicated table of request counts grouped by provider and calendar date (plus token totals), for providers that bill per-request rather than per-token — sortable columns and a single-date filter. New
getProviderDailyUsageRows()query (src/lib/db/usageAnalytics.ts) and its ownGET /api/usage/requests-by-provider-dateroute (kept separate from the frozen/api/usage/analyticsroute). Regression guard:tests/unit/db-provider-daily-usage-4009.test.ts. (#4009 — thanks @tjengbudi) -
fix(resilience): an Ollama Cloud (or any apikey-category provider) account that hit a weekly usage cap kept getting retried every few minutes instead of backing off (#3709) — the upstream 429 body ("you (<account>) have reached your weekly usage limit") was invisible to
checkFallbackError's existing subscription-quota-text classifier (Issue #2321) because that branch is gated byshouldUseQuotaSignal, which is oauth-only, so apikey providers likeollama-cloudfell through to the generic exponential backoff (~1s, capped at 2min) — one account took 285x429 in 48h. A newisWeeklyUsageLimitText/buildWeeklyQuotaFallbackclassifier (extracted, with the existing subscription-quota logic, into a newopen-sse/services/quotaTextCooldowns.tsmodule so the frozenaccountFallback.tsdidn't have to grow) runs unconditionally and applies a 24hQUOTA_EXHAUSTEDcooldown regardless of provider category. Regression guard:tests/unit/ollama-cloud-weekly-quota-cooldown-3709.test.ts. -
fix(providers): an explicit
thinking.budget_tokens: 0is now honored in the OpenAI→Gemini transform (thinking disabled) instead of being treated as unset (#6813, #6821 — thanks @alltomatos). -
fix(bootstrap): filter empty
process.envvalues before spawning embedded services so a blank env var no longer crashes the Docker bootstrap in a restart loop (#6828 — thanks @AndrianBalanescu). -
fix(providers): classify upstream
404responses asMODEL_NOT_FOUND(model lockout) instead of a retryable provider error, stopping the retry storm when a single model is missing (#6829 — thanks @AndrianBalanescu). -
fix(mcp): de-duplicate
TOTAL_MCP_TOOL_COUNTby tool name instead of double-counting collections (#6854) -
fix(usage): xAI's exact provider-reported
cost_in_usd_ticksno longer silently acceptsnull/""/negative values as a valid$0exact cost —extractUsageFromResponse()(open-sse/handlers/usageExtractor.ts) andnormalizeUsage()(open-sse/utils/usageTracking.ts) now requiretypeof value === "number" && Number.isFinite(value) && value >= 0instead of coercing withNumber(x), so a malformed exact cost correctly falls back to the token-based estimate instead of masking it with a bogus$0. Regression guard:tests/unit/xai-exact-cost-2453.test.ts(rejects null/empty/negative exact costs on both call sites). (#6856 — thanks @KooshaPari) -
fix(plugin): the
@omniroute/opencode-plugindynamic provider hook stopped embedding its OC-1.17.8+-gate-compatibleopencode--prefixed provider id into model routing fields (ModelV2.id/providerID, combo catalog keys) — OmniRoute's server has noopencode-<x>provider alias, so every dispatched model failed credential lookup with "No credentials for opencode-omniroute" (#6859). -
fix(sse): apply cliproxyapiModelMapping at CLIProxyAPI dispatch time (#6876)
-
fix(sse): defer
response.completeduntil a trailing usage-only chunk arrives on/v1/responsesstreams (#6906) -
fix(cli): ship
head-response-guard.cjsinto the standalone bundle —server-ws.mjsimported it without a matchingEXTRA_MODULE_ENTRIESentry, so everybuild:releasedist crashed at boot withERR_MODULE_NOT_FOUND; a new regression test derives the required sidecars fromserver-ws.mjsimports (#6908) -
fix(sse): wire the shared quota-fetch throttle into DeepSeek, Bailian, OpenCode, and Crof quota fetchers, not just Codex (#6911)
-
fix(sse): rename client-sent
max_completion_tokenstomax_tokensfor providers/models that only accept the legacy field (e.g. Volcengine Ark / DeepSeek), mirroring the existing reverse rename from #1961 (#6912) -
fix(sse): set includeServerSideToolInvocations on Antigravity tool cloak decoys (#6914)
-
fix(sse): classify LAN embeddings providers (10/8, 192.168/16, CGNAT) as no-auth instead of forcing bearer auth (#6925)
-
fix(sse): Qwen Web executor no longer sends
[object Object]when a message uses structured (array) content — the text parts are now flattened (#6927) -
perf(api): relay chat-completions routes now thread the already-fetched
RelayTokenintocheckRateLimit, skipping a redundantSELECT * FROM relay_tokens WHERE id = ?re-query on every request (#6930) -
fix(sse):
normalizeCodexMessageContentPartnow rewrites explicittype: "input_text"(not justtype: "text") tooutput_texton assistant-role Codex Responses input parts, so replayed assistant history sent by codex-cli asinput_textis no longer rejected by the Codex/OpenAI backend (#6932) -
fix(sse): omit removed
attachmentsfield from Muse Spark Web (Meta AI) persisted GraphQL query to fixUnknown type "AttachmentInput"502s (#6935) -
fix(dashboard): label audio/embeddings/image compatible providers by kind instead of "Chat" on ProviderCard (#6936)
-
fix(api): model-list discovery for LAN-local OpenAI-compatible providers (e.g. LM Studio) now uses the same local-first SSRF guard as the connection test, instead of the stricter guard that always blocked LAN hosts (#6939)
-
fix(providers):
openai->geminitransform now mapsreasoning_effort: "none"tothinkingConfig.thinkingBudget: 0(withincludeThoughts: false), giving callers an explicit, documented off-switch for Gemini thinking; the no-knob-at-all default injection (#4170) is unchanged (#6813, thanks @rafaumeu) -
fix(sse): escape backslash before brackets in ChatGPT-web citation link text, preventing a citation label containing
\from corrupting the generated Markdown link (#6944) — thanks @brick30llc-ctrl -
fix(oauth): tokenHealthCheck now lowercase-normalizes
conn.providerbefore checkingROTATING_REFRESH_PROVIDERS.has()and the GitHub Copilot sub-token refresh guard, so mixed-case provider values (e.g. "OpenAI", "Github") no longer bypass the rotating-refresh-token skip or the Copilot sub-token refresh (#6947) -
fix(sse): make Codex Responses tool-arg normalization schema-aware — drop values equal to the tool's declared JSON Schema
default, generalize empty-optional stripping to any tool (not justRead/Subagent) viaschema.required, and thread each tool's schema from the request'stools[]into the streaming response translator (#6951) -
fix(sse): drop internal commentary-phase Responses output in TRANSLATE-mode streams, not just PASSTHROUGH — codex/Responses-upstream routes translated into another client format (e.g. Claude Code) no longer leak duplicate prose and narrated tool-call arguments into the client text channel (#6952)
-
fix(combos): embeddings-only and rerank-only models (e.g. JinaAI, Gemini auto-imported, OpenRouter custom, reranker models) no longer disappear from the combo builder's model picker — the leftover chat-only
isChatCapablegate inaddModelOption()has been removed (#6975). -
fix(combos): when 2+ distinct model ids from the same provider would render an identical display name in the combo builder picker (e.g. Mistral's
codestral-latest/codestral-2508aliases sharing one upstream catalog name), each colliding entry now falls back to its own id as the display label so every row stays visually distinguishable and findable (#6957).
📝 Maintenance
-
chore(release-captain): v3.8.47 pre-flight closed every deterministic release-tip base-red (#6967): restored the
no-explicit-anyseverity silently downgraded by #6786 (439 bulk suppressions had stopped matching), made the openadapter live-catalog test deterministic (test.after()was tearing the DB down mid-request), aligned the emergency-fallback and Cloud Code Gemini tests with the #6912/#6943 contracts, backfilled the #6909 i18n keys (en + pt-BR), re-exportedrelayProbeStats(db-rules), documentedOMNI_MAX_CONCURRENT_CONNECTIONS, and allowlisted migration gap 121. (thanks @diegosouzapw) -
chore(security): unbiased crypto digits for the doubao synthetic device id (CodeQL
js/biased-cryptographic-random); 405 method-first for/api/keys/{id}/devices(dast-smoke); Zod-validation forPOST /api/github-skills; the missingomni-github-skillsregistry entry + catalog count alignment. (thanks @diegosouzapw) -
chore(quality): cycle-close ratchet work — cleared the cycle's 11 net-new ESLint errors and made
validate-release-greensuppressions-aware; cleared the 2 remaining heavy-gate reds on the release tip; cycle rebaselines (cognitive/file-size/zizmor/coverage pcts) with justification keys. (thanks @diegosouzapw) -
chore(open-sse): removed the vestigial
// @ts-nocheckdirective fromopen-sse/utils/usageTracking.ts(#6173) —tscunder the standardtypecheck:coregate reports 0 errors for this 595-line hot-path file (executed for every provider response), so the suppression was no longer needed; removing it restores type-checking on the token-usage extraction/normalization path. (thanks @KooshaPari) -
chore(cli): the shell-completion cache paths (
readCache/refreshCache/writeCache) inbin/cli/commands/completion.mjsno longer swallow errors into a barecatch {}(#6257) — each now binds the error and, when the newOMNIROUTE_DEBUG_COMPLETIONenv var is set, emits a[omniroute completion]diagnostic tostderr; the caches still fail silently by default so a missing/corrupt cache never breaks tab-completion. (thanks @KooshaPari) -
chore(quality):
validate-release-green --full-cireproduces the fullci.ymlstatic gate set locally — the pre-flight now readsci.ymlitself and runs everynpm run check:*from thelint/quality-gate/quality-extended/docs-sync-strict/pr-test-policyjobs (--ratchet flags preserved,test-maskingagainstGITHUB_BASE_REF=main), skipping only the non-localpr-evidence/codeql-ratchet. Closes the gap where 11 static base-reds leaked to the v3.8.46 release PR in ~2h of layered CI. Also wired intonightly-release-greenso a static base-red opens a tracking issue the night it lands. Regression guard:tests/unit/validate-release-green.test.ts(+5extractCiGatescases). -
refactor(usage):
saveRequestUsage(entry: any)is now typed with a newUsageEntryinterface mirroring theusage_historycolumns 1:1 (#3512) — the other strayanys insrc/lib/usage/usageHistory.ts(getUsageHistoryfilter, thegetUsageDbnext-cursor cast,appendRequestLog's legacytokensparam,getRecentLogs's catch) were cleaned in the same pass, so the file now sits in thecheck:any-budget:t11zero-anyallowlist. The DB-entity ↔ TS-interface convention is documented indocs/architecture/CODEBASE_DOCUMENTATION.md§11. -
Merge-train script (
scripts/release/merge-train.sh): batch-validates N queued PRs as ONE merged result on the runner box — merges every queued PR into a throwaway worktree cut from the release tip, runs the fast-gates parity suite once, and prints the--adminevidence block per PR (merge-gates §7). Replaces O(N²) per-PR CI re-runs in merge-storms. Regression guard:tests/unit/merge-train-plan.test.ts. -
release:
list-uncovered-commits.mjsnow unions the CHANGELOG scan window withchangelog.d/fragment refs (filename<PR>-prefix + every#Nin the body), so a commit covered only by a fragment is no longer reported as an uncovered reconciliation gap (#6857 via #6878) -
chore(quality): restore no-explicit-any severity to error (silently downgraded by #6786, broke 439 bulk suppressions), fix 2 unsuppressed anys in repro-6912 test, allowlist migration gap 121, freeze-bump stream.ts/ProxyRegistryManager/tokenHealthCheck (combined-merge drift)
-
chore(base): pt-BR/en i18n keys for #6909 relay-repair/free-pool UI + align Cloud Code Gemini defaults test with the #6943 non-thinking-model contract
-
chore(base): re-export relayProbeStats from localDb (db-rules gate, #6909 follow-up) and document OMNI_MAX_CONCURRENT_CONNECTIONS in .env.example/ENVIRONMENT.md (env-doc gate, #6590 follow-up)
-
ci: unit fast-path sharding doubled 2→4 (halves the heaviest job's wall time) (#6781); the 3 heaviest fast-path jobs can route to the self-hosted VPS runner pool behind
USE_VPS_RUNNER(#6691);VPS_ALWAYS_ONkeeps the dedicated 24/7 CI host up across releases (teardown becomes a no-op) (#6693). -
docs: routing-strategy count reconciled to 18 across AUTO-COMBO.md, README and AGENTS.md, and
p2ccasing fixed to matchROUTING_STRATEGY_VALUES(#6643, #6644, #6646 — thanks @chirag127); CLAUDE.md updated with the renamed review/triage/implement skill-family names (#6663). -
chore(release): v3.8.47 pre-flight — relocate #6943 orphan test to a collected path, restore no-explicit-any suppression match, testFrozen bump translator-openai-to-gemini (1541→1553), zizmor rebaseline 159→169
-
docs(readme): fix stale counts — 18 routing strategies (adds the missing
pipelinerow), 94 MCP tools, 12-factor Auto-Combo scoring. -
chore(ci): fix two shared base-reds on the release tip that blocked the PR queue — register
cliproxyapi-model-mapping-dispatch.test.tsinstryker.conf.jsontap.testFiles(mutation-coverage gap left by #6903) and updateprovider-models-route-codex.test.tsto expect Codex client version0.144.0(stale assertion left by #6780's production bump). -
docs: refresh
llm.txtto the current project state (248 providers, 94 MCP tools / 30 scopes, 18 routing strategies, 12-factor Auto-Combo scoring, v3.8.47) and sync its 42 i18n mirrors; move the implemented design-system plan from the repo root todocs/architecture/DESIGN_SYSTEM.mdrewritten as a reference doc.
…truncated — the complete v3.8.47 cycle notes live in CHANGELOG.md.
What's Changed
- fix(build): v3.8.47 hotfix — npm tarball missing head-response-guard.cjs (#7065) + electron win spawn by @diegosouzapw in #7055
Full Changelog: v3.8.47...v3.8.48
详细ChangeLogv3.8.47
2026年07月13日
- 9router Codex import: the Codex bulk-import endpoint (
POST /api/oauth/codex/import) now accepts 9router's camelCase account export (accessToken/refreshToken/idToken/expiresAt+ nestedproviderSpecificData), not just snake_case —normalizeCodexImportRecordmaps the camelCase aliases onto the existing snake_case keys, filling each only when absent so snake_case/mixed exports keep working unchanged (#6665) — thanks @deadcoder0904. Regression guard:tests/unit/codexBulkImport.test.ts(9router camelCase record, pre-suppliedproviderSpecificDatawithout an id_token, snake_case-not-overridden, and a full{accounts:[...]}flatten).
✨ New Features
-
feat(plugins): Langfuse observability plugin. (#6577 — thanks @chirag127)
-
feat(combo): context requirements config for per-target filtering in combos. (#6907 — thanks @oyi77)
-
feat(providers): icons for 46 providers that were missing images. (#6926 — thanks @oyi77)
-
feat(compression): vendored GCF (Headroom) codec updated to spec v3.2 (nested flattening). (#6838 — thanks @blackwell-systems)
-
feat(proxy): shorthand proxy formats + protocol header mode for bulk import. (#6867 — thanks @growab)
-
feat(provider): OpenVecta AI inference gateway. (#6833 — thanks @hajilok)
-
feat(i18n): Traditional Chinese (zh-TW) localization for frontend and CLI. (#6320 — thanks @lunkerchen)
-
feat(xai): route xAI clients to Grok's native
/v1/responsesendpoint. (#6709 — thanks @diegosouzapw) -
feat(routing): per-model web-search/web-fetch interception rules. (#3384, #6814 — thanks @diegosouzapw)
-
feat(release):
changelog.d/fragments — eliminates the CHANGELOG merge-storm cascade. (#6783 — thanks @diegosouzapw) -
feat(quality):
validate-release-green --full-cireproduces the entire ci.yml static gate set locally. (#6583 — thanks @diegosouzapw) -
feat(dashboard): sidebar quick-filter — a search input at the top of the expanded dashboard sidebar (
src/shared/components/Sidebar.tsx) filters nav sections/groups/items client-side by label as you type, reusing the existingcommon.search/common.noResultsi18n keys (zero new locale edits) and the sharedInputicon="search"pattern; matching sections auto-expand while searching (bypassing the accordion/pin state) and collapse back to normal once the query is cleared. Pure filtering logic extracted intofilterSidebarSectionsByQuery()(src/shared/utils/sidebarSearch.ts) for isolated unit testing. Regression guard:tests/unit/sidebar-search-filter.test.ts,src/shared/components/Sidebar.search.test.tsx. (#4013 — thanks @crochabe-cyber) -
feat(combo):
auto/*combos gain a strict budget-cap fallback policy —X-OmniRoute-Budget-Fallback: strict(or the persistedconfig.budgetFallback: "strict") makes an over-budget request fail fast withHTTP 402instead of the previous silent fallback to the globally cheapest candidate, which could still exceed the cap. The default (cheapest) preserves existing behavior. Builds on the existingX-OmniRoute-Budget/X-OmniRoute-Modeper-request controls (#6023/#6024/#6025), consolidated intoresolveRequestAutoControls(). Regression guard:tests/unit/auto-combo-budget-fallback-3470.test.ts. (#3470) -
Provider/model param filters: config-driven parameter denylist/allowlist per provider/model with auto-learn from upstream 400s (#6649 — thanks @ThongAccount, closes #6625)
-
Per-combo reasoning token buffer toggle: the combo builder now exposes an explicit checkbox for the
#3587reasoning-modelmax_tokensbuffer, defaulting to the existing enabled behavior, so a combo can opt out without hand-editing raw JSON config (#6702 — thanks @xz-dev) -
feat(dashboard): 9router-parity Routing Strategy settings card on Settings → Routing, plus a per-provider account-routing override on the provider detail page (#6678) — surfaces the existing account round-robin / sticky-limit knobs and adds a new combo-level sticky round-robin (
comboStickyRoundRobinLimit, resolved viaresolveComboStickyRoundRobinLimit()— per-combo → global combo sticky → account sticky cascade) so combo targets can batch calls per target the same way account fallback already does. A newproviderStrategiessetting (Zod-validated map,src/shared/validation/settingsSchemas.ts) lets a specific provider override the globalfallbackStrategy/stickyRoundRobinLimitwithout touching the account-wide default, wired intogetProviderCredentials()(src/sse/services/auth.ts) ahead of the global fallback. Regression guard:tests/unit/combo-rr-sticky-9router.test.ts,tests/unit/settings-ui-layout-static.test.ts. (thanks @SeaXen) -
feat(icons): provider logos now resolve local SVG assets first for faster rendering, with a 5-tier fallback chain — local SVG →
@lobehub/iconsReact components →thesvg.orgCDN (external SVG for unknown providers) → local PNG → generic AI icon — replacing the previous LobeHub-first order. Adds dozens of first-party provider SVGs and migrates several bitmap logos (continue/copilot/cursor/deepgram/heroku/openclaw/ovhcloud) from PNG to SVG. Regression guard:tests/unit/ui/ProviderIcon-icon-url.test.tsx. (#6317 — thanks @hamsa0x7) -
Skill Collector CLI detection: new
GET /api/skills/collect/detect+POST /api/skills/collect/install(and thecli-skill-collectoragent skill) detect which coding CLIs (Claude Code, Codex, Cursor, Copilot, Cline, Hermes, OpenCode, etc.) are installed locally viagetCliRuntimeStatus(), match them against GitHub agent-skill repos, and plan an install path per tool — replacing the standalone Skill Collector Python app. Both new routes andGET/POST /api/github-skillsnow require management auth (requireManagementAuth()) and are loopback-gated (LOCAL_ONLY_API_PREFIXES+SPAWN_CAPABLE_PREFIXES) since the detect route spawns a child process per candidate CLI tool (Hard Rules #15 + #17). Theomniroute_github_skills_installMCP tool now reports the honestaction: "planned"instead of"installed", matching the REST route (#6294 — thanks @Moseyuh333) -
ClinePass dual-auth: ClinePass now offers both sign-in methods on its dashboard page — OAuth (reusing the Cline WorkOS flow) as the primary "Connect" path, or a pasted BYOK API key via "Manual API key", instead of only the API-key-only provider shipped in #5942. The registry alias was aligned to
cp(matching theOAUTH_PROVIDERScatalog alias) so<alias>/<modelId>routing resolves correctly, the OAuth refresh dispatch now routesclinepassto the shared Cline refresh flow, and the duplicate API-key-only catalog entry was removed to keep ClinePass listed once. Regression guard:tests/unit/clinepass-provider.test.ts. (#6126 — thanks @hajilok) -
feat(oauth): Kiro/Amazon Q auto-import now supports enterprise External IdP ("Your organization") logins via Microsoft Entra/Okta/Auth0/OneLogin/Ping/Google/Cognito — these org-issued tokens are not AWS SSO tokens (no
aorAAAAAG-prefixed refresh token) and can't refresh through the AWS OIDC/Kiro-social path, sotryAwsSsoCache()now detects them (authMethod/provider === "externalidp") and refreshes via the org IdP's owntokenEndpoint(public-client OAuth2 refresh grant, no client secret), persistingTokenType: EXTERNAL_IDPgating so the runtime executor sends the header the AWS CodeWhisperer API requires for these accounts;tokenEndpointis SSRF-guarded against an HTTPS + known-IdP-host-suffix allowlist. (#6363 — thanks @artickc) -
Kiro long-lived API key auth: new
/api/oauth/kiro/api-keyroute +KiroService.validateApiKeylet a Kiro account be linked with a long-lived AWS CodeWhisperer/Kiro API key instead of the interactive OAuth device flow, with live per-account model discovery (ListAvailableModels, 5-minute cache) layered over the existing static registry fallback (#6587 — thanks @strangersp) -
Chaos Mode: multi-model parallel/collaborative task execution — dispatches a task to every active provider connection at once (parallel) or chains outputs sequentially so each model builds on the previous one's answer (collaborative), configurable via Dashboard → Chaos Mode (
GET/PUT/DELETE /api/chaos/config) and gated per-API-key via a newchaosModeEnabledpermission (opt-in — disabled by default globally and per key).POST /api/chaos/run(dashboard session) andPOST /api/skills/collect/chaos(external Bearer-token) delegate to a sharedexecuteChaosRun()engine (src/lib/chaos/chaosExecutor.ts) that dispatches in-process via the established synthetic-Request/route-handler pattern (no network hop, no hardcoded port), with a concurrency cap (max 10 parallel), configurablemax_tokens(256–128k), a clear error whenstreamis requested, and collaborative-chain info (provider order + input size). Fixes external Bearer-auth bypass and stale config-cache leakage. Regression guard:tests/unit/chaos-config.test.ts,tests/unit/chaos-executor.test.ts,tests/unit/chaos-api-routes.test.ts. (#6728 — thanks @Moseyuh333) -
feat(cli): 2 new CLI tool integrations on Dashboard → CLI Tools — omp (Oh My Pi) and letta — each with binary detection, config apply/reset, and a settings card following the existing tool-card pattern. Both settings routes shell out to
which omp/which lettato detect the local install, so they're loopback-gated (LOCAL_ONLY_API_PREFIXES, Hard Rules #15/#17) in addition to the sharedrequireCliToolsAuth()management-auth guard every cli-tools route requires, and route errors throughsanitizeErrorMessage();src/lib/db/omp.tsisolates theompCLI's own local SQLite reads behind parameterized queries. (Note: the original PR also proposed pi, codewhale, and jcode integrations — those three had already shipped via a separate PR by the time this one was reconciled, so only omp+letta landed here.) Regression guard:tests/unit/db/omp.test.ts,tests/unit/cli-tools-auth-hardening.test.ts,tests/integration/cli-settings-omp.test.ts,tests/integration/cli-settings-letta.test.ts. (#6318 — thanks @hamsa0x7) -
feat(providers): custom models now support a manual Context Window Override so an operator can correct a provider's misreported context length (e.g. reports 1M when the real limit is 128K) instead of the model silently getting dropped from combo routing once the wrong value lands in the catalog (#4125 — thanks @rucciva). Reuses the existing Feature-5004
model_context_overridestable (source: "manual") — already the priority-0 sourcegetModelContextLimit()(the function combo's context-window filter calls) reads ahead of the models.dev/registry/static catalog — so no new resolver logic was needed, only the missing write path:PUT /api/provider-modelsnow accepts an optionalcontextWindowOverride(number to set,nullto clear),GETsurfaces the current value back on each custom-model row, and the provider detail page's custom-model edit form gained a Context Window Override field + badge. Regression guard:tests/unit/provider-models-context-window-override-4125.test.ts. -
fix(providers): register OpenRouter as a rerank provider so
openrouter/cohere/rerank-*models resolve instead of erroringInvalid rerank model(#6574 — thanks @rafpigna) -
fix(api):
HEADrequests no longer hang until client timeout on any route — valid, unknown, authed, or unauthed (#6400), broader follow-up to the route-specific #6517 (/v1/models). Root cause: Next.js 16's App Router route-handler pipeline (next/dist/server/send-response.js) correctly skips piping aResponsebody forHEAD, but its page-rendering pipeline (next/dist/server/pipe-readable.js→pipeToNodeResponse, used for every app-router page/layout render — including thenot-foundboundary any unmatched path falls through to) has no such check and always streams the full rendered body regardless of method; combined with Node's default keep-alive framing this left some clients unsure whether the (implicitly bodyless)HEADresponse had actually finished. A newscripts/dev/head-response-guard.cjs, wired into both the dev/start custom server (scripts/dev/run-next.mjs) and the packaged standalone server (scripts/dev/standalone-server-ws.mjs) at the same tier as the existinghttp-method-guard.cjs/peer-stamp.mjswrappers, discards any body bytes written for aHEADrequest and forcesConnection: closeonce.end()is called — independent of route existence or auth state, satisfying RFC 9110 §9.3.2. Regression guard:tests/unit/head-request-closes-6400.test.ts. -
feat(dashboard): Provider Quota page (
Dashboard → Quota) fills horizontal whitespace before stacking vertically (#3520) —QuotaCardGridpreviously stacked every provider group in a single verticalflex flex-col, and each group's own card grid didn't go multi-column untilmd(grid-cols-1 md:grid-cols-2 xl:grid-cols-3 2xl:grid-cols-4). Provider groups now flow into a 2-column CSS multi-column layout on very wide (2xl) screens instead of an unconditional vertical stack, and each group's card grid starts at 2 columns immediately (grid-cols-2 md:grid-cols-3 xl:grid-cols-4), reaching higher density sooner on narrower-but-not-mobile viewports. Regression guard:tests/unit/quota-card-grid-horizontal-layout.test.ts(thanks @gdevenyi). -
feat(ws): the live-dashboard WebSocket server now auto-starts in-process (via
instrumentation-node.ts) across every deployment mode — dev, production, Docker, Electron — with no separate sidecar script; the default WS port moved from 20129 to 20132 to avoid colliding withAPI_PORTin split-port setups, the deprecatedOMNIROUTE_DISABLE_LIVE_WSenv was consolidated intoOMNIROUTE_ENABLE_LIVE_WS(default enabled), and the WS path is now derived fromNEXT_PUBLIC_LIVE_WS_PUBLIC_URL's pathname (/live-wsfallback) (#6072 — thanks @ianriizky). -
feat(compression): new omniglyph engine (context-as-image) — renders system prompt, tool docs, and dense history as compact PNG pages the model reads instead of text (~10× fewer tokens on the converted block; 59–70% end-to-end measured). Works stacked with RTK/Caveman (
stackPriority: 90) or standalone (mode: omniglyph); restricted to Claude Fable 5 over the direct Anthropic route, fail-closed gates withskip:<reason>techniques, preview (stable: false, off by default) (#6556). Dependency bumped toomniglyph@^1.0.2for upstream ReDoS fixes (#6661). -
feat(sandbox): the skill sandbox gained a container-provider abstraction that auto-detects and uses the best native runtime per host — Apple Container (macOS 26+), WSL container (
wslc.exe), OrbStack, Podman — instead of hardcodingdocker run, removing the Docker Desktop requirement on macOS/Windows (#6611 — thanks @KooshaPari). -
fix(sse): skip
thinkingConfigfor Gemma models on the OpenAI→Gemini path so OpenAI-shape clients no longer get a 400 from Vertex. (thanks @chy1211) -
feat(xai): route xAI clients to Grok's native
/v1/responsesendpoint instead of the chat-completions bridge. (thanks @ryanngit) -
feat(models): add a Settings → AI "Model Overrides" UI plus
/api/model-capability-overridesCRUD and amodel_capability_overridestable, letting operators set a manual max-output-token override per provider/model (#6727 — thanks @xz-dev). -
feat(resilience): operator-configurable account rotation policy — a new
rotationConfiglayer lets operators tune how connections rotate on failure, wired intoaccountFallback(#6763 — thanks @artickc). -
chore(cursor): add Grok 4.5 effort/fast model IDs (#6774 — thanks @andrewmunsell).
-
feat(codex): Codex provider model discovery now fetches the live catalog from
chatgpt.com/backend-api/codex/modelsusing Codex-shaped headers, falling back to a GitHub-hosted model manifest and then to the local static catalog when the live/GitHub sources are unavailable or return an unexpected shape — newsrc/app/api/providers/[id]/models/discovery/codex.ts(normalization, version-gating, merge/enrich against the local catalog) covered bytests/unit/provider-models-discovery-split.test.tsandtests/unit/provider-models-route-codex.test.ts(#6776 — thanks @JxnLexn). -
feat(cursor): register the Opus 4.8, Fable 5, and Sonnet 5 model families for the Cursor Agent provider so the latest Claude/Fable model ids route correctly (#6779 — thanks @andrewmunsell).
-
Changelog fragments (
changelog.d/): PRs now add their changelog entry as a new fragment file (changelog.d/{features|fixes|maintenance}/<PR>-<slug>.md) instead of editingCHANGELOG.md— two PRs never touch the same file, structurally eliminating the CHANGELOG-eat merge conflicts that forced a re-sync push + full CI re-run after every sibling merge (O(N²) CI runs in a merge-storm).scripts/release/aggregate-changelog.mjs(npm run changelog:aggregate) folds fragments into the living section at release reconciliation, andcheck:changelog-integritynow also validates fragment well-formedness. Regression guard:tests/unit/changelog-fragments.test.ts. -
feat(proxy): add a latency-optimized proxy rotation strategy that ranks pool entries by measured round-trip latency, extending the existing round-robin/random/sticky proxy-pool selection (#6798 — thanks @iamraydoan).
-
feat(fusion): the fusion judge may now draw on its own knowledge and override the panel when every panel answer is wrong or incomplete, instead of being restricted to synthesizing only from panel output (#6804 — thanks @chirag127).
-
feat(dashboard): search box on the Playground's raw model
<select>(#4086) — the sharedModelSelectModal(combo builder + CLI-code cards) already had search, but Playground'sStudioConfigPanemodel dropdown stayed a flat unsearchable list, unusable once a provider like OpenRouter contributed 50+ models. Typing now filters the dropdown (Turkish-safe accent/case-insensitive match viamatchesSearch), while the currently selected model always stays pinned in the list even if it no longer matches the query, so typing never silently swaps the active selection. Reuses the existingcommon.searchi18n key (already translated in all 42 locales) — no new translation key needed. Regression guard:tests/unit/playground-model-selection-3731.test.ts(filterModelsByQuery),tests/unit/ui/playground-model-search-4086.test.tsx. -
feat(usage): Antigravity/agy quota widget now surfaces the weekly window alongside the existing per-model 5-hour window (#4017) — the weekly limit isn't part of the per-model
retrieveUserQuotaresponse the fetcher already calls; it only appears in a separate, undocumentedretrieveUserQuotaSummaryRPC that groups models into families ("Gemini Models", "Claude and GPT models") with one weekly bucket per family. A newusage/antigravityWeeklyQuota.tsleaf fetches that RPC (cached, best-effort — a failure or unavailable RPC never breaks the existing per-model quotas) and parses the weekly bucket per group intogemini_weekly/claude_gpt_weeklyquota entries, merged into the samequotasmap the widget already renders generically. Regression guard:tests/unit/antigravity-weekly-quota-4017.test.ts(bucket parsing, the alternatequotaSummary-nested envelope, and end-to-end merge viagetUsageForProvider). -
feat(codex): Codex CLI compatibility shim — the Responses API
response.created/response.in_progress/response.completedpayloads now carry amodelfield (previously absent), and for Codex-CLI-originated requests it echoes the client-requested, effort-suffixed model id (e.g.gpt-5.5-xhigh) instead of the bare upstream id (gpt-5.5), so the Codex CLI status line/model button shows the active reasoning effort (#3697).openaiToOpenAIResponsesResponse(open-sse/translator/response/openai-responses.ts) now threads the upstream model into the Responses event objects; a newisCodexOriginatedHeaders()(open-sse/config/codexIdentity.ts, reusing PR #3481'soriginator/User-Agent detection) makes chatCore's existing opt-inechoRequestedModelName(#1311) model-echo pipeline fire automatically for Codex clients regardless of the setting, detected by request headers so it still applies whencodex/gpt-5.5-xhighis routed through a combo to a non-codex upstream;echoModelInObject/echoModelInSseLine(open-sse/services/responseModelEcho.ts) now also rewrite the nestedresponse.modelfield the Responses API uses./v1/modelsstill returnsmodels: []for Codex (unchanged). Regression guard:tests/unit/codex-effort-model-echo-3697.test.ts. -
Z.ai Web (free web-session provider): new
zai-webweb-cookie provider drives the free chat.z.ai consumer chat UI via a pasted browser session cookie, distinct from the existing API-keyzai/glm/glm-cn/glmtproviders (api.z.ai) — modeled on thedoubao-web/venice-webcookie executors and the pre-existingchatglm-webcredential requirement/token-extraction entries.ZaiWebExecutor(open-sse/executors/zai-web.ts) posts tochat.z.ai/api/chat/completionswith the cookie forwarded both asCookieand asAuthorization: Bearer <token>, and normalizes both z.ai's internaldelta_content/phaseSSE envelope and a pass-through OpenAI-shapedchoices[].deltaframe into standard chat-completion chunks. Registered inWEB_COOKIE_PROVIDERS,WEB_SESSION_CREDENTIAL_REQUIREMENTS, the provider registry (zai-webentry, GLM-4.6/4.5/4.5V models), andtokenExtractionConfig.tsfor in-app cookie capture. Regression guard:tests/unit/executor-zai-web.test.ts(16 tests — token extraction, frame parsing for both SSE shapes, streaming and non-streaming aggregation, error paths). (#4056) -
feat(compression): update the vendored GCF codec behind the Headroom engine to spec v3.2 (nested flattening) (#6837). Homogeneous arrays whose rows carry nested objects/arrays now tabularize via
>-prefixed path fields instead of a low-yield per-row fallback, so nested MCP tool-result rows (meta:{...},tags:[...]) compact like flat rows. On representative shapes the update takes deeply-nested payloads the old codec left near-uncompressed from ~3% to ~32% vs JSON (k8s pods,cl100k_base), with shallow-nested rows seeing a small bump and flat arrays unchanged. Re-vendored from current gcf-typescript (zero runtime deps, MIT, SPDX-marked, generic-profile only); also folds in the[N]:inline-array quoting fix and canonical decimal formatting. Round-trip stays lossless (order-insensitive), and the decoder is hardened against prototype pollution (a__proto__/constructorpath segment never mutatesObject.prototype, and keys shadowing built-ins liketoStringnow round-trip correctly instead of misparsing). Regression guard:tests/unit/compression/headroom-smartcrusher.test.ts(deep-nested + prototype-pollution cases). -
feat(providers): Add GPT-5.6 support across OpenAI API, Codex, and ChatGPT Web, including Codex Max/Ultra efforts, VS Code metadata, Fast-tier credit accounting, curated live discovery, the Codex 0.144.1 client identity, and correct chat routing for models that also support image generation (#6862) - thanks @backryun
-
chore(providers): Align emitted Claude Code identity headers, bridge fingerprints, provider profiles, and documented defaults with claude-cli 2.1.207 (#6862) - thanks @backryun
🐛 Bug Fixes
-
fix(dashboard): the Proxy Registry settings page crashed at runtime (
ReferenceError: poolLoaded/bulkImportOpen is not defined) — the #6625/#6909 hook-extraction refactors deleted 10 state declarations (poolLoaded,poolSaving, and the 8-member bulk-import family) while ~30 usages remained; all restored (caught by the release E2E;typecheck:coredoes not cover dashboard TSX — follow-up #7021). (thanks @diegosouzapw) -
fix(combo):
comboStickyRoundRobinLimitnow defaults to inherit (null) instead of1— the literal default silently shadowed the documented batched round-robin rotation (stickyRoundRobinLimit: 3), flipping every round-robin combo to per-request alternation (#6678 follow-up, caught by the release CI). (thanks @diegosouzapw) -
fix(ws): the standalone LiveWS startup script exited 0 without ever listening — its bootstrapped child re-spawned with the import-suppressor
OMNIROUTE_ENABLE_LIVE_WS=0and then honored it as an operator disable (#6072 follow-up, caught by the release CI). (thanks @diegosouzapw) -
fix(api): compression PUT schema accepts every catalog engine. (#6792 — thanks @Pitchfork-and-Torch)
-
fix(compression): surface fallback reasons in the preview response. (#6461, #6519 — thanks @chirag127)
-
fix(providers): fail fast on an empty auto-combo pool instead of a 15s timeout. (#6458, #6546 — thanks @chirag127)
-
fix(compression): honor UI-toggled engines in the stackedPipeline dispatch + surface substitutions. (#6463, #6534 — thanks @chirag127)
-
fix(api): return 400 for missing/invalid
messagesbefore model resolution. (#6402, #6515 — thanks @chirag127) -
fix(providers): enrich the model_cooldown 429 body with a
retry_afterISO timestamp + credential count. (#6460, #6523 — thanks @chirag127) -
fix(sse): default reasoning summary for effort-only Responses requests. (#6807 — thanks @rushsinging)
-
fix(sse): compression no-op treated as zero-savings, not inflation/silent-drop. (#6883 — thanks @chirag127)
-
fix(oauth): Trae OAuth client_id embedded via
resolvePublicCred()(Hard Rule #11). (#6870 — thanks @chirag127) -
fix(sse): combo path no longer trips the whole-provider breaker on a plain 429. (#6868 — thanks @chirag127)
-
fix(api): malformed JSON bodies now return 400 instead of 500. (#6871 — thanks @chirag127)
-
fix(fusion): judge selected from a surviving panel member when no explicit judge is configured. (#6869 — thanks @chirag127)
-
fix(sse): combo model lockout honors the parsed upstream quota reset. (#6863, #6866 — thanks @AgentKiller45)
-
fix(dashboard): logs detail modal no longer reopens on first close. (#6830 — thanks @MikeTuev)
-
fix(usage): honor xAI provider-reported exact cost. (#6711 — thanks @diegosouzapw)
-
fix(kiro): probe IdC region during profileArn discovery, cross-region (recovers #6099). (#6840 — thanks @diegosouzapw)
-
fix(antigravity): sanitize Cloud Code safety settings. (#6839 — thanks @diegosouzapw)
-
fix(translator): defer
content_block_startuntil GLM streams the tool name. (#6730 — thanks @diegosouzapw) -
fix(translator): strip empty
cloud_base_branchfrom Cursor Subagent tool calls. (#6729 — thanks @diegosouzapw) -
fix(antigravity): surface aborted Gemini tool calls off
end_turn. (#6713 — thanks @diegosouzapw) -
fix(volcengine): clamp Kimi
max_tokensto the Ark endpoint cap. (#6712 — thanks @diegosouzapw) -
fix(codex): surface capacity errors embedded in 200-OK SSE streams. (#6710 — thanks @diegosouzapw)
-
fix(sse): skip
thinkingConfigfor gemma models in openai→gemini translation. (#6708 — thanks @diegosouzapw) -
fix(oauth): avoid bare-email dedup of Codex OAuth logins. (#6706 — thanks @diegosouzapw)
-
fix(sse): unwrap bare
{function:{…}}tools in openai→claude translation. (#6704 — thanks @diegosouzapw) -
fix(db): eliminate a redundant
getApiKeyMetadatacall in the embeddings route. (#6929 — thanks @oyi77) -
fix(db):
authTypefilter support ingetProviderConnections. (#6946 — thanks @oyi77) -
fix(i18n): the provider-detail (
/dashboard/providers/[id]) visibility + free/paid model filter labels (showVisibleOnly,showHiddenOnly,freeFilterAll,freeFilterFreeOnly,freeFilterPaidOnly,hideAllModels, plus the currently-unusedfilterVisible/filterHidden/filterByVisibility) rendered as the literal__MISSING__:<english>sentinel in 15 locales, including pt-BR (#6694) —providerText()(providerPageHelpers.ts) checkst.has(key)before falling back to clean English, andt.has()returnstrueeven when the stored value is the__MISSING__:sentinelscripts/i18n/sync-ui-keys.mjswrites when mirroring keys across locales, so the sentinel rendered verbatim instead of the fallback. Disjoint key set from #6290 (filterAll/filterActive/filterError/filterBanned/filterCreditsExhausted). All 9 keys now carry real translations across the 15 affected locale files (it,ja,ko,mr,ms,nl,no,phi,pl,pt,pt-BR,ro,ru,sk,sv). Regression guard:tests/unit/i18n-provider-visibility-filter-keys-6694.test.ts. -
fix(cli): the dashboard's Claude Code CLI card could report "Not detected"/"Not installed" even when Claude Code was genuinely installed and previously used (#6701) —
getCliRuntimeStatus()(src/shared/services/cliRuntime.ts) determinedinstalledpurely from binary resolution (known install paths + awhere/whichPATH search), with no fallback when that lookup fails for reasons unrelated to whether the CLI is actually installed (stale PATH inherited by a long-running/background process, the binary having moved, an install method not yet catalogued, etc.) — even though~/.claude/settings.jsonon disk proves the tool was installed and used before. Upstream 9router's equivalent route already has this exact fallback. A newwithSettingsFallback()(src/shared/services/cliInstallFallback.ts) restores 9router parity: when the binary lookup's own reason is"not_found"(never for deliberate security rejections like unsafe/relative env overrides or symlink escapes) and the tool's settings file exists on disk,installednow reportstrue. Regression guard:tests/unit/repro-6701-claude-detect-fallback.test.ts. -
fix(cli): per-agent AgentBridge DNS toggle was broken for 8 of the 9 supported agents, and a failed MITM startup step could orphan the spawned proxy child —
addDNSEntry/removeDNSEntry(src/mitm/dns/dnsConfig.ts) always resolved the legacy Antigravity default hosts regardless of which agent's toggle was flipped, so enabling DNS for Cursor/Codex/Claude Code/etc. silently added onlydaily-cloudcode-pa.googleapis.comwhile the DB recordeddns_enabled=truefor the selected agent. Both functions now accept an optionalagentIdand resolve hosts viaALL_TARGETS;POST /api/tools/agent-bridge/agents/[id]/dnspasses the route'sidthrough and now returns 404 for an id that doesn't match a known target instead of silently falling back. Separately,startMitmInternal()(src/mitm/manager.ts) now wrapsgenerateCert()(log + rethrow), theprovisionDnsEntries()call, and the PID-file write in try/catch so a mid-startup failure can't orphan the already-spawned MITM child process. On Windows,addDNSEntries/removeDNSEntriesalso batch every missing/present entry into a single elevated PowerShell invocation instead of one UAC prompt per host line. Regression guard:tests/unit/dns-config-generic.test.ts(agent-specific resolution + batching),tests/unit/agent-bridge-dns-route-validation.test.ts(404 for unknown agent id). (#6338 — thanks @hamsa0x7) -
fix(guardrails): Vision Bridge's individual-model auto-reroute (route an image-bearing request straight to a vision-capable model instead of describe-then-forward) could bypass a policy-restricted API key's model allowlist/budget (#6640) —
VisionBridgeGuardrail.preCall()(src/lib/guardrails/visionBridge.ts) swapsbody.modelto the best available vision-capable model, but that swap happens in the guardrail pipeline AFTERchat.tsalready calledenforceApiKeyPolicy()against the ORIGINAL model, so a key scoped to a narrowallowedModelslist could still execute against an unvetted (and possibly costlier) vision model the reroute picked.chat.tsnow re-validates any guardrail-driven model change against the same per-key allowlist (isModelAllowedForKey) before honoring it, falling back to the original already-approved model when the reroute target is not allowed. The reroute path also now honors an explicitsettings.visionBridgeModeloperator override (previously ignored, unlike the combo/describe path a few lines below it, which already respects it viagetVisionBridgeConfig). Regression guard:tests/unit/guardrails/visionBridge.test.ts(22 tests). (thanks @herjarsa) -
fix(auth): an API key restricted via
allowedModels/allowedComboscould bypass that restriction entirely over the Codex Responses-over-WebSocket bridge (#6564) —prepare()insrc/app/api/internal/codex-responses-ws/route.tsauthenticated the WS bridge's API key (authenticate()/authorizeWebSocketHandshake()) and honoredallowedConnections, but never calledenforceApiKeyPolicy(), the same model/combo policy gate the HTTP/v1/responsespath enforces viahandleChat()— so a key scoped to e.g.combo/model-1.0could still reach a direct Codex model likegpt-5.5through this transport, as long as an eligible Codex OAuth connection existed. The bridge's WS auth token arrives via query params (api_key/token/access_token), not a normalAuthorizationheader, so a newenforceCodexWsApiKeyPolicy()builds an equivalentRequestcarrying an explicitAuthorization: Bearer <apiKey>header and callsenforceApiKeyPolicy()against the CLIENT-requested model, before any Codex-specific model remapping or credential selection. Regression guard:tests/unit/codex-ws-policy-enforcement-6564.test.ts(a model-restricted key is rejected 403 before reaching credential selection; a combo-restricted key is rejected 403 requesting a disallowed combo; a key that DOES allow the requested model still proceeds past policy). (thanks @Squawk7777 for the report and an independent fix via #6565) -
fix(security): loopback-gate
/api/middleware/*so a leaked JWT over a tunnel can't install or trigger a middleware hook — middleware hooks compile + run arbitrary JS vianew vm.Scripton the request hot path (src/lib/middleware/registry.ts), the same RCE class as the already-gated/api/plugins/*;/api/middleware/is now inLOCAL_ONLY_API_PREFIXESso loopback enforcement runs unconditionally before any auth check (Hard Rules #15 + #17). Regression guard:tests/unit/route-guard-middleware-local-only.test.ts. (#6541) — see PR. (thanks @developerjillur) -
fix(startup): AgentBridge's MITM server no longer fails to start with
ROUTER_API_KEY is requiredon a normal install (#6403) —POST /api/tools/agent-bridge/serverresolved the spawned MITM child's router key from only an explicitapiKeybody field (never sent by the AgentBridge UI — the schema has no such field) and theROUTER_API_KEYenv var (unset by default), sostartMitm()always received""and the child hard-exited, even though OmniRoute already had a usable API key in its own DB. A newresolveRouterApiKey()now falls back topickApiKeyForInternalUse()(the same DB-backed selector the combo-health-check / cloud-sync internal probes use), resolving in order: explicit key →ROUTER_API_KEYenv → an existing DB key. Regression guard:tests/unit/agentbridge-mitm-router-key-6403.test.ts. -
fix(providers): deploying a Cloudflare relay Worker from Dashboard → System → Proxy pool → Cloudflare relay failed immediately with
Cloudflare Worker upload failed: Content-Type must be one of: application/javascript, text/javascript, multipart/form-data, even with a valid token/account (#6416) — the Worker-script upload built a nativeFormDataand letfetchderive the multipart Content-Type automatically, but in productionglobalThis.fetchis patched withnode_modules/undici's own fetch (open-sse/utils/proxyFetch.ts), whoseFormData/Requestclasses differ from the runtime's globalFormData(same cross-realm class mismatch already fixed once for image edits in #3273); passing a nativeFormDatainstance through undici's patched fetch made it serialize the body as the literal string"[object FormData]"withContent-Type: text/plain;charset=UTF-8, which Cloudflare rejects outright.buildCloudflareWorkerUploadRequest()(src/lib/proxyRelay/cloudflareWorkerScript.ts) now builds the multipart body as a rawBufferwith an explicit boundary andContent-Type: multipart/form-data; boundary=…header, accepted verbatim by any fetch implementation. Regression guard:tests/unit/cloudflare-worker-upload-content-type-6416.test.ts+ updatedtests/unit/relay-deploy-5128.test.ts. -
fix(security): SSRF-guard the provider-validation probes so they can no longer be used as an open relay to cloud-metadata endpoints —
directHttpsRequest()(web-cookie / NVIDIA / Z.AI validation, all with a caller-controllablebaseUrl) ran withguard:"none"+allowRedirect:true; it now appliesgetProviderValidationGuard()(defaultblock-metadata: LAN/localhost allowed,169.254.169.254/link-local IMDS rejected, opt-out viaOMNIROUTE_ALLOW_PRIVATE_PROVIDER_URLS) andallowRedirect:falseso a provider can't 3xx-redirect the probe to metadata past the initial-URL guard. Regression guard:tests/unit/provider-validation-ssrf-guard.test.ts. (#6542) — see PR. (thanks @developerjillur) -
fix(startup): AgentBridge's MITM proxy served a mismatched cert for 3 of the 4 antigravity/cloudcode-pa hosts it terminates TLS for, breaking interception (#6494) —
src/mitm/server.cjs'sTARGET_HOSTSdecrypts all 4 hosts locally (daily-cloudcode-pa.googleapis.com,cloudcode-pa.googleapis.com,daily-cloudcode-pa.sandbox.googleapis.com,autopush-cloudcode-pa.sandbox.googleapis.com), butsrc/mitm/cert/generate.ts's self-signed cert only carried a SAN entry for the first host — a request to any of the other 3 got served a cert whose CN/SAN didn't match (confirmed viacurl -k https://cloudcode-pa.googleapis.com/showingCN=daily-cloudcode-pa.googleapis.com).generateCert()now sources its host list fromANTIGRAVITY_TARGET.hosts(src/mitm/targets/antigravity.ts, the single authoritative registry already kept in lock-step withserver.cjs/dnsConfig.ts/mitmToolHosts.tsby their own drift tests) and emits a SAN entry for all 4 hosts instead of hard-coding a second, incomplete copy. Regression guard:tests/unit/agentbridge-antigravity-cert-hosts-6494.test.ts(asserts the host list covers all 4 hosts and that the real generated cert's SAN includes each one). -
fix(resilience): a
prioritycombo never fell back when a target masked credit/quota exhaustion behind an HTTP 200 (#6427) —validateResponseQuality()(open-sse/services/combo/validateQuality.ts) only inspected the response body's top-levelerrorfield whenchoiceswas ALSO missing/empty (the narrower #3424 empty-completion case); a masked 200 that echoed a non-empty stubchoicesalongside a structured error object, or a known exhaustion phrase (e.g. "insufficient credits", "quota exceeded") in the error envelope, slipped through as "valid" and the combo kept returning the dead target's response forever instead of failing over. The quality check now inspects the error envelope — a top-level OpenAI-shapeerrorobject, or a bounded, case-insensitive exhaustion-phrase match againsterror.message/error.code/error.type/top-levelmessage/detail— unconditionally, before any shape-specific branch, and regardless of whetherchoices/outputalso look structurally present. The check never inspectschoices[].message.content, so a legitimate completion that merely mentions "quota" or "credits" in assistant prose is not misclassified. Regression guard:tests/unit/masked-200-exhaustion-fallback-6427.test.ts. -
fix(security): fail-closed CORS for the cookie/session-authed cloud-agent management routes —
getCloudAgentCorsHeaders()reflected any caller'sOriginand paired it withAllow-Credentials: true(a CSRF/exfil hole); it now defers to the central allowlist (resolveAllowedOrigin), echoes only an allowlisted origin withVary: Origin, and emitsAllow-Credentialsonly for an explicitly allowlisted origin — never for aCORS_ALLOW_ALLwildcard echo. Regression guard:tests/unit/cloud-agent-cors-failclosed.test.ts. (#6543) — see PR. (thanks @developerjillur) -
fix(compression): adaptive-compression ladder ranked 6 real catalog engines (
ccr,ionizer,relevance,llmlingua,llm,read-lifecycle) as if they didn't exist (#6533) —ladder.ts'sAGGRESSIVENESSandREDUCTION_FACTORmaps only covered the 7 engines wired intoDEFAULT_LADDER(session-dedup/rtk/headroom/lite/caveman/aggressive/ultra); every other engine registered inopen-sse/services/compression/engines/index.ts— includingccr/llmlingua, which the ladder doc comment already says are intentionally addable vialadderOverride— fell through toaggressivenessOf()'s?? 0default (same rank as"off") andexpectedReductionFactor()'s generic?? 0.9fallback, sofloor-mode escalation could not rank or escalate past them once added to a custom ladder. Both maps now carry entries for all 6 missing real engines, rescaled ×10 (off:0…ultra:70) and placed by each engine's documentedstackPriority(ionizerbetweenrtk/headroom,relevancebeforecaveman,llmlingua/llmbetweenaggressiveandultra, etc.);mcpAccessibility— named in the report — is not a registeredCompressionEngine(it's a separate MCP tool-response truncation mechanism) and was correctly left out. Regression guard:tests/unit/ladder-engine-maps-6533.test.ts(asserts every id fromlistCompressionEngines()ranks above"off"with a non-default reduction factor). (thanks @chirag127) -
fix(api): tool-call arguments could render as
[object Object]sequences instead of the real JSON through the/anthropic(Anthropic-shape/messages) routing path (#6459) —appendToolCallArgumentDelta()(open-sse/utils/toolCallArguments.ts), the shared accumulator the streamingopenai-to-clauderesponse translator,openai-responsestranslator, andresponsesTransformerall call to build up a tool call'sarguments/input_json_deltabuffer, treated any non-stringincomingfragment as an empty string. Some upstreams deliver the fulltool_calls[].function.argumentsvalue as an already-parsed JSON object/array instead of the OpenAI-contracted JSON-encoded string; the old code silently discarded that fragment, leavingtool_use.inputempty, and left downstream buffers open to a plain string coercion of the object ([object Object]) once client-side concatenation kicked in.appendToolCallArgumentDelta()nowJSON.stringify()s a non-string, non-null object/array fragment into a valid JSON fragment instead of dropping it, so the assembledpartial_jsonalways parses back into the original structured value. Regression guard:tests/unit/anthropic-toolcall-args-6459.test.ts. (thanks @chirag127) -
fix(providers):
fusioncombo returned the opaque"All fusion panel models failed"503 even when only a minority of panel members were actually cooling down / rate-limited, and a user-suppliedfusionTuning.minPanel=1was silently overridden (#6454) —handleFusionChat()hard-clamped the quorum floor viaMath.min(Math.max(2, cfg.minPanel), panel.length), so an operator-configuredminPanel=1never took effect:collectPanel()'s straggler-grace timer only starts onceok >= minPanel, and with the floor forced to 2 a single fast success plus N slow-failing stragglers never reached quorum, so the panel sat waiting instead of degrading to the survivor. Per-member failure reasons (straggler_dropped/timeout/threw/status_XXX/empty_content/unparseable) were also logged server-side but never surfaced in the 503 body, leaving operators unable to tell a rate-limit fan-fail from a broader outage. Fixed by honoringMath.max(1, cfg.minPanel)and threading afailures: Array<{ model, reason }>collector into the 503 message (model=reasonper entry) — production fix already merged via #6521; this entry backfills the missing CHANGELOG bullet and adds an 11-member,fusion-free-scale regression test matching the original repro shape (a cooling minority must not sink a healthy majority; a genuinely all-failed panel still returns the documented 503). Regression guard:tests/unit/services/fusion-min-panel-and-failure-detail.test.ts+tests/unit/fusion-partial-panel-failure-6454.test.ts. (thanks @chirag127) -
fix(providers):
fusioncombo strategy silently returned a panel member's raw answer instead of the configuredconfig.judgeModelsynthesis (#6455) —handleFusionChat()'s single-survivor "degrade gracefully" path (added for #6454) returned the lone panel answer directly whenever only one panelist succeeded, regardless of whether an explicitjudgeModelwas configured; with the defaultminPanel: 2and a 2-model panel, any single flaky/rate-limited panelist forced this path on every request, so the configured judge (e.g.auto/claude-opus) was never invoked and the client-visible.modelreflected whichever panelist happened to survive. The judge is now still invoked to synthesize a lone surviving answer wheneverjudgeModelis explicitly configured; the cheap direct-answer shortcut is kept only for the implicit case (nojudgeModelset, where the "judge" is justpanel[0]). Regression guard:tests/unit/fusion-judge-model-6455.test.ts+ updatedtests/unit/combo-fusion-strategy.test.ts. (thanks @chirag127) -
feat(combo): sanitized diagnostic trace on an auto-combo terminal failure — instead of an opaque 503, a terminal combo failure now returns a whitelist-projected trace (candidate pool size, attempted count, excluded provider/reason codes, attempt order, and a terminal-reason code) via the new
errorResponseWithComboDiagnostics()/sanitizeComboDiagnostics()inopen-sse/utils/error.ts— provider/model ids and enumerated reason codes only, never keys/tokens/bodies, length- and count-capped. A reasoning-budget-exhausted panel now returns an actionable "increase max_tokens" message rather than a blind retry-limit 503. Regression guard:tests/unit/combo-diagnostics-trace.test.ts. (#6545) — see PR. (thanks @developerjillur) -
fix(providers): image/diffusion models discovered from an upstream catalog (e.g. HuggingFace's live
/v1/models) are no longer advertised as chat models (#6457) — the chat catalog builder defaulted synced models with no modality info toendpoints: ["chat"], sohuggingface/stabilityai/stable-diffusion-xl-base-1.0showed up in the chat/v1/modelslisting and returned400 "not a chat model"when called.catalog.tsnow skips any synced model already registered as an image model for that provider (via the newisRegisteredImageModel()), leavinggetAllImageModels()to list it with the correcttype: "image". Regression guard:tests/unit/image-model-not-in-chat-catalog-6457.test.ts. -
fix(resilience): combo session stickiness never released a pin on a credits-exhausted/banned/expired account, permanently defeating failover for that conversation (#6692) —
applySessionStickiness()(open-sse/services/combo/sessionStickiness.ts) gated the sticky pin only on 5h/weekly usage-percentage headroom, which is orthogonal to account availability: acredits_exhausted/banned/expiredconnection, or one still inside itsrateLimitedUntilcooldown, reports perfectly healthy headroom, so the pin was force-promoted back to the front of the target list on every subsequent turn.clearStickyBinding()also had zero call sites incombo.ts's failure paths, so a quality-validation-rejected 200 (a masked daily-cap refusal) never released the pin either. The gate now also resolves the bound connection's terminal status/cooldown via a new injectable fetcher seam (fail-open on lookup errors, mirroring the existing saturation fetcher), andcombo.ts's two dispatchers (handleComboChat/handleRoundRobinCombo) release the pin immediately at both their connection-exhaustion classification point and their quality-validation-failure branch via the newreleaseStickyPinOnFailure(). Regression guard:tests/unit/repro-6692-sticky-terminal.test.ts+ extendedtests/unit/combo-session-stickiness.test.ts. -
fix(test): replace the bare
expect(true).toBe(true)tautology inplayground-api-tab.test.tsx's SSE test and close thecheck:test-maskinggap that let it slip through for a full cycle (#6404) — a prior pass (#6548) had already swapped the literal toexpect(sendBtn).toBeDefined(), but that stayed just as vacuous: the test's fetch mock returned an empty/v1/modelslist, soApiTab's Send button is alwaysdisabled(!selectedModel) and the SSE branch never runs — the "SSE infra is verified" comment was never true. The test now mocks a real model, drives the model<select>to enable Send, assertssendBtn.disabled === falsebefore clicking, and asserts the streamed SSE delta ("Hello!") actually reached the response editor. Root cause on the detector side:check-test-masking.mjs's tautology subcheck only compares base-vs-HEAD counts within a PR's own diff (headExtTaut > baseExtTaut) and no-ops locally whenGITHUB_BASE_SHA/GITHUB_BASE_REFare unset ("sem base ref — pulando") — so a tautology merged once, or checked with a bare local run, was invisible forever after. Added a new always-on, PR-independent absolute-floor scan (scanBareTautologies+countBareTautologies) over every git-tracked test file for the bareexpect(true).toBe(true)/assert.equal(1,1)/assert.strictEqual(1,1)patterns specifically (deliberately excludingassert.ok(true), which has ~15 pre-existing verified-legitimate try/catch-fallback uses repo-wide and stays governed by the lenient diff-only subcheck) — verified zero pre-existing hits repo-wide once this file was fixed, so the new floor is safe to enforce unconditionally. Regression guard:tests/unit/check-test-masking.test.ts(newscanBareTautologies/countBareTautologiescases) +tests/unit/ui/playground-api-tab.test.tsx. (thanks @chirag127) -
fix(oauth): Codex/ChatGPT (and every other OAuth provider) connection stays stuck showing "Auth Failed" even after a genuinely successful token refresh (#6352) —
updateProviderCredentials()(the sharedonPersistcallback for the manual "Refresh token" route, the reactive per-request refresh inchat.ts, and the Codex/Claude auth-file importers) correctly reused the storedrefresh_token, persisted the newaccess_token, and replaced a rotatedrefresh_token, but never cleared the staletestStatus/lastError*/errorCodefields left over from a prior expired/invalid refresh or upstream 401/403 — only the separate background health-check sweep did that clearing. A successful refresh now resetstestStatusto"active"and clearslastError,lastErrorAt,lastErrorType,lastErrorSource, anderrorCode(an explicittestStatusfrom the caller still wins). Regression guard:tests/unit/codex-oauth-refresh-persist-6352.test.ts. -
perf(health): short-TTL (1s) cache for the frequently-polled
GET /api/monitoring/healthpayload — rebuilding it every request (DB reads + status aggregation across 8 subsystems) was wasteful under rapid polling; the cache stays near-real-time and is invalidated immediately onDELETE(circuit-breaker reset) so a manual reset is reflected at once. Regression guard:tests/integration/monitoring-health-cache.test.ts. (#6553) — see PR. (thanks @developerjillur) -
fix(resilience):
headroomcombo routing did not always select the Codex account with the most free quota (#6379) —orderTargetsByHeadroom(open-sse/services/combo/quotaStrategies.ts) already loaded the per-connection DB snapshot (with decrypted credentials) viaexpandTargetsByQuotaAwareConnections, but discarded it before callinggetSaturation; for Codex,fetchCodexSaturationforwards straight tofetchCodexQuota(connectionId, connection), which needsconnection(or a priorregisterCodexConnection()call, which never happens before headroom ranking runs) to readaccessToken— so it returnednullfor every candidate, saturation failed open to0across the board, and ranking fell back to the original combo order regardless of actual free quota.getSaturation()and the headroomSaturationFetcherseam now accept and thread the loaded connection snapshot through tofetchCodexQuota. Kilo's dup flag vs #5903 was a false positive — that issue is about session-sticky reset-aware/least-used selection, not headroom's Codex saturation lookup. Regression guard:tests/unit/headroom-codex-quota-snapshot-6379.test.ts. (thanks @eidoog) -
fix(providers): custom models a provider actually has are no longer dropped from the Free Provider Rankings when both "Configured only" and "Available only" filters are applied (#6368), follow-up to #6150 —
freeProviderRankings.ts::getProviderModels()only ever walked the staticopen-sse/config/providerRegistry.tscatalog, so a user-added custom model (e.g. a Puterclaude-fable-5model saved as "Claude Fable 5") never entered the candidate model list the ranking scores against, and could never survive the #6150 configured/available filters even when actually configured and available. It now additively merges the provider's custom models (db/models.ts::getCustomModels) into that candidate list via a new pure, de-dupingmergeProviderModels()helper, before scoring/filtering runs — catalog free/paid filtering elsewhere is untouched. Regression guard:tests/unit/free-provider-rankings-custom-models-6368.test.ts. (thanks @shabeer) -
fix(playground): accept a valid dashboard session for
GET/POST /api/playground/presetsunderREQUIRE_API_KEY=true— the Playground page calls this route with a cookie/session and no API key, which previously 401'd the authenticated dashboard;checkAuth()now accepts a management/dashboard session (requireManagementAuth) as an alternative to an API key, while a presented API key must still be valid and the anonymous-allowed default is preserved. Regression guard:tests/integration/presets-dashboard-auth.test.ts. (#6554) — see PR. (thanks @developerjillur) -
fix(providers):
cloudflare-aino longer silently drops image/non-text content parts (#6390) —transformRequest()'sflattenContent()(added for #2539 to satisfy the Workers AI/ai/v1/chat/completionsplain-stringcontentrequirement) mapped any non-text OpenAI content part (e.g.image_url) to""and joined the rest, so a request carrying an image quietly went out as text-only with the attachment gone and no error surfaced. It now throws a clear error on the first non-text part instead of dropping it silently, which the existing top-levelchatCore.tscatch already routes throughbuildErrorBody()/sanitizeErrorMessage()(same pattern asbuildUrl()'s missing-Account-ID error). Regression guard:tests/unit/cloudflare-ai-image-parts-6390.test.ts. -
fix(providers): stop Antigravity connections from falsely reporting all-accounts quota-exhausted (#6295) —
genericQuotaFetcher.ts::percentUsedForQuota()ignored thefractionReportedflag and defaulted an unreported model'sremainingPercentageto 0, which computed as 100% used; sinceconvertUsageToQuotaInfo()takes the worst-case window across a connection, a single model with no reported fraction dragged the whole account intolimitReachedandquotaPreflightskipped it.percentUsedForQuota()now returnsnull(unknown, window ignored) wheneverfractionReported === false, before falling back toremainingPercentage. Regression guard:tests/unit/generic-quota-fetcher.test.ts. -
fix(fusion): the fusion judge no longer replays a panel member's answer via an idempotency-key collision — fusion's panel + judge sub-requests re-enter
chatCoresharing the client's headers, so they derived the sameIdempotency-Key/x-request-idand a panel answer saved under the key was replayed by the judge's check ~1ms later (inside the 5s window), returning a panel member's answer instead of the judge synthesis (observed live onnexa/conversation-fusion).composeIdempotencyKey()now namespaces the key by target provider/model + a digest of the request messages, so sub-requests can't collide while a genuine client retry (same key/model/body) still replays. Regression guard:tests/unit/idempotency-fusion-collision.test.ts. (#6558) — see PR. (thanks @developerjillur) -
fix(providers): grok-cli (Grok Build) now strips
reasoning_effort/reasoningbefore forwarding the request (#6288) — Claude Code sendsreasoning_efforton every request (routing the Opus slot), which Grok Build's upstream chat-proxy endpoint rejects with a 400;transformRequest()'s existingUNSUPPORTEDsampling-param strip list (#5273) never covered it. Regression guard:tests/unit/grok-cli-reasoning-strip-6288.test.ts. -
fix(cli):
omniroute serveno longer hangs silently on a readiness timeout (#6321) — the child server's stdout was piped to"ignore"whenever--log/OMNIROUTE_SHOW_LOGwasn't set (the default), discarding any debug output, andrunWithSupervisor'swaitForServer(...).then((up) => { if (up) {...} })had noelsebranch, so a boot that never became ready produced zero further output after "⏳ Starting server...". Stdout is now buffered alongside stderr (ServerSupervisor.getRecentLog()), and a timeout prints a clear diagnostic plus the buffered output instead of staying silent. Does not by itself explain why boot never completes on a given machine — see the issue for further reproduction. Regression guard:tests/unit/cli-serve-readiness-timeout-6321.test.ts. -
fix(pricing): Pricing Sync dashboard no longer stuck on "Next Sync: Never" / "Synced Models: 0" (#6325) —
pricingSync.tskept sync state (lastSyncTime,lastSyncModelCount) in module-level vars, but the background periodic sync (instrumentation-node.ts) and the dashboard status route (/api/pricing/sync) each import the module from separate Next.js standalone webpack chunks, giving each its own independent state;getSyncStatus()read the (empty) API-route instance's vars. Sync status is now additionally persisted to a newpricing_sync_statuskey_valuenamespace andgetSyncStatus()falls back to it when the local module instance never ran a sync itself. Regression guard:tests/unit/pricing-sync-cross-instance.test.ts. -
fix(api): stop spuriously 403-ing "Invalid request origin" on
POST /api/providers/health-autopilot/actionsfor Docker/LAN dashboard requests (#6277) — the route carried a duplicate per-routevalidateBrowserMutationOrigincheck re-added by the v3.8.42 release squash after PR #5278 centralized origin enforcement in the authz pipeline; the pipeline stripsPEER_IP_HEADERbefore forwarding, so the stale duplicate check could no longer resolve the LAN "direct-local-host" candidate and rejected legitimate same-origin LAN mutations (e.g. clicking "remove cooldown" when accessed via a LAN IP). Removed the duplicate check — origin validation is now solely enforced by the centralized pipeline check, which already handles this case correctly. Regression guard:tests/unit/serial/provider-health-autopilot.test.ts. -
fix(resilience): a bare, unrecognized
403from a no-credential (authType:"none") provider like mimocode or theoldllm no longer permanently bans the connection (#6315, #6345) —classifyProviderError()'s 403 branch only exemptedapikeyproviders from the terminalFORBIDDENclassification, so these free/stateless proxies (no real account/credential to revoke) fell through toFORBIDDENon the first unmatched 403 and gotisActive:false, testStatus:"banned"with no cooldown or retry. The exemption now also coversauthType:"none"providers, returningnull(recoverable) so the existing connection-cooldown/retry layer handles it. Regression guard:tests/unit/errorClassifier-noauth-403-6315.test.ts. -
fix(providers): the Auggie (Augment CLI) executor no longer fails on Windows with
spawn EINVAL(#6304) — the global-npm install exposesauggieas a.cmdshim, which Node'schild_process.spawncannot launch on win32 withoutshell: true. Both spawn sites (streaming + theauggie --versiontest) now go through a sharedbuildAuggieSpawnOptions()that setsshell: process.platform === "win32"; the argv (built bybuildAuggieArgs()with a registry-validatedmodeland a trailing--end-of-options marker) is unchanged, so the argument-injection surface stays closed on non-Windows. Regression guard:tests/unit/auggie-win32-spawn-6304.test.ts. -
fix(api): the dashboard "Test model" action is now a clean connection test (#6240) —
modelTestRunnersent its probe request without an explicit compression override, so whenever the operator's globalcompression.enabledflag was on the test call inherited compression (and any Output-Styles system prompt), polluting the result. The internal test requests now sendX-OmniRoute-Compression: off, andchatCorehonors an explicitoffheader even whencompression.enabledis globally true. Regression guards:tests/unit/model-test-runner-compression-off-6240.test.ts,tests/integration/test-model-compression-off-6240.test.ts. -
fix(startup): an update/restart could crash the whole server at boot with
TypeError: Cannot create property 'message' on string 'Database closed', masking the real failure and 500-ing every request until manually restarted (#6560, plausibly the root cause of #6594's post-upgrade 500) —driverFactory.ts::preInitSqlJs()cached its sql.js WASM adapter per file path in aglobalThis-backed map for idempotency, but never checked whether the cached adapter had since been closed (e.g. bygracefulShutdown/resetDbInstanceracing a reload); reusing that dead handle made the very next query throw sql.js's own bare string"Database closed"(not anError) straight out ofinstrumentation-node.ts's previously-unguardedensureDbInitialized()call, and Next.js's internalregisterInstrumentation()wrapper unconditionally doeserr.message = ...on whateverregister()rejects with — assigning.messageon a primitive string throws in strict mode, so the secondaryTypeErroris what actually crashed the process. Fixed in two parts:preInitSqlJs()now evicts a closed cached adapter and creates a fresh one instead of returning it; a newensureDbReadyForBoot()wraps the DB-init call, normalizes any non-Error throw vianormalizeBootError(), and retries once specifically for a transient "database closed" message (now succeeding against the fresh adapter) before re-throwing anything else as a realError. Regression guard:tests/unit/instrumentation-database-closed-6560.test.ts. -
fix(api):
POST /api/keysno longer hangs 20–90+ seconds on a fresh install, even with valid auth (#6570) —cloudEnableddefaults totrueinsrc/lib/db/settings.ts::getSettings()on any install with no persisted settings row (i.e. every fresh install), so the create-key handler's unconditionalawait syncKeysToCloudIfEnabled()always attempted a real outboundfetch()toCLOUD_URLviasyncToCloud(); when that endpoint is unset/unreachable/slow, the HTTP response blocked until the request settled or timed out — unlike sibling routes (POST /api/keys/:id/regenerate,GET /api/combos), which never touch this side effect at all.src/app/api/keys/route.tsnow dispatchessyncKeysToCloudIfEnabled()fire-and-forget (void) instead of awaiting it; its internal try/catch already logs failures, so cloud sync still runs, it just no longer blocks the response. Regression guard:tests/unit/api-keys-create-no-hang-6570.test.ts(asserts the route resolves in well under 2s even when the Cloud-syncfetch()is stubbed to never settle) + updatedtests/integration/api-keys.test.ts(the pre-existing "triggers cloud sync"/"still succeeds when cloud sync fails" tests, which previously hung indefinitely on this exact path, now await the fire-and-forget sync tick before asserting). -
fix(api): editing any existing OpenAI Codex provider connection in the dashboard returned "Invalid request" and the edit could never be saved (#6562) —
createProviderConnection()(src/lib/db/providers.ts) auto-increments a new connection'sprioritytoMAX(priority)+1per provider with no upper bound, and OAuth-imported connections (Codexcodex-auth/import/import-bulk, up to 50 accounts per call, callable repeatedly — the standard Codex bulk-account-rotation workflow) never pass throughcreateProviderSchema's Zod validation at all, so nothing ever capped that value;EditConnectionModal'shandleSubmitalways resends the connection's currentpriorityunchanged on every save, andupdateProviderConnectionSchemacappedpriority/globalPriorityatmax(100)— a UI-only ceiling the create path never enforced — so the first edit of any connection whose priority had already grown past 100 (routine once a Codex account count exceeds 100) failed validation regardless of which field the user actually changed. Raised the ceiling tomax(100_000)on both fields — still bounded (a genuinely out-of-range value is still rejected), just wide enough to accept priorities the app itself already produces. Regression guard:tests/unit/codex-connection-edit-6562.test.ts(a Codex OAuth connection whose priority already exceeds the old 100 cap now validates + persists on edit; a control payload with a still-genuinely-invalid priority is still rejected with "Invalid request"). -
mimocode: rotate accounts on MiMoCode's rate-limit-style 400s (body-classified) instead of failing on the first account; malformed 400s still fail fast with the real upstream error (#6648 — thanks @pizzav-xyz)
-
fix(cli): compression CLI REST fallback now reads/writes the canonical
defaultModefield (surfaced asstrategy) instead of a nonexistentenginekey, and table output renders nested objects as JSON instead of[object Object](#6571 — thanks @charleszolot) -
fix(providers): web-cookie providers without a
providerRegistry.tsentry (lmarena,gemini-business,poe-web,venice-web,v0-vercel-web) now reportunsupported: trueinstead of silently "OK" (#6309) —validateWebCookieProvider()(src/lib/providers/validation.ts) previously required a registry entry and returned "Provider not found in registry" for these; a fallback toWEB_COOKIE_PROVIDERS[provider].websitewas proposed, but live verification showed the${website}/modelsprobe does not reliably signal session validity for these providers (redirects/SPA 200s regardless of cookie validity — e.g. lmarena's real API isarena.ai, notlmarena.ai; Poe's real endpoint is a GraphQL POST, not a REST/models), so it would report an expired or garbage cookie as valid. Until each provider has a verified, side-effect-free auth probe against its real API host, the fallback now returnsunsupported(no network call) instead of a false positive. Regression guard:tests/unit/web-cookie-validation-fallback.test.ts. (thanks @oyi77) -
fix(api):
POST /api/middleware/hooksandPUT /api/middleware/hooks/[name]no longer leak raw internal error messages in their 500 responses (#6645 — thanks @chirag127) — both catch blocks returnederror?.messagedirectly (Hard Rule #12), which could surface internal SQLite path fragments on a DB failure; both now route throughsanitizeErrorMessage()fromopen-sse/utils/error.ts. Regression guard:tests/unit/middleware-hooks-error-sanitization.test.ts. -
fix(docker): compile better-sqlite3 for the server Docker image (Dokploy/self-hosted builds) via a direct
node-gyp rebuildinsidenode_modules/better-sqlite3, instead ofnpm rebuild better-sqlite3(#6700) — thebuilderstage installs dependencies withnpm ci --ignore-scripts(deliberate: closes the supply-chain surface where a transitive dep's install script runs arbitrary code) and re-enables the native build for the one package that needs it;npm rebuild <pkg>re-runs that indirectly through the package's own install script, which under npm 11 depends on npm's script-allowlist machinery correctly re-enabling it — some self-hosted build environments (e.g. Dokploy) hit a broken/mismatched native binding through that indirection. Invokingnode-gyp rebuilddirectly bypasses npm's script-running layer entirely and is deterministic regardless of npm version. Regression guard:tests/unit/dockerfile-better-sqlite3-node-gyp-6700.test.ts. (thanks @nowhats-br) -
fix(providers): the Cloudflare relay Worker deploy fix in #6416/#6618 still failed uploads in practice — it changed the multipart Content-Type but kept the emitted worker source as an ES module (
export default { fetch(...) }) withmain_modulemetadata; Cloudflare's Workers upload API parses a plainapplication/javascriptscript part as Service Worker syntax regardless of themain_modulemetadata field, andmain_modulerequires the script to actually be an ES module (top-levelexport), so the mismatch still rejected the upload (#6496).buildCloudflareWorkerScript()(src/lib/proxyRelay/cloudflareWorkerScript.ts) now emits Service Worker syntax (addEventListener("fetch", ...), no top-levelexport) and the upload metadata usesbody_partinstead ofmain_module. Regression guard:tests/unit/relay-deploy-5128.test.ts(asserts the emitted script has noexport default, registers afetchlistener, and the upload metadata carriesbody_part/omitsmain_module; also proves the inlinedisPrivateHostname()SSRF guard still rejects bracketed IPv6 loopback/ULA hosts like[::1]/[fd00::1]after the script-body rewrite). (thanks @SeaXen) -
fix(providers): ChatGPT Web (
chatgpt-web) responses rendered raw ChatGPT UI citation markup — private-use marker tokens (e.g.citeturn0search0) andurl…inline-link markers — instead of real Markdown links, since these only ever get resolved client-side by chatgpt.com's own JS usingmessage.metadata.content_references(#6635) —cleanChatGptText()now resolvescontent_references(grouped webpages, footnote sources, inlinewebpage/urlmentions) into[label](url)Markdown links for both the streaming and non-streaming response builders, and for the GPT-5.5 Prostream_handoffpolled-answer path, falling back to stripping any marker that has no resolvable source instead of leaking the raw private-use bytes. The citation parsing/rendering logic was extracted into a new pure sibling module (open-sse/executors/chatgpt-web/citations.ts) to keep the executor under the frozen file-size cap. Regression guard:tests/unit/chatgpt-web-citations.test.ts(non-streaming citation resolution, streaming marker buffering across split SSE chunks, and the Pro-handoff polled-answer path). (thanks @Thinkscape) -
fix(dashboard): the live-dashboard WebSocket descriptor handshake (
GET /api/v1/ws?handshake=1) and the lightweightGET /api/health/pingliveness probe both 401'd for unauthenticated callers, even though both are metadata-only reads intended to be public (#6335) —clientApiPolicyrequired a bearer/dashboard-session before the WS route handler could even return its ownwsAuth/protocol descriptor, and/api/health/pingwas never added toPUBLIC_READONLY_API_ROUTE_PREFIXESdespite its own docstring documenting it as "No auth required."clientApiPolicy.evaluate()now allows an anonymous{kind:"anonymous", id:"ws-handshake"}subject for GET/HEAD/OPTIONS on/api/v1/ws?handshake=1(the route handler still performs its own real wsAuth/dashboard/API-key decision before opening the socket), and/api/health/pingis now inPUBLIC_READONLY_API_ROUTE_PREFIXES. Regression guard:tests/unit/authz/client-api-policy.test.ts(WS handshake allowed, including relative request URLs),tests/unit/public-api-routes.test.ts, andtests/unit/authz/classify.test.ts(/api/health/pingclassifiedPUBLIC). (thanks @JxnLexn) -
fix(providers): wire the Devin cloud-agent provider into the generic provider-page validator and static model catalog, matching the existing
julescloud-agent pattern (#6142) -
fix(providers): honor a provider-level proxy assigned to no-auth providers like MiMoCode Free (#6272)
-
fix(api): merge tool_call continuation deltas that carry only
id(noindex) so tool-call arguments are no longer split/lost in request/response logs (#6276) -
fix(providers): modernize the
lmarenaprovider for the Arena.ai rebrand — route chat througharena.aicreate-evaluation with Chrome TLS impersonation, seed a static Direct-chat Text/Search + Image catalog, and keep thelmarena/lmawire id for back-compat (#6280) — thanks @backryun -
fix(providers): web-provider model discovery updated — qwen-web uses the slash-terminated models endpoint (avoiding a blocked 307 redirect), and kimi-web matches the current request shape (POST with bearer +
kimi-authcookie replay) with its catalog refreshed to the current non-agent models (#6308 — thanks @janeza2). -
fix(logs): the request-log detail modal no longer reopens by itself after being closed — a stale in-flight detail refresh resolved after close and re-triggered the modal open state (#6323 — thanks @xz-dev).
-
fix(providers): update SenseNova Token Plan support — register the token-plan model ids/constants and adjust the SenseNova registry so token-plan accounts route correctly (#6330 — thanks @xz-dev).
-
fix(providers): give v0-vercel-web its own alias so its credentials are detected (#6343)
-
fix(providers): route AgentRouter key validation through the CC wire image so a valid key no longer 403s as "Invalid API key" (#6377)
-
fix(db): stop legacy log-archive migration from deleting the live app-logger directory and crashing startup on a stat/stream race (#6401, #6799)
-
fix(docs): document Turbopack build memory tradeoff and
OMNIROUTE_USE_TURBOPACK=0webpack fallback for RAM-constrained machines (#6409) -
fix(compression): surface silently-dropped stacked-pipeline steps (session-dedup, ccr) and stop the aggregate inflation guard from misfiring on a genuine no-op (#6479, #6480, #6491)
-
fix(providers): honor the
max_tokencapability override in the reasoning-token-buffer output cap (#6524) -
fix(dashboard): the onboarding tier-flow diagram rendered broken — its SVGs lived in the repo-root
images/(not a served path); moved topublic/images/so Next.js serves them (#6538 — thanks @ianriizky). -
fix(routing): the
autocombo's no-auth candidate pool now honors a disabled provider connection's ownisActive=false(the toggle on the main Providers grid card), not just the separate globalblockedProviderssetting — disabling opencode/mimocode/etc. via the grid toggle no longer leaves it in rotation (#6557). -
fix(sse): server-tool literal names (e.g.
web_search) are preserved in message history andtool_choiceinstead of being namespaced/rewritten, so follow-up turns referencing those tools keep working (#6586 — thanks @MikeTuev). -
fix(api): recognize OpenRouter reasoning/reasoning_details in non-streaming OpenAI-to-Claude conversion (#6623)
-
fix(db): share one in-flight sql.js load across concurrent
preInitSqlJs()callers to stop the boot-time thundering-herd re-decode of the whole database file (#6628) -
fix(db): unwrap lone named-parameter objects before
sql.jsstmt.bind()so@/:/$-style named placeholders bind correctly instead of throwing "Wrong API use" (#6802) -
fix(db): break probe-failed/restore loop on large storage.sqlite (#6632 — thanks @KooshaPari).
-
fix(ci): exclude check-test-masking.test.ts's own tautology fixtures from the diff-based test-masking gate and recognize
✗in validate-release-green's failure-line detector (#6634) -
fix(routing): recognize Kimi-style "exceeded model token limit" 400 as context overflow so combo fallback continues to the next target (#6637)
-
fix(cli): Claude Code installed via WinGet is now detected on Windows (the WinGet install path was missing from the binary lookup) (#6647 — thanks @enjoyer-hub).
-
fix(providers): removed obsolete/defunct providers from the catalog (glhf, kluster, cablyai, inclusionai) (#6675 — thanks @backryun).
-
fix(sse): requests rejected before
handleChatCore(circuit-breaker/cooldown gate or combo with all targets exhausted) are now recorded inusage_historytoo, so a key whose traffic was entirely gate-rejected no longer shows "zero requests" in the per-API-key usage counter (#6698). -
fix(sse): unwrap bare
{function:{…}}tools so OpenAI-shape clients no longer have tools silently dropped in Claude translation. (thanks @samir-abis) -
fix(oauth): stop merging distinct Codex OAuth logins that share an email but lack a verifiable account id, preventing silent token overwrite. (thanks @lucasjustinudin)
-
fix(codex): detect "model at capacity"/overloaded errors embedded in a 200-OK SSE stream and surface them as a real error so account fallback rotates, instead of passing them through as a successful response. (thanks @ryanngit)
-
fix(volcengine): clamp
max_tokensto the VolcEngine Ark endpoint cap for the Kimi model so oversized values no longer 400. (thanks @whale9820) -
fix(antigravity): surface aborted/malformed Gemini tool calls (e.g.
MALFORMED_FUNCTION_CALL) as an explicit non-end_turnfinish reason instead of a silent clean completion. (thanks @anhdiepmmk) -
fix(routing): the reasoning-token headroom buffer clamps to the model's explicit output cap instead of inflating past it, and
getExplicitModelOutputCapfalls through to the registry/spec cap when a synced capability row exists without a numericlimit_output(#6714) — thanks @xz-dev -
fix(api):
omniroute health(andhealth components/health watch) returnedError: HTTP 404(#6677) —bin/cli/commands/health.mjscalledapiFetch("/api/health", ...), a route that was moved toGET /api/monitoring/health(src/app/api/monitoring/health/route.ts) without updating the CLI;src/app/api/health/on disk only hasdegradation/route.tsandping/route.ts, no top-level handler.runHealthCommand()/runHealthComponentsCommand()now call/api/monitoring/healthand read its actual payload shape (activeConnections,circuitBreakers: {open, halfOpen, closed},memoryUsage) instead of the old, nonexistentrequests/breakers/cache/memoryfields. Regression guard:tests/unit/cli-health-monitoring-route.test.ts. -
fix(startup): webpack build broke on case-insensitive filesystems (macOS APFS default, Windows) with a casing-collision warning plus "not exported" errors in
StudioConfigPane.tsx/ChatTab.tsx(#6584) —src/app/(dashboard)/dashboard/playground/components/ReasoningControls.tsx(the component) andreasoningControls.ts(the utils module) shared the same lower-cased stem in the same directory, and two importers used the extensionless formfrom "./reasoningControls", the exact resolution path that becomes ambiguous once casing is folded. Renamed the utils module toreasoningControlUtils.ts(no collision) and updated the 3 import sites. Regression guard:tests/unit/case-collision-6584.test.ts(scanssrc//open-sse/for any same-directory, case-only filename collision). (#6584) -
fix(build): Turbopack production build emitted an "Overly broad patterns can lead to build performance issues" warning per entry point importing
src/lib/agentSkills/generator.ts(603 warnings reported on v3.8.46, up from 379 on v3.8.45) (#6582) —generator.ts'soutputBaseis built aspath.isAbsolute(outputDir) ? outputDir : path.join(process.cwd(), outputDir), whereoutputDiris a runtime function parameter, not a compile-time literal, so Turbopack's build-time file-tracing analyzer can't statically narrow the several dynamicreaddirSync/rmSync/readFileSync/writeFileSynccall sites a few lines below and falls back to a project-wide glob; #6366's commit message claimed to "anchor the base path with a literal" but the shipped code never did. Since this fs access is legitimate and bounded (skills/<id>/SKILL.md, ~48 known IDs),next.config.mjs'sturbopack.ignoreIssue(Next.js 16.2+) now suppresses this specific, known-benign diagnostic, mirroring the existingwebpack.ignoreWarnings/isNextIntlExtractorDynamicImportWarningprecedent already in the same file for the webpack path. Regression guard:tests/unit/next-config.test.ts(asserts theturbopack.ignoreIssuerule shape targetingsrc/lib/agentSkills/**). -
fix(providers): Codex Desktop requests to
gpt-5.3-codex-sparkfailed with[400]: Tool 'image_generation' is not supported with gpt-5.3-codex-spark, even on paid-plan accounts (#6651) —CodexExecutor.transformRequest(open-sse/executors/codex.ts) only dropped the Codex Desktop-injectedimage_generationhosted tool whenisCodexFreePlan()matched the account's plan, with no awareness that Spark-scope Codex models rejectimage_generationupstream regardless of plan.dropImageGenerationnow also drops it whengetCodexModelScope(model) === "spark"(the existing Spark classifier fromopen-sse/config/codexQuotaScopes.ts), independent of account plan. Regression guard:tests/unit/codex-spark-image-generation.test.ts(thanks @alltomatos for independently catching and fixing it via #6819). -
fix(providers): the provider quota card's weekly/session bars re-sorted by remaining percentage instead of staying in a fixed, deterministic order (#6687) —
QuotaCardExpanded.tsx'ssortQuotasByRemaining()(added in #5977) was applied unconditionally viauseMemo(() => sortQuotasByRemaining(quotas), [quotas]), undoing the deterministicCODEX_QUOTA_ORDER/GLM_QUOTA_ORDERwindow orderquotaParsing.ts'ssortCodexOrder()/sortGlmOrder()(added in #6336) already established for Codex and the GLM family — since #6336 never touchedQuotaCardExpanded.tsx, the two orderings never composed, so e.g. a Codexsessionwindow with less headroom thanweeklyrendered after it instead of staying first. A newhasFixedQuotaOrder()(quotaParsing.ts) andresolveQuotaDisplayOrder()(QuotaCardExpanded.tsx) now skip the remaining-% re-sort for providers with a fixed window order, threadingproviderIdfromQuotaCard.tsxthrough to the display layer; every other provider still gets the remaining-% sort. Regression guard:tests/unit/quota-card-expanded-fixed-order-6687.test.ts. -
fix(i18n): pt-BR was missing 194 UI keys present in
en.json— a real, silent data-sync gap, not covered by any duplicate/mislabeled #6694 (that issue's 9providers.*keys are disjoint, present-but-untranslated sentinels caused by a separateproviderText()fallback bug) (#6695) —scripts/i18n/sync-ui-keys.mjs(which mirrors newly-addeden.jsonkeys into every locale) wasn't re-run after recenten.jsonadditions, and the CIi18n:check-ui-coveragegate only fails a locale below an 80% threshold, so pt-BR stayed green at 93.8% coverage despite the gap. Backfilled all 194 missing keys intosrc/i18n/messages/pt-BR.json(translated to Brazilian Portuguese, no leftover__MISSING__markers) vianpm run i18n:sync-ui -- --locale=pt-BR+ manual translation. Regression guard:tests/unit/i18n-pt-br.test.ts(new case asserting fullen.json→pt-BR.jsonkey parity, so a future drift fails a fast unit test instead of silently degrading the coverage percentage). -
fix(startup):
omniroute --mcpcrashed at Node ESM link time withERR_MODULE_NOT_FOUNDforioredison installs where the published MCP bundle didn't happen to haveioredisrescued from a parentnode_modules(#6559) —src/shared/utils/rateLimiter.tshad a top-level staticimport Redis from "ioredis"; that module is only ever reached via a lazyawait import(...)several call-sites deep in the MCP tool chain, but esbuild's--packages=externalbundling of the MCP server (scripts/build/prepublish.tsStep 8.5) still hoisted rateLimiter.ts's own static import into a real top-level ESM import in the compileddist/open-sse/mcp-server/server.js, forcing Node to resolveioredisat module-link time — before any--mcpstartup code runs — andioredisis not guaranteed to ship in the MCP-only bundle'snode_modules.getRedisClient()now lazily importsioredison first use (matching the established soft-dependency pattern insrc/lib/quota/redisQuotaStore.ts) while still throwing synchronously when Redis isn't configured. Regression guard:tests/unit/build/mcp-bundle-no-eager-ioredis.test.ts(bundles the real MCP server entrypoint with the exact publish-time esbuild flags and asserts no top-level staticioredisimport remains, while the pre-existing lazyawait import("ioredis")inredisQuotaStore.tsstays intact). -
fix(providers): Kiro sent the adaptive-thinking
additionalModelRequestFieldsenvelope forclaude-sonnet-4.5/claude-haiku-4.5, which Kiro/CodeWhisperer rejects upstream with a raw[400]: additionalModelRequestFields is not supported for this model(#6576) —buildKiroPayload()(open-sse/translator/request/openai-to-kiro.ts) gated the field on the generic Anthropic-APIsupportsReasoning()capability flag, which istruefor both models on Anthropic's direct API but does not reflect what Kiro's CodeWhisperer backend actually accepts; onlyclaude-sonnet-5is confirmed adaptive-thinking-capable there. A new Kiro-specific allowlist (supportsKiroAdaptiveThinking()inopen-sse/translator/request/openai-to-kiro/adaptiveThinking.ts) now gates the envelope instead. Regression guard:tests/unit/repro-6576-kiro-thinking-unsupported-model.test.ts. -
fix(translator): Cursor's local Subagent tool call is no longer rejected with
cloud_base_branch may only be specified when environment equals cloud— the Responses→Chat tool-arg cleanup (stripEmptyOptionalToolArgs) was scoped to Claude Code'sReadtool only, so Cursor'sSubagenttool passed through with the cloud-onlycloud_base_branch: ""(Cursor treats an empty string as "specified" and rejects the call before starting the local subagent). The cleanup now covers an allowlist ofRead+Subagent; arbitrary tools are still left untouched (empty strings/arrays can be valid payloads for them). Regression guard:tests/unit/openai-responses-subagent-strip-2446.test.ts. (thanks @like3213934360-lab) -
fix(translator): GLM 5.2 (and other OpenAI-compatible upstreams that stream a tool call's
idandfunction.namein separate SSE chunks) no longer produce an empty tool name /No such tool available:error through the Claude/messagespath — theopenai-to-claudestreaming translator emittedcontent_block_startimmediately on the id-only chunk with an emptyname, and the Claude SSE protocol cannot patch a block after it is emitted, so the later name-only chunk was silently dropped. It now deferscontent_block_startuntil the tool name arrives (falling back to starting the block when arguments arrive first), so the emittedtool_usealways carries the real name. Regression guard:tests/unit/openai-to-claude-glm-split-tool-name-2077.test.ts. (thanks @itiwant) -
fix(resilience): OmniRoute didn't respect an exhausted Ollama Cloud (or any other apikey-category provider) quota — it retried the account seconds later instead of waiting out the real reset window (#6638) —
shouldPreserveQuotaSignalsFor429()/checkFallbackError()(open-sse/services/accountFallback.ts) only applied body-text quota classification (daily/monthly/weekly quota-exhausted detection) to OAuth-category providers; apikey-category 429s (Ollama Cloud, OpenAI, etc.) always fell through to the generic short rate-limit cooldown regardless of what the error body said, andparseRetryFromErrorText()also had no support for day-granularity reset hints ("Your quota will reset in 3 days.") — only Xh/Ym/Zs combos. An explicit quota-exhausted signal in the body (looksLikeQuotaExhausted()) now overrides the apikey-category default via the newshouldPreserveQuotaSignals()(open-sse/services/quotaResetParsing.ts), andparseDayGranularityResetMs()parses whole-day reset countdowns so the real multi-day window is honored instead of a few seconds of backoff. Regression guard:tests/unit/issue-6638-ollama-quota.test.ts+ 2 alignedtests/unit/account-fallback-service.test.tscases that previously asserted the buggy rate_limit_exceeded/undefined-dailyQuotaExhausted behavior for apikey-provider quota text. -
fix(resilience): a combo step "pinned" to one fingerprint account (mimocode/mcode/opencode multi-account providers) never actually resolved to that account, so it couldn't fail over when the pinned account was depleted (#6696, relates #6612) — the combo builder UI encodes an account pin as a composite connectionId (
${rowId}|fp|${fingerprint},src/lib/combos/builderOptions.ts), butexpandTargetsByFingerprints()(open-sse/services/combo/fingerprintExpansion.ts) looked that composite string up directly inconnectionById(keyed by real DB row ids), gotundefined, and passed the target through unchanged, still carrying the bogus composite id — so downstream credential resolution could never match it either.expandTargetsByFingerprints()now splits the|fp|composite id back into the real connection row id + the pinned fingerprint (newsplitFingerprintPin()helper) before any lookup, resolving the target to the real connectionId (with the pinned fingerprint carried on the newpinnedFingerprintfield) instead of the inert composite string. Regression guard:tests/unit/combo-fingerprint-pin-6696.test.ts. -
fix(api): Responses passthrough emitted event-only SSE frames (no
data:line) for every dropped commentary event, breaking the OpenAI Python SDK'ssse.json()parser (#6561), follow-up to #6199/#6232 — the commentary-dropcontinue;branches inopen-sse/utils/stream.tsskipped thedata:line for a dropped commentary event but never cleared the already-bufferedevent:line for that same frame, so the next blank line flushed the staleevent:line alone. Both drop sites now callclearPendingPassthroughEvent()beforecontinue, discarding the buffered prefix along with the dropped payload; the commentary-drop decision itself was extracted into a newopen-sse/utils/responsesCommentaryDrop.tsso the fix does not grow the frozenstream.ts. Regression guard:tests/unit/responses-commentary-event-frame-6561.test.ts(realisticevent:\ndata:\n\nframes — the existing #6199 test only used baredata:lines and never exercised this path). -
fix(compression):
/api/compression/preview's top-leveloriginalTokens/compressedTokensdiverged fromengineBreakdown[0]'s counts for the same single-engine run (tiktoken outer counts vs theJSON.stringify(...).length/4estimate per engine), worst on small inputs. A newreconcileSingleEngineTokens()overwrites the single-engine breakdown entry with the outer, more accurate figures; multi-step pipeline breakdowns are left untouched (#6488). Regression guard:tests/unit/compression/preview-outer-engine-token-reconcile-6488.test.ts. -
fix(resilience): account selection could pick an account already out of quota upstream on every credentialed route except
chat/codex(#6686) —getProviderCredentials()(src/sse/services/auth.ts) only skips a connection when a local cache already flags it exhausted (isQuotaExhaustedForRequest/src/domain/quotaCache.ts); it never itself calls the registered upstreamQuotaFetcher. OnlygetProviderCredentialsWithQuotaPreflight()performs that live upstream check, and it was wired into exactly 2 call sites (src/sse/handlers/chat.ts,src/app/api/internal/codex-responses-ws/route.ts) — every other credentialed route (rerank,images/generations,images/edits,audio/transcriptions|speech|translations,videos/generations,music/generations,ocr,providers/[provider]/embeddings,providers/[provider]/images/generations,web/fetch,moderations,search) called the plain, cache-only selector, so an account whose cache entry was never populated (e.g. its first request landed on one of these routes) could be selected even at 0% quota remaining. Those 14 call sites now go throughgetProviderCredentialsWithQuotaPreflight()instead, matching chat/codex coverage. Regression guard:tests/unit/issue-6686-quota-preflight-coverage.test.ts(static check that none of the routes call the plain selector anymore + a behavioral check that the preflight-aware selector blocks a 100%-used account). -
fix(api):
reasoning_content(extended-thinking text) was silently dropped from/v1/chat/completionsSSE on theclaude-webandv0-vercel-webexecutors (#6662) — every chunk builder in both adapters hardcodeddelta: { content: ... }with no reasoning path, unlike the established pattern already used bydefault.ts/deepseek-web.ts/bedrock.tsand the real-Anthropic-APIclaude-to-openai.tstranslator (thinking_delta→reasoning_content).v0-vercel-web.tsnow forwards an upstreamdelta.reasoning_contentfield (streaming and non-streaming) the same waydeepseek-web.tsdoes.claude-web.ts'sbuildClaudeStreamingResponsenow maps acontent_block_start(type: "thinking")/content_block_delta(delta.thinking) pair ontodelta.reasoning_content, andclaude-web/payload.ts'stransformToClaude()no longer hardcodesthinking_mode: "off"— a newwantsExtendedThinking()derives it from the request'sreasoning_effort/reasoning.effort/thinking.typesignal, so extended thinking can actually be requested. Regression guard:tests/unit/issue-6662-repro.test.ts(RED→GREEN for both adapters). -
fix(api): the compression config PUT schema now accepts
enableRenderersfor the RTK engine instead of rejecting the documented option (#6703, #6757 — thanks @alltomatos, with an independent duplicate fix from @chirag127 via #6756). -
fix(api): raised the provider
apiKeylength cap for cookie-based web providers, whose session-cookie credentials legitimately exceed the previous limit (#6715, #6759 — thanks @alltomatos). -
fix(ci): publish electron-updater
latest*.ymlmanifests in electron release assets so auto-update can find them (#6766) -
fix(i18n): translate hardcoded Portuguese dashboard strings to English (#6761, #6768) (#6769 — thanks @chirag127).
-
fix(providers): strip redundant node prefix when resolving custom OpenAI/Anthropic-compatible connections by raw connection id, preventing double-namespaced model ids from 400ing upstream (#6772)
-
fix(providers): scope nvidia NIM 404s to the single failing model instead of cooling down the whole connection (#6773)
-
fix(codex): bump the default Codex CLI client identity from
0.142.0to0.144.0for compatibility with newer Codex-backed models (#6780) — thanks @quanturbo -
fix(ci): the blocking "Impacted unit tests (TIA)" step false-redded any PR whose impact graph reached a dashboard component — it ran every selected test under
--import tsx/esm, buttests/unit/dashboard/**requires the--import tsxCJS transform (ESM-only deep imports like@lobehub/icons/es/*), exactly as the canonicaltest:unit:ci:shardalready does per segment. The impacted selection is now split by segment with matching loaders (closes #6787). -
fix(translator): read PDF/video
file_dataattachments on the OpenAI→Gemini/Antigravity and OpenAI→Claude paths so multimodal documents (not just images) reach the upstream — PDFs map todocument/inlineDataand videos keep theirvideo/mp4mime instead of being dropped (#6790 — thanks @Witroch4, with an independent report/fix from @samimozcan via #6762/#6753). -
fix(providers): ensure DeepSeek Web SSE emits [DONE] after FINISHED (#6791 — thanks @Pitchfork-and-Torch).
-
fix(api): the compression config
PUTschema (stackedPipelineStepSchema) now accepts everyENGINE_CATALOGid — the structural enginessession-dedup/ccr/headroom/relevance/llmlingua/omniglyphand theaggressiveultraintensity — so aGET→PUTround-trip of a stacked pipeline no longer 400s on a valid engine the discriminated union had omitted (#6747 — thanks @Pitchfork-and-Torch). -
fix(cursor): send the Agent CLI build id as
x-cursor-client-versionso Cursor upstream accepts requests from the current CLI build instead of a stale hardcoded version (#6795 — thanks @andrewmunsell). -
fix(cli): waitForServer() no longer reports ready from a raw TCP accept alone — requires a fast HTTP rejection or a real health response, so the "OmniRoute is running!" banner no longer fires 30-60s before the server can actually answer requests (#6800)
-
fix(sse): de-flake timing-sensitive combo cooldown/breaker tests + add explicit MCP audit shutdown timeout (#6803)
-
fix(codex): strip include from compact responses requests (#6805 — thanks @yinaoxiong).
-
fix(dashboard): surface Claude extraUsage credits in quota card when quotas is empty (#6806)
-
Request count by provider & date: Dashboard → Analytics now shows a dedicated table of request counts grouped by provider and calendar date (plus token totals), for providers that bill per-request rather than per-token — sortable columns and a single-date filter. New
getProviderDailyUsageRows()query (src/lib/db/usageAnalytics.ts) and its ownGET /api/usage/requests-by-provider-dateroute (kept separate from the frozen/api/usage/analyticsroute). Regression guard:tests/unit/db-provider-daily-usage-4009.test.ts. (#4009 — thanks @tjengbudi) -
fix(resilience): an Ollama Cloud (or any apikey-category provider) account that hit a weekly usage cap kept getting retried every few minutes instead of backing off (#3709) — the upstream 429 body ("you (<account>) have reached your weekly usage limit") was invisible to
checkFallbackError's existing subscription-quota-text classifier (Issue #2321) because that branch is gated byshouldUseQuotaSignal, which is oauth-only, so apikey providers likeollama-cloudfell through to the generic exponential backoff (~1s, capped at 2min) — one account took 285x429 in 48h. A newisWeeklyUsageLimitText/buildWeeklyQuotaFallbackclassifier (extracted, with the existing subscription-quota logic, into a newopen-sse/services/quotaTextCooldowns.tsmodule so the frozenaccountFallback.tsdidn't have to grow) runs unconditionally and applies a 24hQUOTA_EXHAUSTEDcooldown regardless of provider category. Regression guard:tests/unit/ollama-cloud-weekly-quota-cooldown-3709.test.ts. -
fix(providers): an explicit
thinking.budget_tokens: 0is now honored in the OpenAI→Gemini transform (thinking disabled) instead of being treated as unset (#6813, #6821 — thanks @alltomatos). -
fix(bootstrap): filter empty
process.envvalues before spawning embedded services so a blank env var no longer crashes the Docker bootstrap in a restart loop (#6828 — thanks @AndrianBalanescu). -
fix(providers): classify upstream
404responses asMODEL_NOT_FOUND(model lockout) instead of a retryable provider error, stopping the retry storm when a single model is missing (#6829 — thanks @AndrianBalanescu). -
fix(mcp): de-duplicate
TOTAL_MCP_TOOL_COUNTby tool name instead of double-counting collections (#6854) -
fix(usage): xAI's exact provider-reported
cost_in_usd_ticksno longer silently acceptsnull/""/negative values as a valid$0exact cost —extractUsageFromResponse()(open-sse/handlers/usageExtractor.ts) andnormalizeUsage()(open-sse/utils/usageTracking.ts) now requiretypeof value === "number" && Number.isFinite(value) && value >= 0instead of coercing withNumber(x), so a malformed exact cost correctly falls back to the token-based estimate instead of masking it with a bogus$0. Regression guard:tests/unit/xai-exact-cost-2453.test.ts(rejects null/empty/negative exact costs on both call sites). (#6856 — thanks @KooshaPari) -
fix(plugin): the
@omniroute/opencode-plugindynamic provider hook stopped embedding its OC-1.17.8+-gate-compatibleopencode--prefixed provider id into model routing fields (ModelV2.id/providerID, combo catalog keys) — OmniRoute's server has noopencode-<x>provider alias, so every dispatched model failed credential lookup with "No credentials for opencode-omniroute" (#6859). -
fix(sse): apply cliproxyapiModelMapping at CLIProxyAPI dispatch time (#6876)
-
fix(sse): defer
response.completeduntil a trailing usage-only chunk arrives on/v1/responsesstreams (#6906) -
fix(cli): ship
head-response-guard.cjsinto the standalone bundle —server-ws.mjsimported it without a matchingEXTRA_MODULE_ENTRIESentry, so everybuild:releasedist crashed at boot withERR_MODULE_NOT_FOUND; a new regression test derives the required sidecars fromserver-ws.mjsimports (#6908) -
fix(sse): wire the shared quota-fetch throttle into DeepSeek, Bailian, OpenCode, and Crof quota fetchers, not just Codex (#6911)
-
fix(sse): rename client-sent
max_completion_tokenstomax_tokensfor providers/models that only accept the legacy field (e.g. Volcengine Ark / DeepSeek), mirroring the existing reverse rename from #1961 (#6912) -
fix(sse): set includeServerSideToolInvocations on Antigravity tool cloak decoys (#6914)
-
fix(sse): classify LAN embeddings providers (10/8, 192.168/16, CGNAT) as no-auth instead of forcing bearer auth (#6925)
-
fix(sse): Qwen Web executor no longer sends
[object Object]when a message uses structured (array) content — the text parts are now flattened (#6927) -
perf(api): relay chat-completions routes now thread the already-fetched
RelayTokenintocheckRateLimit, skipping a redundantSELECT * FROM relay_tokens WHERE id = ?re-query on every request (#6930) -
fix(sse):
normalizeCodexMessageContentPartnow rewrites explicittype: "input_text"(not justtype: "text") tooutput_texton assistant-role Codex Responses input parts, so replayed assistant history sent by codex-cli asinput_textis no longer rejected by the Codex/OpenAI backend (#6932) -
fix(sse): omit removed
attachmentsfield from Muse Spark Web (Meta AI) persisted GraphQL query to fixUnknown type "AttachmentInput"502s (#6935) -
fix(dashboard): label audio/embeddings/image compatible providers by kind instead of "Chat" on ProviderCard (#6936)
-
fix(api): model-list discovery for LAN-local OpenAI-compatible providers (e.g. LM Studio) now uses the same local-first SSRF guard as the connection test, instead of the stricter guard that always blocked LAN hosts (#6939)
-
fix(providers):
openai->geminitransform now mapsreasoning_effort: "none"tothinkingConfig.thinkingBudget: 0(withincludeThoughts: false), giving callers an explicit, documented off-switch for Gemini thinking; the no-knob-at-all default injection (#4170) is unchanged (#6813, thanks @rafaumeu) -
fix(sse): escape backslash before brackets in ChatGPT-web citation link text, preventing a citation label containing
\from corrupting the generated Markdown link (#6944) — thanks @brick30llc-ctrl -
fix(oauth): tokenHealthCheck now lowercase-normalizes
conn.providerbefore checkingROTATING_REFRESH_PROVIDERS.has()and the GitHub Copilot sub-token refresh guard, so mixed-case provider values (e.g. "OpenAI", "Github") no longer bypass the rotating-refresh-token skip or the Copilot sub-token refresh (#6947) -
fix(sse): make Codex Responses tool-arg normalization schema-aware — drop values equal to the tool's declared JSON Schema
default, generalize empty-optional stripping to any tool (not justRead/Subagent) viaschema.required, and thread each tool's schema from the request'stools[]into the streaming response translator (#6951) -
fix(sse): drop internal commentary-phase Responses output in TRANSLATE-mode streams, not just PASSTHROUGH — codex/Responses-upstream routes translated into another client format (e.g. Claude Code) no longer leak duplicate prose and narrated tool-call arguments into the client text channel (#6952)
-
fix(combos): embeddings-only and rerank-only models (e.g. JinaAI, Gemini auto-imported, OpenRouter custom, reranker models) no longer disappear from the combo builder's model picker — the leftover chat-only
isChatCapablegate inaddModelOption()has been removed (#6975). -
fix(combos): when 2+ distinct model ids from the same provider would render an identical display name in the combo builder picker (e.g. Mistral's
codestral-latest/codestral-2508aliases sharing one upstream catalog name), each colliding entry now falls back to its own id as the display label so every row stays visually distinguishable and findable (#6957).
📝 Maintenance
-
chore(release-captain): v3.8.47 pre-flight closed every deterministic release-tip base-red (#6967): restored the
no-explicit-anyseverity silently downgraded by #6786 (439 bulk suppressions had stopped matching), made the openadapter live-catalog test deterministic (test.after()was tearing the DB down mid-request), aligned the emergency-fallback and Cloud Code Gemini tests with the #6912/#6943 contracts, backfilled the #6909 i18n keys (en + pt-BR), re-exportedrelayProbeStats(db-rules), documentedOMNI_MAX_CONCURRENT_CONNECTIONS, and allowlisted migration gap 121. (thanks @diegosouzapw) -
chore(security): unbiased crypto digits for the doubao synthetic device id (CodeQL
js/biased-cryptographic-random); 405 method-first for/api/keys/{id}/devices(dast-smoke); Zod-validation forPOST /api/github-skills; the missingomni-github-skillsregistry entry + catalog count alignment. (thanks @diegosouzapw) -
chore(quality): cycle-close ratchet work — cleared the cycle's 11 net-new ESLint errors and made
validate-release-greensuppressions-aware; cleared the 2 remaining heavy-gate reds on the release tip; cycle rebaselines (cognitive/file-size/zizmor/coverage pcts) with justification keys. (thanks @diegosouzapw) -
chore(open-sse): removed the vestigial
// @ts-nocheckdirective fromopen-sse/utils/usageTracking.ts(#6173) —tscunder the standardtypecheck:coregate reports 0 errors for this 595-line hot-path file (executed for every provider response), so the suppression was no longer needed; removing it restores type-checking on the token-usage extraction/normalization path. (thanks @KooshaPari) -
chore(cli): the shell-completion cache paths (
readCache/refreshCache/writeCache) inbin/cli/commands/completion.mjsno longer swallow errors into a barecatch {}(#6257) — each now binds the error and, when the newOMNIROUTE_DEBUG_COMPLETIONenv var is set, emits a[omniroute completion]diagnostic tostderr; the caches still fail silently by default so a missing/corrupt cache never breaks tab-completion. (thanks @KooshaPari) -
chore(quality):
validate-release-green --full-cireproduces the fullci.ymlstatic gate set locally — the pre-flight now readsci.ymlitself and runs everynpm run check:*from thelint/quality-gate/quality-extended/docs-sync-strict/pr-test-policyjobs (--ratchet flags preserved,test-maskingagainstGITHUB_BASE_REF=main), skipping only the non-localpr-evidence/codeql-ratchet. Closes the gap where 11 static base-reds leaked to the v3.8.46 release PR in ~2h of layered CI. Also wired intonightly-release-greenso a static base-red opens a tracking issue the night it lands. Regression guard:tests/unit/validate-release-green.test.ts(+5extractCiGatescases). -
refactor(usage):
saveRequestUsage(entry: any)is now typed with a newUsageEntryinterface mirroring theusage_historycolumns 1:1 (#3512) — the other strayanys insrc/lib/usage/usageHistory.ts(getUsageHistoryfilter, thegetUsageDbnext-cursor cast,appendRequestLog's legacytokensparam,getRecentLogs's catch) were cleaned in the same pass, so the file now sits in thecheck:any-budget:t11zero-anyallowlist. The DB-entity ↔ TS-interface convention is documented indocs/architecture/CODEBASE_DOCUMENTATION.md§11. -
Merge-train script (
scripts/release/merge-train.sh): batch-validates N queued PRs as ONE merged result on the runner box — merges every queued PR into a throwaway worktree cut from the release tip, runs the fast-gates parity suite once, and prints the--adminevidence block per PR (merge-gates §7). Replaces O(N²) per-PR CI re-runs in merge-storms. Regression guard:tests/unit/merge-train-plan.test.ts. -
release:
list-uncovered-commits.mjsnow unions the CHANGELOG scan window withchangelog.d/fragment refs (filename<PR>-prefix + every#Nin the body), so a commit covered only by a fragment is no longer reported as an uncovered reconciliation gap (#6857 via #6878) -
chore(quality): restore no-explicit-any severity to error (silently downgraded by #6786, broke 439 bulk suppressions), fix 2 unsuppressed anys in repro-6912 test, allowlist migration gap 121, freeze-bump stream.ts/ProxyRegistryManager/tokenHealthCheck (combined-merge drift)
-
chore(base): pt-BR/en i18n keys for #6909 relay-repair/free-pool UI + align Cloud Code Gemini defaults test with the #6943 non-thinking-model contract
-
chore(base): re-export relayProbeStats from localDb (db-rules gate, #6909 follow-up) and document OMNI_MAX_CONCURRENT_CONNECTIONS in .env.example/ENVIRONMENT.md (env-doc gate, #6590 follow-up)
-
ci: unit fast-path sharding doubled 2→4 (halves the heaviest job's wall time) (#6781); the 3 heaviest fast-path jobs can route to the self-hosted VPS runner pool behind
USE_VPS_RUNNER(#6691);VPS_ALWAYS_ONkeeps the dedicated 24/7 CI host up across releases (teardown becomes a no-op) (#6693). -
docs: routing-strategy count reconciled to 18 across AUTO-COMBO.md, README and AGENTS.md, and
p2ccasing fixed to matchROUTING_STRATEGY_VALUES(#6643, #6644, #6646 — thanks @chirag127); CLAUDE.md updated with the renamed review/triage/implement skill-family names (#6663). -
chore(release): v3.8.47 pre-flight — relocate #6943 orphan test to a collected path, restore no-explicit-any suppression match, testFrozen bump translator-openai-to-gemini (1541→1553), zizmor rebaseline 159→169
-
docs(readme): fix stale counts — 18 routing strategies (adds the missing
pipelinerow), 94 MCP tools, 12-factor Auto-Combo scoring. -
chore(ci): fix two shared base-reds on the release tip that blocked the PR queue — register
cliproxyapi-model-mapping-dispatch.test.tsinstryker.conf.jsontap.testFiles(mutation-coverage gap left by #6903) and updateprovider-models-route-codex.test.tsto expect Codex client version0.144.0(stale assertion left by #6780's production bump). -
docs: refresh
llm.txtto the current project state (248 providers, 94 MCP tools / 30 scopes, 18 routing strategies, 12-factor Auto-Combo scoring, v3.8.47) and sync its 42 i18n mirrors; move the implemented design-system plan from the repo root todocs/architecture/DESIGN_SYSTEM.mdrewritten as a reference doc. -
chore(security): scrub hardcoded live-instance credentials (API key + auth cookie + host URL) from the
tests/boundary/*.live.test.tsfiles landed via #6786 — they now readOMNIROUTE_TEST_BASE/OMNIROUTE_TEST_BEARER/OMNIROUTE_TEST_COOKIEfrom the environment and stay gated behindRUN_BOUNDARY_LIVE=1.
🙌 Contributors
Thanks to everyone whose work landed in v3.8.47:
详细ChangeLogv3.8.46
2026年07月08日
✨ New Features
- feat(sse): hide paid-only models from
auto/*routing whenhidePaidModelsis on (#6512) — follow-up to #6328/#6495. PR #6495 hid paid-only models from theGET /v1/modelslisting, butauto/*combos (auto/best-coding,auto/glm, …) could still pick a paid-only backend into their candidate pool → a 402/403 at request time.createVirtualAutoCombonow filters the candidate pool through the new pureopen-sse/services/autoCombo/paidModelFilter.ts(filterPaidOnlyCandidates), applying the same free-model predicate #6495 uses incatalog.ts(providerHasFreeModels(provider) && isFreeModel(provider, {id})) wheneversettings.hidePaidModels === true. Applied before the category/tier/family narrowing, so it covers everyauto/*combo; an all-paid pool degrades to the existing graceful empty-pool path. Opt-in — default OFF leaves the pool unchanged (identity). Regression guard:tests/unit/autoCombo/paid-model-filter-6512.test.ts(4, incl. the default-off identity guard). - feat(sse): provider-family auto combos —
auto/glm,auto/minimax,auto/mimo,auto/zai,auto/gemma,auto/llama,auto/gemini(#6453) — new routable ids that materialize an on-demand virtual combo spanning whatever installed backends currently expose that model family, degrading gracefully as backends rotate. A new pureopen-sse/services/autoCombo/modelFamily.ts(detectModelFamily) classifies by model-id prefix for six families;zaiis instead resolved by provider id (z.ai's hosted API serves the sameglm-*model ids as every other GLM backend, soauto/zaimeans "route to my z.ai backend specifically" vsauto/glm's "any connected GLM backend"). Reuses the existingcreateVirtualAutoComboon-demand materialization path (no DB writes) and the/v1/modelscatalog advertising loop. Regression guard:tests/unit/autoCombo/provider-family-combos.test.ts(11). - feat(proxy): native proxy-pool round-robin / egress IP rotation (#6365) — a scope (global / provider / account) can now hold multiple proxies as a pool with a rotation strategy, so outbound requests cycle their egress IP instead of pinning one proxy per scope. Migration
117_proxy_pool_rotation.sqllifts theUNIQUE(scope, scope_id)constraint (rebuild via the canonical rename/copy/drop; existing single assignments become 1-element pools) and adds aproxy_scope_rotationcompanion table holding the per-scope strategy + a persisted monotonic round-robin cursor. Strategies:round-robin(default, monotonic cursor — neverMath.random),random, andsticky-per-N-min. Resolution (resolveProxyForScopeFromRegistry/resolveProxyForConnectionFromRegistry) now fetches the alive, position-ordered candidate set (unchangedPROXY_ALIVE_PREDICATE) and applies the strategy; an empty / all-dead pool still returnsnull— the #6246 fail-closed guard is untouched (never falls through to direct egress). Backend + DB only; dashboard pool-builder UI is a follow-up. Regression guard:tests/unit/proxy-pool-rotation-6365.test.ts(8, incl. fail-closed + backward-compat). - feat(providers): end-to-end tool/function calling on the native Gemini
/v1betaendpoint (#6222) — both directions of the Gemini↔OpenAI conversion now preserve tool data (previously silently dropped). Request side:convertGeminiToInternal(extracted to its own testable module) mapstools[].functionDeclarations→ OpenAItools, priorfunctionCallparts → assistanttool_calls, andfunctionResponseparts →tool-role messages. Response side:convertOpenAIResponseToGeminiemitsparts[].functionCall {name,args}frommessage.tool_calls, and the streamingopenAIChunkToGeminiChunkaccumulates fragmentedtool_callsdeltas by index into completefunctionCallparts. The non-Gemini client paths (Claude, OpenAI-Responses) already preserved tool calls — this closes the gap specific to the native Gemini surface. Regression guard:tests/unit/v1beta-gemini-tool-calling-6222.test.ts(6, incl. a streaming SSE round-trip). - feat(providers): copilot-m365-web enterprise / work tier support (#6334) — mirrors the EDU-tier pattern (#6210):
M365ConnectionParamsgains anagentfield, a new opt-inM365_ENTERPRISE_OVERRIDESpreset (agent=work,scenario=officeweb,licenseType=Premium) applies viaproviderSpecificData.tier="enterprise"(alias"work"), andagentis also overridable directly viaproviderSpecificData.agent.buildWsUrlwas hardcodingagent="web"(the one enterprise-distinguishing param with no override path), so a Premium work account handshook then returned an empty stream. The individual and EDU paths are untouched. Kilo's dup flag vs #6210 (EDU tier) was a false positive — different tier. Regression guard:tests/unit/copilot-m365-enterprise-6334.test.ts(7). End-to-end confirmation on a real Premium work account is a live-VPS validation follow-up (Hard Rule #18). (thanks @Forcerecon) - feat(api): standardized, provider-agnostic
effort+thinkingrequest params (#6241) — a thin standardization layer over the existing mature per-provider reasoning plumbing (no provider mapper touched).providerChatCompletionSchemagains a canonicaleffort(reusing the sharednone/low/medium/high/xhighvocabulary — the UI tiersextra/maxcollapse ontoxhigh) and a booleanthinking. A purenormalizeReasoningRequest(wired once insrc/sse/handlers/chat.ts, before any reasoning field is read) folds them onto the fields the translators already consume (reasoning_effort/reasoning.effort/thinking), so they fan out to Anthropic / Gemini / xAI / Responses — an explicit clientreasoning_effort/ object-shapedthinkingalways wins (backward-compatible)./modelsadditively exposessupportsThinking+effort_tiersso the frontend can render the toggles (UI component is a follow-up). Regression guard:tests/unit/effort-thinking-standardization-6241.test.ts(12). (thanks @Iammilansoni, @shabeer) - feat(combo): new
pipeline(sequential) combo strategy (#6297) — the 18th routing strategy runs targets in order, threading each step's output into the next step's input, with an optional per-stepprompt(system instruction); only the final step's response is returned. Distinct fromfusion(parallel fan-out + judge). Implemented as a self-containedopen-sse/services/pipeline.ts(sibling tofusion.ts), dispatched fromcombo.ts; the step list reusescombo.modelsorder and reads an optionalpromptoff each target (backward-compatible — ignored by every other strategy). Intermediate steps run non-streaming with tools stripped (complete prose to thread forward); the final step keeps the client'sstreamflag + tools. A failing/empty/unparseable intermediate step fails the whole pipeline explicitly via a sanitized error (never silently swallowed). Kilo's dup flag vs #563 was a false positive (that's model→chain selection; this is a sequential chain). Regression guard:tests/unit/combo-pipeline-strategy.test.ts(5). (thanks @ofekbetzalel) - feat(ci):
check:test-maskingnow flags inline-reimplemented prod conditions (#6348) — a new report-only subcheck (v2, 6A.10 family) catches the wrong-shape contract test: a test that recomputes the condition under test inline instead of importing/exercising the real function (the #6216 class, where=== 500→>= 500stayed green because the test re-implemented the branch). For each added/modified test file it warns when the file textually duplicates a ≥3-token conditional from a production file touched in the same PR and does not import the symbol/module owning it, via a pure, fixture-testedfindReimplementedConditions()with an allowlist mirroringassertReductionAllowlist. Report-only for now (does not fail the gate) — to be promoted to blocking after a triage cycle. Regression guard:tests/unit/check-test-masking.test.ts(45). - feat(sse): per-connection routing override (native vs CLIProxyAPI) (#6339) — the previously-dead
isCliproxyapiDeepModeEnabledhelper is now wired intoresolveExecutorWithProxy: a single connection can opt itself into the CLIProxyAPI passthrough executor viaproviderSpecificData.cliproxyapiMode="claude-native", with precedence connection override > providerupstream_proxy_configmode > default.resolveExecutorWithProxynow receives the resolved connection'sproviderSpecificData(threaded fromchatCore.ts), so one connection can deep-route while the provider's other connections stay native — no DB schema change (the toggle rides inproviderSpecificData). Also resolves the same-provider mixing ask in #6340. Regression guard:tests/unit/chatcore-executor-proxy.test.ts(9). (thanks @RaviTharuma) - feat(dashboard): "Add session cookie" modal now shows a prominent "Open ‹host› →" link to the provider's own site (#6268) — every
-webcookie-session provider (chatgpt-web, claude-web, gemini-web, kimi-web, lmarena, qwen-web, m365-copilot-web, …) renders a one-click external link (opening the provider's login/home page in a new tab) so operators no longer tab away to retype the URL mid-setup. The host resolves from a pure, unit-testedresolveWebProviderHost()(prefersWEB_COOKIE_PROVIDERS[id].website, falls back to the registrybaseUrlorigin); non-web providers render exactly as before. Kilo's dup flag vs #6265 (modal-too-small-on-1080p) was a false positive — distinct concern. Regression guard:tests/unit/resolve-web-provider-host.test.ts(5). (thanks @chirag127) - feat(providers): add DigitalOcean AI (serverless inference) as an OpenAI-compatible API-key provider (#6373) — base
https://inference.do-ai.run/v1, wired through the shared OpenAI-compatible registry with full model passthrough (open-sse/config/providers/registry/digitalocean/,src/shared/constants/providers/apikey/inference-hosts.ts). Regression guard:tests/unit/digitalocean-provider.test.ts. (thanks @newnol) - feat(providers): add Huancheng Public API (
hcnsec) as an OpenAI-compatible regional provider (#6410) — Xinjiang Huancheng Cybersecurity's public LLM platform (basehttps://api.hcnsec.cn/v1, free credits via daily check-ins), wired through the shared OpenAI-compatible registry with full model passthrough (open-sse/config/providers/registry/hcnsec/,src/shared/constants/providers/apikey/regional.ts). Regression guard:tests/unit/hcnsec-provider.test.ts. (thanks @UnrealAryan) - feat(dashboard): the web-session credential guide now shows an "Open {host}" link (#6316) to the provider's sign-in site (derived from the provider
websiteviagetProviderWebsiteHost), so you can jump straight to the page where the cookie/session must be captured. Regression guard:tests/unit/web-session-provider-link-6316.test.ts. (thanks @jordansilly77-stack) - feat(cerebras): add the Gemma 4 31B model (
gemma-4-31b) to the Cerebras registry + pricing table (#6331). Regression guard extendstests/unit/t28-model-catalog-updates.test.ts. (thanks @backryun) - feat(providers): add Yuanbao (web) as a cookie-session provider (#6196) —
yuanbao-web(Tencent Yuanbao,yuanbao.tencent.com) with cookie-only auth (hy_user/hy_token+ public agent id), SSE→OpenAI translation incl.reasoning_content, exposing DeepSeek V3/R1 + Hunyuan / Hunyuan-T1. Regression guard:tests/unit/providers-yuanbao-web.test.ts.together-webwas deferred (no verifiable web-session endpoint — needs a captured request) andhuggingchat-webdropped (the existinghuggingchatalready is a web-cookie provider). (thanks @chirag127) - feat(providers): route the built-in agentrouter through the dynamic Claude-Code wire image (#6056) — a small static allow-set (
CC_WIRE_IMAGE_BUILTINSinopen-sse/services/ccWireImageBuiltins.ts), consulted byisClaudeCodeCompatible/isClaudeCodeCompatibleProvider/applyFingerprint, makes agentrouter adopt the CC wire-image headers + fingerprint while guarding the CC baseUrl/auth branches so it keeps its own registrybaseUrlandx-api-keyauth. Regression guard:tests/unit/agentrouter-cc-wire-image.test.ts(asserts the wire image is applied AND agentrouter's baseUrl/auth are preserved). Live WAF-acceptance against agentrouter.org is a VPS validation follow-up (Hard Rule #18). - feat(providers): bulk-add API keys for Cloudflare Workers AI (#6174) —
cloudflare-aiis removed from the bulk-add exclusion list and the bulk parser gains a 3-fieldname|accountId|apiKeymode; the bulk route now builds a per-entryproviderSpecificDataso each key carries its ownaccountId(fixing the previous shared-object reuse), and both the create + key-validation paths receive it. Regression guard:tests/unit/bulk-api-key-parser-cloudflare.test.ts. (thanks @muflifadla38) - feat(dashboard): routing/settings UX clarity (#6147) — (1) weighted combos show the effective routing share % next to each weight when weights don't sum to 100 (
WeightTotalBar.tsx); (2) the status widget's user-facing "Cloud Sync" label is renamed to "Remote Settings Sync" (CloudSyncStatus.tsx; internal ids/state untouched); (3) built-in providers gain an opt-in advanced base-URL override (isBaseUrlOverrideEligibleProvider, hidden behind an "Advanced" toggle, reusing the existingproviderSpecificData.baseUrlpersistence — not globally widened). Regression guard:tests/unit/routing-settings-ux-6147.test.ts. - feat(combo): add an option to disable session stickiness, per-combo or globally — round-robin / random combos can rotate to a different connection on every request instead of pinning a whole conversation to one connection by its first-message hash. Resolution precedence per-combo
config.disableSessionStickiness→ globalsettings.disableSessionStickiness→ defaultfalse(preserves the #3825 prompt-cache/504 fix); gates both stickiness call sites inopen-sse/services/combo.ts. Exposed as a global toggle (Combo Defaults) and a per-combo Inherit/on/off control. (#6168) Regression guard:tests/unit/combo-disable-session-stickiness.test.ts. (thanks @RCrushMe) - feat(docker): add the
OMNIROUTE_NO_SUDOenv flag for root-less / user-namespaced deployments — the MITM cert-trust command path (resolveSudoSpawninsrc/mitm/systemCommands.ts) now strips the leadingsudowhen the flag is truthy, in addition to the existing root / sudo-missing cases, so the Proxy Agent runs withoutsudo(the operator trusts the CA manually, e.g. viaNODE_EXTRA_CA_CERTS). Argv-arrayspawnpreserved — no shell interpolation (Hard Rule #13). (#6122) Regression guard:tests/unit/mitm-systemCommands-no-sudo.test.ts. (thanks @powellnorma) - feat(providers): add Requesty as an OpenAI-compatible gateway provider (BYOK, base
https://router.requesty.ai/v1, ~200 free requests/day) — wired through the shared OpenAI-compatible registry with full model passthrough (open-sse/config/providers/registry/requesty/,src/shared/constants/providers/apikey/gateways.ts). (#6120) Regression guard:tests/unit/requesty-provider.test.ts. (thanks @chirag127) - feat(dashboard): add configured-only / available-only filters to the Free Provider Rankings page (#6150) — hide providers you haven't configured, or whose connections are all rate-limited / out of quota, via server-side query params (
?configuredOnly/?availableOnlyonGET /api/free-provider-rankings) backed by a testable lib helper reusing the in-process connection state (no Redis). Both filters default off, so the default view is unchanged; this supersedes the earlier client-side "Configured Only" toggle (#6245) with an available-only dimension and unit-tested logic. Regression guard:tests/unit/freeProviderRankings-filters.test.ts.
🔧 Bug Fixes
-
fix(dashboard): adding a second API key connection for the same provider no longer silently overwrites the first — the Add-API-key modal now derives a unique default connection name (
main, thenmain-2,main-3, …) so the backend name-based upsert can't collide (#6499 — thanks @dilneiss). -
fix(compression): the session-dedup engine now also deduplicates a large multi-line block repeated within a single message (intra-message dedup), not just across turns; the compression-preview API surfaces a
fallbackReason, and the fusion panel reports how many models were rate-limited vs failed on total-panel failure (#6501 — thanks @chirag127). -
fix(compression): a stacked-pipeline step naming an unregistered engine now surfaces a
validationErrorsentry instead of silently no-op'ing, so misconfigured pipelines are visible in the preview API (#6506 — thanks @chirag127). -
feat(usage): add a Codex reset-credit redemption flow to the Provider Limits UI (#6361) — a
useCodexResetCreditRedemptionhook +/api/usage/codex-reset-creditroute +codexResetCreditslib let you redeem banked Codex reset credits from the quota card. Regression guard:tests/unit/codex-reset-credits.test.ts. (thanks @JxnLexn) -
feat(glm): add team-plan quota settings for
glm-cnconnections (#6351) — a dedicatedGlmTeamQuotaFieldsform section (team quota id / limits) threaded through the Add/Edit connection modals, persisted viaproviderSpecificData, with the GLM usage service reading the team quota. Regression guards:tests/unit/glm-team-quota.test.ts,provider-specific-data-schema.test.ts. (thanks @hao3039032) -
feat(providers): add TinyFish web-fetch/search support (#6349) — a
tinyfish-fetchexecutor +/v1/web/fetchroute + MCP web-fetch tool, registered as a specialty-media provider with request-validation and a search-provider catalog entry. Regression guards:tests/unit/executor-tinyfish-fetch.test.ts,web-fetch-handler.test.ts,mcp-web-fetch-tool.test.ts,provider-validation-tinyfish.test.ts. (thanks @dtybnrj) -
fix(cli):
omniroute launch-codexnow spawnscodex.cmdthrough a shell on Windows (the npm.cmdshim is unresolvable by barespawn→ ENOENT), mirroring the qodercli Windows fix (#6263) (#6312). Regression guard:tests/unit/launch-codex-windows-spawn-6312.test.ts. (thanks @swingtempo) -
fix(codex): isolate the Spark quota from the shared Codex quota and stabilize the quota UI ordering / hydration so per-scope limits render consistently (#6336). Regression guards:
tests/unit/codex-quota-selection-hydration.test.ts,provider-limits-ui.test.ts+ 3 more. (thanks @xz-dev) -
feat(api): add a
hidePaidModelssetting that filters paid-only models out of the/v1/modelscatalog. Regression guard:tests/unit/models-catalog-hide-paid.test.ts. (thanks @chirag127) -
fix(api-manager): the fallback model picker now preserves combos instead of dropping them when a primary model is unavailable (#6443). Regression guard:
tests/unit/api-manager-page-static.test.ts. (thanks @jmengit) -
fix(providers): recoverable Antigravity / Cloud-Code (Gemini Code Assist)
403responses (#6452) are now classified as a retryable project-config error instead of a terminal account ban, so a fixable project/API-disabled 403 no longer forces a ~1-year cooldown / full OAuth reconnect. Regression guard:tests/unit/errorclassifier-antigravity-403.test.ts. (thanks @developerjillur) -
fix(mitm):
sanitizeHeadersnow redactsSet-Cookieresponse headers so upstream session cookies never leak into logs / diagnostics (#6451). Regression guard:tests/unit/mitm-sanitize-headers.test.ts. (thanks @developerjillur) -
fix(api):
/api/compression/previewnow acceptsmode: "caveman"and correctly handles stacked / zero-compression previews (#6425). Regression guard:tests/unit/api/compression-preview-caveman-and-stacked-6425.test.ts. (thanks @chirag127) -
feat(providers): add Zed hosted LLM aggregator as a native-app provider (#6118) — OAuth sign-in via the Zed hosted flow, registered through the shared provider registry + executor. Regression guards:
tests/unit/zed-oauth-provider.test.ts,zed-import-utils.test.ts,zed-docker-detect.test.ts,mitm-handler-zed.test.ts. VPS-validated via live operator login (Hard Rule #18). -
fix(oauth): the Kiro SSO-cache auto-import now preserves the IDC region — cross-region Amazon Q / Kiro profiles imported from the SSO cache are no longer collapsed to the default region (#6113). Regression guard:
tests/unit/kiro-auto-import-idc-2059.test.ts. VPS-validated via live operator login (Hard Rule #18). -
fix(dashboard): passthrough model aliases no longer collide when two namespaced model ids share a last segment (port from 9router#1850, #6431).
enx/gpt-5.5andenx/codebuddy/gpt-5.5both auto-generated the aliasgpt-5.5, so the second model could never be added (the UI just alerted "alias already exists"). Aliases are now disambiguated deterministically — bare last segment when free, then parent-qualified (codebuddy-gpt-5.5), then a numeric suffix — while re-adding the exact same model id is still blocked. Regression guard:tests/unit/passthrough-alias-1850.test.ts. (thanks @arpicato) -
fix(translator): preserve a Gemini
functionResponseco-located with other parts (anotherfunctionCall, or trailingtext) in the same content when translating Gemini → OpenAI (#6376).convertGeminiContent()early-returned the tool message on the firstfunctionResponsepart, dropping any co-located parts; such contents are now pre-split (one tool message perfunctionResponse, emitted first, plus one message for the remaining parts). Regression guard:tests/unit/gemini-to-openai-function-response.test.ts. (thanks @warelik) -
fix(headroom): detect a python interpreter managed by mise / pyenv / asdf / conda (port from 9router#2353, #6382). Headroom's python probe (
src/lib/headroom/detect.ts) searched a hardcodedPATH, but version managers expose their interpreters via shim dirs that only joinPATHthrough interactive-shell activation — which the non-interactive server never runs, so a managed python (≥3.10) was invisible and Headroom reported it missing. The search path now prepends the well-known shim/bin dirs (~/.local/share/mise/shims,~/.pyenv/shims,~/.asdf/shims,$CONDA_PREFIX/bin,~/.local/bin, respectingMISE_DATA_DIR/PYENV_ROOT/ASDF_DATA_DIRwhen set), and a newHEADROOM_PYTHONenv override lets operators point straight at their interpreter (mirroringHEADROOM_URL). Still shell-free (execFileSync). Regression guard:tests/unit/headroom-detect.test.ts(5). (thanks @loopyd) -
fix(executors): strip the OpenAI-Codex/Claude-CLI
client_metadatapassthrough field for NVIDIA requests (port from 9router#1887, #6411). NVIDIA's OpenAI-compatible wrapper rejects it with400 Unsupported parameter, the same class already handled forcerebras/mistral;nvidia(executordefault) was missing from the strip allowlist so Codex/Claude-Code passthrough requests 400'd. Regression guard:tests/unit/executor-default-strip-client-metadata.test.ts(+nvidia case). (thanks @phidinhmanh) -
fix(translator): strip the Claude-style
thinkingfield for NVIDIAz-ai/glm-5.2(port from 9router#2023, #6413). NVIDIA's OpenAI-compatible wrapper 400s onthinking(a Claude-format client routed here leaves athinking:{type:"adaptive"}); the existing strip rule only droppedreasoning. Same class already handled forminimax-m2.7. Regression guard:tests/unit/nvidia-minimax-thinking-strip.test.ts(+glm-5.2 case). (thanks @phidinhmanh) -
fix(translator): suppress the streamed
</think>close marker for the Antigravity IDE client (port from 9router#1061, #6415). On thinking-only turns Antigravity rendered a bare</think>as the sole visible content, tripping its loop-detection and wasting requests. Antigravity's UA (vscode/<v> (Antigravity/<v>)) is added to the marker-suppress allowlist (alongside OpenCode); Claude Code / Cursor still get the marker, andx-omniroute-thinking-marker: onforce-restores it. Regression guard:tests/unit/think-close-marker-suppress-5245.test.ts. (thanks @abdofallah) -
fix(executors): strip nested
reasoning_contentfrom messages for Mistral (port from 9router#1649, #6417). Mistral's API returns422 extra_forbiddenwhen an assistant message carriesreasoning_content(replayed thinking from a prior turn, e.g. via the Codex/responsespath); the generic top-level 400 field-downgrade retry never covered the nested per-message field.DefaultExecutornow strips it for providermistralonly, so DeepSeek (which requires replayedreasoning_content) is unaffected. Regression guard:tests/unit/mistral-strip-reasoning-content-1649.test.ts. (thanks @xxy9468615) -
fix(executors): strip the
client_metadatapassthrough field on the OpenCode path (port from 9router#1442, #6418). OpenCode upstreams (e.g.kimi-k2.6via opencode-go) reject it with400 "Extra inputs are not permitted, field: 'client_metadata'"; the DefaultExecutor strip only covered cerebras/mistral andOpencodeExecutorextendsBaseExecutordirectly, so nothing removed it there. Regression guard:tests/unit/opencode-strip-client-metadata-1442.test.ts. (thanks @yanpaing007) -
fix(executors): inject the
reasoning_contentecho for the native Moonshot Kimi provider (port from 9router#1480, #6419). Kimi (executordefault) is a thinking-mode upstream that 400s with "reasoning_content must be passed back" when a prior assistant turn lacks it; the placeholder injection was only wired into the OpenCode meta-provider, so direct multi-turn Kimi conversations failed. Scoped tokimi(gateway-served models matching the thinking-model name pattern are unaffected). Regression guard:tests/unit/kimi-native-reasoning-injected-1480.test.ts. (thanks @2220258345) -
fix(executors): recover from a strict gateway's
context_management: Extra inputs are not permitted400 (port from 9router#1468, #6420). Claude Code always sends a top-levelcontext_managementfield; strict anthropic-compatible gateways reject it. The dedicated context-editing 400-fallback only fired when OmniRoute's owncontextEditingfeature was enabled (default off), so a client-sent field passed through untouched and 400'd.context_managementis now in the generic reactive field-strip list, so it's stripped-and-retried once regardless of the feature flag (with correct request re-signing for claude-compatible relays). Regression guard:tests/unit/provider-field-strips.test.ts. (thanks @ohahe52-dot) -
fix(network): enable RFC 8305 Happy Eyeballs (
autoSelectFamily) on the direct-egress undici dispatcher (port from 9router#1237, #6423). When DNS returns both IPv6 (AAAA) and IPv4 (A) and the IPv6 route is broken (e.g. a NAT6464:ff9b::prefix without routing), undici tried IPv6 first and hung untilETIMEDOUT(then a 502 + account lockout), even thoughcurlreached the same host. The direct dispatcher now races both families and uses whichever connects first. Proxy paths pin family viaproxyTlsand are unaffected. Regression guard:tests/unit/direct-dispatcher-pipelining-4580.test.ts. (thanks @adentdk) -
fix(combo): round-robin now advances the rotation pointer past the model that actually served, not the eagerly-scheduled one (port from 9router#948, #6428). With
stickyLimit: 1(true round-robin), when the scheduled model failed and a different model served via fallback, the counter had already advanced +1 from the scheduled index — so the next request reused the fallback-served model, degrading round-robin into hot-spotting on whichever model was healthy. The pointer now advances to the served index + 1 (mirroring the sticky-limit>1 path). Session-stickiness (#3825) and distribution are preserved. Regression guard:tests/unit/combo-rr-fallback-advance-948.test.ts. (thanks @binsarjr) -
fix(sse): a non-string
modelfield is now rejected with a400before the resolver, instead of crashing downstream.toLowerCase()/.split()calls into an empty-body500that escapes the error sanitizer (#6407). Regression guard:tests/unit/chat-non-string-model-6407.test.ts. (thanks @chirag127) -
fix(api): unknown
/api/*routes now return a JSON404(instead of the dashboard HTML shell) and scalar chat params (model/temperature/etc.) are validated before the provider lookup so malformed requests fail fast with a clear400(#6424, #6412). Regression guards:tests/unit/api/api-catchall-json-404.test.ts,tests/unit/chat-early-schema-validation-6412.test.ts. (thanks @chirag127) -
fix(api):
/v1/chat/completionsnow rejects a non-JSONContent-Typewith a400before parsing the body (#6414). Regression guard:tests/unit/v1-chat-completions-content-type-6414.test.ts. (thanks @chirag127) -
fix(api): the
X-OmniRoute-Compressionresponse header is now echoed on/v1/chat/completionsand/v1/completions(#6422). Regression guard:tests/unit/compression-header-echo-6422.test.ts. (thanks @chirag127) -
fix(api): concurrent
GET /v1/modelsrequests are coalesced into a single catalog build (#6408). Regression guard:tests/unit/v1-models-concurrent-6408.test.ts. (thanks @chirag127) -
fix(api):
/v1/completionsnow echoes the requestedbody.modelin its JSON + streamed responses (#6429). Regression guard:tests/unit/completions-body-model-echo.test.ts. (thanks @chirag127) -
fix(api): env-var master keys now see the full
/v1/modelscatalog (#6406). Regression guard:tests/unit/models-catalog-envkey-6406.test.ts. (thanks @chirag127) -
fix(api): non-streaming
/v1/completionsresponses now echobody.modelaligned with theX-OmniRoute-Modelheader (#6426). Regression guard:tests/unit/v1-completions-model-header-match-6426.test.ts. (thanks @chirag127) -
fix(api): unknown
/v1/*routes now return a JSON404 not_foundinstead of the Next.js dashboard HTML shell (#6405). Regression guard:tests/unit/api/v1-catchall-json-404.test.ts. (thanks @chirag127) -
fix(api): the per-connection provider models route now degrades to the shipped catalog when a provider's
/modelsendpoint answers with a redirect (#6267) — aqwen-webimport failed with a rawRedirect blocked … (307)503.safeOutboundFetchthrowsREDIRECT_BLOCKEDon the 307,getSafeOutboundFetchErrorStatusmaps it to 503, andbuildDiscoveryErrorFallbackResponsetreated every 503 as a hard error — so the non-emptygetModelsByProviderId("qwen-web")catalog was never surfaced. A models-endpoint redirect is not a fixable-config error (unlikeURL_GUARD_BLOCKED/INVALID_URL, which stay hard errors), so it now falls back to the local/cached catalog before the 503 short-circuit. General fix — covers any config-driven provider that 307s. Regression guard:tests/unit/provider-models-qwen-web-redirect-6267.test.ts. (thanks @chirag127) -
fix(api): the per-connection provider models route (MCP
list_models_catalog+ the dashboard import view) now merges USER-ADDED custom models into its response (#6247) — custom models live in thekey_valuenamespacecustomModels, which the live REST/api/v1/modelsalready merges, butsrc/app/api/providers/[id]/models/route.tsnever readgetCustomModels, so custom models were dropped on both the discovery-success and local_catalog paths. They are now folded into the returned model list (deduped by id, stampedowned_by: provider), fixing MCP + the dashboard import view in one place. Regression guard:tests/unit/provider-models-custom-merge-6247.test.ts. (thanks @RCrushMe) -
fix(providers): GitLab Duo tool-calling follow-up turns no longer fail upstream with
422 {"detail":"Validation error"}(tokens 0/0, rejected pre-inference). The #6234 tool-result-feedback fix serialized the entire multi-turn conversation into GitLab's single-filecode_suggestions(small_file) generation endpoint — folded history that turn-N sent as an oversizedcurrent_file.content_above_cursorand duplicated verbatim intouser_instruction, tripping the AI-Gateway'ssmall_filevalidation guard. The executor now bounds that prompt: it keeps system + latest user message + the most-recent tool round (dropping older turns), caps oversized tool results, and stops duplicating the full prompt intouser_instruction(which now carries only the short latest user message) — while still feeding the most-recent tool result back so the agent continues (open-sse/executors/gitlab.ts, #6220). The unit test covers the bounding logic; the upstream 422→200 clearing is VPS-only (Hard Rule #18). Regression guard:tests/unit/gitlab-tool-exchange-bounded-6220.test.ts. -
fix(i18n): the provider-detail (
/dashboard/providers/[id]) connection-status filter labels no longer render as__MISSING__:All/__MISSING__:Active/__MISSING__:Error/__MISSING__:Banned/__MISSING__:CreditsExhaustedin non-English locales (notably pt-BR) (#6290). Root cause was not the namespace mismatch the issue guessed — theproviders.filter*keys resolve correctly inen.json; the debt lived in the locale mirrors (src/i18n/messages/*.json), where these five keys carried the__MISSING__:sync sentinel in ~15 locales and were absent entirely in ~26 others, so next-intl found the key and echoed the sentinel verbatim. All 40 non-English/-Chinese mirrors now ship real translations for the fiveproviders.filter*labels. Regression guard:tests/unit/i18n-provider-filter-keys-6290.test.ts. (thanks @diegosouzapw) -
fix(providers): the
copilot-m365-webstreaming executor now emitsdebug-level WebSocket diagnostics (#6210) — the outbound WS URL (with theaccess_tokenredacted viaredactWsUrl()), handshake success/failure, and each received SignalR frame'stype/target. Previously the streaming path logged nothing, so an emptycontent:nullresponse (the M365 Education / Starter tier symptom fixed in #6234) was undiagnosable even atAPP_LOG_LEVEL=debug. The change is debug-level and side-effect-free — it does not alter streaming behavior or the frame parser, and the token never reaches the logs. Regression guard:tests/unit/copilot-m365-web-logging-6210.test.ts(thanks @qpeyba) -
fix(resilience): a round-robin combo no longer returns
503 all upstream accounts are unavailablewhen a compatibility-rejected target is actually healthy (#6238).filterTargetsByRequestCompatibilitydrops request-incompatible targets (tool/vision/structured-output unsupported, or below the required context window) before any availability check runs, and itscompatible.length === 0safety net only fired when all targets were filtered — not when the kept targets later all turned out runtime-unavailable (circuit-open / cooldown / no credentials). So a combo could 503 while a compat-rejected-but-healthy provider sat unused.handleRoundRobinCombonow keeps the compat-rejected set and, when every compat-kept target was skipped without a single real attempt, probes those rejected targets as a last-resort fallback tier (via the new pureopen-sse/services/combo/comboCompatFallback.ts) before crystallizing the 503. Regression guard:tests/unit/combo-roundrobin-compat-fallback-6238.test.ts. (thanks @ThongAccount) -
fix(startup): best-effort self-heal for a corrupted Turbopack dev cache on Windows (#6289). On Windows,
pnpm devcan fail at startup when Turbopackmmaps a persistent-cache SST file and the OS refuses the mapping (os error 1455— "paging file too small"), which Turbopack surfaces as a misleadingModule not found: Can't resolve '@/shared/utils/machine'. This is a known upstream Turbopack cache-corruption bug — not our code. The dev launcher (scripts/dev/run-next.mjs) now wrapsnextApp.prepare()and, when it rejects with that signature (isTurbopackCacheCorruptionin the newscripts/dev/turbopackCacheHeal.mjs), purges.build/next/**/cache/turbopackand retries once with a clear log. Caveat — best-effort only: the corruption often surfaces as a runtime overlay rather than aprepare()rejection, so this cannot always intercept it; the reliable remedy remains manually deleting the Turbopack cache dir. Regression guard:tests/unit/turbopack-cache-heal-6289.test.ts. (thanks @chirag127) -
fix(providers): qodercli PAT auth no longer fails with
spawn qodercli ENOENTon Windows (#6263) —spawnQoderClispawned the bareqodercliname withshell:falseand an unenriched env, so the npm.cmdwrapper under%APPDATA%\npm(a user-PATH directory) was never resolved. It now resolves the absolute.cmd/.exepath through the existinggetCliRuntimeStatus("qoder")resolver insrc/shared/services/cliRuntime.ts(memoized), spawns withshellwhen the target is a.cmd/.bat, and uses the cliRuntime-enriched env (PATH + PATHEXT + APPDATA); the ENOENT error now lists the searched paths plus theCLI_QODER_BINoverride. End-to-end spawn on a real Windows host is host-only (Hard Rule #18); the path-resolution logic is unit-tested. Regression guard:tests/unit/qodercli-windows-resolve-6263.test.ts. (thanks @chirag127) -
fix(sse): the reasoning-token buffer no longer inflates probe-sized
max_tokens(#6274) — Claude Code's/modelcapability check sendsmax_tokens: 1, but for a thinking-capable model with a large output cap (e.g.glm-5.2) the #3587 headroom heuristic (max(current + 1000, ceil(current * 1.5))) rewrote it to1001and forwarded that upstream, wasting tokens on a request that was never a genuine reasoning budget.resolveReasoningBufferedMaxTokens()(open-sse/services/reasoningTokenBuffer.ts) now short-circuits and returns the caller's value verbatim when it is below the newREASONING_BUFFER_MIN_TRIGGER(256) threshold — a tiny explicit limit is a probe, not a reasoning request. Real budgets still receive the #3587 headroom unchanged, and the guard runs after the existing capability checks so unknown / non-reasoning models keep returningnull. Regression guard:tests/unit/reasoning-token-buffer-6274.test.ts. (thanks @brightfiscalband) -
fix(cli):
omniroute reset-passwordnow works as a real subcommand, and password resets over piped (non-TTY) stdin actually apply (#6261, #6258). Two coupled defects: (1) #6261 —bin/omniroute.mjsrouted everything through Commander with only two pre-Commander bypasses (--mcp,reset-encrypted-columns), soomniroute reset-passwordwas rejected as an unknown command; only the separateomniroute-reset-passwordbin worked, while the docs falsely advertised the subcommand (incl. a bogus "legacy alias still works"). A pre-Commander bypass mirroringreset-encrypted-columnsnow dynamically importsbin/reset-password.mjs(which self-executes) before Commander parses; the three doc lines were corrected. (2) #6258 —bin/reset-password.mjsissued two sequentialrl.questionprompts; under piped stdin the second read never settled at EOF, somain()never reachedresetManagementPasswordand the reset was a silent no-op (both prompts printed, no success, password unchanged). The CLI now detects non-TTY stdin and reads it once (first line = password, second line = confirm if present, else reused), adds a--password-stdinflag (entire stdin is the password, no confirmation), and exits0explicitly so the success line always flushes; interactive TTY behavior is unchanged. Regression guard:tests/unit/reset-password-cli-6261-6258.test.ts(3). (thanks @chirag127) -
fix(db): the mass-migration safety abort now tells the operator how to bypass it and stops flooding the log (#6260) — after restoring a backup that wiped the migration tracking table,
runMigrations()threw the abort on every downstreamensureDbInitialized(), re-logging the full banner 11+ times, and the message never mentioned the existingOMNIROUTE_MAX_PENDING_MIGRATIONSescape hatch. The abort text now appends a bypass hint (setOMNIROUTE_MAX_PENDING_MIGRATIONS=0inserver.env/DATA_DIR/.env), and a newMigrationSafetyAbortErroris memoized so repeated calls in the same process throw the same instance and emit a single concise line instead of the full cascade. Regression guard:tests/unit/migration-safety-abort-6260.test.ts. (thanks @chirag127) -
fix(auth): importing a distinct Codex/ChatGPT OAuth
auth.jsonis no longer falsely rejected as "already exists" when it belongs to a different user in the same workspace (#6301).findExistingCodexConnection(insrc/lib/oauth/utils/codexAuthImport.ts) deduped only onproviderSpecificData.workspaceId === accountId, whereaccountIdis the sharedchatgpt_account_id/tokens.account_id— so two members of the same ChatGPT Team collapsed onto a single connection (409duplicate_account). The id_token'shttps://api.openai.com/authclaim carries a per-userchatgpt_user_idalongside the workspace id (the device-flow path already persisted it aschatgptUserId, but the import path did not). NowparseAndValidateCodexAuthextractsuserId(chatgpt_user_id→user_id→ JWTsub) intoParsedCodexAuth, the create/update paths persistchatgptUserIdinproviderSpecificData(mirroringcodex.ts), and dedup keys onworkspaceIdANDchatgptUserId— with a backward-compat fallback to legacy accountId-only matching when no stored connection for that workspace records achatgptUserId, so genuinely-same accounts still dedup. Regression guard:tests/unit/codex-auth-import-userid-dedup-6301.test.ts(4). (thanks @anungma) -
fix(providers): importing models for the venice-web provider no longer fails with a red "Provider venice-web does not support models listing" (#6269).
venice-webis a web-cookie provider with an executor but no upstream/v1/modelsendpoint and no registrymodels, so the models route fell through to the tail400. Mirroring thejules/linkup-search/ollama-searchfix (#5569), it now ships a static local catalog entry insrc/lib/providers/staticModels.ts— seeding the current Venice lineup (venice-uncensored,llama-3.3-70b,qwen3-235b,qwen3-4b,deepseek-r1-671b; Venice rotates its catalog, see docs.venice.ai/models/overview) — so the route returns200withsource:"local_catalog",intentional:true. Regression guard:tests/unit/static-models-venice-web-6269.test.ts. (thanks @chirag127) -
fix(api): the specialty model catalogs (
/v1/embeddings,/v1/images,/v1/music,/v1/videosmodel lists) are now derived from the unified catalog filtered by a predicate (getSpecialtyModelsResponse) instead of ad-hoc per-route logic, so they consistently respect active-credential visibility and stay in sync with the main catalog (#6303). Regression guard:tests/unit/specialty-model-catalog-routes.test.ts. (thanks @makcimbx) -
fix(api): the agent-bridge server route now resolves the MITM manager via a dynamic
import("@/mitm/manager.runtime")so Turbopack does not statically pull the stub (or over-bundle the manager), and the agent-skills generator anchors its output base path withpath.join(process.cwd(), …)so Turbopack's static analyzer stops tracing the whole project root (#6329, #6366). Regression guard:tests/unit/agent-bridge-server-route-dynamic-import.test.ts. (thanks @Iammilansoni) -
fix(api): internal probes (combo-test, cloud-sync verify) now pick a management-scoped / allow-all API key instead of naively grabbing
getApiKeys()[0]— a restrictedself:usagefirst row made the probe fail with "Model X is not allowed for this API key" even when the combo path was healthy (pickApiKeyForInternalUseinsrc/lib/db/apiKeys.ts). The API-manager model editor also falls back to/api/models?all=truewhen/v1/modelsis catalog-protected (#6372). Regression guard:tests/unit/pick-internal-api-key-6372.test.ts. (thanks @jmengit) -
fix(live-ws): the Live Dashboard WebSocket server now rejects on bind failure (e.g.
EADDRINUSEwhen the API bridge already holds the port) instead of letting the error surface as an unhandlederrorevent that crash-loops the process — theerrorlistener is attached towss(notserver) and releases the EventBus subscription on a failed start (#6324). Regression guard:tests/unit/live-ws-eaddrinuse-6324.test.ts. (thanks @vinayakkulkarni) -
fix(dashboard): the Home provider-topology widget now trusts the live provider-metrics snapshot — it uses
topology.errorProviderand liveactiveRequestsdirectly instead of re-deriving state from a stalelastErrorAtor applying a frontend timeout filter, so the topology reflects real-time provider health (#6322). Regression guard:tests/unit/home-provider-topology-live-state.test.ts. (thanks @xz-dev) -
fix(sse): strip zero-width markers from streamed tool-call arguments — a follow-up to #5857. That PR removed injected zero-width joiners (U+200D) from streamed assistant text/reasoning but deliberately left tool-call argument JSON byte-exact. The request-side obfuscation (
open-sse/services/claudeCodeObfuscation.ts) injects ZWJ into agent words — including the temp path inside the Bash tool description — and Claude models copy that verbatim into generated commands, which are delivered as tool-call arguments rather than assistant text. As a result the ZWJ survived and corrupted code blocks (e.g. a temp path rendered with an invisible joiner). Nowopen-sse/handlers/responseSanitizer.tsstrips zero-width code points from tool-call argument strings at every emit site (OpenAI non-stream/stream chattool_calls+ legacyfunction_call, native Responsesfunction_callitems, the OpenAI→Responses conversion, and the native Responses streamingresponse.function_call_arguments.delta/.doneevents). Only zero-width code points are removed; JSON structure and all other bytes stay identical (no parse/restringify), so normal arguments remain byte-exact. Regression guard: 6 new cases intests/unit/response-sanitizer.test.ts(suite 50/50). -
fix(nodejs): the default app log path now resolves under
DATA_DIR(~/.omniroute/logs/application/app.log) instead ofprocess.cwd()(#6197) — the globally-installed CLI runs from an arbitrary working directory, so anchoring the default to cwd made file logging silently write to (or no-op under) an unrelated directory, contradicting the documented.env.exampledefault.getAppLogFilePath()now computes the default lazily via the pureresolveDataDir()resolver (honours a per-processDATA_DIR, no directory-creation side effect); an explicitAPP_LOG_FILE_PATHstill wins. Regression guard:tests/unit/logenv-datadir-path-6197.test.ts(3). -
fix(docker): AgentBridge/
startMitmno longer aborts in containers/headless when the Antigravity-default DNS step can't write/etc/hosts(#6127), and the privileged command's stderr now reachesapp.loginstead of only a bare exit code hitting the toast (#6198). The default DNS step (addDNSEntry) was called unguarded while cert install and the two sibling DNS steps were each best-effort — in the runtime Docker image (USER node, nosudo, read-only/etc/hosts) it threwCommand failed with code 1out ofstartMitmInternaland killed the whole start, discarding the stderr. The three DNS steps are extracted into a best-effortprovisionDnsEntries()where each failure is logged with the fullerr(stderr included, folded in bysystemCommands.ts) and never aborts the start. Regression guard:tests/unit/mitm-dns-graceful-degrade-6127.test.ts(4). -
fix(providers): copilot-m365-web now supports the M365 Education "Starter / OfficeWebIncludedCopilot" tier and no longer returns an empty
content:nullstream (#6210). Two gaps: (1)buildWsUrl()hardcoded the individual-consumer scenario (OfficeWebPaidConsumerCopilot,isEdu=false) — the EDU tier is now opt-in viaproviderSpecificData.tier="edu", emittingscenario=OfficeWebIncludedCopilot/isEdu=true(the individual path is unchanged); (2) the EDU/GPT-5.5 path streams deltas viaarguments[0].writeAtCursor(incremental) instead of onlymessages[].text(accumulated snapshots), which the parser dropped — a newaccumulateBotContent()folds both formats, withtype:2 item.result.messageas a last-resort fallback. Regression guard:tests/unit/copilot-m365-edu-writeatcursor-6210.test.ts(10). (thanks @qpeyba) -
fix(providers): GitLab Duo executor now feeds tool results back into the prompt instead of looping (#6220) —
buildPrompt()branched only onsystem/userand tookuserParts.at(-1), silently dropping theassistant{tool_calls}+tool{result}turns the client appended, so the reconstructed prompt was byte-identical to turn 1 and the model re-emitted the same<tool>call forever. When a tool exchange is present the full conversation is now serialized, folding each tool result back keyed by itstool_call_id; simple conversations keep the legacy shape. Complements the tool_call emission from #6051 (thekilo-duplicatelabel was a false positive — different, sequential defect). Regression guard:tests/unit/gitlab-tool-result-feedback-6220.test.ts(4). -
fix(providers): opencode-go/opencode-zen can now synthesize the OpenCode CLI identity headers Cloudflare requires on VPS egress (#5997) — on a datacenter VPS,
opencode.ai/zen/go/v1/chat/completions403s (HTML challenge) requests lacking CLI identity, while the reporter's control curl proved thatUser-Agent: opencode-cli/1.0.0+x-opencode-client: cli+x-opencode-project: default+ fresh request/session UUIDs succeed. Opt-in viaOPENCODE_SYNTHESIZE_CLI_HEADERS=true(values overridable viaOPENCODE_GO_USER_AGENT/OPENCODE_USER_AGENT/OPENCODE_CLIENT/OPENCODE_PROJECT); it fills only headers the client did not already send. Kept off by default — the forward-only path is deliberate (fabricating a wrong value risks upstream rejection; a prior dedup regressed withopencode/local), so this replaces the fragile local header-injection shim without changing default behavior. Regression guard:tests/unit/opencode-cli-headers-synthesis-5997.test.ts(6). (thanks @aleksesipenko) -
fix(resilience): sticky session affinity now evicts and fails over to another account when the pinned account is exhausted/unavailable (#6219)
-
fix(sse): Responses API passthrough now drops internal commentary-phase output before forwarding to clients (gated by RESPONSES_PASSTHROUGH_DROP_COMMENTARY, default on) (#6199)
-
fix(sse): tool-call function schemas with a root
type: nullare now coerced totype: "object"before dispatch (#6359) — clients like the Codex app emitparameters: { type: null, ... }for some tools, which OpenAI-compatible upstreams reject with400 Invalid schema for function '...': schema must be a JSON Schema of 'type: "object"', got 'type: null', failing the whole request.toolSchemaSanitizeralready stripped the null; it now re-adds the mandatory root"object"type (and emptyproperties/openadditionalPropertieswhen absent). Combinator roots (anyOf/oneOf/allOf) and explicit root types are left untouched. Regression guard: 5 new cases intests/unit/tool-schema-sanitizer.test.mjs. -
fix(docker): AgentBridge no longer fails to start on npm/Electron/VPS installs with "MITM manager stub reached at runtime" (#6344) — v3.8.45 flipped the production bundler default to Turbopack, but
next.config.mjsaliased@/mitm/managerto its Docker-only degraded stub unconditionally. That was harmless while Docker (which sets the alias intentionally for #3390 graceful degradation) was the sole Turbopack consumer, but once every artifact built with Turbopack the stub shipped to all non-Docker users andstartMitmthrew on the first Agent-Bridge start. The alias is now opt-in viaOMNIROUTE_MITM_STUB=1(set only by the Dockerfile) through the sharedscripts/build/mitm-stub-flag.mjshelper; default builds bundle the real manager. Regression guard:tests/unit/mitm-stub-alias-6344.test.mjs(4). -
fix(proxy): stop the v3.8.44 proxy regression that leaked the real IP and disabled healthy proxies (#6246). Two coupled defects from the new health scheduler: (1) IP leak — when a proxy assigned to a connection was marked
inactive, resolution fell through to a direct egress instead of blocking, exposing the operator's real IP; (2) over-deactivation — the sweep flipped a proxy toinactiveon the first failed probe and counted our own 5s timeout / a probe-target5xxas the proxy's fault, so healthy paid proxies vanished from egress selection ("my proxies are not being used anymore"). Fix: the sweep decision is extracted into a pure, network-freedecideProxyHealthAction(src/lib/proxyHealth/decision.ts) — by default the health check now only counts/logs and never downgrades status (a proxy is downgraded/removed only withPROXY_AUTO_REMOVE=true, afterPROXY_AUTO_REMOVE_AFTERconsecutive conclusive failures); probes are classified tri-state so an inconclusive result (our timeout, or a5xxfrom the probe target) never penalizes the proxy, and the probe timeout is raised 5s→15s. Separately,safeResolveProxynow fails closed via the existing policy: a connection whose assigned proxy is dead is blocked instead of leaking direct (hasBlockingProxyAssignment), honoring the explicitproxy offtoggles and thePROXY_FAIL_OPEN=trueopt-out. Existing proxies stuckinactiveby the old behavior need a one-time manual re-activate (the operator owns proxy status). Regression guards:tests/unit/proxy-health-decide-action-6246.test.ts,tests/unit/proxy-assigned-unavailable-6246.test.ts. -
fix(proxy): make "Test All" read-only and add bulk enable/disable (#6246). Complements the core fail-closed / scheduler fix (#6296) with the two remaining reporter asks. (1) The "Test All" button (
POST /api/settings/proxies/auto-test) used to flip a proxy toinactiveon a failed reachability probe; since the egress selector excludesinactiveproxies, a flaky probe (an unreachablehttpbin.org, a proxy that blocksHEAD, or a slow paid proxy) silently disabled every proxy that failed — "Test All" is now read-only by default (only the operator sets a proxy active/inactive; opt back into the legacy test-and-set withPROXY_HEALTH_AUTO_DEACTIVATE=true). (2) Adds a bulk enable/disable proxies endpoint + toolbar action (POST /api/settings/proxies/batch-activate) so an operator can re-activate proxies in one click. Regression guard:tests/unit/proxy-health-6246.test.ts. (thanks @tenshiak) -
chatcore (tools): stop the default 128-tool cap from silently dropping opencode's
task/MCP tools. opencode (used as an MCP/agent host) sends a large tool list; when it exceeds the speculativeMAX_TOOLS_LIMIT(128) default,truncateToolListdid a blindtools.slice(0, 128), dropping every tool past index 128 — including opencode's built-intasktool (subagent launch) and many MCP tools, so models routed through OmniRoute could no longer spawn subagents or reach part of their tools. The cap exists to avoid upstream400s for providers with real hard limits (e.g. grok-cli 200), so it is kept for those: detection of the opencode client (isOpencodeClient— anyx-opencode-*header, oropencodein the user-agent) now only bypasses the speculative 128 default, never a known provider ceiling. Precedence is explicit — a proactive/detected provider limit always truncates (even for opencode); otherwise opencode forwards its full tool list; otherwise the unchanged 128 default applies to every other client. RefactorsgetEffectiveToolLimitintogetKnownToolLimit(provider) ?? DEFAULT_LIMIT(byte-identical for existing callers) and fixes a cosmetic debug-log that reported the truncated count instead of the original. Regression guard:tests/unit/tool-limit-detector.test.ts. -
fix(mitm): the macOS MITM-cert install check now matches the system keychain again.
security find-certificate -a -Zprints the SHA-1 as a colon-less hex string, but the installed-check compared it againstgetCertFingerprint()'s colon-separated form, so the substring match never hit — the cert was reported as not-installed and re-prompted for the sudo install on every run. Fingerprints are now normalized (colons stripped, upper-cased) on both sides via the extractedmacCertOutputHasFingerprinthelper. Regression guard:tests/unit/mitm-cert-mac-fingerprint.test.ts. (#6204, closes #6134 — thanks @rianonehub) -
fix(api):
/v1/messages/count_tokensnow countstool_use,tool_resultandthinkingcontent blocks (and array-formsystemprompts) in the local-estimation path, instead of onlytext. Real agentic conversations keep ~95% of their tokens inside tool results; the previous estimate returned near-zero for them, which silently broke Claude Code's auto-compaction (context grew past the window with no compaction until the upstream API rejected the request). The real provider-side count path is unchanged. Regression guard:tests/unit/messages-count-tokens-route.test.ts. (#6221 — thanks @luweiCN) -
fix(antigravity): strip a trailing assistant prefill turn for Vertex Claude models to avoid upstream 400s (#6114). Regression guard:
tests/unit/antigravity-claude-prefill-strip.test.ts. (thanks @anki1kr) -
fix(security): the mutable cloud-agent routes (
/api/cloud/credentials/update,/api/cloud/models/alias) now require management auth instead of being treated as public. They were classified as public API routes, so a request without management credentials could update stored cloud-agent credentials and model aliases. They are removed from the public-route set, classified as management routes in the authz pipeline, and gated byrequireManagementAuth; cloud read/auth routes stay public. Regression guards:tests/unit/cloud-write-auth.test.ts,tests/unit/authz/classify.test.ts,tests/unit/public-api-routes.test.ts. (#6233 — thanks @vittoroliveira-dev) -
refactor(dashboard): extract the onboarding-wizard "Open provider details" link target into a pure, unit-tested
buildProviderDetailsHref(connection)helper. The wizard already routes byconnection.id(the node UUID) rather than the provider category slug (#6144/#6145); this hardens that behavior behind a tested helper that guards a missing id/connection. Regression guard:tests/unit/provider-onboarding-href.test.ts. (#6166 — thanks @KooshaPari) -
fix(security): the doubao synthetic device-id generator now derives its digits via an unbiased crypto-random draw (rejection sampling over
crypto.randomBytes()) instead of a% 10reduction, closing a CodeQLjs/biased-cryptographic-randomfinding. -
fix(agentSkills): the GitHub-skills generator now resolves
outputDirto an absolute path before writing, fixing a regression introduced by #6366 (relative-to-cwd base path) that could write generated skill files to the wrong directory. -
fix(security):
/api/keys/{id}/devicesnow checks the HTTP method before auth/validation, returning a405for non-GET/DELETE verbs instead of a misleading401/500(closes adast-smokeQUERY-method finding). -
fix(quality): clear the last 2 heavy quality-gate reds on the release tip (cycle pre-flight).
-
fix(mitm): the test suite and CI can never mutate the OS trust store —
OMNIROUTE_SKIP_SYSTEM_TRUST=1is set globally for tests/CI soinstallCert/uninstallCert/installTproxyCaskip the privileged OS dispatch (#6310; full detail is under the [3.8.45] section below — this branch received it via the parallel-cycle sync-back). -
fix(api):
POST /api/github-skillsnow Zod-validates its request body; documented the new quality-gate env vars and pinned the merge-integrity GitHub Actions to a commit SHA. -
fix(skills): generate the missing
omni-github-skillsregistry entry and align the agent-skills catalog-count tests (follow-up to #6186). -
fix(quality): clear the cycle's 11 net-new ESLint errors and make
validate-release-greensuppressions-aware.
📝 Maintenance
- i18n(it): add 118 missing Italian (
it) translations (net-additive — no existing keys dropped, valid JSON), improving Italian UI coverage. (#6212 — thanks @serverless83) - chore(providers): remove deprecated MiMo V2 model entries from the catalogs (xiaomi-mimo, opencode-go, zenmux-free, audio TTS) — the upstream V2 line is superseded by MiMo V2.5; drops
mimo-v2-tts,mimo-v2-pro,mimo-v2-omni,mimo-v2-flash,mimo-v2-flash-freeand realigns the provider-catalog tests. (#6248 — thanks @backryun) - chore(release): ~50 commits on this branch are v3.8.45 pre-flight/hardening fixes and CI-perf work that landed here via the parallel-cycle sync-back (
sync-next-cycle.mjs, Hard Rule #21) after thev3.8.45git tag was cut, and are already fully documented under the [3.8.45] section below — listed here only so the per-cycle commit-coverage check (npm run release:uncovered) doesn't flag them as gaps. Provider/catalog/UX/backend fixes: #6041, #6078, #6108, #6135, #6148, #6149, #6154, #6158, #6161, #6162, #6163, #6164, #6165, #6170, #6177, #6178, #6181, #6186, #6187, #6191, #6193, #6194, #6195, #6200, #6205, #6208, #6209, #6211, #6213, #6223, #6224, #6225, #6226, #6227, #6228, #6229, #6230, #6235, #6291, #6292. CI/release-pipeline work: #6167, #6203, #6214, #6215, #6218, #6273, #6275, #6283, #6284, #6285, #6300, #6305. - chore(release): additional zero-ref release-cycle plumbing on this branch, kept out of
release:uncoveredon purpose (no#Nin the commit subject to cite): opening the v3.8.46 cycle, opening/closing the v3.8.45 cycle, the finalized [3.8.45] CHANGELOG i18n sync-back to 42 mirrors, the v3.8.45 cognitive/cyclomatic and file-size drift rebaselines, ESLint stale-suppression pruning (4,273 → 4,233), and clearing test-masking/docs-all pre-flight reds for v3.8.45.
⚡ Performance & Infrastructure
- perf(release-green): the pre-flight validator (
scripts/quality/validate-release-green.mjs) now runs its 4 slow suites (unit / vitest / integration / pack-artifact) concurrently viaPromise.all— pre-flight wall time drops from ~the sum of the suites to ~the slowest one (~30min saved per round; Phase 0 was the nº1 bottleneck of the v3.8.45 release benchmark, 2h54 of 6h34 e2e). Guard:tests/unit/validate-release-green.test.ts("runs the slow suites CONCURRENTLY"). (#6319) - fix(ci):
scripts/release/sync-next-cycle.mjs— two defects found live in its first production run (v3.8.45 Phase 5): (1) thegit()helper's default 1 MiBmaxBuffercrashed withENOBUFSongit show origin/main:CHANGELOG.md(the CHANGELOG alone is >1 MiB) — widened to 64 MiB; (2) the i18n resync only propagated the[NEXT](TBD) section, leaving the just-shipped finalized section as "— TBD" in all 42 mirrors — it now also syncs[prevVersion]bounded by the heading below it (new exported pure helperversionAfter). Guards: +5 tests intests/unit/sync-next-cycle.test.ts(8/8). (#6327) - test(ci): concurrency-sensitive flaky tests are quarantined into a serial pass (
tests/unit/serial/,--test-concurrency=1, appended to every unit runner incl. sharded variants — the serial pass is sharded too so concurrent shard jobs never self-collide). Initial set:glm-coding-plan-monthly-3580,quota-division-blocks,provider-health-autopilot,combo-health-autopilot— the class behind the ~28min CI wedges/re-runs (two live 1h+ wedges cancelled during this PR's own validation). Discovery + TIA gates track the new glob; systemic root cause (async logger writing after teardown) tracked in #6360. Guard:tests/unit/test-serial-quarantine.test.ts(4). (#6347)
🙌 Contributors
Thanks to everyone whose work landed in v3.8.46:
| Contributor | PRs / Issues |
|---|---|
| @2220258345 | direct commit / report |
| @abdofallah | direct commit / report |
| @adentdk | direct commit / report |
| @aleksesipenko | direct commit / report |
| @anki1kr | direct commit / report |
| @anungma | direct commit / report |
| @arpicato | direct commit / report |
| @backryun | #6248 |
| @binsarjr | direct commit / report |
| @brightfiscalband | direct commit / report |
| @chirag127 | #6501, #6506 |
| @developerjillur | direct commit / report |
| @dilneiss | #6499 |
| @dtybnrj | direct commit / report |
| @Forcerecon | direct commit / report |
| @hao3039032 | direct commit / report |
| @Iammilansoni | #6150, #6245 |
| @jmengit | direct commit / report |
| @jordansilly77-stack | direct commit / report |
| @JxnLexn | direct commit / report |
| @KooshaPari | #6166 |
| @loopyd | direct commit / report |
| @luweiCN | #6221 |
| @makcimbx | direct commit / report |
| @muflifadla38 | direct commit / report |
| @newnol | direct commit / report |
| @ofekbetzalel | direct commit / report |
| @ohahe52-dot | direct commit / report |
| @phidinhmanh | direct commit / report |
| @powellnorma | direct commit / report |
| @qpeyba | direct commit / report |
| @RaviTharuma | direct commit / report |
| @RCrushMe | direct commit / report |
| @rianonehub | #6134, #6204 |
| @serverless83 | #6212 |
| @swingtempo | direct commit / report |
| @tenshiak | direct commit / report |
| @ThongAccount | direct commit / report |
| @UnrealAryan | direct commit / report |
| @vinayakkulkarni | direct commit / report |
| @vittoroliveira-dev | #6233 |
| @warelik | direct commit / report |
| @xxy9468615 | direct commit / report |
| @xz-dev | direct commit / report |
| @yanpaing007 | direct commit / report |
| @diegosouzapw | maintainer |
What's Changed
- Release v3.8.46 by @diegosouzapw in #6314
Full Changelog: v3.8.45...v3.8.46
详细ChangeLogv3.8.45
2026年07月06日
✨ New Features
- feat(providers): add Yuanbao (web) as a cookie-session provider (#6196) —
yuanbao-web(Tencent Yuanbao,yuanbao.tencent.com) with cookie-only auth (hy_user/hy_token+ public agent id), SSE→OpenAI translation incl.reasoning_content, exposing DeepSeek V3/R1 + Hunyuan / Hunyuan-T1. Regression guard:tests/unit/providers-yuanbao-web.test.ts.together-webwas deferred (no verifiable web-session endpoint — needs a captured request) andhuggingchat-webdropped (the existinghuggingchatalready is a web-cookie provider). (thanks @chirag127) - feat(providers): route the built-in agentrouter through the dynamic Claude-Code wire image (#6056) — a small static allow-set (
CC_WIRE_IMAGE_BUILTINSinopen-sse/services/ccWireImageBuiltins.ts), consulted byisClaudeCodeCompatible/isClaudeCodeCompatibleProvider/applyFingerprint, makes agentrouter adopt the CC wire-image headers + fingerprint while guarding the CC baseUrl/auth branches so it keeps its own registrybaseUrlandx-api-keyauth. Regression guard:tests/unit/agentrouter-cc-wire-image.test.ts(asserts the wire image is applied AND agentrouter's baseUrl/auth are preserved). Live WAF-acceptance against agentrouter.org is a VPS validation follow-up (Hard Rule #18). - feat(providers): bulk-add API keys for Cloudflare Workers AI (#6174) —
cloudflare-aiis removed from the bulk-add exclusion list and the bulk parser gains a 3-fieldname|accountId|apiKeymode; the bulk route now builds a per-entryproviderSpecificDataso each key carries its ownaccountId(fixing the previous shared-object reuse), and both the create + key-validation paths receive it. Regression guard:tests/unit/bulk-api-key-parser-cloudflare.test.ts. (thanks @muflifadla38) - feat(dashboard): routing/settings UX clarity (#6147) — (1) weighted combos show the effective routing share % next to each weight when weights don't sum to 100 (
WeightTotalBar.tsx); (2) the status widget's user-facing "Cloud Sync" label is renamed to "Remote Settings Sync" (CloudSyncStatus.tsx; internal ids/state untouched); (3) built-in providers gain an opt-in advanced base-URL override (isBaseUrlOverrideEligibleProvider, hidden behind an "Advanced" toggle, reusing the existingproviderSpecificData.baseUrlpersistence — not globally widened). Regression guard:tests/unit/routing-settings-ux-6147.test.ts. - feat(combo): add an option to disable session stickiness, per-combo or globally — round-robin / random combos can rotate to a different connection on every request instead of pinning a whole conversation to one connection by its first-message hash. Resolution precedence per-combo
config.disableSessionStickiness→ globalsettings.disableSessionStickiness→ defaultfalse(preserves the #3825 prompt-cache/504 fix); gates both stickiness call sites inopen-sse/services/combo.ts. Exposed as a global toggle (Combo Defaults) and a per-combo Inherit/on/off control. (#6168) Regression guard:tests/unit/combo-disable-session-stickiness.test.ts. (thanks @RCrushMe) - feat(docker): add the
OMNIROUTE_NO_SUDOenv flag for root-less / user-namespaced deployments — the MITM cert-trust command path (resolveSudoSpawninsrc/mitm/systemCommands.ts) now strips the leadingsudowhen the flag is truthy, in addition to the existing root / sudo-missing cases, so the Proxy Agent runs withoutsudo(the operator trusts the CA manually, e.g. viaNODE_EXTRA_CA_CERTS). Argv-arrayspawnpreserved — no shell interpolation (Hard Rule #13). (#6122) Regression guard:tests/unit/mitm-systemCommands-no-sudo.test.ts. (thanks @powellnorma) - feat(providers): add Requesty as an OpenAI-compatible gateway provider (BYOK, base
https://router.requesty.ai/v1, ~200 free requests/day) — wired through the shared OpenAI-compatible registry with full model passthrough (open-sse/config/providers/registry/requesty/,src/shared/constants/providers/apikey/gateways.ts). (#6120) Regression guard:tests/unit/requesty-provider.test.ts. (thanks @chirag127) - feat(dashboard): add configured-only / available-only filters to the Free Provider Rankings page (#6150) — hide providers you haven't configured, or whose connections are all rate-limited / out of quota, via server-side query params (
?configuredOnly/?availableOnlyonGET /api/free-provider-rankings) backed by a testable lib helper reusing the in-process connection state (no Redis). Both filters default off, so the default view is unchanged; this supersedes the earlier client-side "Configured Only" toggle (#6245) with an available-only dimension and unit-tested logic. Regression guard:tests/unit/freeProviderRankings-filters.test.ts. - feat(rankings): add a 'Configured Only' filter to the Free Provider Rankings page, so the table can be narrowed to just the providers you have configured connections for (with an empty-state hint when none are configured). New
en.jsonkeys and a pure filter helper covered bytests/unit/free-provider-rankings-configured-filter.test.ts. (#6245, closes #6150 — thanks @Iammilansoni)
🔧 Bug Fixes
-
fix(mitm): the test suite and CI can never mutate the OS trust store again —
OMNIROUTE_SKIP_SYSTEM_TRUST=1(set by the global test setup and all CI workflows) makesinstallCert/uninstallCert/installTproxyCaskip the privileged OS dispatch while preserving the #4546 environment-skip contract. Root cause of the self-hosted runner incident: a cert-flow integration test installed a 105-byte fake PEM into/usr/local/share/ca-certificates, breaking ALL system TLS on the VM. Regression guard:tests/unit/system-trust-test-guard.test.ts. (#6310) -
fix(security):
/api/keys/{id}/devicesanswers a clean method-first 405 for undocumented HTTP methods (e.g. the newQUERY) via a dedicatedhttp-method-guardrule — the auth layer was answering 401 first, failing schemathesis's unsupported-methods check. Same pattern as the v3.8.44 TRACE fix. Regression guard:tests/unit/dast-method-not-allowed.test.ts. -
fix(combo): the #6216 empty-stream failover is restricted to truly empty bodies (zero bytes — the Gemini HTTP-200-empty case), restoring the #3399/#3685 pass-through contracts for
[DONE]-terminated empty streams and incomplete Claude lifecycles. New guard:#5976 truly EMPTY streaming body → invalid for combo failover(87/87 across both suites). -
fix(combo): 5 streaming-path fixes — locked-stream 500, error-frame-only-if-no-content, Gemini
MALFORMED_RESPONSE→content_filter failover, correlationId substring search, per-model-500 lockout skip + request-logger UI detail. Maintainer follow-up:releaseQualityClonecancels the abandoned quality-check tee branch (per-request memory) + regression test. (#6216 — thanks @hartmark) -
fix(skills): generate the missing
omni-github-skillsregistry entry (the #6186 catalog addition never ran the generator — 8 integration assertions split between old/new counts) and align the agent-skills catalog counts across integration + unit suites (43 = 23 API + 20 CLI; 44 with config). -
fix(a2a): finish the #6186 catalog-count update —
listCapabilitiesmetadata reportedcoverage.api.total: 22(type literal + value) andSkillCoverageSchemapinnedz.literal(22), so the schema would REJECT the correct runtime value with 23 API skills. All three aligned to 23. -
fix(github-skills): add a missing import, unit tests and a settings JSON-parse fix for the GitHub agent-skill discovery/import flow. (#6186 — thanks @Moseyuh333)
-
fix(api):
POST /api/github-skillsvalidates its body with a Zod schema (validateBody) instead of blindrequest.json()destructuring — a non-arraytargetswould crash.map. Regression guard:tests/unit/github-skills-route-validation.test.ts. -
fix(docker): add
id=to the BuildKit cache mounts so strict builders (e.g. buildkitd with strict frontend parsing) accept the Dockerfile. (#6291 — thanks @karimalsalah) -
fix(oauth): register
zedin the OAuthPROVIDERSmap (fixes "Unknown provider" on the Zed sign-in flow) (#6078 — thanks @anki1kr), and alignzedinOAUTH_PROVIDER_IDS+ the config enum after the merge. -
fix(doubao-web): switch the Doubao web provider to the Dola global endpoint. (#6235 — thanks @backryun)
-
fix(doctor): resolve two false-positive WARNs in the doctor diagnostics (#6163, closes #6162 — thanks @arssnndr)
-
fix(providers): refresh the GitHub Copilot model catalog to the current upstream set. (#6154 — thanks @backryun)
-
fix(providers): correct the Kiro model catalog to real upstream ids — fabricated
claude-opus-4.7/claude-sonnet-4.6entries removed, realclaude-sonnet-5/claude-sonnet-4.5/claude-haiku-4.5kept. (#6170) -
feat(sse): surface Kiro adaptive-thinking reasoning frames as
reasoning_contentin the OpenAI-shaped stream. (#6213 — thanks @VXNCXNX) -
fix(cli): use
OMNIROUTE_SERVER_HOSTinstead of the POSIX auto-setHOSTNAMEfor the bind address (fixes wrong bind on POSIX shells that export HOSTNAME). (#6195, closes #6194 — thanks @Theadd) -
feat(provider): add Claude 5 Sonnet to the Claude Web provider catalog. (#6209, closes #6200 — thanks @Iammilansoni)
-
fix(providers): add
nvidiatoPROVIDER_TOOL_LIMITS(1536) to prevent silent tool-list truncation. (#6177 — thanks @LuisAlejandroVega) -
fix(translator): strip the
reasoningparam for nvidiaz-ai/glm-5.2(upstream 400s on it). (#6181 — thanks @kanztu) -
fix(dashboard): providers page gains a data-timeout guard and the live-WS standalone wiring (no more indefinite spinner when the data fetch stalls). (#6211)
-
fix(sse): surface the ChatGPT-web image silent-drop as an accurate error instead of an empty success. (#6208)
-
fix(cline): force upstream streaming for Cline/ClinePass (streaming-only API) — non-stream client requests are served from the buffered SSE. (#6165)
-
fix(dashboard): remove the always-on Auto-Routing (combo) banner from the home page — it did not reflect live routing state and reappeared on every fresh browser. Replacement guard:
tests/unit/home-no-autorouting-banner.test.ts. (#6164) -
fix(dashboard): stop a model-test error from freezing the page (React #31 object-as-child toast) — errors go through
extractApiErrorMessage. (#6161) -
fix(oauth): extract the keychain-import-only guard to its own module, restoring the oauth file-size freeze. (#6158)
-
fix(sse): strip zero-width markers from streamed tool-call arguments — a follow-up to #5857. That PR removed injected zero-width joiners (U+200D) from streamed assistant text/reasoning but deliberately left tool-call argument JSON byte-exact. The request-side obfuscation (
open-sse/services/claudeCodeObfuscation.ts) injects ZWJ into agent words — including the temp path inside the Bash tool description — and Claude models copy that verbatim into generated commands, which are delivered as tool-call arguments rather than assistant text. As a result the ZWJ survived and corrupted code blocks (e.g. a temp path rendered with an invisible joiner). Nowopen-sse/handlers/responseSanitizer.tsstrips zero-width code points from tool-call argument strings at every emit site (OpenAI non-stream/stream chattool_calls+ legacyfunction_call, native Responsesfunction_callitems, the OpenAI→Responses conversion, and the native Responses streamingresponse.function_call_arguments.delta/.doneevents). Only zero-width code points are removed; JSON structure and all other bytes stay identical (no parse/restringify), so normal arguments remain byte-exact. Regression guard: 6 new cases intests/unit/response-sanitizer.test.ts(suite 50/50). -
fix(nodejs): the default app log path now resolves under
DATA_DIR(~/.omniroute/logs/application/app.log) instead ofprocess.cwd()(#6197) — the globally-installed CLI runs from an arbitrary working directory, so anchoring the default to cwd made file logging silently write to (or no-op under) an unrelated directory, contradicting the documented.env.exampledefault.getAppLogFilePath()now computes the default lazily via the pureresolveDataDir()resolver (honours a per-processDATA_DIR, no directory-creation side effect); an explicitAPP_LOG_FILE_PATHstill wins. Regression guard:tests/unit/logenv-datadir-path-6197.test.ts(3). (root cause independently diagnosed by @subhansh-dev in #6298 — thanks!) -
fix(docker): AgentBridge/
startMitmno longer aborts in containers/headless when the Antigravity-default DNS step can't write/etc/hosts(#6127), and the privileged command's stderr now reachesapp.loginstead of only a bare exit code hitting the toast (#6198). The default DNS step (addDNSEntry) was called unguarded while cert install and the two sibling DNS steps were each best-effort — in the runtime Docker image (USER node, nosudo, read-only/etc/hosts) it threwCommand failed with code 1out ofstartMitmInternaland killed the whole start, discarding the stderr. The three DNS steps are extracted into a best-effortprovisionDnsEntries()where each failure is logged with the fullerr(stderr included, folded in bysystemCommands.ts) and never aborts the start. Regression guard:tests/unit/mitm-dns-graceful-degrade-6127.test.ts(4). -
fix(providers): copilot-m365-web now supports the M365 Education "Starter / OfficeWebIncludedCopilot" tier and no longer returns an empty
content:nullstream (#6210). Two gaps: (1)buildWsUrl()hardcoded the individual-consumer scenario (OfficeWebPaidConsumerCopilot,isEdu=false) — the EDU tier is now opt-in viaproviderSpecificData.tier="edu", emittingscenario=OfficeWebIncludedCopilot/isEdu=true(the individual path is unchanged); (2) the EDU/GPT-5.5 path streams deltas viaarguments[0].writeAtCursor(incremental) instead of onlymessages[].text(accumulated snapshots), which the parser dropped — a newaccumulateBotContent()folds both formats, withtype:2 item.result.messageas a last-resort fallback. Regression guard:tests/unit/copilot-m365-edu-writeatcursor-6210.test.ts(10). (thanks @qpeyba) -
fix(providers): GitLab Duo executor now feeds tool results back into the prompt instead of looping (#6220) —
buildPrompt()branched only onsystem/userand tookuserParts.at(-1), silently dropping theassistant{tool_calls}+tool{result}turns the client appended, so the reconstructed prompt was byte-identical to turn 1 and the model re-emitted the same<tool>call forever. When a tool exchange is present the full conversation is now serialized, folding each tool result back keyed by itstool_call_id; simple conversations keep the legacy shape. Complements the tool_call emission from #6051 (thekilo-duplicatelabel was a false positive — different, sequential defect). Regression guard:tests/unit/gitlab-tool-result-feedback-6220.test.ts(4). -
fix(providers): opencode-go/opencode-zen can now synthesize the OpenCode CLI identity headers Cloudflare requires on VPS egress (#5997) — on a datacenter VPS,
opencode.ai/zen/go/v1/chat/completions403s (HTML challenge) requests lacking CLI identity, while the reporter's control curl proved thatUser-Agent: opencode-cli/1.0.0+x-opencode-client: cli+x-opencode-project: default+ fresh request/session UUIDs succeed. Opt-in viaOPENCODE_SYNTHESIZE_CLI_HEADERS=true(values overridable viaOPENCODE_GO_USER_AGENT/OPENCODE_USER_AGENT/OPENCODE_CLIENT/OPENCODE_PROJECT); it fills only headers the client did not already send. Kept off by default — the forward-only path is deliberate (fabricating a wrong value risks upstream rejection; a prior dedup regressed withopencode/local), so this replaces the fragile local header-injection shim without changing default behavior. Regression guard:tests/unit/opencode-cli-headers-synthesis-5997.test.ts(6). (thanks @aleksesipenko) -
fix(resilience): sticky session affinity now evicts and fails over to another account when the pinned account is exhausted/unavailable (#6219)
-
fix(sse): Responses API passthrough now drops internal commentary-phase output before forwarding to clients (gated by RESPONSES_PASSTHROUGH_DROP_COMMENTARY, default on) (#6199)
-
fix(proxy): stop the v3.8.44 proxy regression that leaked the real IP and disabled healthy proxies (#6246). Two coupled defects from the new health scheduler: (1) IP leak — when a proxy assigned to a connection was marked
inactive, resolution fell through to a direct egress instead of blocking, exposing the operator's real IP; (2) over-deactivation — the sweep flipped a proxy toinactiveon the first failed probe and counted our own 5s timeout / a probe-target5xxas the proxy's fault, so healthy paid proxies vanished from egress selection ("my proxies are not being used anymore"). Fix: the sweep decision is extracted into a pure, network-freedecideProxyHealthAction(src/lib/proxyHealth/decision.ts) — by default the health check now only counts/logs and never downgrades status (a proxy is downgraded/removed only withPROXY_AUTO_REMOVE=true, afterPROXY_AUTO_REMOVE_AFTERconsecutive conclusive failures); probes are classified tri-state so an inconclusive result (our timeout, or a5xxfrom the probe target) never penalizes the proxy, and the probe timeout is raised 5s→15s. Separately,safeResolveProxynow fails closed via the existing policy: a connection whose assigned proxy is dead is blocked instead of leaking direct (hasBlockingProxyAssignment), honoring the explicitproxy offtoggles and thePROXY_FAIL_OPEN=trueopt-out. Existing proxies stuckinactiveby the old behavior need a one-time manual re-activate (the operator owns proxy status). Regression guards:tests/unit/proxy-health-decide-action-6246.test.ts,tests/unit/proxy-assigned-unavailable-6246.test.ts. -
fix(proxy): make "Test All" read-only and add bulk enable/disable (#6246). Complements the core fail-closed / scheduler fix (#6296) with the two remaining reporter asks. (1) The "Test All" button (
POST /api/settings/proxies/auto-test) used to flip a proxy toinactiveon a failed reachability probe; since the egress selector excludesinactiveproxies, a flaky probe (an unreachablehttpbin.org, a proxy that blocksHEAD, or a slow paid proxy) silently disabled every proxy that failed — "Test All" is now read-only by default (only the operator sets a proxy active/inactive; opt back into the legacy test-and-set withPROXY_HEALTH_AUTO_DEACTIVATE=true). (2) Adds a bulk enable/disable proxies endpoint + toolbar action (POST /api/settings/proxies/batch-activate) so an operator can re-activate proxies in one click. Regression guard:tests/unit/proxy-health-6246.test.ts. (thanks @tenshiak) -
chatcore (tools): stop the default 128-tool cap from silently dropping opencode's
task/MCP tools. opencode (used as an MCP/agent host) sends a large tool list; when it exceeds the speculativeMAX_TOOLS_LIMIT(128) default,truncateToolListdid a blindtools.slice(0, 128), dropping every tool past index 128 — including opencode's built-intasktool (subagent launch) and many MCP tools, so models routed through OmniRoute could no longer spawn subagents or reach part of their tools. The cap exists to avoid upstream400s for providers with real hard limits (e.g. grok-cli 200), so it is kept for those: detection of the opencode client (isOpencodeClient— anyx-opencode-*header, oropencodein the user-agent) now only bypasses the speculative 128 default, never a known provider ceiling. Precedence is explicit — a proactive/detected provider limit always truncates (even for opencode); otherwise opencode forwards its full tool list; otherwise the unchanged 128 default applies to every other client. RefactorsgetEffectiveToolLimitintogetKnownToolLimit(provider) ?? DEFAULT_LIMIT(byte-identical for existing callers) and fixes a cosmetic debug-log that reported the truncated count instead of the original. Regression guard:tests/unit/tool-limit-detector.test.ts. (#6193 — thanks @DKotsyuba) -
fix(mitm): the macOS MITM-cert install check now matches the system keychain again.
security find-certificate -a -Zprints the SHA-1 as a colon-less hex string, but the installed-check compared it againstgetCertFingerprint()'s colon-separated form, so the substring match never hit — the cert was reported as not-installed and re-prompted for the sudo install on every run. Fingerprints are now normalized (colons stripped, upper-cased) on both sides via the extractedmacCertOutputHasFingerprinthelper. Regression guard:tests/unit/mitm-cert-mac-fingerprint.test.ts. (#6204, closes #6134 — thanks @rianonehub) -
fix(api):
/v1/messages/count_tokensnow countstool_use,tool_resultandthinkingcontent blocks (and array-formsystemprompts) in the local-estimation path, instead of onlytext. Real agentic conversations keep ~95% of their tokens inside tool results; the previous estimate returned near-zero for them, which silently broke Claude Code's auto-compaction (context grew past the window with no compaction until the upstream API rejected the request). The real provider-side count path is unchanged. Regression guard:tests/unit/messages-count-tokens-route.test.ts. (#6221 — thanks @luweiCN) -
fix(antigravity): strip a trailing assistant prefill turn for Vertex Claude models to avoid upstream 400s (#6114). Regression guard:
tests/unit/antigravity-claude-prefill-strip.test.ts. (thanks @anki1kr) -
fix(security): the mutable cloud-agent routes (
/api/cloud/credentials/update,/api/cloud/models/alias) now require management auth instead of being treated as public. They were classified as public API routes, so a request without management credentials could update stored cloud-agent credentials and model aliases. They are removed from the public-route set, classified as management routes in the authz pipeline, and gated byrequireManagementAuth; cloud read/auth routes stay public. Regression guards:tests/unit/cloud-write-auth.test.ts,tests/unit/authz/classify.test.ts,tests/unit/public-api-routes.test.ts. (#6233 — thanks @vittoroliveira-dev) -
refactor(dashboard): extract the onboarding-wizard "Open provider details" link target into a pure, unit-tested
buildProviderDetailsHref(connection)helper. The wizard already routes byconnection.id(the node UUID) rather than the provider category slug (#6144/#6145); this hardens that behavior behind a tested helper that guards a missing id/connection. Regression guard:tests/unit/provider-onboarding-href.test.ts. (#6166 — thanks @KooshaPari) -
fix(api): relay worker now binds the SSRF guard to a stable
constname so minified standalone (Docker) builds resolve it (#6149) — the Vercel/Deno relay generators embedded the sharedresolveRelayTargetguard as a bare${fn.toString()}declaration while the worker body called the hardcoded literal name; SWC minification mangled the source function's name, so the deployed worker defined<mangled>but still calledresolveRelayTarget→ReferenceError. Both templates now emitconst resolveRelayTarget = ${fn.toString()};(the const name is a template literal, immune to minification). Regression guard:tests/unit/relay-minified-fn-6149.test.ts(4). (thanks @SeaXen) -
fix(providers): refresh the stale NVIDIA NIM model registry — drop EOL
z-ai/glm-5.1, addz-ai/glm-5.2andnvidia/nemotron-3-ultra-550b-a55b(#6108). Regression guard:tests/unit/nvidia-nim-registry-6108.test.ts. (thanks @andrea-kingautomation) -
fix(backend): GPT-family (codex) models now report a distinct
max_input_tokens(272000) below their 400Kcontext_lengthvia an optionalmaxInputTokensonRegistryModel, so coding agents auto-compact correctly instead of overflowing the real input cap (#6191). Regression guard:tests/unit/gpt-max-input-tokens-6191.test.ts. (thanks @luweiCN) -
fix(backend): call logs now record a reasoning source/char-count (migration 116,
reasoning_source/reasoning_chars) for models that emitreasoning_content/<think>but report zero reasoning tokens in usage, sotokens_reasoningno longer silently under-represents reasoning — cost math is unchanged (the pricedtokens_reasoningstays usage-derived) (#6187). Regression guard:tests/unit/reasoning-token-source-6187.test.ts. (thanks @andrea-kingautomation) -
fix(auth): a stale/changed
STORAGE_ENCRYPTION_KEYnow surfaces as a clear 424storage_encryption_stale("re-enter the API key") instead of a misleading "Auth failed: 401" — the connection's ciphertext failed to decrypt and was coerced to an empty Bearer, hiding the real cause (#6148). Regression guard:tests/unit/decrypt-stale-key-hint-6148.test.ts. (thanks @chirag127) -
fix(backend): memory injection now keeps the injected system message first for providers that require it (via a
PROVIDERS_SYSTEM_MUST_BE_FIRSTcapability), instead of the cache-safe mid-array splice that made strict providers reject the request with a 400 (#6135). Regression guard:tests/unit/memory-system-first-6135.test.ts. -
fix(services): 9Router embed panel no longer 404s (optional catch-all route) and the supervisor probes the port before spawning to avoid raw EADDRINUSE (#6205). Regression guards:
tests/unit/ninerouter-embed-port-6205.test.ts,tests/unit/services/ServiceSupervisor.test.ts. (thanks @jonlwheat2-gif) -
fix(mcp): forward the MCP request
extracontext through static tool loops so stdio callers keep their scope/identity (#6178)
⚡ Performance & Infrastructure
- perf(test): test-suite loader quick wins (#6214) — the 19 test scripts switch
--import tsx→--import tsx/esm(the repo is pure ESM; the unused CJS hook cost ~1.3s per test process × 2,462 processes — CI fast-path unit shards dropped 14.8→7.5 min, −49%), tsx bumped to ^4.23.0 (tsx#809 startup-regression fix), 37 orphan.test.mjsfiles (224 cases) recovered into the canonical glob (they matched no runner and never ran in any CI job;check:test-discoverynow scans.mjstoo), and ci.yml/quality.yml unit jobs now call the canonical npm scripttest:unit:ci:shard(single source of truth — closes two silent drifts: missingsetupPolyfillimport in CI andmemory/+usage/dirs absent from the fast-path glob).tests/unit/dashboard/**keeps the full tsx hook in its own invocation (@lobehub/iconses/ build internallyrequire()s ESM-syntax files). - ci: heavy-pipeline dedup (#6215) — the release-PR pipeline ran the unit suite 4× per sync (95 jobs, 208 machine-min; the v3.8.44 cycle fired 123 such runs, 88 cancelled). Now: Node 24/26 compat matrices move to a daily
nightly-compat.yml(−28%/run; resolves the active release branch, opens a tracking issue on failure), coverage is collected inside the unit shards themselves via c8/NODE_V8_COVERAGE(−18%/run; the Coverage Shard ×8 matrix is gone — nodejs/node's own CI pattern), the ~40-job per-language i18n matrix becomes 1 job (the account has 20 concurrent-job slots total), and heavy jobs skip draft PRs — paired with/generate-releasenow opening the living release PR as draft (flipped ready at the new Phase 0a.0a), killing the per-merge churn for the whole cycle. Validated by a fullworkflow_dispatchof the new pipeline: 35 jobs, 0 failures, 23 min, merged coverage 80.16% (> ratchet baseline). - feat(quality): no-new-warnings per PR (#6218) — native ESLint bulk suppressions (≥9.24) freeze the pre-existing debt (476 files / 4,273 violations in
config/quality/eslint-suppressions.json);npm run lint, lint-staged (pre-commit) and a new fork-awarelint-guardjob in quality.yml all run suppressions-aware, so a NEW warning goes red in the PR that introduces it instead of accruing invisibly (+41/+88 per cycle) and being blind-rebaselined at release. 3 warn rules promoted to error insrc/**(react-hooks/exhaustive-deps,@next/next/no-img-element,import/no-anonymous-default-export);collect-metricsmeasures under the frozen baseline (ratchet metric = net-NEW debt; baseline tightened 4,279→0 in-PR per require-tighten); fork PRs run report-only (contributors are never blocked — the maintainer campaigns fix via co-authorship). Baseline stock shrinks via--prune-suppressionsat release reconciliation. - ci: test jobs no longer wait on the Build gate (#6275) —
test-unit×8,vitest,integration×2 andsecuritydeclaredneeds: buildbut never download thenext-buildartifact; they now start at minute 0 (needs: changes, sameifas Build), cutting ~15–20 min of wall-clock per heavy run.e2e/package-artifact/electron-smokekeepneeds: build(they consume the artifact for real). - ci(build): the ci.yml Build job compiles Next.js with Turbopack (
OMNIROUTE_USE_TURBOPACK=1) (#6273) — Build job 20 min → 6 min 59 s (~2.9×) on ubuntu-latest; the webpackactions/cachestep is removed. Validated end-to-end pre-merge viagh workflow run ci.yml --ref <branch>. - feat(build): Turbopack becomes the default bundler for
next buildandnext dev(#6283) —build-next-isolated.mjs,run-next.mjsand the playwright-runner default to Turbopack;OMNIROUTE_USE_TURBOPACK=0is the explicit webpack escape hatch.nightly-compat.yml/npm-publish.ymlinherit the default. Regression guard:tests/unit/build-bundler-default-turbopack.test.ts. - feat(docker): the Docker image builds with Turbopack (
ENV OMNIROUTE_USE_TURBOPACK=1) (#6285) — the v3.8.27 ImportTracer panic ("unreachable: there must be a path to a root") does not reproduce on Next 16.2.9: amd64 (659 s) and arm64 (qemu) build clean, 0 panics, smoke health 200. - ci: opt-in self-hosted VPS runners for the release window (#6284) —
scripts/vps/release-runner-up.sh/down.shmanage the runner VM, andbuild/test-unit/vitestpick a dynamicruns-ongated byvars.USE_VPS_RUNNER == 'true'and own-origin (fork PRs never reach self-hosted runners). Wired into/generate-release(VM up at Phase 1, mandatory down at Phase 3).
📝 Maintenance
- quality(release-green): full pre-flight hardening for this release — the cycle's 11 net-new ESLint errors typed/fixed and
validate-release-greenmade suppressions-aware with per-gate logs (_artifacts/release-green/) and a--hermeticmode; test-masking allowlist entries for the cycle's verified-legitimate assert reductions; stale ESLint suppressions pruned (4,273 → 4,233); the 7 net-newas anycasts from #6292 typed;githubSkillToolsMCP errors routed throughsanitizeErrorMessage();combo-provider-cooldown-siblingadded to the Stryker tap set; executors/env docs count fixes. - ci(quality): merge-integrity fast-gates per PR —
check:changelog-integrity(no base CHANGELOG bullet may vanish in the merge result — the auto-resolve "CHANGELOG-eat" pattern) andcheck:agent-skills-sync(generated SKILL.md ≡ catalog), blocking for own-origin branches and report-only for forks (Princípio Zero). (#6300) - ci(vps): hermetic
nightly-release-greenpre-flight on the dedicatedomni-releaseself-hosted runner (dynamicruns-on, clean env); e2e/integration/electron stay on hosted runners (per-VM port collision + concurrent artifact-download limits documented in the PR). (#6305) - chore(quality): v3.8.45 cycle-close drift rebaselines — file-size (13 files grown by merged cycle PRs), cognitive 867→877, cyclomatic 2028→2035, kiro-translator test debt from #6213; all with dated justification keys.
- docs(architecture): sync stale DB-layer counts (45+/55 → 95+/110+) in REPOSITORY_MAP, the db-schema diagram and llm.txt (+42 i18n mirrors). (#6167)
- chore(release): parallel-cycle flow —
sync-next-cycle.mjs+ Hard Rule #21 semantics (#6203); v3.8.45 development cycle opened. - i18n(it): add 118 missing Italian (
it) translations (net-additive — no existing keys dropped, valid JSON), improving Italian UI coverage. (#6212 — thanks @serverless83) - chore(providers): remove deprecated MiMo V2 model entries from the catalogs (xiaomi-mimo, opencode-go, zenmux-free, audio TTS) — the upstream V2 line is superseded by MiMo V2.5; drops
mimo-v2-tts,mimo-v2-pro,mimo-v2-omni,mimo-v2-flash,mimo-v2-flash-freeand realigns the provider-catalog tests. (#6248 — thanks @backryun)
🙌 Contributors
Thanks to everyone whose work landed in v3.8.45:
| Contributor | PRs / Issues |
|---|---|
| @aleksesipenko | direct commit / report |
| @andrea-kingautomation | direct commit / report |
| @anki1kr | #6078 |
| @arssnndr | #6162, #6163 |
| @backryun | #6154, #6235, #6248 |
| @chirag127 | direct commit / report |
| @DKotsyuba | #6193 |
| @hartmark | #6216 |
| @Iammilansoni | #6150, #6200, #6209, #6245 |
| @jonlwheat2-gif | direct commit / report |
| @kanztu | #6181 |
| @karimalsalah | #6291 |
| @KooshaPari | #6166 |
| @LuisAlejandroVega | #6177 |
| @luweiCN | #6221 |
| @Moseyuh333 | #6186 |
| @muflifadla38 | direct commit / report |
| @powellnorma | direct commit / report |
| @qpeyba | direct commit / report |
| @RCrushMe | direct commit / report |
| @rianonehub | #6134, #6204 |
| @SeaXen | direct commit / report |
| @serverless83 | #6212 |
| @subhansh-dev | #6298 (diagnosis, landed via #6234) |
| @tenshiak | direct commit / report |
| @Theadd | #6194, #6195 |
| @vittoroliveira-dev | #6233 |
| @VXNCXNX | #6213 |
| @diegosouzapw | maintainer |
What's Changed
- Release v3.8.45 by @diegosouzapw in #6202
Full Changelog: v3.8.44...v3.8.45
详细ChangeLogv3.8.44
2026年07月05日
✨ New Features
- feat(resilience): throttle upstream quota fetches on the per-request preflight path (#6009) — a new global min-interval gate (
open-sse/services/quotaFetchThrottle.ts) spaces the actual network calls made by the Codex quota fetcher so that many accounts on one IP no longer fetch quota in the same second (which, perrouter-for-me/CLIProxyAPI#2385, can get a Codex OAuth token revoked). Complements the existing bulk-sync spacing (PROVIDER_LIMITS_SYNC_SPACING_MS) which already serialized the periodic provider-limits sync — this covers the concurrent combo/preflight path it didn't. Cache hits are never delayed; fail-open (only ever awaits a timer). Configurable viaOMNIROUTE_QUOTA_FETCH_MIN_INTERVAL_MS(default 250ms, clamped 0..5000;0disables). Regression guard:tests/unit/quota-fetch-throttle-6009.test.ts(5). (thanks @powellnorma) - feat(autoCombo): add per-request Auto-Combo controls via two headers (#6024 / #6025 / #6023) —
X-OmniRoute-Modesteers anautocombo's scoring for a single request (friendly presetsfast/balanced/quality/cheap/reliable/offlineor a raw mode-pack name;balancedforces the default weights), andX-OmniRoute-Budgetsets a hard per-request USD cost ceiling. Both override the combo's stored config only for the request that carries them; unknown/garbage values are ignored so the saved config is preserved. The resolvers are pure (open-sse/services/autoCombo/requestControls.ts) and feed the engine's existingconfig.modePack/config.budgetCapinputs — no engine changes. Regression guard:tests/unit/auto-combo-request-controls-6024.test.ts(5). (thanks @chirag127) - feat(providers): add the Kenari OpenAI-compatible gateway (BYOK). Regression guard:
tests/unit/kenari.test.ts. (thanks @doedja) - feat(models): add
claude-sonnet-5to the Antigravity model catalog (alias mapping inantigravityModelAliases.ts) (#6103). Regression guard:tests/unit/antigravity-model-aliases.test.ts. (thanks @anki1kr) - feat(api): add
/v1/ocrendpoint (Mistral OCR), an OCR provider category, and Mistral moderation support. (#5950) (thanks @waguriagentic) - Discovery tool (Phase 2): add the
discoveryResultsDB module (CRUD over thediscovery_resultstable, migration 074) and wire the opt-in provider-discovery service to persist and read findings through it (persistDiscoveryResult,getDiscoveryResults,getDiscoveryResultById,markVerified,deleteDiscoveryResult) with(provider, method, endpoint)upsert de-duplication. Adds the/api/discovery/*HTTP surface —GET /results,GET|DELETE /results/:id,POST /scan,POST /verify/:id— under strict loopback-only authorization (/api/discovery/is inLOCAL_ONLY_API_PREFIXESand is NOT manage-scope-bypassable, so thescanroute's outbound probes can never be reached from a tunnel/remote origin). Adds a dashboard UI tab (Tools → Discovery,/dashboard/discovery) to run scans and review, verify, or delete findings. The service stays opt-in / default-off. (#5939) - feat(api): expose a read-only provider plugin manifest at
GET /api/v1/provider-plugin-manifestfor sidecar/relay discovery. (#6001) (thanks @KooshaPari) - feat(sidecar): advertise the provider manifest URL to Bifrost/CLIProxyAPI via the
X-OmniRoute-Provider-Manifest-Urlheader (OMNIROUTE_PROVIDER_MANIFEST_URL). (#6007) (thanks @KooshaPari) - feat(autoCombo): add a latency/speed-optimized routing mode (shared
rankBySpeedscoring core) plus theomniroute_pick_fastest_modelMCP tool. (#6011) (thanks @KooshaPari) - feat(resilience): surface Codex banked reset credits per connected account (#5199) — the Codex quota parsers (
buildCodexUsageQuotas,parseCodexUsageResponse) now additively readrate_limit_reset_credits.available_count(+ optionalrate_limit_reached_type) from the/wham/usagepayload OmniRoute already fetches, and the provider-limits dashboard renders a "Banked Reset Credits" row when a positive count is present. Display-only and fail-open — the field is eligibility-gated, so accounts without it are unaffected (parsers never throw on absent/garbage shapes); redemption (an unofficial mutating endpoint) is intentionally out of scope. Regression guard:tests/unit/codex-banked-reset-credits-5199.test.ts(8). (thanks @ofekbetzalel) - feat(providers): add sign-up geo-restriction notices for SenseNova and StepFun (#5462) — the provider add-form now warns that SenseNova's console appears to require a Chinese (+86) phone number with no documented international path, and that StepFun's default endpoint is its China platform while a global StepFun Open Platform (
platform.stepfun.ai, operated by Sparkling AI Pte. Ltd., Singapore) with email/Google/Discord login exists for international users. Informationalnoticeonly — neither provider is disabled. Regression guard:tests/unit/regional-provider-cn-notices-5462.test.ts. (thanks @chirag127) - feat(usage): add on-demand period-scoped usage-data reset (Settings → System Storage) with a purge API and time-window selector. (#5831)
- feat(claude-code): add an opt-in auto-permission classifier compat mode (off/auto/always) for Claude Code, toggleable from the CLI Code settings. (#5810)
- feat(providers): add optional client-identity header profiles for compatible nodes — preset User-Agent/fingerprint headers (e.g. matching a known CLI) merged into the existing customHeaders field. (#5812)
- feat(build): add a backend-only fast build mode (
scripts/build/build-next-isolated.mjs+backendOnlyPages.mjs) that skips compiling the dashboard frontend pages, cutting local/CI build time for backend-only changes. (#6119 — thanks @artickc) - feat(minimax): extract MiniMax M3's raw
<think>...</think>leakage intoreasoning_contenton the 8 OpenAI-format provider tiers, leaving the Claude-formatminimax/minimax-cntiers untouched (they already report reasoning correctly). (#6073 — thanks @KooshaPari) - feat(services): promote Bifrost (
@maximhq/bifrost— Go AI-gateway) from an env-only relay sidecar to a first-class embedded/supervised service, matching the existing cliproxy/9router model — installer, bootstrapSERVICES[]entry, migration 113 DB seed, 7 lifecycle API routes under/api/services/bifrost/(loopback-only), a dashboard tab, and relay auto-wiring that defaultsBIFROST_BASE_URLto the supervised port when running. Implements item #2 of #5670; the broader RouterBackend contract (items #1, #3-#5) stays out of scope. (#5817, part of #5670) - feat(services): add Mux (
coder/mux— local agent-orchestration daemon) as a fourth-tier embedded service on the existingServiceSupervisorframework — npm-based installer,bootstrap.tsregistration, migration 113 DB seed, 7 lifecycle API routes under/api/services/mux/(loopback-only, defense-in-depth bind to 127.0.0.1), and a dashboard tab reusing the shared service-management components. (#6034) - feat(xai): surface Grok/xAI usage on the quota dashboard via local
usageHistoryaggregation (getXaiUsage) — since xAI exposes no per-account quota API, this sums tokens routed to the connection fromusage_historyand reports them as a cumulative, uncapped quota, mirroring the existing Xiaomi MiMo self-track pattern. (#5806) - feat(minimax): extract MiniMax M3's raw
<think>...</think>tags into a separatereasoning_contentfield on the 8 provider tiers that register M3 withformat:"openai"(trae, huggingchat, bazaarlink, ollama-cloud, opencode, cline, opencode-zen, codebuddy-cn) — previously the thinking text leaked directly intocontent. Reuses the existingextractThinkingFromContentprimitive, extending its allowlist with a minimax-m3-only pattern; the two direct minimax/minimax-cn tiers are untouched since they already surface reasoning natively over Anthropic's Messages format. (Inspired by 9router#2231.) (#6050 — thanks @KooshaPari) - feat(i18n): auto-detect the browser language on first visit — a pure
detectBrowserLocale()matcher (exact match,zh-HK/zh-MOfolded tozh-TW, language-prefix match, elsenull) plus a client-onlyLocaleAutoDetectcomponent mounted once in the root layout. When no locale cookie is set yet, it readsnavigator.languages, computes a match against the supported locales, and persists it via the same cookie/localStorage writerLanguageSelectoralready used (extracted toshared/lib/persistLocale.ts). (Inspired by 9router#1324.) (#5979) - feat(cli-tools): add CodeWhale — the actively-maintained successor to DeepSeek TUI (same author, renamed project) — as a dual dashboard entry alongside the existing "deepseek-tui" catalog entry, so existing DeepSeek TUI users keep a working card while new users are steered to CodeWhale. New
/api/cli-tools/codewhale-settingsroute writes~/.codewhale/config.tomland keeps the legacy~/.deepseek/config.tomlin sync. (Inspired by 9router#1761.) (#5996) - feat(server): support reverse-proxy
basePathdeployment via a new opt-inOMNIROUTE_BASE_PATHenv var (empty by default), using Next.js's nativebasePathsupport so a deployment behind a reverse-proxy subpath (e.g.https://host/omniroute/) works without manual header stripping; the two hardcoded auth-redirect targets insrc/server/authz/pipeline.tsnow prefix withrequest.nextUrl.basePath. Default empty basePath is a no-op for existing root-path deployments. (Inspired by 9router#1810.) (#5992) - feat(providers): add SumoPod (
ai.sumopod.com) and X5Lab (api.x5lab.dev) OpenAI-compatible BYOK aggregator gateways, wired via the default executor with bearer API-key auth; both usepassthroughModelswith a live/v1/modelsfetcher instead of a hardcoded catalog. Regression guard:tests/unit/sumopod-x5lab-provider.test.ts. (Inspired by 9router#1288.) (#5963) - feat(providers): add Charm Hyper (
hyper.charm.land) as a new OpenAI-compatible, bearer-auth API-key gateway provider with a free tier (100 monthly Hypercredits); models resolve via passthrough (modelsUrl+ live/v1/models) since the catalog isn't publicly documented. (Inspired by 9router#2006.) (#5961) - feat(providers): add Nube.sh (
ai.nube.sh) as a new BYOK OpenAI-compatible gateway (LiteLLM proxy), Bearer/API-key auth. Its live model catalog is only reachable with a valid key, so no model IDs are hardcoded — it usespassthroughModels+modelsUrlfor live enumeration. (Inspired by 9router#2294.) (#5936 — thanks @whale9820) - feat(providers): add b.ai (
api.b.ai) as a new OpenAI-compatible BYOK provider, distinct from the existing thebai/theb.ai provider, using passthrough model discovery with no hardcoded model list. (Inspired by 9router#963.) (#5969) - feat(providers): add Qiniu (七牛云) AI inference gateway as a BYOK API-key provider — proxies many upstream models (DeepSeek V3/V4, Claude, Kimi, and more) behind a single key, shipping with an empty static seed and relying on
passthroughModels+ the live/v1/modelscatalog instead of a stale hardcoded model id. Regression guard:tests/unit/qiniu-provider.test.ts. (Inspired by 9router#911.) (#5966) - feat(providers): port ModelScope (Alibaba 魔搭) as a new API-key, OpenAI-compatible provider — verified against ModelScope's own docs that the real production domain is
api-inference.modelscope.cn(.cn, not the upstream PR's.ai) and shippedpassthroughModels: truewith an empty seed +modelsUrlinstead of the upstream PR's static 5-model snapshot, since the open-model catalog moves fast. (Ported from 9router#1764.) (#5965 — thanks @tn5052) - feat(providers): add Augment (Auggie CLI) as a new local, no-auth provider that spawns the user's local
auggieCLI and pipes a flattened prompt via stdin, wrapping stdout as an OpenAI-compatible SSE stream or single JSON body. Auth is delegated toauggie loginoutside OmniRoute (syntheticnoAuth: trueconnection, no DB row required); "Test Connection" spawnsauggie --version. Hardened against the untrusted-input spawn sink: noshell: trueon Windows (argv passed straight to the OS loader, no metacharacter interpretation), andmodelis validated against the registry allowlist before spawn (rejecting unknown or--prefixed values) with a trailing--end-of-options marker. (Inspired by 9router#1200.) (#5972 — thanks @chamdanilukman) - feat(providers): add NVIDIA NIM image generation — a dedicated
nvidia-nimimage format/handler (separate host,ai.api.nvidia.com/v1/genai/<model>, native NIM body shape) for the 4 FLUX models (flux.1-dev, flux.1-schnell, flux.1-kontext-dev, flux.2-klein-4b), shaping each model's per-model request body (dimension/mode validation, required input image + aspect ratio, optional edit image) and normalizing the NIM response's varying shapes into the OpenAI{created, data}shape. (Inspired by 9router#1195.) (#5971) - feat(oauth): import a Codex connection from a raw ChatGPT access token — OmniRoute's only Codex import path previously required both
access_tokenandrefresh_token, leaving no path for a user with only a bare ChatGPT website access token.createProviderConnectiongains an explicitaccess_tokenauth-type branch (intentionally never deduped), a newPOST /api/oauth/codex/import-tokenroute (Zod-validated), andOAuthModal's manual-paste path now detects aneyJ-prefixed pasted token and posts it to the new endpoint, mirroring the existing grok-cli raw-token flow. The executor'srefreshCredentials()already degrades safely tonullwithout a refresh token, forcing re-auth on expiry. (Inspired by 9router#1290.) (#5995 — thanks @ryanngit) - feat(dashboard): add a tool-source diagnostics settings toggle — a new Settings → Advanced card lets operators flip the existing
logToolSourcesflag from the UI instead of editing the DB row directly;logToolSourcesis added to the.strict()/api/settingsZod PATCH schema (previously rejected). (Inspired by 9router#1825.) (#5978 — thanks @DuyPrX) - feat(dashboard): collapse and sort provider quota rows by remaining percentage — the expanded quota list is sorted highest-remaining-first and collapsed to the first 3 rows by default, with a "Show N more"/"Show less" toggle when a connection reports more than 3 quotas, keeping at-risk quotas visible above a long list of healthy ones. Sort/slice logic extracted into pure, directly-unit-tested helpers (
sortQuotasByRemaining,getVisibleQuotas). (Inspired by 9router#1919.) (#5977) - feat(dashboard): suggest HuggingFace Hub media models — a new
GET /api/v1/providers/suggested-modelsroute proxies the public HF Hub models search API (Zod-validated, no token exposed client-side) andImageExampleCardmerges the results into the model picker as a selectable chip row for the huggingface provider; also adds a dedicatedhuggingface-imageformat/handler for HF's raw-image-bytes response. (Inspired by 9router#1633.) (#5990) - feat(cli-tools): add a Crush entry to the dashboard CLI-Tools catalog plus a new
/api/cli-tools/crush-settingsroute (GET/POST/DELETE) — OmniRoute already shipped acrushCLI setup command (bin/cli/commands/setup-crush.mjs) but the dashboard catalog had no matching entry; the new route writes to the same canonical~/.config/crush/crush.jsonpath so the dashboard and CLI command agree. (Inspired by 9router#1233.) (#5970) - feat(providers): extend Vercel AI Gateway (
vercel-ai-gateway/vag) beyond chat-only to support embeddings and image generation — the gateway's OpenAI-compatible/v1API also exposes/embeddingsand/images/generations, so entries were added toEMBEDDING_PROVIDERS(embeddingRegistry.ts) andIMAGE_PROVIDERS(imageRegistry.ts) modeled on the existingopenaientries. (#5968 — thanks @tantai-newnol) - feat(api-keys): add per-key device/connection tracking — a SHA-256 fingerprint of IP + User-Agent, with a 30-minute TTL and per-key/global caps, tracks distinct client devices seen with each API key (in-memory only, raw IP never stored). A new
GET /api/keys/[id]/devicesroute exposes masked device details, and the API Keys dashboard tab gets a "Devices" count badge alongside the existing Sessions badge. This is a new granularity distinct from the existingmaxSessionscap, which limits concurrent sticky-routing sessions rather than tracking device identity. (#5998 — thanks @mugni-rukita) - feat(proxy): add Webshare (
proxy.webshare.io) as a fourth source in the free-proxy provider framework alongside 1proxy, Proxifly, and IPLocate.WebshareProviderpaginates the account's/api/v2/proxy/list/endpoint, upserts proxies into the sharedfree_proxiestable, and tombstones proxies the account no longer lists while never touching rows already promoted into the live proxy pool. Unlike the other sources, Webshare is a paid per-account list, gated onFREE_PROXY_WEBSHARE_API_KEY. (#5993 — thanks @ricatix) - feat(antigravity): support custom Google Cloud project ID settings from the connection edit modal (Antigravity family). (#5905 — thanks @nickwizard)
- feat(dashboard): add a wildcard-CORS runtime warning banner (Settings → Authorization) when
CORS_ALLOW_ALL/*origins are in effect, plus a newdocs/security/CORS.mdsecurity guide covering the risk and safer alternatives. (#5602, #5759) - feat(api): add a
/v1/audio/translationsendpoint (Whisper-style audio translation), a newaudioTranslationhandler, and translation providers wired intoaudioRegistry. Regression guard:tests/unit/audio-translations-route.test.ts(8, incl. no-stack-leak). (#5809) - feat(providers): allow a custom icon URL for compatible provider nodes (migration 113 +
nodes.ts+ Zod schema + API routes + catalog +ProviderIconUI). Regression guards: 14 backend + 5 frontend(vitest) + 24 page-utils tests. (#5815) - feat(xai): register a dedicated
XaiExecutorwith reasoning-effort suffix parsing. Regression guard:tests/unit/executors/xai-executor.test.ts(6). (#5800) - feat(webfetch): support self-hosted FireCrawl instances via
FIRECRAWL_BASE_URL/FIRECRAWL_TIMEOUT_MS. Regression guard:tests/unit/executors/firecrawl-fetch.test.ts(4). (#5793) - feat(providers): add ClinePass as a first-class API-key (BYOK) provider — Cline's paid gateway (
cline-pass/*models, plain Bearer key), distinct from the existing OAuthclineprovider. Regression guard: 16 clinepass tests. (#5942 — thanks @adentdk) - feat(relay): gate Bifrost auto-routing by the provider plugin manifest — only manifest-eligible providers reach the sidecar; ineligible/unknown providers fall back to the existing TS routing path with explicit reasons. Regression guards: 4 provider-plugin-manifest + 11 relay-routing-backend tests. (#5870 — thanks @KooshaPari)
- feat(providers): wire Claude Sonnet 5 end-to-end across the model pipeline — registries,
modelSpecs, pricing (×3), cost, Sonnet-family fallback, 1M-context, and static models. (#5833 — thanks @ggiak)
🔧 Bug Fixes
-
dashboard (
/dashboard/system/proxy500 on every render):ProxyRegistryManagercalleduseProxyBatchOperations(load)before theconst load = useCallback(...)declaration in the component body, so every server render threw a TDZReferenceError: Cannot access 'load' before initializationand the whole proxy page 500'd (#5918 regression, caught by the release-PR e2e smoke — the PR→release fast-gates never render pages). The hook block now sits after theloaddeclaration. Regression guard:tests/unit/ui/ProxyRegistryManager-tdz-render.test.tsx(SSR renderToString — the exact crash mode). -
server (TRACE/TRACK/CONNECT returned raw 500 on every route): methods that undici/fetch cannot represent blew up inside Next's middleware adapter (
TypeError: 'TRACE' HTTP method is unsupported.) as an unhandled 500 (caught by the release-PR dast-smoke Schemathesis negative tests on the new/api/keys/{id}/devicesendpoint). The raw HTTP method guard now answers a clean 405 +Allowheader for these methods on any path, before Next sees the request. Regression guard:tests/unit/dast-method-not-allowed.test.ts(new case). -
i18n (auto-detect refreshed every first visit):
LocaleAutoDetect(#5979) calledrouter.refresh()on every cookie-less first visit — even when the detected browser locale was exactly the one the server had just rendered — re-navigating the page mid-interaction (flaky e2e "execution context destroyed" + a visible flash for every new visitor). It now refreshes only when the detected locale differs from the server-rendered<html lang>. Regression guard:tests/unit/ui/LocaleAutoDetect-refresh.test.tsx. -
models (
oc/alias must reach the no-auth OpenCode provider): restore the #2901 routing contract after the #5918 transitive-alias change made the registered no-authopencodeprovider unreachable by any prefix (oc/chained through the manualopencode→opencode-zenslug override and misrouted its combo entries).resolveProviderAliasnow stops the alias chain as soon as a hop lands on a registered provider id, while keeping #5918's transitivity across alias-only hops and its loop/depth guards. Regression guards:tests/unit/combo-builder-opencode-prefix.test.ts,tests/unit/provider-alias-transitive-5918.test.ts. -
providers (Auggie executor EPIPE crash): a fast-exiting
auggieCLI (e.g. binary present but immediately failing) deliveredEPIPEasynchronously as an'error'event on the child's stdin stream — which a plain try/catch aroundstdin.write()cannot catch — crashing the request instead of surfacing the sanitized CLI error. Both spawn sites now attach a stdin'error'handler so the child's own exit/close handlers report the failure. Regression guard:tests/unit/auggie-executor.test.ts(deterministic 3/3 locally). -
dashboard (CoolingConnectionsPanel broke
next build): the cooling-connections panel from #6061 importedCardfrom a shadcn-style path that does not exist in this repo (@/components/ui/card) and pulled the server DB barrel (@/lib/localDb) into a client component —next buildfailed to compile on the release branch. The panel now renders with repo-native markup and readsformatResetCountdownfrom the new client-safesrc/shared/utils/formatting.ts. Regression guards:tests/unit/format-reset-countdown.test.ts,tests/unit/ui/CoolingConnectionsPanel.test.tsx. (#6155) -
oauth (Zed "Unknown provider" crash): adding Zed from the providers dashboard threw an unhandled
OAuth GET error: Unknown provider: zed(500) (#6041). Zed is a keychain-import-only provider — it's listed in the OAuth catalog so the UI shows it, but has no OAuth handler, so the generic/api/oauth/[provider]/[action]route hitgetProvider("zed")and crashed. The route now recognizes keychain-import-only providers and returns a clear 400 pointing users at the Import button (for both GET and POST OAuth actions), instead of a 500. Regression guard:tests/unit/oauth-keychain-import-only-6041.test.ts. (thanks @imblowsnow) -
fix(providers): disable the unsupported
thinkingparam forminimax-m2.7on NVIDIA NIM (the upstream rejects it) (#6102). Regression guard:tests/unit/nvidia-minimax-thinking-strip.test.ts. (thanks @anki1kr) -
fix(mitm): add an in-process guard so concurrent MITM server starts no longer race — a second start while one is already in flight is short-circuited instead of double-binding the listener (#6107). Regression guard:
tests/unit/mitm-start-guard.test.ts. (thanks @anki1kr) -
translator (Responses → Chat Completions): strip the Responses-API-only
truncationfield before forwarding a/v1/responsesrequest to a non-OpenAI Chat Completions upstream (#6109). Strict upstreams (e.g. NVIDIA NIM) rejected it with HTTP 400Unsupported parameter(s): truncation, breaking Codex-style clients routed to those providers.client_metadata,background, andsafety_identifierwere already stripped —truncationwas the remaining gap. Regression guard:tests/unit/responses-strip-truncation-2311.test.ts. (thanks @TuanNguyen0708) -
combo (prefer known context capacity over unknown): when a combo filters out at least one target for exceeding a known context limit, the router now prefers the remaining known-compatible targets over targets whose context metadata is simply unknown, instead of letting unknown-metadata targets be the only survivors. If no known-compatible context target remains, context-only candidates fall back to the normal strategy order. Regression guard:
tests/unit/combo-context-window-filter.test.ts. (#6088 — thanks @Thinkscape) -
models (GLM-5.2 context normalization): stop treating every hosted GLM-5.2 provider alias as the native 1M-context model. Native/bare GLM-5.2 and verified OpenCode / ZenMux routes keep their 1,000,000-token context, while hosted-provider aliases now respect the caps declared in their provider metadata instead of inheriting the native max. Regression guards:
tests/unit/model-capabilities-registry.test.ts,tests/unit/models-catalog-route.test.ts. (#6091 — thanks @Thinkscape) -
providers (Gemini Web): refresh the Gemini Web cookie handling and model catalog so live Gemini Web sessions keep authenticating and routing to current models. Regression guard:
tests/unit/gemini-web.test.ts. (#6095 — thanks @backryun) -
providers (Perplexity Web): refresh the Perplexity Web model catalog to the current set (GPT-5.4/5.5, Claude Sonnet 5.0 / Opus 4.8, GLM-5.2, Kimi K2.6, Nemotron 3 Ultra) and update the internal mode /
model_preferencemappings and thinking variants so requests resolve to live upstream models. Regression guard:tests/unit/perplexity-web.test.ts. (#6106 — thanks @backryun) -
dashboard ("Update now" → Internal Server Error): clicking Update now on the dashboard home could crash the page with a blank "Internal Server Error" screen (
Minified React error #31). The handler POSTs the loopback-only/api/system/versionauto-update endpoint and, on a non-OK JSON response (e.g. a403when the dashboard is reached through a reverse proxy / non-loopback origin), passed the raw error envelope object{ error: { code, message, correlation_id } }straight tonotify.error(), which rendered the object as a React child and threw #31. The update-error path now funnels the body throughextractApiErrorMessage()(the same safe extractor added in #5340), so a readable string always reaches the toast. Regression guard:tests/unit/ui/home-update-error-render-5991.test.ts. (#5991) -
fix(onboarding): route the provider-details link in the onboarding wizard by the node's stable id instead of the composite provider slug, which could point at the wrong provider details page for multi-account/fingerprint nodes. Regression guard:
tests/unit/onboarding-wizard-details-link-6145.test.ts. (#6145 — thanks @chirag127) -
fix(cli): give
setup-claudea fallback profile generator mirroringsetup-codex, so profile generation no longer silently no-ops when the primary generator path is unavailable. Regression guard:tests/unit/cli/setup-claude.test.ts(new cases). (#6138 — thanks @derhornspieler) -
fix(glm): suppress a leaked
</think>close marker in the GLM Anthropic transport, which was surfacing the raw reasoning-close tag in visible response content instead of being consumed as part of the thinking-block framing. Regression guard:tests/unit/glm-think-close-marker-leak.test.ts. (#6133 — thanks @dhaern) -
fix(provider-limits): close a TOCTOU race in quota-recovery clearing by moving the check-then-clear to a CAS (compare-and-swap) primitive in
src/lib/db/providers.ts, so two concurrent recovery paths can no longer both observe stale state and double-clear/re-lock a connection. Regression guard:tests/unit/provider-limits-recovery.test.ts. (#6139 — thanks @janeza2) -
fix(provider-limits): clear transient rate-limit state (
rateLimitedUntil,lastError,backoffLevel) as soon as quota recovers, instead of leaving stale rate-limit fields behind that could keep a now-healthy connection looking unavailable. Regression guard:tests/unit/provider-limits-recovery.test.ts. (#6128 — thanks @janeza2) -
combos (OpenCode/MiMo fingerprint accounts): expand fingerprint-scoped OpenCode/MiMo accounts into their full per-fingerprint set in the combo builder, which previously showed only the first matching account entry and hid the rest from combo target selection. Regression guard:
tests/unit/combo-builder-fingerprint-expansion.test.ts. (#6092, closes #6087 — thanks @anki1kr) -
fix(auth): persist quota-preflight account lockouts until the reset window elapses, instead of losing the lockout on process restart and letting a still-quota-exhausted account be selected again immediately. Regression guards:
tests/unit/sse-auth.test.ts,tests/unit/opencode-quota-fetcher.test.ts,tests/unit/usage-service-hardening.test.ts. (#6090 — thanks @Thinkscape) -
combo (fingerprint-based provider expansion): expand fingerprint-based providers into per-fingerprint combo targets (
open-sse/services/combo/fingerprintExpansion.ts) so a combo referencing a fingerprint-scoped provider fans out to every matching fingerprint account instead of collapsing onto one. Regression guards:tests/unit/combo-fingerprint-expansion.test.ts,tests/integration/fingerprint-expansion.test.ts. (#6082 — thanks @pizzav-xyz) -
fix (safety-net redirect
reqIdcrash): fix areqIdReferenceErrorthrown inside the safety-net combo redirect path insrc/sse/handlers/chat.ts, remove dead code insrc/domain/quotaCache.ts, and rename the stray rootDESING.mdtoDESIGN.md. Regression guard:tests/unit/chat-safetynet-reqid-6097.test.ts. (#6097 — thanks @fix2015) -
fix(compression): send a patch-only body to
PUT /api/settings/compressionfromCompressionHub, instead of round-tripping the full settings object and risking clobbering fields changed elsewhere between load and save. Regression guard:tests/unit/ui/CompressionHub-patch-only.test.tsx. (#6077, closes #6039 — thanks @anki1kr) -
fix(codex): use
access_token.expinstead ofid_token.expwhen computingexpiresAton Codex auth import, since theid_tokencan expire far sooner than the actual access token, causing imported connections to be treated as expired while still usable. Regression guard:tests/unit/codex-auth-import-expiry.test.ts. (#6084, closes #6075 — thanks @anki1kr) -
fix(security): persist the IP allow/block-list configuration (it was resetting to Disabled and clearing configured IPs on every restart/update) and actually enforce it in the authz pipeline (
src/server/authz/pipeline.ts), where it was previously validated but never applied. Regression guards:tests/unit/ip-filter-persistence-6131.test.ts,tests/unit/authz/ip-filter-enforcement-6131.test.ts,tests/unit/ip-filter.test.ts. (closes #6131, #6132) -
fix (Claude tool_result adjacency): reattach an OpenAI-shaped
tool_resultto sit directly adjacent to its originatingtool_usebefore translating to Claude's message format (open-sse/translator/request/openai-to-claude/toolResultAdjacency.ts), since Claude's API rejects/mishandles a tool result separated from its tool call by intervening messages. Regression guard:tests/unit/translator-openai-to-claude.test.ts(new cases). (#6035 — thanks @KooshaPari) -
fix(config): externalize
ws/bufferutil/utf-8-validateinnext.config.mjsso thecopilot-m365-webexecutor's WebSocket masking path works at runtime — chat requests through it were silently timing out because the bundler was inliningwsinstead of leaving it as a real Node dependency. Regression guard:tests/unit/next-config.test.ts. (#6130, closes #6062 — thanks @anki1kr, whose #6098 fix it re-lands) -
fix(registry): update grok-cli model context lengths to match the actual Grok CLI
/contextcapacities —grok-build128k→256k,grok-composer-2.5-fast128k→200k — so context-aware routing stops filtering these models out for exceeding a stale, too-low limit. Registry-only. (#5913 — thanks @Chewji9875) -
fix(providers): strip an orphan
tool_result(one with no precedingtool_use) on the Antigravity MITM path before translating to OpenAI format, since an unpaired tool result upstream caused request failures. Regression guard:tests/unit/antigravity-orphan-toolresult-6026.test.ts. (closes #6026, #6115) -
fix(providers): emulate OpenAI-style
tool_callsin the GitLab Duo executor (newopen-sse/executors/gitlabResponses.ts), since the executor previously didn't emulate tool-call semantics for Duo, breaking tool-using clients routed to GitLab Duo. Regression guard:tests/unit/gitlab-duo-toolcalls-6051.test.ts. (closes #6051, #6111) -
fix(429 / accountFallback): persist the per-account 429 cooldown cascade across the request boundary and classify OpenCode's "Monthly usage limit. Resets in N days." message as a connection-scoped quota exhaustion with an N-day cooldown (instead of a ~5s transient retry), so an exhausted account stops being re-selected until its window resets. (#6061 — thanks @KooshaPari / @anki1kr, whose superseded #6086 carried the same day-parser approach)
-
combo (sibling-model fallback on per-model-quota 500s): when a combo held multiple models from the same provider (e.g. two Gemini models) and the first returned a server 500, the router retried the same locked model and surfaced a 429 "cooling down" instead of trying the sibling —
markConnectionLevelExhaustionwas wrongly tripped by a model-level 500 for per-model-quota providers (gemini, github, passthrough, compatible), and the retry loop didn't checkisModelLockedbefore re-hitting the same model. Both gaps are fixed; the combo now falls through to the untried sibling model. Regression guard:tests/unit/combo/combo-target-exhaustion.test.ts(21 cases). (#5976 — thanks @hartmark) -
providers (Cline non-streaming envelope): Cline can return OpenAI-compatible chat completions wrapped as
{ success, data: { choices, usage, ... } }; the non-streaming path checked the top-level body for empty content before unwrapping, so a valid wrapped response could be misclassified as malformed/empty. The envelope is now unwrapped immediately after provider-envelope handling, before empty-content detection, usage extraction, and translation. Regression guard:tests/unit/cline-response-envelope.test.ts. (#6046 — thanks @KooshaPari) -
providers (kimi-web, qwen-web): align the kimi-web model catalog and request-scenario selection with
www.kimi.com's liveGetAvailableModelsresponse, and stop aliasingqwen3-coder-pluson qwen-web now that it is present as its own model in the live Qwen web catalog. (#5915 — thanks @janeza2) -
translator (Antigravity/Gemini tool schemas): strip
multipleOffrom function-declaration parameters before forwarding to Antigravity/Gemini — it is not part of the Gemini OpenAPI 3.0 schema subset accepted upstream and triggered a hard 400 ("Unknown name multipleOf"). Added toGEMINI_UNSUPPORTED_SCHEMA_KEYSso it is stripped at every schema level;minimum/maximumare unaffected since Gemini accepts them. (Ported from 9router#2309, reported by @abil0321.) (#6052) -
translator (Kiro system prompt leak): Kiro/CodeWhisperer has no system role, so system messages were normalized into a bare user turn — the full Claude Code system prompt then appeared as raw user text, polluting model context. System-origin content is now wrapped in
<system-reminder>tags before merging into the Kiro user message; real user turns are unaffected. (Ported from 9router#2306, reported by @VitzS7.) (#6053) -
fix(codex): convert Chat Completions
json_schemaresponse_format→ Responses APItext.formaton the Codex path, and preserve an existingtext.formatthrough verbosity normalization. Regression guards: 48 translator-openai-responses-req + 8 codex-verbosity tests. (#5933 — thanks @yusufrahadika) -
fix(thinking): only inject the
redacted_thinkingreplay block whentool_useis present and thinking is enabled, avoiding a fabricated replay block on plain (non-tool) turns. (#5945, #5953) -
fix(resilience): honor active codex session affinity over per-request reset-aware re-scoring, so an in-flight session sticks to its pinned account instead of being re-scored away mid-conversation. New
src/sse/services/sessionAffinityPin.tsmodule. Regression guard:tests/unit/codex-session-affinity-reset-aware-5903.test.ts. (#5903, #5943) -
fix(resilience): compute per-window
is_exhaustedand honor the quota-exhaustion preflight for priority combos, so a combo no longer keeps routing to a target whose current window is already exhausted. Newopen-sse/services/combo/quotaExhaustionCutoff.ts. Regression guard:tests/unit/combo-priority-quota-exhaustion-cutoff-5923.test.ts. (#5923, #5941) -
fix(providers): strip a
/v1suffix from the base URL unconditionally in both models-discovery paths, avoiding a doubled/v1/v1/modelsfetch error (e.g. Api Airforce). Regression guard:tests/unit/airforce-v1-double-prefix-5899.test.ts. (#5899, #5920 — thanks @anki1kr) -
fix(api): relax provider-scoped chat completion validation on
/api/providers/[provider]/chat/completions. Regression guard:tests/unit/provider-scoped-chat-completions-validation.test.ts. (#5907 — thanks @nickwizard) -
fix(providers): validate v0 Platform (Vercel) API keys via the
/chatsendpoint instead of a probe that rejected valid keys. Regression guard:tests/unit/provider-validation-specialty.test.ts. (#5954 — thanks @vittoroliveira-dev) -
fix(mcp): auto-recover stale streamable HTTP MCP sessions on
initializeinstead of failing the reconnect. Regression guard:tests/unit/mcp-session-sweep.test.ts. (#5957 — thanks @Chewji9875) -
fix(translator): enforce strict Anthropic content-block compliance when converting an antigravity → openai request. Regression guard:
tests/unit/translator-antigravity-to-openai.test.ts(9). (#5935) -
fix(sse): strip ANSI/VT100 escape codes from
gemini-clistream frames using a ReDoS-safe pattern. Regression guard:tests/unit/gemini-cli-ansi-sanitization.test.ts(5). (#5934 — thanks @anki1kr) -
fix(discovery): resolve a doubled
/v1discovery path and aREDIRECT_BLOCKEDprobe-loop abort in the model-discovery route. Regression guard:tests/unit/provider-models-route.test.ts. (#5904 — thanks @hamsa0x7) -
fix(providers): Perplexity Web now emits real
tool_callsin streaming mode — previously only non-streaming requests (hasTools && !stream) converted<tool>{...}</tool>text into OpenAItool_calls; streaming requests (the default for agentic coding clients) got the raw<tool>text as plaindelta.contentand never emitted atool_callsSSE delta. Now mirrors thechatgpt-webtoolModehelpers (buildToolModeResponse()/toolCompletionToSseStream(), extended with a caller-suppliedidSeedso tool-call ids stay provider-specific), buffering the completion and emitting a terminal SSE replay carryingdelta.tool_calls+finish_reason: tool_callsregardless of the caller's stream flag. (#5927, #5937) -
providers (openai-family model inference no longer hijacks cataloged models):
resolveModelByProviderInference()had an unconditional/^gpt-/iheuristic that hijacked any model id starting withgpt-/o1/o3into provideropenai, even when the id is cataloged under other providers — breaking bare (non-combo) requests for open-weight models likegpt-oss-120b(served by fireworks/cerebras/scaleway/byteplus/sambanova/heroku), which don't exist on openai's catalog, producing a 404 with no fallback. The heuristic is now gated onproviders.length === 0so it only fires for genuinely uncataloged openai-family ids. Regression guard:tests/unit/gptoss-provider-inference-5852.test.ts. (#5852, #5938) -
fix(providers): deepseek-web reliability — auto-refresh the session on
401/403, refresh the v2.0.0 client headers, and fix the token-kind bulk import path. Regression guards:tests/unit/deepseek-web-autorefresh-401-response.test.ts,tests/unit/bulk-web-session-import.test.ts. (#5988 — thanks @backryun) -
fix(api): guard the shared frontend API client (
handleResponseinsrc/shared/utils/api.ts) against non-JSON error responses — it previously calledresponse.json()unconditionally and readdata.errordirectly, throwing an unrelated parse error (orundefined) instead of a useful message when an upstream/proxy returned a non-JSON error body. Now routes throughparseResponseBody/getErrorMessageto build a safe message regardless of body shape. Regression guard:tests/unit/shared-api-utils.test.ts. (#5973) -
fix(embeddings): forward the connection-level proxy configuration to embedding requests —
src/lib/embeddings/service.tspreviously ignored a connection's configured proxy when making embedding calls, so proxy-only network setups leaked embedding traffic outside the proxy. Regression guard:tests/unit/embeddings-proxy-forwarding.test.ts. (#5975) -
fix(resilience): parse
Retry-Afterfrom a 429's JSON body for cooldown calculation, not just the HTTP header — a newretryAfterJson.tshelper extracts a retry-after hint from common JSON error-body shapes andaccountFallback.ts's cooldown path now prefers it when the header is absent. Regression guard:tests/unit/account-fallback-retry-after-json.test.ts. (Includes #6013's retry-after-json extraction.) (#5974 — thanks @KooshaPari)
📝 Maintenance
-
release close (release-PR one-pass CI sweep): restore Zod validation on the provider-scoped chat route with a
.passthrough()schema that keeps #5907's relaxed semantics (t06 route-validation gate); point/api/keys/{id}/devices' 401 response at the management error envelope indocs/openapi.yaml(Schemathesis schema-conformance); rebaselinei18nUiCoverage.pct77.5→76.8 (~1352 new en.json UI keys from the cycle await the async translation workflow — same shape as the v3.8.39 rebaseline); dismiss 2 CodeQLjs/incomplete-url-substring-sanitizationfalse positives on unit-test asserts (v3.8.35 precedent). -
release close (Phase 0 pre-flight): align cycle-stale tests with merged behavior — provider count 166→167 (Kenari #6104), Linux-regenerated translate-path golden (+
kenari), OpenCode quota scopeprovider→connection(#6061) — and absorb cycle ratchet drift (file-size caps foroauth/[provider]/[action]/route.ts960,providerLimits.ts998,chat.ts1662,auth.ts2426, with #6158 tracked to restore the oauth-route freeze). The test-masking gate gains a narrowly-scoped_deletedWithReplacementallowlist section (deletion is exempt ONLY when the declared replacement test file exists in HEAD — used fortargetExhaustion.test.ts→tests/unit/combo/combo-target-exhaustion.test.ts, which has MORE coverage: 21 cases/52 asserts vs 13/37), plus 5 new gate unit tests and reduction-allowlist entries for the verified-legitimate #5958/#6088/#5816 assert migrations. -
test (deflake
setup-claude):tests/unit/cli/setup-claude.test.tsfailed ~50% of runs withUnable to deserialize cloned data due to invalid or unsupported versionat file teardown (all subtests passed), randomly reddeningUnit Tests fast-path (2/2)/Fast Quality Gatesacross the PR→release queue. Root cause:node --teststreams each file's report to the parent as V8-serialized frames on fd 1 (stdout), and the CLI helper under test (syncClaudeProfilesFromModels) prints progress viaconsole.log— that stdout output interleaved with the serialized frames and corrupted the stream. The test now silences the stdout-writingconsolemethods for the file's duration (no assertion inspects stdout), making it deterministic (15/15 green locally). (#5959) (#6021) -
API validation: add a
validatedJsonBody(request, schema)helper insrc/shared/validation/helpers.tsthat fuses JSON body parsing and Zod validation into a single call, returning either the type-narrowed data or a ready-to-return 400NextResponsewith the standard error envelope. Salvaged from the closed refactor PR #5075 (Tier 1 portable helper) with a focused 6-case regression test. Co-authored-by: KooshaPari KooshaPari@users.noreply.github.com -
repo (Windows case-conflict cleanup): remove the stale root
DESIGN.md, which case-conflicted withdesign.mdand broke checkouts/clones on case-insensitive Windows filesystems. (#6140 — thanks @backryun) -
i18n(zh-CN): translate the CHANGELOG entries and section headings, adopting zh-CN as a fully translated locale alongside the existing supporting docs. (#6043 — thanks @studyzy)
-
docs (env-doc-sync base-red): document
BIFROST_PORTin.env.example/docs/reference/ENVIRONMENT.md— the Bifrost embedded-service merge referencedprocess.env.BIFROST_PORT(default 8080) without documenting it, socheck:env-doc-syncfailed on the release tip and reddened Fast Quality Gates for every open PR→release. Docs-only (8d7e3e28f). -
test (CI-runner-independent translate-path golden): normalize OS/arch-derived request headers (
X-Stainless-Os/X-Stainless-Arch,(OS;arch)User-Agent segments, and Antigravity'sos.platform()-derived platform substring) in the provider translate-path golden snapshot, so the test no longer depends on the OS/arch of the CI runner that generated it — a Mac-literal Antigravity UA was failing on Linux CI. Regression guard:tests/unit/provider-translate-path-golden.test.ts. (#6076 — thanks @KooshaPari) -
release-green base-reds (#5695 regex + file-size rebaseline):
tests/unit/ui/quick-start-api-keys-link-5695.test.tsnow tolerates Prettier splitting a multi-line<Link href=...>so thestep1Descregex matches the/dashboard/api-managerlink instead of skipping tostep2's single-line/dashboard/providerslink (test was brittle, not the code). Also rebaselines 5 files that grew via already-merged release-tip PRs inconfig/quality/file-size-baseline.json(ApiManagerPageClient3017→3058,OAuthModal969→989,cliRuntime1090→1100,webProvidersA805→809,deepseek-web.test1081→1092), with shrink tracked in #3501. (#6093) -
release close (LEDGER-4 base-red): the
cline-passprovider'sminimax-m3registry entry was missingsupportsVision, breaking the LEDGER-4 registry-consistency test (everyminimax-m3entry must setsupportsVisionto matchlite.ts— the model is multimodal). Flagged it to match every otherminimax-m3entry (trae, bazaarlink, cline, ollama-cloud, ...). (#6003) -
release close (stryker
tap.testFilesdrift): additional release-green cleanup clearing theqoderregistry'sminimax-m3supportsVisionLEDGER-4 base-red andstryker.conf.json'stap.testFilesdrift. (#6012) -
install (pnpm 11+ support): pnpm 11 introduced
ERR_PNPM_IGNORED_BUILDSfor native addon packages — without explicitallowBuildsapproval, packages silently skip their build scripts and OmniRoute fails to start with missing native modules. SetsallowBuilds=truefor all 13 native addon packages inpnpm-workspace.yaml(@parcel/watcher,@swc/core,better-sqlite3,core-js,esbuild,keytar,koffi,libxmljs2,onnxruntime-node,protobufjs,sharp,tls-client-node,unrs-resolver) and migratesonlyBuiltDependenciesfrom the deprecatedpackage.jsonfield to a newpnpm.json. (commit 39349da — thanks @chirag127) -
refactor (Block J hot-path decomposition): extract pure leaves with no behavior change from the executor, translator, combo, and SSE hot paths — orphaned executor tests moved to top-level so a runner collects them, and
handleComboChat's auto-strategy/target-timeout regions split into named helpers. (#6063, #6049, #6036, #6030, #6020, #6018, #6017, #6016, #6015, #6014, #6008, #6006, #6000, #5999, #5994, #5967, #5962, #5960, #5947, #5949, #5940, #5932) -
chore (quality/CI housekeeping): rebaseline residual ESLint/cognitive-complexity/file-size drift accumulated over the v3.8.44 cycle, move orphaned executor tests to a top-level location so a runner actually collects them, harden the release pipeline with a test-masking pre-flight gate plus contributors/uncovered helpers, and make the
pr-evidenceFAIL output tell the author to push (a body edit alone does not re-run the gate). (#5926, #5944, #5952, #6027, #5928, plus a #5975-collateral test hardening pinning a seeded connection to direct egress in route-edge-coverage) -
docs (housekeeping): normalize mixed-language documentation content, restore the OpenAPI coverage ratchet by documenting 9 newly-added routes, record Hard Rule #22 (cross-session safety —
git stash+ in-flight PR bans), and document the compression-engine's upstream sync policy for the RTK/Caveman engines. (#6105, #5955, #5948, plus docs-only commit 926b08a)
🙌 Contributors
Thanks to everyone whose work landed in v3.8.44:
| Contributor | PRs / Issues |
|---|---|
| @adentdk | #5942 |
| @anki1kr | #5899, #5920, #5934, #6039, #6061, #6062, #6075, #6077, #6084, #6086, #6087, #6092, #6098, #6130 |
| @artickc | #6119 |
| @backryun | #5988, #6095, #6106, #6140 |
| @chamdanilukman | #5972 |
| @Chewji9875 | #5913, #5957 |
| @chirag127 | #6145 |
| @derhornspieler | #6138 |
| @dhaern | #6133 |
| @doedja | direct commit / report |
| @DuyPrX | #5978 |
| @fix2015 | #6097 |
| @ggiak | #5833 |
| @hamsa0x7 | #5904 |
| @hartmark | #5976 |
| @imblowsnow | direct commit / report |
| @janeza2 | #5915, #6128, #6139 |
| @KooshaPari | #5870, #5974, #6035, #6046, #6050, #6061, #6073, #6076, #6086 |
| @mugni-rukita | #5998 |
| @nickwizard | #5905, #5907 |
| @ofekbetzalel | direct commit / report |
| @pizzav-xyz | #6082 |
| @powellnorma | direct commit / report |
| @ricatix | #5993 |
| @ryanngit | #5995 |
| @studyzy | #6043 |
| @tantai-newnol | #5968 |
| @Thinkscape | #6088, #6090, #6091 |
| @tn5052 | #5965 |
| @TuanNguyen0708 | direct commit / report |
| @vittoroliveira-dev | #5954 |
| @waguriagentic | direct commit / report |
| @whale9820 | #5936 |
| @WslzGmzs | direct commit / report |
| @yusufrahadika | #5933 |
| @diegosouzapw | maintainer |
What's Changed
- test(security): fix CodeQL #689 — Kimi Web URL host substring sanitization (main) by @diegosouzapw in #6048
- fix(config): externalize 'ws' to fix copilot-m365-web chat timeout by @anki1kr in #6098
- chore(deps): bump github/codeql-action/init from 4.36.2 to 4.36.3 by @dependabot[bot] in #6123
- chore(deps): bump actions/cache from 6.0.0 to 6.1.0 by @dependabot[bot] in #6124
- chore(deps): bump github/codeql-action/analyze from 4.36.2 to 4.36.3 by @dependabot[bot] in #6125
- Release v3.8.44 by @diegosouzapw in #5925
Full Changelog: v3.8.43...v3.8.44
详细ChangeLogv3.8.43
2026年07月02日
[3.8.43] — 2026-07-02
✨ New Features
-
usage (quota percentages + provider USD drilldown):
@@om-usageand the HTTP usage endpoint now report personal API-key quotas as remaining percentages (USD amounts stay out of the command output), provider quota remaining is scaled by the configured quota cutoff so the protected reserve reads as 0% left, and the quota dashboard regains a provider USD cost drilldown (/api/usage/provider-window-costs+ProviderUsdCostModal, management-auth gated). Also honors observed provider quota resets: a same-resetAtreset (usage dropping back to the reset floor) is detected and preferred over stale recorded weekly events for provider USD windows and API-key USD quotas. Newsrc/lib/usage/providerWindowCosts.ts. Regression guards:tests/unit/provider-window-costs.test.ts,tests/unit/internal-usage-command.test.ts,tests/unit/api-key-usage-limits.test.ts,tests/unit/lib/quota-reset-events.test.ts. Extracted from #5863 by @Witroch4. -
dashboard (live WS behind reverse proxy): the live dashboard WebSocket can now be fronted by a reverse proxy or Cloudflare Tunnel via
NEXT_PUBLIC_LIVE_WS_PUBLIC_URL(e.g.wss://ws.my-ai.com/live-ws). The URL is honored both at build time (env inlined into the bundle) and at runtime for prebuilt Docker/npm images: the/api/v1/ws?handshake=1handshake now echoes a lazily-readlive.publicUrl(onlyws:///wss://values are accepted; anything else is rejected tonull), anduseLiveDashboardresolves the URL from that handshake before connecting, falling back to the previousws(s)://hostname:20129default. Also documentsLIVE_WS_ALLOWED_HOSTSand aligns the GitLab Duo OAuth scopes line in.env.examplewith the live config (ai_features read_user). Regression guard:tests/unit/live-ws-public-url.test.ts(5). (#5877 by @ianriizky) -
providers (CLI profile auto-sync): opt-in toggles to auto-regenerate CLI tool profiles after a provider model sync. When enabled, a model-catalog change (re)writes that tool's profile files from the live catalog — Codex (
~/.codex/*.config.toml) and now Claude Code (~/.claude/profiles/<name>/settings.json, via an extractedsyncClaudeProfilesFromModels+ a newclaudeProfileAutoSync.tsmirroring the Codex path). Both are off by default and never touch the active/default CLI config; they are backed by theOMNIROUTE_AUTO_SYNC_CODEX_PROFILES/OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILESfeature flags (DB/dashboard override > env > default "false") and additionally gated behind the existingCLI_ALLOW_CONFIG_WRITESwrite-guard. A "CLI profile auto-sync" card on the CLI Code dashboard toggles each (moved from the providers dashboard in #5778 — thanks @rdself). Regression guards:tests/unit/claude-profile-auto-sync-gate.test.ts,tests/unit/codex-profile-auto-sync-gate.test.ts,tests/unit/cli/setup-claude.test.ts(follow-up to #5737). -
cli (startup banner): the
servestartup banner now prints the running OmniRoute version (v3.8.x) beneath the ASCII logo, so the active version is visible at a glance without a separate--versioncall. Regression guard:tests/unit/cli-serve-version-banner.test.ts. Thanks @chirag127 (#5752). -
analytics (subscription cost): flat-rate providers now show $0 in cost analytics instead of an inflated per-token estimate. Subscription / coding-plan providers (every cookie-web provider — ChatGPT Web, grok-web, … — plus the dedicated Minimax Coding, Kimi Coding, GLM Coding, Alibaba Coding Plan, and Xiaomi MiMo plans) bill a flat fee, not per token, yet still carry per-token pricing rows used for estimates — so the analytics dashboard over-reported their cost. A new flat-rate classifier (
src/lib/usage/flatRateProviders.ts) is consulted by the analytics surfaces (analytics route, usage stats, usage analytics) via an opt-inflatRateAsZerocost option, so those providers read $0 while budget / quota / routing keep estimating unchanged. Deliberately NOT zeroed:codex/cx(OmniRoute actively tracks Codex token cost — Fast-tier multipliers, GPT-5.x pricing — and Codex can be a metered account),byteplus(metered ModelArk),minimax-cn(metered China API). Regression guard:tests/unit/flat-rate-cost-5552.test.ts. (#5552) -
mcp (RTK): expose the RTK tool-output learn/discover workflow as two new MCP tools so an agent can grow the RTK filter catalog without leaving the protocol.
omniroute_rtk_discoveranalyzes recently captured raw tool output (discoverRepeatedNoise/suggestFilter) and returns candidate noise patterns plus a suggested filter;omniroute_rtk_learnlists the captured command samples (listRtkCommandSamples) and resolves a command to its RTK filter id (commandToId). Both are read-only (scoperead:compression), wrap the existing RTK discovery primitives (no new logic in the engine), and log to the MCP audit trail. Regression guard:tests/unit/compression/rtk-mcp-tools.test.ts(4). gaps v3.8.42 — T07. -
compression (LLM tier): add an opt-in, default-off LLM-tier compression engine (
llm) that condenses the prose of non-system messages via a pluggable chat-completion backend. It mirrors thellmlinguaengine's contract but is safe by construction: the default backend is a no-op pass-through (the engine never mutates the payload until an operator both enables it and wires a real backend viasetLlmCompressorBackend()), it is not part of the default stacked pipeline,enableddefaults tofalse, fenced code blocks andsystemmessages are never sent to the model, and every backend error fails open (the original segment/body is kept, never thrown). AminTokensfloor skips small prompts. The real production backend is intentionally a VPS-validated follow-up (Hard Rule #18), exactly as thellmlinguaworker backend is gated. Newopen-sse/services/compression/engines/llm/index.ts. Regression guard:tests/unit/compression/llm-compressor-engine.test.ts(8). gaps v3.8.42 — T05/C3. -
memory (typed decay): add opt-in typed memory decay (TV6) so the conversational memory store stops accumulating stale
episodicnoise. Each injected memory now tracks anaccess_count+last_accessed_at(always-on, non-destructive telemetry; migration111_memory_typed_decay), and an opt-in, default-off sweep (MEMORY_TYPED_DECAY_ENABLED, defaultfalse) deletes memories that are past a per-type TTL and not immune. Onlyepisodicdecays by default (30d, env-tunable);factual/procedural/semanticare immune, and any memory accessed>= 3times earns access immunity (mirroring "guardrail/convention/decision never decay"). The decay clock re-bases on the last access, so used memories survive. Deletions reusedeleteMemory(SQLite + sqlite-vec + Qdrant stay in sync) and fail open; an optional periodic sweep is doubly opt-in (also needsMEMORY_TYPED_DECAY_SWEEP_INTERVAL>0). With the flag off nothing is ever deleted (Rule #20 spirit). Newsrc/lib/memory/typedDecay.ts. Regression guard:tests/unit/memory/typed-decay.test.ts(15). gaps v3.8.42 — T10/TV6. -
dashboard (combos): the named-combos editor now lets you drag to reorder the stacked-compression pipeline instead of only editing fixed-position steps. A new pure model (
src/shared/components/compression/compressionPipelineModel.ts) owns add/remove/move/update with the engine→intensity invariant and a never-empty guarantee, and a@dnd-kit/sortableeditor (CompressionPipelineEditor.tsx, matching the sidebar reorder pattern) replaces the inline list inCompressionCombosPageClient. Order persists through the existing combos endpoint. Regression guards:tests/unit/compression-pipeline-model.test.ts(11) +tests/unit/ui/compression-pipeline-editor.test.tsx(4). A dedicatedtests/e2e/compression-studio.spec.ts(Tela A render + tab switch) closes the studios e2e gap the combo-live spec did not cover. gaps v3.8.42 — T06 + T03. -
compression (pipeline): add an opt-in, default-off per-engine circuit-breaker to the stacked compression pipeline (T02). When an engine throws repeatedly across requests, its breaker opens and the stacked loops skip that engine (keeping the body verbatim for that step — fail-open) for a cooldown, then probe once (lazy half-open); success closes it, a failed probe re-opens it. This is distinct from the provider circuit-breaker (
src/shared/utils/circuitBreaker.ts, provider-scoped + DB-persisted) — the newpipelineEngineBreaker.tsis engine-scoped, process-local, and adds zero DB/IO on the hot path. It composes with the existing per-request TV1 bail-out (which skips within a single request); the breaker adds cross-request memory. Default off (COMPRESSION_PIPELINE_BREAKER_ENABLED=false) → byte-identical to the pre-breaker pipeline (a throwing engine still propagates unless TV1 is separately enabled). Configurable per-call, per-CompressionConfig, or via env (_THRESHOLD/_COOLDOWN_MS). Regression guard:tests/unit/compression/pipeline-circuit-breaker.test.ts(9, incl. a throwing-engine integration); existing strategySelector/bail-out suites stay green. gaps v3.8.42 — T02 (2.2). -
compression (CCR): the CCR retrieval-feedback (H8) is now graduated instead of a binary cliff. Previously a block retrieved
>= 3times was flagged do-not-compress and everything below that stayed fully compressible. Now each prior retrieval raises a block's effectiveminCharslinearly (effectiveMinChars), so frequently-retrieved content is compressed progressively less; the>= 3exclusion is preserved (asInfinity). The ramp is controlled by aretrievalRampFactor(default2, per-combo config orCOMPRESSION_CCR_RETRIEVAL_RAMP_FACTOR);1reproduces the exact legacy binary behavior. Per-(principal, hash)isolation is unchanged. Regression guard:tests/unit/compression/ccr-retrieval-ramp.test.ts(12); existing CCR suites (51) stay green. gaps v3.8.42 — T08/H8. -
compression (cache-aware): add an opt-in, default-off usage-observed prefix freeze (H5). The cache-aware guard previously preserved the system prompt only for providers a static heuristic recognized as caching. It now also learns which system prompts actually recur: once a system prompt has been observed
>=a threshold across requests, it is treated as a stable cacheable prefix and preserved from compression even for providers the static check misses — recovering prompt-cache hits that a prefix-compressing mode would otherwise bust. Content-addressed by a hash of the system prompt (OpenAI / Claude / Gemini shapes), in-memory + bounded, zero DB/IO; a "freeze" only preserves the prefix, so it never mutates a payload. Default OFF (COMPRESSION_PREFIX_FREEZE_ENABLED, threshold_THRESHOLD); respects theneverpreserve-mode (never freezes). Newopen-sse/services/compression/prefixFreeze.ts, wired intoresolveCacheAwareConfig. Regression guard:tests/unit/compression/prefix-freeze.test.ts(10); 44 existing cache-aware / preserve-mode tests stay green. gaps v3.8.42 — T08/H5. -
compression (read-lifecycle): add a new opt-in, default-off
read-lifecycleengine (H7) that collapses stale/superseded file-Read tool results. In agentic conversations the same file is Read repeatedly; an earlier Read becomes stale once the same path is re-read (superseded by a newer view) or modified by a later Write/Edit. The engine replaces those earlier Read results with a short stub — keeping only the current (last, un-superseded) Read intact — recovering the tokens the model no longer needs. Unlikesession-dedup(identical-content) orccr(reversible markers), this is semantic + lossy, so it is opt-in (enableddefaultsfalse). Conservative by construction: matches only well-known Read/Write tool names, compares exact paths, collapses a Read only when a strictly-later invocation touches the same path, and fail-opens on any unexpected shape. Supports both the Anthropic (tool_use/tool_result) and OpenAI (tool_calls+role:"tool") shapes. Newopen-sse/services/compression/engines/readLifecycle/index.ts. Regression guard:tests/unit/compression/read-lifecycle.test.ts(10). gaps v3.8.42 — T08/H7. -
observability (correlation IDs): requests now carry a correlation id threaded through logs so a single request can be traced end-to-end across the pipeline. (#5834 — thanks @hartmark)
-
cli (startup banner — boot time): the
serveready banner now shows how long startup took, so slow-boot conditions are visible at a glance. (#5799 — thanks @ishatiwari21) -
api (quota-policy bypass scope): add an opt-in API-key provider quota-policy bypass scope, so a designated key can be exempted from provider quota enforcement without disabling quotas globally. (#5731 — thanks @Witroch4)
-
providers (Ollama local): add a first-class Ollama local-provider card to the providers dashboard so the local LLM runtime can be configured like any other provider. (#5712 — thanks @diegosouzapw)
-
codex (fallback profiles): generate fallback CLI profiles for Codex-compatible models so compatible models get a usable profile automatically. (#5701 — thanks @skyzea1)
-
api (response-body validation + failover): add a configurable response-body validation step that can fail a target over to the next candidate when the upstream returns a structurally-invalid body (routing/#4985). (#5684 — thanks @diegosouzapw)
-
providers (SenseNova): complete the SenseNova free Token Plan — chat completions plus Text-to-Image (ported from 9router#2233). (#5679 — thanks @diegosouzapw)
-
db (self-correcting context windows): add self-correcting model context-window overrides so a model whose advertised context length is wrong is corrected automatically (models/#5004). (#5667 — thanks @diegosouzapw)
-
routing (latency strategy): optimize the latency routing strategy using observed per-target performance metrics for better candidate selection. (#5629 — thanks @KooshaPari)
-
compression (preserveSystemPrompt mode): add a
preserveSystemPromptmode enum (always|whenNoCache|never) with legacy back-compat, giving operators explicit control over when the system prompt is protected from compression (T05/C5). (#5653 — thanks @diegosouzapw) -
commandCode (vision): add multimodal image support for Command Code vision models. (#5557 — thanks @Stazyu)
-
compression (read-lifecycle engine): T08/H7 (2.5) — an opt-in read-lifecycle engine that collapses superseded file reads so stale earlier reads of the same file are pruned from the context. (#5754 — thanks @diegosouzapw)
-
compression (usage-observed prefix freeze): T08/H5 (2.4) — opt-in prefix freeze driven by observed usage, keeping a stable cached prefix from being rewritten by downstream engines. (#5744 — thanks @diegosouzapw)
-
compression (CCR retrieval-feedback ramp): T08/H8 (2.3) — a graduated Context-Compression-Ratio retrieval-feedback ramp that tunes compression aggressiveness from retrieval signals. (#5739 — thanks @diegosouzapw)
-
compression (per-engine circuit breaker): T02 — an opt-in per-engine pipeline circuit-breaker that disables a misbehaving compression engine without failing the whole pipeline. (#5735 — thanks @diegosouzapw)
-
compression (LLM-tier engine): T05/C3 — an opt-in LLM-tier compression engine that uses a model pass for higher-ratio semantic compression. (#5702 — thanks @diegosouzapw)
-
dashboard (compression pipeline editor): T06/T03 — a drag-to-reorder compression pipeline editor plus a compression-studio e2e flow. (#5727 — thanks @diegosouzapw)
-
memory (typed decay): T10/TV6 — opt-in typed memory decay so aged, low-value memories fade on a per-type schedule. (#5723 — thanks @diegosouzapw)
-
mcp (RTK tools): T07 — expose the RTK learn/discover capabilities as first-class MCP tools. (#5691 — thanks @diegosouzapw)
-
providers (CLI profile auto-sync): opt-in CLI profile auto-sync toggles, including Claude Code auto-sync, so generated CLI profiles can track provider changes automatically. (#5755 — thanks @diegosouzapw)
🔧 Bug Fixes
-
fix(opencode): stop fabricating
User-Agent: opencode/localandx-opencode-client: cliheaders when the client sends none — the executor-dedup refactor (#5720) accidentally re-introduced header fabrication, violating the forward-only contract (inventing opencode-internal values risks upstream rejection). Restored to forward-only: those headers are emitted only when a real client source is present. Regression guard:tests/unit/opencode-executor.test.ts. (thanks @diegosouzapw) -
fix(executors):
resolveEffectiveKeyreturnsundefined(not"") when no API key is present — a type-coercion cleanup (#5798) changedapiKey ?? ""to satisfy the typechecker, silently mutating auth-key resolution semantics. Widened the return type tostring | undefinedand reverted the coercion so OAuth-only credentials resolve correctly. Regression guard:tests/unit/refactor-buildHeaders-preamble.test.ts. (thanks @diegosouzapw) -
fix(translator): restore the terminal
message_delta+message_stopon Responses→Claude streams — the doubled-tool-args dedup (#5828) guarded the finish handler on the sharedstate.finishReason, which the openai-responses→openai leg sets first in the hub path, so the openai→claude leg dropped its terminal events and the stream ended aftercontent_block_delta. The dedup now uses a dedicatedstate.claudeFinishEmittedflag. Regression guard:tests/unit/claude-code-rendering-fixes.test.ts. (thanks @diegosouzapw) -
fix(pricing): add the Kiro
claude-sonnet-5pricing row so the newly-catalogued model (#5796) no longer reports$0.00usage. Regression guard:tests/unit/catalog-updates-v3x.test.ts. (thanks @diegosouzapw) -
fix(github): keep Copilot access-token sessions active. GitHub Copilot device-flow accounts can hold a GitHub access token plus a short-lived Copilot token without a refresh token; the proactive health check treated that as terminal
no_refresh_tokenand marked the connection expired minutes after login. The health check now keeps those sessions active, clears staleno_refresh_tokenstate, and refreshes the Copilot sub-token when needed. Regression guard:tests/unit/token-health-no-refresh-token-expired-5326.test.ts. Extracted from #5863 by @Witroch4. -
fix(kiro): bound the Claude model-id dash→dot normalization to a 1–2 digit minor so date-suffixed ids (e.g. claude-opus-4-20250514) are no longer corrupted. (thanks @voravitl)
-
fix(usage): preserve (bounded) tool definitions in request logs even when the request body is truncated, so the request-details view can still show available tools. (thanks @noir017)
-
fix(providers): route OpenAI responses-only models to
/v1/responsesinstead of 404ing on/v1/chat/completions. The curatedgpt-5.5-pro/gpt-5.4-proentries never worked (OpenAI only serves*-proreasoning models via the Responses API), and "Test all models" surfaced the same 404s. The registry entries now carrytargetFormat: "openai-responses"(reusing the existing per-model translation plumbing shared withgh/codex),DefaultExecutor.buildUrlswaps theopenaiendpoint to/responsesin lockstep (honoring custom base URLs), and a-prosuffix heuristic covers dynamically-synced ids such aso1-pro/gpt-5.2-pro(same spirit as the gh executor's/codex/irouting, 9router#102). Legacy completions-only ids (e.g.gpt-3.5-turbo-instruct) are out of scope — they are not in the catalog and OmniRoute has no legacy/v1/completionsupstream. Regression guard:tests/unit/openai-responses-only-models-5842.test.ts(8). Thanks @maikokan. (#5842) -
fix(image): keep bare codex image aliases (e.g.
gpt-5.5) resolving to the codex image pipeline even when a combo shares the same name. A chat combo namedgpt-5.5used to shadow the bare image alias inresolveImageRouteModel, hijacking/v1/images/*requests to a chat target (regression path adjacent to #5887); codex bare models are now reserved before bare-combo resolution, while non-codex aliases (e.g.gpt-image-2) remain user-shadowable (#3214/#3215 behavior preserved). Regression guard:tests/unit/image-routes-combo-edits-3214-3215.test.ts(9). (#5902 by @KooshaPari) -
fix(ci): re-green the
release/v3.8.43fast-gates queue — every PR→release was inheriting base-reds (#5798). Five distinct blockers cleared: (1) stalemodelContextOverridesentry in thecheck:db-rulesintentionally-internal allowlist (#5827 allowlisted it while the #5609 fix re-exported it fromlocalDb.ts; the re-export stays, the obsolete entry goes, classification guard re-pinned to 33); (2)LIVE_WS_ALLOWED_HOSTS/NEXT_PUBLIC_LIVE_WS_PUBLIC_URLdocumented indocs/reference/ENVIRONMENT.md(env/docs contract, from #5877); (3) the Router Backends ADR's references to the not-yet-merged registry (#5868) marked as landing-with-PR socheck:fabricated-docs --strictpasses; (4)antigravity-429-quota-tdd+middleware-header-strip-5849added to strykertap.testFiles(check:mutation-test-coverage); (5) file-size / complexity / cognitive-complexity ratchets rebaselined with justification notes — all drift measured identical on the pristine tip and this PR (net-zero). Regression guard:tests/unit/check-db-rules-classification.test.ts. (#5798) -
providers (codex image auto-routing regression): an unprefixed
gpt-5.5request from a codex-only setup (no OpenAI connection) now correctly infers thecodexprovider again — the OpenAI static-catalog short-circuit inresolveModelByProviderInferencewas preempting the codex-preference block, sogpt-5.5(added to the OpenAI catalog) stopped auto-routing to Codex image generation. Users with an active OpenAI connection are unaffected (OpenAI stays default). Regression guard:tests/unit/codex-gpt55-routing-5887.test.ts. (#5887) -
api (proxy header hygiene): upstream
x-middleware-*control headers (emitted by providers hosted behind Next.js, e.g. synthetic.new) are now stripped from proxied responses instead of forwarded verbatim — forwardingx-middleware-rewritemade Next 16 throwNextResponse.rewrite() was used in a app route handlerand return 500 despite a successful upstream call. Applies to both streaming and JSON paths. Regression guard:tests/unit/middleware-header-strip-5849.test.ts. (#5849) -
docs (pnpm global install): replaced the unsupported
pnpm approve-builds -gstep with the install-timepnpm add -g omniroute@latest --allow-build=better-sqlite3flag across README + Setup Guide (and i18n mirrors), fixing native-build approval for pnpm v11 global installs. (#5554) -
dashboard (token badge): the red "Token Expired" connection badge no longer flashes for OAuth refresh-capable providers (Antigravity/Gemini) whose access token merely lapsed but is auto-refreshed — it now shows only when the connection is terminally expired (
testStatus === "expired"). Continuation of #5326. Regression guard:tests/unit/ui/connection-row-token-badge-5836.test.tsx. (#5836) -
db (auto backup toggle): full pre-write SQLite backups now honor the persisted
backup.autoBackupEnableddashboard setting — previously only theDISABLE_SQLITE_AUTO_BACKUPenv var was checked, so disabling auto-backup in the UI had no effect and ~70MB pre-write snapshots kept firing. Manual and pre-restore backups still always run. Regression guard:tests/unit/db-backup-autobackup-setting-5871.test.ts. (#5871) -
providers (auto/ routing for custom providers): custom OpenAI-/Anthropic-compatible providers (dynamic
*-compatible-*connection IDs) are no longer excluded fromauto/routing — the Auto-Combo virtual factory previously skipped any connection whose provider was absent from the static registry. It now falls back to the connection'sdefaultModel. Regression guard:tests/unit/auto-custom-provider-5873.test.ts. (#5873) -
middleware (hook sandbox): operator-authored pre-request hook code now runs inside a hardened Node
vmsandbox (minimal context, no ambient globals/process.env, execution timeout, norequire) instead ofnew Function()in the main process — closing the Hard Rule #3 / SonarCloud S1523 exposure. Regression guard:tests/unit/middleware-hook-sandbox-5872.test.ts. (#5872) -
mcp-server (auth forwarding): the per-caller MCP identity forwarded via
withMcpHttpAuthContextnow wins over the staticOMNIROUTE_API_KEYenv fallback in the internal-fetch helpers (apiFetch,omniRouteFetch) — previously the env key was spread after the forwarded headers and clobbered the caller'sAuthorization. Regression guard:open-sse/mcp-server/__tests__/httpAuthContext.test.ts. (#5819) -
dashboard (Modal provider — two-field auth): the Modal provider connection form now exposes two fields — Token ID + Token Secret — instead of a single API-key input, since Modal authenticates with
Authorization: Bearer <token-id>:<token-secret>. The dashboard combines the two fields into theid:secretcredential before saving (combineModalCredential, trims both parts), while a value pasted in the legacy single-field format keeps working verbatim (empty secret → passthrough), so existing saved connections need no migration; the key-help link points at Modal's token settings. Regression guard:tests/unit/modal-credential-combine.test.ts(5). (#5881, closes #5446) Follow-up: the Validation Model Id field is now pre-filled for Modal with the same model the server-side validator probes (Qwen/Qwen3-4B-Thinking-2507-FP8, shared viaMODAL_DEFAULT_VALIDATION_MODEL_IDinsrc/shared/constants/modal.ts), closing the last checklist item of #5446. Regression guard:tests/unit/modal-validation-model-prefill.test.ts. -
api (chat completions — early SSE keepalive gate): the
/v1/chat/completionsroute wrapped the response in the early-stream keepalive wheneverstreamwas not explicitlyfalse, so a client that omittedstreamand asked for JSON (Accept: application/json) could receive premature SSE framing. The keepalive wrapper is now gated on an explicitstream: truein the body or an Accept header that forces SSE (acceptHeaderForcesStream); the parsed body is passed to the chat handler untouched, so the actual stream/JSON framing stays decided bychatCore/resolveStreamFlag— preserving OmniRoute's legacy streaming default whenstreamis omitted and the per-keystreamDefaultMode: "json"opt-in. Regression guard:tests/unit/chat-combo-live-test.test.ts("returns JSON without early SSE framing when stream is omitted and Accept is application/json"). (#5866 by @rdself) -
fix(github): drop a trailing assistant prefill before dispatching to GitHub Copilot chat to avoid 400 errors. (thanks @baslr)
-
fix(oauth): prevent cross-IdP account overwrites by disambiguating OAuth connections on
usernamewhen present, not email alone. (thanks @KunN-21) -
fix(mitm): best-effort revert privileged /etc/hosts entries on exit when a sudo password is cached, instead of always leaving orphaned state. (thanks @manhdzzz)
-
providers (Kiro — Claude Sonnet 5): the Kiro provider's model catalog was missing
claude-sonnet-5, so the model could not be selected or routed even on accounts that already had access to it ("claude-sonnet-5 is not supported"). Added the model to the Kiro registry (open-sse/config/providers/registry/kiro/index.ts) as a 1M-context / 128K-output Claude model, mirroring the existing Claude entries; the registrymodels[]feeds both the model selector and the live CodeWhispererListAvailableModelsfallback, so the model is now selectable and routable. Regression guard:tests/unit/kiro-claude-sonnet-5-2267.test.ts. (thanks @openbioinfo) -
settings (model aliases — self-heal after restart): the Settings → Routing page showed "No exact-match aliases configured" after a server restart even though the aliases were persisted in the DB. Aliases are held in a module-local
_customAliasesmap inmodelDeprecation.tsthat the boot path hydrates, but Next.js compiles the app-route module graph separately from the startup graph (the same webpack chunk-splitting class as #5312), so theGET /api/settings/model-aliaseshandler read a different, un-hydrated copy. The handler now self-heals: when its in-memory alias map is empty it readssettings.modelAliasesfrom the DB (via the existinggetSettings()db module — no raw SQL in the route) and repopulates the map, so the UI reflects the persisted aliases on the first GET after a restart. Follow-up: the root cause is now also fixed — the_customAliasesstore inmodelDeprecation.tsis backed byglobalThis(key__omniroute_customAliases__), so the startup and app-route module graphs share one store and the route reads the boot-hydrated aliases directly (the DB self-heal remains as a harmless fallback), mirroring the sameglobalThissingleton pattern already applied tothinkingBudget.ts/backgroundTaskDetector.ts(#5312). Regression guards:tests/unit/model-aliases-settings-route-selfheal.test.ts+tests/unit/model-aliases-globalthis-5777.test.ts. (#5777 — thanks @jleonar2) -
providers (grok-cli token auto-refresh): grok-cli OAuth tokens were never proactively refreshed before their real expiry.
mapTokenshardcodedexpiresIn: 21600(6 h) regardless of the token's actual lifetime, so the persistedexpiresAtwas always "now + 6 h" and the proactivetokenHealthChecksweep (refresh whenexpiresAt - now < 5 min) fired 6 h after import instead of shortly before the token really expired.mapTokensnow computesexpiresInfrom the authoritativeexpires_atfield in~/.grok/auth.json(ISO → epoch-seconds) with a fallback to the JWTexpclaim (payload-only decode, no signature trust); the hardcoded21600is kept only when neither is present. An already-expired token (realexpires_at/expin the past) is now clamped to a positiveexpiresInviaMath.max(1, …), so the import route stores a near-futureexpiresAtand AutoCombo refreshes the connection instead of reading a past date and excluding it outright. Regression guards: 5 cases intests/unit/grok-cli-oauth.test.ts(JWTexp, JSONexpires_at, the21600fallback, and the two expired-token clamps). (#5775 — thanks @Chewji9875) -
compression (CCR retrieve via MCP HTTP): the
omniroute_ccr_retrieveMCP tool returned"CCR block not found"for blocks stored earlier in the same session when called over the MCP HTTP transports (SSE / Streamable HTTP), e.g. from OpenCode in a Docker deployment. Compression stores each block keyed by the API-key principal (String(apiKeyInfo.id)), but the tool resolved the caller viaextra.authInfo.clientId— which the MCP SDK never populates for API-key auth — so it fell back to"anonymous"and the compound store-key never matched. The retrieve tool now resolves the caller's API-key id from the MCP HTTP auth context (httpAuthContext) using the samegetApiKeyMetadatalookup used at storage time, so retrieval matches storage. Cross-tenant IDOR isolation is preserved: a different key resolves to a different id → miss; no key → the anonymous bucket only. Regression guard:tests/unit/compression/ccr-mcp-principal-5649.test.ts(extraction, distinct-principal isolation, fail-closed, end-to-end store→retrieve). (#5649) -
compression (context-editing telemetry): streaming responses now record Context Editing savings. Anthropic surfaces
context_management.applied_edits[]on the finalmessage_deltasnapshot of an SSE stream, but the streaming reconstruction (buildStreamSummaryFromEvents→ Claude branch) droppedcontext_managemententirely and no telemetry hook was wired into the streaming finalizer — so the delegated server-side context-clear savings (cleared_input_tokens/cleared_tool_uses) surfaced under enginecontext-editingin compression analytics only for non-streaming responses. The collector now preservescontext_managementfrom the final snapshot (last-writer-wins), andonStreamCompletemirrors the non-streamingrecordContextEditingTelemetryHook(best-effort, Claude-only, HTTP 200 only). Purely additive telemetry — no payload mutation, no new env flag, no behavior change when the stream carries nocontext_management. Regression guard:tests/unit/context-editing-streaming-telemetry.test.ts(3). gaps v3.8.42 — T01 (5.1). -
proxy (relay test diagnostics): the Proxy Pool "Test" button showed a bare "failed" with nothing in the server logs when a relay (Vercel / Deno / Cloudflare) responded with a non-200 — e.g. a
401from an auth-token mismatch after aSTORAGE_ENCRYPTION_KEYrotation. The relay success-path response setsuccess: falsebut carried noerrorfield, so the dashboard had no reason to show and the server logged nothing. The test now returns an actionableerror(the HTTP status, plus an auth/encryption-key hint on401/403) and logs the failure server-side; the SOCKS5/HTTP proxy path now logs its failures too. Shaping extracted tobuildRelayTestResultwith a regression guard (tests/unit/proxy-relay-test-error-5716.test.ts). Note: this surfaces why a relay fails — it does not repair a genuinely broken/misconfigured relay. (#5716) -
fix(dashboard): add error boundaries for the Combos and MITM Proxy pages so a render error shows a recoverable fallback instead of a blank page. (thanks @wahyuzero)
-
providers (onboarding wizard — unsupported validation): adding a provider whose credentials have no live validator (LMArena, PiAPI, …) failed silently in the Add-Provider wizard. The
/api/providers/validateendpoint returnsHTTP 400 + { unsupported: true }for these (#5565/#5567), but the wizard'svalidateOnboardingApiKeyran it throughexpectOk, which threw on the non-200 — so the flow jumped to the error step and the connection was never created. The wizard now treatsunsupported: trueas a non-blocking "can't verify" and proceeds to save, mirroringAddApiKeyModal. Regression guard added totests/unit/provider-onboarding-wizard.test.ts. (related to #5692) -
dashboard (Quick Start step 1): the Quick Start "Create API key" step told users to "Go to Endpoint → Registered Keys" and linked to
/dashboard/endpoint, but API keys are created on the API Manager page (/dashboard/api-manager, sidebar "API Keys") — the Endpoint page has no "Registered Keys" section, so users followed the link and could not find where to create a key. Step 1 now reads "Go to API Keys" and links to/dashboard/api-manager. Regression guard:tests/unit/ui/quick-start-api-keys-link-5695.test.ts. (#5695) -
providers (DashScope/Alibaba setup link): the "Get API key" link for the Alibaba and Alibaba (China) providers pointed at the bare API host (
dashscope-intl.aliyuncs.com/dashscope.aliyuncs.com), which returns 404 in a browser — API hostnames have no homepage. Repointed to the consoles where keys are actually issued:bailian.console.alibabacloud.com(international) anddashscope.console.aliyun.com(China). Same class as #5572/#5574/#5576; regression guard added totests/unit/provider-setup-links-5572.test.ts. (#5665) -
thinking / runtime-config (module-graph fix): operator-configured proxy settings that are hydrated at boot but read per-request were silently ignored in production. Next.js compiles
instrumentation.ts(boot hydration viaapplyRuntimeSettings/ restore hooks) as a separate webpack module graph from the app-route / open-sse executors, so a module-locallet _configsingleton is duplicated — the boot copy is hydrated but the request path reads a different, un-hydrated copy. Live VPS validation proved the Thinking-Budget hydration ran to completion at boot yetbase.tsstill saw thepassthroughdefault (this is why #5312 fix A stayed broken even after the boot-wiring fix). Fixed by backing the singletons withglobalThis(the patternsystemPrompt.tsalready uses for the Global System Prompt, #2470), so all module-graph copies share one instance:thinkingBudget.ts(the dashboard Thinking-Budget mode now reaches the executor),backgroundTaskDetector.ts(the opt-in background-model degradation now actually fires on requests), andsystemTransforms.ts(operator pipeline overrides now reach the request path).payloadRules.tswas already safe (it lazily self-loads from the DB per request, #2986). Regression guards:tests/unit/thinking-budget-globalthis-5312.test.ts+tests/unit/runtime-config-globalthis-5312.test.ts(assert globalThis-backed sharing; a module-localletfails them). (#5312) -
thinking (Claude OAuth): restore the proxy-level Thinking-Budget config on startup. The dashboard mode (
auto/custom/adaptive) is persisted undersettings.thinkingBudget, but the boot-time hydration (hydrateThinkingBudgetConfig) was only wired intosrc/server-init.ts— an unused module that never runs in production — so the operator's choice silently reverted to thepassthroughdefault on every restart (#5312 fix A was non-functional, even though its direct unit test passed). The hydration now runs in the real boot path (src/instrumentation-node.ts), alongside the Global System Prompt restore. Surfaced by live Anthropic-OAuth validation on the VPS. Regression guard:tests/unit/thinking-budget-boot-wiring-5312.test.ts(asserts the production boot module calls the hydration, not just the function in isolation). (#5312) -
translator/chatcore (hardening): re-apply two defensive review-fixes that were dropped in a branch rebuild before #5661 / #5662 landed. (1)
mergeConsecutiveSameRoleContents(OpenAI→Gemini) now shallow-copies each entry and itspartsarray instead of pushing the input reference, so the consecutive-same-role merge never mutates the caller's objects. (2)defaultClaudeToolType(Claude tool defaults) now passes any non-object array entry (null/ primitive) through unchanged instead of spreading it into a fabricated{ type: "custom", … }tool. No behavior change on real payloads (Gemini contents are freshly built; Claude tools are always objects); both properties are now locked by regression tests intests/unit/translator-gemini-consecutive-role-2191.test.tsandtests/unit/claude-tool-type-default-2195.test.ts. -
providers (grok-cli): truncate the tool list when it exceeds a provider's hard limit, so grok-cli (
cli-chat-proxy.grok.com, max 200 tools) no longer rejects requests withMaximum tools limit reached. Adds a proactivePROVIDER_TOOL_LIMITSmap (grok-cli: 200, consulted before the reactive cache), a corrected limit-parsing regex that captures the stated maximum (200) instead of the supplied count (427), and removes the broken< MAX_TOOLS_LIMITtruncation gate so truncation now fires whenevertools.lengthexceeds the effective limit. Regression guard:tests/unit/tool-limit-detector.test.ts. (#5563 — thanks @Chewji9875) -
resilience (antigravity): record model lockout for Antigravity
429 rate_limit_exceedederrors. Antigravity's"Resource has been exhausted (e.g. check quota)."text was matched by overly broadQUOTA_PATTERNSand misclassified asQUOTA_EXHAUSTED, so the combo retry path was skipped (providerExhausted) and the model was never cooled down. Classification now prefers the structured error code —classifyErrorText(structuredError?.code || errorText)— so arate_limit_exceededcode is treated as a transient rate-limit (not quota), and the two broad patterns (/resource.*exhaust/i,/check.*quota/i) were replaced with Antigravity-specific ones (individual quota reached,enable overages). (#5579 — thanks @Chewji9875) -
providers (OpenAI-compatible): Codex MCP /
tool_searchdeferred discovery (andapply_patch) now works through a Custom OpenAI-compatible provider. When such a provider received a Responses-API-shaped request that carried MCP /tool_searchtools, OmniRoute downgraded it to/chat/completions, which drops the deferred tool-discovery mechanism — so the MCP namespaces never surfaced to the model andapply_patchwas mis-handled as a JSON tool. The executor now detects a Responses-shaped request (input/previous_response_id/max_output_tokens/reasoning) that carriesnamespace/tool_search*tools and routes it to the upstream/responsesendpoint natively instead of downgrading (it can also be forced viaproviderSpecificData._omnirouteForceResponsesUpstream). This is a distinct code path from the official Codex OAuth backend (#3033 / #4539, which the earlier fix never touched). Regression guard:tests/unit/executor-default-base.test.ts. Thanks to @KooshaPari for the fix. (#5483) -
dashboard (routing): selecting the fusion strategy on the Global Routing defaults tab now reveals fusion-specific config instead of only the generic resilience fields. Fusion's engine knobs —
judgeModel(the model that synthesizes the panel answers) andfusionTuning(minPanel/stragglerGraceMs/panelHardTimeoutMs) — already existed in the schema and the per-combo editor, but the Global Routing tab never surfaced them, so picking "fusion" there was effectively a no-op. The fields are now shown (extracted into a newFusionDefaultsFieldscomponent). Voting / aggregation-mode / per-provider-weight are intentionally not shown — those don't exist in the fusion engine. Regression guard:tests/unit/ui/combo-defaults-fusion-5598.test.tsx. (#5598) -
dashboard (free proxy pool): the free proxy pool "Sync All" no longer fails silently with
Total: 0. Three fixes: (1) the IPLocate source fetched…/protocols/<proto>.jsonand parsed it as JSON, but the upstream list is plain text (<proto>.txt, oneip:portper line) — every protocol 404'd / failed to parse; it now fetches.txtand parses the line list. (2) The sync route isolates each source in its own try/catch, so one provider throwing (e.g. a TLS handshake failure) no longer aborts the whole sync — the working sources still populate the pool. (3) The UI now surfaces the per-source errors the route already returns, instead of discarding the response, so a partial/empty sync explains itself. Regression guards:tests/unit/free-proxy-providers.test.ts,tests/unit/proxy-pool-sync-4878.test.ts,tests/unit/free-pool-tab.test.tsx. (#5595) -
dashboard (memory engine): the memory engine status page no longer mixes English and Portuguese. The embedding / vector-store / rerank status detail strings were hardcoded in Portuguese in the backend (
resolveEmbeddingSource,engineStatus), e.g.auto: nenhuma fonte de embedding disponívelandsqlite-vec ativo, dim=…, while the surrounding UI labels render from the English i18n bundle — so an English user saw a half-translated page. The backend detail strings are now English (auto: no embedding source available,sqlite-vec active, dim=…, etc.), matching the rest of the page. Regression guard:tests/unit/memory-engine-status.test.ts. (#5596) -
providers (cline): stop falsely mapping valid Cline (OAuth) responses to
502 empty_choices+ account cooldown.detectMalformedNonStreamonly recognizedchoices[].message.contentas a string, but some OpenAI-compatible upstreams — Cline via OAuth among them — returncontentas an array of Anthropic-style text blocks inside an OpenAI envelope. A non-empty response (recvBytes > 0) was therefore classified asempty_choicesand turned into a 502 that also cooled the account down. The malformed-response detector now also treats a content array carrying at least one non-emptytextblock as real output. Regression guard:tests/unit/diagnostics.test.ts. (#5559) -
embedded services (Windows): fix CLIProxyAPI install failing instantly with
spawn unzip ENOENTon Windows. The binary extractor spawnedunzip, which is not a Windows system command — it only ships inside Git for Windows'usr/bin, a directory Node'sspawnPATH never sees, so even users with Git installed hit the error. On Windows the extractor now uses PowerShell's built-inExpand-Archive(viaexecFileAsync, no shell — paths pass as a single non-interpreted arg, with''-escaping +-LiteralPathas defense in depth); other platforms keep usingunzip. This is distinct from #5379 (that wasnpm.cmdneedingshell: true). Regression guard:tests/unit/binary-manager-extract-zip-5590.test.ts. (#5590) -
storage (daemon): fix a Node.js out-of-memory crash on startup when
storage.sqlitegrows large (~170 MB+). The boot-time call-log cleanup (cleanupExpiredLogs→rotateCallLogs) ran two unboundedSELECT … FROM call_logs … .all()queries —listReferencedArtifacts(every artifact path) anddeleteCallLogsBefore(every id before the retention cutoff).node:sqlite'sStatementSync.all()materializes the entire result set as JS objects at once, so on a large table the V8 heap blew up and the process crashed before binding (FATAL ERROR: … heap out of memory, native framenode::sqlite::StatementSync::All). Both queries now page throughcall_logsin bounded 5 000-row chunks (newsrc/lib/usage/callLogsBoundedQueries.ts), keeping peak memory flat regardless of table size — no more manual--max-old-space-sizebump required. Regression guard:tests/unit/call-log-oom-unbounded-5618.test.ts. (#5618) -
dashboard (provider setup): fix three provider setup links that pointed at 404 pages. Ollama Cloud / ollama-search linked to
ollama.com/settings/api-keys→ corrected toollama.com/settings/keys(the page moved; Ollama Cloud is a real keyed service, so the field stays). SearchAPI linked to the baresearchapi.io/docs(404) →searchapi.io/docs/google. You.com linked toyou.com/docs/search/overview(404) →you.com/business/api/(the developer portal). All three replacements were verified live. Regression guard:tests/unit/provider-setup-links-5572.test.ts. (#5572, #5574, #5576) -
providers (AI/ML API): the model-import step now loads the live AI/ML API catalog (400+ models) instead of falling back to a stale 6-model seed. The registry had no
modelsUrl, so the route silently used the bundled catalog with an "API unavailable — using local catalog" warning even when the key was valid. AI/ML API exposes its full catalog at the public, auth-freehttps://api.aimlapi.com/modelsendpoint (a bare array of{ id, type, info }, distinct from the OpenAI-compat/v1/models); it's now wired into the models route's discovery config, with the bundled catalog kept as the offline fallback. Regression guard:tests/unit/provider-models-route.test.ts. (#5570) -
providers (CablyAI): mark CablyAI deprecated —
cablyai.comno longer resolves (DNSNXDOMAIN, verified 2026-06-30); the domain is gone. The provider is removed from the models-route discovery config so the import step returns a clean error instead of an unhandled 500 crash (the dead-domain fetch threw with no local-catalog fallback), and the registry entry now carriesdeprecated: true/riskNoticeVariant: "deprecated"so the dashboard flags existing connections (same treatment as the shut-downglhf/kluster.aigateways). Regression guard:tests/unit/provider-models-route.test.ts. (#5568) -
dashboard (provider add): non-LLM search/agent providers no longer fail the model-import step with a red
Provider <id> does not support models listing. Jules (Google Labs coding agent), linkup-search (Linkup web search), ollama-search (Ollama Cloud web search — distinct from the local Ollama LLM), and searchapi-search (SearchAPI SERP) have no/v1/modelsendpoint, so the import surfaced a failure for expected behavior. Each now ships a small static catalog of its selectable capability ids — Linkup'sfast/standard/deepsearch depths, SearchAPI'sgoogle/bing/youtube/… engines, a single Jules/Ollama-web-search entry — so the import step returns a usable list (source: local_catalog) instead of an error. Regression guard:tests/unit/provider-models-route.test.ts. (#5569, #5571, #5573, #5575) -
dashboard (provider add): providers without a live key/cookie validator (e.g. LMArena (Free), PiAPI) can now be saved. The Add-connection modal treated the backend's
"Provider validation not supported"response as a hard Invalid state and blocked Save entirely, leaving those providers impossible to add. The validate route now returnsunsupported: truealongside the message, and the modal treats that as a non-blocking warning — the "Check" badge still shows "validation not supported" (informational), but Save persists the credential as-is. Regression guards:tests/unit/ui/add-api-key-modal-unsupported-save-5565.test.tsx(Save proceeds) andtests/unit/providers-validate-route.test.ts(wire-format). (#5565, #5567) -
providers (codex): fix the Codex Responses WebSocket path (
/v1/responses), which regressed in v3.8.40 with a client-visibleInvalid JSON bodyand bypassed the configured proxy. (1) #5591 — PR #5237 bumped the impersonation TLS profile tochrome_149, butwreq-js@2.3.1only supports up tochrome_147; the unknown profile produced a degenerate fingerprint and ChatGPT rejected the upstream upgrade. The Codex WS path is reverted to the provenchrome_142(the v3.8.39 value), and the over-bumpedgrok-web/claude-webprofiles (masked by their circuit-breaker but silently dropping TLS impersonation) are restored tochrome_146. A new regression guard asserts every configuredchrome_*profile exists in the installedwreq-jstypings (tests/unit/tls-profiles-valid-5591.test.mjs). (2) #5611 — the upstreamwreq-js.websocket()connect ignored the Proxy Registry, so a no-direct-egress Docker container failed with a DNS error; the prepare route now resolves the Global/provider proxy and threads it through to the WS connect. Regression guard intests/unit/responses-ws-proxy.test.mjs. (#5591, #5611) -
providers (GLM): GLM 5.1 / 5.2 now keep the
systemrole instead of having the system prompt folded into the first user turn.roleNormalizer.tsmatched everyglm*id with a blanketstartsWith("glm")/startsWith("glm-")prefix, so the next-generation models — which z.ai documents as supporting thesystemrole (GLM > 5.0) — were normalized as if they rejected it, degrading instruction-following. The matcher is now version-aware: it strips the system role only for bareglm, the 4.x family, and the 5.0 generation, and preserves it forglm-5.1/glm-5.2(and the Fireworksglm-5p1point alias). The ZenMux vendor-prefixedz-ai/glm-*compressed-history rule and the ERNIE rule are unchanged. Regression guards intests/unit/role-normalizer.test.ts. (#5610) -
Security hardening follow-ups (v3.8.15): the
auth_tokencookie now sets an explicit 30-daymaxAgeso sessions persist as intended (Seg3); the management bootstrap warns at boot whenINITIAL_PASSWORDis left at the insecureCHANGEMEdefault (Seg2); VS Code path-token endpoints (/api/v1/vscode/raw/[token]) emit a once-per-process security warning since the API key travels in the URL and can leak via logs/proxies (Seg4); the system version route resolves the real global install path vianpm root -ginstead of a hardcoded/app(Bug3); and auto-update mode detection segment-matchesnode_modulesinstead of substring-matching, eliminating false "global install" positives (Bug1). -
fix(cli): rename the Node process title to
omnirouteso it shows correctly in ps/htop. (thanks @waguriagentic) -
dashboard (model picker): guard against null model-alias values so opening Create Combo for a custom provider node no longer crashes.
ModelSelectModal's custom-provider branch filteredmodelAliasesentries with a rawfullModel.startsWith(...), which threw aTypeErrorwhenever an alias value wasnull/undefined(a stale/partial entry persisted to settings). The filter/map logic is extracted into a newbuildNodeAliasModelshelper (mirroring the sibling passthrough-alias guard, #485) that requirestypeof fullModel === "string"before calling.startsWith. Regression guard:tests/unit/model-select-null-alias-guard-2247.test.ts. (thanks @wahyuzero) -
fix(translator): strip orphaned tool results (results with no matching tool call) across request formats to avoid upstream 400s. (thanks @warelik)
-
fix(kiro): stop injecting a placeholder user turn on trailing tool-result turns so agentic loops aren't disrupted. (thanks @jetmiky)
-
fix(translator): prevent doubled tool arguments in OpenAI-to-Claude responses (duplicate finish_reason guard + string tool-input passthrough). (thanks @vishalrajv)
-
codex (agent goal streams): protect long-running agent goal streams so extended agent runs are no longer cut off prematurely. (#5772 — thanks @nguyenxvotanminh3)
-
sse (zero-width markers): strip zero-width markers from streamed responses, matching the non-streaming path so streamed output is byte-clean parity. (#5857 — thanks @DKotsyuba)
-
usage (om-usage endpoint): restore the
om-usageHTTP endpoint. (#5859 — thanks @Witroch4) -
sse (stream readiness): tune adaptive stream-readiness timeouts so slow-first-token upstreams are handled more reliably. (#5767 — thanks @nguyenxvotanminh3)
-
security (provider node URL): harden provider node URL validation. (#5760 — thanks @nguyenxvotanminh3)
-
cli (Windows doctor): correct
rootDirresolution indoctor.mjson Windows. (#5845 — thanks @arssnndr) -
providers (Antigravity): fix a 429 hang on credit exhaustion and apply a precise reset-time model lockout instead of stalling — cleaned re-implementation of #5823. (#5846 — thanks @Chewji9875 / @diegosouzapw)
-
providers (qwen-web): unblock the validator and chat completion — the retired endpoint is replaced and the missing SPA version header is now sent. (#5855 — thanks @janeza2)
-
providers (kimi-web): migrate to the
www.kimi.comConnect-RPC API afterkimi.moonshot.cnwas retired. (#5858 — thanks @janeza2) -
dashboard (CSRF): unify the dashboard CSRF origin fallback so dynamic/public origins validate correctly. (#5856 — thanks @rdself)
-
db (health check interval): preserve
healthCheckInterval=0across connection create/update instead of coercing it to a default. (#5822 — thanks @atomlong) -
sse (claude→codex streaming): stop the reasoning-summary drop and duplicated deltas on claude→codex streaming — reasoning snapshots are now synthesized in TRANSLATE mode and the sequence-number watermark is tracked per-stream (#5786). (#5832 — thanks @diegosouzapw)
-
deps (runtime): add the missing runtime dependencies
@toon-format/toonandsafe-regexso the published package resolves them at runtime. (#5771 — thanks @chirag127) -
system (Windows auto-update): route in-app auto-update
npmcalls through the win32 shell helper so updates run correctly on Windows (#5542). (#5797 — thanks @diegosouzapw) -
dashboard (validation badge): show a neutral badge for unsupported validation and make OAuth error messages clickable links (#5442, #5486). (#5795 — thanks @diegosouzapw)
-
providers (metadata): correct stale/broken provider metadata (#5487, #5461, #5534, #5470). (#5790 — thanks @diegosouzapw)
-
providers (local-catalog imports): import intentional local-catalog-only providers instead of surfacing a 502 (#5460, #5465). (#5787 — thanks @diegosouzapw)
-
proxyfetch (failover): skip the failover retry for non-replayable request bodies so a consumed stream isn't re-sent empty. (#5770 — thanks @Ardem2025)
-
batch (recovery): persist batch item checkpoints during recovery so an interrupted batch resumes from where it left off. (#5753 — thanks @ag-linden)
-
memory (Qdrant): enabling Qdrant now activates it as the retrieval engine (the
autodefault never selected it) and adds inline guidance (#5597). (#5741 — thanks @diegosouzapw) -
chat (non-streaming aggregation): harden non-streaming SSE aggregation against malformed upstream event sequences. (#5746 — thanks @rdself)
-
sse (cooldown parsing): the anti-thundering-herd guard now tolerates numeric-epoch cooldown values. (#5747 — thanks @diegosouzapw)
-
api (body size): raise the LLM API payload limit for the responses routes so larger requests aren't rejected. (#5652 — thanks @JxnLexn)
-
providers (HuggingChat): fix HuggingChat web-session routing (#5592). (#5592 — thanks @backryun)
-
sse (heap pressure): bound the chat hot-path heap — pressure-aware admission, response cap, and clone reductions — to avoid OOM under load (#5152). (#5425 — thanks @josevictorferreira)
-
providers (M365 Copilot): validate M365 Copilot web credentials. (#5432 — thanks @skyzea1)
-
providers (chatgpt-web): restore the dot-form Pro model ids. (#5549 — thanks @Thinkscape)
-
security (error stacks): avoid rendering error stacks in responses. (#5624 — thanks @KooshaPari)
-
security (linkify): restrict
linkifyTexthrefs to an explicithttp(s)scheme allowlist. (#948d2d7 — thanks @diegosouzapw) -
translator (doubled tool args): prevent doubled tool-call arguments in the OpenAI→Claude translation path. (#5828 — thanks @diegosouzapw)
-
translator (orphaned tool results): strip orphaned tool-result turns across request formats so an upstream doesn't reject a tool result with no matching call. (#5805 — thanks @diegosouzapw)
-
translator (Gemini/Claude hardening): re-apply lost defensive hardening for the Gemini merge path and Claude tool defaults. (#5706 — thanks @diegosouzapw)
-
kiro (tool-result turns): stop injecting a placeholder user turn on tool-result turns, which corrupted otherwise-valid Kiro conversations. (#5807 — thanks @diegosouzapw)
-
providers (Kiro catalog): add
claude-sonnet-5to the Kiro model catalog. (#5796 — thanks @diegosouzapw) -
oauth (connection disambiguation): disambiguate OAuth connections on username so two different identity providers no longer overwrite each other. (#5803 — thanks @diegosouzapw)
-
github (Copilot prefill): drop the trailing assistant prefill for Copilot chat, which some Copilot models rejected. (#5802 — thanks @diegosouzapw)
-
mitm (hosts cleanup): clean up privileged
/etc/hostsentries on exit when possible so a crashed/interrupted run doesn't leave stale redirects behind. (#5808 — thanks @diegosouzapw) -
dashboard (model picker): guard null
modelAliasesvalues in the model picker so a connection with no aliases no longer throws. (#5792 — thanks @diegosouzapw) -
dashboard (error boundaries): add error boundaries for the Combos and MITM Proxy pages so a render error no longer blanks the whole dashboard. (#5788 — thanks @diegosouzapw)
-
cli (process title): rename the running process title to
omniroute. (#5791 — thanks @diegosouzapw) -
compression (context-editing telemetry): record Context Editing telemetry on the streaming path, not just the non-streaming path. (#5761 — thanks @diegosouzapw)
-
security (v3.8.15 hardening follow-ups): land the Seg2/Seg3/Seg4/Bug3 hardening follow-ups from the v3.8.15 security review. (#5512 — thanks @diegosouzapw)
📝 Maintenance
-
docs (architecture): add
docs/architecture/ROUTER_BACKENDS.md— an ADR pinning down how the routing engines (tsnative,bifrost,cliproxy,9router, VibeProxy-compatible) relate to each other along two orthogonal axes (lifecycle: in-process / supervised / external vs. relay selection backend), answering the architecture questions raised in #5603 (backend interface model, why CLIProxy spawns a process, feature-flag swapping, actionable route-contract errors). The typed router-backend registry the ADR describes lands separately via #5868. (#5891) -
tests (autoCombo): stabilize the
getTaskFitnessWithSource identifies fitness_table as source for known modelsunit test, which flaked whenever the models.dev capabilities DB was populated in CI: the fixture modelgpt-4ois a real models.dev catalog id, so the fitness resolution chain returnedmodels_dev_tierinstead of the expected staticfitness_tablesource. The fixture now usesclaude-sonnet(a shortened alias absent from the models.dev catalog, matching the sibling resolution-chain test), which deterministically falls through to the static table — the exactsourceand score assertions are preserved (0.95=FITNESS_TABLE.coding["claude-sonnet"]). (#5890) — thanks @KooshaPari -
oauth (dead-code removal): delete the superseded legacy OAuth service-class hierarchy under
src/lib/oauth/services/. The live OAuth flow runs throughsrc/lib/oauth/providers.ts+src/lib/oauth/providers/(wired into the genericoauth/[provider]/[action]route); the old per-providerclass *Service extends OAuthServiceimplementations plus their barrel had zero production or test references. Removedoauth.ts(base class),openai.ts,github.ts,claude.ts,codex.ts,antigravity.ts,qwen.ts,qoder.ts, and theindex.tsbarrel (−1559 LOC). Kept the three still-live files that routes import directly by path:kiro.ts(Kiro import/exchange routes),cursor.ts(Cursor import route), andcodexImport.ts(utility fns for the Codex bulk-import route). Proven safe bytypecheck:corestaying green (any live reference would fail the build) + a filesystem guardtests/unit/oauth-legacy-services-removed.test.tspinning the removal against re-introduction. Salvage of the closed PR #5039. gaps v3.8.42 — T10 (5.7). -
refactor (god-file decomposition): extracted pure leaf modules across db, sse, usage, api, memory, evals, models, resilience, and dashboard god-files (types/mappers/helpers/pure-transform leaves; behavior-preserving, test-guarded): db/providers, db/proxies, db/models, db/settings, usageAnalytics, migrationRunner (#5714, #5717, #5705, #5709, #5722, #5721); sse openai-to-gemini / cursor-protobuf / rate-limit-headers / reasoning-tag (#5824, #5794, #5736, #5734); usage families / callLogs / usageHistory / providerLimits (#5782, #5725, #5728, #5730); api provider-models discovery / unified-catalog (#5758, #5699); memory retrieval scoring (#5733); evals golden-set suites (#5740); modelsDevSync transform layer (#5743); resilience settings split (#5745); dashboard sidebarVisibility split (#5683); executor shared-utility dedup + tests (#5720 — thanks @pizzav-xyz). — thanks @diegosouzapw
-
chore (Bun script runner): adopt Bun
1.3.10as a locked, allow-listed build/dev script runner for a small set of validated TS gate/generator scripts (Node stays the published runtime): locked runtime dependency, CI script-checks + validated-scripts run under Bun, and a bun-safe pack validator. (#5615, #5617, #5612, #5643 — thanks @KooshaPari; docs #5703 — thanks @diegosouzapw) -
docs (sync & housekeeping): i18n CHANGELOG mirror sync for the [3.8.43] section (#5789); MCP tool count synced to 95 + routing-strategy count (#5732); README faster/leaner install notes, refreshed metrics/badges, 17-strategy + Quota-Share listing, provider counts, and grammar fixes (#5713, #5738 — thanks @chirag127); security docs for banned-keyword/account-ban detection (#5756) and the full LOCAL_ONLY route set + GHSA advisory + audit path (#5748); relay backend-routing contract clarification (#5621 — thanks @KooshaPari); release-freeze scoped to
/generate-releaseonly (#5839);.editorconfigrepository standards (#5879 — thanks @shiva24082). — thanks @diegosouzapw -
test/ci (stabilization & ratchets): guard the tsx/esm→esbuild boot transform (#5773); align t3-web web-session metadata (#5835); repoint the sidebar quota-share placement scan (#5711); lightweight health probe for batch e2e (#5651 — thanks @KooshaPari); make release-green pre-flight gates visible + bounded (#5644); stabilize nightly-mutation (tap.testFiles drift guard + anti-flake eps) (#5682); close the QG v2 tail (#5681); normalize check route paths on Windows (#5613 — thanks @KooshaPari); pass
sonar.projectVersionto the SonarQube scan (#5880); plus strykertap.testFilesregistration, compression-studio smoke re-anchoring,rtk_discoverde-flake, and v3.8.43-cycle ratchet rebaselines (deadExports 225→227, complexity 1981→1982, cognitive-complexity 842→845, eslintWarnings 4121→4158→4199). — thanks @diegosouzapw -
refactor (oauth): remove dead legacy OAuth service classes. (#5838 — thanks @diegosouzapw)
🙌 Contributors
Thanks to everyone whose work landed in v3.8.43:
| Contributor | PRs / Issues |
|---|---|
| @ag-linden | #5753 |
| @Ardem2025 | #5770 |
| @arssnndr | #5845 |
| @atomlong | #5822 |
| @backryun | #5592 |
| @baslr | direct commit / report |
| @Chewji9875 | #5563, #5579, #5846 |
| @chirag127 | #5738, #5771 |
| @DKotsyuba | #5857 |
| @hartmark | #5834 |
| @ishatiwari21 | #5799 |
| @janeza2 | #5855, #5858 |
| @jetmiky | direct commit / report |
| @josevictorferreira | #5425 |
| @JxnLexn | #5652 |
| @KooshaPari | #5613, #5621, #5624, #5629, #5643, #5651, #5890 |
| @KunN-21 | direct commit / report |
| @manhdzzz | direct commit / report |
| @nguyenxvotanminh3 | #5760, #5767, #5772 |
| @noir017 | direct commit / report |
| @pizzav-xyz | #5720 |
| @rdself | #5746, #5856 |
| @shiva24082 | #5879 |
| @skyzea1 | #5432, #5701 |
| @Stazyu | #5557 |
| @Thinkscape | #5549 |
| @vishalrajv | direct commit / report |
| @voravitl | direct commit / report |
| @waguriagentic | direct commit / report |
| @wahyuzero | direct commit / report |
| @warelik | direct commit / report |
| @Witroch4 | #5731, #5859, #5863 |
| @diegosouzapw | maintainer — cycle reconciliation, release-close base-red fixes, god-file decomposition, compression/memory features |
What's Changed
- Correct grammatical errors in the README file. by @chirag127 in #5738
- Release v3.8.43 by @diegosouzapw in #5609
Full Changelog: v3.8.42...v3.8.43
详细ChangeLogv3.8.42
2026年06月30日
✨ New Features
-
compression (pipeline): add an honest default-on inflation guard to the stacked compression pipeline (T02 / Headroom H1). If the fully-stacked engines produce a body that did not actually shrink — its token count is
>=the original — the compressed body is discarded and the verbatim original request is sent upstream instead, with apipeline-inflation-guardwarning recorded in the compression stats. This is safe by construction (the only fallback is the unmodified original, always a valid payload) and complements the existing opt-in per-step TV1 bail-out, which governs step-to-step advancement rather than the final output. Newopen-sse/services/compression/pipelineGuards.ts; wired at the singlefinalizeStackedResultchoke point shared by the sync and async stacked paths. Regression guards (incl. an inflating-engine integration test) intests/unit/compression-pipeline-inflation-guard.test.ts. -
compression (caveman): complete the German, French, and Japanese rule packs with the
dedup(repeated-context collapsing) andultra(abbreviation / terse) categories they were missing — these three languages previously shipped onlycontext/filler/structural, whileen/es/id/pt-BRhad all five. So a de/fr/ja conversation compressed at higher intensities now collapses repeated boilerplate ("wie bereits besprochen" → "Siehe oben.", "comme mentionné précédemment" → "Voir ci-dessus.", "前述のとおり" → "(上記参照)") and abbreviates dense technical vocabulary (Datenbank→DB,Authentifizierung→Auth;base de données→BD,authentification→auth;データベース→DB,アプリケーション→app). Patterns mirror the existingespack and stay ReDoS-safe (bounded literal alternations; the CJK pack uses no\bsince Japanese has no word boundaries). Regression guard:tests/unit/caveman-packs-de-fr-ja.test.ts(packs load + validate + shrink a representative sample). gaps v3.8.42 — T05/C2. -
compression (caveman): add a Chinese (zh / wenyan 文言) input-side rule pack — the counterpart of the existing output-side
terse-cjkstyle. Newrules/zh/{dedup,filler,ultra}.jsoncollapse repeated context ("如前所述" → "见上。"), drop pleasantries/hedging ("请帮我…/谢谢/我觉得"), strip sentence-final modal particles ("吗/呢/吧"), and abbreviate dense technical terms ("数据库"→"DB", "应用程序"→"app"). Chinese is now auto-detected:detectCompressionLanguagedistinguishes zh from ja by Han-without-kana (kana is Japanese-exclusive, so a Han-heavy Japanese sentence still resolves toja), andzhis listed inlistSupportedCompressionLanguages. Patterns are ReDoS-safe (bounded literal alternations, no\bsince CJK has no word boundaries). Regression guard:tests/unit/caveman-packs-zh-wenyan.test.ts(packs load + validate + shrink; zh/ja/non-CJK detection). gaps v3.8.42 — T05/C6. -
compression (RTK): add Gradle and .NET CLI (
dotnet) to the RTK tool-output filter catalog. Tool output forgradle/gradlewanddotnet build|test|restore|publishis now recognized (both by command and by output content) and compressed: Gradle daemon/welcome banners and no-op> Task … UP-TO-DATE/SKIPPED/FROM-CACHElines are dropped whileBUILD SUCCESSFUL/FAILED, "What went wrong", and stack traces are preserved; the .NET build banner, copyright, andDetermining projects to restore/Restored …chatter are dropped whileBuild succeeded/FAILED,error CS####/warning CS####, and test summaries are preserved. New builtin filtersengines/rtk/filters/{gradle,dotnet}.json(with inline tests run by the catalog gate) plusgradle/dotnetentries in the command detector. Regression guard:tests/unit/rtk-gradle-dotnet-filters.test.ts. gaps v3.8.42 — T07/R9.
🔧 Bug Fixes
-
providers (chatgpt-web): fix
502 ChatGPT sentinel failed: Digest method not supportedon the Electron desktop app, which made everychatgpt-web/*request fail. The sentinel proof-of-work hashed with nativecreateHash("sha3-512"), but Electron's Node is built against BoringSSL, which does not implement the SHA-3 family (electron/electron#30530), so the digest threw at construction — the provider was unusable on the desktop build (works under plain Node/OpenSSL). The PoW now hashes through a new runtime-portable helper (open-sse/utils/sha3-512.ts) that prefers the native digest and transparently falls back to a dependency-free pure-JS Keccak-f[1600] when native SHA-3 is absent. The fallback is validated bit-for-bit against nativecreateHash("sha3-512")(300 random inputs) and the published FIPS-202 known-answer vectors. Regression guards intests/unit/chatgpt-web-sha3-boringssl-5531.test.ts. (#5531) -
providers (bytez): fix Bytez key validation ("Provider validation endpoint not supported") and the chat base URL, verified live with a real key. Bytez is OpenAI-compatible at
…/models/v2/openai/v1, but the registry stored the bare…/models/v2base, so the validation chat-probe hit…/models/v2/chat/completions→404→ the misleading "endpoint not supported". Two parts: (1) the registrybaseUrlnow carries the full OpenAI-compat chat path (…/models/v2/openai/v1/chat/completions); (2) key validation no longer uses a chat probe — a Bytez account only serves models explicitly added to its catalog, so even valid keys 404 on any model id. A dedicatedvalidateBytezProviderinstead probes the auth-onlyGET …/models/v2/list/tasksendpoint (200⇒ valid,401/403⇒ invalid), which is independent of catalog provisioning. Regression guard:tests/unit/bytez-validation-5422.test.ts. (#5422) -
dashboard (provider add): two provider-add UX fixes. (1) #5420 — the "Import Models" button now stays hidden for tool-only providers (web search / web fetch), not just
*-searchids:firecrawlandjina-reader(declaredserviceKinds: ["webFetch"]) previously showed an Import button that hit the400 "does not support models listing"route. A new capability check (providerLacksModelListingover the resolved serviceKinds) gates the section without ever hiding an LLM/media provider. (2) #5426 — Coze key validation no longer leaks the raw upstream envelope ({code,msg,logId,from}) into the UI; the Coze-shaped error becomes a friendlyCoze rejected the key: <msg> (code <n>)message (scoped toprovider === "coze"so no other provider is affected). Regression guards:tests/unit/model-listing-capability-5420.test.ts,tests/unit/coze-validation-error-5426.test.ts. (#5420, #5426) -
providers (friendliai, novita): fix two provider registry endpoints that rejected valid keys (verified live with real keys). FriendliAI pointed at
…/dedicated/v1/chat/completions, which403 Forbiddens a serverlessflp_*token — switched to…/serverless/v1/chat/completions(+ a serverlessmodelsUrl). Novita pointed at the legacy…/v3/…base with a typo'd model idai-ai/llama-3.1-8b-instruct(both404) — switched to the OpenAI-compatible…/openai/v1/…base + the validmeta-llama/llama-3.1-8b-instructid. Regression guard:tests/unit/provider-endpoints-friendliai-novita.test.ts. (#5430, #5455) -
providers (muse-spark): align the Muse Spark Web (Meta AI) cookie copy with the live cookie name. The default session cookie migrated from the retired
abra_sesstoecto_1_sess(META_AI_DEFAULT_COOKIE), but the provider form hint and one 401 auth-failure message still told users to pasteabra_sess— a cookie that no longer exists. Both strings now nameecto_1_sess. Regression guard:tests/unit/muse-spark-cookie-copy-5449.test.ts. (#5449) -
dashboard (provider add): fix three rough edges in the Add-API-Key / model-import flow reported across the provider-catalog audit. (1) The Validation Model and Account ID form fields shipped untranslated i18n stub copy (
"Validation Model Id Label","Account Id Placeholder", …) that surfaced verbatim in the modal — replaced with real labels/placeholders/hints inen.json. (2) Model import silently fell back to the cached/local catalog: the route already returned awarning("API unavailable — using local catalog"), butuseModelImportHandlersonly readmodels/errorand dropped it, so the user got local models with no indication — the warning is now surfaced as an import log line (new pure helperextractImportWarning). (3) The required connection-name field defaulted to"", which let browser autofill inject garbage (e.g.wiw) — it now defaults to"main". Regression guard:tests/unit/provider-add-ux-i18n-import-warning.test.ts. (#5421, #5428, #5429, #5431, #5435) -
services (installer): fix
spawn EINVALwhen installing an embedded service (9Router / CLIProxy) on Windows + Node.js 24+. Node 24 stopped lettingchild_process.execFile()run.cmdbatch files without a shell (nodejs/node#52554), and npm on Windows isnpm.cmd, sorunNpm()threwEINVALthe moment a user clicked Install.runNpmnow enablesshellon win32 only. To keep Hard Rule #13 intact under a shell — where the shell, notexecFile, parses argv — the install--prefix(aDATA_DIRpath that can legitimately contain spaces, e.g.C:\Users\John Doe\.omniroute\…) is now passed via thenpm_config_prefixenvironment variable instead of an argv path, and the user-supplied installversionis constrained to a dist-tag/semver shape (SERVICE_VERSION_PATTERN) at the route boundary so it can never carry shell metacharacters. With the prefix in the environment and the version validated, every remaining argv entry is a static flag. Regression guards:tests/unit/services/installers/runNpm-shell-5379.test.ts(+ existingninerouter.test.tsaligned to npm'snpm_config_prefixenv). (#5379) -
cli (serve): restore
dist/tls-options.mjsto the npm tarball — the opt-in native HTTPS/TLS sidecar (#5361) was copied into the stageddist/by the build but then pruned by the prepublish allowlist step, soomniroute servecrashed on the published 3.8.41 withERR_MODULE_NOT_FOUND(dist/server-ws.mjsimports./tls-options.mjs). Addedtls-options.mjstoAPP_STAGING_ALLOWED_EXACT_PATHS(survives the prune) anddist/tls-options.mjstoPACK_ARTIFACT_REQUIRED_PATHS(thecheck:pack-artifactgate now fails loudly if it ever vanishes again — same guard pattern aswebdav-handler.mjs). Regression guards intests/unit/pack-artifact-policy.test.ts. (#5452 — thanks @KooshaPari for the parallel fix #5494) -
dashboard: fix the Add Provider / onboarding wizard button silently doing nothing. The
/dashboard/providers/newroute was a redirect stub (it bounced straight back to/dashboard/providers), so every "Add Provider" button and dashboard widget link opened nothing, and the fully-builtProviderOnboardingWizardcomponent stayed orphaned (never rendered by any route). The route now renders the wizard directly; auth is enforced centrally by the(dashboard)layout, same as the sibling provider routes. Regression guard intests/unit/onboarding-wizard-route-5427.test.ts. (#5427) -
db (import): fix
EBUSY: resource busy or lockedwhen importing a database on Windows. The import route deleted the livestorage.sqlite+ WAL/-shm/-journalsidecars with a plainfs.unlinkSyncimmediately afterresetDbInstance(), but Windows releases the SQLite file handle asynchronously afterclose()(mmap / antivirus), so the unlink raced and threwEBUSY. The route now deletes viaunlinkFileWithRetry(EBUSY/EPERM backoff) — the same helper the restore path already uses. Regression guard intests/unit/db-import-ebusy-5406.test.ts. (#5406, consolidated under #5161) -
build: keep
ioredisout of the client/CLI bundle — a dast-smoke regression revealed the module was being pulled into browser/Electron client-side chunks; adding it to theSPAWN_CAPABLE_PREFIXESleaf excludes it from client bundles while keeping it available on the server path. (#5546) -
providers (mimocode): route per-account traffic through SOCKS5 proxy dispatchers — each mimocode account's requests are now dispatched via its configured SOCKS5 proxy rather than the default direct connection. (#5521 — thanks @pizzav-xyz)
-
providers: persist the Configured provider filter selection across page reloads — the filter was resetting to "All" on every navigation. (#5510 — thanks @KooshaPari)
-
providers (chatgpt-web): support GPT-5.5 Pro model handoff — adds the model mapping and handoff routing needed for the GPT-5.5 Pro tier. (#5536 — thanks @Thinkscape)
-
dashboard: keep onboarding schemas browser-safe — the schema module imported a server-side
dbreference that crashed the browser bundle; it is now imported only on the server path. (#5525 — thanks @KooshaPari) -
routing (bifrost): add auto-fallback cooldown for bifrost targets — prevents rapid re-selection of a failing bifrost backend within the cooldown window, complementing the existing circuit-breaker mechanism. (#5519 — thanks @KooshaPari)
-
providers (opencode-plugin): bump the opencode plugin to v0.2.0 and wire auto-publish on release so the plugin package tracks OmniRoute releases automatically. (#5363 — thanks @herjarsa)
-
rate-limit: normalize queue refresh settings — aligns the queue-refresh interval configuration across rate-limit strategies so stale queues are released on a consistent schedule. (#5499 — thanks @KooshaPari)
-
fallback: normalize provider error-rule header extraction — ensures fallback retry decisions correctly read all response headers regardless of casing, fixing cases where a provider's
Retry-Afteror custom error header was silently dropped. (#5473 — thanks @KooshaPari) -
routing: gate Claude adaptive-thinking defaults behind the feature flag — prevents the thinking budget from being injected into requests for models that do not support the extended-thinking parameter, avoiding upstream
400errors on non-thinking Claude variants. (#5480 — thanks @KooshaPari) -
ci: fix post-merge CI regressions introduced by the dead-code sweep — restores test imports and type references broken when the ratchet landed before downstream consumers were updated. (#5467 — thanks @KooshaPari)
-
sse: treat terminal stream cancels as complete — an aborted SSE stream was being left in a partial state, causing downstream consumers to wait indefinitely for a final event that would never arrive. (#5491 — thanks @JxnLexn)
-
api: fix framing of non-streaming JSON responses —
stream: falsechat-completions responses were returned without correct content-length framing, causing some clients to misparse the response body. (#5416 — thanks @rdself) -
dashboard (tests): protect dynamic dashboard endpoint tests with CSRF validation — the test suite was exercising dashboard API routes without CSRF tokens, masking a coverage gap for those endpoints. (#5405 — thanks @rdself)
-
providers: remove the dead Phind provider (service shut down) and deduplicate the HuggingChat catalog listing that had accumulated a stale duplicate entry. (#5530 — thanks @backryun)
-
providers (longcat): correct the LongCat free tier — LongCat-2.0 is now GA; the one-time 10M-token promo (KYC required) is correctly reflected in the catalog, replacing the stale legacy beta entry. (#5508 — thanks @backryun)
📝 Maintenance
-
dashboard (refactor): consolidate the duplicate caveman on/off toggle from the compression settings tab onto the single-source panel (T11), eliminating the stale off-sync copy. (#5524)
-
tests: add quota guard for Claude-Code identity version lockstep (Phase 2) — asserts that the Claude-Code version reported in quota accounting stays in sync with the deployed version, preventing silent drift. (#5514)
-
docs: add relay backend strategy guide documenting supported relay backend types, selection criteria, and configuration patterns. (#5547)
-
docs: clarify bifrost relay backend environment variables — documents which env vars control bifrost's relay backend selection and failover behavior. (#5520 — thanks @KooshaPari)
-
tests: add relay routing fallback header behavior tests — regression guard asserting that fallback-triggered relay requests carry the correct forwarded headers through the routing layer. (#5526 — thanks @KooshaPari)
-
ci: add npm
fetch-retryconfiguration and codify the release-freeze protocol (Hard Rule #21) — reduces transient npm registry fetch failures in CI and establishes the documented procedure for freezing releases. (#5506) -
deps: bump 11 production dependencies to their latest compatible versions. (#5414)
-
deps: bump Electron from 42.4.1 to 42.5.1 in
/electron. (#5413) -
deps: bump the development dependency group with 9 updates. (#5415)
-
maintenance (dead-code): repo-wide sweep of unused exported symbols, types, and schemas — removes 35 no-longer-referenced exports across cloud-agent, a2a, SSE, memory, quota, skills, gamification, codex, qdrant, playground, provider catalog, and combo modules, reducing the exported API surface and eliminating stale misleading types. (#5372, #5373, #5374, #5375, #5376, #5377, #5378, #5380, #5381, #5382, #5383, #5384, #5385, #5386, #5387, #5388, #5389, #5390, #5391, #5392, #5393, #5395, #5396, #5397, #5398, #5399, #5400, #5401, #5402, #5403, #5404, #5463, #5464, #5466, #5468 — thanks @JxnLexn)
-
maintenance (DRY): DRY consolidation of shared helpers — extracts 17 duplicated utilities into single shared modules: vscode metadata helpers, proxy route handlers, auth zip extractors, combo-builder model options, vscode tokenized-request helpers, quota strategy ranking helpers, recharts donut card, provider-specific validation, batch response formatter, Redis runtime helpers, version-manager request parsing, media-generation route helpers, service install helpers, settings transform schemas, relay stream finalizer, machine-id fallback, and node SQLite adapter. (#5471, #5472, #5475, #5477, #5479, #5482, #5484, #5485, #5488, #5490, #5492, #5493, #5495, #5496, #5497, #5498, #5500 — thanks @JxnLexn)
What's Changed
- deps: bump the development group across 1 directory with 9 updates by @dependabot[bot] in #5415
- [codex] add relay backend strategy guide by @KooshaPari in #5533
- Release v3.8.42 by @diegosouzapw in #5459
Full Changelog: v3.8.41...v3.8.42
详细ChangeLog