* feat(fusion): let judge use its own knowledge and override the panel (#6804) The judge prompt said to write an answer 'grounded in that analysis', implicitly capping output at the panel's union. When all panel members miss or are collectively wrong on something, the judge should apply its own reasoning as a full participant and override consensus, while keeping an honesty guard against fabrication. Adds a regression test. Co-authored-by: Chirag Singhal <chirag127@users.noreply.github.com> * fix(api): raise provider apiKey cap for cookie-based web providers (#6715) (#6759) * fix(cli): fall back to settings.json when Claude Code binary is unresolvable (#6701) (#6734) getCliRuntimeStatus() only ever answered `installed` from binary resolution (known install paths + where/which PATH search), so a stale PATH, moved binary, or uncatalogued install method reported "not found" even when ~/.claude/settings.json proved the CLI was installed and used before — regressing behind upstream 9router's checkClaudeInstalled(), which already falls back to the settings file when where/which fails. withSettingsFallback() (new src/shared/services/cliInstallFallback.ts, kept out of the frozen cliRuntime.ts to respect its file-size ceiling) restores that parity: only 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. * fix(providers): honor explicit thinking.budget_tokens 0 in openai->gemini transform (#6813) (#6821) The transform forwarded the Claude-style thinking.budget_tokens into generationConfig.thinkingConfig.thinkingBudget, but the presence check was truthy (&& thinking.budget_tokens). An explicit budget_tokens: 0 — the natural way to disable thinking — is falsy, so it was dropped and the request fell through to the default thinkingConfig injection, making the model think despite an explicit request for zero. Use an explicit numeric check so 0 is honored as thinkingBudget 0; includeThoughts is only set for a non-zero budget. * fix(compression): reconcile outer vs per-engine token counts (#6488) (#6741) * fix(compression): reconcile outer vs per-engine token counts on degenerate output (#6488) Outer originalTokens/compressedTokens (real tiktoken counter over extracted message text) diverged from engineBreakdown[0]'s counts (a crude JSON.stringify(requestBody).length/4 estimate), worst on small/degenerate inputs where JSON structural overhead dominates. A single-engine breakdown entry represents the exact same before/after transformation as the overall response, so reconcileSingleEngineTokens() now overwrites that one entry's counts with the outer, more accurate figures; multi-step pipeline breakdowns are left untouched. * chore(6741): resolve release sync — CHANGELOG.md restored to release tip, entry moved to changelog.d fragment (fragments-first) * fix(api): accept enableRenderers in RTK compression config schema (#6703) (#6757) * fix(db): break probe-failed/restore loop on large storage.sqlite (#6632) Reconstructed onto release/v3.8.47 to drop unrelated main-drift (deps/electron/proxy files belong to #6620, not this PR); keeps only the author's changes. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> * feat(cursor): add Opus 4.8, Fable 5, and Sonnet 5 model families (#6779) Reconstructed onto release/v3.8.47 to drop unrelated main-drift (deps/electron/proxy files belong to #6620, not this PR); keeps only the author's cursor registry + test changes. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> * fix(translator): read PDF/video file attachments for Gemini/Antigravity and Claude (#6790) Reconstructed onto release/v3.8.47 to drop unrelated main-drift (deps/electron/proxy files belong to #6620, not this PR); keeps only the author's translator + test changes. Co-authored-by: Wital <witalorocha216@gmail.com> Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> * fix(codex): strip include from compact responses requests (#6805) * fix(codex): strip include from compact responses requests Reconstructed onto release/v3.8.47 to drop unrelated main-drift (deps/electron/proxy files belong to #6620, not this PR); keeps only the author's changes. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> * fix(6805): move include-strip assertion to standalone test file to keep executor-codex.test.ts under frozen size cap Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> --------- Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> * fix(i18n): translate hardcoded Portuguese dashboard strings to English (#6769) Reconstructed onto release/v3.8.47 to drop unrelated main-drift (deps/electron/proxy files belong to #6620, not this PR); keeps only the author's changes. Co-authored-by: Chirag Singhal <chirag127@users.noreply.github.com> Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> * fix(bootstrap): filter empty process.env values to prevent Docker env crash loop (#6828) Reconstructed onto release/v3.8.47 to drop unrelated main-drift (deps/electron/proxy files belong to #6620, not this PR) and the direct CHANGELOG.md edit (fragments-first); keeps only the author's bootstrap change. Co-authored-by: Andrian B. <andrewbalanesq@gmail.com> Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> * fix(providers): update SenseNova Token Plan support (#6330) Reconstructed onto release/v3.8.47 to drop unrelated main-drift (deps/electron/proxy files belong to #6620, not this PR); the author's constants/registry/snapshot deltas were re-applied cleanly onto the release tip. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> * fix(providers): classify 404 as MODEL_NOT_FOUND to stop retry storm (#6829) Reconstructed onto release/v3.8.47 to drop unrelated main-drift (deps/electron/proxy files belong to #6620, not this PR) and the direct CHANGELOG.md edit (fragments-first); the author's chatCore/errorClassifier deltas were re-applied cleanly onto the release tip. Co-authored-by: Andrian B. <andrewbalanesq@gmail.com> Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> * fix(api): accept all catalog engines on compression PUT schema (#6792) Reconstructed onto release/v3.8.47 to drop unrelated main-drift (deps/electron/proxy files belong to #6620, not this PR). Resolved the release's OmniGlyph engine addition additively (types.ts/compression.ts kept both 'relevance' and 'omniglyph') and extended stackedPipelineStepSchema + STACKED_PIPELINE_ENGINE_INTENSITIES with the omniglyph branch so the ENGINE_CATALOG-parity test passes. Co-authored-by: Pitchfork-and-Torch <Pitchfork-and-Torch@users.noreply.github.com> Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> * fix(api): point CLI health command at /api/monitoring/health (#6677) (#6717) * fix(api): point CLI health command at /api/monitoring/health (#6677) bin/cli/commands/health.mjs called GET /api/health, a route that was moved to /api/monitoring/health without updating the CLI; the top-level /api/health handler never existed on disk (only degradation/ and ping/ sub-routes). Point runHealthCommand()/runHealthComponentsCommand() at /api/monitoring/health and read its real payload shape (activeConnections, circuitBreakers: {open,halfOpen,closed}, memoryUsage) instead of the old nonexistent requests/breakers/cache/memory fields. * chore(6717): re-sync onto release tip; move CHANGELOG entry to changelog.d fragment (fragments-first) * chore(cursor): add Grok 4.5 effort/fast model IDs (#6774) Reconstructed onto release/v3.8.47 to drop unrelated main-drift (deps/electron/proxy files belong to #6620, not this PR); keeps only the author's changes. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> * fix(providers): ensure DeepSeek Web SSE emits [DONE] after FINISHED (#6791) * fix(providers): ensure DeepSeek Web SSE emits [DONE] after FINISHED Reconstructed onto release/v3.8.47 to drop unrelated main-drift (deps/electron/proxy files belong to #6620, not this PR); keeps only the author's changes. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> * refactor(deepseek): extract done-terminator helper to keep frozen file under cap Extracts the FINISHED-drain scheduler and finish-once guard added for the [DONE] terminator fix (#6777) into a new deepseek-web-done-terminator.ts module, so deepseek-web.ts stays under its frozen line cap (1148). Behavior is unchanged. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> --------- Co-authored-by: Pitchfork-and-Torch <Pitchfork-and-Torch@users.noreply.github.com> Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> * feat(models): add capability override UI (#6727) * feat(models): add capability override UI Reconstructed onto release/v3.8.47 to drop unrelated main-drift (deps/electron/proxy files belong to #6620, not this PR); renumbered the migration 118 -> 119 to resolve the collision with 118_provider_param_filters.sql already on release/v3.8.47; the author's i18n/localDb deltas were re-applied cleanly onto the release tip. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> * fix(6727): import model-capability-overrides DB fns directly (not via localDb barrel) to keep localDb under file-size cap; aligns with anti-barrel convention * chore(db): satisfy known-symbols contract for modelCapabilityOverrides Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> --------- Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com> * fix(cursor): use Agent CLI build id for x-cursor-client-version (#6795) * fix(cursor): use Agent CLI build id for x-cursor-client-version Reconstructed onto release/v3.8.47 to drop unrelated main-drift (deps/electron/proxy files belong to #6620, not this PR); the author's .env.example/docs deltas were re-applied cleanly onto the release tip. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> * chore(changelog): re-sync CHANGELOG.md to release tip (restore #6701 bullet) Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> --------- Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> * fix(startup): rename reasoningControls.ts to avoid webpack casing collision (#6584) (#6718) * fix(startup): rename reasoningControls.ts to avoid webpack casing collision (#6584) * chore(6718): re-sync onto release tip; CHANGELOG entry → changelog.d fragment (fragments-first) * fix(build): suppress Turbopack over-bundling warning from agentSkills generator (#6582) (#6720) * fix(build): suppress Turbopack over-bundling warning from agentSkills generator (#6582) generator.ts builds outputBase from a non-literal outputDir parameter, so Turbopack's file-tracing analyzer can't narrow it and emits an "Overly broad patterns" warning per entry point that imports the module (603 warnings on v3.8.46, up from 379). The fs access is legitimate and bounded, so next.config.mjs now suppresses this specific diagnostic via turbopack.ignoreIssue, mirroring the existing webpack.ignoreWarnings precedent in the same file. * chore(6720): re-sync onto release tip; CHANGELOG entry → changelog.d fragment (fragments-first) * fix(providers): drop image_generation for Codex Spark models regardless of plan (#6651) (#6721) * fix(providers): drop image_generation for Codex Spark models regardless of plan (#6651) * chore(6721): re-sync onto release tip; CHANGELOG entry → changelog.d fragment (fragments-first) * fix(providers): stop quota card re-sorting Codex/GLM bars by remaining % (#6687) (#6722) * fix(providers): stop quota card re-sorting Codex/GLM bars by remaining % (#6687) QuotaCardExpanded.tsx unconditionally re-sorted quotas by remaining percentage via sortQuotasByRemaining(), discarding the deterministic CODEX_QUOTA_ORDER/GLM_QUOTA_ORDER window order quotaParsing.ts's sortCodexOrder()/sortGlmOrder() had already established. A new hasFixedQuotaOrder() + resolveQuotaDisplayOrder() skip the re-sort for providers with a fixed window order (codex, glm family), threading providerId from QuotaCard.tsx through to the display layer. Regression guard: tests/unit/quota-card-expanded-fixed-order-6687.test.ts * chore(6722): re-sync onto release tip; CHANGELOG entry → changelog.d fragment (fragments-first) * fix(startup): lazy-import ioredis in rateLimiter to fix MCP ERR_MODULE_NOT_FOUND (#6559) (#6725) * fix(startup): lazy-import ioredis in rateLimiter to fix MCP ERR_MODULE_NOT_FOUND (#6559) * chore(6725): re-sync onto release tip; CHANGELOG entry → changelog.d fragment (fragments-first) * fix(resilience): resolve fp-pinned combo account back to real connection id (#6696) (#6732) * fix(resilience): resolve fp-pinned combo account back to real connection id (#6696) * chore(6732): re-sync onto release tip; CHANGELOG entry → changelog.d fragment (fragments-first) * fix(api): Responses passthrough emits event-only SSE frames after filtering commentary output (#6561) (#6735) * fix(api): Responses passthrough emits event-only SSE frames after filtering commentary output (#6561) The #6199 commentary-drop `continue;` branches in stream.ts skipped the data: line for a dropped commentary event but never cleared the already-buffered event: line for the same frame, so the next blank line flushed the stale event: line alone -- an event-only SSE frame that crashes the OpenAI Python SDK's json.loads(). Both drop sites now call clearPendingPassthroughEvent() before continue. The commentary-drop decision was extracted into a new responsesCommentaryDrop.ts module so the fix does not grow the frozen stream.ts. * chore(6735): re-sync onto release tip; CHANGELOG entry → changelog.d fragment (fragments-first) * fix(api): emit reasoning_content on claude-web + v0-vercel-web SSE (#6662) (#6743) * fix(api): emit reasoning_content on claude-web + v0-vercel-web /v1/chat/completions SSE (#6662) * chore(6743): re-sync onto release tip; CHANGELOG entry → changelog.d fragment (fragments-first) * fix(sse): unwrap bare {function:{…}} tools in openai→claude translation (#6704) * fix(sse): unwrap bare {function:{…}} tools in openai→claude translation Some OpenAI-shape clients send a tool as a bare `{ function: {...} }` object, omitting the spec-required `type: "function"` parent wrapper. The tools-mapping in openai-to-claude.ts (~line 366) only unwrapped `tool.function` when `tool.type === "function"` was ALSO true, so a bare-function tool fell through to `toolData = tool` (the wrapper itself, with no `.name`), producing an empty `originalName` and silently dropping the tool from the translated request — worse than a 400, since the caller has no signal the tool never made it upstream. Unwrap `tool.function` whenever present, independent of the parent `type` field. Regression guard: tests/unit/openai-to-claude-bare-tool.test.ts. Co-authored-by: Samir Abis <me@samirabis.com> Inspired-by: https://github.com/decolua/9router/pull/2473 * chore(6704): re-sync onto release tip; CHANGELOG → changelog.d fragment (fragments-first) --------- Co-authored-by: Samir Abis <me@samirabis.com> * fix(oauth): avoid bare-email dedup of Codex OAuth logins (#6706) * fix(oauth): avoid bare-email dedup of Codex OAuth logins When an incoming Codex OAuth connection has no verifiable workspace/account id, do not merge it into an existing row on email match alone — that silently overwrote the other account's token pair. Require a matching chatgptUserId (a stable per-account JWT id) before merging; otherwise insert a distinct connection row. Co-authored-by: lucasjustinudin <34107354+lucasjustinudin@users.noreply.github.com> Inspired-by: https://github.com/decolua/9router/pull/2477 * chore(6706): re-sync onto release tip; CHANGELOG → changelog.d fragment (fragments-first) --------- Co-authored-by: lucasjustinudin <34107354+lucasjustinudin@users.noreply.github.com> * fix(sse): skip thinkingConfig for gemma models in openai→gemini translation (#6708) open-sse/translator/request/claude-to-gemini.ts already guards against sending thinkingConfig for gemma-4-* models (Gemma doesn't support it — Vertex returns 400: "Thinking budget is not supported for this model"), but the OpenAI-shape path (openai-to-gemini.ts) lacked the same guard, so OpenAI-shape clients hitting a vertex gemma-4-* model still got a 400. Mirrors the existing claude-to-gemini.ts guard: wrap the reasoning_effort and Claude-shape thinking.budget_tokens branches with a model.startsWith ("gemma-4") check. Branch 3 (default includeThoughts for modern Gemini models) already excludes non-"gemini" model ids and needed no change. Inspired-by: https://github.com/decolua/9router/pull/2480 Co-authored-by: chy1211 <31048289+chy1211@users.noreply.github.com> * fix(codex): surface capacity errors embedded in 200-OK SSE streams (#6710) * fix(codex): surface capacity errors embedded in 200-OK SSE streams Codex sometimes answers with HTTP 200 and a text/event-stream body whose payload carries a transient error mid-stream (e.g. "Selected model is at capacity...", server_is_overloaded, service_unavailable_error). Because the outer HTTP status was 200, this looked like a successful response to every caller — no retry, no circuit breaker, and no combo/account fallback ever engaged, so a healthy account sat idle while the request silently failed or truncated. Add peekCodexSseTransientError() to open-sse/executors/codex.ts: it peeks the first bytes of a text/event-stream Codex response, pattern-matches the known transient-error signatures, and converts a match into a real 503 Response via errorResponse() (Hard Rule #12 — sanitized, never raw upstream text). A 503 is already a recognized provider-failure status in accountFallback.ts, so combo routing and connection cooldown pick it up automatically. When no error signature is found, the peeked prefix is prepended back onto the remaining upstream body so the passthrough stays byte-identical to the unmodified response. Regression guard: tests/unit/codex-sse-capacity-fallback.test.ts — a model-at-capacity payload and a server_is_overloaded/service_unavailable_error payload both convert to 503; a normal single-chunk SSE stream and one split across multiple network chunks both reassemble byte-for-byte unchanged. Inspired-by: https://github.com/decolua/9router/pull/2452 (sub-bug #3 only — OmniRoute already covers PR #2452's other two sub-bugs: service_tier "fast" normalization and reasoning_effort "max" normalization). Co-authored-by: ryanngit <74137224+ryanngit@users.noreply.github.com> * chore(6710): re-sync onto release tip; CHANGELOG → changelog.d fragment (fragments-first) --------- Co-authored-by: ryanngit <74137224+ryanngit@users.noreply.github.com> * fix(volcengine): clamp Kimi max_tokens to Ark endpoint cap (#6712) * fix(volcengine): clamp Kimi max_tokens to Ark endpoint cap VolcEngine Ark's Kimi coding-plan endpoint (ark.cn-beijing.volces.com) enforces max_tokens <= 32768 server-side and returns 400 "integer above maximum value, expected a value <= 32768" for anything over that ceiling. OmniRoute's StripRule only supported dropping params outright, with no numeric clamp mechanism, so a client sending a larger max_tokens (common default, e.g. 65536) 400s outright against volcengine's kimi-k2-5-260127. The 32768 cap is independently confirmed against two live-endpoint bug reports hitting this exact Ark endpoint for both kimi-k2.5 and kimi-k2.7-code (NousResearch/hermes-agent#51773, MoonshotAI/kimi-cli#1124), not just upstream's own value — same cap upstream 9router#2460 uses. StripRule gains two optional fields: `clampToModelMaxOutput` (clamp to the model's own catalog maxOutputTokens ceiling, when set) and `maxOutputCap` (a fixed endpoint-imposed ceiling); when both apply, the lower wins. The new rule is scoped to the literal id `kimi-k2-5-260127` (OmniRoute's real volcengine Kimi model, not upstream's `Kimi-K2.7-Code`), not a broad /kimi/i regex, so it can never clamp an unrelated future Kimi listing whose Ark cap may differ. glm-4-7-251222 (the other volcengine model) is unaffected. Inspired-by: https://github.com/decolua/9router/pull/2460 Co-authored-by: whale9820 <whale9820@users.noreply.github.com> * chore(6712): re-sync onto release tip; CHANGELOG → changelog.d fragment (fragments-first) --------- Co-authored-by: whale9820 <whale9820@users.noreply.github.com> * fix(antigravity): surface aborted Gemini tool calls off end_turn (#6713) * fix(antigravity): surface aborted Gemini tool calls off end_turn Gemini/Antigravity aborts a turn with finishReason MALFORMED_FUNCTION_CALL (or a sibling like UNEXPECTED_TOOL_CALL) instead of completing cleanly. Both Claude-facing translators collapsed these to a clean end_turn, hiding the aborted tool call as a successful completion: - the OpenAI hub path (openai-to-claude.ts convertFinishReason default), and - the DIRECT Gemini->Claude path (gemini-to-claude.ts), which is the one Claude Code actually hits through an antigravity/Gemini-routed model. Add isAbortFinishReason() to finishReason.ts and map these reasons to tool_use on both paths; genuinely unknown reasons still fall back to end_turn. Co-authored-by: anhdiepmmk <n08ni.dieppn@gmail.com> Inspired-by: https://github.com/decolua/9router/pull/2462 * chore(6713): re-sync onto release tip; CHANGELOG → changelog.d fragment (fragments-first) --------- Co-authored-by: anhdiepmmk <n08ni.dieppn@gmail.com> * fix(translator): strip empty cloud_base_branch from Cursor Subagent tool call (#6729) * fix(translator): strip empty cloud_base_branch from Cursor Subagent tool call (port from 9router#2446) The Responses->Chat tool-arg cleanup (stripEmptyOptionalToolArgs) only stripped empty-string/empty-array optional args for Claude Code's Read tool. Cursor's local Subagent tool call therefore passed through with the cloud-only field cloud_base_branch: "", which Cursor rejects ("cloud_base_branch may only be specified when environment equals cloud") before starting the subagent. Extend the cleanup to an allowlist of Read + Subagent; arbitrary tools stay untouched. Reported-by: like3213934360-lab (https://github.com/decolua/9router/issues/2446) * chore(6729): re-sync onto release tip; CHANGELOG → changelog.d fragment (fragments-first) * fix(translator): defer content_block_start until GLM streams the tool name (#6730) * fix(translator): defer content_block_start until GLM streams the tool name (port from 9router#2077) GLM 5.2 (and similar OpenAI-compatible upstreams) stream a tool call's id and function.name across separate SSE delta chunks. The openai-to-claude streaming translator emitted content_block_start immediately on the id-only chunk with an empty name; the Claude SSE protocol cannot patch a block after emission, so the later name-only chunk was dropped and Claude Code rejected the tool_use with an empty tool name / "No such tool available:". Defer content_block_start until the name arrives (start on args if they arrive first), and emit a start for any orphaned id-only tool call at finish so content_block_stop is never orphaned. Reported-by: itiwant (https://github.com/decolua/9router/issues/2077) * chore(6730): re-sync onto release tip; CHANGELOG → changelog.d fragment (fragments-first) * feat(dashboard): add search to Playground model picker dropdown (#4086) (#6811) * feat(dashboard): add search to Playground model picker dropdown (#4086) The shared ModelSelectModal (combo builder + CLI-code cards) already had search, but the Playground's raw model <select> in StudioConfigPane stayed a flat unsearchable list - unusable once a provider like OpenRouter contributed 50+ models. Adds a search input above the dropdown that filters options via filterModelsByQuery() (Turkish-safe accent/case-insensitive match, reusing matchesSearch()). The currently selected model always stays pinned in the list even when it doesn't match the query, so typing never silently swaps the active selection. Reuses the existing common.search i18n key already translated in all 42 locales - no new key needed. * chore(6811): re-sync onto release tip; CHANGELOG → changelog.d fragment (fragments-first) * feat: request count log per provider, per date (#4009) (#6812) * feat(dashboard): request count log per provider, per date (#4009) Some providers bill by request rather than by token, so operators need a plain per-provider, per-date request count breakdown, not just token aggregates. Adds a new getProviderDailyUsageRows() aggregation query (src/lib/db/usageAnalytics.ts), a dedicated GET /api/usage/requests-by-provider-date route (kept separate from the frozen /api/usage/analytics route to respect the file-size baseline), and a sortable, single-date-filterable table on Dashboard -> Analytics. Closes #4009 * chore(6812): re-sync onto release tip; CHANGELOG → changelog.d fragment (fragments-first) * feat(xai): route xAI clients to Grok native /v1/responses endpoint (#6709) * feat(xai): route xAI clients to Grok native /v1/responses endpoint xAI ships a native /v1/responses endpoint (https://api.x.ai/v1/responses) alongside /v1/chat/completions, but XaiExecutor extended BaseExecutor without overriding buildUrl(), so every request always resolved to the static chat-completions baseUrl regardless of target format — the last genuinely-missing slice of decolua/9router#2439 (grok-build-0.1, the reasoning-effort suffix routing, and bare grok-* routing were already ported in prior cycles). Add responsesBaseUrl to the xai registry entry and tag grok-4.20-multi-agent-0309 (upstream's own Responses-only id) with targetFormat: "openai-responses", mirroring the existing model-tag-driven routing pattern already used by the gh executor (9router#102) and the "openai" -pro heuristic in open-sse/executors/default.ts — the per-model registry tag is the single source of truth that also drives chatCore's body translation, so URL and body stay in lockstep. XaiExecutor.buildUrl now checks getModelTargetFormat("xai", model) and resolves to the native Responses endpoint only for tagged models, leaving every other grok-* model on the existing chat-completions bridge. TDD: tests/unit/executor-xai.test.ts adds a RED-then-GREEN case asserting grok-4.20-multi-agent-0309 resolves to https://api.x.ai/v1/responses and a control case asserting grok-4.3 still resolves to https://api.x.ai/v1/chat/completions. Co-authored-by: ryanngit <74137224+ryanngit@users.noreply.github.com> Inspired-by: https://github.com/decolua/9router/pull/2439 * chore(6709): re-sync onto release tip; CHANGELOG → changelog.d fragment (fragments-first) --------- Co-authored-by: ryanngit <74137224+ryanngit@users.noreply.github.com> * fix(resilience): route remaining credential-selection call sites through quota preflight (#6686) (#6742) * fix(resilience): route remaining credential-selection call sites through quota preflight (#6686) * chore(6742): re-sync onto release tip; CHANGELOG entry → changelog.d fragment (fragments-first) * fix(resilience): apikey-provider 429s honor explicit quota-exhausted text (#6638) (#6731) * fix(resilience): apikey-provider 429s honor explicit quota-exhausted text (#6638) Ollama Cloud (and any other apikey-category provider) 429s skipped body-text quota classification entirely; a genuine multi-day quota exhaustion was misclassified as a plain rate_limit_exceeded with a few seconds of cooldown, so combo routing retried the account immediately. shouldPreserveQuotaSignals() now lets an explicit quota-exhausted signal (looksLikeQuotaExhausted) override the apikey-category default, and parseDayGranularityResetMs() adds day- granularity reset-hint parsing ("...reset in 3 days.") alongside the existing Xh/Ym/Zs parsing. Regression guard: tests/unit/issue-6638-ollama-quota.test.ts (RED before the fix, GREEN after). Aligned two tests/unit/account-fallback-service.test.ts cases that had codified the old buggy behavior for apikey-provider quota text. * chore(6731): re-sync onto release tip; CHANGELOG entry → changelog.d fragment (fragments-first) * feat(resilience): weekly-429 cooldown for fetcher-less providers (#3709) (#6817) * feat(resilience): weekly-429 cooldown for fetcher-less providers (#3709) Ollama Cloud free-tier accounts have a hard WEEKLY request cap. On cap the upstream returns 429 "you (<account>) have reached your weekly usage limit", but ollama-cloud is an apikey-category provider, so the existing oauth-only shouldUseQuotaSignal gate in checkFallbackError skips the subscription-quota-text classifier (Issue #2321) for its 429s -- the account fell through to the generic exponential backoff (~1s, capped at 2min) and got retried every few minutes for the rest of the week (one account took 285x429 in 48h). Adds a new, ungated weekly-usage-limit text classifier that applies a 24h QUOTA_EXHAUSTED cooldown regardless of provider category. Extracted the new classifier -- together with the existing #2321 subscription-quota logic -- into a new open-sse/services/quotaTextCooldowns.ts module so the frozen accountFallback.ts (file-size-baseline cap) didn't have to grow; net effect shrinks accountFallback.ts by 20 lines. This is Phase A of the plan (open-sse/services/accountFallback.ts:1038-1045 "weekly-429 cooldown"); Phase B (generic local request-counter preflight for manual provider_plans dimensions) is a separate, larger follow-up per the plan's own phasing. * chore(6817): re-sync onto release tip; CHANGELOG entry → changelog.d fragment (fragments-first) * fix(providers): Kiro adaptive-thinking allowlist excludes sonnet-4.5/haiku-4.5 (#6576) (#6726) * fix(providers): Kiro adaptive-thinking allowlist excludes sonnet-4.5/haiku-4.5 (#6576) * chore(6726): re-sync onto release tip; CHANGELOG entry → changelog.d fragment (fragments-first) * test(kiro): migrate selector-strip test to claude-sonnet-5 (only Kiro adaptive-thinking model, #6576) Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> * chore(quality): rebaseline complexity 2053->2054 (merge-burst drift, v3.8.47) Inherited drift from today's /implement-prs merge burst (~36 PRs). check:complexity does not run on the PR->release fast-path, so the branch accrued +1 unmeasured. No orphan/feature PR introduces a NEW violation (complexity-net-zero); the only flagged function is the pre-existing getResolvedModelCapabilities. Owner-approved rebaseline to unblock the FQG of ~7 green-except-complexity orphans. * chore(stryker): register ollama-quota covering tests (merge-burst drift, v3.8.47) The 3 covering unit tests from #6731/#6817/#6742 (issue-6638-ollama-quota, ollama-cloud-weekly-quota-cooldown-3709, issue-6686-quota-preflight-coverage) exist on release but were never added to tap.testFiles when those PRs merged. Completes the registration so mutant kills count; unblocks every PR touching a mutated module. Part of the owner-approved merge-burst drift cleanup. * fix: auto-start WS server in-process and change default port to 20132 (#6072) * feat: change default LIVE_WS_PORT from 20129 to 20132 Update the default WebSocket port for the live dashboard server from 20129 to 20132 across all configuration files, documentation, code comments, and tests. Also consolidate OMNIROUTE_DISABLE_LIVE_WS and OMNIROUTE_ENABLE_LIVE_WS into a single OMNIROUTE_ENABLE_LIVE_WS flag. Wire the live WebSocket server to start in-process via instrumentation-node.ts. * feat: clarify NEXT_PUBLIC_LIVE_WS_PUBLIC_URL path usage and derive upgrade path from URL Update .env.example and ENVIRONMENT.md to document that the pathname portion of NEXT_PUBLIC_LIVE_WS_PUBLIC_URL (e.g. /live-ws) is used as the WebSocket upgrade path by the dev proxy, handshake response, and client connection logic. Extract deriveLiveWsPath() into shared/utils/wsPath.ts and wire it through: - src/app/api/v1/ws/route.ts — handshake response path field - src/hooks/useLiveDashboard.ts — build * fix: use the standard URL API to safely parse and update the effectiveWsUrl * build(docker): expose live WebSocket server port and configure CORS origins Add LIVE_WS_PORT (20132), LIVE_WS_HOST (0.0.0.0), and LIVE_WS_ALLOWED_ORIGINS environment variables to all Docker Compose profiles and expose the WebSocket port mapping. Prevent infinite self-loop in standalone-server-ws.mjs by skipping proxy when the server itself is running on the LiveWS port. * docs(env): fix comment formatting for HOST and HOSTNAME variables --------- Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> * fix(logs): prevent stale detail refresh reopening modal (#6323) * fix(logs): prevent stale detail refresh reopening modal * chore(stryker): register ollama-quota covering tests (release drift from merge burst) Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> --------- Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com> Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> * \ feat: operator-configurable account rotation\ (#6763) * feat(resilience): operator-configurable account rotation Reconstructed onto release/v3.8.47 to drop unrelated main-drift (deps/electron/proxy files belong to #6620, not this PR); the author's accountFallback/.env deltas were re-applied cleanly onto the release tip. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> * docs(env): document configurable account-rotation env vars in ENVIRONMENT.md Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> * refactor(rotation): extract rotation gate/context helpers to keep accountFallback.ts under frozen cap Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> * chore(changelog): re-sync CHANGELOG.md to release tip (restore lost base bullet) Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> * test(stryker): register rotation-config test in tap.testFiles for mutation coverage Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> * test(stryker): register ollama-quota covering tests (drift from #6731/#6817/#6742) + re-sync Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> --------- Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com> * fix(lmarena): modernize Arena web provider + static Direct-chat catalog (#6280) * fix(lmarena): modernize Arena web provider + static Direct-chat catalog Update the lmarena provider for arena.ai (product rebranded from LMArena): - Route chat via arena.ai create-evaluation with Chrome TLS impersonation (tls-client-node) and optional browser-minted recaptchaV3Token. - Seed Text+Search (48) into the chat registry; seed Image (27) only into IMAGE_PROVIDERS. Disable live HTML model discovery; resolve public names to Arena UUIDs from the static TypeScript allowlist (no scrape JSON in-repo). - Soft-exclude 404/502 model ids; slow/stop bulk test-all probes for this provider. - Do not fold IMAGE_PROVIDERS/video specialty into the chat provider catalog when a chat registry already exists (lmarena/openai/xai). - Display name Arena (Free); keep wire id `lmarena` / alias `lma` for back-compat. - Theme-aware provider icons: arena-light.svg / arena-dark.svg. - Preserve split Supabase SSR cookie reconstruction for arena-auth-prod-v1.*. * fix(providers): align provider-models-route test fixture + regen provider reference Fold the topaz image-only catalog entry's apiFormat/supportedEndpoints into the local-catalog test fixture (route now tags media-only providers per the lmarena PR's staticModels.ts change), regenerate PROVIDER_REFERENCE.md against the merged release providers.ts, and add the changelog fragment for #6280. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> * test: align web-cookie fallback suite — lmarena now has a registry entry (probe path) Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> --------- Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> * docs(changelog): reconcile 3-day merge burst — 16 fragments, 4 promised credits, contributors hall 32→63 - changelog.d fragments for the 20 merged PRs that landed without a bullet (#6072 #6308 #6323 #6538 #6556 #6586 #6611 #6647 #6675 #6698 #6757 #6759 #6804 #6821 + ci rollup #6781/#6691/#6693 + docs rollup #6643/#6644/#6646/#6663; omniglyph bump #6661 folded into the #6556 bullet) - deliver the 4 credits promised in close comments but never written: @alltomatos (#6819 dup of #6721), @samimozcan (#6762/#6753 subsumed by #6790), @chirag127 (#6756 dup of #6757), @Squawk7777 (#6565 dup of #6564 — appended to the existing #6564 bullet; changelog-integrity flags that edit as a removal, intentional: ALLOW_CHANGELOG_REMOVALS justification) - rebuild the v3.8.47 Contributors hall from merged-PR authors + thanks credits + prior hall: 32 → 63 contributors * Clamp reasoning token buffer to model output cap (#6714) * fix(combo): clamp reasoning buffer to model output cap * fix(routing): preserve near-cap reasoning max tokens * fix(routing): getExplicitModelOutputCap falls through to registry cap on non-numeric synced limit_output getExplicitModelOutputCap short-circuited to null whenever a synced capability row existed, even if that row's limit_output was not a number (models.dev commonly omits it). That silently disabled the reasoning-token buffer clamp for any model with a synced row lacking an output limit. Now only return the synced value when it IS a number; otherwise fall through to registryModel.maxOutputTokens / spec.maxOutputTokens, matching the ??-chain precedence already used by getResolvedModelCapabilities(). Adds a standalone regression test (proves the fallthrough returns the real registry cap, not null) and hardens the #6274 fixture id so its no-output-cap case does not prefix-match the real glm-5.2 static spec. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> * chore(stryker): register ollama-quota covering tests (release drift from merge burst) Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> --------- Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com> Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> * feat(i18n): add Traditional Chinese (zh-TW) localization for frontend and CLI (#6320) * feat(i18n): add Traditional Chinese (zh-TW) localization for frontend and CLI - Add src/i18n/messages/zh-TW.json translating frontend web UI - Add bin/cli/locales/zh-TW.json translating CLI commands and descriptors - Register zh-TW in config/i18n.json and docs/guides/I18N.md - Update scripts/i18n/generate-multilang.mjs matching the new locale setup * fix: update i18n locale count from 42 to 43 after adding zh-TW The docs strict checker (check-docs-counts-sync.mjs) validates that README.md and I18N.md reflect the real locale count. Adding zh-TW bumped the count from 42 → 43. * fix(i18n): translate providers free-filter labels in zh-TW (#6694 guard) Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> --------- Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com> Co-authored-by: Diego Rodrigues de Sa e Souza <8016841+diegosouzapw@users.noreply.github.com> Co-authored-by: lunkerchen <lunkerchen@users.noreply.github.com> * feat(proxy): implement latency-optimized proxy rotation strategy (#6798) * feat(proxy): implement latency-optimized proxy rotation strategy Reconstructed onto release/v3.8.47 to drop unrelated main-drift (deps/electron/proxy files belong to #6620, not this PR) and the direct CHANGELOG.md edit (fragments-first); the author's env/docs/i18n deltas were re-applied cleanly onto the release tip. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> * fix(proxy): add latency-rotation env var to .env.example PROXY_LATENCY_WINDOW_HOURS was referenced in src/lib/db/proxies.ts and documented in docs/reference/ENVIRONMENT.md, but missing from .env.example, tripping the env/docs sync gate. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> * chore(changelog): re-sync CHANGELOG.md to release tip Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> * refactor(proxy): extract latency-strategy helpers to keep frozen files under cap Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> * test(db-rules): expect 35 audited modules (proxyLatency joins INTENTIONALLY_INTERNAL) Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> --------- Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> * docs(readme): fix stale strategy/tool/scoring counts (#6853) README still claimed 17 routing strategies (the table was missing pipeline), 95 MCP tools, and 9-factor Auto-Combo scoring. Align with the source (ROUTING_STRATEGY_VALUES has 18 entries) and the canonical docs (MCP-SERVER.md: 94 tools; AUTO-COMBO.md: 12-factor). * fix(antigravity): sanitize Cloud Code safety settings (#6839) Co-authored-by: kfiramar <83420275+kfiramar@users.noreply.github.com> * fix(kiro): probe IdC region during profileArn discovery, cross-region (recovers #6099) (#6840) * fix(kiro): route Amazon Q runtime by profileArn region for cross-region IdC Enterprise AWS IAM Identity Center accounts whose IdC instance lives outside the two Amazon Q Developer profile regions (us-east-1 / eu-central-1) - e.g. eu-north-1 (Stockholm), start URL https://d-XXXX.awsapps.com/start - showed no limits and returned 502 on every request. Root cause: the backend used the IdC/OIDC token region (providerSpecificData.region, e.g. eu-north-1) for every CodeWhisperer runtime call, hitting q.eu-north-1.amazonaws.com - a host that does not exist as a Q Developer runtime endpoint. Per AWS docs ("Supported Regions for the Q Developer console and Q Developer profile"), the Q Developer *profile* (which produces the profileArn and hosts generateAssistantResponse / GetUsageLimits / ListAvailableModels / ListAvailableProfiles) is only hosted in us-east-1 and eu-central-1, regardless of the IdC region; "data is stored in the Region where you create the Amazon Q Developer profile." Fix (new open-sse/services/kiroRegion.ts) decouples the two regions: - providerSpecificData.region stays the IdC/OIDC region, used ONLY for oidc.{region}.amazonaws.com token mint/refresh. - The runtime region is derived from the profileArn (resolveKiroRuntimeRegion): profileArn region -> a valid stored profile region -> us-east-1. A stored IdC region that is not a Q profile region (eu-north-1) is ignored for runtime. - Profile discovery (discoverKiroProfileArnAcrossRegions) probes the Q profile regions (EU IdC -> eu-central-1 first) with the cross-region SSO token instead of q.{idcRegion}. Wired into: executors/kiro.ts (generateAssistantResponse targets the profile region), services/usage/kiro.ts (getKiroUsage multi-region discovery + profileArn runtime region so Limits resolves), services/kiroModels.ts (ListAvailableModels), and src/lib/oauth/providers/kiro.ts (login-time postExchange profile discovery). Adds tests/unit/kiro-idc-cross-region.test.ts (15 cases). All Kiro suites pass (60 tests). * fix(kiro): probe the IdC region too during profileArn discovery (any IdC region) Make profile discovery general for an IdC in ANY of the ~30 IdC-supported AWS regions (us-west-2, ap-southeast-2, me-central-1, af-south-1, ...), not just eu-north-1. buildKiroProfileDiscoveryRegions now probes the two documented Q Developer profile regions FIRST (us-east-1 / eu-central-1, EU-first for EMEA IdC regions to cut latency), then appends the IdC/stored region itself as a forward-compatible fallback: if AWS ever co-locates the profile with the IdC or expands the profile-region list, a same-region probe still finds it. Probing a region with no profile simply returns nothing and we fall through. The profileArn's own region remains authoritative for every runtime call (resolveKiroRuntimeRegion), so a newly-issued ARN in any region is honored automatically. Adds ap-southeast-2 (APAC) cross-region coverage and updates the discovery-order tests. --------- Co-authored-by: artickc <artur1992123@mail.ru> * feat(providers): manual context-window override for custom models (#4125) (#6822) Add a manual per-model "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 getting silently dropped from combo routing once the wrong value lands in the catalog. Reuses the existing Feature-5004 model_context_overrides table (source="manual") — already the priority-0 source getModelContextLimit() (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-models now accepts an optional contextWindowOverride (number to set, null to clear), persisted via setModelContextOverride/ removeModelContextOverride. - GET /api/provider-models surfaces the current override value + source back on each custom-model row. - CustomModelsSection.tsx: edit form gained a Context Window Override input + a badge on the model row when an override is set. Regression guard: tests/unit/provider-models-context-window-override-4125.test.ts (manual override wins over a misreported catalog value, GET round-trip, clearing via null, default-unchanged behavior). * feat(dashboard): improve Provider Quota page horizontal density (#3520) (#6815) QuotaCardGrid stacked every provider group vertically in a single flex flex-col container, and each group's own card grid didn't go multi-column until the md breakpoint. 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, filling horizontal whitespace sooner on narrower-but-not-mobile viewports. Regression guard: tests/unit/quota-card-grid-horizontal-layout.test.ts * refactor(usage): type saveRequestUsage with UsageEntry interface + any-budget ratchet (#3512) (#6809) Replace saveRequestUsage(entry: any) with a typed UsageEntry interface mirroring the usage_history columns 1:1. Fields stay optional/nullable since different writers (chatCore success/failure, rejected-request accounting, Codex Responses WS) populate the row incrementally; tokens stays unknown since callers pass either raw provider-shaped usage or the normalized {input,output,cacheRead,...} shape. Also cleaned the file's other any usages (getUsageHistory filter, getUsageDb next-cursor cast, appendRequestLog tokens param, getRecentLogs catch) so it now sits at zero any and can be added to the check:any-budget:t11 zero-any allowlist. Documents the DB-entity <-> TS-interface convention in docs/architecture/CODEBASE_DOCUMENTATION.md Sec 11. * feat(combo): strict budget-cap fallback policy for auto/* combos (#3470) (#6816) Auto-combo transparency + budget controls: the engine's budgetCap enforcement always degraded to the globally cheapest candidate when every candidate exceeded the cap - silently overspending instead of respecting the cap. - engine.ts: budgetFallback "cheapest" (default, legacy) | "strict" (BudgetExceededError when no candidate fits budgetCap) - requestControls.ts: X-OmniRoute-Budget-Fallback header + resolveRequestAutoControls() consolidating mode/budget/fallback parsing - resolveAutoStrategy.ts / autoConfig.ts: thread combo-level config.budgetFallback and catch BudgetExceededError into an HTTP 402 - chat.ts: switch to the consolidated resolveRequestAutoControls() helper (net line reduction, stays under the frozen file-size baseline) Regression guard: tests/unit/auto-combo-budget-fallback-3470.test.ts * fix(usage): honor xAI provider-reported exact cost (#6711) OmniRoute's calculateCost() always estimated request cost from token counts x static pricing, discarding xAI's exact provider-reported cost when present. xAI's chat-completions usage object reports the precise billed cost via cost_in_usd_ticks (docs.x.ai/developers/cost-tracking and the API reference's usage schema: "TICKS_IN_USD_CENT: i64 = 100_000_000" => 1e10 ticks/USD, e.g. 37756000 ticks ~= $0.0038). calculateCost()/computeCostFromPricing() now short-circuit to this exact figure when present -- before any pricing DB lookup, so it also works for models without a local pricing row -- and still fall back to the token-based estimate when it is absent. The field is threaded through both the streaming (extractUsage/normalizeUsage) and non-streaming (extractUsageFromResponse) usage-extraction paths. Corrected divisor vs upstream: the upstream PR used /1e12 (a 100x under-report, e.g. reporting $0.00123 as the doc's $0.123 example); this port uses the doc-verified /1e10 instead, confirmed against both the cost-tracking guide and the API reference's usage-object schema. Inspired-by: https://github.com/decolua/9router/pull/2453 Co-authored-by: ryanngit <74137224+ryanngit@users.noreply.github.com> * docs: rename /implement-prs → /merge-prs in Hard Rule #21 (skill renamed 2026-07-11) (#6847) * docs: refresh stale llm.txt facts + relocate design.md to docs/architecture/DESIGN_SYSTEM.md (#6849) * docs: refresh stale llm.txt facts + move design.md to docs/architecture/DESIGN_SYSTEM.md llm.txt was frozen at the v3.8.8 era (177 providers, 37 MCP tools, 14 strategies, 9-factor scoring, 75% coverage gate). Update every factual claim to the current state (248 providers, 94 tools / 30 scopes, 18 strategies, 12-factor scoring, ratchet + 60% floor, TS 6, current docs/ layout) and re-sync the 42 exact-copy i18n mirrors. design.md at the root was a standardization plan whose phases 1-6 all shipped; rewrite its header as a permanent reference and relocate it to docs/architecture/DESIGN_SYSTEM.md per the root-hygiene policy (root = configs + canonical docs only). * docs: add MDX frontmatter to DESIGN_SYSTEM.md (in-app docs pipeline requires it) * feat: per-model web-search interception rule (#3384) (#6814) * feat(routing): per-model web-search interception rule (#3384) Adds a per-provider/per-model interceptSearch rule (src/lib/db/interceptionRules.ts, key_value namespace interception_rules) that overrides the existing native web-search bypass defaults (Codex/Gemini/Claude->Claude passthrough) in webSearchFallback.ts. Wired at the existing prepareWebSearchFallbackBody() call site in chatCore.ts. Resolution precedence: per-model rule > provider-level rule > existing native-bypass defaults. This lands Phase 1-2 of the plan (rule store + search interception). Web-fetch interception and the dashboard UI toggle are tracked as follow-up phases. * fix(db): register interceptionRules in localDb re-export layer (db-rules gate) * fix(db): renumber interception_rules migration 119→120 (collision with model_capability_overrides) * feat: sidebar search/filter input (#4013) (#6810) * feat(dashboard): add search/filter input to the dashboard sidebar (#4013) Adds a search box at the top of the expanded sidebar that filters nav sections/groups/items client-side by label, so users don't have to hunt through the growing nav tree. Reuses the existing common.search / common.noResults i18n keys (no new locale edits needed) and the shared Input icon="search" pattern. Matching sections auto-expand while searching and the accordion/pin state is restored once the query is cleared. Filtering logic is extracted into a pure filterSidebarSectionsByQuery() helper (src/shared/utils/sidebarSearch.ts) so it is trivially unit testable independent of React/next-intl/next-navigation. * fix(test): move Sidebar.search test to a runner-collected path (test-discovery gate) * fix(i18n): backfill 194 missing pt-BR keys (#6695) (#6723) * fix(i18n): backfill 194 missing pt-BR keys and add key-parity regression test (#6695) * Merge branch 'release/v3.8.47' into fix/6695-i18n-drift Resolve i18n key-parity and CHANGELOG-fragment conflicts: - Convert the #6695 CHANGELOG.md bullet to a changelog.d/ fragment (the fragment convention landed on release/v3.8.47 after this PR branched, per changelog.d/README.md). - Backfill 61 additional pt-BR keys that entered en.json on release/v3.8.47 after this PR's original 194-key backfill, so the PR's own key-parity regression test (tests/unit/i18n-pt-br.test.ts) stays green against the moving release baseline. * Discover live Codex models (#6776) * Add live model discovery for provider catalog * Fix model discovery request headers * fix(codex): sync live model limits with local catalog * test(codex): split live model discovery coverage into dedicated route tests * fix(codex): use chatgpt account id for live model sync * Add GitHub-backed Codex model discovery fallback * fix(providers): tighten oauth config tests and provider model display comments * test: align client version expectations with release default * fix(codex): keep discovery complexity within baseline * fix: rebase live Codex model discovery onto release/v3.8.47, preserving kimi-web buildHeaders (#6308) Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> --------- Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com> Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> * feat(codex): echo requested effort-suffixed model id in Responses payloads (#3697) (#6820) * feat(codex): echo requested effort-suffixed model id in Responses payloads (#3697) Codex CLI compatibility shim: the Responses API response.created/ response.in_progress/response.completed payloads now carry a `model` field (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. - openai-responses.ts translator threads the upstream model into the Responses event objects (additive, omitted when unknown). - New isCodexOriginatedHeaders() (codexIdentity.ts) reuses PR #3481's originator/User-Agent detection, header-based so it still fires when a combo routes codex/gpt-5.5-xhigh to a non-codex upstream. - chatCore's existing opt-in #1311 echoModel pipeline now also fires automatically for Codex clients on the Responses API, regardless of the echoRequestedModelName setting. - responseModelEcho.ts now also rewrites the nested response.model field the Responses API uses (previously only top-level model). - /v1/models keeps returning models: [] for Codex (unchanged, #3481). Regression guard: tests/unit/codex-effort-model-echo-3697.test.ts. Closes #3697 * chore(merge): re-sync with release/v3.8.47 (restore CHANGELOG, keep own bullet) * chore(merge): re-sync with release/v3.8.47; move changelog bullet to changelog.d fragment (merge-storm proof) * feat(usage): surface Antigravity weekly quota alongside the 5-hour window (#4017) (#6818) * feat(usage): surface Antigravity weekly quota alongside the 5-hour window (#4017) Antigravity enforces both a 5-hour and a weekly usage limit, but the agy/antigravity quota widget only exposed the 5-hour window. The weekly limit isn't in the per-model retrieveUserQuota response already fetched — it lives in a separate, undocumented retrieveUserQuotaSummary RPC that groups models into families (Gemini Models, Claude and GPT models) with one weekly bucket per family. Adds a self-contained usage/antigravityWeeklyQuota.ts leaf: a cached, best-effort fetch of that RPC + a pure parser that extracts the weekly-labeled bucket per group (window inferred from bucketId/displayName text, matching the reverse-engineered shape documented by third-party Antigravity clients) into gemini_weekly/ claude_gpt_weekly quota entries, merged into the existing quotas map the widget already renders generically. A failed/unavailable RPC never affects the existing per-model quotas. Live VPS validation attempt (192.168.0.15, real antigravity account): both retrieveUserQuota and retrieveUserQuotaSummary currently return 429 RESOURCE_EXHAUSTED for that account, so the live response shape could not be captured directly. The parser was instead validated via TDD against the bucket shape documented by CodexBar (steipete/CodexBar), a third-party Antigravity client that reverse-engineered the same RPC, and is defensive against both response envelopes it has observed (top-level groups[] and nested quotaSummary.groups[]). * chore(merge): re-sync with release/v3.8.47 (restore CHANGELOG, keep own bullet) * chore(merge): re-sync with release/v3.8.47; move changelog bullet to changelog.d fragment (merge-storm proof) * feat: add Z.ai Web free web-cookie provider (#4056) (#6823) * feat(providers): add Z.ai Web free web-cookie provider (#4056) New zai-web web-session provider drives the free chat.z.ai consumer chat UI via a pasted browser cookie, distinct from the existing API-key zai/glm/glm-cn/glmt providers (api.z.ai). ZaiWebExecutor posts to chat.z.ai/api/chat/completions with the cookie forwarded both as Cookie and Authorization: Bearer <token>, and normalizes both z.ai's internal delta_content/phase SSE envelope and a pass-through OpenAI-shaped choices[].delta frame into standard chat-completion chunks. Registered in WEB_COOKIE_PROVIDERS, WEB_SESSION_CREDENTIAL_REQUIREMENTS, the provider registry (GLM-4.6/4.5/4.5V models), the executor factory, and tokenExtractionConfig.ts for in-app cookie capture. * fix(providers): regenerate translate-path golden for zai-web + reduce cognitive complexity * fix(providers): rename ZaiWebExecutor.buildHeaders to avoid incompatible BaseExecutor override * chore(merge): re-sync with release/v3.8.47; move changelog bullet to changelog.d fragment (merge-storm proof) * fix(codex): bump default client version to 0.144.0 (#6780) Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> * refactor(usage): extract per-group parsing in antigravityWeeklyQuota (cognitive-complexity gate 886→885, release-level drift from #6818 merge) * ci(quality): cut PR gate wall time without dropping protection (#6716) Collapse duplicate CI spend while keeping each gate's existence reason: - quality.yml: TIA __RUN_ALL__ defers full unit to fast-unit 4-shard (#6781); path filters via classify-pr-changes; docs-gates split; draft skip - ci.yml: wire docs/i18n/code path filters; ESLint JSON artifact for quality-gate; drop advisory typecheck:noimplicit; float actions/cache@v6 - TIA parity: memory/usage/combo/serial; **/*.test.mjs any depth; electron/bin no longer force unit __RUN_ALL__ - check:complexity-ratchets: one ESLint walk, ruleId-isolated baselines + cache - check:api-docs-refs + lib/apiRoutes: shared API route inventory - husky pre-push: intentionally light (gates live in pre-commit); CLAUDE.md + QUALITY_GATES.md docs synced - collect-metrics / lint:json: path.resolve cache path; Windows-safe eslint bin - env-doc allowlist for ESLINT_RESULTS_JSON / COMPLEXITY_ESLINT_REPORT - release-green --full-ci expects check:api-docs-refs (not docs-symbols alone) Tests: select-impacted, classify-pr-changes, api-routes lib, complexity-rule-count, validate-release-green. Reconciled after #6781 (fast-unit 2→4 shards) per maintainer request on #6716. Co-authored-by: Diego Rodrigues de Sa e Souza <8016841+diegosouzapw@users.noreply.github.com> * fix(docs): document Turbopack build memory tradeoff for RAM-constrained machines (#6409) (#6885) * fix(routing): recognize Kimi token-limit 400 as context overflow for combo fallback (#6637) (#6893) combo.ts's isContextOverflow400() guard required the literal word 'context' in the 400 error body before letting a combo fall through to the next target. Kimi's exact wording ('Your request exceeded model token limit: 262144 (requested: 308458)') never says 'context', so the guard misclassified it as a body-specific error and halted the whole combo instead of trying the next (larger-context) target. accountFallback.ts's CONTEXT_OVERFLOW_PATTERNS already recognized this wording one layer below (via checkFallbackError -> shouldFallback), so the two independently-maintained classifiers disagreed and the stricter one won. Export CONTEXT_OVERFLOW_PATTERNS from accountFallback.ts and reuse it inside combo.ts's isContextOverflow400() so both layers share a single source of truth. Regression test: tests/unit/repro-6637-kimi-token-limit.test.ts (RED on unfixed code -> GREEN after the fix). Existing #4519 guard tests (tests/unit/combo-param-validation-fallback-4519.test.ts) still pass, including the negative case that a genuinely body-specific 400 is NOT misclassified as overflow. * fix(providers): honor a provider-level proxy assigned to no-auth providers (#6272) (#6895) No-auth providers (mimocode, opencode, ...) are always dispatched with a single hardcoded connectionId ("noauth" — SYNTHETIC_NOAUTH_CONNECTION_ID in src/sse/services/auth.ts). No provider_connections row ever has id="noauth", so resolveProxyForConnection() in src/lib/db/settings.ts could never populate connectionRecord for them, and its provider-level proxy lookup (Steps 6/8) only runs when connectionRecord is present. A proxy assigned via Settings -> Providers -> mimocode was therefore silently ignored, reproducing the reporter's "same thing happen when i set the proxy directly in the provider menu" symptom. Adds a best-effort fallback (src/lib/db/settings/noAuthProxyFallback.ts): when connectionRecord could not be resolved, scan the known no-auth provider ids for a configured provider-level proxy (registry first, then legacy) before falling through to the global/direct steps. Regression test: tests/unit/proxy-noauth-provider-6272.test.ts (RED on unfixed code — resolved to level=direct/proxy=null; GREEN after the fix). * fix(dashboard): surface Claude extraUsage credits in quota card (#6806) (#6896) Enterprise-tier Claude accounts (default_raven_enterprise) don't get five_hour/seven_day utilization windows from Anthropic's OAuth usage endpoint — only an extra_usage credit-billing block. parseClaude() only read data.quotas, so quotas stayed {} and the dashboard showed "No quota data" even when extraUsage showed the account 100% exhausted. parseClaude() now folds an enabled extraUsage block into a credits-style quota row (mirroring parseCodex's bankedResetCredits pattern), both when quotas is empty and when it's already populated. * fix(db): share sql.js preinit across callers, fix named-param bind (#6628, #6802) (#6899) - preInitSqlJs() now memoizes an in-flight Promise (not just the resolved adapter) per filePath, so concurrent BATCH/STARTUP/HealthCheck/ ProviderLimitsSync callers at boot share one full-file read+WASM decode instead of each independently reloading the whole database — the thundering-herd amplifier of the OOM condition #6632 already partly fixed, left un-implemented by the reporter's own proposed fix (#6628). - sqljsAdapter's run/get/all now unwrap a lone named-parameter object (e.g. .all({ isActive: 1 }) for "WHERE is_active = @isActive", the same call shape getProviderConnections() already uses against better-sqlite3) before calling sql.js's stmt.bind(), expanding it to the @/:/$ sigil variants sql.js's own named-bind path requires. Previously the object was wrapped into an array and sql.js took the positional-bind path, throwing "Wrong API use : tried to bind a value of an unknown type ([object Object])." whenever the sql.js WASM fallback driver was active — exactly the error #6802 reported (misattributed to better-sqlite3). Regression tests added to tests/unit/db-adapters/driverFactory.test.ts and tests/unit/db-adapters/sqljsAdapter.test.ts, both proven RED against the prior code and GREEN after the fix. * fix(plugin): split OC-gate provider id from OmniRoute-facing routing id (#6859) (#6900) resolveOmniRoutePluginOptions() auto-prefixes providerId with "opencode-" (commit75b52e286) so OpenCode 1.17.8+'s native-adapter gate accepts it as a registered provider id. That prefixed value was being reused for the OmniRoute-server-facing identifiers too: mapRawModelToModelV2's id/providerID, mapComboToModelV2's providerID, and the dynamic provider hook's combo catalog keys. OmniRoute's server has no "opencode-<x>" provider alias, so every dispatched model failed credential lookup with "No credentials for opencode-omniroute" / "No active credentials for provider: opencode-omniroute". Add a second, unprefixed omnirouteProviderId field and thread it through the four dynamic-hook call sites that emit server-facing identifiers, while leaving the OC-gate-prefixed providerId in place for AuthHook.provider, provider registration (hook.id), and the static-catalog path (which OC strips before dispatch, per the existing static-block comments). * fix(compression): surface silently-dropped stacked-pipeline steps and fix inflation-guard no-op misfire (#6479, #6480, #6491) (#6901) Two related root causes in the stacked compression pipeline: - #6479/#6491: a dispatched step whose engine legitimately finds nothing eligible (session-dedup with no repeated blocks, ccr below its min-chars threshold) returns `{ stats: null }`. `mergeStackStep()` silently dropped that step from `engineBreakdown` with zero trace — no warning, no error. Now records a `"<engine>: skipped (no eligible content)"` validation warning for any null-stats step, covering every engine that follows this convention (session-dedup, ccr, headroom, relevance, llm, llmlingua, ionizer, readLifecycle), not just the two reported. - #6480: `finalizeStackedResult` ran the aggregate `guardPipelineInflation` check unconditionally, even when the loop-level `compressed` flag stayed false (no step ever advanced `currentBody`). Since tokens are trivially equal when nothing ran, the guard mislabeled a genuine no-op as `fallbackApplied: true` with a misleading "reverted to original" warning. Extracted the guard into `applyStackedInflationGuard()` in `pipelineGuards.ts` (keeps `strategySelector.ts` under its frozen line budget) and gated it on `compressed === true`. Also fixes `compression-pipeline-inflation-guard.test.ts`'s wire test, which passed a bare engine-id string to the pipeline; `normalizePipelineStep()` only recognizes a fixed set of built-in string aliases and silently downgrades any other string to `{ engine: "caveman" }`, so the test's custom inflating engine was never actually exercised. Passing a step object restores the test's original intent. New regression tests: tests/unit/compression/repro-6479-6491-null-stats-silent-drop.test.ts, tests/unit/compression/repro-6480-noop-guard-misfire.test.ts. * fix(mcp): de-duplicate TOTAL_MCP_TOOL_COUNT by tool name (#6854) (#6902) TOTAL_MCP_TOOL_COUNT in open-sse/mcp-server/server.ts summed collection sizes additively, double-counting tools registered in more than one collection. The agent-skills trio (omniroute_agent_skills_list/get/coverage) is intentionally defined in both MCP_TOOLS (schemas/tools.ts) and agentSkillTools (tools/agentSkillTools.ts), inflating the reported count from 96 unique tools to 99. Replace the additive sum with countUniqueMcpTools() (new open-sse/mcp-server/toolCount.ts), which unions all collection tool names into a Set before counting, so any future overlap self-corrects instead of double-counting. Regression test: tests/unit/mcp-tool-count-dedup-6854.test.ts * fix(sse): apply cliproxyapiModelMapping at CLIProxyAPI dispatch time (#6876) (#6903) * fix(providers): honor max_token capability override in reasoning buffer clamp (#6524) (#6904) getExplicitModelOutputCap() (the clamp ceiling used by resolveReasoningBufferedMaxTokens) only ever read the unvalidated synced limit_output / registry / static-spec chain — it ignored the operator-settable max_token capability override that getResolvedModelCapabilities() already consulted. When a provider's synced catalog row reports a wrong limit_output (e.g. ollama-cloud/deepseek-v4-flash: limit_output=1048576, same as limit_context, while the real upstream cap is 65536), the reasoning-buffer clamp trusted the bad number and inflated max_tokens 64000 -> 96000, which upstream rejected with "exceeds model's maximum output tokens (65536)". The override table (model_capability_overrides, "max_token" key, /api/model-capability-overrides) is the existing, already-shipped remediation path for exactly this class of bad catalog data, but reasoningTokenBuffer.ts had no way to benefit from it. Extracted the override lookup into a shared getMaxTokenCapabilityOverride() helper and made getExplicitModelOutputCap() consult it first, so both read paths now agree. * fix(api): merge id-only tool_call continuation deltas in stream summary (#6276) (#6905) * fix(dashboard): logs detail modal no longer reopens on first close (#6830) LogsPage recomputed initialId from window.location on every render, but the App Router syncs window.location only after the navigation commits. Closing the detail modal re-rendered the page while the URL still carried ?id=X, so initialSelectedId flipped null -> X and the child's one-shot deep-link effect (guard still unarmed after the open-click render, where location was stale in the other direction) reopened the modal. Only the second close worked. Read the id once via lazy useState so the prop stays stable for the page's lifetime; deep links still open the modal on mount. Regression test reproduces the App Router ordering with a router.replace mock that re-renders the page before committing the URL. * fix(fusion): select judge from a surviving panel member when no explicit judge (#6869) When no explicit judgeModel is configured, the judge defaulted to panel[0] before fan-out and was never reassigned. If panel[0] failed fan-out (timeout / rate-limit / dropped straggler → it lands in `failures`, not `answers`), the multi-answer synthesis path still dispatched the judge to that dead panel[0], erroring the whole fusion request even though a quorum of other panel members succeeded — exactly the failure fusion exists to tolerate. Resolve the effective synthesis judge from a survivor when no explicit judge is set: prefer panel[0] only when it survived, otherwise the first surviving answer. An explicitly configured judge is still honored unchanged (operator intent), and the answers.length===0 (503) and single-survivor branches keep their existing semantics. Co-authored-by: Chirag Singhal <chirag127@users.noreply.github.com> * fix(api): return 400 (not 500) on malformed JSON body (#6871) Co-authored-by: Chirag Singhal <chirag127@users.noreply.github.com> * chore(ci): fix shared base-reds blocking PR queue (stryker registration + codex 0.144 test) - register tests/unit/cliproxyapi-model-mapping-dispatch.test.ts in stryker.conf.json tap.testFiles (gap from #6903) - update provider-models-route-codex.test.ts client_version 0.142.0 -> 0.144.0 (stale test from #6780 prod bump) * chore(quality): rebaseline cognitiveComplexity 885->890 (v3.8.47 merge-train burst) Owner-approved merge-burst reconciliation. cognitive-complexity does not run on PR->release fast-gates, so incidental growth across the 23-PR merge-ready batch accrued unmeasured (measured 890 on the combined merge-train tip vs 885 pristine). * chore(deps): bump github/codeql-action/analyze from 4.36.3 to 4.37.0 (#6831) * fix(electron): bump electron 42→43 + build better-sqlite3 from source (ABI 148) (#6605) fix(electron): bump electron 42→43 + rebuild better-sqlite3 from source against the Electron ABI (148). Electron 43 raises NODE_MODULE_VERSION to 148; better-sqlite3@12.11.1 has no electron-v148 prebuild, so the packaged app died with 'Nenhum driver SQLite disponível'. prepare-electron-standalone now compiles better-sqlite3 from source against the electron headers into build/Release (where 'bindings' resolves it). Validated by Electron Package Smoke (green) + local (node_register_module_v148). Supersedes #6378. (--admin: the only reds are SonarQube/SonarCloud failing on a coverage-report artifact digest-mismatch — a GitHub Actions infra flake, not this diff; Sonar is green on main and the diff touches only the electron build.) * deps: bump the development group across 1 directory with 6 updates (#6588) deps: bump the development group (6 updates). Rebased onto current main; all checks green after the electron-smoke fix (#6605). * fix(proxy): force CONNECT tunnel for HTTP proxied requests (undici 8.7) + production deps bump (#6620) fix(proxy): force CONNECT tunnel for HTTP proxied requests (undici 8.7) + production deps bump. undici 8.6+ changed ProxyAgent to forward plain-HTTP via request-proxy instead of CONNECT, breaking OAuth refresh through a connection proxy (501). proxyDispatcher now passes proxyTunnel:true. Validated: Unit Tests 3/8 (the OAuth-proxy test) green, new regression test green (fails without the fix on undici 8.7), SonarQube green. Supersedes #6380. (--admin: the only red is Electron Package Smoke failing on a next-build artifact 'digest-mismatch' — a GitHub Actions infra flake corrupting the asar ('file data stream has unexpected number of bytes'); the better-sqlite3 rebuild itself succeeded (gyp ok) and the electron path is unchanged from #6605 which passed the smoke. Not this diff.) * chore(deps): bump github/codeql-action/analyze from 4.36.3 to 4.37.0 Bumps [github/codeql-action/analyze](https://github.com/github/codeql-action) from 4.36.3 to 4.37.0. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](54f647b7e1...99df26d4f1) --- updated-dependencies: - dependency-name: github/codeql-action/analyze dependency-version: 4.37.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: Diego Rodrigues de Sa e Souza <8016841+diegosouzapw@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump github/codeql-action/init from 4.36.3 to 4.37.0 (#6832) * fix(electron): bump electron 42→43 + build better-sqlite3 from source (ABI 148) (#6605) fix(electron): bump electron 42→43 + rebuild better-sqlite3 from source against the Electron ABI (148). Electron 43 raises NODE_MODULE_VERSION to 148; better-sqlite3@12.11.1 has no electron-v148 prebuild, so the packaged app died with 'Nenhum driver SQLite disponível'. prepare-electron-standalone now compiles better-sqlite3 from source against the electron headers into build/Release (where 'bindings' resolves it). Validated by Electron Package Smoke (green) + local (node_register_module_v148). Supersedes #6378. (--admin: the only reds are SonarQube/SonarCloud failing on a coverage-report artifact digest-mismatch — a GitHub Actions infra flake, not this diff; Sonar is green on main and the diff touches only the electron build.) * deps: bump the development group across 1 directory with 6 updates (#6588) deps: bump the development group (6 updates). Rebased onto current main; all checks green after the electron-smoke fix (#6605). * fix(proxy): force CONNECT tunnel for HTTP proxied requests (undici 8.7) + production deps bump (#6620) fix(proxy): force CONNECT tunnel for HTTP proxied requests (undici 8.7) + production deps bump. undici 8.6+ changed ProxyAgent to forward plain-HTTP via request-proxy instead of CONNECT, breaking OAuth refresh through a connection proxy (501). proxyDispatcher now passes proxyTunnel:true. Validated: Unit Tests 3/8 (the OAuth-proxy test) green, new regression test green (fails without the fix on undici 8.7), SonarQube green. Supersedes #6380. (--admin: the only red is Electron Package Smoke failing on a next-build artifact 'digest-mismatch' — a GitHub Actions infra flake corrupting the asar ('file data stream has unexpected number of bytes'); the better-sqlite3 rebuild itself succeeded (gyp ok) and the electron path is unchanged from #6605 which passed the smoke. Not this diff.) * chore(deps): bump github/codeql-action/init from 4.36.3 to 4.37.0 Bumps [github/codeql-action/init](https://github.com/github/codeql-action) from 4.36.3 to 4.37.0. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](54f647b7e1...99df26d4f1) --- updated-dependencies: - dependency-name: github/codeql-action/init dependency-version: 4.37.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: Diego Rodrigues de Sa e Souza <8016841+diegosouzapw@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * feat(provider): add OpenVecta AI inference gateway (#6833) * fix(electron): bump electron 42→43 + build better-sqlite3 from source (ABI 148) (#6605) fix(electron): bump electron 42→43 + rebuild better-sqlite3 from source against the Electron ABI (148). Electron 43 raises NODE_MODULE_VERSION to 148; better-sqlite3@12.11.1 has no electron-v148 prebuild, so the packaged app died with 'Nenhum driver SQLite disponível'. prepare-electron-standalone now compiles better-sqlite3 from source against the electron headers into build/Release (where 'bindings' resolves it). Validated by Electron Package Smoke (green) + local (node_register_module_v148). Supersedes #6378. (--admin: the only reds are SonarQube/SonarCloud failing on a coverage-report artifact digest-mismatch — a GitHub Actions infra flake, not this diff; Sonar is green on main and the diff touches only the electron build.) * deps: bump the development group across 1 directory with 6 updates (#6588) deps: bump the development group (6 updates). Rebased onto current main; all checks green after the electron-smoke fix (#6605). * fix(proxy): force CONNECT tunnel for HTTP proxied requests (undici 8.7) + production deps bump (#6620) fix(proxy): force CONNECT tunnel for HTTP proxied requests (undici 8.7) + production deps bump. undici 8.6+ changed ProxyAgent to forward plain-HTTP via request-proxy instead of CONNECT, breaking OAuth refresh through a connection proxy (501). proxyDispatcher now passes proxyTunnel:true. Validated: Unit Tests 3/8 (the OAuth-proxy test) green, new regression test green (fails without the fix on undici 8.7), SonarQube green. Supersedes #6380. (--admin: the only red is Electron Package Smoke failing on a next-build artifact 'digest-mismatch' — a GitHub Actions infra flake corrupting the asar ('file data stream has unexpected number of bytes'); the better-sqlite3 rebuild itself succeeded (gyp ok) and the electron path is unchanged from #6605 which passed the smoke. Not this diff.) * feat(provider): add OpenVecta AI inference gateway OpenVecta (https://openvecta.com/) is an OpenAI-compatible AI inference gateway hosting LLMs (GLM, Claude, DeepSeek, GPT OSS, Llama, Kimi, Nemotron...) plus text-embedding-* models behind a single Bearer key. Wiring (7 integration points): - src/shared/constants/providers/apikey/inference-hosts.ts: catalog entry - open-sse/config/providers/registry/openvecta/index.ts: registry w/ 9 seed LLMs - open-sse/config/providers/index.ts: wire into REGISTRY - src/app/api/providers/[id]/models/discovery/providerModelsConfig.ts: live /v1/models URL - src/app/api/providers/[id]/models/discovery/providerSets.ts: NAMED_OPENAI_STYLE_PROVIDERS - public/providers/openvecta.svg: brand icon - tests/unit/openvecta-provider-registration.test.ts: regression guard (6 tests, all pass) No executor needed — buildOpenAiCompatibleRegistryEntry wires format=openai / executor=default / authType=apikey / authHeader=bearer. Live catalog discovery uses the existing NAMED_OPENAI_STYLE_PROVIDERS path (live /v1/models fetch + registry seed as offline fallback). Validation: - npm run typecheck:core clean - npm run typecheck:noimplicit:core 4 errors in unchanged files (combo.ts, cliRuntime.ts); 0 in new code - npm run lint clean - node --import tsx/esm --test tests/unit/openvecta-provider-registration.test.ts 6/6 pass - sibling tests/unit/openai-style-providers-4239-4155-3841.test.ts 18/18 pass (no regression) * chore(merge): drop unrelated main-drift from PR fork + fix count/golden drift The fork branch predated main's electron 42→43 bump (#6605) and several other package.json/lockfile churn; those files are unrelated to the OpenVecta provider addition and were reintroducing an older/stale state (version 3.8.46, electron 42, older bun/eslint-config-next) that broke the Electron Package Smoke check. Restored package.json, package-lock.json, electron/package.json, electron/package-lock.json, and scripts/build/prepare-electron-standalone.mjs to match origin/release/v3.8.47. Also updates the two provider-count assertions (166->167) and regenerates the translate-path golden snapshot to account for the new openvecta entry. Co-authored-by: hajilok <120608486+hajilok@users.noreply.github.com> --------- Co-authored-by: Diego Rodrigues de Sa e Souza <8016841+diegosouzapw@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com> Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com> * fix(sse): combo model lockout honors parsed upstream quota reset (#6863) (#6866) * fix(sse): combo model lockout honors parsed upstream quota reset (#6863) The combo failure path recorded model lockouts from checkFallbackError's cooldownMs only, discarding quotaResetHintMs — the ungated channel that carries a parsed upstream quota reset (e.g. Antigravity 429 "Resets in 92h27m28s"). With OAuth profiles defaulting useUpstreamRetryHints=false, the lockout fell back to the base cooldown (seconds), so quota-dead accounts were re-walked serially by every combo request for days (measured 122s per request, 494s worst case in #6863). Thread max(cooldownMs, quotaResetHintMs) into selectLockoutCooldownMs at both combo lockout sites, mirroring the single-model path pattern in src/sse/services/auth.ts (v3.8.43). All three resilience fences are preserved: useUpstreamRetryHints still gates connection cooldowns, the hint only affects model-scope lockouts, and combo 429s remain non-persistent. TDD: tests/unit/combo-lockout-quota-reset-6863.test.ts fails on base (lockout 5000ms) and passes with the fix (~92.5h). * test(sse): tighten #6863 lockout assertion to parsed-reset bounds; prettier pass Assert remainingMs falls within (parsedResetMs - 5s, parsedResetMs] so a hardcoded long cooldown cannot satisfy the regression test. Also apply Prettier to both changed files (includes one pre-existing formatting fix in handleRoundRobinCombo picked up by --write). * fix(sse): align combo lockout hint selection with single-model path; register test in mutation gate Adopt review feedback: replace Math.max(cooldownMs, quotaResetHintMs) with the auth.ts pattern (usedUpstreamRetryHint ? cooldownMs : quotaResetHintMs) so a parsed reset SHORTER than the fallback cooldown wins too — e.g. the subscription-quota branch returns a 1h fallback while the body says "resets in 45m"; max() would over-lock by 15 minutes. Add a regression test for the short-reset case (fails against the max() variant) and register the new test file in stryker.conf.json tap.testFiles to satisfy check:mutation-test-coverage --strict. * chore(quality): register cliproxyapi dispatch test in mutation gate tests/unit/cliproxyapi-model-mapping-dispatch.test.ts landed on release/v3.8.47 via #6903 without a tap.testFiles entry, so check:mutation-test-coverage --strict fails on the branch tip and on every PR merge ref. Register it so the gate is green again. --------- Co-authored-by: judy459 <JUDYZHU459@outlook.com> Co-authored-by: Diego Rodrigues de Sa e Souza <8016841+diegosouzapw@users.noreply.github.com> * feat(proxy): shorthand proxy formats + protocol header mode for bulk import (#6867) * fix(electron): bump electron 42→43 + build better-sqlite3 from source (ABI 148) (#6605) fix(electron): bump electron 42→43 + rebuild better-sqlite3 from source against the Electron ABI (148). Electron 43 raises NODE_MODULE_VERSION to 148; better-sqlite3@12.11.1 has no electron-v148 prebuild, so the packaged app died with 'Nenhum driver SQLite disponível'. prepare-electron-standalone now compiles better-sqlite3 from source against the electron headers into build/Release (where 'bindings' resolves it). Validated by Electron Package Smoke (green) + local (node_register_module_v148). Supersedes #6378. (--admin: the only reds are SonarQube/SonarCloud failing on a coverage-report artifact digest-mismatch — a GitHub Actions infra flake, not this diff; Sonar is green on main and the diff touches only the electron build.) * deps: bump the development group across 1 directory with 6 updates (#6588) deps: bump the development group (6 updates). Rebased onto current main; all checks green after the electron-smoke fix (#6605). * fix(proxy): force CONNECT tunnel for HTTP proxied requests (undici 8.7) + production deps bump (#6620) fix(proxy): force CONNECT tunnel for HTTP proxied requests (undici 8.7) + production deps bump. undici 8.6+ changed ProxyAgent to forward plain-HTTP via request-proxy instead of CONNECT, breaking OAuth refresh through a connection proxy (501). proxyDispatcher now passes proxyTunnel:true. Validated: Unit Tests 3/8 (the OAuth-proxy test) green, new regression test green (fails without the fix on undici 8.7), SonarQube green. Supersedes #6380. (--admin: the only red is Electron Package Smoke failing on a next-build artifact 'digest-mismatch' — a GitHub Actions infra flake corrupting the asar ('file data stream has unexpected number of bytes'); the better-sqlite3 rebuild itself succeeded (gyp ok) and the electron path is unchanged from #6605 which passed the smoke. Not this diff.) * feat(proxy): add shorthand formats + protocol header mode for bulk import Supports 6 new shorthand formats alongside the existing pipe-delimited parser: - ip:port - ip:port:user:pass - user:pass@ip:port - user:pass:ip:port - protocol://ip:port - protocol://user:pass@ip:port Protocol header mode: a bare protocol name (http/https/socks5) on its own line sets the default type for subsequent protocol-less shorthand lines. Explicit protocol:// prefix always takes precedence over the header default. Changes: - Rewrite parseBulkProxyImport.ts with parseShorthandLine helper - Use Record<string, true> for static lookup tables (VALID_PROXY_TYPES, VALID_PROXY_STATUSES) per project convention - Add looksLikeHost() heuristic to disambiguate 4-colon format (ip:port:user:pass vs user:pass:ip:port) - Update BULK_IMPORT_TEMPLATE in ProxyRegistryManager.tsx with full documentation and examples for all formats - Update en.json bulkImportDescription to list all supported formats - Add 30 unit tests covering every format, edge cases, and regressions for the existing pipe-delimited path --------- Co-authored-by: Diego Rodrigues de Sa e Souza <8016841+diegosouzapw@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com> * fix(sse): stop combo path tripping whole-provider breaker on plain 429 (#6868) * fix(sse): stop combo path tripping whole-provider breaker on plain 429 The combo path recorded a whole-provider circuit-breaker failure for a plain rate-limit 429, opening the breaker after N consecutive 429s and blocking every account+model on that provider. This contradicts the single-model path and the documented RESILIENCE_GUIDE policy. - Single-model path uses PROVIDER_BREAKER_FAILURE_STATUSES = Set([408, 500, 502, 503, 504]) (src/sse/handlers/chat.ts:206) — 429 excluded. - Combo path gated shouldRecordProviderBreakerFailure on isProviderFailureCode (accountFallback.ts), whose PROVIDER_FAILURE_ERROR_CODES INCLUDES 429 for connection-cooldown scope — so a plain 429 wrongly tripped the whole-provider breaker. Fix scopes tightly: comboPredicates now tests a local PROVIDER_BREAKER_FAILURE_STATUSES set mirroring the single-model constant (429 excluded), instead of isProviderFailureCode. The shared isProviderFailureCode / PROVIDER_FAILURE_ERROR_CODES are deliberately left untouched — they drive connection-cooldown / model-lockout logic where 429 must still count. A genuine quota/token-limit terminal 429 is handled elsewhere; only the whole-provider breaker-recording gate changes. Adds tests/unit/combo-breaker-429.test.ts covering the 429 exclusion, the 408/5xx inclusion, and the sameProviderNext / skipProviderBreaker suppression paths. * test(quality): register combo-breaker-429.test.ts in stryker tap.testFiles Fast Quality Gates' mutation-coverage drift check flagged this PR's new covering test for comboPredicates.ts as unregistered. Co-authored-by: Chirag Singhal <chirag127@users.noreply.github.com> --------- Co-authored-by: Chirag Singhal <chirag127@users.noreply.github.com> Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com> Co-authored-by: Diego Rodrigues de Sa e Souza <8016841+diegosouzapw@users.noreply.github.com> * fix(oauth): embed Trae OAuth client_id via resolvePublicCred (Hard Rule #11) (#6870) * fix(oauth): embed Trae OAuth client_id via resolvePublicCred (Hard Rule #11) * fix(quality): register tests/unit/trae-publiccred.test.ts in stryker tap.testFiles The mutation test-coverage gate (check:mutation-test-coverage --strict) flags new covering unit tests that mutate open-sse/utils/publicCreds.ts but aren't listed in stryker.conf.json's tap.testFiles, so their mutant kills wouldn't count. Register the new test file alphabetically. Co-authored-by: Chirag Singhal <chirag127@users.noreply.github.com> --------- Co-authored-by: Chirag Singhal <chirag127@users.noreply.github.com> Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com> Co-authored-by: Diego Rodrigues de Sa e Souza <8016841+diegosouzapw@users.noreply.github.com> * fix(ci): publish electron-updater latest.yml manifests in release assets (#6766) (#6881) * fix(ci): publish electron-updater latest.yml manifests in release assets (#6766) * fix(ci): register new mutation-covering test + realign stale codex-cli version fixture - stryker.conf.json: add tests/unit/cliproxyapi-model-mapping-dispatch.test.ts to tap.testFiles so it counts toward mutation coverage for the newly-added comboContextCache.ts coverage (check:mutation-test-coverage --strict was failing). - tests/unit/provider-models-route-codex.test.ts: DEFAULT_CODEX_CLIENT_VERSION was bumped to 0.144.0 on release/v3.8.47 after this test's fixtures were written; realign the hardcoded 0.142.0 expectations to the current constant. * fix(ci): exclude check-test-masking.test.ts fixtures from self-referential tautology gate (#6634) (#6884) * fix(ci): exclude check-test-masking.test.ts fixtures from self-referential tautology gate (#6634) * fix(ci): extend test-masking self-fixture exclusion to sibling gate regression files (#6634) The #6634 fix added isSelfTestFixtureFile()/scanBareTautologies() exclusions that only matched check-test-masking.test.ts exactly. Its own new regression file check-test-masking-selfref-6634.test.ts also embeds tautology-pattern literals as fixtures/documentation, so the absolute-floor scanBareTautologies gate self-tripped a HARD failure on the PR's own file. Generalize the exclusion to the whole check-test-masking* self-test family and lock it with two regression tests. * fix(api): route error responses through sanitizeErrorMessage (Hard Rule #12) (#6886) * fix(api): route error responses through sanitizeErrorMessage (Hard Rule #12) 9 API routes returned raw String(error)/error.message directly in HTTP 500 bodies, leaking SQLite paths, SQL text and internal messages. Route all through sanitizeErrorMessage() per Hard Rule #12: - settings/compression (GET+PUT), settings/compression/mcp-accessibility (GET+PUT) - cache/entries (GET+POST), db/health (GET+POST), db-backups/exportAll - assess, combos/test, settings/notion, settings/obsidian Test: tests/unit/rule12-error-sanitization-sweep.test.ts asserts sanitized 500 bodies contain no absolute paths / stack tails. * test(stryker): register rule12 error-sanitization sweep in tap.testFiles The new tests/unit/rule12-error-sanitization-sweep.test.ts covers a mutated module, so it must be listed in stryker.conf.json tap.testFiles for the mutation-coverage gate (check-mutation-test-coverage.mjs --strict) to pass. Co-authored-by: Chirag Singhal <chirag127@users.noreply.github.com> --------- Co-authored-by: Chirag Singhal <chirag127@users.noreply.github.com> Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com> Co-authored-by: Diego Rodrigues de Sa e Souza <8016841+diegosouzapw@users.noreply.github.com> * fix(routing): honor no-auth provider connection isActive in auto-combo pool (#6557) (#6889) * fix(providers): strip redundant node prefix on connId-addressed custom models (#6772) (#6890) * fix(providers): give v0-vercel-web its own alias so credentials are detected (#6343) (#6891) * fix(providers): give v0-vercel-web its own alias so credentials are detected (#6343) * test(6343): type casts to satisfy no-explicit-any gate * test(6343): register v0-web + cliproxyapi tests in stryker tap.testFiles The two unit tests added on this branch cover mutated modules (src/sse/services/auth.ts, comboContextCache.ts) but were missing from stryker.conf.json tap.testFiles, tripping check:mutation-test-coverage --strict. Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com> * fix(cli): waitForServer must not report ready on bare TCP accept (#6800) (#6892) waitForServer() polled /api/monitoring/health but fell back to declaring the server ready once the port had merely accepted TCP connections for >= 3s, even if no HTTP response was ever received. On CPU-bound warmup (e.g. small VPS running Next.js standalone), the OS-level listener accepts TCP almost immediately while the request pipeline is still compiling, so the fallback fired within ~3-7s and the CLI printed 'OmniRoute is running!' 30-60s before any route actually answered. Classify each health poll into ready / fast-reject / hanging / not-listening: only a fast HTTP rejection (fetch error that is not a timeout, e.g. ECONNRESET before the route mounts) grants the original #2460 Windows-cold-start grace window. A request that times out with zero response (the reported #6800 symptom) resets the grace window instead of accumulating toward it. Regression tests: tests/unit/waitForServer-tcp-fallback-6800.test.mjs (new RED-then-GREEN probe from the bug analysis) and tests/unit/cli-waitForServer.test.mjs (existing suite realigned to the corrected contract, plus a new case for the hanging-socket scenario). * fix(providers): wire devin cloud-agent into provider validation and static models (#6142) (#6894) * fix(sse): de-flake timing-sensitive combo cooldown/breaker tests (#6803) (#6897) * fix(sse): de-flake timing-sensitive combo cooldown/breaker tests (#6803) Extracts 3 wall-clock-sensitive assertions (combo-quota-share cooldown ceiling x2, circuit-breaker HALF_OPEN race) into tests/unit/serial/ (--test-concurrency=1, the repo's established remedy for this class of test) and widens their margins, since a starved CI-runner event loop can blow even a serialized test's timing window. Also adds an explicit 30s vitest timeout to the MCP audit shutdown test, which had no override and inherited vitest's 5000ms default. Regression proof: reproduced RED locally under real devbox CPU contention (2644ms/1796ms elapsed vs the old 1500ms ceiling, exactly the reported failure mode); confirmed GREEN after the fix under the same contention. * fix(quality): register new serial timing tests + prune stale any-suppression count - stryker.conf.json: add tests/unit/serial/combo-quota-share-cooldown-wait-timing.test.ts and tests/unit/serial/combo-strategy-fallbacks-half-open-timing.test.ts to tap.testFiles so their mutant kills count for accountFallback.ts and circuitBreaker.ts (PR #6897 added these files but didn't register them). - eslint-suppressions.json: combo-strategy-fallbacks.test.ts's no-explicit-any suppression count was stale (35) after this PR trimmed 2 any-usages out of the file when extracting the half-open timing test; corrected to 33. Co-authored-by: Diego Rodrigues de Sa e Souza <souzamiriamrodrigues790@gmail.com> --------- Co-authored-by: Diego Rodrigues de Sa e Souza <souzamiriamrodrigues790@gmail.com> * fix(db): stop legacy log-archive migration from deleting the live app-logger directory and crashing startup on a stat/stream race (#6401) (#6898) archiveLegacyRequestLogs() swept the entire DATA_DIR/logs directory as a single "legacy" target and recursively deleted it after zipping. Since PR #6234 moved the default app-log path to DATA_DIR/logs/application, the migration was deleting the live file logger's own directory on every boot until its marker file existed (#6799). Separately, yazl's addFile() does an internal stat-then-stream read; if a target file grows between those two steps (e.g. an actively-written log), yazl emits "error" directly on the ZipFile instance. That event had no listener, so Node re-threw it as an uncaughtException that crashed the whole process at startup (#6401) — misdiagnosed upstream as Turbopack/Windows chunk corruption because the stack trace pointed into a bundled chunk. Fix: - listArchiveTargets() now enumerates DATA_DIR/logs entries individually and skips the live app-logger directory (resolved via logEnv.getAppLogFilePath()), so the shared parent directory is never deleted wholesale. - createLegacyArchive() wires a zipFile.on("error", ...) handler so a stat/stream race rejects the promise (caught by the existing try/catch) instead of escaping as an uncaughtException. Regression test: tests/unit/usage-migrations-legacy-archive-safety.test.ts (RED on unfixed code, GREEN after fix). Updated tests/unit/request-log-migration.test.ts to the corrected contract — DATA_DIR/logs itself now survives the archive sweep. Gates run clean: file-size, complexity, cognitive-complexity, changelog-integrity, typecheck:core, eslint (suppressions), and the existing usage-migrations/request-log-migration unit suites. * fix(cli): ship head-response-guard.cjs in the standalone bundle (#6908) * fix(cli): ship head-response-guard.cjs in the standalone bundle server-ws.mjs imports ./head-response-guard.cjs, but assembleStandalone had no EXTRA_MODULE_ENTRIES entry for it, so every build:release bundle crashed at boot with ERR_MODULE_NOT_FOUND (found deployingd1d75fdbfto the VPS on 2026-07-11). Adds the missing entry plus a regression test that derives the required sidecar list from server-ws.mjs's own relative imports, guarding the whole class of missing-sidecar bugs. * docs(changelog): fragment for #6908 * fix(responses): escape literal control chars in tool call JSON; emit … (#6786) * fix(responses): escape literal control chars in tool call JSON; emit status=failed on upstream error #6785 Two bugfixes in the Responses API translator: 1. escapeJsonStringValues() sanitizes tool call arguments containing literal 0x0A/0x0D/0x09 bytes (emitted by Gemma4 models) into valid JSON \n/\r/\t escapes, preventing SSE framing corruption. Only escapes inside JSON string contexts — already-escaped sequences and structural JSON pass through unchanged. 2. sendCompleted() checks state.upstreamError and emits status="failed" with error.code + error.message instead of silently hardcoding status="completed" + error=null, so mid-stream errors (e.g. Gemini 503 after partial content) are properly surfaced to the client. 3. stream.ts: calls translateResponse(null,...) before controller.error() so the translator can emit close events (reasoning item done, response.completed) before the stream is terminated. * test(boundary): fix ESLint no-explicit-any warnings and quality gates Green the PR against release/v3.8.47 quality gates without weakening tests: - Replace @typescript-eslint/no-explicit-any in the new boundary/gemma4 tests with proper interfaces (ResponseBody, ToolDef, ToolArgs, SseEvent item accessors) — fixes the "No new ESLint warnings" gate. - Split tests/unit/translator-resp-openai-responses.test.ts (1079 LOC) by extracting the round-trip suite into a sibling file so both stay under the 800-line test cap — fixes check:file-size. - Rename the 5 live boundary tests to *.live.test.ts, gate them behind RUN_BOUNDARY_LIVE=1, add a test:boundary:live npm script and register the glob in check-test-discovery COLLECTORS — fixes check:test-discovery (they hit a live remote and must never run unopted in CI). Co-authored-by: Markus Hartung <mail@hartmark.se> --------- Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com> * fix(providers): route AgentRouter key validation through CC wire image (#6377) (#6882) * fix(providers): route AgentRouter key validation through CC wire image (#6377) * test(6377): type fetch mock to satisfy no-explicit-any gate * fix(api): recognize OpenRouter reasoning/reasoning_details in non-streaming OpenAI-to-Claude conversion (#6623) (#6887) * fix(providers): scope nvidia NIM 404s to the single failing model (#6773) (#6888) * fix(providers): scope nvidia NIM 404s to the single failing model (#6773) The nvidia registry entry multiplexes 17 models from 9 different upstream vendors (z-ai/, minimaxai/, deepseek-ai/, qwen/, mistralai/, stepfun-ai/, moonshotai/, openai/, nvidia/) behind one connection, but was missing passthroughModels: true — unlike 34 other multi-model registries (modelscope, synthetic, kilo-gateway, etc). Without it, hasPerModelQuota returns false for nvidia, so a 404 on a single stale/renamed model falls through checkFallbackError's generic catch-all as a connection-wide cooldown instead of being scoped to just that model, poisoning all 17 nvidia models for the cooldown window. Add passthroughModels: true to the nvidia registry entry so 404/429s on one model lock out only that model. Regression test: tests/unit/nvidia-passthrough-models-6773.test.ts * fix(quality): register nvidia passthrough test in stryker tap.testFiles check-mutation-test-coverage.mjs --strict flagged tests/unit/nvidia-passthrough-models-6773.test.ts as an unregistered covering test for accountFallback.ts (Fast Quality Gates). Co-authored-by: Diego Rodrigues de Sa e Souza <souzamiriamrodrigues790@gmail.com> --------- Co-authored-by: Diego Rodrigues de Sa e Souza <souzamiriamrodrigues790@gmail.com> * fix(usage): strict validation for xAI exact provider-reported cost (#6856) extractUsageFromResponse() and normalizeUsage() used Number(x) coercion for cost_in_usd_ticks, which silently turned null/"" into 0 -- accepted downstream as a valid $0 exact cost instead of falling back to the token-based estimate. Both call sites now require typeof === "number" && Number.isFinite && >= 0. Rebased onto current release/v3.8.47 tip (already carries #6711) and trimmed to just the incremental validation fix + 2 regression tests, replacing the stale-base diff that re-added the whole already-merged feature. Co-authored-by: KooshaPari <koosha@phenotype.io> Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> * fix(sse): treat compression no-op as zero-savings, not inflation/silent-drop (#6883) A structural engine (ccr / session-dedup) that finds nothing to compress returns the body unchanged. That no-op was mishandled three ways — the code-level root cause of the #6465–#6493 "0% savings, no reason" symptom class: A. Inflation guard mislabelled a no-op as inflation. guardPipelineInflation used `compressedTokens >= originalTokens`, so an unchanged body (compressedTokens === originalTokens) tripped the guard, setting fallbackApplied=true and emitting a misleading "did not shrink; reverted to original" warning. Changed to strict `>` — only a strictly larger output is inflation; equality is a no-op. Genuine inflation still reverts. B. Disabled-engine skip was silent and asymmetric with the breaker skip. Both stacked loops (sync + async) skipped a registry-disabled engine with a bare `continue`, recording no validationWarning — while the sibling breaker-open branch does. Both loops now add `${engine}: skipped (engine disabled in registry)`, mirroring the breaker branch. C. No-op engine lost its identity in engineBreakdown. mergeStackStep early-returned on null stats, pushing no breakdown entry, so ensureEngineBreakdown synthesized a generic "stacked" 0% node. It now records a zero-savings entry keyed on the engine that actually ran, preserving identity. Tests: tests/unit/compression-noop-guard.test.ts covers A (equal-token no-op not inflated; strictly-larger still reverts), B (disabled skip surfaces a "disabled" warning), and C (no-op engine keeps its own id in the breakdown). Updated the existing inflation-guard test whose net-zero case encoded the old buggy behaviour, and switched its wire test to object-form pipeline steps so the intended engine runs. Co-authored-by: Chirag Singhal <chirag127@users.noreply.github.com> Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com> Co-authored-by: Diego Rodrigues de Sa e Souza <8016841+diegosouzapw@users.noreply.github.com> * chore(quality): freeze file-size for #6909 (localDb 805) + #6807 (translator test 1195) Owner-approved /merge-prs tail freeze. localDb.ts is re-export-only (Hard Rule #2); translator test grew from #6807's regression suite. Both frozen (shrink-only). * fix(sse): default reasoning summary for effort-only Responses requests (#6807) A Chat-Completions client can only express reasoning via the top-level reasoning_effort hint and has no way to request a reasoning summary. When that hint is promoted to the Responses API's reasoning.effort, the upstream returns an empty summary and downstream chat clients see no thinking stream (encrypted reasoning only). Default reasoning.summary "auto" plus include ["reasoning.encrypted_content"] on the effort-only path so the summary actually streams back to the chat client, mirroring the Codex executor's ensureCodexReasoningSummary. An explicit reasoning object from a Responses-shaped client is preserved untouched, and reasoning_effort "none" is left without a summary. Adds regression tests for the effort-only default, the none case, and keeps the existing explicit-reasoning-object behavior unchanged. Co-authored-by: Diego Rodrigues de Sa e Souza <8016841+diegosouzapw@users.noreply.github.com> * fix(proxy): relay repair + free-pool UX + relay awareness (#6909) * feat(proxy): relay repair + free-pool UX + relay awareness * fix(proxy): preserve existing notes fields on relay repair; fix cpu limit /1000 across all 5 container providers * feat(proxy): extract bulk-import and pool-modal hooks (#6625) * fix: prevent relay type normalization to http on PATCH Bug: updateProxyRegistrySchema inherited .default("http") from the base schema, causing PATCH to silently overwrite relay types (vercel/deno/cloudflare) with "http" when the client didn't send a type field. - Move .default('http') from proxyRegistryFieldsSchema to createProxyRegistrySchema (only applies to new proxies) - Strip undefined keys from validated changes before passing to updateProxy — .partial() leaves absent fields as undefined, which the spread merge in updateProxyRow would propagate to the DB - Add console.warn in extractRelayAuth when decrypt fails on a known-encrypted relayAuthEnc blob Closes #6905 * feat: replace Load More with page-number pagination in FreePoolTab - Adds page-number pagination controls with prev/next buttons - Shows per-page summary with total counts - Resets to page 1 on filter change via wrapper setters * fix(proxy): restore free-proxy sync-error tracking reverted by pagination commit commit2c8e79f13(page-number pagination for FreePoolTab) accidentally reverted the recordFreeProxySyncErrors/clearFreeProxySyncErrors/ getFreeProxySyncErrors functions and the search/sortBy list options that an earlier commit (6f9ce75f3) in this same PR had added to src/lib/db/freeProxies.ts and src/app/api/settings/free-proxies/route.ts. localDb.ts still re-exported the three sync-error functions, so every module that transitively imports it (which is most of the unit test suite, plus the Next.js build used by dast-smoke) failed at load time with "The requested module './db/freeProxies' does not provide an export named 'clearFreeProxySyncErrors'". Restores the reverted implementation (verbatim) and fixes the two new "requires management auth" tests, which asserted 401 for an unauthenticated request without configuring INITIAL_PASSWORD + requireLogin first — on a fresh DB with no password configured, isAuthRequired() treats a loopback request as the pre-setup bootstrap path and allows it through, so the assertion needs the same password/requireLogin setup already used by tests/unit/api/settings-audit.test.ts. Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com> * fix(proxy): trim unrelated scope from relay-repair/free-pool PR Remove two subsystems that are unrelated to relay repair and the free-pool UX and were never wired into the app: - open-sse/services/combo/capabilityRequirements.ts and CapabilityRequirementsEditor.tsx: a combo capability-filtering feature never imported by combo.ts or combos/page.tsx, with no test coverage. - src/lib/skills/containerProvider.ts CPU-limit rescale: an untested change to sandbox resource limits, unrelated to the proxy/relay subsystem this PR targets. Restored to the release baseline. Also renumber the free_proxy_sync_errors migration 121 -> 122 to avoid colliding with #6855, which independently claims 121 on the same release branch. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> --------- Co-authored-by: oyi77 <oyi77@users.noreply.github.com> Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com> Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> * chore(security): scrub hardcoded live-instance creds from boundary tests (#6786) The 3 tests/boundary/*.live.test.ts files merged via #6786 hardcoded a real Bearer API key, an auth_token JWT cookie, and a live instance URL. Replaced with env reads (OMNIROUTE_TEST_BASE/BEARER/COOKIE), preserving the RUN_BOUNDARY_LIVE gate. The leaked key/cookie must still be revoked/rotated on the affected instance and purged from history separately (operator action). * feat(compression): update vendored GCF (Headroom) codec to spec v3.2 — nested flattening (#6838) * feat(compression): update vendored GCF (Headroom) codec to spec v3.2 (nested flattening) Homogeneous arrays whose rows carry nested objects/arrays now tabularize via GCF v3.2 `>`-path flattening instead of a low-yield per-row fallback, so nested MCP tool-result rows (meta:{...}, tags:[...]) compact like flat rows. Round-trip stays lossless (order-insensitive deepEqual). Re-vendored from current gcf-typescript into the Headroom generic-profile codec (open-sse/services/compression/engines/headroom/gcf/); still zero runtime deps, MIT, SPDX-marked, generic-profile only. Also folds in two upstream round-trip-safety fixes: the [N]: inline-array quoting fix and canonical decimal formatting. Regression guard: tests/unit/compression/headroom-smartcrusher.test.ts gains a deep-nested case (two-level object + array-of-objects) asserting the v3.2 flatten paths and order-insensitive round-trip. Vendored-code baseline bumps (complexity 2053->2055, cognitive 885->888, decode_generic no-explicit-any 18->22) each carry an inline _rebaseline_2026_07_10_gcf_v3_2 justification noting the growth is the vendored surface, not new project code. * chore(changelog): add fragment for headroom GCF v3.2 nested flattening (#6838) * docs(readme): note Headroom handles nested arrays (GCF v3.2) in the engine-stack table * docs(readme): note Headroom handles nested arrays (GCF v3.2) in the engine-stack table * fix(compression): harden vendored GCF decoder against prototype pollution The v3.2 flatten/unflatten paths (and the pre-existing inline-object parser) built decoded objects with bracket assignment and `key in obj` membership, so a hostile or unusual payload could pollute Object.prototype via a `__proto__` path segment, and any key shadowing an Object.prototype member (`toString`, `constructor`) was misparsed or wrongly flagged duplicate. - Encoder (`analyzeFlattenable`): builds the shape map with `Object.create(null)` and refuses to flatten objects carrying `__proto__`/`constructor`/`prototype` keys (they round-trip whole instead). - Decoder: `unflattenPaths` drops any path with an unsafe segment; a shared `safeAssign` writes a literal `__proto__` key as an own data property (JSON.parse semantics) instead of reassigning the prototype, used at every object-build site; `checkDup` and orphan-merge use `hasOwnProperty` so built-in-named keys are not spuriously treated as duplicates. Also a losslessness fix: objects with keys named `toString`/`constructor`/ `valueOf` now round-trip. Regression guard: prototype-pollution + built-in-key cases in tests/unit/compression/headroom-smartcrusher.test.ts. Prototype pollution is JS/TS-specific; the Go/Python/Rust/Swift/Kotlin SDKs use native maps and are unaffected. * fix(compression): apply GCF decoder review hardening (hasOwnProperty sweep, unflatten null-guard, strict count) Addresses the second-round review on the vendored codec: - Replace every `key in obj` membership test with `Object.prototype.hasOwnProperty.call(...)` across generic.ts (flatten shape analysis, key-chain resolution, inline-schema/shared-array helpers, row encode) so inherited names (`toString`/`constructor`) never match the prototype chain, and remove a redundant `obj` re-declaration in the ">" field attachment loop. - `unflattenPaths` guards each intermediate segment: a missing OR non-object slot is replaced with a fresh object before traversal, so malformed/hostile input can no longer dereference a primitive and crash. - Use the strict `parseCount` helper (not `parseInt`) for the shared-schema count so malformed counts fail the mismatch check instead of coercing. The decoder grew past the 800-line file-size cap; frozen at 880 in file-size-baseline.json with a justification (vendored file kept faithful to upstream gcf-typescript for clean re-vendoring). Verified: prototype-pollution + hostile-input + built-in-key round-trip probes, 54/54 compression tests, typecheck, lint, cyclomatic/cognitive baselines unchanged, compression-budget. * fix(compression): do not flatten a nested object that is null in any row (losslessness) analyzeFlattenable skipped null values during shape analysis, so a field that was an object in some rows and null in others was still flattened. On decode, the null row's leaves resolved as absent ("~") and unflattened to a missing key instead of null, silently dropping the value (e.g. {meta:{owner:null}} decoded to {}). analyzeFlattenable now bails (returns null) when the field is null in any row, routing it through the lossless whole-object attachment path. Applies at every nesting depth via the existing recursion. Regression guard: null nested-object cases in tests/unit/compression/headroom-smartcrusher.test.ts. * fix(compression): narrow the null-nested flatten bail to intermediate nulls only The previous fix bailed flattening whenever a nested field was null in any row. That is correct but over-broad: a top-level null round-trips losslessly through flattening (it emits "-" and reconstructs via the all-null rule). Only a null at an intermediate nesting level loses data (its leaves encode as absent "~" and unflatten to a missing key). Bail only when parentPath is non-empty, so top-level nulls keep flattening (compression preserved) while intermediate nulls fall back to the lossless attachment path. Matches GCF conformance fixtures 004/013. --------- Co-authored-by: Diego Rodrigues de Sa e Souza <8016841+diegosouzapw@users.noreply.github.com> * feat(providers): add GPT-5.6 model family (#6862) * feat(providers): add GPT-5.6 model family * fix(chatgpt-web): resume temporary chat handoffs * fix(codex): auto-merge discovery, filter denylist, revalidate on lifecycle Restore live/GitHub auto-merge for Codex catalogs, drop models via explicit denylist (GPT-5.4 family), and run scrub+live re-sync once on first-start, app upgrade, or setup completion. Success log: kill deprecated models complete. * fix(codex): preserve live catalog reconciliation Expose remote-only Codex models without dropping user custom entries, and complete lifecycle revalidation only after a successful internal sync. Keep credentialed self-fetches pinned to the active dashboard listener. --------- Co-authored-by: backryun <backryun@daonlab.local> * fix(release): read changelog.d fragments in list-uncovered-commits (#6857) (#6878) * fix(release): count changelog.d fragment refs in list-uncovered-commits (#6857) Since fragments-first (#6783), a merged PR's changelog entry usually lives in changelog.d/{features,fixes,maintenance}/<PR>-<slug>.md and is only folded into CHANGELOG.md at release time. list-uncovered-commits.mjs scanned only CHANGELOG.md, so every fragment-covered commit was reported as an uncovered gap (some fragments — e.g. 6708, 6709 — carry no #N in the body, only in the filename). Add fragment-aware ref collection: fragmentFilenameRef() reads the leading <N>- of a fragment filename, fragmentRefs() unions filename PR numbers with every #N in the body, and collectChangelogRefs() unions the CHANGELOG scan window with the fragment refs. main() now reads changelog.d via readChangelogFragments() and feeds it into the union. On release/v3.8.47 tip this moves 44 commits from uncovered to covered (215/341 vs the prior 171/341) without changing the covered/uncovered classification logic. * chore(release): add changelog.d fragment for #6878 Housekeeping item requested in review: the PR fixing changelog-fragment coverage tracking (#6857) did not itself have a changelog.d fragment. --------- Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com> Co-authored-by: Diego Rodrigues de Sa e Souza <8016841+diegosouzapw@users.noreply.github.com> * [trim] feat(combo): add context requirements config for target filtering (#6907) * feat(combo): add context requirements config for target filtering Add contextRequirements config field to combo runtime config: - minContextWindow: filter models below threshold (0-10M tokens) - preferLargeContext: sort targets by context size descending - contextFilterMode: 'strict' excludes unknown limits, 'lenient' includes them Implementation: - Added Zod schema validation in combo.ts - Created contextRequirements.ts module with applyContextRequirements() - Integrated filtering after filterTargetsByRequestCompatibility() - Full test coverage with unit + integration tests Tests: 17/17 pass (combo-context-requirements.test.ts + integration) * feat(combo): add ContextRequirementsEditor UI component Add standalone React component for editing context requirements config: - Slider for minContextWindow (0 to 1M tokens) with presets - Toggle for preferLargeContext sorting - Radio group for contextFilterMode (strict/lenient) - Tooltips explaining each option - Active filters summary display Component features: - Shadcn UI components (Card, Slider, Switch, RadioGroup) - Preset buttons for common context sizes (8K, 32K, 128K, 1M) - Conditional display of filter mode when minContextWindow > 0 - Clear visual feedback of active filters Integration: Import and use in combo config form where other config fields like fusionTuning and judgeModel are edited. Pass combo.config.contextRequirements as value prop and update on onChange. Example usage: <ContextRequirementsEditor value={config.contextRequirements} onChange={(val) => updateConfig({ contextRequirements: val })} /> UI matches existing combo config editor patterns. * feat(combo): wire ContextRequirementsEditor into combo config form Adds context requirements section to combo edit page (strategy section), matching existing ResponseValidation pattern. Placed after response validation block, before agent features. * docs(combo): add context requirements feature documentation Covers: config schema, behavior, use cases, UI integration, troubleshooting, and test instructions. * fix(combo): pass provider+modelStr to getModelContextLimit for accurate context resolution Per gemini-code-assist review feedback: model names are not globally unique across providers. Passing both provider and modelStr ensures correct context limit resolution in applyContextRequirements(). * fix(combo): repair broken doc links and restore test:unit:fast flag - Point docs/combo-context-requirements.md 'Related' links at real docs (routing/AUTO-COMBO.md, architecture/RESILIENCE_GUIDE.md) — the three placeholder links (strategies.md/model-capabilities.md/fusion-tuning.md) did not exist and failed check:doc-links (Docs Gates fast-path). - Revert an out-of-scope package.json change to test:unit:fast that dropped --test-isolation=none; restore to match release/v3.8.47. Co-authored-by: oyi77 <oyi77@users.noreply.github.com> * test(combo): validate context requirements against the real Zod schema Point tests/unit/combo-context-requirements.test.ts at the real comboRuntimeConfigSchema export (src/shared/validation/schemas/combo.ts) instead of hand-duplicating the Zod schema inline, so the test catches schema drift. Also declare contextRequirements on DEFAULT_COMBO_CONFIG so resolveComboSetupConfig's inferred return type includes the key — combo.ts reads config.contextRequirements but the property was missing from the object typecheck:core infers types from, causing a build error. Co-authored-by: oyi77 <oyi77@users.noreply.github.com> * fix(combos): remove dead ContextRequirementsEditor scaffolding (broken ui/card+label imports) The editor imported @/components/ui/card and @/components/ui/label which do not exist in the repo, breaking the Turbopack build. Removed the editor + its page.tsx usage + doc mention; the real fix (comboConfig contextRequirements default + test) is preserved. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> --------- Co-authored-by: oyi77 <oyi77@users.noreply.github.com> Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com> Co-authored-by: Diego Rodrigues de Sa e Souza <8016841+diegosouzapw@users.noreply.github.com> * feat: add icons for 46 missing provider images (#6926) * feat: add icons for 46 missing provider images - Add SVG icons for 46 providers missing brand images - Add 3 LOBE aliases (bai, clinepass, copilot-m365-web) - Register all new SVGs in KNOWN_SVGS lookup New SVG icons cover: api-airforce, auggie, bluesminds, byteplus, bytez, charm-hyper, chipotle, chutes, crof, dgrid, digitalocean, dit, duckduckgo-web, factory, freeaiapikey, freemodel-dev, galadriel, gitlawb, gitlawb-gmi, hackclub, haiper, hcnsec, ideogram, kenari, leonardo, llm7, modelscope, nube, openadapter, orcarouter, pioneer, publicai, qiniu, requesty, sumopod, t3-web, theoldllm, tokenrouter, uncloseai, veoaifree-web, wafer, x5lab, yuanbao-web, zed-hosted, zenmux, zenmux-free * fix(icons): restore accidentally-deleted cohere alias in LOBE_PROVIDER_ALIASES The 46-icon addition dropped the existing `cohere: "Cohere"` entry; restore it alphabetically between codex-cloud and comfyui. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> --------- Co-authored-by: oyi77 <oyi77@users.noreply.github.com> Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> * fix(sse): wire shared quota-fetch throttle into all provider fetchers (#6911) (#6963) OMNIROUTE_QUOTA_FETCH_MIN_INTERVAL_MS / quotaFetchThrottle.ts documents itself as used by 'the provider quota fetchers' (plural), but only codexQuotaFetcher.ts ever called throttleQuotaFetch(). N accounts on one IP for DeepSeek, Bailian, OpenCode, or Crof still burst simultaneously. Wire throttleQuotaFetch() into fetchDeepseekQuota, fetchBailianQuota (both the primary and China-region retry fetch sites), fetchOpencodeQuota, and fetchCrofUsage, placed after the existing cache short-circuit so cache hits stay unaffected (mirrors the codexQuotaFetcher.ts pattern). PROVIDER_LIMITS_SYNC_SPACING_MS / providerLimits.ts's OAuth vs non-OAuth split is left unchanged — that split is intentional by design and already regression-guarded by tests/unit/provider-limits-oauth-sequential-sync.test.ts. The generic usage.ts::getUsageForProvider dispatch path (github, glm, minimax, nanogpt, xai, etc.) is intentionally out of scope for this fix to avoid scope creep; ENVIRONMENT.md now documents the actual post-fix coverage instead of the prior overclaim. * fix(sse): rename max_completion_tokens to max_tokens for volcengine/DeepSeek (#6912) (#6964) * fix(sse): defer response.completed until trailing usage-only chunk (#6906) (#6965) Real OpenAI-compatible upstreams with stream_options.include_usage=true send finish_reason in one chunk (usage: null) and the actual token counts in a separate, trailing usage-only chunk (choices: [], usage: {...}). Both the live translator (openai-responses.ts) and the legacy transformer (responsesTransformer.ts) fired response.completed as soon as they saw finish_reason, so the trailing usage chunk's token counts were captured into state but never emitted -- Codex CLI and other /v1/responses consumers saw response.completed with no usage field (permanent 0% context-used). Both translators now defer response.completed via an awaitingTrailingUsage state flag when finish_reason arrives without usage already captured, and complete on the next usage-only chunk (or at stream end via the existing flush fallback) instead. Extracted the duplicated events/emit boilerplate into a new openai-responses/eventEmitter.ts leaf to keep the frozen openai-responses.ts file under its file-size baseline. Fixes 3 existing tests that encoded the old chunk ordering and adds a permanent regression test (tests/unit/responses-usage-trailing-6906.ts) covering both translators. * fix(sse): omit removed attachments field from Muse Spark Web request (#6935) (#6960) * fix(dashboard): label audio/embeddings/image compatible providers by kind on ProviderCard (#6936) (#6961) ProviderCard's compatibility badge used a binary apiType ternary (responses vs everything-else -> "Chat"), so audio-transcriptions, audio-speech, images-generations and embeddings compatible providers (e.g. a locally-hosted speaches TTS/STT server) were mislabeled as "Chat". Reuse the existing KIND_LABEL map (stt/tts/image/embedding) instead of adding new i18n keys. * fix(sse): classify LAN embeddings providers as no-auth (#6925) (#6962) Private/LAN embeddings provider_nodes (10.0.0.0/8, 192.168.0.0/16, 100.64.0.0/10 CGNAT) were excluded by a hand-rolled hostname filter that only matched localhost/127.0.0.1/172.16-31, forcing them through the apikey/bearer credential fallback and returning 401 for keyless local providers like a LAN Ollama instance. Reuse the shared isPrivateHost()/isCloudMetadataHost() classification from outboundUrlGuard.ts in both the dynamic-provider filter and the provider_node fallback branch, so any private host resolves to authType 'none' while cloud-metadata endpoints stay blocked. * fix(api): use local-first SSRF guard for LAN model-list discovery (#6939) (#6966) * fix(sse): set includeServerSideToolInvocations on Antigravity tool cloak decoys (#6914) (#6959) cloakAntigravityToolPayload() injects decoy functionDeclarations (search_web, browser_subagent, read_url_content, generate_image) that mimic Antigravity's built-in server-side agent tools whenever any real tool is declared, but never set the companion toolConfig.includeServerSideToolInvocations flag a genuine Antigravity client sends alongside them. Google's Cloud Code backend (Gemini 3+) requires this opt-in whenever server-side built-in tool categories are combined with custom function declarations, causing every Antigravity tool-calling request to fail upstream. Set toolConfig.includeServerSideToolInvocations = true whenever decoy tools are injected. * fix(sse): escape backslash in ChatGPT-web citation link text (#6569) (#6944) * fix(sse): escape backslash in ChatGPT-web citation link text (#6569) markdownLinkText() escaped [ and ] but not the backslash itself, so a citation label ending in (or containing) a backslash produced a broken Markdown link — e.g. [Path C:\](url), where the trailing \ escapes the closing bracket and consumes the link. Escape the backslash first, then the brackets. Clears the CodeQL js/incomplete-sanitization alerts at open-sse/executors/chatgpt-web/citations.ts:52 (2 of the 9 new alerts on the v3.8.47 release PR). Regression guard: tests/unit/chatgpt-web-citations-escape.test.ts (trailing backslash, backslash-before-bracket, bracket-only, plain). * chore(changelog): add changelog.d fragment for #6944 Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> --------- Co-authored-by: brick30llc-ctrl <admin@brick30.com> Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com> Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> * fix(sse): flatten structured (array) content in Qwen Web executor (#6927) * fix(sse): flatten structured (array) content in Qwen Web executor foldMessages did String(m.content), turning OpenAI-style content-part arrays into the literal "[object Object]" prompt. Add contentToText() to extract the text parts. Reported on the support mesh. TDD: red->green regression test tests/unit/qwen-web-content-array-serialization.test.ts * docs(changelog): add fragment for #6927 * fix(stryker): register qwen-web content-array test in tap.testFiles Fast Quality Gates flagged the new coverage for open-sse/executors/qwen-web.ts as missing from stryker.conf.json's tap.testFiles list. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> * fix(db): add authType filter support to getProviderConnections (#6946) getProviderConnections ignored the authType query param, causing callers like tokenHealthCheck.ts and /api/token-health to fetch and decrypt every connection instead of only the OAuth ones they asked for. Add the missing auth_type WHERE clause and a regression test. Rebased to drop the unrelated 46-icon commit (duplicate of #6926) and the accidentally-committed tests/unit/authz/__stub_apiKeys.mjs runtime artifact; replaced with a real unit test asserting the authType filter excludes non-matching connections. Co-authored-by: oyi77 <oyi77@users.noreply.github.com> Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> * fix(tokenHealthCheck): case-sensitive provider comparisons break rotating/gh checks (#6947) ROTATING_REFRESH_PROVIDERS.has(conn.provider) fails for 'OpenAI' or 'Github' - the set is all lowercase. Same issue for the GitHub Copilot sub-token refresh guard. Both now normalize to lowercase before comparison, matching the established pattern from getHealthCheckSkipProviders() (line 201) and isGitHubAccessTokenOnlyConnection() (line 94). Replaces the whole-file regex assertion in oauth-providers-error-handling (which passed even on unfixed code) with two statement-scoped regression tests that fail against origin/release/v3.8.47's unfixed source and pass only once both call sites are normalized. Co-authored-by: oyi77 <oyi77@users.noreply.github.com> Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> * perf: thread pre-fetched token to checkRateLimit avoiding re-query (#6930) * perf: thread pre-fetched token to checkRateLimit avoiding re-query getRelayTokenByHash already fetches the full RelayToken row. A few lines later checkRateLimit(token.id) does a second SELECT * FROM relay_tokens on a different predicate (id instead of token_hash). Change: - checkRateLimit accepts an optional existingToken parameter; when provided, skips the re-query entirely. - Both relay routes (chat completions + bifrost) pass the already- fetched token. - The function now uses RelayToken (camelCase) instead of RelayTokenRow (snake_case) when the token is passed in. PR-URL: fix-relay-thread-token * test(db): add regression coverage for checkRateLimit existingToken fast-path Adds node:test coverage for src/lib/db/relayProxies.ts::checkRateLimit proving the existingToken fast-path (pre-fetched RelayToken threaded in, no re-query) agrees with the legacy re-query path (no token passed), and that the per-minute cap is still enforced through the fast-path. Also adds a changelog.d fragment for the perf fix in9d4cd90e7. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> --------- Co-authored-by: oyi77 <oyi77@users.noreply.github.com> Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> * fix: eliminate redundant getApiKeyMetadata call in embeddings route (#6929) enforceApiKeyPolicy() already fetches the API key metadata and returns it as policy.apiKeyInfo. The old code at line 72 called getApiKeyMetadata a third time per request (third hash+DB query after isValidApiKey and enforceApiKeyPolicy's internal fetch). Change: use policy.apiKeyInfo directly instead of re-querying. Also removes the now-unused getApiKeyMetadata import. Adds a regression test exercising the dashboard-playground-key path (no bearer token, only enforceApiKeyPolicy's resolvePlaygroundTestKey fallback resolves the key) — the old apiKeyRaw-gated call always produced a null apiKeyMeta on that path, while policy.apiKeyInfo correctly carries it through to the downstream call log. Split out of the original PR: dropped the unrelated 46-provider-icon commit that had been bundled onto the same branch. Co-authored-by: oyi77 <oyi77@users.noreply.github.com> Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> * fix(sse): normalize assistant input_text to output_text in Codex Responses input (#6932) codex-cli replays assistant history with content parts typed as `input_text`, but the Responses API only accepts `output_text` (or `refusal`) on assistant turns — `input_text` is user-only. `normalizeCodexMessageContentPart` previously only rewrote parts literally typed `text`, leaving explicit `input_text` on assistant turns untouched, which the Codex/OpenAI backend rejects with a 400. Rewrite explicit `input_text` (and `text`) to `output_text` on assistant-role parts, dropping the assistant-only `annotations`, `logprobs`, and `obfuscation` fields. Mode-agnostic, applies to all Codex models. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> * fix(6813): fix thinking budget zero drop and default thinkingConfig injection (#6943) * fix(electron): bump electron 42→43 + build better-sqlite3 from source (ABI 148) (#6605) fix(electron): bump electron 42→43 + rebuild better-sqlite3 from source against the Electron ABI (148). Electron 43 raises NODE_MODULE_VERSION to 148; better-sqlite3@12.11.1 has no electron-v148 prebuild, so the packaged app died with 'Nenhum driver SQLite disponível'. prepare-electron-standalone now compiles better-sqlite3 from source against the electron headers into build/Release (where 'bindings' resolves it). Validated by Electron Package Smoke (green) + local (node_register_module_v148). Supersedes #6378. (--admin: the only reds are SonarQube/SonarCloud failing on a coverage-report artifact digest-mismatch — a GitHub Actions infra flake, not this diff; Sonar is green on main and the diff touches only the electron build.) * deps: bump the development group across 1 directory with 6 updates (#6588) deps: bump the development group (6 updates). Rebased onto current main; all checks green after the electron-smoke fix (#6605). * fix(proxy): force CONNECT tunnel for HTTP proxied requests (undici 8.7) + production deps bump (#6620) fix(proxy): force CONNECT tunnel for HTTP proxied requests (undici 8.7) + production deps bump. undici 8.6+ changed ProxyAgent to forward plain-HTTP via request-proxy instead of CONNECT, breaking OAuth refresh through a connection proxy (501). proxyDispatcher now passes proxyTunnel:true. Validated: Unit Tests 3/8 (the OAuth-proxy test) green, new regression test green (fails without the fix on undici 8.7), SonarQube green. Supersedes #6380. (--admin: the only red is Electron Package Smoke failing on a next-build artifact 'digest-mismatch' — a GitHub Actions infra flake corrupting the asar ('file data stream has unexpected number of bytes'); the better-sqlite3 rebuild itself succeeded (gyp ok) and the electron path is unchanged from #6605 which passed the smoke. Not this diff.) * fix(6813): fix thinking budget zero drop and default thinkingConfig injection - Fix truthy check for budget_tokens to allow 0 - Stop injecting default thinkingConfig when no knobs present - Add tests covering all scenarios Related: #6813 --------- Co-authored-by: Diego Rodrigues de Sa e Souza <8016841+diegosouzapw@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * feat(sse): add connection backpressure for chat handler (#6590) Add checkConnectionCapacity guard with 429 + Retry-After in handleChat(). Introduce OMNI_MAX_CONCURRENT_CONNECTIONS env-bound cap, disabled (0) by default so existing deployments are unaffected until an operator opts in. Reconstructed from PR #6590, isolating only the backpressure change — the original branch also carried unrelated headroom/docker/perf work from the author's separate #6572 branch. Co-authored-by: oyi77 <oyi77@users.noreply.github.com> Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> * chore(base): fix 2 mechanical release-tip base-reds (relayProbeStats re-export + OMNI_MAX_CONCURRENT_CONNECTIONS docs) * fix(sse): apply commentary-phase drop filter in TRANSLATE mode (#6952) (#6990) The #6199/#6561 commentary-phase filter (shouldDropResponsesCommentaryEvent) was wired only into createSSEStream's PASSTHROUGH branch. The TRANSLATE-mode loop (openai-responses upstream -> another client format, e.g. codex routes streaming into Claude Code) called translateResponse() on every raw chunk without checking phase, so internal commentary-phase scratchpad text leaked into the client-visible content channel as duplicate prose and narrated tool-call arguments. Extends the same stateful filter into TRANSLATE mode via a small factory (createTranslateCommentaryFilter) that owns its own item/index Sets, keeping the wiring in stream.ts (a frozen file) to a single guarded line. Fail->pass evidence: - tests/unit/repro-6952-commentary.test.ts against origin/HEAD (pre-fix): FAILED - "commentary-phase prose must not reach the translated client stream" - Same test against the fix: PASSED (2/2) * fix(combos): show embedding/rerank models and disambiguate duplicate names in builder options (#6975, #6957) (#6991) Removes the leftover chat-only isChatCapable gate from addModelOption() (#6975) and adds a name-disambiguation pass at the end of buildModelOptions() so distinct model ids sharing the same upstream display name fall back to their id (#6957). Both proven with TDD repro tests (RED->GREEN). * fix(sse): schema-aware optional tool-arg normalization for Codex routes (#6951) (#6992) stripEmptyOptionalToolArgs was allowlist-only (Read/Subagent) and only stripped empty-string/empty-array values, so Responses API strict mode (every property forced into `required`) could forward a forced non-empty value (e.g. Agent.isolation) or a schema-declared default value verbatim to the client. Add schema-aware drop-if-default and generalized drop-if-empty (any tool, gated on schema.required), and thread each tool's JSON Schema from the request's tools[] into the two streaming call sites (response.output_item.done handling). Closes #6951 * chore(base): fix release-tip base-reds — eslint severity revert (#6786 regression), migration gap 121, file-size freeze bumps * fix(test): align emergency fallback budget-exhaustion test with #6912 max_tokens normalization (#6967) The test asserted both max_tokens and max_completion_tokens=4096 on the nvidia/openai/gpt-oss-120b emergency fallback request. Commita34fb6b3e(#6912, merged into this release tip) added a symmetric normalization in chatCore.ts that renames/deletes the redundant max_completion_tokens field whenever the target provider supportsMaxTokens() (nvidia does), so only max_tokens reaches the upstream request. The old dual-field assertion is an outdated contract, not a regression. Align the test to the new intentional behavior while keeping the max_tokens=4096 cap assertion as the fallback-cap guard. * fix(test): deterministic openadapter live-catalog import repro (#6967) * chore(base): backfill #6909 i18n keys (en+pt-BR) and align gemini defaults test with #6943 (unit-full pre-flight) * chore(release): v3.8.47 pre-flight fixes — orphan test relocation (#6943), eslint suppression match, file-size/zizmor rebaselines * fix(quality): restore zizmorFindings ratchet object shape (value 169 + justification) * chore(quality): v3.8.47 cycle-close pct rebaselines (openapiCoverage 39.3->38, i18nUiCoverage 76.8->75.5) with justification * chore(release): open v3.8.48 development cycle * chore(release): bump v3.8.49 (development cycle version) * test(build): derive pack-artifact closures for all npm-shipped entrypoints (#7065 class) (#7081) The server-ws closure test hardcoded ONE wrapper and ONE import form. This generalizes it: every dist-root wrapper in EXTRA_MODULE_ENTRIES that ships in the npm channel has its local imports (static, dynamic import(), require()) required in both APP_STAGING_ALLOWED_EXACT_PATHS and PACK_ARTIFACT_REQUIRED_PATHS, and the bin/omniroute.mjs CLI boot path is closure-checked too — its direct imports bin/cli/data-dir.mjs and bin/cli/utils/storageKeyProvision.mjs were only covered by an allowlist PREFIX (absence from the tarball had no gate) and are now required paths. TDD: the bin closure test failed on those two before the policy fix. * docs(quality): codify retry policy per runner + release-level drift rule (WS5.4/WS5.5) (#7107) * chore(release): gate the sync-back push on release-green --quick (WS0.3) (#7083) The parallel-cycle sync-back (sync-next-cycle.mjs) is the one write path to the release branch with no CI gate — a red merged tree pushed there turns every PR in the cycle's queue red (G1). The script now runs validate-release-green --quick on the merged tree between the commit and the push; on HARD failure the commit stays local in the sync worktree for inspection. --skip-green-gate is the documented emergency hatch for reds verified pre-existing on the tip. TDD: greenGateArgs() flag contract + source guard asserting the gate call sits between main() and the push. * feat(ci): boot-smoke the packed npm tarball (check:pack-boot, #7065 class killer) (#7086) Three releases shipped a tarball that crashed on every boot (tls-options/3.8.41, head-response-guard #7040/#7065) because no gate ever EXECUTED the artifact. check:pack-boot packs the tree, installs the tarball into a clean prefix (postinstall runs for real), boots the installed CLI on a reserved port with an isolated DATA_DIR and polls /api/monitoring/health until it returns 200 with the packed version — failing loudly with the server's last output otherwise. Wired into the CI package-artifact job (reuses the dist/ the job already assembles) and into check:release-green --with-build (parallel slow wave). Live evidence: packed v3.8.49, installed and booted in 16.6s, health 200. * feat(ci): continuous release-green — on-push quick gate + 3x/day full sweep (WS5.1) (#7089) The v3.8.49 cycle started with what looked like a shared base-red because the tip had NO gate between pushes and the nightly (24h MTTD): the captain's sync-back is a direct push, and merged PR combinations are never validated together. nightly-release-green.yml becomes 'Release-Green (continuous)': - push to release/v* (code paths) → validate-release-green --quick (~5-8min) against exactly the pushed ref, with per-branch concurrency so merge storms collapse to the newest commit. The failure issue now names the offending push range (before..after, one merge per push in the normal queue — direct attribution without bisect). SHAs enter the shell via env (injection-safe); commit subjects go to the issue body through a file, never interpolated. - schedule → full --with-build --full-ci, now 3x/day (05:23/12:23/18:23 UTC). Workflow-only change (no production code); YAML parse validated. * feat(ci): duration-balanced E2E shards via LPT bin-packing (WS4.1) (#7090) Playwright --shard distributes by count (per file with fullyParallel:false), blind to duration — measured skew on the 9-shard matrix: 24m47s worst vs 1m47s best (14x), putting E2E on the CI critical path (~25min of the 33min gate). - scripts/quality/balance-e2e-shards.mjs: LPT greedy (heaviest first into the lightest shard) over config/quality/e2e-timings.json; deterministic (weight desc, filename tiebreak); new specs get the median weight; the CLI self-verifies the shard union equals the discovered spec list and exits non-zero on ANY inconsistency (missing timings, lost spec) so the CI step falls back to plain --shard — never fewer specs than before. - config/quality/e2e-timings.json: relative weights seeded from spec LOC (proxy); replace with real per-file durations from a full run when convenient (documented in _meta). LOC-seeded packing already lands at 742-761 per shard (1.03x skew) vs the alphabetical round-robin that produced 14x. - ci.yml test-e2e: balanced list per shard with logged assignment + fallback. TDD: 5 unit tests (LPT invariants, determinism, completeness, median fallback, seed-vs-specs drift guard). * feat(ci): TypeScript 7 native shadow for typecheck:core (WS4.2, advisory) (#7091) TS7 went GA 2026-07-08 (native Go compiler). Hybrid adoption is the officially documented pattern: the Compiler API only arrives in 7.1, so typescript-eslint, type-coverage and the Stryker checker must stay on typescript 6.x — only the pure type-check gate can move. This adds an ADVISORY shadow step to the fast-gates job running the SAME tsconfig.typecheck-core.json under TS7 via an isolated npx (deliberately NOT a dependency: an alias install could collide node_modules/.bin/tsc with 6.x and silently swap the blocking gate's binary). Live parity evidence (this tree): TS7 exit 0 / 0 errors vs TS6 exit 0 / 0 errors — identical verdicts. Local wall: 25s -> 19s (warm dev box; upstream reports 8-12x on cold/large runs — the shadow exists to measure OUR CI number). Promotion to blocking after ~1 week of parity, per the v3.8.49 plan. * feat(ci): hotfix fast-lane + tests-only E2E skip (WS3.1) (#7088) A hotfix with 3 fixes paid the full 33min gate 3x in v3.8.48 (owner: '6h to re-validate 3 fixes makes no sense'). Modeled on the Chromium/VS Code/Node emergency lanes — skip WAITING, never validation: - PRs labeled 'hotfix' (owner-applied; entry policy: production-broken only, previous green heavy-run linked as evidence, cherry-pick-only scope — documented in docs/ops/RELEASE_CHECKLIST.md) skip test-e2e (9 shards, the ~25min critical path), test-coverage, quality-gate and quality-extended. Build, unit shards, integration, vitest, lint bag, docs-sync, pack-artifact and the tarball boot-smoke still run: green in ~15min. - classify-pr-changes gains a testsOnly output: a diff entirely under tests/ with nothing in tests/e2e/ cannot change the served app, so the E2E matrix skips automatically (changing an e2e spec still runs e2e). TDD: 4 new classifier tests red->green; full-shape asserts aligned additively. * feat(ci): Windows leg for Electron prepare smoke (WS1.5) (#7113) The Electron rebuild/spawn path executed for the FIRST time on the release tag: the v3.8.48 Windows failure (npx.cmd spawned without shell) could only surface at release. The Electron Package Smoke job becomes a 2-leg matrix: ubuntu keeps the full pack + headless smoke; windows-latest runs prepare:bundle — the exact ABI rebuild + spawn-plan path that broke — on every release PR instead of tag day. tar extraction of the build artifact works on windows-latest (bsdtar). Workflow-only change; YAML parse validated. * chore(ci): gate hygiene — secrets baseline 0, semgrep drop, hadolint (WS6/D3 + WS1.7) (#7099) * chore(ci): gate hygiene — secrets baseline 0, semgrep metric drop, hadolint gate (WS6/D3 + WS1.7) - .gitleaks.toml: allowlist (with mandatory justification) for the 3 frozen generic-api-key false positives — latencyP50Ms/latencyP95Ms are metric FIELD NAMES and interleaved-thinking-2025-05-14 is Anthropic's PUBLIC beta header. quality-baseline secretFindings 3 -> 0: the ratchet is now zero-tolerance (verified: check:secrets --ratchet reports 0 findings, no regression). - quality-baseline: semgrepFindings removed — orphaned metric never wired to a blocking gate (semgrep.yml only echoes the count); CodeQL covers OWASP. - ci.yml lint job: hadolint on the Dockerfile (image pinned by digest, --failure-threshold error). Verified green against the current Dockerfile (5 pre-existing warnings visible, 0 errors). Also evaluated publint for the fast path (WS1.6) and REJECTED it with data: 1554 findings, ~all noise from the vendored dist/node_modules of the standalone package — wrong tool for this package shape; check:pack-boot is the real gate. * chore(ci): surgical baseline edit — preserve unicode formatting (was json.dump ensure_ascii noise) * feat(ci): Mergify merge queue + manual-train fallback runbook (WS3.4/WS3.2) (#7112) * feat(ci): Mergify merge queue for release branches + manual-train fallback runbook (WS3.4/WS3.2) D5 final decision (owner, 2026-07-13, post vendor research): Mergify OSS plan — free/unlimited for the public repo, with the two features the volume demands (85-100 active authors/month, 300+ PRs/week peaks, ONE merger): batching + automatic bisection of red batches (log2(N) vs N revalidations). Proven at larger scale by NixOS/nixpkgs. - .mergify.yml: queue for base ~= release/vX.Y.Z (the wildcard GitHub's native queue cannot do); entry ONLY via the owner-applied 'queue' label AFTER the pre-merge star gate (the label IS the approval — Mergify executes, never decides); merge_conditions '#check-failure=0' + '#check-pending=0' respect the path-filtered fast-gates; squash keeps one-commit-per-PR history; label auto-removed after merge. Freeze/cross-session guardrails documented in-file. - docs/ops/MERGE_TRAIN.md (WS3.2): the manual merge-train codified as the FALLBACK runbook (batch -> validate once -> bisect halves on red) + the tiering rationale (per-PR fast-gates, per-tip continuous release-green, per-release full matrix — nothing validated less, just per batch not per PR). - 'queue' label created in the repo. Config validated (YAML parse); Mergify's own config check runs on this PR. * fix(ci): mergify queue must not fail open — require the always-on Merge-integrity check as affirmative success * feat(ci): Codecov patch coverage (informational) + fix missing lcov reporter (WS5.6) (#7114) Two changes to the test-coverage job: - The CI c8 report step never emitted lcov (only text/json summaries), so the coverage-report artifact silently skipped coverage/lcov.info (if-no-files-found: warn) — the very file the Sonar job consumes. Adding --reporter=lcov makes the artifact real for both consumers. - codecov/codecov-action v5 (SHA-pinned) uploads the lcov after the summary, with codecov.yml keeping BOTH statuses informational during calibration (D7 decision: informative first, blocking only after ~2 weeks without false blocks). Philosophy: strict patch, lenient project — the global floor/ratchet already lives in c8 60% + quality-baseline.json; Codecov adds the diff view. Workflow+config-only change; YAML parse validated; CODECOV_TOKEN secret already created by the owner. * chore(ops): runner-box janitor + operations runbook (WS3.3) (#7115) * chore(ops): runner-box janitor script + operations runbook (WS3.3) Codifies what was manual discipline on the .113 self-hosted pool (two live incidents on the v3.8.47 release day): 30min cron sweeping stale runner temp/work dirs (>24h), disk-pressure alert at >=85% (SQLITE_FULL killed shards mid-run), and the proven 4-runner ceiling on the 16 GB box (8-wide OOM'd jobs; stopping a busy runner cancels its job — documented). Script smoke-tested live (disk 82%, 1 active runner, exit 0); bash -n clean. * docs(ops): reword error-code/bash-env mentions the fabricated-docs env detector misreads * fix(ops): harden janitor sweep — no symlink follow, -xdev, narrowed patterns (root-cron on world-writable /tmp) * fix(tests+providers): env-dependent tests exposed by GH-hosted runners (#6634 selfref shallow checkout + yuanbao live-network 401) (#7174) * fix(tests): #6634 selfref test tolerates shallow checkouts (fetch origin/main on demand, skip offline) * fix(providers): yuanbao cookie validation rejects foreign pairs locally (was a hidden live-network test dependency) * feat(release): post-publish verifier — clean-container install + boot (WS1.4) (#7109) * feat(release): post-publish verifier — clean-container install + boot (WS1.4) verify-published.mjs installs the PUBLISHED version from the public registry inside node:24-slim and boots it until /api/monitoring/health returns 200 with the expected version — validating the exact bytes users install, on a machine with no repo/devbox state. Version + knobs travel as docker env vars, never interpolated into the container script (Hard Rule #13); strict semver arg validation. Wired into the release Phase 4 monitoring playbook. Live evidence: omniroute@3.8.48 from the real registry installed and booted in a clean container — HTTP 200, version 3.8.48, exit 0. Tests: 4 pure-function guards (semver strictness incl. shell-hostile rejects, env-passing invariant, clean-image pin, health-poll source guard). * chore(quality): allowlist verify-published container env vars in env-doc-sync * feat(release): npm staged publishing + pre-publish boot-smoke (WS1.3) (#7092) * feat(release): npm staged publishing + pre-publish boot-smoke (WS1.3) v3.8.47 shipped an npm tarball that crashed on every boot and had to be deprecated — the publish path had no runtime gate and the owner's 2FA happened BEFORE any proof. Two changes to npm-publish.yml: - check:pack-boot runs right before any publish (dist/ is already assembled by build:cli in the same job) — a non-booting tarball now fails the workflow before anything reaches the registry. - npm publish becomes 'npm stage publish' (staged publishing, GA 2026-05-22, npm >= 11.15 ensured in-job): the exact bytes are parked on the registry but NOT installable until the owner runs 'npm stage approve <id>' with 2FA. The workflow summary prints the approve/verify/reject flow; RELEASE_CHECKLIST documents the owner flow, the one-time Trusted Publisher stage-only config, and the deprecate-first rollback playbook. publish_mode=direct (workflow_dispatch) is the emergency fallback to the legacy immediate publish. First real-registry exercise happens on the next release with the fallback one dispatch away (D2 decision, v3.8.49 plan). GitHub Packages secondary publish unchanged. YAML parse validated. * docs(release): reference upcoming verifier without file paths (docs-all strict) * fix(release): pin npm 11.15.0 in the staged-publish version guard (no @latest in the publish job) * feat(homolog): real-environment E2E homologation suite (npm run homolog) (#7133) * feat(homolog): scaffolding da suíte de homologação E2E (deps + npm run homolog) * feat(homolog): L0 avaliador de paridade de deploy (TDD) * feat(homolog): L1a ciclo de vida de API key efêmera (login admin -> create -> revoke) * feat(homolog): L1b suite httpYac de API (models, chat, auth de management, health) * feat(homolog): L1c checker SSE de streaming real (TDD no parser) * feat(homolog): L2 smoke de providers reais via promptfoo gerado do catálogo * feat(homolog): L4a Playwright homolog config + login storageState * feat(homolog): L4b smoke de todas as rotas do dashboard (descoberta via fs) * feat(homolog): L4c fluxo criar/revogar API key pela UI * fix(homolog): resiliencia real-environment — stream:false no smoke promptfoo, retry de socket keep-alive, key efemera com sufixo unico * feat(homolog): L5 orquestrador npm run homolog + relatorio CTRF unificado * docs(homolog): guia de operacao da suite + fragment de changelog + allowlist env-doc-sync * fix(homolog): paraleliza o sweep de rotas do dashboard (fullyParallel + 8 workers) * fix(homolog): isola outputs crus em homolog-report/raw para nao quebrar o ctrf merge * fix(homolog): outputDir absoluto do reporter CTRF da UI (path relativo escapava do worktree) * chore(quality): allowlist the 5 homolog-suite devDependencies (ctrf-io trio, httpyac, promptfoo) after registry verification * chore(quality): register the homolog Playwright suite as a test-discovery collector (run.mjs -> tests/homolog/ui) * fix(ci): raise dast-smoke timeout 12->25min (build alone eats up to 11min) (#7139) * fix(tests): vitest UI suite back to green (69 fails triaged — WS6.1) (#7127) test:vitest:ui was advisory/parked with 70 failing tests across 30 files (of 159 total). Triaged by grouping failures by root cause instead of fixing one-by-one: - 15 files (use-virtual-list, use-traffic-stream, use-system-proxy-exit-guard, use-session-recorder, use-resizable-panels, traffic-inspector-page, timing-i18n, stats-tab, session-recorder-bar, same-context-filter, historic-session-banner, conversation-tab, conversation-tab-separators, cli-tools-no-mitm-tab, agent-bridge-server-card-a11y) were authored against node:test but live under tests/unit/ui/*.test.tsx, which vitest.config.ts collects but test:unit's glob (only *.test.ts) never does — orphaned. Fixed by switching their describe/it/beforeEach imports to "vitest". - jsdom does not implement window.matchMedia, and several dashboard components read it via useTheme() (directly, or transitively through ProviderIcon). Added tests/_setup/vitestUiPolyfills.ts (wired into vitest.config.ts) with a minimal MediaQueryList polyfill — fixed providerCascadeNode, ProviderIcon-icon-url, CliAgentsPage, playground-studio, comboLiveStudio, memories-tab, home-topology-hidden, ProxyRegistryManager-tdz. - playground-build-tab.test.tsx (9 tests) and compressionHub*.test.tsx (2 tests) asserted against pre-redesign UI: BuildTab now sits behind a 3-step BuildWizard (mode picker -> configure -> run), and CompressionHub is a Phase-2 thin overview without the old master toggle/mode selector/pipeline list. Rewrote the build-tab test to drive the wizard, and removed the two compressionHub.test.tsx assertions already superseded by compressionHub-active-selector.test.tsx. compressionHub-context-editing.test.tsx asserted stale Portuguese copy against a component that deliberately uses literal English strings (documented hydration workaround) — aligned to the real text. - search-tools-compare-tab.test.tsx: the D22 4-provider cap documented in docs/frameworks/SEARCH_TOOLS_STUDIO.md was never implemented in CompareTab — fixed the component (disable extra toggles + cap selectAll + warning message) since the test was correct and the component was the bug. Also fixed an assertion looking for a <table> that never existed (the results panel is a div-based side-by-side layout). - CliAgentsPage.test.tsx: the agent-tool catalog grew from 6 to 8 (omp, letta added) since the test was written — updated the fixture and expected count. - memories-tab.test.tsx: a call-order-dependent fetch mock (mockResolvedValueOnce + fallback) broke once MemoriesTab started firing an immediate health check that raced its 300ms-debounced list fetch — switched to a URL-keyed mock like the rest of the file. - home-topology-hidden-4596.test.tsx: useLiveDashboard now runs an async handshake fetch before opening the WebSocket — stubbed fetch and awaited it. - same-context-filter.test.tsx: the filter branch moved from useTrafficStream.applyFilter into the extracted, reusable matchesTrafficFilter() helper — updated the source-grep target. - tests/unit/ui/provider-plan-config.test.tsx deleted: it tested ProviderPlanConfigClient, which tests/unit/quota-plans-route-retired.test.ts proves was deliberately retired (Plans screen removed). Result: test:vitest:ui 158/158 files, 870/870 tests passing (was 30 failed / 159, 70 failed / 743). test:vitest (MCP/autoCombo) still green at 28/28, 253/253. Not promoted to blocking in this PR per the task — the owner promotes after reviewing the green suite. * chore(ci): promote test:vitest:ui to blocking (suite green after #7127) (#7147) * fix: preserve relayAuth for pool-referenced relay proxies (#5716) (#7182) * fix(providers): reject chat requests for cloud-agent-only jules provider (#6699) (#7193) * fix(db): cap OOM probe-failure cycle in getDbInstance() (#6835) (#7186) When better-sqlite3/node:sqlite are unavailable and the sql.js WASM fallback OOMs while probing storage.sqlite, getDbInstance() rethrew an identical 'Out of memory while probing' error on every call, forever — unlike the generic-corruption probe-failure path (#6632), which correctly caps at 3 attempts via the restore-count cycle breaker. Because the OOM path never renames the file away (intentional — OOM is not corruption), the existing cap is structurally unreachable for this branch, so every independent background poller (BATCH, ProviderLimitsSync, HealthCheck, ModelSync) kept re-triggering the same failure with no terminal diagnostic, hanging the app forever. Adds an independent __omnirouteDbOomFailureCount cycle-breaker mirroring the existing threshold of 3, throwing a distinct terminal 'Aborting startup' diagnostic after repeated OOM failures instead of looping. Does not touch the rename/backup safety mechanism. Reported-by: xHmeyer, mostafa-binesh * fix: route zai-web (and other registry-entry web-cookie providers) connection-test cookie probe through the configured proxy (#7058) (#7192) * fix: restore mobile grid-cols-1 fallback on quota page card grid (#7072) (#7194) * fix: include proxyId when testing a saved registry proxy (#7080) (#7189) * fix: surface real claude-web error body for non-SSE 400s (#7134) (#7196) tlsFetchStreaming() streams the upstream response to a temp file via tls-client-node's streamOutputPath mode. For a non-SSE, non-2xx response the native binding resolves with an empty in-memory `body` field even though the real error bytes were already written to (and peeked from) the temp file, so genuine Claude 400/403/429/500 error details were silently discarded and replaced with "no response body". Fall back to a bounded read of the temp file when the resolved response's body is empty, and export tlsFetchStreaming for dependency-injected testing without --experimental-test-module-mocks. * fix(dashboard): agent bridge dns toggle uses POST, not PUT (#7157) (#7197) The dns toggle button called fetch(..., { method: "PUT" }) but src/app/api/tools/agent-bridge/agents/[id]/dns/route.ts only exports POST, so Next.js auto-returned 405 on every Start/Stop DNS click. Fixes the frontend caller to match the documented POST contract (docs/frameworks/AGENTBRIDGE.md:490) already covered by tests/unit/agent-bridge-dns-route-validation.test.ts. Adds a regression test asserting the fetch call uses method: POST. * fix(dashboard): implement missing handleToggleSource on Free Pool tab (#7161) (#7200) * fix: sanitize non-Latin1 chars in combo diagnostic headers (#6612) (#7190) * fix(providers): refresh OpenCode (oc) free-tier model catalog (#6998) (#7188) The oc registry entry (opencode.ai/zen/v1) hardcoded 6 free-tier model IDs (minimax-m3-free, minimax-m2.5-free, ling-2.6-1t-free, trinity-large-preview-free, nemotron-3-super-free, qwen3.6-plus-free) that were delisted upstream and now return 401 "Model X is not supported". Live upstream instead offers 4 different free models (mimo-v2.5-free, hy3-free, nemotron-3-ultra-free, north-mini-code-free) that were never added to our static catalog. Swap the 6 delisted IDs for the 4 currently-live ones, confirmed against https://opencode.ai/zen/v1/chat/completions on 2026-07-14. Updates two existing tests (minimax-m3-model-registry, provider-registry-qwen-vision) that asserted the now-delisted minimax-m3-free was present in the oc catalog — they now assert its absence, matching the corrected contract. * fix: honor combo-level proxy assignments from the registry (#7149) (#7201) * fix(providers): DuckDuckGo VQD 429 misclassified as 503 (#6996) (#7185) acquireVqdHeaders() discarded the upstream HTTP status of the /duckchat/v1/status call and collapsed every non-2xx response to {vqd4:null, vqdHash1:null}. execute() then always returned a hardcoded 503 when the token could not be acquired, regardless of whether DuckDuckGo actually returned 429 (rate limit), 403, or a genuine 5xx. This mattered beyond the confusing error message: per the resilience contract only 408/500/502/503/504 should trip the whole-provider circuit breaker, not 429. Mislabeling a real 429 as 503 caused the entire ddgw/* catalog to get knocked offline for the breaker reset window instead of a short cooldown. Now acquireVqdHeaders()/acquireAuthHeaders() thread the real status and Retry-After header through, and execute() surfaces a genuine 429 (with Retry-After) instead of the hardcoded 503; the 503 fallback is kept for non-429 failures and network errors. Regression test: tests/unit/duckduckgo-vqd-429-misclassification-6996.test.ts * fix: wire modelAliases fetch into HermesAgentToolCard (#7151) (#7195) * fix: stop duplicating text in Gemini Web streamed responses (#7163) (#7198) * fix: extend turbopack ignoreIssue suppression to compression module (#7051) (#7180) * fix: wire adaptive context-budget dial into settings schema and DB (#7005) (#7183) * fix: wire adaptive context-budget dial into settings schema and DB (#7005) * chore(db): re-export compressionContextBudget from localDb.ts per db-rules gate (#7005) * chore(db): keep localDb.ts line-neutral after compressionContextBudget re-export (#7005) * fix: recognize Ollama Cloud session usage-limit 429 as quota-exhausted (#7071) (#7181) * fix: recognize Ollama Cloud session usage-limit 429 as quota-exhausted (#7071) Ollama Cloud's 5-hour "session" usage-limit 429 body ("you (NAME) have reached your session usage limit...") was never recognized as quota-exhausted -- only the sibling "weekly usage limit" wording was fixed (#6638/#3709). Neither the generic QUOTA_PATTERNS list nor the dedicated weekly-quota classifier matched the session wording, so checkFallbackError() fell through to the generic ~3s rate-limit backoff instead of a long QUOTA_EXHAUSTED cooldown -- combo/LKGP routing cycled back to the "exhausted" account almost immediately instead of advancing to the next one. Adds isSessionUsageLimitText()/buildSessionQuotaFallback() to quotaTextCooldowns.ts, mirroring the weekly-quota pair, with a 5h cooldown matching Ollama Cloud's documented session window. Wired unconditionally into checkFallbackError() next to the weekly check so apikey-category providers like ollama-cloud are covered. * chore(test): register issue-7071-ollama-session-quota.test.ts in stryker tap.testFiles (#7071) * fix: stop opencode-go quota lookup defaulting to Z.AI endpoint (#7022) (#7187) * fix: stop opencode-go quota lookup defaulting to Z.AI endpoint (#7022) getOpenCodeGoUsage() defaulted OPENCODE_GO_QUOTA_URL to https://api.z.ai/api/monitor/usage/quota/limit, a Zhipu AI (Z.AI/GLM) endpoint unrelated to opencode.ai. Whenever a connection had no dashboard-scraping config (workspaceId/authCookie), the user's real OpenCode Go API key was sent as a Bearer token to that third-party host by default, with no operator opt-in. Remove the hardcoded default: the quota-by-API-key fetch now only runs when the operator explicitly sets OMNIROUTE_OPENCODE_GO_QUOTA_URL. With it unset (the default), getOpenCodeGoUsage() returns a descriptive message and makes zero outbound calls, since OpenCode Go has no public quota API. Also updates .env.example and both EN/zh-CN copies of docs/reference/ENVIRONMENT.md to drop the stale Z.AI default value and fix the stale open-sse/services/usage.ts source-file reference. Regression test: tests/unit/opencode-go-quota-no-zai.test.ts (RED on current code, GREEN after the fix). * fix: align opencode-go-usage tests with opt-in quota URL contract (#7022) The prior commit removed the hardcoded api.z.ai default from OPENCODE_GO_QUOTA_URL, making the quota-by-API-key path opt-in via OMNIROUTE_OPENCODE_GO_QUOTA_URL. Six pre-existing tests in opencode-go-usage.test.ts still asserted the old default-fetch behavior and the old Z.AI-specific error wording, so they broke. Set OMNIROUTE_OPENCODE_GO_QUOTA_URL before the module import (the value is read once at load time) to simulate an operator who opted in, and update the two error-message assertions to the new generic wording ("the configured OMNIROUTE_OPENCODE_GO_QUOTA_URL endpoint" instead of "the Z.AI quota API"). Each test still verifies exactly the same behavior it did before (invalid key, fetch failure, 200 with auth error in body, invalid JSON, quota shape) — only the opt-in setup and message wording changed. * fix: filter hidden custom models out of legacy combo model picker (#7156) (#7199) * fix: filter hidden custom models out of legacy combo model picker (#7156) * chore(test): move model-select-modal-hidden-models-7156 test into tests/unit/ui (collector coverage) (#7156) * fix(ci): run quality gates on Mergify merge-queue draft PRs (anchor check never ran, queue always dequeued) (#7202) * fix: add dashboard-scoped typecheck gate covering src/app/(dashboard) TSX (#7033) (#7203) typecheck:core (the only blocking CI typecheck gate) runs against a curated 27-file allowlist that excludes all src/app/(dashboard) TSX, and next.config.mjs sets typescript.ignoreBuildErrors: true so next build never type-checks it either. Orphaned-identifier regressions there (the exact class fixed in #6625/#6909) were invisible to CI. Adds tsconfig.typecheck-dashboard.json (extends tsconfig.json, scoped to src/app/(dashboard)/**/*.ts(x)) plus check:dashboard-typecheck, a gate script that runs tsc against it and diffs per-file/per-TS-code error counts against a frozen baseline (config/quality/dashboard-typecheck-baseline.json, 262 pre-existing errors), following the same stale-enforcement allowlist pattern as check-known-symbols. Only NEW errors beyond the baselined count fail the gate; wired as a new blocking step in ci.yml (lint job) and quality.yml (fast-gates). Regression test (tests/unit/build/check-dashboard-typecheck.test.ts, 8 tests) reproduces the #6625/#6909 orphaned-identifier bug class against the pure parseTscOutput/diffAgainstBaseline helpers. * fix: raise main server keepAliveTimeout/headersTimeout above Node's 5s default (#7003) (#7191) * fix: raise main server keepAliveTimeout/headersTimeout above Node's 5s default (#7003) JetBrains AI Assistant's pooled java.net.http.HttpClient reuses a keep-alive connection past Node's unconfigured 5_000ms keepAliveTimeout, hitting a socket the server already tore down and getting 0 response bytes back ("HTTP/1.1 header parser received no bytes"). Wire a new getMainServerTimeoutConfig() (mirroring apiBridgeServer's pattern) into run-next.mjs so the main dashboard/API server raises keepAliveTimeout to 65s and headersTimeout to 66s by default, both env-overridable. * fix: wire main-server keepAlive timeouts into standalone/production server path (#7003) getMainServerTimeoutConfig() was only wired into scripts/dev/run-next.mjs, the dev-only entry point for `npm run dev`/`npm start`. The server real end users run — `omniroute serve` (npm-installed CLI), Docker, and Electron — spawns the standalone Next build's server.js via run-standalone.mjs, which prefers server-ws.mjs (built verbatim from scripts/dev/standalone-server-ws.mjs by assembleStandalone.mjs) over the bare server.js precisely because it wraps http.createServer with production behavior the bare server lacks. That wrapper never configured keepAliveTimeout/headersTimeout, so the JetBrains AI Assistant reconnect bug this issue reports still hit the production entry point after the first pass of this fix. Wire the same helper into the wrapped server object there too. * feat(ci): Trunk Flaky Tests uploads for vitest + Playwright E2E (WS5.2/5.3) (#7175) * feat(ci): Trunk Flaky Tests upload on the fast-path vitest job (per-PR volume) (#7205) * Add cliproxy provider exposure controls and manifest injection * fix(ui,services): expose cliproxy provider in manifest when absent upstream * test(services): assert service providers expose models via v1 provider models API * refactor(services): centralize service-provider backend identifiers * test(services): cover service backend helper primitives * refactor(services): share embedded service manifest metadata * test(services): lock manifest metadata for synthesized backend providers * chore(release): script the 0a.0b PR re-home with a verified read-back (#7312) The parallel-cycle model hands the frozen release/vX to the captain and cuts release/vX+1 for everyone else. Phase 0a.0b step 3 then re-homes every open PR onto the new cycle — today as a hand-run loop of gh pr edit --base. Three things make that loop unreliable at exactly the moment it matters: 1. gh pr edit --base FAILS SILENTLY (v3.8.42). It exits 0 and leaves the base untouched, so every edit needs a gh pr view --json baseRefName read-back. A human mid-release skips that. 2. gh pr list caps at 30 results by default. A loop written without --limit re-homes the first 30 of 148 and reports success. 3. Volume: the v3.8.49 freeze had 148 open PRs — roughly 450 API calls across edit, verify and comment. The script does the read-back on every PR, uses --limit 300, is idempotent (a PR already on the next base is skipped, so a resumed release re-runs safely), refuses to start when the next branch does not exist yet, and exits non-zero listing any PR whose retarget did not take. It also prints the reminder that it cannot solve the other half: PRs opened AFTER it runs. Those need the repo default_branch pointed at the live cycle — contributors open PRs against the default branch, and while that stays on main they never target a release branch at all (6 such PRs on 2026-07-15). classify() is pure and unit-tested: retarget open and draft PRs on the frozen branch; never touch main (the release PR's own lane), an older shipped release, or a PR already re-homed. Refs #7307 * fix(build): packed tarball boot crash — server-ws timeout import escaped the package (#7065 class) (#7308) * fix(build): server-ws timeout helper as shipped sibling — ../../src import crashed every packed boot (#7065 class) * test(build): align pack-artifact-policy fixture with the new dist/main-server-timeouts.mjs required path * fix(skills): register cli-skill-collector in the agent-skills catalog (Integration 2/2 base-red) (#7310) * fix(skills): register cli-skill-collector in the agent-skills catalog (#6294 shipped the dir only) * chore(skills): regenerate cli-skill-collector SKILL.md via the generator, preserving the #6294 authored workflow in the custom block * fix(skills): derive coverage totals from the id lists + align remaining count assertions (45 catalog / 21 cli) * fix(skills): SkillCoverage totals are number, not stale literals * test: update stale ninerouter version fixture and prune stale suppressions * chore(ci): make the Electron Windows leg advisory with bash stderr capture (first-run failure diagnosis) (#7340) * fix(ci): Coverage job timeout 10->20min (lcov reporter pushed it past the old cap) (#7342) * test(ci): make #6634 selfref guard hermetic — read file from disk, no git ref (#7327) check-test-masking-selfref-6634.test.ts did git I/O inside a unit test (`git show origin/main:<file>`), the single most common red across today's babysit sweep — GitHub-hosted runners use shallow/single-ref checkouts with no origin/main, so the show fails with "fatal: invalid object name". The prior hotfix (2e42b8efc, #7174) wrapped it in try/catch + on-demand fetch + t.skip() on failure, but t.skip() itself trips the PR Test Policy weakened-assert gate (confirmed today on #7300), and origin/main was the wrong ref anyway — PRs target release/v3.8.49, not main. Ported the hermetic version proven on PR #7300 (@growab): read the real current source of check-test-masking.test.ts from disk instead of diffing against a git ref, and use an empty-string base (baseTaut/baseExtTaut = 0) instead of the pre-#6404 git snapshot — this maximizes headTaut - baseTaut, the strictest input for the exclusion under test, so the guard is exercised at least as hard as before. No git ref, no skip, no CI-shape dependency. Verified both directions locally: - SELF_TEST_FIXTURE_RE neutralized in check-test-masking.mjs -> test FAILS (10 new bare tautologies + 28 new extended tautologies reported) - restored -> test PASSES, and the full check-test-masking.test.ts suite (55 tests) stays green, confirming the #6634 self-referential-fixture regression this guard exists for is still covered. Co-authored-by: growab <nekron@icloud.com> * chore(quality): tighten the coverage ratchet to the CI's real numbers (#7326) The Quality Ratchet has been red on main, and not for a regression — the report says 'OK (57 métricas, 11 melhoraram)'. It fails the --require-tighten step: ✗ coverage.branches: melhorou de 73 para 78.1 (delta 5.1000 > slack 5) — rode 'npm run quality:ratchet -- --update' e commite o baseline apertado The gate was asking for this in plain text. The baseline's own note names the same trigger: 'Apertar via quality:ratchet -- --update a partir do 1o run de coverage mergeada do CI que popule essas chaves.' Values are the CI's, not a local run. The baseline warns that a local test:coverage measures ~68% against the CI's ~76.5% — tightening to local numbers would write the wrong floor. So this reproduces the CI's exact inputs: eslint-results + coverage-report artifacts downloaded from the merged-coverage run on main (29387411665), re-rooted from the runner's paths to the local cwd so extractModuleCoverage can match CRITICAL_MODULE_PATHS, then quality:collect + quality:ratchet --update. Collected output matches the CI's report line for line (branches 78.1, statements/lines 80.8, functions 86.44, chatCore 72.98, combo 85.42, accountFallback 96.78, auth 92.55). Verified: no baseline key added or removed (56 before, 56 after) — only the 12 coverage values moved. The 57-vs-56 metric count between the CI's run and a local one is --allow-missing skipping the metrics only CI collects (mutation scores, CodeQL, bundle size). Worth recording why the improvement appeared now: it is real, but it surfaced because Coverage had been SKIPPED whenever unit shards went red — so the ratchet was passing trivially over ABSENT data. Fixing the shards on #7300 made coverage run and the ratchet finally had something to compare. * fix(stream): reconcile encrypted Codex reasoning visibility without mutating upstream item (#7304) * fix(stream): reconcile encrypted Codex reasoning visibility without mutating upstream item Resolves the collision between two open PRs on ensureVisibleResponsesReasoningSummary: #7095 (xz-dev) found that chat clients see nothing when Codex exposes reasoning only as encrypted_content, and added a visible placeholder — but did so by mutating item.summary in place. #7176 (JxnLexn) found that same mutation corrupts the forwarded response item, discarding the encrypted_content shape Codex needs for follow-up requests, and removed the mutation — but that also silently dropped the placeholder, so chat clients went back to seeing nothing. The mutation existed only so a later line could read the summary text back off the same item. getVisibleResponsesReasoningSummaryText() computes that text without touching the item, so: - synthetic response.reasoning_summary_text.delta / .part.done events still carry the placeholder for chat clients (#7095's goal), and - the forwarded response.output_item.done payload keeps its original encrypted_content intact with no fabricated summary field (#7176's goal). Applied at both call sites #7095 identified: the native Responses passthrough in stream.ts/passthroughTailProcessor.ts, and the Responses-to-Chat-Completions translator in openai-responses.ts. Closes #7095, closes #7176. Co-authored-by: Xiangzhe <xiangzhedev@gmail.com> Co-authored-by: Jan Leon <Jan.gaschler@gmail.com> * test(stream): guard the encrypted-reasoning mutation via the completed backfill path The output_item.done line is echoed verbatim on the wire, so a re-introduced item.summary mutation does NOT surface in that event — verified by re-injecting the mutation, which left the existing assertion green. The mutation does surface in the response.completed snapshot, where the captured reasoning item is re-serialized when upstream sends an empty output (store: false). Adds that case, which fails as expected when the mutation is re-introduced, making the #7176 half of the reconciliation an enforced regression guard rather than an incidental property of the current code path. Co-authored-by: Xiangzhe <xiangzhedev@gmail.com> Co-authored-by: Jan Leon <Jan.gaschler@gmail.com> --------- Co-authored-by: Xiangzhe <xiangzhedev@gmail.com> Co-authored-by: Jan Leon <Jan.gaschler@gmail.com> * chore(ci): stop dependabot proposing typescript majors — peer-blocked by typescript-eslint (#7306) typescript-eslint pins a hard upper bound on its typescript peer (8.64.0 → ">=4.8.4 <6.1.0"). A major TS bump violates it, so the failure is not one check — it is the whole toolchain at once. #7068 is the demonstration: dependabot grouped typescript ^6→^7 with six harmless dev bumps (@types/node, eslint, fast-check, knip, prettier, typescript-eslint) and turned Build, Lint, Quality Ratchet, Unit (6/8, 8/8), Integration (1/2, 2/2) and dast-smoke red in a single PR. The six innocuous updates were blocked by the one that could never pass. Ignoring the major lets the rest of the group flow on its own. TS majors are a toolchain migration and deserve their own PR and their own CI run — not a weekly automated attempt that cannot succeed until typescript-eslint widens the peer. Refs #7068 * test(dashboard): dedicated regression guard for #6815 density guarantee (#7291) * test(dashboard): dedicated regression guard for #6815 density guarantee Coverage for the #6815 multi-column density guarantee was only ever asserted incidentally, by two other guards (#7072, #3520) that pinned the literal sm:grid-cols-2 token. That coupling evaporated the coverage when PR #7027 migrated the component to a container-driven auto-fit template and the literal token was removed from both files. Adds a dedicated guard that simulates, from the shipped className, how many columns the per-group card grid renders at a wide container width -- supporting both the breakpoint-ladder and auto-fit mechanisms this component has shipped with -- and asserts >1 column, without asserting any specific Tailwind token. * chore(changelog): fragment for #7291 density guard * test(ci): mock route bridge surfaces error message, not raw stack (#7354) The E2E mock HTTP server's 500 catch sent error.stack straight to the response body, which CodeQL flags as js/stack-trace-exposure (medium). It's test-only localhost code, but the repo-wide CodeQL ratchet counts open alerts across all branches — so this one alert (baseline 0 → 1) turned the Quality Ratchet red on EVERY open PR into both main and release, masking whatever each PR actually changed. Surface error.message instead: clears the alert, keeps a useful signal for a failing mock route, and doesn't log to stderr (node:test native runner corrupts its report stream on console output). The test only asserts status 200, so the 500 body is not checked. Introduced by the #7304 integration test added this cycle. * ci(release-green): add a main-green arm to detect when main goes red (#7355) The release-green workflow already reproduces the release-equivalent gate on release/** and opens a tracking issue on HARD failures — but main had no such watch. Under the parallel-cycle model main only receives merged work at the release squash, so a gate/infra fix that landed only on the release branch leaves main red the whole cycle, and repo-wide gates (CodeQL alert count, ratchet baselines) turn EVERY PR into main red on a check unrelated to its diff. v3.8.49 hit this 3× in one night. Adds a dedicated main-green job (push to main + the same 3 crons + dispatch) that checks out main literally (no resolver, no injection surface), runs the same validate-release-green.mjs, and opens/updates a '🔴 main branch not green' issue pointing at the companion-PR fix. Gates the existing release-green job with an if: so a push to main doesn't re-validate release and vice-versa; schedule/dispatch sweep both. Detection backstop for the prevention rule in _shared/merge-gates.md §8. * fix(sse): sanitize non-ok Antigravity streaming error body (port from 9router#2461) (#7106) Root cause: the STREAMING branch of AntigravityExecutor.executeOnce() had no !response.ok check at all — it unconditionally wrapped the upstream response body in a pass-through TransformStream, unlike the sibling non-streaming branch which already built a sanitized error via buildAntigravityUpstreamError. When Google's 403 error body was binary/non-UTF8 (observed: gzip-magic-byte payloads), those raw bytes were forwarded verbatim, corrupting the client-visible error message ('[ERROR] [403]: <control-byte garbage>'). Fix: add the same !response.ok guard to the streaming branch, routing through buildAntigravityUpstreamError()/buildErrorBody() (hard rule #12) instead of piping unknown bytes through as if they were an SSE stream. Reported-by: Duongkhanhtool (https://github.com/decolua/9router/issues/2461) * fix(6954,6953): preserve system role + strip empty-signature thinking blocks (#6982) * fix(6954,6953): preserve system role + strip empty-signature thinking blocks #6954 — System turns misattributed as assistant (claude-to-openai.ts:352) The ternary `msg.role === 'user' || msg.role === 'tool' ? 'user' : 'assistant'` mapped any non-user/non-tool role (including 'system') to 'assistant'. Mid-conversation system turns (Claude format) lost their role on translation to OpenAI format, causing them to be treated as assistant output. Fix: add explicit 'system' branch to the ternary. #6953 — Empty-signature thinking blocks poison Anthropic leg (openai-to-claude.ts) Non-Anthropic providers (codex/gpt-5.x) synthesize thinking blocks with signature:''\. On replay, the old code fabricated a DEFAULT_THINKING_CLAUDE_SIGNATURE to fill the empty signature — but Anthropic rejects foreign signatures with HTTP 400, permanently degrading combo/blend routes to codex-only. Fix: strip thinking blocks with empty/missing signatures and redacted_thinking blocks with empty/missing data entirely. They carry no replayable value. Tests: 8 new tests (4 per bug), all passing. Existing #5312 and #5945 regression tests still pass — no interference. * fix(6953): strip only signature:"" thinking blocks, preserve undefined signature CI caught a regression: translator-helper-branches test had a Claude-format thinking block without signature field (undefined) that was being stripped by the original fix. The fix was too aggressive — it stripped both signature:"" (non-Anthropic synthesized) and signature: undefined (legitimate Claude-format). Correct behavior: - signature === "" (empty string): strip — hallmark of codex/gpt-5.x block - signature === undefined: preserve with DEFAULT_THINKING_CLAUDE_SIGNATURE fallback - redacted_thinking data === "": strip - redacted_thinking data === undefined: preserve with fallback Added regression test for undefined-signature preservation. --------- Co-authored-by: Diego Rodrigues de Sa e Souza <8016841+diegosouzapw@users.noreply.github.com> * fix(6980): classify Cloudflare AI neuron exhaustion as quota_exhausted (#6983) Cloudflare Workers AI free tier (10k Neurons/day, account-wide) returns 429 with body 'you have used up your daily free allocation of 10,000 neurons' which matched no QUOTA_PATTERNS keyword — falling through to rate_limit (~60s cooldown) instead of quota_exhausted. Two layers: 1. Provider-specific rule for 'cloudflare-ai' in providerRuleRegistry (scope: connection — budget is account-wide, not per-model) 2. Defense-in-depth: /daily free allocation/i in classify429 QUOTA_PATTERNS Tests: 11/11 pass (provider rule + classify429 paths covered). Closes #6980 Co-authored-by: Diego Rodrigues de Sa e Souza <8016841+diegosouzapw@users.noreply.github.com> * fix(models): preserve chat-capable image model rows (#7004) Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com> Co-authored-by: Diego Rodrigues de Sa e Souza <8016841+diegosouzapw@users.noreply.github.com> * fix(sse): register ollama-cloud in USAGE_FETCHER_PROVIDERS (#7026) (#7041) Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com> Co-authored-by: Diego Rodrigues de Sa e Souza <8016841+diegosouzapw@users.noreply.github.com> * fix(quality): read cognitiveComplexity= machine line in validate-release-green (#7009) (#7042) Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com> Co-authored-by: Diego Rodrigues de Sa e Souza <8016841+diegosouzapw@users.noreply.github.com> * fix(relay): bound Bifrost stream lifetime (#7093) Co-authored-by: Diego Rodrigues de Sa e Souza <8016841+diegosouzapw@users.noreply.github.com> * fix(sse): recognize xiaomi-tokenplan mimo as a thinking-mode model (#7098) * fix(sse): recognize xiaomi-tokenplan mimo as a thinking-mode model (port from 9router#1321) The reasoning_content injector already handles DeepSeek/Kimi/K2/MiniMax thinking-mode upstreams, echoing a placeholder reasoning_content on assistant turns that lack one. Its THINKING_MODEL_PATTERNS list omitted the xiaomi-tokenplan mimo family, so requests through xiaomi-tokenplan/mimo-v2.5-pro still hit upstream's 400 'reasoning_content in the thinking mode must be passed back to the API', making the model unusable in multi-turn conversations (e.g. Codex CLI). Add a /\bmimo\b/i pattern so mimo models get the same treatment. Reported-by: z.wl (@xxue-z) (https://github.com/decolua/9router/issues/1321) * docs(changelog): add fragment for #7098 mimo thinking-model fix * fix(codex): strip regex lookaround from tool schema patterns (#7100) * fix(codex): strip regex lookaround from tool schema patterns (port from 9router#1556) Codex/OpenAI's Responses API rejects JSON Schema pattern fields using regex lookaround (e.g. ^(?=.*@).+$) with a 400 'regex lookaround is not supported' error. The existing numeric-field sanitizer (coerceSchemaNumericFields) was only wired into the translated-request path (openai-to-claude.ts), not the native codex/openai passthrough path (normalizeCodexTools in open-sse/executors/codex/tools.ts), so lookahead/lookbehind patterns reached upstream unmodified and broke tool calls for clients that emit them (e.g. IDE agent harnesses validating an email field). Reported-by: evin (@evinjohnn) (https://github.com/decolua/9router/issues/1556) * chore(changelog): move #1556 entry to changelog.d fragment Consistency with the repo's canonical changelog.d/fixes/ workflow (avoids merge-storm re-conflicts from editing CHANGELOG.md directly). * refactor(codex): table-drive the regex-strip recursion to keep the complexity ratchet at baseline The #1556 lookaround strip walked every sub-schema field with its own copy-pasted if-block (properties / patternProperties / definitions / $defs, then prefixItems / anyOf / oneOf / allOf), pushing stripUnsupportedRegexPatterns past the cyclomatic threshold and check:complexity to 2057 > baseline 2056. Collapse the eight near-identical blocks into two loops over the field-name constants, with the object-map recursion factored into a helper. Same fields, same traversal order, same behavior — complexity is back at baseline 2056 and the #1556 regression tests still pass. * fix(compression): Headroom SmartCrusher skips developer-role messages (port from 9router#2132) (#7102) Root cause: SmartCrusher's system-message guard only excluded role === "system", but Codex CLI (open-sse/executors/codex.ts) sends its instructions/tool-schema turn with role "developer" (the Responses-API equivalent of system used by newer models). Every other system-exclusion guard in this codebase also covers developer (roleNormalizer.ts, contextManager.ts, claudeUpstreamMessages.ts, etc.) except this one, so Headroom happily tabular-compacted JSON arrays embedded in the developer turn (e.g. an update_plan tool schema example), corrupting the instructions the model needs to call the plan tool and breaking Codex CLI plan mode. Fix: extend the guard in crushMessages()/collectCompactableArrays() (smartcrusher.ts) to skip role === "developer" alongside role === "system". Reported-by: SingCJ (https://github.com/decolua/9router/issues/2132) * fix(providers): surface a warning on 404 model_not_found in OpenAI-compatible Check (port from 9router#2032) (#7103) Root cause: validateOpenAICompatibleProvider's chat-completions probe fallback treated ANY 4xx other than 401/403/429/400 as a silent 'credentials valid' pass with no warning, so a bogus/non-standard model id (e.g. Featherless/OpenRouter vendor/model typos) went undetected at Check time. The first real request then hit the upstream 404 model_not_found and the per-model lockout, holding the model unavailable for the configured reset window with no prior indication anything was wrong. User-visible effect: 'Check' now returns valid:true with an explicit warning (including the upstream error message when parseable) whenever the chat probe answers 404, so a bad model id is caught before it reaches production traffic and the lockout. Reported-by: advane204f (https://github.com/decolua/9router/issues/2032) * fix(executors): forward X-Session-ID/X-Title agent metadata headers (#7104) * fix(executors): forward X-Session-ID/X-Title agent metadata headers (port from 9router#2413) Custom agent clients (e.g. non-OpenCode providers) commonly send X-Session-ID and X-Title headers for upstream request tracking/attribution, but forwardOpencodeClientHeaders() only forwarded x-opencode-* keys plus User-Agent, silently dropping these for every client. Extends the existing case-insensitive allowlist forwarding path with x-session-id/x-title. Reported-by: Atikur Rahman Chitholian (@chitholian) (https://github.com/decolua/9router/issues/2413) * chore(changelog): move #2413 entry to changelog.d fragment Consistency with the repo's canonical changelog.d/fixes/ workflow (avoids merge-storm re-conflicts from editing CHANGELOG.md directly). * fix(cli): verify better-sqlite3 native binary is actually loadable (#7105) * fix(cli): verify better-sqlite3 native binary is actually loadable (port from 9router#2493) isBetterSqliteBinaryValid() only checked the .node file's magic bytes (ELF/Mach-O/PE header), never whether the binary was built for the ABI (NODE_MODULE_VERSION) of the Node runtime that loads it. A stale or foreign-ABI binary passed the check and then segfaulted the process on the first database call instead of triggering a rebuild via npmInstallRuntime(). The fix adds a real load probe (require() in a throwaway subprocess) after the magic-byte check, so an incompatible binary is now correctly reported as invalid and the runtime self-heal reinstalls it. Reported-by: Manikandan (@mrprohack) (https://github.com/decolua/9router/issues/2493) * chore(changelog): move #2493 entry to changelog.d fragment Consistency with the repo's canonical changelog.d/fixes/ workflow (avoids merge-storm re-conflicts from editing CHANGELOG.md directly). * fix(sse): handle space-separated arg name/value in Composer tool calls (port from 9router#1811) (#7116) parseInnerCall only split arg segments on a newline between the arg name and its value. Cursor's live Composer/Auto output has been observed using a single space instead, so those segments were treated as one long (space-containing) arg name with an empty value, silently no-opping Write/tool calls for Composer/Auto models. Fall back to splitting on the first whitespace boundary when no newline is present in the segment. Reported-by: way-art (https://github.com/decolua/9router/issues/1811) * fix(cli): remove MITM DNS spoof entries before killing server process (#7117) * fix(cli): remove MITM DNS spoof entries before killing server process (port from 9router#1809) stopMitm() killed the spawned MITM server process first and only removed the /etc/hosts DNS-spoof entries afterward. During that window any client whose DNS still resolved a target host to 127.0.0.1 but whose MITM listener was already dead got connect ECONNREFUSED 127.0.0.1:443 — exactly the community-confirmed workaround (stop DNS before stopping the server) proves. Swap the two steps so DNS is always cleared first, mirroring the ordering already used by repairMitm() and handleExitCleanup(). Reported-by: dionisius95 (https://github.com/decolua/9router/issues/1809) * refactor(mitm): extract repair planning out of manager to respect the file-size cap The #1809 DNS-before-kill ordering fix pushed src/mitm/manager.ts to 813 lines, over the 800-line cap check:file-size enforces for non-frozen files. Move the pure repair-planning pieces (collectManagedHosts, the RepairPlan shape and its filesystem/cert/DNS sweep) into a sibling src/mitm/repair.ts. The in-memory session bookkeeping repairMitm() owns — cached sudo password, orphaned flag, PID file — deliberately stays in manager.ts, so the seam is "plan the repair" vs "own the session". manager.ts is now 731 lines; behavior is unchanged. The DNS-first ordering fix and its regression guard (tests/unit/mitm-stop-dns-before-kill-1809.ts) are untouched and still pass. * fix(mitm): split stopMitm() DNS/kill steps to fix complexity ratchet regression stopMitm()'s new DNS-before-kill ordering (#1809) pushed its cyclomatic complexity to 18 (max 15), regressing the complexity ratchet from 2056 to 2057. Extract the DNS-removal step and the process-kill step (in-memory + PID-file fallback) into two private helpers, mirroring the existing performRepairSteps() extraction pattern in repair.ts. Behavior unchanged; complexity back at 2056 (cognitive-complexity drops to 889, one under baseline). * fix(api): check Vercel SSO-protection PATCH response on relay deploy (#7119) * fix(api): check Vercel SSO-protection PATCH response on relay deploy (port from 9router#1037) The Vercel relay deploy route disabled Deployment Protection (SSO) by firing a PATCH request with .catch(() => {}) and never checking res.ok. When Vercel rejects or no-ops the PATCH (plan doesn't allow disabling protection, an under-scoped token, etc.), the relay was still saved and activated as a healthy proxy pool, and later requests routed through it failed with an undiagnosed 403 Access denied from Vercel's own deployment protection — indistinguishable from an upstream-provider rejection (e.g. Codex/ChatGPT edge-IP blocking). Extract disableSsoProtection() to check the PATCH response and surface an ssoProtectionWarning in the deploy response when it fails, so the failure source can be diagnosed instead of silently masked. Reported-by: Rico Aditya (@ricatix) (https://github.com/decolua/9router/issues/1037) * refactor(api): extract vercel-deploy POST helpers to keep the cognitive-complexity ratchet at baseline The SSO-protection check added to POST pushed its cognitive complexity from 15 to 21, regressing the cognitive-complexity ratchet (891 > baseline 890). Extract two pure helpers with identical behavior: - buildDeployErrorResponse(): the sanitized non-ok Vercel deploy response - resolveSsoProtectionWarning(): the SSO PATCH check + warning string POST now reads as a flat sequence of guards. No behavior change. * fix(combos): reject oversized fusion panels before fan-out (port from 9router#1905) (#7120) A fusion combo fans every panel model out in parallel and buffers each model's full response text in memory simultaneously. With the runtime heap capped by Dockerfile's OMNIROUTE_MEMORY_MB (default 1024MB), a large panel (reported: ~73 models via an 'auto' combo with strategy: fusion) with sizable concurrent responses can exceed the heap ceiling and OOM-crash the whole container instead of failing one request. handleFusionChat now rejects panels above a configurable hard cap (FUSION_DEFAULTS.maxPanel = 40, overridable per-combo via fusionTuning.maxPanel) with a clean 400 before fan-out begins. Reported-by: Phong Vu (@fontvu) (https://github.com/decolua/9router/issues/1905) * fix(combo): detect empty content_block in streaming SSE peek (#7121) * fix(combo): detect empty content_block in streaming SSE peek (port from 9router#1382) The bounded SSE peek in validateResponseQuality() treated ANY content_block_start/delta/stop event as proof of real output and stopped buffering immediately, without checking whether the block actually carried text/tool_use content. Some upstreams (reported: DeepSeek, GLM via claude→openai translation) can open and close a text content_block with empty text and no tool_use on tool-heavy requests — the gateway logged success and forwarded a client-visible empty completion, and combo routing never failed over to the next model. Track real content separately from 'a content_block_* event was seen': a tool_use/redacted_thinking block start is self-evidently real signal, a text/thinking block start is not (real content only confirmed via a subsequent delta carrying non-empty text/thinking, or an input_json_delta streaming tool arguments). A completed lifecycle (message_start + message_delta/stop) that never produced real content now fails validateResponseQuality(), matching the existing content_filter empty-stream detection path (#3685). Reported-by: heishen6 (https://github.com/decolua/9router/issues/1382) * refactor(combo): extract SSE lifecycle applier to keep the complexity ratchets at baseline The #1382 empty-content_block peek added a branchy switch inline in parseAccumulatedSse, pushing check:complexity to 2057 > baseline 2056. Move the switch to a module-level applySseLifecycleEvent() and hold the four lifecycle booleans in a single SseLifecycleFlags object threaded through it, so the closure no longer copies flags in and out per event. The per-event predicates (content_block_start / content_block_delta / message_delta) are split into small guard helpers, which keeps the applier flat — cognitive complexity punishes nesting, and an earlier switch-only extraction traded the cyclomatic ratchet for a cognitive regression at 891 > 890. Logic is unchanged; both ratchets are now green (complexity 2055, cognitive-complexity 890) and the #1382 regression tests still pass. * fix(auto): use p95 fallback in speed factors (#7128) Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com> Co-authored-by: Diego Rodrigues de Sa e Souza <8016841+diegosouzapw@users.noreply.github.com> * Use OpenAI chunks for early chat keepalives (#7136) * Use OpenAI chunks for early chat keepalives * Update keepalive assertion to match chat completion chunk format --------- Co-authored-by: Diego Rodrigues de Sa e Souza <8016841+diegosouzapw@users.noreply.github.com> * refactor: reduce cognitive complexity in provider-plugin-manifest route (ratchet gate) Extract isValidServiceModelEntry() and toProviderPluginModel() out of pickServiceModels() so the filter predicate and per-model normalization are named helpers instead of inline closures. This drops the function's cognitive-complexity score from 16 back under the 15 threshold without changing behavior (covered by tests/unit/api/v1/provider-plugin-manifest-route.test.ts). Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> * ci: retrigger with fresh base snapshot (PR Test Policy ran against a stale GITHUB_BASE_SHA) Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> * [needs-vps] fix(dashboard): add vision-capability toggle for custom OpenAI-compatible models (#7124) * fix(dashboard): add vision-capability toggle for custom OpenAI-compatible models (port from 9router#1904) detectVisionInput()/getCustomVisionCapabilityFields() already honoured an explicit supportsVision flag on a custom-model record, but there was no way to set it: the POST/PUT /api/provider-models Zod schema and updateCustomModel()/addCustomModel() silently dropped the field, and the 'Custom Models' add/edit UI had no checkbox at all. Self-hosted/local backends that don't self-report an image input modality (OpenRouter-style architecture.input_modalities) therefore had no way to be flagged vision-capable, so the vision tag never appeared and image inputs were rejected. Reported-by: nguyenphi37 (https://github.com/decolua/9router/issues/1904) * refactor(dashboard): extract providerCredentialText from providerPageHelpers to respect the file-size gate providerPageHelpers.ts is a frozen god-file (cap 1053, split(\n).length metric) and this PR's own +3 lines (the #1904 supportsVision field) pushed it to 1054, failing check:file-size. Extract the cohesive providerText utility + the 4 web-session-credential label/hint/title helpers into a new leaf module (providerCredentialText.ts), re-exported from providerPageHelpers.ts for backward compatibility so all existing import sites keep working unchanged. File now sits at 946 lines, well under the frozen cap. * refactor(db): extract tri-state override helper to keep the complexity ratchet at baseline The #1904 supportsVision override added a second copy of the "absent keeps / null clears / else coerce" block already used by preserveOpenAIDeveloperRole, pushing updateCustomModel to 84 lines and check:complexity to 2057 > 2056. The file-size failure was masking this one: the gate exits on its first red, so complexity never ran until providerPageHelpers was back under its cap. Fold both blocks into applyTriStateBooleanOverride(). Behavior is unchanged — updateCustomModel is back under max-lines-per-function and the global count returns to the 2056 baseline (cognitive-complexity stays at 890). * [needs-vps] fix(dashboard): align onboarding tier content (#7125) * fix(dashboard): align onboarding welcome feature cards vertically * fix(dashboard): align onboarding tier content * chore: scope onboarding PR to UI fix * i18n(pt-BR): add onboarding.tier.flowCaption + afterSetup keys The two new tier keys added to en.json were missing from pt-BR.json, tripping the i18n-pt-br no-drift test (#6695). Add their pt-BR translations. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> --------- Co-authored-by: Diego Rodrigues de Sa e Souza <8016841+diegosouzapw@users.noreply.github.com> * fix(ci): fetch full base history in pr-test-policy (shallow graft broke merge-base) (#7501) With --depth=1 the base ref is grafted, so 'git diff base...HEAD' resolves a wrong merge-base for PR branches that recently merged the release branch. The three-dot diff then attributes ALREADY-MERGED sibling PRs' changes to the PR under test, producing false high-signal reds (deleted test files / weakened asserts that exist in no ref reachable from the PR). Observed live on #7329: the job blamed it for tests/unit/ui/provider-plan-config.test.tsx (deleted by an unrelated merged PR) and for #7106's antigravity files. Local reproduction with full history returns PASS for the same head. The job's checkout is already fetch-depth: 0, so the full base fetch only updates the ref — negligible cost. * [needs-vps] fix(electron): materialize Turbopack hashed-module symlinks during packaging (#6724, #6594) (#6794) * fix(electron): materialize Turbopack hashed-module symlinks during packaging Reconstructed onto release/v3.8.47 to drop unrelated main-drift (deps/electron/proxy files belong to #6620, not this PR); keeps only the author's changes. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> * fix(electron): actually enable materializeSymlinks on the electron standalone path The option existed in assembleStandalone but no production callsite passed it, so packaged builds still shipped absolute symlinks into the build machine's worktree for Turbopack hashed externals (better-sqlite3-<hash>, sqlite-vec-<hash>) — verified by dpkg -c on a freshly built .deb. One-line enablement on the electron prepare path, which is exactly the surface #6724/#6594 report. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> --------- Co-authored-by: huohua-dev <258873123+huohua-dev@users.noreply.github.com> Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> * fix(grok): strip reasoningEffort for grok cli models (#6938) Co-authored-by: minisforum <no@mail.com> Co-authored-by: Diego Rodrigues de Sa e Souza <8016841+diegosouzapw@users.noreply.github.com> * chore(quality): rebaseline zizmor 169->175 (cycle workflow drift) +6 from v3.8.48/v3.8.49 workflow changes (npm-publish WS1.3 #7092, electron-release, nightly-compat, nightly-release-green, CI restructures incl. #7501). Breakdown vs v3.8.47: +3 unpinned-uses (@vN convention), +2 cache-poisoning (own release-workflow artifact upload/cache -- operator-controlled, not fork-PR exploitable), +1 excessive-permissions (nightly-compat issues perm). No new template-injection/artipacked/dangerous-triggers. Measured zizmor 1.25.2 = 175 onda3a0be69. Unblocks Quality Gates (Extended) for #7329. * ci: re-trigger against release with #7501 (pr-test-policy full fetch) + zizmor 175 baseline Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: Chirag Singhal <76880977+chirag127@users.noreply.github.com> Co-authored-by: Chirag Singhal <chirag127@users.noreply.github.com> Co-authored-by: Ronaldo Davi <ronaldodavi@gmail.com> Co-authored-by: Diego Rodrigues de Sa e Souza <8016841+diegosouzapw@users.noreply.github.com> Co-authored-by: Andrew Munsell <andrew@wizardapps.net> Co-authored-by: WITALO ROCHA <witalo_rocha@hotmail.com> Co-authored-by: Wital <witalorocha216@gmail.com> Co-authored-by: Aoxiong Yin <i@yinaoxiong.cn> Co-authored-by: Andrew B. <37745667+AndrianBalanescu@users.noreply.github.com> Co-authored-by: Andrian B. <andrewbalanesq@gmail.com> Co-authored-by: Xiangzhe <32761048+xz-dev@users.noreply.github.com> Co-authored-by: Jon Bailey <297513015+Pitchfork-and-Torch@users.noreply.github.com> Co-authored-by: Pitchfork-and-Torch <Pitchfork-and-Torch@users.noreply.github.com> Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com> Co-authored-by: Samir Abis <me@samirabis.com> Co-authored-by: lucasjustinudin <34107354+lucasjustinudin@users.noreply.github.com> Co-authored-by: chy1211 <31048289+chy1211@users.noreply.github.com> Co-authored-by: ryanngit <74137224+ryanngit@users.noreply.github.com> Co-authored-by: whale9820 <whale9820@users.noreply.github.com> Co-authored-by: anhdiepmmk <n08ni.dieppn@gmail.com> Co-authored-by: Septianata Rizky Pratama <19322988+ianriizky@users.noreply.github.com> Co-authored-by: NOXX - Commiter <artur1992123@mail.ru> Co-authored-by: backryun <bakryun0718@proton.me> Co-authored-by: lunkerchen <labanchen@gmail.com> Co-authored-by: lunkerchen <lunkerchen@users.noreply.github.com> Co-authored-by: Ray Doan <raydoan.contact@gmail.com> Co-authored-by: kfiramar <83420275+kfiramar@users.noreply.github.com> Co-authored-by: Jan Leon <Jan.gaschler@gmail.com> Co-authored-by: Someres <168349709+quanturbo@users.noreply.github.com> Co-authored-by: MikeTuev <ra9ftm@gmail.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Imam Wahyu Widodo <120608486+hajilok@users.noreply.github.com> Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com> Co-authored-by: AgentKiller45 <jamalzzj45@gmail.com> Co-authored-by: judy459 <JUDYZHU459@outlook.com> Co-authored-by: growab <nekron@icloud.com> Co-authored-by: Diego Rodrigues de Sa e Souza <souzamiriamrodrigues790@gmail.com> Co-authored-by: Markus Hartung <mail@hartmark.se> Co-authored-by: KooshaPari <koosha@phenotype.io> Co-authored-by: Jade Guo <jade.gly@gmail.com> Co-authored-by: Paijo <14921983+oyi77@users.noreply.github.com> Co-authored-by: oyi77 <oyi77@users.noreply.github.com> Co-authored-by: Dayna Blackwell <dayna@blackwell-systems.com> Co-authored-by: backryun <backryun@daonlab.local> Co-authored-by: brick30llc-ctrl <brick30llc@gmail.com> Co-authored-by: brick30llc-ctrl <admin@brick30.com> Co-authored-by: Saren <saren@dumstruck.com> Co-authored-by: Rafael Dias Zendron <mmmarckos@gmail.com> Co-authored-by: Xiangzhe <xiangzhedev@gmail.com> Co-authored-by: Rafael Dias Zendron <rafael.zendron22@gmail.com> Co-authored-by: KooshaPari <62650152+KooshaPari@users.noreply.github.com> Co-authored-by: Wibias <37517432+Wibias@users.noreply.github.com> Co-authored-by: huohua-dev <celentanohertor@gmail.com> Co-authored-by: huohua-dev <258873123+huohua-dev@users.noreply.github.com> Co-authored-by: CitrusIce <31264099+CitrusIce@users.noreply.github.com> Co-authored-by: minisforum <no@mail.com>
1.2 MiB
Changelog (ไทย)
🌐 Languages: 🇺🇸 English · 🇸🇦 ar · 🇧🇬 bg · 🇧🇩 bn · 🇨🇿 cs · 🇩🇰 da · 🇩🇪 de · 🇪🇸 es · 🇮🇷 fa · 🇫🇮 fi · 🇫🇷 fr · 🇮🇳 gu · 🇮🇱 he · 🇮🇳 hi · 🇭🇺 hu · 🇮🇩 id · 🇮🇹 it · 🇯🇵 ja · 🇰🇷 ko · 🇮🇳 mr · 🇲🇾 ms · 🇳🇱 nl · 🇳🇴 no · 🇵🇭 phi · 🇵🇱 pl · 🇵🇹 pt · 🇧🇷 pt-BR · 🇷🇴 ro · 🇷🇺 ru · 🇸🇰 sk · 🇸🇪 sv · 🇰🇪 sw · 🇮🇳 ta · 🇮🇳 te · 🇹🇭 th · 🇹🇷 tr · 🇺🇦 uk-UA · 🇵🇰 ur · 🇻🇳 vi · 🇨🇳 zh-CN
[3.8.31] — 2026-06-20
[3.8.49] — TBD
[3.8.48] — 2026-07-13
⚠️ 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)
[3.8.47] — TBD
Living section — bullets land here as PRs merge into release/v3.8.47 (parallel-cycle model; cycle opened at the v3.8.46 release freeze). Finalized at the v3.8.47 release.
📝 Maintenance
- 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).
[3.8.46] — 2026-07-07
✨ 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. -
fix(security): proxy-pool
randomrotation now selects viacrypto.randomIntinstead ofMath.random— silences the post-release CodeQLjs/insecure-randomnessalerts (#698/#699) that flaggedMath.randomflowing into the selected proxy's credentials. Load-balancing selection is not a secret, but the crypto source is unbiased and clears the alert at the origin (#6365 follow-up).
📝 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 |
[3.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 |
[3.8.44] — TBD
✨ 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(providers): refresh The Old LLM (Free) model catalog (#5181) — seed the current free
/api/chatgpttier (GPT-5/5.1/5.2/5.3/5.4, o3/o4-mini, Gemini 3 Pro / 2.5 Pro / 2.0 Flash / 1.5 Flash, Claude 4.6 Opus/Sonnet & 4.5 Haiku, GPT-4o, Grok 4, DeepSeek V3/R1, Sonar Pro) while keeping the legacy alias IDs for saved-preference compatibility. Also fixes a latent routing bug:mapModel()now passes known upstream IDs through unchanged, so Gemini/o-series/Grok/DeepSeek/Sonar models no longer silently collapse ontoGPT_5_4. Regression guard:tests/unit/theoldllm-model-refresh-5181.test.ts. (thanks @WslzGmzs) - 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. (commit39349da18— 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 commit926b08aa8)
🙌 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 |
[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 |
[3.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)
[3.8.41] — 2026-06-29
✨ New Features
- feat(relay): selectable relay backend (TS / Bifrost /
auto) — the OpenAI-compatible relay endpoint can now route its hot path through a native Bifrost sidecar without clients changing URLs.OMNIROUTE_RELAY_BACKEND/RELAY_ROUTING_BACKEND=ts | bifrost | auto: defaults to the existing TypeScript relay;autoselects Bifrost whenBIFROST_BASE_URLis set (andBIFROST_ENABLED≠0) and falls back to TS automatically if the sidecar is unreachable;bifrostkeeps strict failure behavior. Auth, per-IP/token rate limits, prompt-injection checks, and model allowlists still run in the Next relay route before dispatch (control plane stays in the app); responses carryX-Routing-Backend/X-Routing-Fallback. Regression guards:tests/unit/api/v1/relay-routing-backend.test.ts,tests/unit/api/v1/bifrost-sidecar.test.ts. (#5315, #5316 — thanks @KooshaPari)
🔧 Bug Fixes
- translator (claude): synthesize a minimal
userturn when an OpenAI→Claude request carries onlysystem/developermessages, so the request stops failing with[400]: messages: at least one message is required.openaiToClaudeRequesthoists every system/developer turn into Claude's top-levelsystemfield and filters them out ofmessages; an all-system input (OpenCode compaction / title-generation requests) leftmessages: [], which the Messages API rejects — surfacing in OpenCode as a mid-taskstream errorthat drops the conversation. The guard fires only whenmessageswould otherwise be empty (system instructions still drive the response), so non-empty requests are unaffected. (#5342 — thanks @wild-feather) - providers (gemini): drop retired Google AI Studio model ids and align the catalog to what the live GenAI API actually serves (verified 2026-06-29 against the official deprecations page). Removes long-retired
gemini-1.5-pro/gemini-1.5-flash, the shut-downgemini-2.0-flash/gemini-2.0-flash-lite, and dead experimentals; renamesgemini-3.1-flash-lite-preview→ the GAgemini-3.1-flash-lite; swaps the retiredtext-embedding-004for the livegemini-embedding-001/gemini-embedding-2; and adds gracefulmodelDeprecationforwards so legacy/renamed ids redirect to the GA model instead of 404ing. Native AI-Studio-direct image/video/music registration is intentionally out of scope (needs real executor work; those models stay reachable via Antigravity/Vertex/aggregators). (#5337 — thanks @backryun) - services (dashboard): fix the embedded-services dashboard failures (#5298) — service supervisors are now lazily initialized from
/api/services/[name]/logssocliproxy/9routerlogs no longer 404 before bootstrap registers a supervisor; lifecycle buttons send JSON (empty install bodies default toversion: "latest", malformed JSON still returns400 Invalid JSON body); lifecycle and log-stream failures surface as actionable UI errors instead of silently showing no logs; Tailscale CGNAT100.64.0.0/10peers count as private-LAN local for local-only service access; a parent/dashboard/context→/dashboard/context/settingsredirect stops RSC prefetch 404s; and/api/v1/providers/{cliproxyapi,9router}/modelsreturn synced embedded-service models instead ofinvalid_provider. (#5299, #5298 — thanks @KooshaPari) - thinking (claude): fix three independent defects in Claude adaptive-thinking on the OpenAI-compatible path (Cursor → Claude OAuth). (A) the dashboard Thinking-Budget setting was dropped on every restart —
setThinkingBudgetConfigwas never called at boot, so a saved{mode:"adaptive"…}silently reverted to passthrough; it's now hydrated from settings inserver-init. (B) the Claude executor force-injected adaptive thinking after translation, ignoring the operator's budget — it now honorsmode:"auto"(strip) while keeping the default (passthrough) behavior byte-identical so native Claude Code is unaffected, and remaps an operatorthinking.type:"enabled"to theadaptiveshape Opus 4.7/4.8 require (enabled→ 400). (D) on replay, signature-lessreasoning_contentwas reconstructed as athinkingblock carrying a fabricated signature → Anthropic400 "Invalid signature in thinking block"; it now emits a signature-lessredacted_thinkingblock (real signatures are still preserved verbatim). Regression guards:tests/unit/thinking-budget-hydration-5312.test.ts,base-thinking-budget-config-5312.test.ts,openai-to-claude-redacted-replay-5312.test.ts(existing #5123/#4479/#2454 suites stay green). The</think>content-marker channel mismatch (RC-C, shared with #5245) is tracked as a follow-up pending a live Anthropic validation. (#5312 — thanks @vitalNohj) - opencode (proxy pool): the OpenCode Free per-account proxy modal now offers the global Proxy Pool dropdown (by-id reference) instead of forcing manual Host/Port/credentials on every account — Gap 1 of #5217. A Saved / Custom toggle: "Saved" picks a pre-saved proxy from
GET /api/settings/proxiesand stores{fingerprint, proxyId}, so updating that pool proxy applies to every account using it; "Custom" keeps the manual inputs (stored inline) as an escape hatch. Resolution happens server-side (resolveAccountProxiesFromRegistry) so the executor still receives a resolved proxy unchanged; existing inline entries keep working and an unknown/deletedproxyIddegrades safely to direct. Regression guards:tests/unit/noauth-proxy-resolution.test.ts,tests/unit/ui/noauth-account-card.test.tsx. (#5217 Gap 1 — thanks @daniij) - thinking (claude): let reasoning_content-native clients (e.g. Cursor) opt out of the
</think>close-marker so it no longer leaks an orphan</think>into visiblecontent(RC-C of #5312, shared with #5245). The marker-suppression machinery already existed (UA allowlist, #5348) but Cursor's UA was deliberately excluded; this adds an explicit request headerx-omniroute-thinking-marker: off(alsoon/keepto force-keep) that overrides the UA policy. With the header absent the behavior is byte-identical — Claude Code/Cursor-composer clients that scancontentfor the marker (#4633) still receive it. Regression guard:tests/unit/think-close-marker-suppress-5245.test.ts(#5123 case-b + #4479 stay green). (#5312, #5245 — thanks @vitalNohj, @wild-feather) - cors: browser/Electron clients (e.g. Wayland AI) can now use OmniRoute as an OpenAI-compatible provider out-of-the-box. The token-authenticated API surface (
/v1/*,/v1beta/*) now returns a permissiveAccess-Control-Allow-Origin(echoes the requestOrigin,*when absent) by default — matching 9router and the OpenAI-compatible ecosystem — so a rendererfetchcan read the response instead of failing CORS-blocked as "site not found" / empty catalog (whilecurl, which sends no preflight, worked). This is safe: those routes auth viaAuthorization/x-api-keyheaders browsers never auto-attach (no credentialed-session/CSRF exposure), andAccess-Control-Allow-Credentialsis never paired with the echo/wildcard. Cookie-authed MANAGEMENT/dashboard routes stay exactly fail-closed;CORS_ALLOW_ALL/CORS_ALLOWED_ORIGINSstill take precedence. Regression guards:tests/unit/cors/origins.test.ts,tests/unit/authz/pipeline.test.ts. (Bug 2 of #5242 — thanks @jonlwheat2-gif) - grok-web: forward the Cloudflare clearance cookies and stop mislabeling IP-reputation blocks as a bad cookie. "Check cookie" returned
Invalid SSO cookieeven with a valid, complete browser session — but the cookie parser was never the problem (it robustly extractssso/sso-rwfrom a full DevTools header). Two real gaps fixed: (1)buildGrokCookieHeadernow forwardscf_clearanceand__cf_bmwhen pasted (it dropped them before; AIClient2API forwards them too) — strictly additive, a baressoblob still yields exactlysso=…; (2) when the user supplied acf_clearance, a 401 / invalid-credentials-403 from grok.com is now surfaced as an IP-reputation/anti-bot block (cf_clearance is IP+TLS+UA-pinned and can't be replayed from a different machine) instead of the misleading "Invalid SSO cookie — re-paste". A bare cookie with no clearance still gets the re-paste hint. Regression guards inweb-cookie-auth.test.ts+provider-validation-specialty.test.ts. (#5350 — thanks @SeaXen) - cli (serve): opt-in native HTTPS/TLS for
omniroute serve— so strict-CSP Electron apps and browsers can reach OmniRoute overhttps://instead of plainhttp://localhost. Provide--tls-cert <path> --tls-key <path>(orOMNIROUTE_TLS_CERT/OMNIROUTE_TLS_KEY) and the standalone server terminates TLS on the same listener (no extra port/proxy); WebSocket upgrade (live dashboard +/v1streaming) works overwss://unchanged sincehttps.Server extends http.Server. With no TLS flags the HTTP path is byte-identical to before; only one of cert/key, or an unreadable path, logs a warning and stays HTTP (never half-enables, never crashes). Auto-generated self-signed certs for localhost are a follow-up; for now provide an explicit cert/key (or front OmniRoute with a TLS terminator). Regression guard:tests/unit/tls-options.test.ts. (Bug 1C of #5242 — thanks @jonlwheat2-gif) - opencode/observability: make OpenCode Free account/proxy rotation visible and fix two real defects surfaced alongside it. (1) the per-request rotation selection log (
dispatch via account … through proxy …) wasdebug(hidden at defaultAPP_LOG_LEVEL=info) — promoted toinfoso the shuffle/cooldown lifecycle is auditable (token stays masked). (2)[ProxyEgress]reportedproxy=directeven when an account proxy was applied, because the egress logger ran outside the executor's nested proxy context — the effective applied proxy is now captured (via an applied-proxy sink threaded through the proxy AsyncLocalStorage) and reflected in the egress log. (3)[callLogs] too many SQL variables—deleteCallLogRowsByIdsdeleted up to 5000 ids in oneIN (…), exceeding SQLite's ~999 bound-param cap and aborting log trimming/retention; ids are now chunked (≤500 per statement). Regression guards:tests/unit/call-log-trim-sql-vars-5217.test.ts,apply-executor-proxy-info-5217.test.ts, extendedopencode-proxy-rotation-4954.test.ts. The Proxy Pool dropdown (by-id) UI (Gap 1) is a follow-up requiring browser validation. (#5217 — thanks @daniij) - chatgpt-web: wire tool/function calling into the
chatgpt-webprovider. It was the only web-session executor that never readbody.tools— both response builders hardcodedfinish_reason:"stop"and emitted only content, so tool calls were silently dropped (the model answered in prose). It now uses the sharedwebToolsprompt-emulation shim (a<tool>-contract system message +<tool>{…}</tool>response parsing) exactly like its 9 sibling executors (qwen-web, perplexity-web, …) — it was simply omitted from the #3259 rollout. Tool mode buffers and emitstool_calls+finish_reason:"tool_calls"(gated off the image-gen path); plain chat is unchanged. Regression guard:tests/unit/chatgpt-web-tools-5240.test.ts. (#5240 — thanks @Rougler) - oauth/dashboard: fix the persistent/false Antigravity "Token Expired" badge (continuation of #3679/#3850). Two causes: (1) new OAuth connections never set
tokenExpiresAt(onlyexpiresAt), so the dashboard badge — which preferstokenExpiresAt || expiresAt— fell back to the original grant clock and could flash a false "Token Expired" until the first background refresh. Creation now mirrorsexpiresAtintotokenExpiresAtacross all 5 OAuth create paths (a sharedbuildOAuthConnectionCreatePayload), consistent with every refresh path which already writes both. (2) when a refresh-capable connection has no usable refresh token, the health-check sweep silently skipped it, leavingtestStatus="active"forever while the cosmetic badge showed expired; it now surfaces a terminaltestStatus="expired"("needs re-auth"), tightly gated so it never clobbers non-refresh providers or already-terminal/cooldown states. Regression guards:tests/unit/oauth-connection-tokenexpiresat-5326.test.ts,tests/unit/token-health-no-refresh-token-expired-5326.test.ts. (#5326) - routing: auto-disable a depleted API key on upstream
402 "Insufficient account balance"for API Key Round-Robin connections (multiple keys in one connection'sextraApiKeys). The per-connection path already terminalized 402 (→credits_exhausted), but the per-KEY health tracker (recordKeyHealthStatus) only recorded failures for401, so a 402-depleted key stayed in rotation and kept getting retried. Now a 402 marks the current key invalid immediately (terminal — balance won't recover mid-session) via a newrecordKeyTerminal, so the rotator skips it and falls over to the next healthy key; the state persists across restarts. Also addedinsufficient balance/insufficient_balance/insufficient account balanceto the credits-exhausted body signals so non-402 out-of-credit responses terminalize too. Regression guard:tests/unit/key-health-402-disable-5239.test.ts. (#5239 — thanks @muflifadla38) - cli:
omniroute serveno longer discards a user-setNODE_OPTIONS=--max-old-space-size=…. It used to unconditionally overwriteNODE_OPTIONS(and pass an explicit--max-old-space-sizeCLI arg) with the calibrated default, so a user who exported--max-old-space-size=8192still ran at the old cap and OOM'd (#5238 reporter set 8192, crashed at ~505MB). Now it mirrors the Electron and standalone launchers: ifNODE_OPTIONSalready pins the heap, that value wins (and the duplicate CLI arg is suppressed); otherwise the calibrated--max-old-space-sizeis appended, preserving unrelated flags. Regression guard:tests/unit/serve-node-options-preserve-5238.test.ts. (Defect C of #5238; theb.mask/OOM-root parts are tracked separately.) - dashboard: restore the
{active}/{total} activemodel-count badge in a provider's Available Models toolbar (provider detail page). It was dropped during the v3.8.13 god-file decomposition (#3327) — theModelVisibilityToolbarstill receivedactiveCount/totalCountbut they were orphaned as unused_-prefixed params and the rendering<span>was never carried over (themodelsActiveCounti18n key stayed). Re-wired the existing props to the existing key; zero data-layer or i18n change. Regression guard:modelVisibilityToolbarActiveCount.test.tsx. (#5264) - rerank:
/v1/rerankno longer rejects SiliconFlow and DeepInfra Qwen3-Reranker models with400 "Invalid rerank model"even though/v1/modelslists them. The model-ID parser was never the problem (it already splits on the first slash, sosiliconflow/Qwen/Qwen3-Reranker-8Bparses correctly) —siliconflowanddeepinfrawere just missing from the rerank provider registry. Added both: SiliconFlow as Cohere-compatible, DeepInfra via a newdeepinfraadapter (model in the URL pathPOST /v1/inference/<model>,{queries,documents}request, positional{scores}response mapped to Cohereresults[]). Regression guard:tests/unit/rerank-providers-5332.test.ts. (#5332 — thanks @maikokan) - authz/dashboard: stop rejecting every dashboard mutation with
403 INVALID_ORIGINwhen the dashboard is reached over a LAN IP / non-localhost host. The origin-pinning check (#5278) only accepted the configured*_PUBLIC_BASE_URL(typicallyhttp://localhost:20128) plus the internalrequest.urlorigin — which Next.js standalone reports as the bind host, not the realHost. So opening the dashboard at e.g.http://192.168.0.15:20128made the browser's same-originOriginmatch no candidate, and every POST/PUT/DELETE (save API key, save provider, test connection) failed while GETs still worked. Two fixes: (a) the requestHost(or a trustedX-Forwarded-Host) is now accepted as a valid mutation origin, gated by two independent checks — the token-stamped socket peer must be loopback/private-LAN and the Host itself must be a loopback/private-LAN IP literal, so a DNS-rebinding domain (which classifies asremote) can never become a trusted origin and the protocol is pinned to the actual connection; (b) theINVALID_ORIGINresponse now carries an actionable message (setOMNIROUTE_PUBLIC_BASE_URL) and the dashboard surfaces API error.messagevia a sharedextractApiErrorMessagehelper instead of rendering the raw error object. Regression guards:tests/unit/authz/public-origin.test.ts(direct LAN/loopback + DNS-rebinding defense),tests/unit/api-error-message-5340.test.ts. (#5340)
📝 Maintenance
- chore(dead-code): repo-wide sweep of unused exported symbols and a matching dead-code baseline ratchet — trimmed unused exported helpers, validation/settings/encryption-config schemas, utility/domain/static-constant/formatting helpers, runtime test helpers, the request-timeout fetch wrapper, event-bus, semantic-cache (maintenance + expiry), correlation-middleware, MCP-scope, service-registry, build-profile, api-key-format, authz-class, models.dev-context, embedding-cache, provider-limits-scheduler, search-validator, webhook-example, agent-skills-repo-URL and command-code-auth-cleanup exports. Pure dead-code removal validated by
typecheck:core(no remaining referencing site) — no behavior change. (#5321, #5322, #5324, #5325, #5328, #5329, #5330, #5331, #5333, #5334, #5335, #5336, #5338, #5339, #5353, #5354, #5355, #5356, #5357, #5359, #5362, #5364, #5365, #5366, #5368, #5369, #5371 — thanks @JxnLexn)
[3.8.40] — TBD
In development — bullets added per PR; finalized at release.
[3.8.39] — 2026-06-28
✨ New Features
- feat(oauth): remote Antigravity login via local helper + paste-credentials — Antigravity (and other Google "native/desktop" OAuth providers) use Google's
firstparty/nativeappconsent, which only releases the auth code when the loopback redirect (127.0.0.1:<port>) is reachable from the approving browser. On a remote VPS install that loopback lives on the server, so the consent hangs forever and never emits a code — the "paste the callback URL" fallback has nothing to paste (a Google-side constraint, identical in upstream 9router). A newomniroute login antigravityCLI helper runs the OAuth on the user's own machine (where 127.0.0.1 works), exchanges the code, and prints a single-lineomniroute-cred-v1.…credential blob; the dashboard's Antigravity Connect → Step 2 field now accepts that blob (alongside callback URLs) and persists the connection via a newpaste-credentialsaction (server-side onboarding, provider-allowlisted, with the blob's embedded provider required to match the route). The SSH local-forward tunnel is documented as a zero-tooling alternative. Seedocs/guides/REMOTE-MODE.md. (#5203) - feat(agent-bridge): graceful cert-install fallback for containers / headless — when the MITM root CA can't be installed into the system trust store automatically (Docker / headless / no sudo / read-only trust store), the Agent Bridge no longer hard-fails on start with a generic "Certificate install failed". It now starts in skip mode and the dashboard surfaces a platform-specific manual-install guide (plus a CA download link) so the operator can trust the certificate by hand. The trust-cert endpoints return a structured
{ skippable, manualGuide }response (HTTP 200) for environment failures instead of a 500; an explicit user cancellation is still reported distinctly. (#4546 — thanks @phuchptty) - feat(compression): CCR ranged/grep/stats retrieval (ReDoS-safe, backward-compat) — extends the
omniroute_ccr_retrieveMCP tool and/api/compression/retrieveendpoint with optionalrange(byte/line slice),grep(ReDoS-safe literal or bounded-pattern match against stored lines), andstats(byte/line/word counts) parameters so agents pull exactly the slice or summary they need instead of re-expanding the entire stored block. All parameters are optional — no parameters returns the full block byte-identical to the existing behavior; the CCR store written by ionizer/fuzzy/headroom is fully compatible. Sixth item of the compression roadmap. (#5187) - feat(compression): TOON best-of-N candidate encoder + encoder A/B table — adds
@toon-format/toonas a candidate encoder in the headroom compression engine via a best-of-N scheme: both GCF and TOON run per prompt and the shorter result is kept, rather than hard-swapping encoders (GCF already encodes the headroom block and TOON is not a lossless universal win). An encoder A/B comparison table (GCF vs TOON vs JSON — bytes and cl100k tokens) is now surfaced in the compression studio. Fifth item of the compression feature-extraction roadmap (bench: #5080, gate: #5127, fuzzy/gate: #5143, ionizer: #5148). (#5163)
🔧 Bug Fixes
-
fix(oauth): Antigravity refresh no longer nulls the stored refresh_token on an empty upstream response — Google's OAuth token endpoint uses non-rotating refresh tokens: a refresh response normally OMITS
refresh_tokenand occasionally returns it as an empty string. The Antigravity executor'srefreshCredentialsusedtypeof tokens.refresh_token === "string" ? tokens.refresh_token : credentials.refreshToken, and becausetypeof "" === "string"is true, an empty-string response overwrote the good token with""— nulling it on first refresh. The check now treats a non-string or empty value as absent and preserves the stored token, matching the canonicalrefreshGoogleToken(tokens.refresh_token || refreshToken) semantics. (#3850 — thanks @3xa228148) -
fix(api): LAN/Tailscale dashboard access —
ws:CSP scheme, GET-exempt version route, surface combo field errors — three failures when opening the dashboard from a non-loopback host: (1) CSPconnect-srcallowed thews:scheme only for loopback origins, blocking the dashboard'sws://<lan-host>:*Live WebSocket from LAN/Tailscale clients; the barews:scheme is now permitted (symmetric with the barewss:already allowed), kept declarative innext.config.mjswith no global middleware (the project has none by design); (2)GET /api/system/versionwas blocked byLOCAL_ONLY_API_PREFIXESfor all methods despite onlyPOSTspawning child processes (git/npm/pm2) — a newLOCAL_ONLY_API_GET_EXEMPTIONSset exempts safe read methods for this path while keepingPOST/PUT/PATCH/DELETEstrictly loopback-only; (3)COMBO_002validation errors only surfaced the generic message —firstField/firstMessageare now extracted from the first Zod issue and included in the response body. (#5083 — thanks @KooshaPari for the diagnosis and original PR #5084) -
fix(sse): defer
</think>close so it never leaks beforetool_callsin Claude→OpenAI streaming — when a Claude thinking block was followed by a tool_use block, the translator unconditionally emitted acontent: "</think>"chunk atcontent_block_stop, injecting a spurious assistant text chunk immediately before thetool_callsdelta and corrupting OpenAI-compatible clients (e.g. Kimi Coding). The close marker is now deferred: it is flushed at the firsttext_deltathat follows the thinking block (preserving the #4633 / decolua/9router#454 behavior for Claude Code / Cursor) or at stream finish when no tool_calls were collected. Tool-use streams never get atext_deltaafter the thinking block, so</think>is never emitted into content beforetool_calls. (#5123) -
fix(sse): normalize array user-message content in the Command Code executor to prevent upstream 400 — when a client sends a user turn whose
contentis an array of content parts (e.g.[{type:"text",text:"…"}, …]), the raw array was forwarded verbatim to the Command Code upstream, which requiresmessages[N].contentfor theuserrole to be a plain string — resulting inexpected string, received array/ HTTP 400 on DeepSeek V4-Pro and other Command Code models. The user branch ofconvertMessagesnow callsnormalizeContentText()(already used by system, assistant, and tool branches) so multi-part user content is joined to a string before dispatch. Partially addresses (#5166); the 0-output-token symptom on reasoning-only models is tracked separately. -
fix(mcp): return HTTP 404 (not 400) for an unknown/expired Streamable HTTP session id — when an MCP session is terminated or idles out and the client reuses the stale
Mcp-Session-Idheader, the Streamable HTTP transport replied with HTTP 400. The MCP spec (2025-03-26 and 2025-11-25, Session Management) mandates HTTP 404 Not Found in that case, and spec-compliant clients only re-initialize a session on 404 — so the 400 was non-recoverable. The handler now returns 404 for a present-but-unknown session id, while a missing session id on a non-initialize request correctly stays 400. (#5169 — thanks @czer323) -
fix(api): blocking "Auto (Zero-Config)" in Security settings now removes
auto/*from/v1/models— the built-inauto/*combo advertiser (#4164 / #4235) at the top of the models catalog ignoredsettings.blockedProviders, so checking Auto (Zero-Config) under Security → Blocked Providers had no effect and the model picker kept listing everyauto/*entry. The injection loop now skips the entireauto/*block when the system providerauto(its id and alias are bothauto) is blocked, consistent with how every other provider is filtered from the catalog. (#5192 — thanks @WslzGmzs) -
fix(cli): auto-calibrate the server V8 heap from physical RAM instead of a fixed 512MB default — the server was spawned with a hard-coded
--max-old-space-size=512(omniroute serve) or with no heap flag at all (Electron desktop, which then inherited the runtime's low ~512MB default), so RAM-rich machines still OOM-crashed under load (FATAL ERROR: Ineffective mark-compacts near heap limit … ~500MBat code=134) with many providers/accounts and large model catalogs (one report: 16GB RAM, 65 providers, ~100 accounts, ~2600 models). A newcalibrateHeapFallbackMb(os.totalmem())helper derives the default heap as ~35% of physical RAM, clamped to[512, 4096], and is wired into bothbin/cli/commands/serve.mjsandelectron/main.js. An explicitOMNIROUTE_MEMORY_MB(or a pre-set--max-old-space-size) still wins, so the #2939 override contract is unchanged. (#5172, #5160, #5152 — thanks @manchairwang, @Xyzjesus) -
fix(oauth): Antigravity login no longer hangs — fire-and-forget onboarding + bounded post-exchange — the dashboard's Antigravity OAuth login spun indefinitely because
postExchangeawaited theonboardUserretry loop inline (up to 10 × 5 s per attempt, each fetch with no timeout), blocking the/exchangeresponse forever. Matching the upstream 9router web flow:onboardUsernow runs fire-and-forget in a background task; the/exchangeendpoint is bounded by a 10 s hard timeout so it always returns; a progress endpoint lets the dashboard poll onboarding completion state. (#5193) -
fix(antigravity): retry Antigravity accounts by quota family before escalating the combo — when one Antigravity account returns a quota or rate-limit
429for a Gemini model (e.g.gemini-3.5-flash-medium), combo orchestration could prematurely advance to the next combo model instead of trying other eligible Antigravity accounts for the same quota family. Antigravity quota-family awareness is now added to the fallback path so a429on one account triggers a bounded same-model retry across other Antigravity accounts sharing that quota bucket before the combo degrades to a lower-tier model. (#5180 — thanks @Ardem2025) -
fix(translator): accept Claude Messages shape in the non-stream malformed-200 guard — when a Claude client (e.g. Claude Code) is routed to a non-Claude provider, the translated non-streaming response body is in Claude Messages shape (
type: "message", content[]) produced byconvertOpenAINonStreamingToClaude.detectMalformedNonStreamonly recognized OpenAIchoices[].messageand Responses APIoutput[], so this shape fell through toempty_choices→ 502. The guard now recognizes the Claude Messages shape: text, tool_use, and thinking blocks carrying asignaturecount as valid output, while a genuinely emptycontent: []is still flagged. (#5156 — thanks @NomenAK) -
fix(sse): resolve nameless deepseek-web
<tool>blocks via parameter-schema match — whenchat.deepseek.comemits a<tool>block with no<name>child, no JSON bodyname/typekey, and no tag suffix, every name-resolution path inextractCallreturnednulland the raw XML leaked to the client as plain text. A conservative schema-based fallback now compares the block's extracted parameter names against each declared tool's schema keys; if exactly one tool matches, its name is used. Zero or ambiguous (>1) matches still returnnullso no calls are misattributed. (#5154, #5173) -
fix(stream): normalize provider safety finish reasons to
content_filter— Gemini and Antigravity can return safety/prohibited terminal reasons (SAFETY,RECITATION,BLOCKLIST,PROHIBITED_CONTENT) that OpenAI-compatible downstream clients do not recognize. A shared finish-reason normalization helper now maps these to the standardcontent_filtervalue, applied in both the streaming and JSON collection paths for both providers. (#5197 — thanks @rdself) -
fix(responses): normalize non-array Responses API
inputbefore routing — the OpenAI Responses API acceptsinputas a string, object, or list, but OmniRoute only handled list-shaped payloads; a string or objectinputwas silently dropped on the Responses→Chat Completions path. The translator now normalizesinputto a list before dispatch; the Codex-native Responses path also normalizes before forwarding (preventing upstream400 Input must be a list); and the prompt-injection and PII sanitizer extraction paths are guarded against object-valuedinputso security checks do not throw. (#5204 — thanks @wilsonicdev) -
fix(zenmux): normalize vendor-prefixed GLM system roles for Z.AI models — ZenMux exposes Z.AI GLM via vendor-prefixed OpenAI-compatible IDs such as
z-ai/glm-5.2. The existing GLM detection only matched bareglm-*/glmids, sozenmux/z-ai/glm-5.2kept system messages in place; Z.AI rejects compressed histories ending with a system turn beforeassistant(tool_calls) → toolsequences. The fix extends GLM detection to coverz-ai/glm-*prefixes and routes them through the existingnormalizeSystemRolepath. (#5158 — thanks @Thinkscape) -
fix(xai): add OAuth connection test probe + normalize xAI reasoning effort aliases — xAI rejects unsupported reasoning effort values (
max,xhigh) with HTTP 400 after a provider update; the xAI translator now mapsmaxandxhightohighbefore forwarding. Additionally, xAI OAuth connections had no dashboard test configuration, so provider tests returned"Provider test not supported"; a dedicated OAuth test probe is now wired for xAI accounts with regression coverage for the effort normalization. (#5157 — thanks @nguyenxvotanminh3) -
fix(serve): honour
HOSTNAMEfrom.envinstead of hardcoding0.0.0.0—bin/cli/commands/serve.mjsspreadprocess.envinto the child-process environment but immediately overwroteHOSTNAMEwith a literal"0.0.0.0", silently discarding any user-configured bind address even thoughHOSTNAMEis documented in.env.exampleanddocs/reference/ENVIRONMENT.md.dist/server.jsalready readprocess.env.HOSTNAMEcorrectly; only the CLI wrapper was overriding it. The fix appliesprocess.env.HOSTNAME || "0.0.0.0"so the env value takes effect. (#5134, #5170 — thanks @anki1kr / @Angelo90810) -
fix(cli): force
NODE_ENVto match dev/start run mode in the custom Next server — when.env.exampleshipsNODE_ENV=production, startingnpm run devviascripts/dev/run-next.mjsforwarded that value to the programmaticnext()entry, which — unlike thenextCLI — does not normalize it to match the run mode. The resulting production flag caused PostCSS to skip Tailwind's CSS transform, surfacing asModule parse failed: Unexpected character '@'onglobals.css. The custom server now explicitly forcesNODE_ENV=developmentfor thedevpath andNODE_ENV=productionfor thestartpath regardless of.env. (#5189 — thanks @backryun) -
fix(cli): raise dev server Node heap limit to 8 GB to prevent OOM —
npm run devcrashed withFATAL ERROR: Ineffective mark-compacts near heap limit — Allocation failed - JavaScript heap out of memorywhile compiling heavy dashboard routes becausenode scripts/dev/run-next.mjsran on V8's ~4 GB default with no--max-old-space-sizeflag. Thedevnpm script now passes--max-old-space-size=8192at invocation time (the only point where this flag can be set for that process). (#5198 — thanks @backryun) -
fix(cli): re-enable Turbopack as the default
npm run devbundler — PR #4092 forced webpack because an earlier Turbopack 16.2.x panic (internal error: entered unreachable code: there must be a path to a rootinturbopack-core/module_graph) blocked the OmniRoute module graph. That panic no longer reproduces on the pinned Next 16.2.9, soOMNIROUTE_USE_TURBOPACKis flipped from0to1in.env.example, aligning it withdocs/reference/ENVIRONMENT.mdwhich had already documented the default as1. (#5206 — thanks @backryun) -
fix(auth): allow synthetic no-auth fallback for mimocode — mimocode connections without explicit credentials were blocked before reaching the executor. The auth layer now permits a synthetic no-auth fallback for the mimocode provider so credential-free access patterns work as intended. (#5205 — thanks @KooshaPari)
-
fix(combo): reject empty Responses API
output: []as a fail-over trigger — a non-streaming Responses API body withobject: "response"andoutput: []was accepted as a valid HTTP 200 by the combo response-quality validator, allowing a combo target to stop rather than fail over to the next leg. The non-stream validator now inspects Responses-API-shaped bodies before the genericoutputshortcut and rejects an emptyoutput: []asempty_choices; structural non-empty output (e.g.function_call) remains valid. (#5207 — thanks @KooshaPari) -
fix(proxy): close cached dispatchers when clearing the proxy cache — cached proxy and direct-retry dispatchers were not closed on cache clear, leaking open connection handles. The cache-clear path now calls
close()on all evicted dispatchers; dispatcher cache and lifecycle helpers have been extracted from the oversized proxy-dispatcher module into a dedicated helper for reuse. (#5202 — thanks @KooshaPari) -
fix(proxy): coalesce concurrent fast-fail health probes per proxy URL — under high concurrency each simultaneous request opened its own TCP health probe for the same proxy URL, creating a thundering-herd burst. Concurrent proxy fast-fail checks are now coalesced so only one TCP probe runs per proxy URL at a time; the completed-result health cache is preserved so subsequent same-URL checks return immediately. (#5109, #5208 — thanks @KooshaPari)
-
fix(pwa): prefer cached navigation before showing the offline page — the service worker was too eager to display
/offlineon transient navigation failures. It now caches successful navigation responses and consults the cached route or app shell before falling back to/offline;/offlineremains the final fallback when no cached navigation or app shell exists. (#5165, #5209 — thanks @KooshaPari) -
fix(request-logger): never render a negative percentage in the compression badge — when every prompt token was compressed (
totalIn = 0, compressed > 0), the compression pill displayed(-100%)because the badge format hard-coded a leading-before the percentage value. The badge now omits the negative sign in this case, correctly representing the saving as a positive ratio. (#5201 — thanks @KooshaPari) -
fix(dashboard): use amber for home update-step warning icon — the warning-state icon in the home update steps (
HomePageClient.tsx) usedtext-yellow-500(Tailwind#eab308), which has poor contrast on light backgrounds (~1.9:1, below WCAG AA) and is inconsistent with theamberwarning convention used by every sibling element in the same component. Switched totext-amber-500— a one-lineclassNamechange with no behavior change. (#5176)
📝 Maintenance
- test(combo): deterministic context-relay universal-handoff coverage — covers the universal (provider-agnostic) session-handoff path in
context-relay(combo.ts:2099–2139), which previously had only a definition-order assertion and aTODO(phase-2). The test drives the real pipeline via session seams (x-session-id→relayOptions.sessionId→maybeGenerateUniversalHandoff) without live infrastructure. (#5168) - test(combo): end-to-end quota-share DRR routing-decision coverage (matrix parity) — adds the missing E2E test for the
quota-sharestrategy, driving the realhandleChat→ chatCore →selectQuotaShareTarget→ executor pipeline via in-process seams and asserting which connection is dispatched. The DRR selector already had 29 unit tests; this closes the E2E gap and brings quota-share to parity with the 17-strategy public matrix. (#5179) - test(combo): deterministic context-relay codex quota-handoff coverage (closes last gap) — covers the codex-specific handoff block of
context-relay(combo.ts:2143–2183), which #5168 left documented-but-untested because it requires acodexconnection. All seams (fetchCodexQuota, handoff generation, session relay) are mocked deterministically without live infra. (#5195) - test(ci): wire antigravity-quota-family test under
test:vitest(fix test-discovery orphan) —open-sse/services/__tests__/antigravity-quota-family.test.ts(introduced by #5180) was not collected by any active runner, causingcheck:test-discoveryto report a new orphan and gate every subsequent PR on the release branch. The file is now added tovitest.mcp.config.tsincludeand the corresponding orphan-allowlist entry is removed. (#5196) - test(security): regression guard — PII redaction stays opt-in (default off) + Hard Rule #20 — adds a test asserting both
PII_REDACTION_ENABLEDandPII_RESPONSE_SANITIZATIONfeature-flagdefaultValuefields are"false"and that data passes through all three application points (piiMasker,piiSanitizer,streamingPiiTransform) untouched when both flags are off, encoding Hard Rule #20 as a CI-enforced contract and fixing a misleading doc implication that PII masking was on by default. (#5159) - docs(i18n): add Traditional Chinese (zh-TW) README + update zh-CN — adds a new Traditional Chinese translation (
docs/i18n/zh-TW/README.md) and updates the Simplified Chinese README to the current English baseline; the language index (docs/i18n/README.md) and rootREADME.mdbadge row are updated accordingly. (#5162 — thanks @lunkerchen) - docs(i18n): full sync of zh-TW and zh-CN README to canonical English v3.8.39 — brings both translations to full parity, adding the complete What's New section, compression real-token examples, and all sections updated in the v3.8.38/39 English README. (#5171 — thanks @lunkerchen)
- docs(combo): sync combo/routing-strategy docs to current state + document test coverage — removes a stale ordinal from the Fusion bullet in
README.md; adds a new Testing & Coverage section todocs/routing/AUTO-COMBO.mddocumenting the deterministic strategy matrix (npm run test:combo:matrix), quota-share DRR E2E coverage, and context-relay handoff tests delivered across the v3.8.39 cycle. (#5185) - fix(docker): copy the
open-sseworkspace manifest beforenpm ciso workspace-only deps install — the Dockerfile copied only the rootpackage*.json, sonpm ciskippedsafe-regexand@toon-format/toon(declared inopen-sse/package.json, not hoisted to root), breaking the multi-arch image build withModule not foundduringnpm run build. (thanks @diegosouzapw)
[3.8.38] — 2026-06-27
✨ New Features
- feat(sidebar): colored menu icons — sidebar menu icons now render with a per-item accent color: curated colors for known items (
SIDEBAR_ICON_ACCENTS) plus a deterministic hash-based fallback (getSidebarIconAccent) so every item gets a stable, distinct color across sessions. (#3812 — thanks @rafacpti23) - feat(providers): add Factory (factory.ai) as a subscription gateway provider —
factory(Factory Droids' hosted gateway) is now a first-class routing provider on the OpenAI-compatiblehttps://api.factory.ai/v1endpoint with Bearer apikey auth; the key is supplied from the Dashboard connection (not env). (#5065 — thanks @KooshaPari) - feat(providers): add Grok Build (xAI) provider with OAuth import-token flow —
grok-cli(aliasgc) routes through Grok's CLI chat proxy; users paste their~/.grok/auth.json(or the JWT), with automaticrefresh_tokenrotation. The public xAI client_id is embedded viaresolvePublicCred("grok_id")(Hard Rule #11), never a literal. (#5020 — thanks @fulorgnas) - feat(dashboard): click-to-edit model alias in the provider page — click an alias to edit it inline (Enter/blur saves, Escape cancels), instead of only being able to delete and re-add it. (#5119 — thanks @waguriagentic)
- feat(providers): add ZenMux Free (session-cookie free-tier) provider —
zenmux-free(aliaszmf) with a dedicated executor translating ZenMux's Anthropic-style SSE to OpenAI format; ships 12 free-tier models (DeepSeek V3.2, GLM 4.7 Flash Free, etc.). (#5105 — thanks @mrnasil) - feat(providers): allow local/private provider URLs by default (
Allow Local Provider URLsflag) — adding/validating an OpenAI-compatible provider on a loopback/LAN address (e.g.http://127.0.0.1:3264/api) was rejected by the SSRF guard with "Blocked private or local provider URL", even though OmniRoute is local-first. A newOMNIROUTE_ALLOW_LOCAL_PROVIDER_URLSfeature flag (default ON, toggle in Settings → Feature Flags) now scopes the provider-validation guard to allow local/private hosts while still blocking cloud-metadata endpoints (169.254.169.254, metadata.google.internal). Disable it to restore strict public-only blocking. Webhook/remote-image SSRF defaults are unchanged. (#5066, thanks @daniij) - feat(blackbox): refresh provider model catalog with latest models. (thanks @ptkelanatechsolutions)
- kiro: inline
<thinking>stream splitter — when<thinking_mode>enabled</thinking_mode>is present,assistantResponseEventcontent is now split into separatedelta.content/delta.reasoning_contentSSE chunks (newopen-sse/executors/kiroThinking.tsmodule wired intoKiroExecutor.transformEventStreamToSSE). - feat(cursor): parse Cursor Composer DeepSeek-style inline tool calls — Composer
cu/composer-2.5*models embed tool invocations in their visible text using<|tool▁calls▁begin|>…<|tool▁calls▁end|>markers instead of structured protobuf frames; a new streaming parser (composerToolCalls.ts) intercepts these in both streaming and non-streaming paths, suppresses the markers from the client-visible content, and emits proper OpenAItool_callsdeltas so downstream clients handle them natively. (thanks @noestelar) - feat(proxy): support auth-less
host:portbatch import and surface proxy-test failures. (thanks @dimaslanjaka) - feat(video): Alibaba DashScope video provider (
wan2.7-t2v) — adds thealibabavideo provider (DashScope async task → poll → MP4) wired through the standard apikey credential path, so text-to-video requests can route to Alibaba'swan2.7-t2vmodel. (thanks @josevictorferreira) - feat(cc): per-connection "summarized thinking display" toggle for Claude-Code-compatible providers — exposes a connection-level toggle that drives the existing Copilot summarized-thinking marker, so operators can opt a CC-compatible connection into summarized reasoning display from the UI (schema + request defaults + provider modals, with i18n). (thanks @rdself)
- feat(compression): compression playground in the studio (Play + Compare tabs) —
/dashboard/compression/studiogains a synthetic playground: paste text → per-engine lanes (each deterministic engine run alone via/api/compression/preview) plus a combined waterfall ordered bystackPriority, and a free A/B Compare grid with on-demand, USD-capped fidelity verdicts (/api/compression/compare+compare/verify). The preview route now uses the real cl100k tokenizer, returnsengineBreakdown, and accepts an orderedpipeline[]; newcompare/compare/verify/retrieveroutes; the live WS feed moved to/dashboard/compression/live. Management-only. (#5080) - feat(dashboard): expose Fusion
judgeModel+fusionTuningin the combo editor — the Fusion strategy editor now surfaces the judge model (synthesizes the panel answers; defaults to the first panel model) plus the quorum-grace tuning fields (minPanel,stragglerGraceMs,panelHardTimeoutMs) thatopen-sse/services/fusion.tsalready reads. Schema-validated + bounded; empty tuning is never persisted. (#5074) - feat(compression): opt-in per-step fidelity gate for the stacked pipeline — each compression step can now be guarded by a pure fidelity checker (4 invariants, fail-open) so a lossy engine that would degrade the prompt past a threshold is rejected and its lane skipped instead of silently shipping. Configurable via
fidelityGate(advanced thresholds intentionally API-omitted), with a per-lane rejection breakdown surfaced in the studio playground toggle. (#5143) - feat(compression): fuzzy near-duplicate dedup (session-dedup 2nd pass) — the session-dedup engine gains a second fuzzy pass that collapses near-duplicate (not just byte-identical) segments, with a playground toggle to compare on/off. (#5143)
- feat(quota): opt-in Codex/Claude auto-ping keepalive — an opt-in background keepalive can periodically ping Codex/Claude connections to keep their session/quota state warm, reducing cold-start failures on the first real request. (#5102)
- feat(ops): SRE playbooks + ops helper scripts — salvaged from a closed stale PR; adds operator runbooks and ops helper scripts. (#5138 — thanks @KooshaPari / @diegosouzapw)
- feat(mcp): web-session robustness — cookie dedup + browser-pool observability — the MCP web-session path now de-duplicates cookies when (re)hydrating a session (avoiding conflicting duplicate
Cookieheaders) and exposes browser-pool observability (pool size / in-use / acquisition metrics) for the headless web providers. (#5121, builds on #3368) - feat(compression): Ionizer engine — lossy JSON-array sampling reversible via CCR — a new compression engine that down-samples large JSON arrays to a representative subset and records a Compact Change Representation (CCR) so the omitted rows can be reconstructed, trading exactness for a large token reduction on tabular/array-heavy payloads. (#5148)
🔧 Bug Fixes
- fix(proxy): make the SOCKS5 handshake timeout operator-tunable (
SOCKS_HANDSHAKE_TIMEOUT_MS) — under high concurrency against a single residential gateway host, the SOCKS5 connect handshake could exceed the hardcoded 10s even though the proxy was reachable, surfacing as a false[Proxy Fast-Fail] Proxy unreachable(the pool size is already tunable viaOMNIROUTE_PROXY_DISPATCHER_CONNECTIONS). The handshake timeout now readsSOCKS_HANDSHAKE_TIMEOUT_MS(default unchanged at10000, capped at120000) so a concurrency-heavy deployment can raise it without a code change. Mitigation for #5109 (the full concurrency-100 collapse still needs the reporter's live load-test confirmation). (#5109) - fix(api): resolve
GET /v1/models/{id}case-insensitively — clients that normalise the model id (e.g. OpenCode requestingminimax/minimax-m3for the canonical catalog entryminimax/MiniMax-M3) missed the single-model lookup, which is case-sensitive, and fell back to advertisingcontext_length: 0.findModelByIdnow prefers an exact-case match and falls back to a case-insensitive match, so the real entry (and its context window) is returned regardless of casing. (#5082) - fix(services): embed WS proxy honours
LIVE_WS_HOST; reject emptymessagesearly — two headless/Docker deployment fixes (#5110). The embed WebSocket proxy (:20131) only readEMBED_WS_PROXY_HOST, so behind a reverse proxy/tunnel it stayed bound to127.0.0.1even withLIVE_WS_HOST=0.0.0.0set and the Live dashboard showed "WebSocket disconnected"; it now falls back toLIVE_WS_HOST(default still loopback). Separately, a request with an explicitly emptymessages: []array was forwarded upstream and bounced back as a confusing raw400/502;handleChatnow rejects it up front with a clearmessages: at least one message is required(Responses-APIinputrequests are unaffected). (#5110) - fix(proxy): repair one-click Deno & Cloudflare relay deployments — the
/api/settings/proxy/testendpoint only recognized thevercelrelay type, so testing a deployed Deno or Cloudflare relay returnedproxy.type must be http, https, or socks5and never reached the relay; it now routes all relay types throughisRelayType(). On installs withSTORAGE_ENCRYPTION_KEYthe relay-auth token is read viaextractRelayAuth(encryptedrelayAuthEncform), fixing the silent401that leftpublicIpnull. The Cloudflare Worker upload now sends the script part asapplication/javascript(the API rejectsapplication/javascript+module; ES-module semantics come frommain_module), and the proxy-registry schema accepts thedeno/cloudflaretypes +deno-relay/cloudflare-relaysources so editing a deployed relay no longer 400s. (#5128) - fix(kiro): retire
claude-sonnet-4.5from the Kiro catalog + pin the exact Kiro 400 error —claude-sonnet-4.5left the Kiro free-tier lineup (current active models: Opus 4.8/4.7/4.6, Sonnet 4.6, Haiku 4.5), so it is removed from the Kiro registry entry and the free-model catalog. A regression test now pins Kiro's verbatim[400] Invalid model. Please select a different model to continue.to theisModelUnavailableErrormodel-unavailable classification. A 400 on every model (including current ones) points to a server-side Kiro tier/region gate, not an OmniRoute catalog bug. (#5140, closes #4484) - fix(dashboard): preserve every rendered field when loading/saving Resilience settings —
ResilienceTabrenderscomboCooldownWaitandquotaShareConcurrencyLimit, but both the initial-load and save paths rewrote component state without those fields, so after a successful/api/resilienceresponse the cards receivedundefinedand the page fell back to the generic "failed to load" state. A sharedtoResilienceResponse()mapper now keeps all rendered fields, andPATCH /api/resiliencereturnsquotaShareConcurrencyLimitto match GET and the UI contract. (#5139 — thanks @rdself) - fix(quota): hydrate the in-memory quota cache from snapshots + scope auto-combo candidates — after a restart the quota cache was empty, so a known-exhausted connection looked healthy until re-queried;
isAccountQuotaExhaustednow lazily hydrates from persistedquota_snapshots. Auto-combo candidate expansion is also scoped to the connections each combo target actually allows, instead of pulling in every connection for the provider. (#5015 — thanks @JxnLexn) - fix(resilience): harden quota cutoff, Gemini audio MIME, and model-lockout cooldown — stored quota hard-cutoff values are no longer coerced to
enabled=truefrom arbitrary strings; Gemini audio input parts have their MIME type validated/normalized before forwarding; and model lockout now honours the configuredmaxCooldownMsceiling. (#5093 — thanks @KooshaPari) - fix(streaming): harden long OpenAI-compatible SSE streams — a late pipeline-wind-down error can no longer overwrite an already-recorded successful stream (
streamCompletionRecordedguard), client disconnects finalize as499 client_disconnectedinstead of poisoning provider/account failure state, JSON bodies that are actually SSE (wrongapplication/jsoncontent-type) are sniffed and re-streamed, and reasoning fields (reasoning/reasoning_content+ OpenRouter/Gemini encryptedreasoning_details) are preserved through the JSON-as-SSE fallback. (#5124 — thanks @rdself) - fix(usage): dedupe request-usage logging and debounce stats events —
saveRequestUsagenow guards against duplicate inserts (natural key: timestamp + provider + model + connection + api-key + token counts), back-fills a missingendpoint, and only emitsusageRecordedwhen a row was actually inserted; statsupdate/pendingevent bursts are collapsed into a single debounced notification to reduce churn. (#4940 — thanks @nguyenxvotanminh3) - fix(sse): convert the native Gemini request body to OpenAI format in the Antigravity MITM handler —
contents/systemInstruction/generationConfig/thinkingConfigare now translated to OpenAI chat-completions format before forwarding to/v1/chat/completions, so thinking-capable models (e.g.ag/claude-opus-4-6-thinking) no longer fail with provider-side 400 "invalid argument" errors. (#4845 — thanks @anuragg-saxenaa) - fix(db): translate the two pt-BR SQLite driver-fallback log lines to English —
[DB] Pré-inicializando sql.js WASM…and[DB] Drivers síncronos indisponíveis…were the only non-English server log strings, mixing languages in the logs. Now[DB] Pre-initializing sql.js WASM (synchronous drivers unavailable)…/[DB] Synchronous drivers unavailable — falling back to sql.js (WASM), guarded by a test that scans the driver path for accented log strings. (#5103) - fix(diagnostics): non-streaming Claude responses no longer false-502 as
empty_choices— the v3.8.37 malformed-200 detector (#4942) only understood OpenAIchoicesand Responses-APIoutputshapes, so a/v1/messagesresponse that stays in Claude shape ({type:"message", content:[…]}) fell through toempty_choices→ 502 (cascading to "All models failed" in a combo). Most visibly, an extended-thinking turn whose buffered body is a single empty thinking block with a validsignature(Claude Code's non-streaming Bash classifier) 502'd on every call.detectMalformedNonStreamnow understands the Claude shape: text/tool_use blocks and thinking blocks carrying a signature count as valid output, while a genuinely emptycontent:[]is still flagged. (#5108, thanks @insoln) - fix(combo): empty-content 502 now fails over within the same request instead of exhausting the provider — a leg that answers HTTP 200 with no usable completion is rewritten to
502 "Provider returned empty content", but the combo exhaustion classifier treated that synthetic 502 as a connection-level failure (#1731v2) and marked the whole provider/connection exhausted, skipping every remaining same-provider leg in that request. The connection is actually healthy (it just returned an empty body), so empty-content 502s are now classified as model-level transient failures: the request advances to the next leg and the rest of that provider's legs stay eligible. Genuine gateway 502s still trip connection exhaustion. (#5085, thanks @andrea-kingautomation) - fix(dashboard): surface the detailed credential-validation error instead of a bare "invalid" badge — the inline "Check" in the Add-Connection modal discarded the
errormessage returned by/api/providers/validateand showed only aninvalidbadge. For web providers (claude-web / chatgpt-web) the real cause is often an environment error the backend already reports (e.g.TLS impersonation client failed to start: EACCES … mkdir tls-client-node/bin), so users were left guessing. The modal now renders the full reason next to the badge. (#5088, thanks @tkhs101) - fix(executors): strip
client_metadatafrom forwarded body for Cerebras and Mistral — Cerebras returns 400 (wrong_api_format) and Mistral returns 422 (extra_forbidden) when the passthrough body carriesclient_metadata(an OpenAI Codex / Claude CLI field with no equivalent on these upstreams). The default executor now drops it for these two providers before sending downstream; other providers (notablyopenai/codex) keep it. (thanks @saurabh321gupta) - fix(codebuddy): only send reasoning params when the client requests reasoning. (thanks @anki1kr)
- fix(sse): keep streaming for forceStream providers when a JSON client requests it. Providers marked
forceStream:truerejectstream:falseupstream (HTTP 400);resolveStreamFlagnow guards against this so stream-only providers keep streaming even when the client sendsAccept: application/jsonorstream:false. (thanks @anki1kr) - fix(sse): prevent non-JSON SSE lines and duplicate
[DONE]from breaking clients. (thanks @qianze0628) - fix(sse): dedupe case-variant Anthropic headers in the executor
buildHeaderspath — Node/undici'sfetchmergesanthropic-versionandAnthropic-Versioninto a single"v, v"value that the Anthropic API rejects, so both case variants are now collapsed to one canonical lowercase header (same foranthropic-beta). (thanks @Delcado19) - oauth(kiro): support Kiro IDC (organization) token import — when the
~/.aws/sso/cachetoken carries aclientIdHash, auto-import now reads the linked client registration file to obtainclientId/clientSecret, probes the Kiro IDEprofile.jsonforprofileArn(ARN region normalized tous-east-1for the runtime gateway), and refreshes via the regional AWS OIDC endpoint instead of the social path; the import schema and modal forward these credentials so manual imports also work for IDC tokens. (thanks @enjoyer-hub) - fix(translator): preserve client
cache_controlbreakpoints when routing Claude-format requests (e.g. Claude Code) to Alibaba DashScope's OpenAI-compatible providers (alibaba/alibaba-cn). The Claude→OpenAI translation previously stripped the markers from the system and message text blocks, so DashScope's explicit caching never engaged and every request was a cache miss. Cache hints now survive when preservation is requested for caching-capable OpenAI-format providers. (thanks @sacrtap) - fix(tts): resolve Gemini TTS models from catalog and add
gemini-3.1-flash-tts-previewas the new default Vertex TTS model. (thanks @nguyenha935) - fix(sse): don't cool down a healthy connection on a self-inflicted upstream timeout (504) — when OmniRoute's own deadline elapses (surfaced as
TimeoutError/BodyTimeoutError→ 504), the connection is no longer disabled/failed-over, so a slow-but-healthy provider isn't penalised for our timeout. Genuine upstream 5xx/429 still trigger cooldown; antigravity keeps its own policy. (thanks @costaeder) - fix(translator): forward image
tool_resultblocks asimage_urlinstead of stringifying base64. (thanks @alican532) - fix(sse): robust Anthropic
/v1/messagesstreaming — real ping keepalive + client-disconnect guard — slow first tokens on reasoning models could trip strict clients' idle-read watchdog; the route now keeps the stream warm with a realevent: ping(Anthropic clients ignore SSE comments) from the very first frame, and a client disconnect (AbortError / controller-closed) no longer counts as a provider failure (no failover/cooldown). (thanks @costaeder) - fix: preserve model hidden flags (
isHidden) across model sync —replaceCustomModelspruned the compat-override list to the new custom-model ids, silently wiping theisHiddenflag of eye-hidden SYNCED models on every periodic sync / import (all hidden models turned back on). The redundant cleanup is removed (per-model removal already handles its own compat cleanup), so eye-hidden models stay hidden across re-sync. (#5086 — thanks @herjarsa) - fix(models): derive model-discovery config from the registry
modelsUrl— providers absent from the hardcodedPROVIDER_MODELS_CONFIGbut carrying a registrymodelsUrl(e.g. MiniMax) now get an auto-derived Bearer/v1/modelsdiscovery config, so "discover models" works instead of returning nothing. (thanks @herjarsa) - fix(compression): resolve worker + rule/filter assets via runtime anchors (standalone bundle) — the LLMLingua worker and the RTK rule/filter loaders relied on
fileURLToPath(import.meta.url), which the standalone bundle freezes to the build-machine path, so the worker never spawned and rule/filter packs failed to resolve. They now anchor onprocess.cwd()/argv[1](withpathToFileURLfor the worker URL). (thanks @fulorgnas) - fix(api): sanitize error responses on seven management routes (Rule #12 hardening) —
cli-tools/backups,cli-tools/guide-settings/[toolId],logs/export,models/catalog,providers/test-batch,settings/import-jsonandusage/proxy-logsno longer return rawerror.message; they wrap caught errors insanitizeErrorMessage(...), and the routes are removed from thecheck-error-helperallowlist. (thanks @JxnLexn) - fix(sse): keep
output_text-only Responses bodies from being dropped/false-502'd — some upstreams return a shorthand Responses body whose answer is only inoutput_textwith an emptyoutput[].sanitizeResponsesApiResponsediscarded the text, so the response then tripped the malformed-200 guard. The sanitizer now synthesizes anoutput[]message item from a non-emptyoutput_text(complements the Claude-native fix in #5108; both stem from #4942). - fix(executors): preserve a lone caller-supplied
Anthropic-Versionheader casing — the case-variant dedupe (#4846) unconditionally rewroteAnthropic-Version/Anthropic-Betato lowercase even when only one variant was present, clobbering the caller's header. Dedupe now runs only when both case variants coexist (the actual undici-merge collision it was meant to fix). - fix(responses): default
text.formatto{ type: "text" }for openai-compatible responses providers — some Responses-compatible upstreams (e.g. LM Studio) reject atextobject missingtext.formatwith a 400missing_required_parameter; the default executor now fills the Responses-API default before forwarding (guarded toopenai-compatible-*responses*, never overwriting an existing format). (thanks @StevanusPangau) - fix(translator): stop stripping client-provided
reasoning_contentfor reasoning-replay providers — the #4849 agentic-context strip (which dropsreasoning_contentfrom tool-call assistant turns to avoid O(n²) token growth) ran unconditionally, so replay providers (DeepSeek V4, Kimi K2, Qwen-Thinking, etc.) lost the client's reasoning and the reasoning-replay cache then overwrote it with a stale cached value (and such upstreams 400 without the original reasoning). The strip now skips reasoning-replay targets while non-reasoning providers keep the O(n²) protection. (#5122) - fix(providers): add MiniMax M3 & Nemotron 3 Ultra to the Cline catalog — the two models were missing from Cline's provider catalog and could not be selected; both are now registered. (#5136, closes #3321)
- fix(dashboard): key model-visibility toggle on the canonical
providerId— the per-model visibility toggle keyed off a display id, so toggling a model on one provider alias could mis-target another; it now keys on the canonicalproviderId. (#5091 — thanks @Theadd) - fix(diagnostics): recognize the Claude API format in
detectMalformedNonStream— salvaged null-guard so a Claude-shaped non-streaming body is no longer misclassified. (#5141 — thanks @herjarsa / @diegosouzapw) - fix(logging): track the final connection IDs in failover logs — failover log lines now record the connection that actually served (or last failed) the request, instead of only the first attempt. (#5016 — thanks @JxnLexn)
- fix(sse): ignore disconnect races during in-band stream error handling — a client disconnect that races with in-band upstream error handling no longer surfaces as a spurious provider failure. (#5007 — thanks @JxnLexn)
- fix(dashboard): surface the server error on
handleToggleCombofailure — a failed combo toggle now shows the backend error instead of silently no-op'ing. (#5138 — thanks @KooshaPari / @diegosouzapw) - fix(quota): track provider quota reset windows + enrich the Codex playground — observed quota reset windows are tracked and surfaced, and the Codex playground gains the enriched quota metadata. (#5141 — thanks @Witroch4 / @diegosouzapw)
- fix(sidebar): drop the orphan
settingsaccent color — removed a dangling accent-color entry that broketypecheck:core. (#5142) - fix(sse): preserve non-stream reasoning fields for compatible clients — non-streaming responses now keep the upstream reasoning fields (
reasoning/reasoning_contentand OpenRouter/Geminireasoning_details) instead of stripping them inresponseSanitizer, so clients that render reasoning on buffered responses no longer lose it. (#5155 — thanks @rdself) - fix(i18n): add missing English UI labels — fills in untranslated English strings that were surfacing as raw keys in the dashboard. (#5153 — thanks @rdself)
🔒 Security
- fix(security): exact-host Anthropic
baseUrlcheck — the Anthropic base-URL guard used a substring match that a crafted host could partially satisfy; it now requires an exact host match (resolves CodeQLjs/incomplete-url-substring-sanitizationalert #674). (#5130)
📝 Maintenance
- refactor(store): remove dead legacy store modules — salvaged cleanup of unused legacy store code. (#5138 — thanks @JxnLexn / @diegosouzapw)
- test(combo): deterministic routing-decision matrix for all 17 strategies — a deterministic E2E matrix pins the routing decision of every combo strategy. (#5146)
- chore: baseline reconciliations (complexity / file-size / cognitive), golden-snapshot + apikey-count alignment for new providers, orphan-test relocation, release base-red repairs, CHANGELOG i18n mirror sync, and an
actions/cache5→6 bump. (#5145, #5144, #5125, #5126, #5120, #5117, #5112) - test: gated live smoke for combo strategies (in-process + VPS HTTP) and refreshed release expectations to match current code. (#5151, #5150 — thanks @KooshaPari / @diegosouzapw)
[3.8.37] — 2026-06-26
✨ New Features
-
feat(providers): add DGrid AI gateway provider — OpenAI-compatible gateway at
api.dgrid.ai/v1(aliasdgrid, API-key auth, passthrough models). Free router tier (10 RPM / 100 RPD); a $5 lifetime top-up raises limits to 20 RPM / 1,000 RPD. (#4931 — thanks @dgridOP) -
feat(providers): add Pioneer AI (Fastino Labs) provider — OpenAI-compatible chat completions at
api.pioneer.ai/v1. Registered with aliaspn,X-API-Keyauth, and a catalog of 10 open-tier serverless models (Qwen3, Llama 3.1/3.2, Gemma 3, SmolLM3). Free $75 credits, no credit card required. Gated enterprise models (Claude/GPT/Gemini) require prior fine-tuning on the Pioneer platform and are intentionally excluded from the catalog. (#4909 — thanks @HikiNarou) -
feat(providers): add xAI Grok inbound translators and a thinking patcher — Grok requests are now translated on the inbound path and reasoning is normalized so Grok modes behave consistently across clients. (#4910 — thanks @mugnimaestra)
-
feat(oauth): Codex bulk-import endpoint —
POST /api/oauth/codex/importaccepts multiple Codex OAuth credentials in one call for fast multi-account onboarding. (#4914 — thanks @beaaan) -
feat(embeddings): add a
dimensionsoverride field to embedding combos so an embedding combo can pin the output vector size per target. (#4913 — thanks @wenzetan) -
feat(sse): auto-promote successful combo model — a new opt-in
comboAutoPromoteEnabledsetting reorders a combo's persisted model list so that, when a combo model responds successfully, it is moved to position #1 for future requests. (#4852 — thanks @arssnndr) -
feat(sse): add toggleable tool-source diagnostics — an opt-in switch surfaces where each tool definition originated when debugging tool-routing issues. (#4856 — thanks @DuyPrX)
-
feat(headroom): proxy lifecycle management + dashboard UI — start/stop/monitor a Headroom compression proxy from the dashboard, with Docker sidecar support. (#4649 — thanks @diegosouzapw / @carmelogunsroses)
-
feat(sse):
x-omniroute-strip-reasoningrequest header to dropreasoning_contentfrom upstream responses (opt-in, preserving reasoning-aware clients). (#4678 — thanks @anuragg-saxenaa / @diegosouzapw) -
feat(cli): multi-model support for the Factory Droid CLI integration. (#4682 — thanks @anuragg-saxenaa / @diegosouzapw)
-
feat(sse): parse Gemini CLI 429
retryDelayfrom the structuredRetryInfopayload so cooldowns honor the upstream-provided backoff. (#4738 — thanks @NoxzRCW) -
feat(sse): add GPT-4 and GPT-4o mini to the GitHub Copilot provider catalog. (#4798, #4797 — thanks @decolua)
-
feat(api): add the
MiniMax-M3pricing row (canonical + lowercase alias) so the new MiniMax default model gets accurate per-request cost accounting instead of falling back to a zero/default rate. (#4814 — thanks @octo-patch)
🔧 Bug Fixes
-
fix(sse): dense, deterministic
response.outputordering inresponse.completed— items are now sorted by their actualoutput_index(via a recorded-as-emitted accumulator + stable sort) instead of being rebuilt from unordered state dicts;normalizeOutputIndexreplaces fragileparseIntcalls for robust index coercion; superseded tool calls (replaced at the same index mid-stream) are excluded from the final output array. (#4906 — thanks @Marco9113) -
fix(sse): normalize Codex custom/freeform tools (
apply_patch,type:"custom"with noparameters) to a{ input: string }function schema instead of an empty schema — the empty schema made models invokeapply_patchwith{}, breaking the Codex runtime which expects{ input: string }. Also mapscustom_tool_call/custom_tool_call_outputinput items and streamsapply_patchtool calls viacustom_tool_call_input.delta/.doneevents. (#4862 — thanks @nstung463) -
fix(sse): preserve the
requiredarray when translating Draft 2020-12 antigravity tool schemas (e.g. from OpenCode), stripping unsupported JSON Schema meta keywords while keeping mandatory arguments required so the model no longer calls tools without them. (#4843 — thanks @anuragg-saxenaa) -
fix(sse): Kiro tool-schema sanitizer — strip unsupported JSON-Schema keywords (
anyOf/$ref/if-then, etc.) and hash-truncate tool names >64 chars before dispatch, mapping the streamed tool-call name back for the client, so Kiro no longer rejects tool calls with400 "Improperly formed request". (#4847 — thanks @smarthomeblack) -
fix(sse): make the
anthropic-versiondefault-guard case-insensitive foranthropic-compatible-*providers, so a caller/operator-suppliedAnthropic-Version(any casing) is no longer clobbered by a second lowercaseanthropic-version: 2023-06-01header. (#4823 — thanks @zakirkun) -
fix(db): validate HuggingFace API tokens via the
whoami-v2endpoint as a pure auth probe so fine-grained Inference-Provider tokens (valid even when model/task endpoints reject them) are no longer falsely marked invalid; only 401/403 means an invalid key, other non-OK statuses surface as transient upstream errors. (#4819 — thanks @Delcado19) -
fix(sse): reject the Anthropic-only
[1m]context-1m suffix inbuildKiroPayloadbefore it reaches AWS Bedrock — Kiro is Bedrock-backed and cannot honor the beta, so a forwardedkr/*[1m]model id was malformed upstream; callers now get a clear error pointing them at a direct-Anthropic provider for 1M-context routing. (#4816 — thanks @Delcado19) -
fix(dashboard): align the Engine Combos editor engines with the API schema — the named-combos pipeline dropdown offered four engines (
headroom,session-dedup,ccr,llmlingua) thatPUT /api/context/combos/[id]rejects, so selecting one made the save return 400 while the UI swallowed the error. The dropdown is now sourced from a single canonical engine map shared withstackedPipelineStepSchema(parity guarded by a unit test), and the editor surfaces save errors plus empty-name/empty-pipeline validation instead of failing quietly. (#5062 — closes #4955) -
fix(sse): surface malformed HTTP-200 upstream responses instead of treating them as success, so combo fallback can trigger. (#4942 — thanks @haipham22)
-
fix(antigravity): retry transient upstream failures rather than failing the request outright. (#4941 — thanks @Jordannst)
-
fix(sse): exclude WS-bridge controller-closed errors from the provider circuit breaker so a client disconnect no longer trips the whole provider. (#4870 — closes #4602, thanks @huohua-dev)
-
fix(sse): resolve custom combos by id and case-insensitive name. (#4869 — closes #4446, thanks @herjarsa)
-
fix(sse): forward AI SDK image parts in the Responses translator. (#4859 — thanks @mugnimaestra)
-
fix(sse): emit valid concatenable Kiro
tool_calls.argumentsdeltas. (#4855 — thanks @wahyuzero) -
fix(sse): strip
temperaturefor Claude models with extended thinking enabled (the upstream rejects it). (#4853 — thanks @noestelar) -
fix(sse): unwrap the Qoder HTTP-200 SSE error envelope so combo fallback can trigger. (#4850 — thanks @vianlearns)
-
fix(sse): strip reasoning blobs from agentic context to prevent O(n²) token growth across multi-turn agent loops. (#4849 — thanks @GodrezJr2)
-
fix(sse): close the reasoning block before message content in the Responses stream so clients render reasoning and answer in the right order. (#4848 — thanks @kwanLeeFrmVi)
-
fix(config): sync the full SiliconFlow model list into the registry. (#4844 — thanks @letanphuc)
-
fix(sse): strip Composer
<|final|>sentinel markers that leaked after Composer reasoning. (#4842 — thanks @noestelar) -
fix(build): trace-include
sql.js'ssql-wasm.wasmin the standalone bundle so SQLite-WASM works in the packaged build. (#4839 — thanks @Delcado19) -
fix(cli): persist lazily-installed native runtime deps (
better-sqlite3,systray2) to the shared runtimepackage.jsonwith--save-exactinstead of--no-save, so installing one no longer prunes the other as "extraneous" — fixing a "No SQLite driver available" failure after a--trayinstall. (#4841 — thanks @omartuhintvs) -
fix(sse): resolve bare model names to a connection's
defaultModelbefore upstream calls. (#4825 — thanks @anuragg-saxenaa) -
fix(api): surface a Docker-localhost hint on provider-node validation connection errors. (#4822 — thanks @anuragg-saxenaa)
-
fix(sse): strip Gemini built-in tools when
functionDeclarationsare present in the Antigravity envelope (the two are mutually exclusive upstream). (#4821 — thanks @vanszs) -
fix(sse): strip
X-Stainless-*headers and normalize the SDKUser-Agentfor OpenAI-compatible endpoints. (#4820 — thanks @anuragg-saxenaa) -
fix(oauth): allow a per-connection refresh lead-time override via
providerSpecificData.refreshLeadMs. (#4818 — thanks @anuragg-saxenaa) -
fix(dashboard): resolve passthrough model aliases by
providerIdinModelSelectModal. (#4815 — thanks @anuragg-saxenaa) -
fix(sse): strip
enumDescriptionsfrom Antigravity tool schemas. (#4813, #4740 — thanks @anuragg-saxenaa) -
fix(dashboard): keep the desktop sidebar visible via an explicit CSS class. (#4812 — thanks @Delcado19)
-
fix(sse): filter nameless hosted tools when converting Responses API to Chat format. (#4789 — upstream, thanks Владимир Акимов)
-
fix(sse): stream-writer mock
abort()now returns a Promise (test-stability fix). (#4788 — thanks @decolua) -
fix(sse): use the WorkOS auth-token shape for Cline. (#4787 — thanks @apeltekci)
-
fix(api): fall back to the existing access token for any OAuth provider when a refresh fails. (#4786 — thanks @decolua)
-
fix(sse): read Antigravity usage from the
response.usageMetadataenvelope. (#4785 — thanks @decolua) -
fix(oauth): verify Cursor installation on Linux before auto-import. (#4770 — upstream, thanks Ibrahim Ryan)
-
fix(cli): fall back to the default data dir when
DATA_DIRis not writable. (#4767 — upstream, thanks Thiên Toán) -
fix(sse):
json_schemafallback for OpenAI-compatible providers that don't support structured outputs. (#4766 — thanks @mustafabozkaya) -
fix(cli): verify launchd registration and skip self-SIGTERM in macOS autostart. (#4765 — thanks @ntdung6868)
-
fix(sse): finalize the
tool_callsfinish_reasonon early stream end in the OpenAI Responses translator. (#4764 — thanks @decolua) -
fix(sse): gate Kiro image attachments behind a Claude-capability check. (#4763 — thanks @decolua)
-
fix(sse): track Ollama streaming usage from raw NDJSON chunks. (#4754 — thanks @fresent)
-
fix(sse): include low-level cause details in
formatProviderError. (#4741 — thanks @decolua) -
fix(executors):
anthropic-compatible-*gateways now get aBearertoken alongsidex-api-key. (#4729 — thanks @hodtien) -
fix(translator): strip the
x-anthropic-billing-headerin the claude-to-openai path. (#4728 — thanks @weimaozhen) -
fix(translator): preserve
reasoning_effortfor non-Copilot Responses clients. (#4688 — thanks @ryanngit / @diegosouzapw) -
fix(codex): treat an OAuth 401 as an unrecoverable refresh failure (stop retrying a dead token). (#4686 — thanks @sacwooky / @diegosouzapw)
-
fix(translator): coerce tool descriptions to strings in OpenAI normalization. (#4675 — thanks @East-rayyy / @diegosouzapw)
-
fix(dashboard): stop double-masking an already-masked API key in the list view (E2E 3/9 regression). (#4671 — thanks @diegosouzapw)
-
fix(combo): flatten Anthropic tool messages + tool history to prevent an upstream 503. (#4648 — thanks @warelik / @diegosouzapw)
-
fix(providers): require a Default Model in the compatible-provider API-key setup flow. (#4641 — thanks @arden1601)
🔒 Security
-
fix(auth): only trust forwarding headers (
X-Forwarded-For/X-Real-IP) from loopback TCP peers, so a non-loopback client can't spoof its origin to bypass local-only route guards. (#4689 — thanks @Jordannst / @diegosouzapw) -
fix(sse): redact the API key from the AUTH debug log in the chat handler. (#4858 — thanks @sacwooky)
-
fix(oauth): classify
/api/oauth/cursor/auto-importas a local-only route in the route guard, so the loopback-enforced process-spawning endpoint can't be reached through a tunneled/leaked JWT (Hard Rule #17). (#5070 — thanks @diegosouzapw)
📝 Maintenance
-
chore(ci): harden the release flow — decouple the Quality Ratchet from coverage-shard flakes (
if: !cancelled()+--allow-missing), add fast-path drift gates (check:complexity,check:cognitive-complexity,check:pack-policy,check:build-scope), and raise the default build heap to 8 GB. (#5054 — thanks @diegosouzapw) -
docs(routing): sync the combo strategy docs for Fusion (17 strategies). (#5067 — thanks @diegosouzapw)
-
test(sse): golden-lock the
provider.tstranslate-path across all providers. (#4734 — thanks @diegosouzapw / @decolua) -
docs(env): document
HEADROOM_URLin.env.example+ENVIRONMENT.md. (thanks @diegosouzapw) -
chore(quality): rebaseline the file-size ratchet across the rc17 PR-batch levas (leva2/leva3/leva4) to absorb cycle drift. (thanks @diegosouzapw)
[3.8.36] — 2026-06-25
✨ New Features
Quota-Share system
- feat(quota): introduce a dedicated
quota-sharecombo strategy (Fase 3 #9) — Deficit Round Robin scheduling with per-model in-flight gating (P2C), automatic DB migration that promotes existingqtSd/*combos, and per-policy gating so invalid allocations cannot bleedallowto unintended connections. (#4939, #4901) - feat(quota): multi-window usage buckets, per-(key,model) caps, and session stickiness — connections now track consumption across 5 h, 7 d, and per-model windows;
quota_allocation_model_capsenforces per-key/model limits; session stickiness preserves prompt-cache integrity across turns. (#4928, #4927, #4929) - feat(quota): headroom strategy + proactive saturation — new
headroomcombo strategy selects connections by available quota margin; universal proactive saturation via upstream token-usage response headers; real Claude quota saturation sourced from/api/oauth/usage. (#4908, #4907, #4885) - feat(quota): concurrency control + cooldown-wait (Fase 2.1) —
max_concurrentis enforced at dispatch time; quota-share combos queue concurrent requests with a short cooldown-wait and re-dispatch on slot availability (Variant A); a cron heal proactively restores connections after their window resets. (#4965, #4970, #4967, #4900)
Combo routing
- feat(combo): task-aware routing strategy — routes requests to the best-fit connection based on task-type metadata, enabling per-task provider specialization within a combo. (#4945)
- feat(combo): Fusion strategy (16th strategy) — fan out to a configurable panel of models in parallel, then synthesize results through a judge model. (#4652)
- feat(combos): add an editable per-combo
descriptionfield. The routing-combo form now has a Description input, persisted in the combodatablob via/api/combos(POST/PUT) and round-tripped through GET — no new DB column. (#5005) - feat(routing): honor
X-Route-Modelrequest header to overridebody.model, enabling per-request model switching without modifying the request body. (#4863 — thanks @costaeder)
Providers & models
- feat(providers): update volcengine-ark model list, adding DeepSeek-V4-Flash and DeepSeek-V4-Pro. (#4905 — thanks @kenlin8827)
- feat(provider): add CodeBuddy CN (
copilot.tencent.com) — full OAuth + executor + model catalog stack. (#4664) - feat(opencode-go): advertise
glm-5.2andkimi-k2.7-codeto align with official Go endpoints. (#4711) - feat(sse): add Google Flow video-generation provider. (#4769)
- feat(api/v1): include alias-backed models in the
/v1/modelslisting. (#4630)
Proxy pool
- feat(proxy-pool): Cloudflare Workers proxy deployer + pool integration — deploy Cloudflare Workers relays directly from the dashboard and register them in the proxy pool. (#4640)
- feat(proxy-pool): Deno Deploy relays + group action buttons — deploy Deno Deploy relay workers and manage proxy groups with new bulk-action controls. (#4643)
Compression & infrastructure
- feat(compression): Kiro/CodeWhisperer tool-result compression engine — dedicated compressor for Kiro/CodeWhisperer tool outputs integrated into the streaming pipeline. (#4635)
- feat(endpoint): per-endpoint custom system prompt injection. A toggle + text field in the Endpoint settings card lets users inject a custom system prompt into every model request, applied via the existing system-prompt engine. Stored in settings DB. (#5022 — thanks @whale9820)
- feat(live-ws): allow non-loopback clients via
LIVE_WS_ALLOWED_HOSTSenv var, enabling multi-host setups to access the live WebSocket API. (#4877 — thanks @KooshaPari) - feat(db): track API endpoint dimension on
usage_historyfor per-endpoint cost and usage analytics. (#4676)
🔧 Bug Fixes
Translator
- fix(translator): regroup parallel tool results to be adjacent to their originating assistant turn, fixing tool-message ordering for providers that require strict interleaving. (#4882)
- fix(translator): preserve literal empty-string tool arguments in OpenAI-to-Claude streaming — they were previously dropped, causing tool calls to arrive with missing parameters. (#4959)
- fix(translator): normalize tools to Anthropic-native shape for non-Anthropic providers, ensuring tool definitions pass validation regardless of the format at the call site. (#4650)
- fix(translator): provider thinking compatibility — correct thinking-block serialization for DeepSeek and Gemini providers. (#4946)
- fix(translator): emit
</think>close marker for Anthropic thinking blocks, fixing truncated reasoning output in streamed responses. (#4633) - fix(translator): normalize
developerrole tosystemfor OpenAI-format providers. (#4625) - fix(translator): strip top-level
client_metadataon the OpenAI passthrough path (port from 9router#1157). (#4624) - fix(translator): replay
reasoning_contenton plain Xiaomi MiMo turns (port from 9router#1321). (#4639)
Copilot / GitHub executor
- fix(copilot): never route Gemini/Claude model variants to the
/responsesendpoint — these models require the chat-completions path only. (#4627) - fix(github): route Copilot Codex models to
/responses(port from 9router#102). (#4626) - fix(copilot,antigravity): cap
maxOutputTokensat 16384 to stop "Invalid Argument" 400 errors on high-token requests. (#4636) - fix(codex): drop non-standard
codex.*streaming events that breakresponses.streamconsumers. (#4715 — thanks @jeffer1312)
Claude / Anthropic
- fix(claude): omit
adaptive_thinkingandoutput_config.effortfor Haiku model variants, which reject those parameters. (#4661) - fix(claude): skip
mcp__tool-name cloaking and guard against missingconnectionIdto prevent crashes on Claude-native MCP tool calls. (#4861 — thanks @costaeder) - fix(claude-oauth): respect
429backoff headers on the Claude OAuth usage endpoint to reduce polling spam during quota checks. (#4655)
Routing & SSE
- fix(sse): fail over on
400responses that carry rate-limit text in the body, not just on canonical429status codes. (#4986) - fix(sse): honor per-account proxy and fingerprint-rotation settings in the opencode executor. (#4989)
- fix(sse): soft-penalize exhausted providers in auto-combo scoring instead of hard-excluding them, improving fallback resilience. (#4990)
- fix(sse): drop the CCP pin when the pinned provider is durably unhealthy, with anti-flap logic to prevent oscillation. (#4864 — thanks @costaeder)
- fix(combo): fetch models dynamically from custom provider endpoints instead of relying on a static list. (#4860)
- fix(combo): propagate the selected connection ID to fallback error responses so model lockout applies to the correct connection rather than the wrong fallback target. (#4809 — thanks @Chewji9875)
- fix(sse): skip third-party tool-name cloaking for Anthropic-native server tools to prevent naming conflicts. (#4808 — thanks @NomenAK)
Quota
- fix(quota): quota-exclusive
qtSd/*connections now appear in/v1/modelslistings; EPSILON-threshold check no longer falsely blocks under-budget allocations. (#4830) - fix(quota): migration 107 correctly activates the
quota-sharestrategy on existingqtSd/*combos. (#4962)
API / responses
- fix(api): parse the
/v1/responsesrequest body once instead of 3–4 times on the hot path, reducing per-request overhead. (#4958) - fix(api): evict stale in-memory rate-limit windows to stop a slow heap leak on long-running instances. (#4957)
- fix(api): require authentication on the compression
run-telemetryendpoint; documentOMNIROUTE_EVAL_CREDENTIALSenv var. (#4796) - fix(api): stop
GET /api/system/env/repairreturning HTTP500on packaged installs (it broke the onboarding wizard).createRequire(import.meta.url)ran at module top-level; once webpack bundles the route into the standalone build,import.meta.urlis frozen to the build-machine path andcreateRequirethrows during evaluation, so the whole route failed to load.createRequireis now resolved lazily inside the guardedbetter-sqlite3block, root-dir resolution falls back toprocess.cwd(), and the route passes an explicitrootDir. (#5028)
Dashboard
- fix(dashboard): show custom provider given-name instead of internal id across dashboard pages — cache, combo health, compression analytics, cost overview, health/autopilot, provider stats, route explainability, provider utilization, runtime. Adds shared
resolveProviderNameresolver anduseProviderNodeMaphook. (#4603) - fix(dashboard): on OAuth providers (e.g. GLM Coding), "Test all models" with auto-hide-failed now switches the model list to the "visible" filter after the run, so just-hidden failed models actually disappear on-screen — parity with the passthrough-provider path (#3610). Previously they were hidden in the DB but stayed visible under the "All" filter, so it looked like nothing was hidden. (#4887)
- fix(dashboard): restore the home-page provider topology card that was hidden by a default state change in #4596. (#4963)
- fix(dashboard): proxy-pool success gating, sync-timestamp persistence, and opt-in Redis backend. (#4988)
- fix(dashboard): show custom vision models in the LLM selector dropdown. (#4653)
Providers
- fix(pollinations): stop forcing
jsonModeon every request. Pollinations treatsjsonMode=trueas "the model MUST return JSON" and rejects (HTTP 400 "messages must contain the word 'json'") any normal chat request whose messages don't mention "json", so all non-JSON chat was broken.jsonModeis now only enabled when the caller actually requests JSON output (response_format.typeofjson_objectorjson_schema). (#3981) - fix(antigravity): default
safetySettingsto all-OFF for parity with the native Gemini paths. The Antigravity (Google Cloud Code) request builder setsafetySettings: undefined, whichJSON.stringifydrops — so no safety settings reached Google and its server-side defaults false-flagged benign technical prompts asprohibited_content(HTTP 200 + blocked body, which combo failover treats as terminal). Now honors a caller-supplied value and otherwise defaults toDEFAULT_SAFETY_SETTINGS, matching the claude-to-gemini / openai-to-gemini paths. (#5003) - fix(antigravity): exclude the standard Gemini rate-limit message from quota-exhaustion keyword matching to prevent false-positive saturation flags. (#4810 — thanks @Chewji9875)
- fix(chatgpt-web): map the advertised
gpt-5.5,gpt-5.5-pro,gpt-5.4-proandgpt-5.2-procatalog ids to their dash-form ChatGPT backend slugs. They were missing fromMODEL_MAP, so the executor sent the dot-form id verbatim, which the ChatGPT backend silently ignored and served the default Plus model instead of the requested one. Adds a drift guard asserting no advertised dot-form id reaches the backend verbatim. (#4665) - fix(gemini): preserve the
patternfield in the Antigravity tool schema sanitizer to avoid stripping valid regex constraints from tool definitions. (#4651) - fix(opencode): preserve DeepSeek reasoning content in streamed responses. (#4631)
- fix(perplexity): validate API keys via the
/v1/modelsendpoint instead of issuing a full chat request. (#4654) - fix(qoder): exchange PAT for
jt-*job token before initiating Cosy chat, fixing auth failures after the Qoder credential format change. (#4884) - fix(executors): strip parameters unsupported by the target provider/model to prevent
400 Invalid parametererrors on strict endpoints. (#4658) - fix(executors): preserve literal
reasoning_effort: "max"for Ollama Cloud instead of normalizing toxhigh. Ollama Cloud acceptshigh|medium|low|max|noneand rejectsxhigh(invalid reasoning value: 'xhigh'); OpenRouter DeepSeekmax→xhighnormalization is unaffected. (#4993 — thanks @Thinkscape) - fix(headroom): translate openai-responses input through OpenAI for external compression.
adaptBodyForCompressionnow serialisesfunction_call_outputitems whoseoutputfield is a JSON object (not a string) so compression engines can process the content — previously those items were excluded from compression becausehasTextContent()returned false for object values. (#5023 — thanks @anki1kr) - fix(proxy): fan out direct dispatcher streams to all registered proxy endpoints. (#4803 — thanks @makcimbx)
Compression
- fix(compression): eliminate ReDoS in the
math_inlinepreservation regex — the previous pattern could catastrophically backtrack on untrusted input. (#4838) - fix(compression): stop RTK over-truncating file-read tool results — RTK now respects the full content length for file-read outputs. (#4987)
Build / CLI / infrastructure
- fix(build): drop
@omniroute/open-ssefromoptimizePackageImportsto fix the Next.js build OOM crash. (#4968) - fix(cli): SIGKILL the systray child PID before closing the IPC channel to prevent macOS NSStatusItem orphan processes. (#4732)
- fix(cli): bump
better-sqlite3runtime pin to 12.10.1 for Node 26 compatibility. (#4685) - fix(cli): harden the systray2 tray runtime (port from 9router#1080). (#4628)
- fix(cli-tools): tolerate JSONC (comments and trailing commas) in tool settings files. (#4659)
- fix(install): make the
transformersdependency optional so CUDA-host installs that lack Python bindings succeed. (#4807 — thanks @megamen32) - fix(db): correct storage tuning settings to prevent WAL runaway on high-write workloads. (#4834 — thanks @rdself)
- fix(image): prevent compatible nodes from shadowing provider aliases in the image routing table. (#4656)
Plugin
- fix(plugin): opencode
auth.jsondual-key fallback for the auto-prefix migration. The config hook now looks up both the prefixed (opencode-omniroute) and bare (omniroute) keys, so users who authenticated before theopencode-prefix landed no longer need to re-auth. (#5027 — thanks @herjarsa)
🔒 Security
- fix(security): block SSRF allowlist bypass via
x-relay-pathheader manipulation on Deno/Vercel relays. (#4899) - fix(security): pin image-fetch DNS resolution to prevent SSRF DNS-rebinding attacks (GHSA-cmhj-wh2f-9cgx). (#4634)
- fix(security): do not trust the loopback socket as local-only when the server is behind a reverse proxy, closing a potential auth bypass path. (#4632)
- fix(security): validate the Kiro region parameter to prevent SSRF via crafted region strings (GHSA-6mwv-4mrm-5p3m). (#4629)
- fix(copilot): replace
execSyncshell interpolation withexecFilein therunOmniRouteClitool to prevent command injection. The user-supplied command is now split into an argv array and passed toexecFile(no shell), so shell metacharacters are treated as literal text; error output is routed throughsanitizeErrorMessage(). (#5024 — thanks @hamsa0x7)
📝 Maintenance
God-file decomposition (continued, #3501)
- refactor(chatCore): extracted 12 focused helpers from
chatCore.tscovering the streaming pipeline (assembleStreamingPipeline), cache-store logic (storeStreamingSemanticCacheResponse,storeSemanticCacheResponse), response headers (assembleStreamingResponseHeaders,buildNonStreamingResponseHeaders), JSON→SSE bridge (maybeConvertJsonBodyToSse), guardrail context (buildPostCallGuardrailContext), usage buffer (applyClientUsageBuffer), plugin hook (runPluginOnRequestHook), analytics (writeCompressionAnalytics,emitOutputStyleTelemetry), and compression predicates/settings (resolveCompressionSettings, et al.). (#4811–#4837) - refactor(sse/db/api): continued decomposition of
services/usage.ts(extracted quota-core, scalar/format helpers, Antigravity/GLM/MiniMax usage families),db/core.ts(schema-column reconciliation, snake↔camel column-mapping),db/apiKeys.ts(row-parsers, model-permission matching), andvalidation.ts(URL/headers/transport leaf layer, web-cookie/Meta-AI validators, enterprise-cloud + probe, audio/speech/apikey, search/embedding/rerank, and OpenAI/Anthropic format validators). (#4921–#4956) - refactor(pricing/providers): decomposed
pricing.tsinto shared tiers + partitionedDEFAULT_PRICINGmodules, and split theproviders.tscatalog into semantic data modules organized by provider family. (#4917, #4918) - refactor(open-sse): extract
safeParseJSONutility and deduptryParseJSONcall sites; extract and dedup the fallbacktool_callID generation helper. (#4735, #4736)
Quality & CI
- chore(quality): release base-red reconciliation + ratchet rebaselines — file-size, env-doc, and catalog baseline updates across multiple gates. (#4630, #4879, #4886, #4915, #4961, #4973)
- ci(quality): shift heavy validation gates to the PR→release merge fast-path to accelerate the release cycle. (#4857)
- fix(ci): include
coverage/lcov.infoin the coverage-report artifact so SonarQube can consume it. (#4670) - fix(test): validate Anthropic-compatible connections via
POST /v1/messagesfor accurate connectivity testing. (#4657)
Docs
- docs(resilience): document Quota-Share Concurrency Control —
max_concurrentenforcement, serialization behavior, and cooldown-wait semantics. (#4980) - docs(perf): add per-endpoint p50/p95/p99 latency and cost budgets reference. (#4867 — thanks @KooshaPari)
- docs(ops): add canonical incident response runbook. (#4868 — thanks @KooshaPari)
- docs(ops): document the release-green family —
green-prs,check:release-green,babysit, and nightly gate workflows. (#4679) - docs(agentbridge): document Electron
NODE_EXTRA_CA_CERTS, real model IDs, and identity caveat for agent bridge integrations. (#4718) - docs: clarify Kiro provides ~50 credits/month per account, not unlimited. (#4690)
Misc
- chore(claude,codex): bump pinned CLI identities — Claude
2.1.158 → 2.1.187, Codex0.132.0 → 0.142.0. (#4883) - chore(dashboard): rename Qoder display label from "Qoder AI" to "Qoder". (#4733)
[3.8.35] — 2026-06-23
✨ New Features
- Adaptive context compression (Phase 4): a four-layer compression upgrade landed across stacked PRs — an Output Styles registry (
terse-prose/less-code/terse-cjk) (#4694 — thanks @diegosouzapw), an opt-in SLMultratier (two-tier LLMLingua with heuristic fallback) (#4707 — thanks @diegosouzapw), a context-budget adaptive dial (reserve-output ladder + floor) (#4716 — thanks @diegosouzapw), and an offline evaluation harness (PII-gated corpus, self-test judge, gold-grader, real-pipeline runner behind aModelClientseam) (#4720 — thanks @diegosouzapw). All four layers share a singleCompressionRunTelemetrycontract. - Redoc-rendered API docs: a consolidated OpenAPI spec now lives at
docs/openapi.yamland is served as interactive Redoc documentation at/api/docs. (#4781 — thanks @KooshaPari / @diegosouzapw)
🔧 Bug Fixes
- db-backups: make the database-import size cap configurable via
OMNIROUTE_DB_IMPORT_MAX_MB(default 100 MB, 4 GB ceiling) so backups larger than 100 MB can be restored; error message now points to the env var and to VACUUM (#4757 — closes #4719, thanks @diegosouzapw). - Onboarding: add the missing
onboarding.tiersstep-title translation so the setup wizard no longer crashes withMISSING_MESSAGE: onboarding.tiers(#4755 — closes #4698, thanks @diegosouzapw). - deepseek-web: fold
role:"tool"results into the single-prompt transcript (messagesToPrompt) so tool outputs reach the model instead of being silently dropped when a follow-up turn omits thetools[]array (#4756 — closes #4712, thanks @diegosouzapw). - Dashboard: remove the dead, unconditional
useLiveRequests()call fromHomePageClient.tsx— it crashed the/homepage in production builds withReferenceError: useLiveRequests is not defined(#4759, #4745) and opened the live-dashboard WebSocket even when Provider Topology was hidden (#4596). The live feed remains owned by the settings-gatedHomeProviderTopologySection(#4761 — thanks @diegosouzapw). - Providers dashboard: dedupe provider nodes by id when adding a compatible provider (
upsertProviderNodeById) so the same provider can no longer appear twice and no-op adds don't invalidate the compatible-provider memo (#4768 — closes #4746, thanks @diegosouzapw). - Storage VACUUM: the scheduled VACUUM job now follows the Storage page settings (
scheduledVacuum/vacuumHour) as the single source of truth; the legacy env-flag control path was removed (#4726 — thanks @rdself). - Tiers: no-auth providers are now counted as free, and the free-tier filter returns an empty set instead of falling through to every provider (#4753 — thanks @megamen32 / @diegosouzapw).
- Combos: auto-promote
zeroLatencyOptimizationsEnabledso legacy configs (pre-3.8.33fallbackCompressionMode="lite") round-trip cleanly on the first GUI edit (#4774 — thanks @KooshaPari / @diegosouzapw).
📝 Maintenance
- chatCore (#3501): continued the incremental decomposition of
executeProviderRequestand the streaming/non-streaming hooks into pure leaf modules — top-level helpers + 6 pure leaves (#4571),resolveExecutorWithProxy+getExecutionCredentials(#4646), Claude message transforms (#4708),persistAttemptLogs(#4717),stageTrace+compressionUsageReceipt(#4721),prepareUpstreamBody(#4730), parse + non-streaming usage-stats (#4762),recordContextEditingTelemetryHook(#4779),scheduleQuotaShareConsumption(#4780),emitRequestGamificationEvent(#4776),runPluginOnResponseHook(#4782),scheduleStreamingQuotaShareConsumption(#4784),recordCompressionCacheStats(#4792),writeCavemanOutputAnalytics(#4794),recordStreamingUsageStats(#4791), andrecordStreamingCost(#4790). (thanks @diegosouzapw) - Quality: expand
check:release-greento reproduce the full release-PR gate set locally (#4758 — thanks @diegosouzapw). - db: re-export
compressionRunTelemetryfromlocalDbto satisfy the db-rules gate (#4775 — thanks @diegosouzapw). - Security docs: add a canonical STRIDE-based threat model (#4783 — thanks @KooshaPari).
- Tests: add a smoke test for the home-client dashboard (#4793 — thanks @JxnLexn).
- Docs: credit ponytail and OmniCompress in the README inspiring-projects list and restore the
check:env-doc-syncrelease-green by exempting the harness-onlyOMNIROUTE_EVAL_CREDENTIALSvar (#4799 — thanks @diegosouzapw); declare the Phase 4 compression layers in the README + GUIDE (#4801 — thanks @diegosouzapw). - Quality: trim
combo-config.test.tscomments back under the file-size cap (follow-up to #4774) (#4800 — thanks @diegosouzapw).
[3.8.34] — 2026-06-23
✨ New Features
- feat(executors): Microsoft 365 Copilot pure framing + connection helpers — adds the request/response framing and connection helpers to support
m365.cloud.microsoft/chatfor individual M365 plans. (#4696 — thanks @skyzea1 / @diegosouzapw) - feat(compression): per-request
x-omniroute-compressionheader (Phase 3) — a request header now overrides the compression plan with the highest precedence (request-header > routing > profile > auto-trigger > Default > off), acceptingoff/default/engine:<id>/<combo>. The response echoesX-OmniRoute-Compression: <mode>; source=<source>. (#4645 — thanks @diegosouzapw) - feat(audio): MiniMax T2A v2 TTS dispatch in
audioSpeech— adds MiniMax text-to-speech dispatch (port of upstream #1043). (#4553 — thanks @diegosouzapw) - feat(opencode): OpenCode Go DeepSeek reasoning variants — registers the Go DeepSeek reasoning model variants. (#4647 — thanks @DevEstacion)
- feat(quota): quota scraping for OpenCode Go and Ollama Cloud — surfaces quota windows for the OpenCode Go and Ollama Cloud providers. (#4642 — thanks @JxnLexn)
- feat(settings): expose stream recovery feature flags — surfaces the stream-recovery toggles in settings. (#4586 — thanks @rdself)
- feat(providers): optional model ID for custom API-key validation — custom API-key connection tests can now specify the model ID used to validate the key. (#4555 — thanks @diegosouzapw)
🐛 Fixed
- fix(db): scheduled cleanup actually runs + queries target the real tables (DB-bloat / OOM) —
runAutoCleanupwas never scheduled, so retention cleanup never executed and tables (compression_analytics,usage_history, …) grew unbounded into multi-GB SQLite files driving high RSS. Worse, several cleanup queries referenced wrong table/column names (call_logs.created_at→timestamp,compression_analytics.created_at→timestamp,mcp_audit_log→mcp_tool_audit,a2a_events→a2a_task_events,memory_entries→memories), so even a manual run silently no-op'd or errored. Fixed the five queries to match the real schema, addedcleanupProxyLogs, and wired astartCleanupScheduler(startup + every 6h, VACUUM after deletes) intoserver-initalongside the existing budget-reset and reasoning-cache jobs. (#4691, extracted from #4428 — thanks @oyi77 / @diegosouzapw) - fix(routing): include all noAuth models in auto-combos + add reka-flash + best-free template — noAuth provider models are no longer skipped when building auto-combos,
reka-flashis registered, and abest-freecombo template is added. (#4621 — thanks @oyi77) - fix: noAuth provider validation + Kimi executor routing — corrects noAuth provider membership checks and removes a mis-routed Kimi alias. (closes #4620) (#4699 — thanks @oyi77)
- fix(executors): Firecrawl
web_fetch500 withinclude_metadata=true— fixes a crash when Firecrawl web_fetch is invoked with metadata extraction enabled. (#4692 — thanks @ponkcore) - fix(proxy): apply
pipelining:0+ connections cap to the direct dispatcher — same-provider concurrent requests no longer serialize behind a long/streaming request on the direct path. (#4684 — thanks @jeffer1312 / @diegosouzapw) - fix(telemetry): back off live-WS event forwarding when the sidecar is unreachable — stops repeatedly attempting to connect to
LIVE_WS_PORTwhen live monitoring is not configured. (#4687 — thanks @FikFikk / @diegosouzapw) - fix(api): serve
GET /v1/models/{model}as JSON, not the HTML dashboard — the per-model endpoint (IDs with slashes via a catch-all route) now returns JSON, unbreaking Claude Code. (#4677 — thanks @papajo / @diegosouzapw) - fix(executors): robust deepseek-web tool-call parsing and agentic context retention — hardens DeepSeek-web tool-call parsing and preserves agentic context across turns. (#4644 — thanks @BugsBag)
- fix(cli): authenticate
omniroute logsand honor the active context — thelogscommand now authenticates and respects the active context. (#4638 — thanks @Rahulsharma0810) - fix(stream): estimate input tokens when upstream reports
prompt_tokens=0— input token usage is estimated when the upstream omits it. (#4615 — thanks @adivekar-utexas) - fix(plugin): auto-prefix providerId with
opencode-for OpenCode 1.17.8+ native gate — adapts provider IDs to the OpenCode 1.17.8+ native provider gate. (#4527 — thanks @herjarsa) - fix(catalog): shorten no-thinking gateway prefix to
no-think/— renames the no-thinking gateway prefix. (#4525 — thanks @Rahulsharma0810) - fix(models): unknown max output limits no longer default to 8192 — models without synced/registry/static
maxOutputTokensresolve the limit as unknown instead of a generic 8192 cap; clamping/injection only happens when a real cap is known. (#4584 — thanks @rdself) - fix(resilience): respect upstream retry-hint toggle — honors the configured toggle for upstream retry hints. (#4585 — thanks @rdself)
- fix(providers): show revealed connection API keys — fixes revealing stored connection API keys in the UI. (#4583 — thanks @rdself)
- fix(logs): make active-request stale sweep configurable — exposes the stale-request sweep interval as a setting. (#4599 — thanks @rdself)
- fix(resilience): retain provider cooldowns for the configured max window — cooldowns persist for the configured maximum window. (#4588 — thanks @KooshaPari)
- fix(resilience): reject invalid provider cooldown bounds — validates cooldown bound configuration. (#4589 — thanks @KooshaPari)
- fix(combo): preserve production combo metrics on shadow eviction — shadow eviction no longer drops production combo metrics. (#4590 — thanks @KooshaPari)
- fix(combo): exclude exhausted connections from auto scoring — exhausted connections are no longer scored as auto-combo candidates. (#4592 — thanks @KooshaPari)
- fix(relay): apply IP rate limit to the Bifrost sidecar — extends IP rate limiting to the Bifrost relay sidecar. (#4593 — thanks @KooshaPari)
- fix(bifrost): finalize SSE relay usage after stream — finalizes relay usage accounting once the SSE stream completes. (#4612 — thanks @KooshaPari)
- fix(quota): expose Bailian quota windows — surfaces Bailian provider quota windows. (#4610 — thanks @KooshaPari)
- fix(dashboard): gate home topology live-WS networking behind widget visibility — the home dashboard no longer starts topology polling / live sockets when topology is hidden. (#4618, #4606 — thanks @KooshaPari)
- fix(dashboard): isolate the quota widget refresh clock — the quota widget refresh no longer drives unrelated re-renders. (#4611 — thanks @KooshaPari)
- fix(dashboard): memoize compatible provider groups — avoids recomputing compatible provider groups on every render. (#4613 — thanks @KooshaPari)
- fix(cli): align
omniroutedata dir and env loading with the runtime — the CLI's data-dir/env loading no longer drifts from the server runtime configuration. (#4619, #4607 — thanks @KooshaPari) - fix(api/settings): prevent cached
/api/settingsresponses — disables caching on the settings endpoint (port from 9router#951). (#4566 — thanks @diegosouzapw) - fix(executors): strip temperature for the GitHub Copilot gpt-5.4 family — removes the unsupported
temperatureparam for Copilot gpt-5.4 models (port from 9router#612). (#4564 — thanks @diegosouzapw) - fix(dashboard): keep play_arrow spinning on provider "Test All" buttons — fixes the spinner state on the provider test buttons (port from 9router#715). (#4563 — thanks @diegosouzapw)
- fix(dashboard): surface manual config CTA when Open Claw CLI auto-detect fails — shows a manual-config call-to-action on the Open Claw CLI card when auto-detection fails. (#4562 — thanks @diegosouzapw)
- fix(oauth): update Qwen OAuth URLs from
chat.qwen.aitoqwen.ai— refreshes the Qwen OAuth endpoints (port of decolua/9router#683). (#4561 — thanks @diegosouzapw)
📝 Maintenance
- refactor(imageGeneration): extract 8 provider families to co-located files — splits the image-generation module into eight co-located per-provider files with no behavioral change. (#4609 — thanks @KooshaPari)
- deps: bump production + development groups; migrate js-yaml to v5 (ESM) — dependency bumps plus a
js-yamlv4→v5 migration to the ESM-only namespace import. (#4697 — thanks @diegosouzapw) - chore(quality): release-green pre-flight validator + nightly signal — new
npm run check:release-green(scripts/quality/validate-release-green.mjs) reproduces the release-equivalent validation (full unit + vitest + ratchets + typecheck + lint, optional--with-buildpackage-artifact) against the current working tree and classifies each red as HARD (real defect) vs DRIFT (ratchet, rebaselined at release) — purely diagnostic, never blocking contributors. A newnightly-release-greenworkflow runs it on the active release branch and opens/updates a tracking issue on hard failures. Closes the gap where the full gate (ci.yml) only ran on the release PR, so reds accrued silently onrelease/**and surfaced in layers at release time. (#4622 — thanks @diegosouzapw) - chore(quality): reconcile file-size baseline for #4644 (
deepseek-web.ts1117→1125) — rebaselines the file-size gate after the deepseek-web hardening. (#4695 — thanks @diegosouzapw)
[3.8.33] — TBD
See English CHANGELOG for v3.8.33 details.
[3.8.32] — TBD
See English CHANGELOG for v3.8.32 details.
[3.8.31] — 2026-06-20
✨ New Features
- perf(dashboard): combos UI leaf-split, Next.js config tuning, 1-click Redis & Bifrost sidecar — delivers four of the five performance/UX tracks from the #3932 thread: the combos dashboard page is split into focused leaf components (smaller bundles, faster reloads),
next.configis tuned for the standalone build, Redis can be provisioned in one click, and a Bifrost sidecar option is wired in. (The fifth track — chatLogHelpers extraction — was already covered upstream and dropped.) (#4381 — thanks @KooshaPari)
🐛 Fixed
- fix(embeddings): NVIDIA NIM asymmetric embedding models inject the required
input_type— NVIDIA NIM asymmetric embedders (e.g.nvidia/nv-embedqa-e5-v5) reject requests without aninput_typeparameter with400 "'input_type' parameter is required", but OmniRoute only forwardedinput_typewhen the client supplied it — so callers (and OpenAI-style SDKs that don't emit the field) got a hard failure. The embedding registry now carries a model-level default (input_type: "query") for the asymmetric NVIDIA model, and the embeddings handler injects a model's default params into the upstream body only when the client didn't already send them — a client-suppliedinput_type(e.g."passage") is respected unchanged, and symmetric models that carry no default are unaffected. (#4341 — thanks @hydraromania) - fix(api): migrate the deprecated Codex
[features].codex_hooksflag to[features].hooks— Codex renamed thecodex_hooksfeature flag tohooks; recent Codex CLI versions ignore the old key and print a deprecation notice. When OmniRoute rewrites an existing~/.codex/config.toml(configuring/resetting the Codex provider) it now carries the user's intent forward by renaming[features].codex_hooks→[features].hooks(preserving its value, never clobbering an already-presenthooks) and dropping the deprecated key. No-op when the flag is absent. (#4342 — thanks @Bian-Sh) - fix(translator): same-format response path no longer leaks a
data: nullSSE event — the streaming response translator's same-format fast path returned[chunk]unconditionally, so the end-of-stream null/flush signal (chunk === null) propagated as a literal[null]. Downstream this surfaced as an emptydata: nullSSE event between chunks and crashed strict clients (e.g. Factory Droid BYOK on/v1/responses). The fast path now drops the null flush (returns[]) while still passing real chunks through unchanged. (#4344 — thanks @thaitryhand) - fix(translator): strip client-only assistant echo fields on the OpenAI target path (Mistral 422) — strict OpenAI-compatible upstreams (e.g.
mistral/codestral-latest) reject client-only assistant "echo" fields sent back as input history with422 extra_forbidden(the report hitmessages[].assistant.reasoning_contentvia Codex/responses). Onlyreasoning_contentwas being stripped on the OpenAI target path; the sibling echo fieldsreasoning,refusal,annotationsandcache_controlleaked through and tripped the 422. They are now all dropped on the non-reasoner OpenAI target path.audiois deliberately preserved (OpenAI audio models reference a prior assistant audio response by id on multi-turn; Mistral never emits audio, so nothing is lost there). (#4350 — thanks @xxy9468615) - fix(translator): accept AI SDK-style
{ type: "image", image: "data:…" }content parts — several OpenAI-input translators only recognized images shaped asimage_url.url(or an object with.source/.url), so an AI SDK-style part whereimageis a bare data-URL string was silently dropped before reaching a vision provider (OpenCode is one affected client; the gap is generic). The OpenAI→Claude, OpenAI→Kiro and OpenAI→Gemini/Antigravity translators now parse a stringimagedata URL into each provider's native image shape (Claude{source:{type:"base64"}}, Kiroimages[].source.bytes, GeminiinlineData). (#4345 — thanks @mugnimaestra) - fix(translator): Gemini accepts HTTP/HTTPS image URLs instead of silently dropping them — the OpenAI→Gemini request helper (
convertOpenAIContentToParts) discarded remoteimage_urlparts (emitting only aconsole.warn) because Gemini'sinlineDataneeds base64 and the synchronous helper can't fetch+encode upstream. It now uses Gemini's nativefileData: { fileUri }part for HTTP/HTTPS URLs (the model fetches the asset itself), so vision requests carrying a URL — not adata:URI — reach Gemini intact. (#4373 — ported from 9router#344, thanks @diegosouzapw) - fix(executors): strip
stream_optionsfor qwen non-streaming / thinking Claude-Code requests — Claude-Code-compatible providers force the executor-levelstreamflag on while the outgoing body keeps the caller's originalstream: false, soDefaultExecutor.transformRequestinjectedstream_options: { include_usage: true }onto a body that still saidstream: false, and qwen rejected it with400 "'stream_options' only set this when you set stream: true". The executor now stripsstream_optionswhenever the body's effectivestreamis false. (#4374 — ported from 9router#663, thanks @anuragg-saxenaa / @diegosouzapw) - fix(executors): don't inject
thinkingwhentool_choiceforces a tool (native Claude) — the Claude-Code wire-image emulation injectsthinking: { type: "adaptive" }for non-Haiku Claude models, but Anthropic rejectsthinkingwhentool_choiceforces a specific tool ({type:"any"|"tool"}) with400 "Thinking may not be enabled when tool_choice forces tool use.". Any Opus/Sonnet call that pins a tool (e.g. Claude Code'smessage_user, or agent harnesses that force a tool) hit a hard 400; the injection is now suppressed whentool_choiceforces a tool. (#4389 — thanks @NomenAK) - fix(codex): request reasoning summaries on Codex Responses requests — Codex/OpenAI Responses can return reasoning-token accounting and empty reasoning items unless visible reasoning summaries are requested, so Codex CLI / pi.dev paths missed visible thinking text. OmniRoute now requests
reasoning.summary: "auto"(and includesreasoning.encrypted_content) when reasoning is enabled — preserving an explicit clientreasoning.summaryand existingincludeentries, and skipping it forreasoning.effort: "none". (#4359 — thanks @xz-dev) - fix(sse): default the combo per-target timeout to 120s for fast failover — a combo's per-target timeout inherited the full
FETCH_TIMEOUT_MS(600s default) when the combo didn't settargetTimeoutMs, so a single hung/slow target (e.g. an openai-compatible upstream returning 524/504) could stall the whole combo for up to 10 minutes before failing over. A newDEFAULT_COMBO_TARGET_TIMEOUT_MS = 120_000is used as the default-when-unset inresolveComboTargetTimeoutMs(backward-compatible 3rd arg, wired inphaseComboSetup); an explicit ceiling/opt-out is preserved. (#4365 — thanks @diegosouzapw) - fix(cli): Tailscale login honors
TAILSCALE_AUTHKEYfor non-interactive sign-in —startTailscaleLoginbuilttailscale upwithout ever readingprocess.env.TAILSCALE_AUTHKEY, so on a pre-authenticated / headless daemon the login waited for an interactive auth URL and timed out (~15s). WhenTAILSCALE_AUTHKEYis set it is now passed via--auth-key=(as a spawn argv element — no shell interpolation) so the daemon authenticates non-interactively; when unset, behavior is unchanged. (#4343 — thanks @ipeterpetrus) - fix(dashboard): OAuth modal shows the real error on a non-JSON server response — the OAuth connect/reauth modal called
await res.json()unconditionally, so when a build/OAuth endpoint returned a plain-text error (e.g. a500 Internal Server Errorpage) the modal threwUnexpected token 'I'…and hid the real failure. Two shared helpers (parseResponseBody/getErrorMessageinsrc/shared/utils/api.ts) now read the body safely (JSON when it is JSON, raw text otherwise) and surface a clean message either way; all modal fetch sites use them. (#4351 — thanks @DNNYF) - fix(dashboard): a disabled connection's last error is now visible — the provider card's error badge counts a disabled connection (
isActive === false) that has an error (its effective status is still error/expired/unavailable), but the connection row hid thelastErrortext for disabled rows — so the operator saw the error count without being able to see what failed. The row now shows the error text whenever there is one, regardless of the active toggle. (#4352 — thanks @ntdung6868) - fix(providers): the "Test Connection One-by-One" OAuth probe can no longer hang the queue forever — the OAuth connection-test path called bare
fetch(url, { method, headers })with noAbortController/signal/timeout, so when a provider's probe endpoint accepted the socket but never responded, the awaited fetch never settled and the one-by-one test queue stalled indefinitely (the API-key path was already bounded viavalidateProviderApiKey'stimeoutMs). Both the initial probe and the post-refresh retry are now bounded withAbortSignal.timeout(30s)— matching the API-key path's 30s budget — and a timed-out probe resolves as a failure with a clearTest timed out after 30smessage in the same shape as every other test error. (#4347 — thanks @ntdung6868) - fix(providers): a deactivated account is labeled distinctly from a revoked token — a Codex connection whose OAuth refresh is fully healthy but whose ChatGPT account has been deactivated by the provider gets a
401from the upstream API. The connection test labeled that the same as a bad credential (Token invalid or revoked→upstream_auth_error), so the operator couldn't tell a deactivated account from a revoked token. The test now reads the401/403body and, when it indicates account deactivation, classifies it asaccount_deactivated— which the dashboard already renders as "Account Deactivated". A plain auth401is unchanged. (#4353 — thanks @ntdung6868) - fix(db): cascade-delete orphaned model aliases when a provider is removed — deleting a custom provider removed its connections and node but left behind the imported model-alias rows (stored as
key=<alias>,value="<providerId>/<model>"). Those stale aliases then blocked re-importing the same provider — the import dedup treated them as "already exists", so no new models appeared. A newdeleteModelAliasesForProvider(providerId)DB helper drops every alias whose stored value begins with<providerId>/(leaving other providers and user-defined settings aliases untouched), and the provider-node DELETE handler now calls it after removing the connections and node, so a fresh import is unblocked. (#4348 — thanks @nguyenvanhuy0612) - fix(api): persist
max_input_tokens/max_output_tokenswhen adding a custom model —POST /api/provider-modelssilently dropped the per-model token limits set in the "add custom model" form: the handler destructured the rest of the body but never readmax_input_tokens/max_output_tokens, andaddCustomModel()had no parameter for them, so the values were thrown away on write. The DB layer (inputTokenLimit/outputTokenLimit) and the/v1/modelscatalog already round-trip these fields — only the write path was missing. The validation schema now accepts the two optional limits, the handler forwards them, andaddCustomModel()persists them so a custom model's context/output window survives into the catalog. (#4349 — thanks @codename-zen) - fix(plugin): the OpenCode static-catalog plugin prefixes combo/raw model keys with the provider id — OpenCode's static-catalog reader misdetected the
omnirouteprovider: combo keys emitted ascombo/MASTERwere parsed as providercombo("No credentials for provider: omniroute"), while a bare-MASTERform was misread as a model with no resolvable provider, and mixedomniroute/MASTER+ bare-raw keys were rejected by OpenCode's schema. The plugin now emits every combo and raw model key prefixed with theomnirouteprovider id, emits the provider id explicitly, and drops the legacycombo/prefix — so the static-catalog reader detects the provider and the auth loader returns the right credentials (the catalog-fetch timeout was also raised so a cold-start server doesn't publish an empty stub). (#4384 — thanks @herjarsa)
🔒 Security
- fix(security): scope the OAuth callback
postMessageto a trusted-origin allowlist — the OAuth callback at/callbackpreviously posted{ code, state, … }towindow.opener.postMessage(…, "*")whenever the opener was cross-origin, so a hostile page that opened the well-known redirect URI in a popup could receive the OAuth code/state and complete the flow as the user. The wildcard fallback is replaced with iteration over a fixed allowlist (same-origin + Codex'slocalhost:1455/127.0.0.1:1455loopback helper); the browser silently dropspostMessageto any opener whose origin isn't listed. (#4372 — ported from 9router#998, thanks @aeonframework / @diegosouzapw) - fix(mitm): exact host membership in the MITM hosts test (CodeQL false positive) —
tests/unit/mitm-tool-hosts.test.tschecked host membership withArray.includes(host), which CodeQL'sjs/incomplete-url-substring-sanitizationheuristic misreads as aString.includes()URL-substring sanitization test (HIGH false positive). Switched to.some((h) => h === host)— identical semantics, no flagged pattern. (#4386)
📝 Maintenance
- docs: one-time feature-documentation catch-up (v3.8.20 → v3.8.30) — reconciled the docs with every user-facing feature shipped since v3.8.20: a new README ✨ What's New section; new guides for CLI integrations, MITM TPROXY transparent decrypt and delegated Anthropic Context Editing; refreshed AUTO-COMBO (
auto/<category>:<tier>+ Arena-ELO), API_REFERENCE (x-omniroute-no-memory), MEMORY (int8 quantization, off-by-default), RESILIENCE (model-lockout success-decay), RTK, AGENTBRIDGE, TRAFFIC_INSPECTOR, GUARDRAILS, CLOUD_AGENT, ENVIRONMENT; regenerated PROVIDER_REFERENCE (231 providers) and synced the provider count in README/CLAUDE/AGENTS. Going forward this runs every release (generate-release step 6b). (#4391) - refactor(chatCore): extract the
checkHeapPressureGuardleaf (god-file decomposition start) — first increment of decomposingchatCore.ts(~5127 LOC, the hottest path — every chat request flows throughhandleChatCore). The V8 heap-pressure guard at the top ofhandleChatCore(rejects with 503 whenheapUsedexceeds the shed threshold) is moved to a self-contained, co-locatedutils/heapPressure.ts::checkHeapPressureGuard(...)with no behavior change. (#4371 — thanks @diegosouzapw) - refactor(combo): de-dup the exhausted-target skip predicate across both dispatchers — the byte-identical
#1731/#1731v2pre-check (skip a target already exhausted on the provider/connection within a request) lived in both combo dispatchers; extracted to a sharedcombo/comboPredicates.tshelper. (#4362 — thanks @diegosouzapw) - refactor(combo): de-dup the upstream-error exhaustion classification across both dispatchers — both dispatchers ran a near-identical post-error block classifying the upstream error and updating the exhaustion Sets (
#1731provider exhausted /#1731v2connection error / transient rate-limited); extracted to a sharedcombo/targetExhaustion.ts::applyComboTargetExhaustion(...). (#4366 — thanks @diegosouzapw) - chore(cli): localize CLI / scraping copy and stabilize fetch, memory & coverage handling — localizes CLI and scraping UX copy plus the Adapta onboarding tutorial (and corrects the CLI Code page title), makes fetch retries honor the start timeout, tightens SSE/response typing, respects configured memory token limits during search, and reduces CI coverage-merge memory by merging V8 data incrementally. (#4383 — thanks @JxnLexn)
- test(combo): reset circuit breakers between stream-readiness cases (restore green) — a stream-readiness fallback case failed on the release branch since the cycle-open tip due to test isolation: earlier combo-dispatch cases in the same file deliberately fail
glm(tripping the module-level provider circuit breaker), and that OPEN state leaked into the next test socombo.tsskipped the model. The test now resets the circuit breakers between cases. (#4396 — thanks @diegosouzapw) - chore(quality): reconcile the complexity ratchet baseline (1896 → 1900) — absorbs the small complexity-metric increase from the v3.8.31
/review-prsmerge batch intoquality-baseline.jsonso the ratchet reflects the shipped code (no production change). (#4410 — thanks @diegosouzapw) - test/gate: reconcile release-time drift surfaced by the full CI gate — three already-merged changes left the release branch's full-CI gate red (the per-PR fast gates don't run it): the Gemini
convertOpenAIContentToPartstests were realigned to the #4373 HTTP/HTTPS-URLfileDatapass-through (they still asserted the old warn-and-drop behavior), thet11any-budget foropen-sse/executors/base.tswas raised to 2 with a justification (#4389 comparestool_choiceagainst the string literal"any", not a TSanytype), and the #4384 opencode-plugin combos test's net-assert reduction (dropping the obsoletecombo/namespace) was allowlisted. No production behavior change. (thanks @diegosouzapw)
[3.8.30] — 2026-06-20
✨ New Features
- feat(dashboard): category (media serviceKind) filter on the providers page —
/dashboard/providersgains a media-category filter row (Image / Video / Music / Text→Speech / Speech→Text / Embedding) that composes with the existing search, free-only and "show configured only" filters. Membership is derived from the backend media registries (a provider that serves a kind is surfaced even if it never declaredserviceKinds), keeping the UI in lockstep with the backend. (#4240) - feat(combo): per-step account allowlist — scope a round-robin/weighted step to a subset of a provider's connections — a combo model step can now carry a first-class account allowlist so a round-robin (or weighted) strategy is scoped to a chosen subset of a provider's connections (e.g. only
foo1+foo2out offoo1..foo4) without hand-pinning one step per account. Empty = the whole active pool (unchanged). When a step both has an allowlist and is tag-routed, the two intersect (most-restrictive wins); a single pinned account still takes precedence. The combo builder's Precision step editor gains an optional "Restrict to accounts" picker. (#3266) - feat(providers): add OpenAdapter, dit.ai and TokenRouter as OpenAI-compatible providers — three community-requested OpenAI-compatible aggregators now register as standard named OpenAI-style providers with live
/v1/modelsdiscovery (the zenmux pattern), falling back to a seeded catalog when the upstream list is unavailable: OpenAdapter (https://api.openadapter.in/v1, free tier, 70+ open-source models — #4239), dit.ai (https://api.dit.ai/v1, dynamic-pricing router/gateway — #4155), and TokenRouter (https://api.tokenrouter.com/v1, free MiniMax model — #3841, thanks @FerLuisxd). No custom executor/translator — default OpenAI passthrough. - feat(api):
x-omniroute-no-memoryrequest header — per-request opt-out of memory/skills injection — clients that manage their own context (e.g. their own RAG/memory) can sendx-omniroute-no-memory: true(mirrors the existingx-omniroute-no-cacheconvention) to skip the gateway injecting up tomemorySettings.maxTokens(~2k) tokens of memory and skills context into that chat request — avoiding the token/cost inflation it otherwise adds on every call. Absent the header, behavior is unchanged. (PRD-2026-06-19-no-memory-header) - feat(dashboard): MITM tool card lists the exact hosts-file entries to add manually — the CLI-tools MITM card's "How it works" section now lists the full set of
127.0.0.1 <host>lines for the selected tool (sourced from the canonical MITM target registry) instead of a single example domain. Users on locked-down machines — where the automatic, sudo-gated hosts-file edit isn't available — can now copy every required entry by hand. (thanks @mrcyclo) - feat(cli):
omniroute launch-codex+setup-codex— run/configure the Codex CLI against OmniRoute — a launcher and setup command that point the Codex CLI at an OmniRoute endpoint (remote-mode aware). (#4270) - feat(cli): Claude Code launcher + setup — remote mode + profiles —
omniroute launch/setupfor Claude Code with remote-mode support and named connection profiles. (#4274) - feat(cli): OpenCode setup — OpenAI-compatible provider + remote-aware plugin —
setup-opencoderegisters OmniRoute as an OpenAI-compatible provider for OpenCode and installs a remote-aware plugin. (#4277) - feat(cli): one-command setup for popular AI coding tools — new
setup-*commands that configure each tool to talk to OmniRoute: Cline (#4280), Kilo Code (#4284), Continue (#4289), Cursor (#4291), Roo Code (#4292), Crush (#4298), Goose (#4300), Qwen Code (#4301), Aider (#4302) and the Gemini CLI (native/v1beta) (#4303). - feat(providers): provider model sweep — live discovery, refreshed catalogs, dead-provider cleanup — a broad sweep that enables live
/v1/modelsdiscovery for more OpenAI-style providers (the zenmux pattern), refreshes the seeded catalogs with current models, and marks dead providersdeprecated. (#4324) - feat(mitm): translate Antigravity cloudcode end-to-end (Gap B) — the MITM decrypt path now translates Antigravity
cloudcodetraffic end-to-end. (#4299) - feat(keys): per-key USD usage quota controls — an API key can now carry a USD spend quota that caps its usage once the threshold is reached. (#4327 — thanks @Witroch4)
🔧 Changed
- change(memory): memory is now OFF by default —
DEFAULT_MEMORY_SETTINGS.enablednow defaults tofalse. Enabling memory injects up to ~2,000 tokens of retrieved context into every chat request (and that context is billed), which was a surprising default for new installs and for clients with their own context. Memory is now an explicit opt-in: installs that already enabled it keep it on; installs that never configured it default to off. The Settings → Memory panel now shows a token-cost warning when memory is enabled. (PRD-2026-06-19-no-memory-header)
🐛 Fixed
- fix(translator): Gemini accepts HTTP/HTTPS image URLs (no longer silently dropped) — OpenAI-style
image_urlparts whose URL washttp://…orhttps://…reachedconvertOpenAIContentToParts(the OpenAI→Gemini request helper) and were dropped with only aconsole.warn, because Gemini'sinlineDatarequires base64 and the helper is synchronous (it cannot fetch + encode). Gemini'sPartschema, however, natively acceptsfileData: { fileUri }for remote URIs — the model fetches the asset itself. The helper now emits afileDatapart (mimeType: "image/*", inferred upstream on fetch) instead of dropping, so vision requests that pass a URL — not a data: URI — now reach Gemini intact.data:URIs still go throughinlineDataunchanged; unsupported schemes (e.g.ftp:) are still skipped. (thanks @East-rayyy) - fix(security): OAuth callback page no longer relays
code/stateto a wildcardpostMessagetarget — the OAuth callback at/callbackposted{ code, state, ... }towindow.opener.postMessage(..., "*")whenever the opener was cross-origin (a fallback for the legitimate remote-dashboard + local-loopback callback scenario). A hostile page that opened the callback URL in a popup against the well-known redirect URI would therefore receive the OAuth code+state and could complete the OAuth flow as the user. The wildcard fallback is replaced with an iteration over a fixed allowlist of trusted target origins (same-origin + Codex's loopback helper atlocalhost:1455/127.0.0.1:1455); the browser silently drops the message for any opener whose origin is not in the list. Methods 2 (BroadcastChannel) and 3 (localStorage) — already in the page — still cover same-origin parents when the opener was severed by COOP. (thanks @aeonframework) - fix(compliance): startup cleanup honors the dashboard data-retention setting instead of always trimming to 7 days — on every restart,
cleanupExpiredLogs()(run at startup) read retention only from theCALL_LOG_RETENTION_DAYS/APP_LOG_RETENTION_DAYSenv vars, which default to 7 days when unset, and trimmedusage_history(the Usage Analysis data) before the dashboard-basedrunAutoCleanup()— which respects the configured retention — ever ran. So a dashboard "Data Retention" of 90 days was silently overridden and the Usage Analysis page only ever showed the last 7 days after a restart. Retention now follows the precedence explicit env var → dashboard DB setting → 7-day default, per table (usage_history→usageHistory,call_logs/proxy_logs/request_detail_logs→callLogs,mcp_tool_audit→mcpAudit); an operator who sets the env var still wins, and non-DB deployments still fall back to it. (#4354 — thanks @akbardwi) - fix(providers): bailian-coding-plan static fallback catalog matches the registry (10 models) — the provider-model sweep (#4324) added four current Model Studio coding-plan models (
qwen3.7-plus,qwen3-coder-plus,qwen3-coder-next,glm-4.7) to thebailian-coding-planregistry entry but missed the static fallback mirror instaticModels.ts, which still listed only the older six. The static catalog (served when live discovery is unavailable) therefore diverged from the registry, and the existing static↔registry parity test went red on the release branch (only surfacing when test-impact analysis happened to select it). The static mirror now carries all ten models in registry order, restoring parity. (#4324) - fix(executors): ArenaLLM accepts LMArena's split Supabase SSR auth cookie — LMArena migrated to
@supabase/ssrchunked auth cookies: the singlearena-auth-prod-v1cookie is now empty and the real session is split acrossarena-auth-prod-v1.0,arena-auth-prod-v1.1, … (ascending). A user who pasted the (now-empty) single cookie therefore sent an empty session and upstream rejected it as "invalid cookie". The LMArena executor now reconstructs the single cookie from its chunks — reading.0,.1, … in ascending numeric order until one is missing and concatenating their raw values (@supabase/ssr'scombineChunksrule: plainjoin(""), no base64-decode, no JSON-parse, thebase64-prefix kept verbatim) — while preserving the rest of the pasted jar. A non-empty single cookie is still forwarded unchanged (back-compat). The credential UX now instructs pasting the full Cookie header and tracks the.0/.1storage keys. (#4271 — thanks @caussao) - fix(compression): preserve the cacheable prefix for automatic-cache providers — OpenAI / Codex (and Azure-OpenAI) use automatic prefix caching: the upstream caches the longest matching prefix of a request (system prompt + earliest messages) without any explicit
cache_controlmarkers in the body. The cache-aware compression guard only protected that prefix when the request carried explicitcache_control, so for automatic-cache providers the guard was skipped — and with compression enabled andpreserveSystemPrompt: false(or a prefix-compressing mode likeaggressive/ultra) it rewrote the system prompt / earliest messages, guaranteeing a cache miss and higher token spend through OmniRoute than going direct. The guard now treats a caching provider as sufficient on its own (isCachingProvideralone, independent ofcache_control) to skip the system prompt and downgrade prefix-compressing modes, and OpenAI/Codex/Azure are now recognized as caching providers. Compression is still off by default — this only affects operators who enabled it with prefix preservation turned off. (#3955) - fix(executors): DuckDuckGo AI Chat uses duckduckgo.com (fixes 400) — the DuckDuckGo AI Chat executor fetched status/chat and set
Origin/Refereragainsthttps://duck.aiwhile still sendingSec-Fetch-Site: same-origin, so the request's same-origin triplet (host + Origin + Referer) was inconsistent and the backend rejected it with HTTP 400. All current DDG reverse-engineering references — and the provider registry's ownbaseUrl— usehttps://duckduckgo.com; the executor now uses it consistently for the status URL, chat URL,Origin, andReferer(the same-origin header is now coherent). Thex-fe-versionscrape regex also required a 40-hex tail but the real served token has a 20-hex tail (e.g.serp_20250401_100419_ET-19d438eb199b2bf7c300), so it silently fell back to a hardcoded default; the pattern is relaxed to a bounded{20,40}tail (still ReDoS-safe). This addresses the DuckDuckGo half of the report; the separate Chipotle/chipotleupstream breakage is tracked independently. (#4037 — thanks @daniij) - fix(security): bound the prompt-injection scan to the first 16 KB (hot-path perf) — the prompt-injection guard joined every message/system string into one buffer and ran several regexes over the whole thing on every chat request, with no size cap — so a 300 KB body (pasted code, RAG context) meant O(body) CPU scanning on the hot path, a self-inflicted latency/GC source under concurrency. Both detection call sites (
detectInjectionininputSanitizer.tsand the custom-pattern scan inpromptInjection.ts) now slice the joined text to the first 16 KB (MAX_INJECTION_SCAN_BYTES) before the regex loop. Injection directives sit near the top of a prompt, so the generous cap preserves real detection while scanning only a bounded prefix; the existing 10 MB body-size cap (which protects ingestion) is unchanged. (#3932 — thanks @KooshaPari) - fix(sse): retry direct-connection socket failures on a fresh socket (fewer
502bursts) — the default direct-connection undici dispatcher pools keep-alive sockets for up to 4 s, but some edges (e.g.nvidia,opencode-zen) silently close idle keep-alive sockets within that window, so the next request reusing a pooled socket fails withUND_ERR_SOCKET("other side closed") — in bursts.proxyFetchalready retried once on such transient errors, but the retry reused the same pooled dispatcher and could grab another stale socket, then fell through to native fetch (which also pools) → the job sat in the rate-limit queue until the 30 s timeout →502+ circuit-breaker open. The retry now uses a dedicated no-keep-alive / no-pipelining dispatcher so it opens a brand-new socket that can't be a dead pooled one; the first attempt still uses the pooled dispatcher (healthy keep-alive reuse is preserved). Complements the v3.8.29 diagnostics (describeFetchCause, #4281). (#4252 — thanks @klimadev) - fix(sse): combo now stops at the first body-specific 400 instead of trying every target — the
#2101guard that detects a body-specific 400 (context overflow / malformed / model-access-denied, e.g. "model is not supported when using Codex with a ChatGPT account") logged "stopping combo" but executed a barebreak, which only exited the inner retry loop;executeTargetthen returnednulland the outer target loop treated that as "this target produced nothing" and advanced to the next model. A combo of N targets that all reject the same request body therefore marched through all N (the report shows a 143-model Codex combo iterating every target), wasting upstream calls and per-attempt work. The guard now surfaces the 400 via the{ ok, response }contract (mirroring the 499 client-disconnect path) so the combo resolves and stops immediately. (#4279) - fix(sse): non-streaming combo over a Responses-API target no longer returns empty content — a Responses-API target (codex/
cx) streams from upstream even onstream:false, and its terminalresponse.completedsnapshot can carry a non-emptyoutputthat lacks the assistant message item (e.g. only areasoningitem) while the streamedoutput_textdeltas had reconstructed the full message. The SSE→JSON aggregator preferred the terminaloutputwholesale, dropping the reconstructed text → HTTP 200 with empty content (hit notably via n8n, which defaults tostream:false). The aggregator now falls back to the reconstructed delta output when the terminal output has no message item but the reconstruction does; the terminal snapshot still wins whenever it already carries the message. (#3948) - fix(executors): preserve tool-name casing on native Claude OAuth (
readno longer leaks back asRead) — native Claude OAuth traffic runs through an anti-fingerprint tool-name cloak that renames a tool literally namedreadtoReadon the wire and records the reverse alias on a non-enumerable_toolNameMap, which the response side uses to restore the client's original casing. Since v3.8.27 the executor returned a JSON-round-tripped copy of the body astransformedBody, and that round-trip dropped the non-enumerable map — so the restore saw an empty map and the cloakedReadstreamed verbatim to the client, corrupting the tool name. The executor now re-attaches the cloak map onto the serialized body (mirroring the Antigravity executor), so tool-name casing round-trips correctly. (#4307 — thanks @dev-cj) - fix(api): cache-HIT
X-OmniRoute-Response-Costnow reports the incremental cost (≈0), not the original — on a semantic-cache HIT the gateway serves the stored response without an upstream call, butX-OmniRoute-Response-Costwas reporting the original call's full cost (recomputed from the cachedusage). A consumer summingresponse-costfor billing was therefore charging for responses that cost ≈$0 to serve (and stale entries could inflate it). Cache hits now billX-OmniRoute-Response-Cost: 0.0000000000(the real incremental cost), and the avoided cost is surfaced in a newX-OmniRoute-Cost-Savedheader for cache analytics — mirroring the existingtokens_savedconcept. The MISS path is unchanged. (PRD-2026-06-19-cache-hit-cost-reporting) - fix(models): imported vision-capable models keep their vision capability — after importing a provider key, vision-capable models (e.g. OpenRouter models whose
architecturedeclares image input, and other synced providers) were listed as text-only in/v1/modelsand the dashboard — even though image requests actually worked. Synced model records never captured the vision flag, and the catalog's OpenRouter live-enrichment (which derives vision fromarchitecture.input_modalities) is skipped once a provider has synced models. Discovery now capturessupportsVisionat sync time (fromarchitecture.input_modalities, the stringarchitecture.modality, or a top-levelinput_modalities), mirroring the existingsupportsThinkingcapture, and the catalog surfacescapabilities.visionfor synced models. (#4264 — thanks @FerLuisxd) - fix(providers): Cloudflare Workers AI model discovery shows model names, not UUIDs — importing a Cloudflare Workers AI key listed models with internal UUID identifiers (e.g.
429b9e8b-d99e-…) instead of their usable slugs (@cf/meta/llama-3.1-8b-instruct). Cloudflare's/ai/models/searchreturns{ id: "<uuid>", name: "@cf/…" }, and discovery was passing the raw objects through — so the UUIDidbecame the callable model id. Thecloudflare-aidiscovery now maps each result'sname→ id, surfacing the real@cf/…model ids. (#4259 — thanks @FerLuisxd) - fix(translator): clamp Responses API
call_idto 64 characters — the OpenAI Responses API rejectscall_idvalues longer than 64 characters with a 400. Long upstream tool-call ids (some clients emit ids well over the limit) are now clamped deterministically on both thefunction_callitem and its matchingfunction_call_output, so the pair stays matched through the orphaned-output filter and the request is accepted. (thanks @anuragg-saxenaa, @ngapngap) - fix(oauth): GitHub Copilot token refresh now sends the public client_id — the
githubprovider config never carried aclientId, so GitHub OAuthrefresh_tokenexchanges either omittedclient_idor sent the literal stringundefined(and a bogusclient_secret=undefined), which GitHub rejects — leaving a Copilot connection stuck once its short-lived token expired and the long-lived refresh path was needed. The provider now resolves its public device-flowclient_idfrom the embedded public credential and omitsclient_secretentirely (GitHub's Copilot app is a public client with no secret). (thanks @baslr) - fix(translator): a tool property named
patternsurvives Gemini/Antigravity schema sanitization — the Gemini schema sanitizer strips JSON-Schema constraint keywords Gemini rejects (pattern,minLength, …) at every nesting level, but it also deleted any tool property literally named one of those keywords. glob/grep tools declare a property calledpattern, so onag/*(Antigravity) backends that argument (and itsrequiredentry) was silently dropped, breaking the tools. Keyword stripping is now position-aware: it only removes constraint keywords at the schema-node level and never against the user-defined names inside apropertiesmap. A genuine string-levelpatternconstraint is still stripped. (thanks @youthanh) - fix(translator): MCP
namespacetools flatten to individual functions on the Responses→Chat path — when a Codex CLI client routes a Responses-API request to a non-Codex backend (e.g.kr/claude-opus-4.7), each MCP server is declared as anamespacetool ({ type:"namespace", name, tools:[…] }). The Responses→Chat translator had nonamespacebranch, so the whole group collapsed into a single empty-schema function namedmcp__<server>__and every MCP call returnedunsupported call: mcp__<server>__, breaking all MCP-based workflows (context7, codegraph, custom MCPs) for that combination. The translator now expands a namespace into one Chat function per sub-tool (preserving each sub-tool's name and parameters); an empty namespace yields no tools instead of a broken placeholder. The native Codex passthrough path was already correct. (thanks @V13t4nh) - fix(cli): the active remote-context credential wins over an ambient
OMNIROUTE_API_KEY— when a remote context is selected, its scoped access token now takes precedence over anOMNIROUTE_API_KEYpresent in the environment, so the connected remote is targeted as expected. (#4364) - fix(cli): wire the
contextscommand into the CLI program — theomniroute contextscommand (list/switch saved remote contexts) was implemented but never registered, so it was unreachable; it is now wired into the CLI program. (#4369) - fix(mitm): mask bare
Bearer <token>header values in the Traffic Inspector — the inspector now redacts bareAuthorization: Bearer …values so tokens don't leak into captured traffic. (#4358) - fix(pricing): price the
gpt-5.x-proOpenAI models + align the opencode-go discovery test — adds pricing for the gpt-5.x-pro models so cost telemetry reports a real cost instead of zero. (#4355) - fix(sse): release the reader and cancel the stream on abort/error (no more Undici pool socket leak) — on abort or a mid-stream error the response reader is released and the stream cancelled, preventing leaked pooled sockets that degraded later requests. (#4309 — thanks @Ardem2025)
- fix(kiro): emit an early role-only start chunk to release the stream-readiness gate — Kiro streams now send an initial role-only chunk so the stream-readiness gate releases promptly instead of stalling. (#4311 — thanks @artickc)
- fix(dashboard): the proxy modal stops pre-filling new scopes with an unrelated proxy — adding a new scope assignment no longer inherits a previously-selected proxy's configuration. (#4312)
- fix(open-sse): inner-ai stops silently rerouting unmatched models to
models[0]— an unmatched model id is no longer silently served by the first available model; the lookup now returns null and the request is handled explicitly. (#4310) - fix(pollinations): handle auth-required premium models (claude, gemini, midjourney) — premium Pollinations models that require authentication are now handled correctly instead of failing. (#4266 — thanks @oyi77)
- fix(codex): isolate the Spark quota scope — Codex Spark usage is tracked under its own quota scope so it no longer bleeds into other Codex quotas. (#4293 — thanks @xz-dev)
- fix(dashboard): improve the API "try it" functionality — fixes the request path used by the dashboard's API "try it" panel. (#4296 — thanks @edrickrenan)
- fix: polyfill
crypto.randomUUIDfor non-secure contexts — restores UUID generation when the dashboard is served over a non-secure (plain-HTTP) origin wherecrypto.randomUUIDis unavailable. (#4287 — thanks @pizzav-xyz) - fix(proxy): allow concurrent proxy dispatcher streams — the proxy dispatcher no longer serializes streams, so concurrent requests through a proxied connection run in parallel. (#4288 — thanks @wilsonicdev)
- fix(build): co-locate llmlingua SLM optionals into
dist/node_modules(postinstall) — the optional llmlingua SLM packages are co-located into the standalone build so the compression worker can actually spawn in production. (#4286) - fix(mitm): surface AgentBridge traffic in the Traffic Inspector (D4 ingest) — AgentBridge requests now appear in the Traffic Inspector. (#4285)
- fix(sse): surface undici
err.causeon dispatcher failure — dispatcher failures now flatten the cause chain (andAggregateErrors) into the error detail for diagnosability. (#4281) - fix(cli): harden
launch/launch-codexwith free-claude-code patterns — the launchers adopt the hardened launch patterns ported from free-claude-code. (#4278) - fix(compression): end-to-end audit — fixes across the whole compression flow — a sweep of the compression pipeline fixing ultra/aggressive/lossless edge cases, accessibility-anchor handling, language detection, and mode decoupling. (#4323)
🧪 Tests
- test: align two tests left red by merged PRs — re-aligns the db-rules classification count (#4335) and the LMArena split-cookie metadata test (#4271) after concurrent merges. (#4346)
- test(ci): reconcile the release/v3.8.30 baseline + test drift — reconciles quality baselines and drifted tests accumulated on the release branch. (#4276)
📝 Maintenance
- refactor(combo):
ComboContext+ extractphaseComboSetup(god-file split, phase 1) — begins decomposing the combo god-file by extracting combo setup into a context object, without touching dispatch/semaphore logic. (#4326) - feat(quality): cap test-file size — anti-reinflation Layer 1 — freezes the existing god-tests and caps new test files at 800 lines to stop re-inflation. (#4273)
- feat(quality): seed per-module mutationScore floors + a blocking aggregation ratchet (T3) — adds per-module mutation-score floors with a blocking aggregate gate. (#4305)
- feat(quality): make the a11y gate real (
@axe-core/playwrightin nightly) — wires the previously-phantom accessibility gate into the nightly run with real baselines. (#4321) - feat(quality): unblock R1 — test-redundancy measurement via
disableBail— enables the test-redundancy measurement that was previously blocked by fail-fast. (#4322) - fix(quality): the complexity gate now covers
bin/+electron/, and tracked-artifacts runs in pre-commit — extends the complexity gate's scope and moves the tracked-artifacts check into the pre-commit hook. (#4318) - fix(quality): restore release/v3.8.30 green — 3 latent reds from concurrent merges — fixes three latent test reds surfaced by concurrent merges into the release branch. (#4335)
- fix(combo): keep
phaseComboSetupunder the complexity ceiling — extracts a helper so the new combo setup phase stays under the complexity gate. (#4338) - ci(mutation): split over-budget batches by range/pair so every batch fits the job cap — re-splits the mutation batches so each fits the CI job budget. (#4272)
- chore(ci): align the electron audit gate to the root advisory policy — the electron-workspace audit gate now follows the same advisory policy as the root. (#4275)
- chore(quality): reconcile the complexity/quality baselines across concurrent-merge drift — rolls up the cycle's baseline reconciliations driven by concurrent merges into the release branch. (#4330, #4336, #4370)
- docs: ban AI-generation footers in commits/PRs/CHANGELOG (Hard Rule #16) — codifies the prohibition on AI-generation footers and bot co-author trailers. (#4328)
- docs(design): add the OmniRoute design system and visual identity specification — adds the design-system / visual-identity specification document. (thanks @diegosouzapw)
🔒 Security
- fix(sse): harden the DuckDuckGo lite scraper sanitization (CodeQL) — closes four HIGH CodeQL alerts in the no-key web-search scraper:
decodeEntitiesnow resolves&last so an already-escaped entity (e.g.&lt;) survives as literal text instead of being double-unescaped (js/double-escaping);stripTagsdecodes entities first, then strips tags in a loop to a fixpoint and drops any trailing unclosed<…, so entity-encoded markup like<script>can never reach the LLM/client as a live tag (js/incomplete-multi-character-sanitization); and the host checks in the search tests usenew URL().hostnameequality instead of substring.includes(js/incomplete-url-substring-sanitization). (#4356)
🔧 Dependencies
- fix(deps): bump undici to 7.28.0 and dompurify to 3.4.11 (security) — addresses the undici SOCKS5-TLS / cache advisories and the dompurify advisory. (#4306)
- chore(deps): bump actions/checkout from 4 to 7 — CI checkout-action update. (#4297)
- fix(executors): strip
stream_optionsfor qwen non-streaming / thinking-mode Claude Code requests — Claude-Code-compatible providers force the executor-levelstreamflag on viaupstreamStream = stream || isClaudeCodeCompatible(open-sse/handlers/chatCore.ts), but the outgoing body keeps the caller's originalstream: false. The sharedstream && targetFormat === "openai"branch inDefaultExecutor.transformRequestthen injectedstream_options: { include_usage: true }onto a body that still saidstream: false, and qwen upstream rejected it with400 "'stream_options' only set this when you set stream: true". Same rejection when the body carriesthinking/enable_thinking. The qwen branch now skips the injection (and strips any client-sentstream_options) when the body explicitly saysstream: falseor requests thinking, leaving regular qwen streaming requests with the usage injection intact. (thanks @anuragg-saxenaa)
[3.8.29] — 2026-06-19
✨ New Features
- feat(cloud-agent): Cursor Cloud Agent via the official API-key REST API (no IDE-OAuth ban risk) — adds a
cursor-cloudcloud agent that drives Cursor's Background / Cloud Agents through the official REST API (api.cursor.com) authenticated with a user or service-account API key — the safer, first-party alternative to re-using the Cursor IDE's OAuth session (the existingcursorprovider, which carries a ban-risk warning). Implemented as a plain REST adapter mirroring the Devin/Jules agents (createTask/getStatus/sendMessage/listSources), so it does not pull in the@cursor/sdkpackage and its per-platform native binaries (Cursor's SDK is itself a thin wrapper over this REST API). Cursor's UPPERCASE status enums (CREATING/RUNNING/FINISHED/ERROR) are mapped explicitly to the sharedCloudAgentStatus, andbaseUrlis overridable per-credential. Credentials are stored encrypted via the existingcloud_agent_credentialstable; no schema change. (#4227 — thanks @MRDGH2821) - feat(routing): OpenRouter-style
auto/<category>:<tier>combos — auto-routing now understands suffixed combos that separate the category (what kind of route) from the tier (how to optimize):auto/coding:fast,auto/coding:cheap(alias:floor),auto/coding:free,auto/coding:pro,auto/coding:reliable, plus the new category rootsauto/reasoning,auto/vision,auto/multimodal. The tier picks the scoring weights —:fast→ ship-fast,:cheap/:floor→ cost-saver,:reliable→ a new reliability-first pack (circuit-breaker health + latency stability) — while:free/:profilter the candidate pool by model tier (classifyTier: free-tier vs. premium models). The category filters the pool by capability (vision/multimodal→ vision-capable models,reasoning→ reasoning/thinking models). Any validauto/<category>:<tier>resolves on demand; a curated set is advertised in/v1/modelsand the dashboard. Filtering is fail-open — if a constraint matches no connected models the full pool is used so routing never breaks. All composition lives in the newopen-sse/services/autoCombo/suffixComposition.ts; the core combo scorer (combo.ts) is untouched. Second slice of #4235 (premium account-tier weighting is a later follow-up). (#4235 — thanks @MRDGH2821) - feat(routing): advertise the
auto/cheap,auto/offline,auto/smartcombos (catalog ↔ README sync) — the README listsauto/cheap(cheapest-per-token first),auto/offline(most quota/rate-limit headroom first) andauto/smart(quality-first + 10% exploration), and they already resolved at request time viaparseAutoPrefix→createVirtualAutoCombo. But they were missing fromAUTO_TEMPLATE_VARIANTS, so/v1/modelsand the dashboard combos list (which iterate that catalog) never showed them — the catalog drifted from the docs (visible in the issue's screenshots). Added the three entries so they're advertised everywhere alongside the other built-inauto/*combos. First slice of #4235 (OpenRouter-styleauto/<category>:<tier>suffixes + new categories follow). (#4235 — thanks @MRDGH2821) - feat(cli): remote mode — drive a remote OmniRoute with scoped access tokens — a new CLI mode that connects to a remote OmniRoute instance using scoped access tokens, so a local CLI can drive a server you don't own a session on. (#4256)
- feat(api): cost-telemetry parity —
X-OmniRoute-*headers on every endpoint + a non-token cost engine — every endpoint now emits theX-OmniRoute-*cost/usage headers, backed by a cost engine that also prices non-token (media/request-based) usage. (#4247) - feat(api): register Kimi K2.7 Code models (
kimi-k2.7-code+-highspeed) — the new Moonshot thinking-only coding models are registered (fixed sampling;temperature/top_pmarked unsupported). (#4183) - feat(catalog): add
kimi-k2.7-codeto the kmca catalog + qwen-web models discovery — surfaces the new Kimi coding model in the kmca catalog and wires qwen-web into model discovery. (#4185) - feat(api): expand the
zaiprovider catalog with GLM-5.2 / GLM-4.7 — adds the real GLM-5.2, GLM-4.7 and GLM-4.7-flash model ids to the Anthropic-directzaiprovider. (#4201) - feat(api): no-thinking gateway model IDs (FCC port, Fase 8.1) — gateway model id variants that force thinking off, ported from free-claude-code. (#4145)
- feat(sse): mid-stream continuation for truncated streams (FCC port, Task 4.4) — when a stream is cut short, OmniRoute can transparently continue it, ported from free-claude-code. (#4147)
- feat(sse): per-provider sliding-window rate-limit fallback (FCC port, Fase 8.2) — a per-provider sliding-window rate limiter as a fallback path, ported from free-claude-code. (#4146)
- feat(sse): transparent stream recovery (FCC port, Fase 4, opt-in) — opt-in transparent recovery of interrupted upstream streams, ported from free-claude-code. (#4131)
- feat(search): free DuckDuckGo web search as a last-resort provider (FCC port, Fase 6) — adds a no-key DuckDuckGo web-search provider used as a last resort, ported from free-claude-code. (#4136)
- feat(logging): credential-redaction safety net in the pino logger (FCC port, Fase 8.3) — a logger-level redaction pass that scrubs credentials from log output, ported from free-claude-code. (#4140)
- feat(memory): opt-in Qdrant scalar int8 quantization (F4.4 Q1) — opt-in int8 scalar quantization for Qdrant-backed memory vectors. (#4187)
- feat(memory): opt-in sqlite-vec int8 vector quantization (F4.4 Q2) — opt-in int8 quantization for the sqlite-vec memory backend. (#4190)
- feat(deploy): keep optional deps on
update(--include=optional) — the in-place update path now passes--include=optionalso native/optional packages aren't dropped on update. (#4260) - feat(dashboard): unified visual identity — grid, primitives, tables, form controls (design phases 1-4) — a sweeping design pass aligning the dashboard with the site: grid wallpaper, button/card/input primitives, theme-aware tables and form controls. (#4122)
- feat(dashboard): grid wallpaper on all standalone screens + fluid 4K layout — the identity grid now backs every standalone screen and the layout scales fluidly to 4K. (#4158)
- feat(dashboard): make the identity grid visible + unify the focus ring on accent — design follow-up making the grid actually visible and standardizing focus rings on the accent color. (#4141)
- feat(dashboard): import only free models + free-model list controls — the model-import page can import just the free models, with controls to manage the free-model list. (#4176 — thanks @felipesartori)
- feat(dashboard): compact grid layout for no-auth provider accounts — a denser grid layout for provider accounts when auth is disabled. (#4137 — thanks @felipesartori)
- feat(dashboard): derive media
serviceKindsfrom the registries (surface MiniMax + the media catalog) —/media-providers/[kind]now derives its service kinds from the registries instead of a hand-maintained list, surfacing ~48 previously-invisible media providers (incl. MiniMax TTS/video/music). (#4212) - feat(traffic-inspector): live (in-flight) request filter (Gap 5) — the Traffic Inspector can filter to in-flight requests as they happen. (#4130)
- feat(agent-bridge): maintenance & diagnostics dashboard controls — adds maintenance and diagnostics controls for the Agent Bridge to the dashboard. (#4127)
- feat(mitm): TPROXY IP_TRANSPARENT native addon + conditional loader (Epic A) — a native
IP_TRANSPARENTaddon with a conditional loader, the foundation for TPROXY capture. (#4148) - feat(mitm): Fase 3 Epic A spike — TPROXY command builder — a transactional builder for the iptables/TPROXY command set. (#4139)
- feat(mitm): TPROXY setup layer — transactional apply/revert (Epic A) — applies and reverts the TPROXY routing setup transactionally. (#4144)
- feat(mitm): add
setSocketMarkto the TPROXY addon (anti-loop primitive) — exposessetSocketMarkso OmniRoute's own egress can be marked and skipped (anti-loop). (#4160) - feat(mitm): TPROXY capture-mode listener +
connectMarked(Epic A) — the capture-mode listener plus a marked-connect primitive. (#4169) - feat(mitm): dynamic per-SNI cert authority for TPROXY (TLS decrypt 1/N) — a per-SNI on-the-fly certificate authority, the first slice of TLS decrypt. (#4173)
- feat(mitm): TLS-terminating capture for TPROXY (decrypt 2/N) — terminates TLS to capture decrypted traffic. (#4179)
- feat(mitm): wire the TLS decrypt engine into TPROXY capture mode (decrypt 3/N) — connects the decrypt engine to the capture-mode pipeline. (#4200)
- feat(mitm): TPROXY capture-mode manager (decrypt 4a/N) — a manager coordinating the TPROXY capture lifecycle. (#4208)
- feat(mitm): local-only route + trust-store installer for TPROXY decrypt (4b/N) — a loopback-only management route plus a CA trust-store installer for the decrypt CA. (#4211)
- feat(dashboard): TPROXY decrypt capture toggle in the Traffic Inspector (4c/N) — a UI toggle to enable/disable decrypted capture. (#4216)
- feat(compression): replace the headroom tabular encoder with a vendored GCF — swaps the tabular encoder for a vendored GCF implementation. (#4167 — thanks @blackwell-systems)
- feat(compression): live per-engine streaming via
compression.step(F3.3) — streams per-engine compression progress through acompression.stepevent. (#4217) - feat(compression): show an engine node for single-engine runs in the studio — the Compression Studio now renders an engine node even when only one engine runs. (#4210)
- feat(compression): expose the WaterfallInspector via a Canvas/Waterfall toggle — adds a Canvas/Waterfall view toggle that surfaces the WaterfallInspector. (#4238)
- feat(compression): make
mcpAccessibilityconfig reachable via a settings sub-route — exposes themcpAccessibilityconfig under a dedicated settings sub-route. (#4237) - feat(compression): runnable A/B benchmark CLI (F2.4) — a CLI to run A/B compression benchmarks. (#4220)
- feat(compression): add a transcript loader to the replay harness — the replay harness can now load real transcripts. (#4246)
- feat(compression): wire MCP tool-cardinality reduction (F4.3, opt-in) — opt-in reduction of MCP tool-set cardinality to shrink prompts. (#4221)
- feat(compression): wire RTK comment-stripping config + honor
preserveDocstrings— RTK comment-stripping is now configurable and honors apreserveDocstringsflag. (#4242) - feat(compression): honor the per-filter RTK
deduplicateflag — RTK filters now respect a per-filterdeduplicateflag. (#4231) - feat(compression): honor the registry
enabledflag in the stacked loop — the stacked compression loop now skips engines disabled in the registry. (#4244) - feat(compression): persist RTK grouping config (unlock R5
enableGrouping) — persists the RTK grouping configuration, unlocking the R5enableGroupingrule. (#4207) - feat(compression): wire ultra's
modelPath/slmFallbackToAggressiveto the LLMLingua SLM tier — connects the ultra tier's small-language-model knobs to the LLMLingua SLM path. (#4257) - feat(quality): Onda 2 mutation-gate tooling — radiography classifier (T1) +
mutationScoreratchet (T3) — new mutation-testing tooling: a survivor-radiography classifier and amutationScoreratchet. (#4234) - feat(ci): wire the F2.4 compression budget-gate ratchet — adds a CI ratchet that gates compression budget regressions. (#4232)
🐛 Fixed
- fix(providers): qwen-web model discovery now lists the live catalog instead of nothing — the
qwen-webcookie provider had no entry inPROVIDER_MODELS_CONFIG, so its model-discovery page returned an empty/stale local catalog (the OAuth fallback at the top of the route only fires forprovider === "qwen", leavingqwen-webto fall through to the no-config branch). Added aqwen-webentry that fetches the publichttps://chat.qwen.ai/api/v2/modelsendpoint (no auth header) and parses the{ data: { data: [{ id, name, owned_by }] } }shape (with a flatter{ data: [] }fallback). This is Problem #3 of #3931 (diagnosed by @thezukiru); Problem #1 — validator bare-token false-positive — shipped earlier in #3958, and Problem #2 — empty stream from Qwen WAF bot-detection on the streaming endpoint — remains a separate upstream/stealth concern. (#3931 — thanks @thezukiru) - fix(providers): ZenMux model discovery now lists the live catalog (incl. the free models) instead of the stale 9-entry hardcoded list — adding a ZenMux key validated fine, but the connection then showed
API unavailable — using local catalogand was missing the free models ZenMux advertises (z-ai/glm-5.2-free,moonshotai/kimi-k2.7-code-free). Root cause:zenmuxcarries a correctmodelsUrlin the registry, but — likellm7/byteplusbefore #3976 — it was not classified by any live-fetch branch of the model-import route (notopenai-compatible-*, not self-hosted, not inNAMED_OPENAI_STYLE_PROVIDERS), so the route never probed the upstream/modelsand fell through to the registry's hardcodedmodels[]. AddedzenmuxtoNAMED_OPENAI_STYLE_PROVIDERS, so the route probeshttps://zenmux.ai/api/v1/models(the/chat/completions-stripped<baseUrl>/modelscandidate) and serves the live list, falling back to the local catalog only when the upstream fetch fails — import never breaks. (#4202 — thanks @mikmaneggahommie) - fix(providers): Vercel AI Gateway "import models" now loads the live catalog instead of nothing — adding a Vercel AI Gateway key worked, but clicking import on the models page loaded nothing usable (manually adding the same models worked). Same class as #4202 (zenmux) / #3976 (llm7/byteplus):
vercel-ai-gatewaycarries a realbaseUrl(https://ai-gateway.vercel.sh/v1/chat/completions, formatopenai) in the registry, but was not classified by any live-fetch branch of the model-import route (notopenai-compatible-*, not self-hosted, not inNAMED_OPENAI_STYLE_PROVIDERS), so the route never probed the upstream/modelsand fell through to the registry's tiny 5-entry hardcodedmodels[]. Addedvercel-ai-gatewaytoNAMED_OPENAI_STYLE_PROVIDERS, so the route probeshttps://ai-gateway.vercel.sh/v1/models(the/chat/completions-stripped<baseUrl>/modelscandidate) and serves the live list, falling back to the local catalog only when the upstream fetch fails — import never breaks. (#4249 — thanks @FerLuisxd) - fix(sse): clear error when the request queue drops a job (no more fake-upstream "This job timed out after Nms") — under concurrent load, requests that exceed the per-connection rate-limit queue budget (
resilienceSettings.requestQueue.maxWaitMs) were dropped by Bottleneck with its rawThis job timed out after <maxWaitMs> ms.message. That string is indistinguishable from an upstream gateway timeout, so the 502 body and call-loglast_errorlooked like a provider outage across unrelated providers (TI:0|TO:0) — an operator spent ~3h misdiagnosing local queue saturation as upstream failures.withRateLimitnow rewrites that specific Bottleneck error into a clear, OmniRoute-owned message that names the knob (requestQueue.maxWaitMs, tunable in Settings → Resilience), explicitly disclaims an upstream timeout, preserves the original ascause, and tagscode: "RATE_LIMIT_QUEUE_TIMEOUT". Behavior is unchanged — the job is still dropped so combo falls back to the next target. (#4165 — thanks @KooshaPari) - fix(api): advertise the built-in
auto/*combos in/v1/models— OmniRoute ships a zero-setupauto/*catalog (auto/best-coding,auto/pro-reasoning, …, 16 variants) that the dashboard advertises and that resolve on demand, but the/v1/modelslisting only emitted persisted DB combos + provider models. Clients that build their model picker from/v1/models(e.g. Hermes Agent) never saw anyauto/*option. The catalog now emits everyAUTO_TEMPLATE_VARIANTSid (asowned_by: "combo") at the top of the list, deduped against persisted combos. (Showing eachauto/*'s dynamically-selected members is a separate enhancement.) (#4164 — thanks @MRDGH2821) - fix(sse): restore MCP / third-party tool names on the native Claude path (MCP dispatch broken in Claude Code) — since 3.8.27, every MCP tool call routed through OmniRoute to a native Claude OAuth provider failed client-side with
Error: No such tool available: <PascalCaseName>: tool schemas arrived fine but the streamedtool_use.namereached Claude Code in its cloaked form (e.g.McpN8nMcpSearchWorkflowsinstead of the registeredmcp__n8n-mcp__search_workflows). The native-Claude tool-name cloak stashes its per-request alias→original map as a non-enumerable_toolNameMapon the request body; the request-inspector capture added in 3.8.27 rebuilds the captured body from its serialized form (JSON.parse(JSON.stringify(...))), which drops non-enumerable properties, sofinalBody._toolNameMapwas empty and the response-side un-cloak silently fell back to the static built-in map — never restoring dynamic MCP / snake_case names. Built-in tools (Bash/Read/…) were unaffected (static map); cross-format paths were unaffected (they attach the map enumerably). The provider-request capture now re-attaches the per-request map (kept non-enumerable, so it still never re-serializes upstream) when the captured copy lost it, restoring MCP tool dispatch. (#4091 — thanks @pedrotecinf, @NakHalal) - fix(dashboard): Logs auto-refresh self-heals in embedded/proxied hosts that pin or mis-fire visibility — a follow-up to #4054: the Request Logger still froze auto-refresh on some hosts (reported on 3.8.28 Docker, works on 3.8.24). #4054 made the initial visibility fail-open, but the pause is event-driven — a host that fires a one-shot
visibilitychange→ hidden and then keeps reporting"hidden"(or recovers without firing the event again) left the cached visibility flag stuckfalse, so the interval ticked but never polled (only the manual Refresh button worked). The poll tick now also re-checks the livedocument.visibilityState, and a windowfocuslistener re-arms polling (a focused window is a reliable signal the page is actively viewed). A genuinely backgrounded browser tab still pauses (it reports"hidden"and never receives focus), preserving the #3109 network-saturation optimization. (#4133 — thanks @tjengbudi) - fix(capabilities): unify vision model-id detection into one shared source — three code paths kept independent, drifting vision-model lists, so the same model id could get up to three different verdicts. Two concrete bugs: lite compression's gate was missing pixtral / llava / qwen-vl / glm-4v / kimi-vl / mistral-medium-3, so it stripped images for those real vision models and blinded them (same class as #4071 / #4012); and the
/v1/modelslist was too broad, flagging text models (gemma, barekimilikekimi-k2) as vision. All three (modelCapabilitiesrouting fallback,/v1/modelslisting, lite image-strip gate) now delegate to a single conservative sourcesrc/shared/constants/visionModels.ts, which also restoresglm-4v/gemini-3coverage and keeps the #3328 MiniMax M3 carve-out. (#4072 — thanks @diego-anselmo) - fix(sse): surface mid-stream Gemini errors instead of returning a truncated 200 — when an upstream Gemini SSE stream emitted some partial content and then a JSON error object (
{"error":{"code":503,"message":"…high demand…","status":"UNAVAILABLE"}}) instead of acandidatespayload, OmniRoute silently dropped it: the gemini→openai translator's no-candidate branch only handledpromptFeedback(content-filter) and returnednullfor anything else, so the stream simply ended and the client got HTTP 200 with a truncated body andfinish_reason: "stop"— masking the failure and skipping combo fallback.geminiToOpenAIResponsenow detects anerrorobject (optionally wrapped inresponse), records it asstate.upstreamError(preserving the real status — 503/UNAVAILABLE, or 429 forRESOURCE_EXHAUSTED), and letsstream.tserror the stream out through the existingonFailure/buildErrorBody/controller.errorpath — the same mechanism the openai-responses translator already uses. (#4177 — thanks @hartmark) - fix(capabilities): resolve models.dev-synced vision metadata for Mistral
-latestaliases — root cause behind the #4071 heuristic:getResolvedModelCapabilities("mistral/pixtral-12b-latest").supportsVisionresolvednull(vision came only from the #4071 model-id heuristic, withattachmentstillnull) even though models.dev exposes the model as multimodal. Confirmed against the live models.dev API: it catalogs Pixtral 12B under the short idpixtral-12b(withattachment: true,modalities.input: ["text","image"]), while requests use the Mistral API aliaspixtral-12b-latest. The synced lookup tried the exact / raw / static-spec-canonical ids — all of which miss the short form — so it fell through to the heuristic.getSyncedCapabilityForResolvednow adds a last-resort fallback that retries with a trailing-lateststripped, so synced metadata (attachment/ image modalities) wins for these aliases; models whose-latestid is stored verbatim (e.g.pixtral-large-latest) keep resolving directly. Note: the models.dev sync is currently manual-only (Settings → models.dev) with no scheduled refresh, so a fresh instance still relies on the #4071 heuristic until that sync runs — a periodic-refresh cadence is left as a separate follow-up. (#4073 — thanks @diego-anselmo) - fix(sse): map Xiaomi MiMo reasoning control to its native
thinking:{type}shape — MiMo (api.xiaomimimo.com) controls chain-of-thought only via top-levelthinking:{type:"enabled"|"disabled"}and does not understand OpenAI'sreasoning_effort/reasoning, while its request validator is strict (400 Param Incorrect). OmniRoute's OpenAI path carried reasoning intent asreasoning_effort, and the claude→openai translator can leave a Claude-shapedthinking:{type, budget_tokens}— so the client's on/off choice was silently dropped andbudget_tokens/reasoning_effortrode along as extra params the validator can reject. Newopen-sse/services/mimoThinking.ts::normalizeMimoThinking(wired inchatCoreforprovider==="xiaomi-mimo") reduces any thinking object to just{type}(disabledstays;enabled/adaptive/other →enabled) and dropsreasoning_effort/reasoning. It deliberately does not synthesize thinking from a barereasoning_effort—mimo-v2-omniis non-thinking, so that could turn a silently-ignored param into a hard error. (#4224) - fix(capabilities): Xiaomi MiMo
*-prochat models are text-only (no vision) — onlymimo-v2.5andmimo-v2-omniaccept images per Xiaomi's docs;mimo-v2.5-pro/mimo-v2-proare text-only, butmodelSpecsmarked them vision-capable and models.dev mislabels them (hermes-agent#18884). SinceresolveVisionCapabilitylets a syncedattachment:truewin first, an image request could be routed to a blind model (the #4071 failure mode). Corrected the specs and added a hard override inresolveVisionCapability(checked before the synced branch, anchored somimo-v2.5-pronever matches the multimodalmimo-v2.5) that beats the wrong synced attachment. Also registered the missing nativemimo-v2-prochat model and the missingmimo-v2-ttsspeech model. (#4224) - fix(sse): Claude Opus 4.7+/Fable 5 use adaptive thinking only (no more manual-budget 400s) — Opus 4.7 and later (Opus 4.7/4.8, Fable 5) removed manual extended thinking:
thinking.type:"enabled"or anythinking.budget_tokensnow returns400("Any request that tries to set a fixed thinking budget gets a 400" — Anthropic migration guide). Reasoning is adaptive-only, steered byoutput_config.effort. OmniRoute's OpenAI→Claude translator mappedreasoning_effortlow/medium/high to a manualthinking:{type:"enabled", budget_tokens}, so those requests hard-400'd on the most-used provider (and a Claude-native passthrough client sending the legacy shape did too). A newadaptiveThinkingOnlymodel flag now drives two fixes: the translator mapsreasoning_effortof every level to{type:"adaptive"}+output_config.effort(preserving the requested level, never a budget) for these models, and anormalizeClaudeAdaptiveThinkingcatch-all at the existing post-translation thinking-normalization chokepoint collapses any residual manual thinking (passthrough legacy shape, per-model defaults) to{type:"adaptive"}, keyed on the resolved upstream model so it covers every routing mode. Pre-4.7 models (Opus 4.6/4.5, Sonnet, Haiku) keep manual budgets unchanged. (#4230) - fix(providers): strip non-default temperature/top_p/top_k for Claude Opus 4.7+/Fable 5 (fixed sampling → no 400) — Opus 4.7 and later reject non-default
temperature/top_p/top_kwith a400(sampling is fixed; reasoning moved tooutput_config.effort). The translator forwarded client-suppliedtemperature/top_punconditionally and the Claude registry models carried nounsupportedParams, so a plain OpenAI-format request withtemperature: 0.7toclaude-opus-4-8hard-400'd. AddedunsupportedParams: ["temperature","top_p","top_k"]to the Opus 4.7+/Fable 5 ids in both theclaude(dashedclaude-opus-4-8) andanthropic(dottedclaude-opus-4.7) registries, so they're stripped at the existinggetUnsupportedParamsdispatch chokepoint. Pre-4.7 Claude models still accept sampling params. (#4230) - fix(providers): conditionally strip temperature/top_p for GPT-5 reasoning on the
openaiChat Completions path (no 400 when an effort is active) — GPT-5 reasoning models reject non-defaulttemperature/top_pwith a400whenever a reasoning effort is active, yet accept them again underreasoning_effort:"none"(the GPT-5.1+ default, i.e. non-reasoning mode). On theopenaiprovider onlyo3carriedREASONING_UNSUPPORTED;gpt-5.5/gpt-5.4/gpt-5.4-mini/gpt-5.4-nanocarried no sampling guard, so atemperature+ active-effort request hard-400'd. A staticunsupportedParamslist can't express thenone-mode carve-out (it would over-strip the legitimate case), so the newgpt5SamplingGuarddropstemperature/top_ponly when the resolved effort is active — wired at the existinggetUnsupportedParamschokepoint and scoped to theopenaiChat Completions surface (thecodexResponses path is already covered by the CodexExecutor allowlist; other providers are untouched). (#4245) - fix(codex): stop silently dropping GPT-5 output verbosity (
verbosity/text.verbosity) — the GPT-5 series added an output-verbosity control:verbosity(low/medium/high) on Chat Completions, nested astext.verbosityon the Responses API. The CodexExecutor gates translated requests through an allowlist that had notextentry, so for thecodexprovider the hint was dropped before reaching upstream (theopenaiChat path already forwarded it).normalizeCodexVerbositynow folds whichever shape arrived into a single validatedtext:{verbosity}before the allowlist (which now permitstext), and the OpenAI Chat↔Responses request translators mapverbosityacross formats so the hint survives a format crossing for non-codex Responses backends too. Invalid/absent verbosity collapses to notext(status quo). (#4245) - fix(sse): map
reasoning_effortto DeepSeek V4's native{high, max}vocabulary — DeepSeek V4 only understandshigh/maxreasoning levels, so otherreasoning_effortvalues are mapped onto its native vocabulary instead of being rejected. (#4219) - fix(glm): default
max_tokensand an extended timeout for GLM-5.2+ thinking — GLM-5.2+ thinking responses are slow and need headroom, so OmniRoute now sets a sensible defaultmax_tokensand a longer timeout for them. (#4255 — thanks @dhaern) - fix(antigravity): default
includeThoughtsfor modern Gemini models — modern Gemini models on the Antigravity path now default to including thoughts so reasoning isn't silently dropped. (#4180 — thanks @dhaern) - fix(provider-registry): add correct
contextLengthto theoldllm models — fills in accurate context-window sizes for theoldllm's models. (#4184 — thanks @herjarsa) - fix(models): expose combo model token limits —
/v1/modelsnow reports token limits for combo models. (#4189 — thanks @megamen32) - fix(combo): keep the passthrough quota fallback scoped — prevents the passthrough quota fallback from leaking across unrelated targets. (#4194 — thanks @Svetznaniy33)
- fix(combo): opt proactive-fallback compression into the TV1 bail-out (no silent target drop) — proactive-fallback compression now participates in the TV1 bail-out so a target is never silently dropped. (#4228)
- fix(compression): show engine preview output — the Compression Studio preview now renders the engine's output. (#4128 — thanks @megamen32)
- fix(compression): harden engines against I/O failures and misconfig (F5.3) — compression engines degrade gracefully on I/O errors and bad configuration instead of throwing. (#4198)
- fix(compression): harden RTK raw-output redaction + ReDoS guard for custom filters (F5.3) — broadens RTK raw-output redaction and adds a ReDoS guard for user-supplied filter patterns. (#4203)
- fix(compression): bound
mcpAccessibilitymaxTextCharson the live read path — the live read path now clampsmaxTextCharsso a small value can't make tools disappear. (#4206) - fix(dashboard): data tables paint an opaque surface so the grid doesn't bleed through — data tables now render on an opaque surface, fixing the grid wallpaper showing through. (#4233)
- fix(dashboard): make the provider card hover visible (was ~1% opacity) — the provider-card hover state was effectively invisible; it now has a visible surface. (#4214)
- fix(vscode): sanitize implicit editor context — redacts sensitive filenames/keywords from the implicit VS Code editor context before it's sent upstream. (#4124 — thanks @zhiru)
- fix(build): raise the Node heap for the local
next buildto stop OOM/stall — bumps the build-time heap so the local production build no longer OOMs or stalls. (#4171) - fix(mitm): TPROXY OUTPUT-based recipe for local traffic (validated e2e on VPS) — switches the TPROXY rules to an OUTPUT-chain recipe so locally-originated traffic is captured; validated end-to-end on the VPS. (#4156)
- fix(mitm): forward anti-loop — put the bypass-marked socket on the Agent (decrypt 4d) — places the bypass-marked socket on the HTTP Agent so OmniRoute's own forwarded traffic never re-enters the capture loop; VPS-validated. (#4229)
- fix(free-tiers): retire dead-tier
hasFree, round the headline to ~1.6B, regenerate the per-provider table — drops dead free tiers from the headline math and regenerates the per-provider free-tier table. (#4142) - fix(free-tiers): retire 4 re-verified-dead free tiers, flag iflytek/sparkdesk ToS, clarify monsterapi one-time — removes four confirmed-dead free tiers and annotates ToS/one-time caveats. (#4152)
🧪 Tests
- test(sse): guard the Antigravity
_toolNameMapcloak map through the request-capture round-trip — follow-up to #4091: the generic capture fix increatePreparedRequestLogger().body()(#4153) re-attaches the non-enumerable_toolNameMapthat the request-inspector drops when it rebuilds the upstream body viaJSON.parse(JSON.stringify(...)), but the only regression test covered the native-Claude OAuth cloak (PascalCase aliases). The Antigravity cloak differs —cloakAntigravityToolPayloadsuffixes custom tools with_ide(workspace_read→workspace_read_ide), leaves native tools untouched, and returns the reverse map separately — so a refactor ofproviderRequestLogging.tsor the executor could silently re-break Antigravity tool dispatch without tripping the Claude test. Adds a dedicated regression test driving the realcloakAntigravityToolPayloadthrough the capture round-trip and asserting the_idereverse map survives, stays non-enumerable (never re-serializes upstream), and that all-native traffic produces no spurious map (verified failing with the #4153 re-attach removed). No production change. (#4181 — thanks @hertznsk) - test(chatcore): dedicated unit tests for 6 leaves + wire into stryker mutate (QG v2 Fase 9 T5 Fase 3) — adds focused unit tests for 6 chatCore leaf helpers and enrolls them in mutation testing. (#4218)
- test(chatcore): telemetry / memory-skills / semantic-cache tests + wire 2 into stryker (QG v2 Fase 9 T5 Fase 3) — new tests for the telemetry, memory-skills and semantic-cache leaves, two of which are added to the mutation set. (#4222)
- test+ci(chatcore): semanticCache HIT-path fixture (15/15 mutate) + 350min budget headroom — closes the semantic-cache HIT path to a full 15/15 mutation score and gives the nightly auth/accountFallback batches more budget headroom. (#4225)
- test(compression): close F5.1 coverage gaps (replay reducer, live accumulator, StatusDot) — fills the remaining F5.1 compression coverage gaps. (#4192)
- test(db,sse): de-flake db-backup + chatcore streaming timing assertions — stabilizes two timing-sensitive tests (fire-and-forget backup completion + a streaming race). (#4132)
- test: align stale integration tests surfaced post-v3.8.28 on main — realigns integration tests that drifted after the v3.8.28 merge. (#4129)
📝 Maintenance
- refactor(sse): split chatCore.ts pure helpers into chatCore/ modules (−561 LOC) — extracts pure helpers out of the chatCore god-file into dedicated modules (Onda 3). (#4159)
- refactor(chatcore): extract passthrough/header/telemetry helpers (QG v2 Fase 9 T5 C2-C3-C5) — further chatCore decomposition. (#4188)
- refactor(chatcore): extract combo/proxy context cache + semaphore helpers (QG v2 Fase 9 T5 C6-C7) — continues the chatCore split. (#4193)
- refactor(combo): god-file split pilot — types + validateQuality + predicates (QG v2 Fase 9 T5 D1-D3) — first slice of the combo.ts decomposition. (#4162)
- refactor(combo): god-file split part 2 — shadow + sorters + structure (QG v2 Fase 9 T5 D4-D6) — continues the combo.ts split. (#4175)
- refactor(combo): god-file split part 3 — auto strategy (QG v2 Fase 9 T5 D8) — extracts the auto strategy from combo.ts. (#4186)
- refactor(combo): extract round-robin sticky state to
combo/rrState.ts(D7a) — moves round-robin sticky state into its own module. (#4196) - refactor(combo): extract the reset-aware quota block to
combo/quotaStrategies.ts(D7b) — moves the reset-aware quota strategies into their own module. (#4204) - refactor(compression): remove vestigial SLM seam + dead deprecated alias — drops dead compression code. (#4253)
- chore(compression): remove vestigial reconstructCcr/SessionDedup round-trip helpers — removes unused round-trip helpers. (#4226)
- chore(compression): remove dead exports + fix stale llmlingua docs — prunes dead exports and corrects stale LLMLingua docs. (#4223)
- chore(build): build + ship the TPROXY native addon in the standalone (prebuilds 4e) — bundles the native TPROXY addon prebuilds into the standalone build. (#4236)
- chore(ci): add quota + 6 covered chatCore leaves to stryker mutate (QG v2 Fase 9 T5 Fase 3 follow-up) — enrolls more covered leaves into mutation testing. (#4209)
- chore(ci): re-add 8 combo split leaves to stryker mutate + expand nightly batch-matrix 3→5 (QG v2 Fase 9 T5 Fase 3) — restores mutation coverage for the split combo leaves and widens the nightly matrix. (#4205)
- chore(quality): close v3.8.28 cycle gate drift (re-baseline + nightly-mutation scope) — reconciles quality-gate baselines after the v3.8.28 cycle. (#4135)
- ci(mutation): split nightly into 3 parallel batches to fit the 180min budget (QG v2 Fase 9 T0) — parallelizes the nightly mutation run. (#4150)
- ci(mutation): restore cold-seed timeout headroom (a/b lost in #4225 squash) + extend to c/d/g/h — restores and extends per-batch cold-seed timeouts. (#4258)
- ci(docs): harden the fabricated-docs checker + enforce
--strict(QG v2 Fase 9 T9) — tightens the anti-hallucination docs checker. (#4149) - ci: derive the oasdiff base-ref from the package version + flag the mutation-toolchain regression — fixes the OpenAPI-diff base-ref and surfaces a mutation-toolchain regression. (#4134)
- docs(ci): correct the mutation-gate note (no regression —
stryker -cis--concurrency); record Task 12 GO — corrects a misread of the stryker flag and records the spike GO. (#4138) - docs(api): document the
/api/v1/wschat WebSocket endpoint in openapi.yaml — adds the WebSocket chat endpoint to the OpenAPI spec. (#4215) - docs(readme): expand Acknowledgments into a themed, star-counted credits hall — reworks the README acknowledgments section. (#4195)
- style(dashboard): shrink the identity grid cell 46px → 32px (~30% smaller) — tightens the identity grid density. (#4143)
🔧 Dependencies
- deps: bump the production group with 5 updates — routine production-dependency bumps. (#4121)
- chore(deps): bump github/codeql-action from 3 to 4 — CI action update. (#4120)
- chore(deps): bump actions/setup-python from 5 to 6 — CI action update. (#4119)
[3.8.28] — 2026-06-17
✨ New Features
- feat(providers): add OrcaRouter (OpenAI-compatible routing gateway) — OrcaRouter is now registered as an API-key provider. Its adaptive router is exposed as
orcarouter/auto(smart routing across 150+ upstream models), alongside a curated flagship set (GPT-5.5, Gemini 3.5 Flash, Claude Opus 4.8, Grok 4.3, DeepSeek V4 Pro, MiniMax M2.7, Qwen3.7 Max).passthroughModelsis enabled so any OrcaRouter model id works. OpenAI-compatible endpoint (https://api.orcarouter.ai/v1), Bearer (sk-orca-…) auth — no custom executor or translator required. (#4070 — thanks @jinhaosong-source) - feat(providers): add Wafer AI (Anthropic-compatible, Bearer auth) — Wafer AI is now a built-in provider speaking the Anthropic Messages format with Bearer authentication, registered with its model catalog so it works out of the box. (#4098 — thanks @diegosouzapw)
- feat(cli):
omniroute launch— zero-config Claude Code launcher — a new CLI subcommand that boots OmniRoute (if not already running) and launches Claude Code pre-wired to it, with no manual env/settings editing. (#4097 — thanks @diegosouzapw) - feat(api): exact offline token counting for the
count_tokensfallback via tiktoken — the localcount_tokensfallback now uses a real tiktoken (BPE) tokenizer for exact offline counts instead of a heuristic estimate, so token budgeting is accurate even when no upstream count endpoint is reachable. (#4087 — thanks @diegosouzapw) - feat(sse): Claude Code quota-probe bypass + command meta-request helpers — ported from free-claude-code: OmniRoute now recognizes Claude Code's quota-probe and command meta-requests and serves them locally instead of burning an upstream call, reducing wasted quota during CLI sessions. (#4083 — thanks @diegosouzapw)
- feat(sse): generic 400 field-downgrade retry + Groq field stripping — when an upstream rejects a request with
400because of an unsupported field, OmniRoute now strips the offending field and retries (a generic downgrade path), with Groq-specific field stripping wired in. Aligned with the existingcontext_managementretry handling. (#4096 — thanks @diegosouzapw) - feat(sse): delegated Anthropic Context Editing — relay coverage + 400-fallback — extends the Claude server-side Context Editing delegation (#4021) with broader relay coverage and a
400-fallback so a request the upstream rejects for the context-management beta degrades gracefully instead of failing. (#4065 — thanks @diegosouzapw) - feat(compression): record per-engine Context Editing telemetry — the compression pipeline now records a
context-editingengine entry so the dashboard attributes server-side Context Editing savings alongside the local compression engines. (#4062 — thanks @diegosouzapw) - feat(compression): RTK learn/discover (sample source + API + UI) — the rule-based RTK compression engine gains a learn/discover workflow: sample a source, surface candidate rules through a new API, and review/apply them in the dashboard. (#4088 — thanks @diegosouzapw)
- feat(dashboard): 2026-06-17 free-tier refresh — honest catalog, uncapped + boost tiers, Layout A budget table — the free-tier page was refreshed with an honest, deep-researched catalog (pooled/realistic figures rather than inflated 24/7 RPM math), new
recurring-uncappedand boost tiers, new providers, and a KPI + budget table (Layout A). (#4089 — thanks @diegosouzapw) - feat(dashboard): Combo Studio connection-cooldown badge (U1b Slice 2) — the Combo Live cascade now surfaces each connection's cooldown state as a badge, complementing the circuit-breaker badge shipped in 3.8.27. (#4068 — thanks @diegosouzapw)
- feat(mitm): attribute intercepted requests to the originating process (Gap 1) — the Traffic Inspector now resolves each intercepted connection back to the originating local process (via
/proc), so captured traffic can be attributed to the app that produced it. (ProxyBridge-inspired hardening.) (#4085 — thanks @diegosouzapw) - feat(mitm): capture-pipeline self-test route (Gap 12) — a diagnostic route that exercises the MITM capture pipeline end-to-end so operators can confirm interception is working without crafting a real upstream call. (#4093 — thanks @diegosouzapw)
- feat(mitm): loop-guard self-check + verbosity control in
server.cjs(Gaps 14+15) — the MITM proxy gains a self-referential loop guard (so it never proxies its own traffic into an infinite loop) and aMITM_VERBOSErouting-decision log level. (#4101 — thanks @diegosouzapw) - feat(agent-bridge): portable JSON import/export of config (Gap 4) — the Agent Bridge / MITM configuration can now be exported to and imported from a portable JSON file, so a working setup can be backed up or moved between machines. (#4094 — thanks @diegosouzapw)
🐛 Fixed
- fix(ws): start the LiveWS sidecar with
cwdat the package root (global/systemd installs) — the standalone LiveWS launcher (scripts/start-ws-server.mjs) re-spawns itself withnode --import tsx <self>but did not setcwd. When the WebSocket sidecar was launched from outside the package directory — a global npm/homebrew install, or asystemd/launchdunit started from$HOME— Node could not resolve thetsxpackage (ERR_MODULE_NOT_FOUND: Cannot find package 'tsx'), and even from the package directorytsxcould not resolve the tsconfig@/*path aliases (e.g.@/types/databaseSettings), so the sidecar never booted. The spawn now pinscwdto the package root (the directory abovescripts/, wherepackage.json+tsconfig.jsonlive), which resolves bothtsxdiscovery and the@/*aliases regardless of launch directory. (#4055 — thanks @Rahulsharma0810) - fix(dashboard): Logs page auto-refresh now works in embedded/proxied dashboards — the Request Logger gated each auto-refresh tick on a static
document.visibilityState === "visible"read. Hosts that report a permanent non-"visible"state without ever firing avisibilitychangeevent (Docker dashboard wrappers, embedded webviews) froze auto-refresh entirely — only the manual Refresh button worked, a regression from 3.8.24's unconditional polling. The pause is now event-driven and fail-open: polling starts enabled and only pauses after a realvisibilitychange→ hidden transition (still preserving the backgrounded-tab optimization for normal browser tabs). (#4054 — thanks @tjengbudi) - fix(docker): raise the build-stage Node heap to stop the production-build OOM — the Docker
builderstage rannpm run buildwith V8's default heap ceiling (~2 GB). After #4052 forced the heavier webpack engine (Turbopack panics on this Next.js version), the production optimization pass exceeded that ceiling and the build died withFATAL ERROR: … JavaScript heap out of memoryat[builder] npm run build. The builder stage now setsNODE_OPTIONS=--max-old-space-size(default 4096 MB, overridable via--build-arg OMNIROUTE_BUILD_MEMORY_MB=…) before the build; the value propagates to the spawnednext build. Build-only — the runtime heap (OMNIROUTE_MEMORY_MBon the runner stage) is unchanged. (#4076 — thanks @kamenkadmitry) - fix(dashboard): "Update Available" banner reappears reliably across Docker/npm/desktop installs — the home-page banner is gated on
GET /api/system/version'supdateAvailable, which derived the latest version ONLY fromnpm info omniroute version --jsonvia thenpmCLI binary. When that binary is absent from the runtime PATH (Docker/desktop/locked-down installs) or the registry is unreachable, the call returnednull→updateAvailable=false→ the banner silently never rendered even when a newer release existed. The route now resolves the latest version throughresolveLatestVersion(): the fastnpmCLI path first, then an npm-binary-free fallback over the registry HTTP API (registry.npmjs.org/omniroute/latest), and a logged warning instead of silent degradation when both fail. Version comparison was also hardened to toleratev-prefixed and pre-release version strings. (#4100) - fix(sse): route image requests only to confirmed-vision combo targets — a combo could route an image-bearing request to a member that doesn't actually support vision. Routing now requires
supportsVision === true(plus a model-id heuristic) before sending images to a target, so multimodal requests land only on members that can handle them. (#4071 — thanks @diego-anselmo) - fix(security): injection guard respects the
INJECTION_GUARD_MODEDB feature flag — the prompt-injection guard ignored the database feature-flag, so operators couldn't change its mode at runtime; it now reads the flag and honors the configured mode. (#4077 — thanks @zhiru) - fix(ws): proxy LAN
/live-wsupgrades and warn on an unsetJWT_SECRET— WebSocket upgrade requests arriving over the LAN proxy path were not forwarded to the LiveWS sidecar; they are now proxied correctly, and the server logs a clear warning whenJWT_SECRETis unset. (#4079 — thanks @Rahulsharma0810) - fix(dev): force webpack in the custom dev server (Turbopack 16.2.x panics) — the custom dev server now forces the webpack engine because Turbopack panics on this Next.js version, so
npm run devboots reliably. (#4092 — thanks @chirag127) - fix(auto): resolve built-in
auto/*catalog combos — referencing a built-inauto/*combo returned a premature400because the catalog entry wasn't resolved; the built-in auto catalog is now resolved before validation so those combos work. (#4058 — thanks @megamen32) - fix(sse): friendly 413 message for ChatGPT-web payload-too-large — an oversized ChatGPT-web payload returned an opaque error; it now returns a clear
413with a human-readable message. (#4080 — thanks @diegosouzapw) - fix(ws): warm the SSE auth import on LiveWS startup; relocate the boot test to integration — the LiveWS sidecar now pre-imports the SSE auth module at startup to avoid a first-request stall, and its boot test was moved to the integration suite. (#4063 — thanks @diegosouzapw)
- fix(mitm): crash-safe system-state teardown + socket timeouts (ProxyBridge-inspired hardening) — the MITM proxy could leave the host's system proxy settings applied if it crashed mid-teardown, and long-lived tunnels could leak as half-open sockets. Teardown is now crash-safe (system state is always restored) and proxied sockets get an idle timeout (
MITM_IDLE_TIMEOUT_MS, default 60s). (#4084 — thanks @diegosouzapw) - fix(responses): clear the
/v1/responseskeep-alive timer on cancel/abort — a cancelled or aborted/v1/responsesstream left its keep-alive timer running, leaking a timer and burning CPU; the timer is now cleared on cancel/abort. (#4105 — thanks @artickc) - fix(usage): reap orphaned pending-request details (unbounded memory leak) — pending-request detail entries whose request never completed accumulated without bound; they are now reaped, closing a slow memory leak. (#4107 — thanks @artickc)
- fix(auth): prune expired entries from the login brute-force guard map (unbounded growth) — the login brute-force guard map grew without bound because expired entries were never removed; expired entries are now pruned. (#4111 — thanks @artickc)
- fix(logger): hard-cap the error-dedup map to bound memory under unique-message bursts — a burst of unique error messages could grow the dedup map without limit; it is now hard-capped. (#4113 — thanks @artickc)
- fix(circuit-breaker): enforce
MAX_REGISTRY_SIZE(declared but never applied) — the circuit-breaker registry declared a maximum size that was never enforced, so it could grow unbounded; the cap is now applied. (#4114 — thanks @artickc) - fix(webhook): clear the abort timer in
finallyto avoid dangling timers on fetch error — a webhook dispatch that threw before clearing its abort timer left the timer dangling; it is now cleared in afinallyblock. (#4115 — thanks @artickc) - fix(combo): detach the per-target listener from the shared hedge abort signal — combo hedging attached a per-target listener to a shared abort signal without detaching it, leaking listeners across requests; the listener is now detached. (#4116 — thanks @artickc)
- fix(timers): unref background interval timers so they don't block clean shutdown — long-lived background interval timers kept the event loop alive and blocked a clean process exit; they are now
unref'd. (#4117 — thanks @artickc)
⚡ Performance
- perf(registry): precompute the model→provider index in
parseModelFromRegistry— model→provider lookups now use a precomputed index instead of scanning the registry on every call. (#4110 — thanks @artickc) - perf(obfuscation): cache per-word regexes instead of recompiling every request — the obfuscation pass now caches its per-word regexes rather than recompiling them on each request. (#4109 — thanks @artickc)
- perf(stream): use
structuredCloneinstead of a JSON round-trip for per-chunk reasoning split — the per-chunk reasoning split now clones withstructuredClonerather thanJSON.parse(JSON.stringify(...)). (#4108 — thanks @artickc) - perf(gemini): cache the reasoning close-tag regex instead of recompiling per token — the Gemini reasoning close-tag regex is now compiled once and reused instead of per token. (#4106 — thanks @artickc)
📝 Maintenance
- ci(quality): flip the TIA impacted-unit-tests gate from advisory to blocking (Fase 9) — the test-impact-analysis gate that runs the unit tests impacted by a diff is now blocking on PRs. (#4069 — thanks @diegosouzapw)
- ci(quality): dedup the doubly-run
check:docs-sync+ record the validated ROI backlog (Fase 9) —check:docs-syncwas running twice in CI; the duplicate was removed and the validated quality-gate ROI backlog recorded. (#4099 — thanks @diegosouzapw) - docs(quality-gates): reconcile the gate inventory with
ci.yml+ add the ROI rationalization backlog — the quality-gate inventory doc was reconciled against the actual CI jobs and a rationalization backlog added. (#4095 — thanks @diegosouzapw) - test(infra): isolate
DATA_DIRper test process; raise Stryker concurrency 1→4 — test processes now get an isolatedDATA_DIR(no shared-DB cross-talk) and the mutation runner's concurrency was raised. (#4078 — thanks @diegosouzapw) - test(dashboard): smoke e2e for the Combo Live Studio page — adds a Playwright smoke test covering the Combo Live Studio page. (#4075 — thanks @diegosouzapw)
- docs(compression): document LLMLingua optional deps + on-demand install — documents the optional LLMLingua dependencies and how they are installed on demand. (#4061 — thanks @diegosouzapw)
- chore(deps): freeze
@huggingface/transformersin dependabot (hard-pin) — the transformers dependency is hard-pinned and frozen in dependabot to protect the VPS-validated LLMLingua + memory-embeddings stack from a breaking major bump. (#4066 — thanks @diegosouzapw) - chore(docs): update the Discord invite link to a non-expiring one — replaces the expiring Discord invite with a permanent link. (#4067 — thanks @diegosouzapw)
- chore(docs): document the new MITM env vars + reconcile the env-doc contract — documents
MITM_IDLE_TIMEOUT_MSandMITM_VERBOSEin.env.example+ENVIRONMENT.md, allowlists the framework-internalTURBOPACKand the Claude CodeANTHROPIC_AUTH_TOKEN, and relocates/prunes stale provider/guide docs. (thanks @diegosouzapw)
🔧 Dependencies
- deps: bump the development group with 10 updates — routine dependabot dev-dependency bumps. (#4051)
- deps(electron): bump electron 42.4.0 → 42.4.1 — (#4049)
- ci(deps): bump
actions/setup-node4 → 6 — (#4048) - ci(deps): bump
actions/cache4.3.0 → 5.0.5 — (#4047) - ci(deps): bump
actions/github-script7 → 9 — (#4046) - ci(deps): bump
ossf/scorecard-action2.4.0 → 2.4.3 — (#4045) - ci(deps): bump
actions/upload-artifact4 → 7 — (#4044)
[3.8.27] — 2026-06-17
✨ New Features
- feat(combos): advertise combo capabilities (multimodal / reasoning / caching) on the import surfaces — importing a combo package into a client (LobeHub / OpenCode / VS Code, via
/v1/combosand the VS Code combo catalog) no longer requires manually enabling multimodal/image-input, reasoning, and caching afterwards.projectCombonow attaches a registry-derivedcapabilitiesblock, gated conservatively:multimodal/reasoningare advertised only when every concrete model step proves the capability (an unprovable nested combo-ref drops them, since the strategy may route to any member), andcachingreflects the combo's explicit Context-Cache-Protection setting (no surprise prompt-cache cost). The public/v1/combosdefault projection (#2300) is unchanged unless the caller opts in. (#3979 — thanks @xenstar) - feat(sse): delegated Anthropic Context Editing for Claude (
clear_tool_uses) — Claude requests can now offload context trimming to Anthropic's server-side context-management API (betacontext-management-2025-06-27,clear_tool_uses_20250919), pruning stale tool-use turns upstream instead of locally. Claude-only by nature (the edit runs server-side); multi-provider context trimming remains the job of the local compression engines. (#4021 — thanks @diegosouzapw) - feat(sse): real LLMLingua-2 ONNX compression engine (stable) — the LLMLingua-2 prompt-compression engine is now a real local ONNX model (TinyBERT default, transformers.js + tfjs), promoted to stable after VPS validation, replacing the previous placeholder. (#4014 — thanks @diegosouzapw)
- feat(compression): capture per-engine analytics + Lite schema fix — the compression pipeline now persists a per-engine breakdown for historical analytics so the dashboard can attribute savings to each engine in a stacked pipeline, and a Lite-schema mismatch was corrected. (#4018 — thanks @diegosouzapw)
- feat(dashboard): real circuit-breaker state in the Combo Live cascade (U1b) — the Combo Live cascade view now surfaces each provider's real circuit-breaker state (CLOSED / OPEN / HALF_OPEN) as a badge, read live from
/api/monitoring/health, instead of inferring health from request outcomes. (#4029 — thanks @diegosouzapw) - feat(openai): honor a custom base URL in model discovery + complete openai/codex pricing — OpenAI-format providers configured with a custom base URL now have that URL honored during model discovery (not just inference), and the openai/codex pricing table was completed. Discovery is routed through the SSRF-guarded outbound fetch. (#4005 — thanks @artickc)
- feat(observability): capture actual upstream provider requests — the request inspector now records the exact payload sent to the upstream provider (post-translation), so you can see what OmniRoute actually dispatched rather than only the client's original request. (#3941 — thanks @rdself)
- feat(providers): provider auth visibility controls — adds controls to show/hide provider auth details in the dashboard so credentials can be revealed only when needed. (#3953 — thanks @rdself)
- feat(providers): model search filter on the provider dashboard — the provider dashboard gains a search filter to quickly narrow a provider's model list. (#3950 — thanks @felipesartori)
- feat(compression): Indonesian caveman rules + language pack — adds an Indonesian "caveman" rule set and language pack to the rule-based compression engine. (#3975 — thanks @Veier04)
- feat(dashboard): sidebar group separator toggles — the dashboard sidebar can now toggle group separators for a cleaner navigation layout. (#3971 — thanks @rdself)
- feat(api): local
@@om-usagecommand for cached per-key usage — API clients can send a message that is exactly@@om-usageto retrieve cached Claude-style usage data locally, without forwarding the prompt to an upstream provider. Gated by a new per-key allowance flag. (#4034 — thanks @Witroch4)
🐛 Fixed
- fix(opencode): forward the OpenCode session id to the upstream regardless of how the user named the provider — the
OpencodeExecutorforwarded thex-opencode-session/request/project/clientheaders, but the OpenCode CLI only emits those when the configuredproviderIDstarts with"opencode". A user who adds OmniRoute as a custom provider (e.g."omniroute") makes the CLI sendx-session-affinity/X-Session-Idinstead (both carry the same session id), which the executor never read — so the session-metadata forwarding was effectively dead code for the realistic provider-naming case. The opencode-family executor now falls back tox-session-affinity/X-Session-Idand maps it ontox-opencode-sessionwhen the client didn't send the header directly, so session continuity to theopencode.aiupstream works for any provider name (a directx-opencode-sessionstill wins). Scoped to this executor only — the genericDefaultExecutorintentionally does not do this, to avoid leaking the client session id to arbitrary third-party upstreams. (#4022 — thanks @pizzav-xyz) - fix(guardrails): Vision Bridge no longer drops the image when the describe call fails (Nvidia NIM "Image unavailable") — the Vision Bridge is enabled by default and engages for any model whose vision capability OmniRoute can't prove from the registry (
supportsVision !== true, which includes uncatalogued models that resolve tonull). When the per-image describe call failed (e.g. no vision model configured), it replaced the image with the literal text[Image N]: (unavailable)and dropped the originalimage_url— so a genuinely vision-capable upstream (Nvidia NIM) received text only and answered "Image unavailable. Cannot provide description without visual data." A describe failure is no longer destructive:replaceImagePartsnow receivesnullfor failed images and preserves the original image part so the upstream can still see it (successful describes still replace the image with the text description;meta.descriptionsobservability is unchanged). (#4012 — thanks @daniij) - fix(kiro): preserve
finish_reason: "tool_calls"on the Kiro streaming path — streaming tool-call requests through the Kiro (Responses API) provider had their terminalfinish_reasonreported as"stop"instead of"tool_calls", so agent clients (Hermes) treated the tool-call turn as a finished turn, never ran the tool, and the next request failed with HTTP 400 on the incomplete tool state.convertKiroToOpenAI's terminalmessageStopEvent/donebranch hardcodedfinish_reason: "stop"regardless of whether the stream had emittedtoolUseEvents. The translator now recordsstate.sawToolUsewhen a tool-use chunk is emitted and reportsfinish_reason: "tool_calls"on the terminal chunk (and instate.finishReason) whenever the stream produced tool calls. The non-streaming path was already correct. (#3980 — thanks @lordavadon2) - fix(resilience): respect connection cooldown stored as a numeric epoch — the router kept dispatching to connections still inside their rate-limit cooldown because
rate_limited_until(aTEXTcolumn) was persisted as a raw epoch number, which SQLite coerced to a string like"1781696905131.0"thatnew Date(...)parsed asNaN, so the cooling connection was never skipped. The cooldown read predicates now normalize numeric-epoch strings via a sharedcooldownUntilMs()helper; ISO behavior is unchanged. (#3995 — thanks @diegosouzapw) - fix(providers): fetch the live
/modelscatalog for LLM7 and BytePlus — importing an LLM7 or BytePlus key surfaced only a small, outdated hardcoded list because neither provider was classified by any live-fetch branch of the model-import route. Both are now inNAMED_OPENAI_STYLE_PROVIDERS, so the route probes<baseUrl>/modelswith the key and serves the live catalog, falling back to the local catalog only when the upstream fetch fails. (#3996 — thanks @FerLuisxd / @diegosouzapw) - fix(dashboard): logs auto-refresh reads live visibility, not a stale mount ref — the Logs page never auto-refreshed when the tab loaded in the background because the auto-refresh interval gated each tick on a visibility ref seeded once at mount; the tick now reads the live
document.visibilityState, so polling self-heals as soon as the tab is visible while still pausing when genuinely hidden. (#3997 — thanks @tjengbudi / @diegosouzapw) - fix(combo): shuffle the strict-random fallback remainder to spread load — with the
strict-randomstrategy a persistently-failing model was retried on essentially every request because only the deck-selected slot 0 was shuffled while the fallback remainder stayed in fixed priority order; the remainder is now shuffled too, so fallback load (and recovery from a failing target) spreads evenly across healthy peers. (#3998 — thanks @KeNJiKunG / @diegosouzapw) - fix(claude): forward the client
tool-search-tool-2025-10-19anthropic-beta on the Claude OAuth path — with deferred tools active, Claude Code negotiates thetool-search-tool-2025-10-19beta, but OmniRoute dropped it on both Claude code paths, so the claude.ai backend rejected every deferred-tool request with400 Tool reference not found. A new allowlist-merge (mergeClientAnthropicBeta) now unions the client's negotiated beta into the outbound set on both paths, appending only allowlisted client betas (preserving the #3415 fix). (#3999 — thanks @huohua-dev / @diegosouzapw) - fix(executor): strip
stream_optionson non-streaming requests (NVIDIA NIM 400) — clients that sendstream_options: { include_usage: true }regardless ofstream(e.g. the OpenAI Python SDK) had it passed through untouched on non-streaming calls, and NVIDIA NIM rejected it with400 "Stream options can only be defined when stream=True".DefaultExecutor.transformRequestnow stripsstream_optionswheneverstreamis false; the streaming injection path is unchanged. (#4000 — thanks @andrea-kingautomation / @daniij / @diegosouzapw) - fix(sse): guard model-less registry entries in
getUnsupportedParams(mimocode) — a registry entry without a model map (mimocode) threw when computing unsupported params; the lookup now guards the model-less case so request validation no longer crashes. (#4015 — thanks @diegosouzapw) - fix(perplexity-web): parse the schematized
diff_blockstream so answers aren't empty — Perplexity web streamed its answer as RFC-6902diff_blockpatches that OmniRoute didn't apply during thePENDINGphase, so responses came back empty; the parser now applies the patches and materializes the text only onCOMPLETED. (#4001 — thanks @artickc) - fix(default-executor): honor a custom
providerSpecificData.baseUrlfor OpenAI-format providers — OpenAI-format providers configured with a custom base URL had it ignored on the inference path; the default executor now honorsproviderSpecificData.baseUrlso requests reach the configured endpoint. (#4002 — thanks @artickc) - fix(live-ws): bridge LiveWS sidecar events to the dashboard — events emitted by the LiveWS sidecar were not reaching the dashboard; they are now bridged so live websocket activity is visible. (A cookie-auth regression in the sidecar's auth-token parsing was also corrected.) (#4004 — thanks @megamen32)
- fix(qwen-web): cookie validation false-positive — check the response body for a user object — Qwen web cookie validation reported a valid cookie as invalid; it now inspects the response body for the
userobject instead of relying on the status code alone. (#3958 — thanks @thezukiru) - fix(vision-bridge): force the bridge for tokenrouter deepseek models — tokenrouter DeepSeek models are now forced through the Vision Bridge so image inputs are handled correctly. (#3946 — thanks @WormAlien)
- fix(api): return 400 (not 500) for malformed JSON on
/api/auth/login— a malformed JSON body on the login endpoint returned an opaque 500; it now returns a proper 400. (#4031 — thanks @rdself) - fix(dashboard): Playground Compare tab loading + HTTP method guard — the Playground Compare tab failed to load; the loading path was fixed and an HTTP method guard added. (#4024 — thanks @rdself)
- fix(proxy): gate the control-plane proxy direct fallback behind a feature flag (fail-closed) — the direct-connection fallback for control-plane ops when a pinned proxy is unreachable is now gated behind a feature flag and fails closed, so a pinned proxy is never silently bypassed unless explicitly allowed. (#3963 — thanks @rdself)
- fix(db): persist backup retention days — the backup retention-days setting was not persisted across restarts; it is now stored durably. (#3970 — thanks @rdself)
- fix(dashboard): refine the provider quota card display — the provider quota card layout was refined for clearer quota/usage presentation. (#3969 — thanks @rdself)
- fix(dashboard): refine compression settings, storage labels, and sidebar grouping — polishes the compression-settings UI, clarifies storage labels, and tidies the sidebar grouping. (#4033 — thanks @rdself)
🔒 Security & Hardening
- fix(security): eliminate a polynomial ReDoS in the combo
<omniModel>tag regex —comboAgentMiddleware's cache-tag pattern wrapped the tag in an unbounded newline run ((?:\n|\r)*), making.test()/.replace()run in O(n²) on inputs with many newlines (CodeQLjs/polynomial-redos). The detection pattern now matches only the core<omniModel>…</omniModel>and the global strip pattern bounds the surrounding newline runs, keeping it linear; detection / extraction / multi-tag stripping behavior is unchanged. (#3982 — thanks @diegosouzapw) - ci(security): harden workflows — artipacked
persist-credentials, cache-poisoning, SC2086 — GitHub Actions workflows were hardened against the artipackedpersist-credentialsleak and cache-poisoning, and shell-quoting (SC2086) issues were fixed. (#3965 — thanks @diegosouzapw) - ci(quality): flip require-tighten + osv + Trivy to blocking (cycle-end) — the per-module require-tighten check and the OSV / Trivy scanners moved from advisory to blocking for the v3.8.27 cycle close, so new dependency or coverage regressions fail CI. (#3984 — thanks @diegosouzapw)
- chore(deps): dependabot security bumps + drop unused gray-matter — applies a batch of Dependabot security bumps and removes the unused
gray-matterdependency from the tree. (#4036 — thanks @diegosouzapw) - chore(deps): automated dependency bumps — Dependabot upgraded the production dependency group (13 updates),
vite,form-data, and thenpm_and_yarngroup. (#3915, #3942, #3943, #3944 — thanks @dependabot)
🧹 Internal / Quality / Docs
- feat(ci): Quality Gate v2 — Onda 0 + Onda 1 — first two waves of the Quality Gate v2 program: gate flips, test-impact analysis (TIA), SAST, DAST-smoke, and mutation-testing infrastructure. (#4016 — thanks @diegosouzapw)
- refactor: modularize the provider registry into individual provider plugins —
providerRegistry.tswas split into individual per-provider plugin modules (non-stacked). A forward-fix restored thebyteplus+mimocodemodules dropped by the move. (#3993 — thanks @oyi77 / @diegosouzapw) - refactor: modularize schemas (non-stacked) — the request/response schema definitions were split into individual modules to reduce file size and improve maintainability. (#3988 — thanks @oyi77)
- fix: restore unit regressions dropped by the lossy schema/registry modularizations — the schema/registry modularizations (#3988, #3993) silently dropped internal logic covered by unit tests; this PR restores the regressed units. (#4030 — thanks @diegosouzapw)
- refactor(dashboard): settings UI layout + API Keys naming — the settings UI layout was reorganized and the "API Keys" naming clarified. (#4020 — thanks @rdself)
- 大量UI显示和i18n优化 (dashboard UI display + i18n improvements) — a batch of dashboard UI-display refinements and i18n string improvements. (#3973 — thanks @rdself)
- fix(ci): scope TIA to
node:testunit files only — test-impact analysis was matching files thenode:testrunner doesn't execute, producing 99 false failures; the TIA glob now mirrors thetest:unitglob exactly. (#4035 — thanks @diegosouzapw) - fix(ci): electron-release publish-npm needs
contents: write— the reusable npm-publish job invoked by the electron release lackedcontents: write, causing a v3.8.26startup_failure; the permission was granted. (#3966 — thanks @diegosouzapw) - test(opencode-plugin): ESM default-export test (drop the stale CJS bundle test) — replaces the stale CJS bundle test with an ESM default-export test, following up the #3883 ESM-only migration. (#3967 — thanks @diegosouzapw)
- fix(ci): Fix promptfoo security-assertion parsing — the promptfoo (DAST/security eval) assertion parser was corrected so security assertions are read reliably. (#4032 — thanks @rdself)
- docs(troubleshooting): note that the MITM proxy cannot intercept Windows-host apps under WSL — documents that the MITM proxy running inside WSL cannot intercept traffic from apps on the Windows host. (#4003 — thanks @diegosouzapw)
- chore(quality): maintenance roll-up — assorted quality-gate hygiene that does not change runtime behavior: re-baseline
validation.tsfor the #3958 qwen body-check, allowlist thesocksdependency declared by #4004, ignore jscpd major bumps (the v5 Rust rewrite breaks the pinned duplication gate), untrack an accidentally-committed rootnode_modulessymlink (and gitignore it), rehome the #3972 logs auto-refresh test so a runner collects it, and open the v3.8.27 development cycle. (thanks @diegosouzapw)
[3.8.26] — 2026-06-15
✨ New Features
- feat(media): Vertex AI (Google) speech, transcription, music & video generation — Vertex AI's Google media models are now routable through dynamic discovery: speech synthesis, audio transcription, music generation, and video generation. (#3929 — thanks @artickc)
- feat(glm): add GLM-5.2 with effort-tier routing (high/max) — GLM-5.2 is registered with high/max effort-tier routing. (#3885 — thanks @dhaern)
- feat(combo): add a sticky round-robin target limit — round-robin combos can cap how many targets stay "sticky" within a session (
stickyRoundRobinLimit), balancing stickiness against spread. (#3846 — thanks @adivekar-utexas) - feat(openrouter): connection presets — OpenRouter connections support reusable presets (provider routing / sort / quantization preferences), selectable when adding a connection. (#3878 — thanks @rdself)
🐛 Fixed
- fix(compression/memory): stop memory + compression from poisoning the upstream prompt cache — with compression and/or memory enabled, requests to caching providers (Anthropic-family) missed the prompt cache on every turn, multiplying cost. Two root causes: (1) memory injection prepended the retrieved memories — which vary per user query — at index 0 of the message array, shifting the entire cacheable prefix every turn; memory is now inserted just before the last user message when the request carries
cache_controlbreakpoints, keeping the cacheable prefix (system prompt + prior turns) byte-stable. (2) the cache-awareskipSystemPromptflag computed bygetCacheAwareStrategy()was dropped byselectCompressionStrategy()(which can only return a mode), so the system prompt could still be compressed under caching; a newresolveCacheAwareConfig()now forcespreserveSystemPrompton for caching requests. (#3936, closes #3890 — thanks @xenstar / @diegosouzapw) - fix(providers): register BytePlus ModelArk so its API key can be added — adding a BytePlus (
ark-…) key reported "invalid".bytepluswas present in the provider catalog (APIKEY_PROVIDERS) but never registered in the routing registry, so key validation fell through to{ unsupported: true }→ HTTP 400 → the UI rendered every key as invalid (and the provider was unusable for inference). Added a registry entry modeled on the existing Volcengine Ark provider: OpenAI-compatible format, basehttps://ark.ap-southeast.bytepluses.com/api/v3(regionap-southeast-1),Authorization: Bearerauth, seeded with the catalog's advertised models (Seed 2.0, Kimi K2 Thinking, GLM 4.7, GPT-OSS-120B). (#3935, closes #3877 — thanks @nikohd12 / @diegosouzapw) - fix(providers): Nous Research key validation no longer fails on a stale probe model — adding a valid Nous Research API key reported "invalid" even though the same key worked via the portal's copy-shell
curl. The validation probe sentmodel: "nousresearch/hermes-4-70b", which Nous does not serve, so the API returned400and the validator (which only treated200/429as success) reported the key invalid. The probe now uses the realHermes-4-70Bslug, and any non-auth 4xx (400/404/422) is treated as a valid key (the request shape was wrong, not the credentials) — mirroring the longcat/nvidia validators so a future model rename can't re-break key validation. (#3934, closes #3881 — thanks @FerLuisxd / @diegosouzapw) - fix(stream): persist mid-stream upstream failures — when an upstream stream fails partway through, the partial response and incremental usage are now finalized and persisted instead of lost; extracts a shared
streamFailureFinalizationpath and merges incremental Claude usage (follow-up to #3879). (#3937 — thanks @rdself) - fix(perplexity-web): update the request payload to schema v2.18 (HTTP 400) — Perplexity web requests started returning HTTP 400; the request payload was updated to Perplexity's v2.18 schema. (#3938 — thanks @artickc)
- fix(stream): keep the in-flight request payload in sync — the pending-by-id request record is now updated in place (
Object.assign) so the in-flight payload stays consistent with what was dispatched (coexists with #3937). (#3940 — thanks @rdself) - fix: stabilize reasoning streams and request logs — reasoning-token streaming and the request-log capture path were stabilized to avoid dropped/duplicated reasoning frames and inconsistent log entries. (#3879 — thanks @rdself)
- fix(opencode-plugin): include nested combo-refs in the LCD context window — the OpenCode plugin now follows nested combo references when computing the least-common-denominator context window, so a combo nested inside another no longer reports an inflated window. (#3910 — thanks @herjarsa)
- fix(models): correct the failed-model auto-hide defaults — the defaults governing when a failed model is auto-hidden were corrected, and auto-hide is now opt-in so models are no longer dropped unexpectedly. (#3930 — thanks @rdself)
- fix(openrouter): show the preset field when editing a connection — the connection-preset field appeared only when creating a connection, not when editing one; it now appears in both (follow-up to #3878). (#3921 — thanks @rdself)
- fix(sse): announce the assistant role on the first delta (Responses→Chat) — the first SSE delta of a Responses-API→Chat-Completions stream now carries
role: "assistant", which strict OpenAI-compatible clients expect before content deltas. (#3911 — thanks @diego-anselmo) - fix(vertex): add the generative-language scope so SA-JSON model discovery works — Vertex service-account (SA-JSON) model discovery failed without the
generative-languageOAuth scope; the scope is now requested. (#3922 — thanks @artickc) - fix(proxy): direct-connection fallback for control-plane ops when a pinned proxy is unreachable — control-plane operations (validation, discovery) now fall back to a direct connection when a connection's pinned proxy is unreachable, instead of failing outright. (#3906 — thanks @zhiru)
- fix(providers): prevent zombie-socket hangs for zai/glm and tighten the default keepAlive — zai/glm could hang on dead keep-alive sockets; the default keepAlive was tightened to evict zombie sockets. (#3907 — thanks @insoln)
- fix(setup): remove the stale CJS bundle check from setup-open-code — the OpenCode setup helper no longer checks for a CJS bundle that the now ESM-only plugin no longer ships. (#3908 — thanks @herjarsa)
- fix(opencode-plugin): drop the CJS bundle to fix the OpenCode plugin loader — the plugin is now ESM-only, fixing the OpenCode loader which failed on the dual CJS/ESM build. (#3883 — thanks @herjarsa)
- fix(mcp): fall back to
node:sqlitewhen the better-sqlite3 binding is missing — the MCP server now falls back to Node's built-innode:sqlitewhen the native better-sqlite3 binding is unavailable, instead of crashing. (#3887 — thanks @megamen32) - fix(models): correct the generate-models alias lookup — alias resolution during model generation was corrected so aliased model ids resolve to their canonical entry. (#3870 — thanks @YunyunZhai)
- fix(combo): guard the candidate pool against an empty array — combo candidate-pool selection no longer throws when the pool resolves to an empty array. (#3871 — thanks @YunyunZhai)
🔒 Security & Hardening
- fix(security): bump form-data + vite (2 HIGH), harden workflow template-injection & allowlist guarded
workflow_run— two HIGH Dependabot advisories (form-data,vite) were upgraded; GitHub Actions workflows were hardened against${{ }}template-injection (untrusted values now passed viaenv:); and the guardedworkflow_runtrigger was allowlisted. (#3949 — thanks @diegosouzapw)
🧹 Internal / Quality / Docs
- fix(ci): grant
contents: writeto the npm publish job for SBOM attach — the v3.8.25 TokenPermissions hardening set the npm-publishpublishjob tocontents: read, but its "Attach SBOM to GitHub Release" step (gh release upload) needscontents: writeand failed with HTTP 403 on the v3.8.25 release (npm / GitHub Packages / opencode-plugin / Docker / Electron all published fine; only the SBOM attach broke — the v3.8.25 SBOM was attached manually). (#3874 — thanks @diegosouzapw) - ci(quality): make zizmor / gitleaks / osv scanners functional + freeze advisory baselines — the supply-chain scanners are now actually executed (correct install + invocation) with frozen advisory baselines so new findings surface as diffs. (#3947 — thanks @diegosouzapw)
- ci(quality): fix scanner install + size-limit preset, promote
codeqlAlertsto blocking — corrected the scanner install and the size-limit preset, and promoted thecodeqlAlertsratchet from advisory to blocking. (#3945 — thanks @diegosouzapw) - ci(quality): wire Stryker mutation testing as an advisory nightly — Stryker mutation testing runs nightly (advisory) — Quality Gates Fase 7 · Task 11. (#3898 — thanks @diegosouzapw)
- ci(quality): freeze per-module coverage floors + wire require-tighten (advisory) — per-module coverage floors are frozen with an advisory "require-tighten" check that flags modules drifting below their floor. (#3901 — thanks @diegosouzapw)
- ci(quality): enforce the stale-allowlist check on
check-known-symbols— stale allowlist entries (suppressing a symbol that no longer exists) now fail the gate — Fase 6A.3 follow-up. (#3899 — thanks @diegosouzapw) - test(ci): de-flake pipeline-payloads via per-test re-seed + honest reset — the pipeline-payloads suite now re-seeds per test and performs an honest cache reset, eliminating a cross-test ordering flake. (#3893 — thanks @diegosouzapw)
- fix(ci): drop the
secrets-in-job-iffrom nightly-llm-security — referencingsecretsin a job-levelifcaused astartup_failureon push; the gating was moved so the workflow starts cleanly. (#3892 — thanks @diegosouzapw) - test: reconcile the runtime-timeouts keepAlive baseline to 4000 after the #3907 source revert — the keepAlive assertion was realigned to the source value (4000) after #3907's source-side revert. (#3933 — thanks @diegosouzapw)
- chore(repo): nest quality-gate state under
config/quality, declutter the repo root — baselines / allowlists / metrics moved underconfig/quality/, trimming the tracked root file count. (#3896 — thanks @diegosouzapw) - docs: refresh the provider count to 226 + regenerate
PROVIDER_REFERENCE.md— the README advertised a stale177 providers; the canonical generator (scripts/docs/gen-provider-reference.ts) now reports 226 unique provider IDs, so the README badges/anchors and the generated provider reference were brought in sync. Also adds a documentation audit/sync report. (thanks @diegosouzapw) - docs: sync all documentation to v3.8.24 + count-guard & wiki/prose CI — a full documentation sync with a strict provider/locale count-guard plus Vale / markdownlint prose CI. (#3804 — thanks @diegosouzapw)
- docs: regenerate stale counts to canonical values — 226 providers / 87 MCP tools / 15 strategies / 42 locales. (#3904 — thanks @diegosouzapw)
- docs(quality): correct the stale gate count + add an opt-in agent-lsp scaffold — (#3902 — thanks @diegosouzapw)
- docs(mcp): correct the MCP tool-inventory diagram source + text to 87 tools — (#3909 — thanks @diegosouzapw)
- docs: update the compression section to the 9-engine multi-layer stack — (#3894 — thanks @diegosouzapw)
- ci(docs): automate GitHub wiki sync (add missing pages + cover counts) — (#3900 — thanks @diegosouzapw)
- docs: require a dedicated git worktree + branch per development task (Hard Rule #19) — codifies the worktree-isolation rule after the shared-checkout incidents. (#3939 — thanks @diegosouzapw)
- fix(docs): add MDX frontmatter to
DOCUMENTATION_AUDIT_REPORTso the fumadocs build passes — the audit report lacked thetitle:frontmatter MDX pages require. (thanks @diegosouzapw)
[3.8.25] — 2026-06-14
✨ New Features
- feat(compression): pluggable compression engines + async pipeline + Compression Studios — a new prompt-compression subsystem with selectable engines (Lite / Aggressive / Ultra), an asynchronous compression pipeline wired into the chat core, and "Compression Studios" tooling for inspecting and tuning compression. (#3848)
- feat(compression-ui): unified compression configuration UI — a Compression Hub with per-engine pages (Lite / Aggressive / Ultra), a combos editor, a dedicated sidebar entry, and live-WS default-on. (#3860)
- feat(security): prompt-injection guard across every LLM route + red-team suite — the prompt-injection guard now runs on all LLM routes (chat, responses, embeddings, images, audio, rerank, search, moderations, videos, music) with a shared input sanitizer and a promptfoo-based red-team suite (Quality Gates Fase 8 · Bloco D). (#3857)
- feat(kiro): live per-account model discovery — Kiro now discovers each account/tier's entitled models via CodeWhisperer
ListAvailableModels(region-matched, with a static-catalog fallback). (#3836 — thanks @artickc) - feat(gemini/vertex): surface Veo video models in dynamic discovery — Veo video models (
predictLongRunning) now appear in Gemini/Vertex dynamic model discovery. (#3839 — thanks @artickc) - feat(mimocode): per-account proxy for multi-account round-robin — each mimocode account can route through its own proxy (resolved per account by fingerprint via
runWithProxyContext), with a "Distribute proxies" UI helper. (#3837 — thanks @pizzav-xyz) - feat(intelligence): expose Arena ELO sync as a feature flag — the LM Arena ELO leaderboard sync is now toggleable (
ARENA_ELO_SYNC_ENABLED, DB-override + env fallback). (#3821 — thanks @rdself)
🐛 Fixed
- test(oauth): prove refresh_token preservation for the real gemini-cli / antigravity dispatch — the #3679/#3766 regression test used a synthetic provider that routes through the generic
tokenUrlpath, so the fix was never proven for the actual Google-family providers, which dispatch throughrefreshGoogleToken()against the hardcodedOAUTH_ENDPOINTS.google.token. Added a test that drivescheckConnectionthrough the realgemini-cli/antigravitypath (redirecting the Google token endpoint to a local server returninginvalid_grant) and asserts therefresh_tokenis preserved (not nulled) — confirming these connections are not spuriously destroyed on a failed refresh. (#3850 — thanks @3xa228148) - fix(oauth): clear setup message for GitLab Duo instead of "Internal server error" — adding a GitLab Duo connection without a registered OAuth client returned an opaque
Internal server errorat the Add Connection step.buildAuthUrlthrew whenGITLAB_DUO_OAUTH_CLIENT_IDwas missing, and the route swallowed it into a generic 500. It now returnsnull(mirroring the Qoder provider) and the authorize route surfaces an actionable message: register an OAuth app athttps://gitlab.com/-/profile/applicationswith redirect URIhttp://localhost:20128/callbackand scopesai_features read_user, then setGITLAB_DUO_OAUTH_CLIENT_ID. (#3861 — thanks @sidinsearch) - fix(db): persist the "Keep latest backups" retention setting — changing the backup-retention count in Settings → Database backup retention had no effect: it always snapped back to 20 on refresh (and editing
.envpost-start was ignored too, sinceprocess.envisn't reloaded).getDbBackupMaxFiles()only read theDB_BACKUP_MAX_FILESenv var — there was no setter and no persisted value. The value now round-trips through a dedicatedkey_valuestore (getDbBackupMaxFilesprecedence: env override → persisted UI value → default 20), and the "Clean old backups" action persists the chosen count. Existing installs keep the historical default of 20 until explicitly changed. (#3834 — thanks @netstratego) - fix(sse): clamp Gemini thinking budget to the model's real cap (
reasoning_effort/effort=high400) — translating OpenAIreasoning_effort=high(and Claude-Codeoutput_config.effort=high) to a Gemini target sent a hardcodedthinkingBudget: 32768, which exceeds Flash-tier Gemini's real max of 24576 → upstream HTTP 400 (thethinkingLevel=highpath already used 24576 and worked on the same model).gemini-2.5-flashnow declares its realthinkingBudgetCap(24576) so the existingcapThinkingBudget()chokepoint actually clamps, and the Claude→Geminioutput_config.effortpath — which previously sent the raw value with no cap at all — now routes through the same clamp (pro-tier, real cap 32768, is left untouched). (#3842 — thanks @andrea-kingautomation) - fix(intelligence): run pricing + models.dev sync from the live startup path — like the Arena ELO sync (v3.8.24), the external pricing sync (
PRICING_SYNC_ENABLED) and the models.dev capability sync (Settings → AI toggle) were only initialized fromserver-init.ts, which the Next standalone runtime never executes — and models.dev had no caller at all. Their toggles were inert in production. Both are now initialized frominstrumentation-node.ts(self-gated, opt-in preserved, non-blocking, never fatal). (thanks @diegosouzapw) - test(proxy): guard the per-connection 'direct' bypass over a global proxy + clearer label — the per-connection "Proxy Off" toggle (
proxyEnabled: false) already overrides a configured global proxy (resolveProxyForConnectionshort-circuits tolevel: "direct"before the global step). Added an explicit regression test proving the bypass beats a global assignment (and round-trips on re-enable), and relabeled the UI to "Direct (bypass proxy)" so operators recognize it. Closes the verification gap in #2996. (thanks @diegosouzapw) - feat(connections): per-connection "disable cooldown" opt-out — a connection can now opt out of the transient cooldown (
providerSpecificData.disableCooling, with a toggle in the Edit Connection modal). When set, a recoverable failure still records the error/backoff but does not take the connection out of rotation, so it stays eligible for selection — useful for a primary key you never want parked on a blip. Terminal states (banned / expired / credits_exhausted) still apply. (#2997 — thanks @diegosouzapw) - fix(combo): restore sessionless combo stickiness + reasoning-aware readiness (504 / TPS regression after v3.8.14) — #3399 (v3.8.16) replaced the
<omniModel>-tag combo pinning with a server-side context-cache pin gated on a clientsessionId. Clients that send no session id (most OpenAI-compatible tools) lost combo stickiness, so combos re-ran strategy selection every turn → upstream prompt-cache misses → cold high-reasoning starts (~78s) → intermittent[504] Upstream request did not return response headers+ TPS collapse (only on combos). The pin now falls back to a stable per-conversation fingerprint (extractSessionAffinityKey(body)) when no session id is present — only whencontext_cache_protectionis on, so #3399's anti-leak behaviour is preserved. Separately, the stream-readiness window now grants the +30s reasoning budget unconditionally for high-reasoning Codex GPT-5.x (small high-reasoning prompts were 504-ing at the 80s base regardless of stickiness). (#3825 — thanks @bypanghu) - test(combo): cover the
skipProviderBreakerconsumer gate — the producer was tested but the consumer (whether a failed combo target trips the whole-provider circuit breaker) was not; the breaker decision is now an exported pure predicate (shouldRecordProviderBreakerFailure, behaviour-identical) with direct tests asserting aconnection_cooldown503 does not trip the breaker while a plain 503 does. Closes another deferred test gap from #2743. (thanks @diegosouzapw) - fix(providers): surface the real Devin error + correct the Windsurf auth instructions — Devin chat returned a generic 502 "Invalid SSE response for non-streaming request" that swallowed the real cause (e.g. "Devin CLI not found"): an error-only SSE chunk (no
choices) is now propagated with its sanitized message. The Windsurf "Visit windsurf.com/show-auth-token" instruction (the bare URL shows no token without an IDE-supplied?state=) now directs users to theWindsurf: Provide Auth Tokencommand-palette flow. (#3324 — thanks @mikmaneggahommie) - fix(grok-web): clearer 403 message for anti-bot / IP-reputation blocks — a Grok Web subscription validating from a flagged datacenter/VPS IP got a 403 that read like an invalid cookie, sending users to chase a cookie that was actually fine. A non-auth 403 (Cloudflare challenge / anti-bot body) now returns a message stating the cookie is likely OK and the block is IP-reputation-based — retry from a residential IP or configure a proxy (auth-shaped 403s keep the re-paste guidance). (#3474 — thanks @friedtofu1608)
- fix(db): make the mass-pending-migrations safety threshold env-overridable — restoring a backup DB from an older version could trip "Detected N pending migrations … threshold is 50" with no way to override the hardcoded
50. The threshold is now configurable viaOMNIROUTE_MAX_PENDING_MIGRATIONS(resolved at startup;0disables the check). (#3416 — thanks @samuraiIT) - test(proxy): cover the Vercel-relay
proxyFetchpath — net-new tests forbuildVercelRelayHeadersand thevercel-type relay short-circuit (x-relay-target/-path/-auth, TCP-skip, missing-auth fail-closed), closing one of the deferred test gaps tracked in #2743. (thanks @diegosouzapw) - fix(cli): surface
omniroute runtime repairin the native-module error messages — after a Node major upgrade,better-sqlite3's prebuilt binary mismatches the ABI and the service can crash-loop; the error only mentionednpm rebuild better-sqlite3(which fails for global / no-toolchain installs). The startup + SQLite error hints now also point to the existing self-heal commandomniroute runtime repair(rebuilds into a user-writable runtime), and a top-levelomniroute repairalias was added. (#3476 — thanks @Rahulsharma0810) - fix(antigravity): per-request Pro-family upstream-id fallback chain (
gemini-3.1-pro-high400) — Antigravity silently renamed the Gemini 3.1 Pro-high upstream id, sogemini-3.1-pro-highstarted returning HTTP 400 (while-lowstill worked) and the live id can't be determined statically (competitor proxies disagree). The executor now retries alternative ids on a 400 (gemini-3.1-pro-high→gemini-pro-agent→gemini-3-pro-high, analogous for pro-low), bounded and only on a 400, with zero extra cost on the happy path; the 1:1 tier-passthrough invariant is preserved (the chain is request-time, not a static alias remap). (#3786 — thanks @aliaksandrsen) - fix(sse): retry once on an early stream close (
STREAM_EARLY_EOF) for single-model requests — flaky OpenAI-compatible upstreams (e.g. NVIDIA NIM with minimax-m3 / qwen3.5 / glm-5.1) intermittently send HTTP 200 then close the SSE with zero useful frames, surfacing as a 502 "Stream ended before producing useful content". Only Antigravity got an early-close retry; every other provider returned the 502 immediately on the non-combo single-model path. A bounded one-retry (early-close only — not readiness-timeout — and without marking the account unavailable) now generalizes it. (The separate qwen-web validation SSRF part of the same report was already fixed in v3.8.24, #3767.) (#3758 — thanks @Svatosalav) - fix(models): preserve eye-hidden models across auto-sync / import — hiding models via the visibility (eye) toggle to keep only a combo's models was undone on every model import or auto-sync, which re-showed all of them. The sync re-import treated "hidden" identically to "deleted" and dropped both; a distinct
isDeletedmarker now separates the trash/delete path (still dropped on re-import, #3199) from the eye toggle (preserved as listed-but-hidden), and eye-hidden models are no longer re-aliased into the routable catalog on sync. (#3782 — thanks @xenstar) - fix(providers): correct the lmarena cookie hint (
session→arena-auth-prod-v1) — the lmarena credential hint asked for a cookie namedsession, but lmarena.ai's real auth cookie isarena-auth-prod-v1, so users who pasted onlysession=…hit validation failures. The credential name, placeholder and storage keys now use the correct name (the legacysessionkey is retained for back-compat with already-saved credentials). (#3810 — thanks @xspylol) - fix(reasoning): normalize OpenAI-compatible
maxeffort toxhighby default — OpenAI-compatible providers do not accept literalmax, but some upstreams (for example DeepSeek through OpenRouter) supportxhigh;maxnow maps toxhighunless the target model explicitly opts out ofxhigh, with Claude alias variants still honoring the canonical Claude opt-out list. (#3826 — thanks @rdself) - fix(combo): return the replay response on the round-robin streaming path — a round-robin combo with a streaming target returned a body already locked by the readiness peek, surfacing as a 500 "ReadableStream is locked"; the round-robin path now returns the replay clone like the priority path does. (#3811 — thanks @0xtbug)
- fix(claude): strip the reasoning-effort suffix from Claude model ids — Claude ids carrying an effort suffix (
…-low……-max) 404'd upstream and tripped the circuit breaker into a misleading "rate-limited" state; the suffix is now stripped before dispatch. (#3807 — thanks @zhiru) - fix(sse): flush routed SSE chunks promptly (ping/zombie readiness filter) — combo stream-readiness now filters ping/zombie frames so routed SSE chunks stream out without waiting on the readiness window. (#3759 — thanks @rdself)
- fix(models): don't auto-hide transient (rate-limited / timeout) failures on Test All — a parallel Test All across many models could rate-limit an account and auto-hide every model that 429'd / timed out (dropping them from
/v1/models); transient failures now surface an error state but stay visible. (#3849 — thanks @lukmanc405) - fix(quota): surface OpenCode Go's missing quota-API as a latched diagnostic — OpenCode Go keys whose quota endpoints return 404/401 no longer hammer the dead endpoints; the gap is latched with a clear message and an
OMNIROUTE_OPENCODE_GO_QUOTA_URLoverride hint. (#3838 — thanks @adivekar-utexas) - fix(pricing): add the missing Kiro model pricing rows — Kiro models the registry serves (e.g.
claude-sonnet-4.6) had no pricing row and reported $0.00; the rows were added. (#3835 — thanks @artickc) - fix(ui): render country flags via flagcdn SVGs for Windows compatibility — Windows doesn't render regional-indicator flag emoji; flags now use flagcdn SVGs with an emoji fallback. (#3814 — thanks @rafacpti23)
- fix(ui): expand the request log table with a vertical resize handle — the request log table now shows ~10 rows and can be resized vertically. (#3820 — thanks @rafacpti23)
- fix(i18n): translate the missing
embeddedServiceskeys across 37 locales — theembeddedServicesstrings showed__MISSING__in 37 locales; they are now translated. (#3819 — thanks @rafacpti23)
🔒 Security & Hardening
- fix(security): CCR cross-tenant IDOR — per-principal scope store + bounded memory — the compression CCR scope store was shared across principals, allowing cross-tenant reads; it is now scoped per-principal with bounded memory. (#3859)
- feat(supply-chain): build provenance, SBOM, Trivy scan & OpenSSF Scorecard (advisory) — added npm build provenance, a CycloneDX SBOM, Trivy image scanning, and an OpenSSF Scorecard workflow (Quality Gates Fase 8 · Bloco A, advisory). (#3824)
🧹 Internal / Quality / Docs
- Consolidate the email-privacy control into Settings → Appearance — the per-page email-privacy toggles were replaced by a single global switch. (#3822 — thanks @rdself)
- docs(ui): clarify the routing-settings copy (strategy sync + sticky limit) — (#3843 — thanks @adivekar-utexas)
- Quality Gates — Fase 7 & 8 — promoted the dead-code / cognitive-complexity / type-coverage ratchets to blocking, installed advisory CI scanners (gitleaks / osv / actionlint / zizmor), and added property + golden + SSE-correctness tests and a runtime-resilience (chaos / heap-growth / k6 soak) suite. (#3809, #3858, #3808, #3854)
- fix(docs): add MDX frontmatter to
SUPPLY_CHAIN.md— the new security doc lacked thetitle:frontmatter that MDX pages require, which broke the production Build + Docker Hub publish; the frontmatter was added. (#3864) - chore(deps): bump
aquasecurity/trivy-action0.28.0 → 0.36.0 (#3862) - chore(quality): reconcile the file-size ratchet baseline for Prettier-inflated v3.8.25 fixes +
chat.tsgrowth — the per-file size baseline was re-frozen to absorb the formatting/line-count growth from this cycle's chat-core and combo fixes (manual edits, never an automatic upward ratchet). (#3823, #3833 — thanks @diegosouzapw) - test(suite): green the unit suite at release time — align stale tests to this cycle's intended behavior + de-flake two new suites — release-gate housekeeping: updated tests that lagged behind intended behavior changes (OpenCode Go latched quota message #3838, the email-privacy control consolidated into Settings #3822, SOCKS5 default-on proxy-type message, the
[id]provider-detail strangler-fig decomposition #3501, Vertex Express-mode keys, Antigravity discovery using a current user-callable model id) and the same-provider 503 fall-through resilience test; de-flaked the compression benchmark reproducibility test (sequential passes) and the ServiceSupervisor crash test (poll instead of fixed sleep). No production code changed. Also documentedOMNIROUTE_MAX_PENDING_MIGRATIONS(#3416) in.env.example+ENVIRONMENT.md. (thanks @diegosouzapw)
[3.8.24] — TBD
See English CHANGELOG for v3.8.24 details.
[3.8.22] — TBD
See English CHANGELOG for v3.8.22 details.
[3.8.21] — 2026-06-11
See English CHANGELOG for v3.8.21 details.
[3.8.20] — Unreleased
Development cycle in progress.
[3.8.19] — Unreleased
Development cycle in progress.
[3.8.18] — Unreleased
Development cycle in progress.
[3.8.17] — Unreleased
Development cycle in progress.
[3.8.16] — Unreleased
Development cycle in progress.
[3.8.15] — Unreleased
Development cycle in progress.
[3.8.14] — Unreleased
Development cycle in progress.
[3.8.13] — Unreleased
Development cycle in progress.
[3.8.12] — Unreleased
Development cycle in progress.
[3.8.11] — Unreleased
Development cycle in progress.
[3.8.10] — Unreleased
[3.8.9] — Unreleased
[3.8.8] — 2026-06-01
Added
- Plugins framework (
src/lib/plugins/,/api/plugins/*,/dashboard/plugins) — hooks + registry unification, plugin SDK (definePlugin), worker-thread sandbox, per-plugin hook rate limiting, SHA-256 integrity verification, semver-gated upgrade, and execution analytics. Plugin routes are loopback-only (isLocalOnlyPath) andchild_processexec is opt-in viaOMNIROUTE_PLUGINS_ALLOW_EXEC. (#2913 / #3041 — thanks @oyi77) - Plugin system: response-hook wiring + startup load + example plugin — wires the plugin
onResponsehook into the chat success path, loads active plugins on server startup so they survive restarts (pluginManager.loadAll()inserver-init), and ships awelcome-bannerexample plugin (examples/plugins/) plus a comprehensive plugin test suite. (#3045 — thanks @oyi77) - API key option: disable non-published models — a per-key flag restricting the key to discovered, public models (combos /
auto/*/qtSd/*routing still allowed). (#3017 — thanks @androw) - SessionPool — modular & provider-agnostic (
open-sse/services/sessionPool/) — pooled cookie/session manager with round-robin fingerprint rotation (distinct fingerprint per pooled session), per-session cooldown/backoff, and a provider-agnosticwebExecutorWrapper. Adds pool support for DuckDuckGo Web and LLM7 providers and an MCPpoolToolstoolset. (#2954 / #2978 — thanks @oyi77) - AgentBridge (
/dashboard/tools/agent-bridge) — MITM proxy consolidating 9 IDE agents (Antigravity, Kiro, GitHub Copilot, OpenAI Codex, Cursor IDE, Zed Industries, Claude Code, Open Code, Trae stub) with server card, per-agent setup wizard, model mapping table, bypass list, upstream CA cert support, and redirect from legacy/dashboard/system/mitm-proxy. Seedocs/frameworks/AGENTBRIDGE.md. (#2858 — thanks @diegosouzapw) - Traffic Inspector (
/dashboard/tools/traffic-inspector) — LLM-aware HTTPS debugger with 4 capture modes (AgentBridge hook, Custom Hosts DNS, HTTP_PROXY :8080, System-wide proxy), DevTools split UI, 7 detail tabs (Conversation, Headers, Request, Response, Timing, LLM Details, Stats), resizable panels, session recording (.har/.jsonl export), SSE stream merger, conversation normalizer (multi-provider), system-prompt fingerprint colorization, and annotations. Seedocs/frameworks/TRAFFIC_INSPECTOR.md. - MITM handler base + 9 agent handlers (
src/mitm/handlers/) —MitmHandlerBaseabstract class withhookBufferStart/hookBufferUpdatefor Traffic Inspector integration; concrete handlers for all 9 agents. - MITM targets registry (
src/mitm/targets/) — declarativeMitmTargetshape per agent; emitsDATA_DIR/mitm/targets.jsonfor dynamicserver.cjsresolution. - Traffic Inspector core (
src/mitm/inspector/) —TrafficBufferin-memory ring,kindDetector,sseMerger(MIT port from chouzz/llm-interceptor),conversationNormalizer(MIT port),contextKeyfingerprinting,httpProxyServer,systemProxyConfig. - AgentBridge passthrough + bypass (
src/mitm/passthrough.ts) — TCP tunnel for non-mapped hosts; bypass list with default sensitive-host patterns + user-defined patterns. - Upstream CA cert (
src/mitm/upstreamTrust.ts) —AGENTBRIDGE_UPSTREAM_CA_CERTfor corporate TLS environments. - Secret masking (
src/mitm/maskSecrets.ts) — sk-/Bearer/generic token masking before any log or Traffic Inspector broadcast. - DB migrations 073–075 —
agent_bridge_state,agent_bridge_mappings,agent_bridge_bypass,inspector_custom_hosts,inspector_sessions,inspector_session_requests. - ~28 API routes under
/api/tools/agent-bridge/(12 routes) and/api/tools/traffic-inspector/(16+ routes). All LOCAL_ONLY + SPAWN_CAPABLE. - i18n PT-BR + EN for all new keys in
agentBridge.*andtrafficInspector.*namespaces; all other locales fall back to EN automatically. - E2E smoke tests —
tests/e2e/agent-bridge.spec.ts,tests/e2e/traffic-inspector.spec.ts,tests/e2e/agent-bridge-traffic-cross.spec.ts(skip-gated on CI byRUN_AGENT_BRIDGE_E2E/RUN_TRAFFIC_INSPECTOR_E2E/RUN_CROSS_E2E). - Documentation —
docs/frameworks/AGENTBRIDGE.mdanddocs/frameworks/TRAFFIC_INSPECTOR.md;docs/architecture/REPOSITORY_MAP.mdupdated;docs/reference/openapi.yamlupdated with ~28 new routes and 20+ new schemas. - i18n: translate Ukrainian (uk-UA) menu and UI strings, plus complete uk-UA UI coverage (#2981 / #2988 — thanks @Lion-killer)
- providers: add SiliconFlow endpoint selector (#2975 — thanks @xz-dev)
- oauth: add Trae SOLO provider (work/code modes) (#2964 — thanks @S0yora)
- providers: add Qwen Web (chat.qwen.ai) web-cookie provider (#2947 — thanks @oyi77)
- Quota Share Engine — multi-provider quota pools — Monitoring/Costs reorg plus a Quota Share Engine: group selector, grouped pool cards, exclusive-quota API keys (
allowedQuotas),quotaShared-*routing models via combos, a 3-step pool wizard (legacy Plans page retired), endpoint + key preview, and full pool editing. Adds quota-pool DB migrations. (#2859 / #3022 / #3032 — thanks @diegosouzapw) - Dashboard page redesigns (Nav Restructure) — agent-skills + omni-skills with a dynamic 42-skill catalog and MCP/A2A discovery (#2827); CLI Code's + CLI Agents + ACP Agents pages (#2839); translator friendly redesign, 5 tabs → 2 (#2847); functional
/batch+/batch/filesredesign (#2849); Playground Studio + Search Tools Studio (#2869); memory engine redesign — sqlite-vec + hybrid RRF + Studio UI (#2873). (thanks @diegosouzapw) - notion: add Notion as an MCP context source — 6 tools (
notion_search,notion_list_databases,notion_get_database,notion_query_database,notion_read,notion_append_blocks) scoped underread:notion/write:notion, with dashboard "Context Sources" tab, settings API, and token persistence inkey_valuetable (#2959)
Changed
- Sidebar Tools group: added
agent-bridgeandtraffic-inspectoritems aftercloud-agents. /api/tools/agent-bridge/and/api/tools/traffic-inspector/added toLOCAL_ONLY_API_PREFIXESandSPAWN_CAPABLE_PREFIXESinsrc/server/authz/routeGuard.ts..env.example: documented 9 new env vars (AGENTBRIDGE_UPSTREAM_CA_CERT,INSPECTOR_BUFFER_SIZE,INSPECTOR_HTTP_PROXY_PORT,INSPECTOR_HTTP_PROXY_AUTOSTART,INSPECTOR_TLS_INTERCEPT,INSPECTOR_SYSTEM_PROXY_GUARD_MINUTES,INSPECTOR_MAX_BODY_KB,INSPECTOR_MASK_SECRETS,INSPECTOR_LLM_HOSTS_EXTRA,INSPECTOR_INTERNAL_INGEST_TOKEN).
Fixed
- codex/providers:
POST /api/providers/[id]/refresh(the manual/auto "refresh token" endpoint) no longer rotates rotating-refresh providers (Codex/OpenAI share one Auth0client_id). This was the last unguarded proactive-refresh entry point: when the dashboard auto-refreshed every expiring connection on a page load (or an old cached frontend bulk-called it), each Codex account's single-use refresh_token was rotated, and Auth0 revoked the whole token family (openai/codex#9648) — every account but the last died with[403] <!DOCTYPE. The endpoint now skips proactive rotation for rotating providers and defers to the reactive, serialized 401 path (same guard asrefreshAndUpdateCredentialsand the connection-test route). - codex/quota: opening the Quota / Providers dashboard no longer disconnects
Codex multi-account setups. The quota-sync path
(
refreshAndUpdateCredentials) proactively refreshed every connection — for rotating-refresh providers (Codex/OpenAI share one Auth0client_id) it refreshed siblings concurrently, so Auth0 revoked the whole token family (openai/codex#9648) and every account but the last died with[403] <!DOCTYPE html>. The quota path now skips proactive refresh for rotating providers (rotationGroupFor) and reuses the current accesstoken, deferring genuine expiry to the reactive, serialized 401 path. Defense in depth:serializeRefreshnow leaves a settle gap between two _queued sibling refreshes (default 2000 ms, tunable viaCODEX_REFRESH_SPACING_MS,"0"to opt out) while releasing a lone refresh immediately, so the reactive path adds no latency. - payload-rules: saved payload rules now survive a server restart. When no
in-memory override is set (fresh process before the boot hook ran, or a
separate module instance in the standalone build),
getPayloadRulesConfignow reads the DB-persisted rules (the source of truth) before the file config, instead of silently returning the empty file default. (#2986) - models/custom: custom models can now carry a per-model
targetFormatoverride (e.g. an opencode-go custom model that must use the Anthropic Messages shape). Previously custom models always routed as OpenAI-compatible becausetargetFormatwas neither persisted nor consulted at routing time. Threaded throughaddCustomModel/replaceCustomModels/updateCustomModel, the API schema/route,getModelInfo, and chatCore's targetFormat resolution. (#2905) - providers/pollinations: route to
gen.pollinations.ai/v1instead of the retiredtext.pollinations.aihost, which now returns404 "legacy API"for all models. The gen gateway is the current OpenAI-compatible endpoint. (#2987) - executors/codex: drop the CLI-injected
image_generationhosted tool for free-plan Codex accounts (workspacePlanType === "free"), which can't run it server-side and would otherwise get an upstream 400. Paid plans keep it. (mirrors CLIProxyAPI's free-plan guard; spun off from the #2980 analysis) - dashboard: custom providers (
openai-compatible-*/anthropic-compatible-*) now show their user-given node name instead of the raw UUID id across the active-requests panel, proxy logger, and home-page provider topology. The display-label resolver was extracted into a shared util reused by all surfaces (previously only the request-log viewer resolved it). (#2968) - docker: the standalone launcher (Docker
CMD) now honorsOMNIROUTE_MEMORY_MB(default 512, clamped [64, 16384]) and overrides the imageNODE_OPTIONSfallback, fixing random OOM crashes under load / with large SQLite DBs. Previously onlyomniroute servehonored the knob. (#2939) - docker: add a
webcompose profile (omniroute-web, targetrunner-web, imageomniroute:web) so web-cookie providers (gemini-web, claude-web, claude-turnstile) work out of the box — the defaultbaseimage ships without Chromium/Playwright, which made those providers fail with "Executable doesn't exist at .../ms-playwright/chromium...". (#2832) - routing/codex: fix two gpt-5.5 Codex defects (#2877). (A) For a Codex-only
account, a bare
gpt-5.5Responses request was rerouted to codex with the model hardcoded togpt-5.5-medium(chatHelpers.ts); the executor read that-mediumsuffix as an explicitmodelEffortthat (per #2331) overrode a clientreasoning.effort=xhigh, silently demoting it — now it keeps the baregpt-5.5id so the client effort wins. (B)gpt-5.5-xhigh/-high/-lowmisrouted toopenai(→ "No credentials" for codex-only users); the suffixed variants are now inCODEX_PREFERRED_UNPREFIXED_MODELSso they infer codex. - sse/chatCore: remove a duplicate
const settingsdeclaration inhandleChatCore(introduced alongside the per-key stream-default-mode feature). The same-scope redeclaration made esbuild/tsx fail with "The symbol 'settings' has already been declared", which turned every unit test that imports chatCore red and broke the production build. The earlier consolidatedsettingsconst is now reused. - db/migrations: resolve a
077migration version collision (077_api_key_stream_default_mode.sqlvs077_quota_pools.sql) that madegetMigrationFiles()throw and blockedgetDbInstance()at startup (app would not boot; every DB-touching test was red). Renumbered the dependency-free, idempotentquota_poolsmigration to085, kept the non-idempotentapi_key_stream_default_modeALTERat077, added a retroactiveisSchemaAlreadyAppliedguard (case085), and a regression test enforcing unique migration prefixes. - routing/reasoning-replay: OpenCode
big-pickle(provideropencode/ocandopencode-zen) now declares the interleavedreasoning_contentcontract via a newRegistryModel.interleavedFieldfield, so follow-up/tool-use turns replay reasoning_content. Previouslybig-picklematched no replay pattern and failed with[400] The reasoning_content in the thinking mode must be passed back to the API(its DeepSeek-thinking upstream is not detectable from the model id, andrequiresReasoningReplaydoes not consumesupportsReasoning).getResolvedModelCapabilitiesnow surfaces the registryinterleavedField. (#2900) - providers/github-copilot: built-in GitHub Copilot Claude Opus and Gemini
models (
claude-opus-4.7,claude-opus-4-5-20251101,gemini-3.1-pro-preview,gemini-3-flash-preview) no longer carrytargetFormat: "openai-responses", so they route throughchat/completions(the provider default, like the workingclaude-opus-4.6) instead of the Responses API, which Copilot does not serve for non-OpenAI models (returned[400]). Native OpenAIgpt-*models keep the Responses API. (#2911) - translator/responses: Codex Desktop injects an
image_generationhosted tool into every Responses API request (even text-only ones), which OmniRoute rejected with[400] image_generation tool type is not supported. It is now treated liketool_search: allowed past the tool-type validator and dropped silently from the tools array before forwarding to Chat Completions. (#2950) - combo/builder: no-auth OpenCode Free combo entries now use the
oc/routing alias instead of theopencode/prefix.parseModel("opencode/<model>")resolves to theopencode-zenapi-key tier (via a manualALIAS_TO_PROVIDER_IDoverride), so combos built with the bare provider id misrouted away from the no-authopencodeprovider;oc/<model>resolves correctly. (#2901) - resilience/providers: a route-restriction
403(e.g. Fireworks Fire Passfpk_*keys returning "…not authorized for this route." on/models, while chat still works) no longer marks the connection unavailable. Provider validation falls through to the chat probe for such 403s instead of returning "Invalid API key", andcheckFallbackErrorshort-circuits them to no cooldown. Genuine auth failures (401 / generic 403) still fail fast. (#2929) - auth/opencode-zen: the OpenCode Zen free model now works in the Playground
and combos without an API key.
opencode-zenserves the public, signup-free endpoint (https://opencode.ai/zen/v1); when no api-key connection is configured, credential resolution now falls back to anonymous (no-auth) access instead of failing with "No credentials for provider: opencode-zen". A configured, active key is still used when present. (#2962) - translator/responses: fixed an upstream
[400] Messages with role 'tool' must be a response to a preceding message with 'tool_calls'when a Codex client sent afunction_callwith an empty/missingcall_id. The orphanedfunction_call_outputpreviously slipped past the orphan filter. Now empty-call_idfunction calls are skipped (no dangling assistant tool_call) and any tool result without a matching tool_call id is dropped. (#2893) - deps: remove the
proxiflynpm dependency (#3000 — thanks @terence71-glitch) - proxy: use connection proxy for OAuth refresh (#3012 — thanks @terence71-glitch)
- usage: export pure helper functions for unit testing (#3015 — thanks @oyi77)
- docs/docker: align memory default docs to 1024MB (#3006 — thanks @terence71-glitch)
- providers: fix DuckDuckGo missing API key & update OpenCode free model list (#3008 — thanks @NekoMonci12)
- claude: bump Claude Code identity to 2.1.158 and sync beta flags (#3010 — thanks @Tentoxa)
- test: increase DB and usage utils coverage to >60% (#3018 — thanks @oyi77)
- oom: resolve memory leak in Bottleneck limiter caches and provider registry (#2965 — thanks @soyelmismo)
- proxy: show registry provider proxies in dashboard after Custom proxy flow moved them into the proxy registry (#2963 — thanks @terence71-glitch)
- routing: add agy to executor map so it uses AntigravityExecutor (#2957 — thanks @ReqX)
- skills: avoid Claude assistant tool_result blocks (#2956 — thanks @terence71-glitch)
- perf: CPU leak from Bottleneck limiter accumulation + per-request optimizations (#2951 — thanks @soyelmismo)
- combo: combo credential resolution ignores target.providerId — prefer combo target's providerId over model-inferred provider (#2946 — thanks @oyi77)
- dashboard: v3.8.8 screen fixes — agent-bridge SSR + audit/logs/memory/playground (#2944)
- claude: sanitize tool schemas + cloak third-party tool names on native Claude OAuth (#2943 — thanks @NomenAK)
- auth: prevent Codex multi-account refresh_token family revocation (#2941)
- combo: fix combo vision passthrough and Codex tool history repair (#2940 — thanks @charithharshana)
- claude: map WebSearch to Responses web_search (#2938 — thanks @makcimbx)
- claude: strip empty Read pages tool input (#2937 — thanks @makcimbx)
- dashboard: improve self-service provider quota visibility (#2931 — thanks @guanbear)
- antigravity: avoid visible signatureless tool history (#2927 — thanks @dhaern)
- sse/web-search: bypass the web-search fallback on a Claude → Claude passthrough so native Claude requests aren't rewritten (#2960 — thanks @terence71-glitch)
- oom: prevent per-request memory accumulation (~256MB heap growth) (#2973 — thanks @soyelmismo)
- perf/proxy: parallelize provider proxy overlay lookups (#2984 — thanks @terence71-glitch)
- privacy/PII: resolve the PII feature flag correctly and fix PII response sanitization in streaming SSE requests (#3021 — thanks @dangeReis)
- electron: improve macOS window chrome (#3029 — thanks @bobbyunknown)
- i18n: fix missing API key scope translations (#3031 — thanks @guanbear)
- stream/responses: drop a leaked chat bootstrap chunk for Responses-API clients (#3035 — thanks @CitrusIce)
- docker: warn-only on the
/app/datapermission check instead ofexit 1, so a non-writable bind mount no longer kills the container at boot (#3036 — thanks @wussh) - mcp: resolve streamable-HTTP transport readiness reporting an offline status (#3037 — thanks @Chewji9875)
- dashboard: use a lightweight ping endpoint for the MaintenanceBanner (fixes #3040) (#3043 — thanks @herjarsa)
- test: resolve pre-existing test failures — env sync, PII, quota, sidebar (#3039 — thanks @oyi77)
- docs/mcp: regenerate the mcp-tools diagram for 43 tools and fix the tool count (#3028 — thanks @diegosouzapw)
- mcp: move
enforceScopesguard beforeMCP_TOOL_MAPlookup, add inlinescopesparameter towithScopeEnforcement(), and declare scopes on all 24 dynamic tool definitions (memory, skills, plugins, gamification, compression) to fix scope enforcement for dynamic MCP tool groups (#2958)
[3.8.7] — 2026-05-29
✨ New Features
- api (self-service): add
GET /api/v1/me/statusso a delegated API key can view its own usage (USD used, budget percent, token totals) and optional shared Codex account quota, backed by migration075_api_key_self_service_usage_scopes(#2908 — thanks @guanbear). - analytics: roll up usage logs to
daily_usage_summarybefore raw log cleanup, and query a SQLUNIONof raw and rolled-up data to prevent analytics history data loss (#2904 — thanks @unitythemaker). - perf (RAM): reduce server memory footprint by capping 11 in-memory caches, limiting SQLite page cache, lazy-loading provider registries via Proxy, and optimizing Next.js startup database probes (#2903 — thanks @soyelmismo).
🔧 Bug Fixes
-
token-accounting: prefer
prompt_tokensover compatibilityinput_tokensfor Anthropic Claude streams to avoid double-counting cached tokens (#2904 — thanks @unitythemaker). -
agy: add the Antigravity CLI (
agy) as a standalone OAuth provider next togemini-cli/antigravity. It reuses the antigravity inference backend (identical Google client,daily-cloudcode-pa.googleapis.com) but ships its own model catalog — notably the Claude models the backend exposes (claude-opus-4-6-thinking,claude-sonnet-4-6) — its own account pool, and connection methods: import theagyCLI token file (paste/upload), auto-detect a local CLI login (~/.gemini/antigravity-cli/antigravity-oauth-token), browser OAuth, and bulk/ZIP import. New routes:POST /api/providers/agy-auth/{import,import-bulk,zip-extract,apply-local}.
Breaking Changes
- proxy-logs:
GET /api/usage/proxy-logsnow returnsclientIpinstead ofpublicIpfor each log entry. External consumers readinglog.publicIpmust update tolog.clientIp. The underlying SQLite column (public_ip) is unchanged, so callers that query the database directly are unaffected (#2880 — thanks @rdself).
Known Inconsistency
- log-export:
GET /api/logs/export?type=proxy-logsreturns raw SQLite rows whose IP field is still namedpublic_ip(the historical column name). This differs from theclientIpfield exposed byGET /api/usage/proxy-logs. The two endpoints are intentionally inconsistent for now and will be aligned in a future migration (#2880).
✨ New Features
- usage: add per-API-key token limits scoped to model/provider/global with two-tier inline enforcement and in-memory cache accelerator (#2888 — thanks @mugnimaestra).
- providers: audit web cookie providers, fix 4 missing registry entries, and add DuckDuckGo AI Chat provider (#2862 — thanks @oyi77).
- compression: expand pt-BR pack with 34 new rules inspired by the troglodita project (#2818 — thanks @leninejunior).
🔧 Bug Fixes
- oauth: hotfix Windsurf login — drop dead PKCE flow, promote import-token, and resolve SQLite bind type errors (#2884 — thanks @yunaamelia).
- models: prune stale synced available models for inactive connections and dynamically map Antigravity MITM aliases loop-safely (#2886 — thanks @herjarsa).
- antigravity: harden signatureless tool history replay by making text representation inert (#2878 — thanks @dhaern).
- i18n: complete 144 missing Portuguese (pt-BR) locale keys and synchronize them with English (#2870 — thanks @alltomatos).
- opencode-go: add OpenCode Go provider limits quota fetcher to retrieve Z.AI quota windows (#2861 — thanks @RajvardhanPatil07).
- reasoning: gate reasoning trace replay injection on model interleaved capability metadata (#2843 — thanks @nickwizard).
- audio: construct multipart body manually for transcription form-data to prevent dropped boundary headers under Next.js fetch (#2842 — thanks @soyelmismo).
- gemini-cli: prefer real Google Cloud project IDs over default-project during model synchronization (#2841 — thanks @nickwizard).
- mcp: redirect console.log and console.warn startup messages to stderr in stdio MCP mode to prevent JSON-RPC parsing failures (#2840 — thanks @disonjer).
- antigravity: normalize unescaped tool calls and classify resource exhaustion 429 errors as lockout cooldowns (#2828 — thanks @Ardem2025).
- sse: repair RTK engine defaults to resolve consecutive-line deduplication and direct compression calls (#2825 — thanks @leninejunior).
- fix(usage): add opencode-go / opencode / opencode-zen quota fetcher so the provider limits page surfaces $12/5h, $30/wk, $60/mo windows alongside other quota-aware providers (#2852 — thanks @apoapostolov)
[3.8.6] — 2026-05-27
🧹 Chores
- gitignore: ignore
.claude/settings.local.jsonso per-user Claude Code permissions never get committed by accident - release: version bump and metadata sync (package.json, package-lock.json, electron, open-sse, openapi.yaml)
v3.8.6 is a maintenance/scaffolding patch. All feature and bug-fix work from the post-v3.8.5 cycle (44 commits — community PRs #2777, #2782–#2787, #2789, #2790, plus internal hotfixes) was already integrated into v3.8.5 and is documented under that section.
[3.8.5] — 2026-05-26
🔒 Security
- authz: redirect
/homeand/home/:path*to/loginwhen unauthenticated (#2712)
🔧 Bug Fixes
- mcp: break callLogs ↔ compliance ESM cycle that deadlocks the bundled MCP server on Node.js 24 (#2650)
- deepseek: guard PoW solver Web Worker handler under Node strict mode (#2724)
- combos: include no-auth providers in the combo builder picker (#2737)
- translator: allow the
web_searchserver-tool family in the Responses API translator (#2695) - oauth: register the missing
traeprovider withimport_tokenflow (#2658) - model: merge settings-based aliases with the legacy DB alias namespace (#2618, #2208)
- kiro: clipboard fallback for HTTP / non-secure contexts (#2689)
- cli: raise
omniroute serveready timeout to 60s with TCP fallback for Windows cold start (#2460)
[Unreleased]
[3.8.23] — TBD
✨ New Features
🔧 Bug Fixes
[3.8.4] — 2026-05-25
Added
- Embedded services (work in progress — 9Router, CLIProxyAPI; see T-15 for full entry).
[3.8.3] — 2026-05-24
✨ New Features
- feat(combos): universal context handoff for cross-model conversation continuity — structured XML summary system (
<context_handoff>) that preserves conversation continuity and handles state transfer when combo routing switches models. (#2653 — thanks @herjarsa) - feat(docs): migrate
/docsto Fumadocs MDX with nested routes — replaces the custom docs engine with Fumadocs, adding[...slug]catch-all routing, search API at/docs/api/search,source.config.tscontent configuration, andmeta.jsonnavigation files across 8 doc sections (architecture/,compression/,frameworks/,guides/,ops/,reference/,routing/,security/). Includes 50+ URL redirects for backward compatibility vianext.config.mjs. (#2614 — thanks @ovehbe) - feat(dashboard): add search and filters to
/dashboard/api-manager— filter bar with search by name/key, active-only toggle (persisted to localStorage), status filter (active/disabled/banned/expired), type filter (standard/manage/restricted), filter count badges, and empty state with "Clear Filters" button. (#2628 / #2641 — thanks @diegosouzapw) - feat(dashboard): free-tier grouping with symbolic link in
/dashboard/providers— groups and displays all free-tier providers from all categories dynamically usinghasFree: trueproperties without removing them from their native lists. Displays category dot and amber dot with localizable tooltips, dedupes search results by provider ID, and corrects free tier count statistics. (#2632 — thanks @diegosouzapw) - feat(dashboard): risk notice modal for sensitive providers — show a soft, informative warning modal when connecting to session-based or OAuth providers (like Claude, Cursor, Copilot) for the first time. Adds
subscriptionRiskproperties to 20 providers, localizable templates, and stores acknowledgment in localStorage. (#2633 / #2638 — thanks @diegosouzapw) - feat(dashboard): refactor free-tier provider dashboard layout — cleans up visual clutter, reorganizes categories, hides redundant banners, and integrates free-tier categories nicely into the primary provider interface. (#2640 — thanks @diegosouzapw)
- feat(dashboard): mini-playground inline (Phase 4) — integrated interactive mini-playground capabilities to provider details pages, including support for specialized example cards (Embedding, Image, LLM Chat, Music, STT, TTS, Video, Web Fetch, Web Search), unified API key loading hooks, model listing hooks, and curl command builder. (#2648 — thanks @diegosouzapw)
- feat(webfetch): category support with dedicated media providers page and executors for Firecrawl, Jina Reader, and Tavily. (#2645 — thanks @diegosouzapw)
- feat(adapta): integrate Adapta Org (
adapta-web) provider with automatic Clerk authentication refresh and custom onboarding tutorial modal. (#2643 — thanks @df4p) - feat(i18n): complete translations for Simplified Chinese — translates 1220 missing keys bringing UI coverage to 98.8% with 0 placeholders. (#2655 — thanks @L-aros)
🔧 Bug Fixes
- fix(settings): Require Login modal Cancel button text and dismissal — modal now renders localized cancel label via the
commonnamespace and closes correctly without modifying settings when cancelled. (#2649 — thanks @Chewji9875) - fix(deepseek-web): re-apply SSE parser, prompt format, and error handling fixes — handles all 3 DeepSeek SSE stream formats (initial fragments, APPEND operations, bare string tokens), uses non-greedy regex for markdown image stripping, simplifies prompt to single-turn, checks
json.codebefore token extraction, and usesaccessTokenfallback for session cache eviction on auth errors. (#2616 — thanks @ovehbe) - fix(deepseek-web): SSE thinking/search routing and session lifecycle — properly routes thinking vs content fragments based on
thinking_enabledflag, handles search results with citation indices, appends search result footnotes, refactorstransformSSE()andcollectSSEContent()with shared helpers. (#2624 — thanks @ovehbe) - fix(codex): use allowlist to strip non-Responses-API fields in non-passthrough path — strips residual Chat Completions fields (
stream_options,service_tier,store,metadata) from the request body when routing through the non-passthrough (translation) code path, preventing GPT-5.5 from receiving invalid parameters. (#2615 — thanks @diegosouzapw) - fix(catalog): skip static PROVIDER_MODELS when synced models exist — prevents stale/duplicate model entries in
/v1/modelsfor auto-synced providers. (#2625 — thanks @herjarsa) - fix(qoder): Cosy auth fallback for PAT tokens + vision support for qwen3-vl-plus — when a PAT token gets 401, falls back to Cosy auth against
api1.qoder.sh; addssupportsVision: trueto qwen3-vl-plus. (#2629 — thanks @herjarsa) - fix(cli): register tsx loader and add opencode config subcommand — registers
tsx/esmat CLI startup so dynamic.tsimports resolve; addsomniroute config opencodeconvenience alias. (#2631 — thanks @amogus22877769) - fix(claude): improve Pi and OpenCode compatibility — adds Pi Coding Agent anchors to system transform removal, stores
_toolNameMapas non-enumerable, stripscontext_managementwhen thinking is disabled. (#2621 — thanks @unitythemaker) - fix(passthrough): restore semantic passthrough system-role-only extraction — reverts full
normalizeClaudeUpstreamMessages()to lighterextractSystemRoleMessages()in CC semantic passthrough paths, preventing document/tool chain corruption. (#2620 — thanks @Tentoxa) - fix(kiro): stabilize conversationId across prompt compression — captures pre-compression body and uses the original first user message as seed for UUID v5, keeping Kiro's AWS conversation context stable. (#2630 — thanks @HALDRO)
- fix(t3-chat-web): close implementation gaps for t3.chat TanStack Start, tracking of stream_options, and retry configurations — parses TSS Turbo Stream Serialization from
_serverFn/*, tracks requestcombo_strategyvia database migration062_usage_history_combo_strategy.sql, and makes batch retry backoffs custom-configurable via environment variables. (#2634 — thanks @oyi77) - fix(reasoning): extend empty
reasoning_contentinjection to prevent tool call loops in Kimi K2 and replay models — injects emptyreasoning_contentfield to Kimi models during tool-calling sequences to bypass loop issues. (#2639 — thanks @herjarsa) - fix(cli): Linux autostart via systemd user service on headless VPS — adds auto-generating systemd user service unit for headless setups on Linux, updating tray configs and system variables allowlist (
LOGNAMEandXDG_CURRENT_DESKTOP). (#2635 — thanks @janeza2) - fix(combo): preserve
<omniModel>tag in SSE stream output for combos when usingcontext_cache_protectionto ensure correct context pinning round-trips. (#2646 — thanks @herjarsa) - fix(rtk): prevent false positives in RTK compression by skipping content-based filter matching for non-shell tool results (e.g. read_file, grep_search). (#2642 — thanks @HALDRO)
- fix(translator): enable Claude extended thinking for Copilot Responses-API requests — handles reasoning budget and translations for Copilot. (#2647 — thanks @ivan-mezentsev)
- fix(tests): remove duplicate assertion in schema coercion & fix(cli): ignore system vars in env check. (thanks @diegosouzapw)
📝 Maintenance
- chore(config): ignore additional agent workflow command files (
.agents/commands/). (thanks @diegosouzapw) - chore(config): ignore
memory-bankand Cursor agent rules from tracking. (thanks @ovehbe)
[3.8.2] — 2026-05-22
✨ New Features
- feat(@omniroute/opencode-plugin): upstream-provider suffix in model display name — appends provider label to enriched names (e.g.
Claude Opus 4.7 · ClaudevsClaude Opus 4.7 · Kiro) so the OC TUI model picker can differentiate same-id models routed through different upstream connections. Default-on, opt-out viafeatures.providerTag: false. (#2602 — thanks @mrmm) - feat(@omniroute/opencode-plugin): provider-tag becomes a prefix + traffic-light compression emoji — provider label now prepends (
Claude - Claude Opus 4.7) for better TUI column grouping, with smart abbreviation for long labels (GitHub Models→GHM). Compression pipelines render intensity as emoji (🟢🟡🟠🔴). (#2604 — thanks @mrmm) - feat(providers): add 7 free-tier providers (Wave 1) — Arcee AI, InclusionAI, Krutrim, Liquid AI, MonsterAPI, Nomic, and Poolside now available as new API-key providers with provider icons, model specs, and full routing support. (#2479 — thanks @oyi77)
- feat(providers): add Astraflow provider support with global + China endpoints — new provider with dual-region base URLs for global and mainland China access. (#2486 — thanks @ucloudnb666)
- feat(providers): add
claude-webprovider — cookie-based Claude Web chat access without OAuth. (#2476 — thanks @oyi77) - feat(providers): add 14 free-tier providers (Wave 1b) — 360AI, Baichuan, Baidu, ByteDance/Doubao, IDEO, Kuaishou/Kling, Kunlun/Skywork, SenseTime/SenseNova, Stepfun, Tencent HunYuan, Zhipu GLM, Replicate, RunPod, and Modal with provider icons, model specs, and routing support. (#2488 — thanks @oyi77)
- feat(hermes): add rich multi-role Hermes Agent CLI support — 7 configurable roles (default, delegation, vision, compression, web_extract, skills_hub, approval), per-role model selection with YAML config generation, dashboard card with preview, and home widget integration. (#2526 — thanks @apoapostolov)
- feat(cloud-agents): cloud agents UX overhaul — tabs (tasks/agents/settings), status filters, Material icons, duration formatting, cloud agent credentials and health API endpoints, memory stats endpoint. (#2516 — thanks @oyi77)
- feat(authz): manage-scope API keys may reach
/api/mcp/*from non-loopback — Route Guard Tiers system (LOCAL_ONLY / ALWAYS_PROTECTED / MANAGEMENT), narrow carve-out for remote MCP access gated bymanagescope;/api/cli-tools/runtime/*stays strict-loopback. Includes dashboard AuthzSection, inventory API, and comprehensive docs. (#2473 — thanks @mrmm) - feat(home): home page customization for experienced users — pin Provider Quota to home, toggle Quick Start and Provider Topology visibility via Appearance settings. (#2531 — thanks @apoapostolov)
- feat(home): automatic refresh of Provider Quota — configurable interval (60s–600s) with toggle in Appearance settings; auto-refreshes pinned quota on the home page. (#2532 — thanks @apoapostolov)
- feat(@omniroute/opencode-plugin): OmniRoute OpenCode plugin — live models fetched from OmniRoute API, combo-aware model listing, Gemini request sanitization, multi-instance support, auth flow integration, and 10 test files. (#2529 — thanks @mrmm)
- feat(executors): forward OpenCode client headers to upstream providers — OpenCode-specific headers are now forwarded through the executor pipeline for improved compatibility. (#2538 — thanks @kang-heewon)
- feat(fireworks): add new models with
modelIdPrefixsupport — generic registry field that stores short model IDs and prepends the full path prefix before upstream API calls. Adds 6 new Fireworks models,modelsUrlfor dynamic sync, and Qwen3 reranker. (#2560 — thanks @HALDRO) - feat(@omniroute/opencode-plugin): readable + filterable + offline-resilient model picker —
usableOnlyfilter (only show providers with healthy connections),diskCachefor offline hydration,Combo:prefix labeling, and compression metadata tags in combo display names. (#2572 — thanks @mrmm) - feat(smart-pipeline): multi-stage pipeline for auto combo routing — rule-based + intent-classifier + domain-specific stages with configurable pipeline router, accuracy benchmarks, and comprehensive tests. (#2551 — thanks @oyi77)
- feat(ops): skip DB health check on startup via
OMNIROUTE_SKIP_DB_HEALTHCHECK=1— replaces slowintegrity_check(7+ min on large WAL) withquick_check, and adds env var to skip entirely. (#2554 — thanks @soyelmismo) - refactor(dashboard): Provider Quota grouped layout with vertical rail — restructures the page to a 2-column per-provider layout (left rail with icon/name/status, right content with dynamic per-provider columns), new
providerColumns.ts/ProviderGroup.tsx/AccountRow.tsxcomponents, env chip-filter row, bulk-refresh per group, and inline expanded panels. (#2528 — thanks @Gi99lin) - feat(providers): add 26 free-tier providers missing from registry — Novita, Avian, Chutes, Kluster, Targon, Nineteen, Celery, Ditto, Atoma, and more. (#2590 — thanks @oyi77)
- feat(providers): add api-airforce free provider with 55 models. (#2587 — thanks @oyi77)
- feat(dashboard): configurable sidebar — presets, drag-and-drop ordering, smart-grouping, and new Settings → Sidebar page. (#2581 — thanks @Gi99lin)
🔧 Bug Fixes
-
fix(validation): stop appending a second
/modelswhen the Gemini base URL already ends in/models— Google AI Studio connections using the default base URL were validating against.../v1beta/models/modelsand failing with404for every connection. (#2545) -
fix(cloudflare-ai): flatten OpenAI content-part arrays to plain strings for the Workers AI (
cf/) executor — Workers AI's/ai/v1/chat/completionsrejectscontent: [{type:"text",...}]with HTTP 400, so requests with array content now have their text parts joined into a string. (#2539) -
fix(i18n): replace leftover Portuguese strings in the English source with English on the Quota dashboards — the quota-share Beta notice (
betaConfigSaved*) and the Provider Quota row'sEdit cutoffs/Refresh nowfallbacks were showing Portuguese. (#2540) -
fix(proxy): honor the legacy per-provider/global proxy config in
resolveProxyForProvider— the Claude OAuth token exchange and token refresh only consulted the new proxy registry, so a proxy configured the legacy way (/api/settings/proxy?level=provider) was ignored and the exchange went out directly from the host, tripping Anthropic's IPrate_limit_erroron VPS deployments. It now falls back to the legacy config, mirroringresolveProxyForConnection. (#2456) -
fix(antigravity): auto-discover a missing Cloud Code
projectIdvialoadCodeAssistbefore failing — a freshly re-added Antigravity account whose storedprojectIdwas empty (OAuth-time discovery returned nothing) now recovers the project on the first request instead of returning422 Missing Google projectId, mirroring thegemini-clibootstrap. (#2334, #2541) -
fix(stream): keep the
/v1/responsesSSE connection warm for strict clients — emit an early keepalive while the upstream produces its first token and lower the heartbeat cadence to 4s, so Codex CLI'sreqwestclient (≈5s idle-read timeout) no longer drops the stream "before completion" on slow/reasoning models.curlwas unaffected because it has no idle timeout. (#2544) -
fix(electron): wait longer for the server on first launch and reload once it responds — long post-upgrade DB migrations could exceed the 30s readiness probe, leaving the desktop app stuck on the "Server starting" screen even though the backend was healthy. The probe now targets the auth-exempt health endpoint with a generous timeout and reloads the window once the server comes up. (#2460)
-
fix(cli): mark
bin/omniroute.mjsas executable (mode 755) so the globally-installed CLI runs directly without a manualchmod +x. (#2469 — thanks @disonjer) -
fix(settings): restore the Global System Prompt into the in-memory config on server startup and after JSON/SQLite import — it was only loaded by the PUT endpoint, so the toggle/prompt silently reverted to defaults after any restart or import. (#2470 — thanks @disonjer)
-
fix(settings): append the Global System Prompt after existing system content instead of prepending it, so provider/agent instructions (Kiro, OpenCode, Hermes, …) injected into the system message no longer override the user's global prompt via recency bias. (#2468 — thanks @disonjer)
-
fix(kiro): refresh imported social tokens (
authMethod === "imported") via the Kiro social-auth endpoint instead of AWS SSO OIDC — imported tokens carry a registeredclientId/clientSecretbut a social-issued refresh token the OIDC client cannot refresh, so auto-refresh was failing with "provider returned no new token". (#2467 — thanks @disonjer) -
fix(antigravity): resolve the Cloud Code
projectIdfromproviderSpecificDataas a fallback (and preserve it across token refresh) so the Gemini/v1betastreaming path stops returning a spurious422 Missing Google projectIdfor connections that store the project there. (#2480) -
fix(api):
GET /v1beta/modelsnow lists only models whose provider has an active/validated connection, matching the OpenAI-format/v1/modelsbehavior, instead of returning the entire catalog. (#2483) -
fix(cli): persist
STORAGE_ENCRYPTION_KEYintoDATA_DIR(not only~/.omniroute) and refuse to auto-generate a fresh key when astorage.sqlitealready exists — a new key cannot decrypt previously-encrypted credentials, so silently regenerating it locked users out of their database. The CLI now mirrors the serverbootstrapEnvguard. (reported by Daniel Nach; original key persistence by @Chewji9875 — follow-up to #1622) -
fix(gemini): preserve and re-attach the
thoughtSignatureon Gemini thinking-model tool calls — thread the signature namespace through theFORMATS.GEMINIandFORMATS.GEMINI_CLIrequest translators so the cached signature (keyed by connection + tool-call id) is found on the follow-up turn. Fixes[400]: Function call is missing a thought_signature in functionCall partson agentic Gemini tool use. (#2504) -
fix(translator): accept PDFs sent in the Responses-API
input_fileshape on the Gemini path, and the Gemini-styledocumentshape on the Responses/Codex path — content parts are now normalized acrossinput_file/file/documentso a PDF reaches the model regardless of which field name the client used. (#2515) -
fix(stream): count
thinkingarrays andreasoning_detailsas useful stream output — a reasoning-only response (e.g. Mistral/StepFun with a lowmax_tokens) was misclassified as "Stream ended before producing useful content" and turned into a spurious 502; it is now recognized as valid output. (#2520) -
fix(claude): extract system/developer role messages in Claude Code semantic passthrough paths — moves
role:"system"/role:"developer"messages from themessages[]array to the top-levelsystemparameter before sending to Anthropic, which rejects them inside messages. Fixes memory injection context being silently dropped. (#2497 — thanks @unitythemaker) -
fix(vision-bridge): auto-route non-standard provider models through OmniRoute self-loop — vision-bridge now detects when a model doesn't natively support vision and automatically re-routes the image through OmniRoute's own endpoint for format translation. (#2487 — thanks @herjarsa)
-
fix(mitm): add IPv6 DNS redirect, modular antigravity target, improved logging — MITM DNS handler now correctly redirects IPv6 (AAAA) queries alongside IPv4, adds a dedicated
antigravity.tstarget module, and enhances DNS/TLS logging for debugging. (#2514 — thanks @herjarsa) -
fix(usage): improve Claude and MiniMax plan label detection — better tier name resolution for Claude OAuth usage (tier/plan/subscription_type/org fields) and new MiniMax plan label inference from quota totals. (#2498 — thanks @Gi99lin)
-
fix(codex): fan out image
nrequests in parallel — when Codex requestsn > 1images, the image-generation handler now dispatches them concurrently instead of sequentially, significantly reducing total latency. (#2499 — thanks @nmime) -
fix(embeddings): strip stale
Content-Encodingheaders from upstream response — prevents clients from receiving gzip-encoded responses withidentityencoding declared, which caused silent data corruption. (#2477 — thanks @lordavadon2) -
fix(model): return clear error instead of silent OpenAI default for unrecognized models — previously, an unrecognized model silently fell back to OpenAI; now returns a 404 with a descriptive message listing known providers. (#2492 — thanks @herjarsa)
-
fix(dark-mode): correct background token on Compression Override select — the combo compression override
<select>was using a hard-coded white background that was invisible in dark mode. (#2513 — thanks @apoapostolov) -
fix(antigravity): align subscription tier detection with Antigravity Manager —
extractCodeAssistSubscriptionTiernow parses the correct nested field from theloadCodeAssistresponse, and a newextractCodeAssistOnboardTierIdfallback handles the onboarding flow. Subscription info is cached per access-token with 5-min TTL. (#2496 — thanks @Gi99lin) -
fix(opencode-zen): add
opencodeprovider alias and sync model list with live API —opencode-zenandopencode-goare now also reachable via the shorteropencodealias, and the default model list is kept in sync with the live/v1/modelscatalog. (#2508 — thanks @herjarsa) -
fix(combo): clarify log message when combo target is skipped due to unavailable credentials — previously logged a misleading "provider not found" message; now says "skipped: credentials unavailable". (#2494 — thanks @herjarsa)
-
fix(security): replace
Math.randomwithcrypto.randomUUIDingenerateTaskId/ActivityIdand fix URL hostname check in test — eliminates weak PRNG usage flagged by CodeQL. (#2489) -
fix(electron): downgrade to Electron 41.x for better-sqlite3 V8 compatibility — Electron 42.x shipped a V8 version that broke
better-sqlite3native bindings at runtime; pinning to 41.x restores stability. -
fix(@omniroute/opencode-provider): include
limit.contextin model entries for OpenCode context window detection — OpenCode readslimit.contextto determine usable context length for compaction and overflow detection. -
fix(providers): make
gitlawb/gitlawb-gmimodel entry optional — prevents provider initialization failure when the model is not available in the catalog. (#2476 — thanks @oyi77) -
fix(translator): inject
omniroute_web_searchin the Responses-API flat tool shape ({ type, name }) when the target provider speaks the Responses API — previously it was always emitted in the Chat Completions nested shape, so Codex/relay upstreams rejected the request. (#2390) -
fix(kiro): serialize non-string
role:"tool"message content before sending to CodeWhisperer — structured/array tool output was collapsing tocontent:[{ text: "" }], which Kiro rejects with400 Improperly formed request. (#2446) -
fix(claude): gate the heavy-agent beta headers (
context-1m,effort,advanced-tool-use) on Opus/Sonnet only — Haiku with OAuth was receivingcontext-1mand rejecting it with 400. Also sanitizes historicalthinkingblock signatures in passthrough. (#2454 — thanks @havockdev) -
fix(perplexity-web): route requests through a Firefox-148 TLS-impersonating client so Perplexity's Cloudflare edge stops rejecting VPS/datacenter IPs with a 403 challenge. (#2459 — thanks @havockdev)
-
fix(validation): guard
apiKey/modelsUrlagainst non-string values before calling.startsWith()/.trim()in the provider connection-test path. (#2463) -
fix(cost): prevent double-billing of
cache_creation_input_tokens—prompt_tokensfrom token extractors already includes bothcache_readandcache_creation, sononCachedInputnow subtracts both cache types to avoid pricing cache at the full input rate. (#2522 — thanks @herjarsa) -
fix(handler): always normalize system role messages in Claude passthrough paths —
normalizeClaudeUpstreamMessages()is now called unconditionally in bothcompatibleBridgeand pure passthrough, ensuringrole:"system"messages are always extracted to the top-levelsystemparameter. (#2519 — thanks @herjarsa) -
fix(handler): capture Gemini
thought_signaturein non-streaming response path — the non-streaming translator now capturesthoughtSignaturefrom Gemini thinking model parts and persists them so follow-up turns can resolve them correctly. (#2518 — thanks @herjarsa) -
fix(kiro): replace broken social OAuth with device flow — rewrites Kiro's Google/GitHub social login from the broken PKCE
kiro://custom protocol to AWS Cognito device flow, which works correctly in web/proxy environments. (#2524 — thanks @disonjer) -
fix(providers): resolve
opencode/→opencode-zenslug mismatch + add 40+ new models —opencodeis now a proper alias foropencode-zenin executor, model resolver, and provider registry; adds GPT 5.x, Claude 4.x, Gemini 3.x, Grok, Kimi, and other models with tests. (#2517 — thanks @herjarsa) -
fix(antigravity): fail over stalled Antigravity sessions — new
ANTIGRAVITY_PRE_RESPONSE_TIMEOUT_CODEshared constant for pre-response timeout detection, automatic failover to next account when session stalls before headers arrive. Node.js engine range relaxed to>=20.20.2. (#2464 — thanks @dhaern) -
fix(deepseek-web): fix SSE parser, prompt format, and error handling — handles all 3 DeepSeek SSE stream formats (initial fragments, APPEND operations, bare string tokens), simplifies prompt to single-turn to prevent chat marker leakage, and checks
json.codebefore token extraction. (#2502 — thanks @ovehbe) -
fix(codex): accept
auth.jsonwithoutauth_modefield on import — Codex CLI no longer writesauth_mode; import now accepts both formats as long as required tokens are present. Semantic cache read now requires explicittemperature: 0. (#2536 — thanks @janeza2) -
fix(freetheai): add
/chat/completionsto baseUrl to resolve 404 errors. (#2557 — thanks @lordavadon2) -
fix(qoder): route PAT tokens to Qoder native API instead of DashScope — detects
pt-prefixed tokens and routes toapi.qoder.comwith proper User-Agent header. (#2559 — thanks @herjarsa) -
fix(perf): cache compiled RegExp in RTK compression hot path — eliminates thousands of redundant
new RegExp()instantiations per second. (#2553 — thanks @soyelmismo) -
fix(reasoning-cache): auto-start periodic cleanup on module load — the
server-init.tsjob was never imported (dead code), causing thereasoning_cachetable to grow indefinitely. Now runs 30-min cleanup cycles automatically. (#2552 — thanks @soyelmismo) -
fix(claude): omit
context-1mbeta for Sonnet — restrict to Opus-only to avoid long-context credit gate errors. Addafk-mode-2026-01-31, replaceredact-thinkingwiththinking-token-count-2026-05-13. (#2568 — thanks @unitythemaker) -
fix(codex): relax
auth_modecheck in frontend import preview — acceptundefined/null/"chatgpt"instead of requiring"chatgpt"strictly, matching the backend fix in #2536. (#2567 — thanks @janeza2) -
fix(kimi): declare vision capability for Kimi K2.6 in all 4 layers —
providerRegistry,modelSpecs,catalog.tskeyword list, and PlaygroundVISION_MODELS; previously the model silently rejected image uploads. (#2573 — thanks @herjarsa) -
fix(dashboard): paginate request-log viewer beyond 300 rows —
getCallLogsnow acceptsoffsetwith parameterized SQL (eliminates string-interpolatedLIMIT);RequestLoggerV2grows its window via "Load more" + IntersectionObserver infinite scroll, resetting on filter change. (#2576) -
fix(cli): use
/api/monitoring/healthfor server readiness check —waitForServer()was polling the auth-protected/api/health(401), causingomniroute serveto hang indefinitely. (#2578 — thanks @amogus22877769) -
fix(combo): detect invalid model errors via structured error codes + regex fallback — when a combo target rejects a model (e.g. free account vs Pro), the router now recognizes
model_not_found/deployment_not_foundcodes and 6 regex patterns, and falls through to the next target instead of stopping the loop. (#2534 — thanks @HALDRO) -
fix(security): post-review hardening batch —
spawnSyncarg-array replacesexecSyncstring-template (command injection), CSPunsafe-evalgated on!app.isPackaged,requireManagementAuthguard on budget/bulk and resilience/reset endpoints, error messages sanitized in gemini-web/claude-web/copilot-web/oauth/agents catch blocks, circuit breaker persistslastFailureKind, and combo resetsexhaustedProvidersper set-retry iteration. (#2435) -
fix(@omniroute/opencode-plugin): honor
geminiSanitizationandfetchInterceptorfeature flags — both were applied unconditionally; now each fetch layer is gated by its flag (default ON), and disabling both falls back to plain SDK fetch. (#2546) -
fix(#2575): check DB feature flag override in
arePrivateProviderUrlsAllowed()— supports runtime toggle without restart. (#2595 — thanks @herjarsa) -
fix(mimo): add
supportsVisionflag to MiMo-V2.5, V2.5-Pro, and V2-Omni — previously image uploads were silently rejected. (#2592 — thanks @herjarsa) -
fix(ops): propagate
OMNIROUTE_SKIP_DB_HEALTHCHECKenv var to periodic DB health check scheduler — companion fix to #2554. (#2591 — thanks @soyelmismo) -
fix(github): remove incorrect
openai-responsestargetFormat from GitHub Copilot's Haiku/Sonnet models. (#2583 — thanks @oyi77) -
fix(copilot): stabilize responses configuration — removes 865 lines of unstable config, simplifies handler. (#2579 — thanks @ivan-mezentsev)
-
fix(#2544): add SSE heartbeat keepalive to Responses API transform stream — prevents Codex CLI 0.130.0 from disconnecting during long thinking/reasoning phases. (#2599 — thanks @herjarsa)
-
fix(memory): extract system role messages in semantic passthrough path to prevent 400 on memory injection — system messages were being passed as-is to providers that reject mixed roles. (#2474 — thanks @Tentoxa)
-
fix(@omniroute/opencode-provider): include
limit.contextin model entries for OpenCode context window detection — previously OpenCode couldn't determine model context size. (#2482 — thanks @herjarsa) -
fix(mimo): add
supportsVisionflag to Kimi K2.6 in providerRegistry + comprehensive vision tests for MiMo V2.5/V2.5-Pro/V2-Omni. (#2600 — thanks @herjarsa) -
fix(proxy): prefer scoped proxies over registry global fallback — legacy provider-specific proxy was being shadowed by a registry-global fallback across both storage backends. Resolution now follows strict specificity: account → provider → combo → global. (#2606 — thanks @terence71-glitch)
-
fix(@omniroute/opencode-plugin): canonical-twin dedup + alias-fallback enrichment —
/v1/modelsreturned the same model under both alias (cc/claude-opus-4-7) and canonical (claude/claude-opus-4-7) names; now drops ~75 canonical duplicates and rescues ~88 raw-id rows with proper provider prefix via alias-index fallback. Also emitscost,release_date,modalitiesfields in static catalog and raises provider label threshold to 12 chars (preservesAssemblyAI,Antigravityverbatim). (#2607 — thanks @mrmm) -
fix(registry): populate empty models arrays for HuggingFace (6 models) and HackClub (3 models) + fix Snowflake placeholder baseUrl to
{account}template pattern. (#2611 — thanks @oyi77)
🌐 Internationalization
- i18n(zh-CN): translate 830 missing UI strings — replaces all
__MISSING__:placeholders with proper Chinese translations. (#2523 — thanks @InkshadeWoods) - i18n(dashboard): add missing dashboard keys and fix EN fallbacks — hundreds of hardcoded English strings across cache, caveman, costs, skills, memory, and evals pages replaced with
t()calls. (#2500 — thanks @Gi99lin) - i18n(pt-BR): complete and fix Brazilian Portuguese translation — comprehensive overhaul of pt-BR locale with ~3000 lines of quality translations, filling all missing keys and correcting existing entries. (#2543 — thanks @alltomatos)
- i18n(ru): comprehensive Russian translation update — ~2000 lines of corrected and filled translations. (#2550 — thanks @AgentAlexAI)
- i18n(all): comprehensive localization and UI refactoring — 42 locale files synchronized with missing keys, cloud-agents page i18n rewrite, and consistent
t()usage across 21 dashboard components. (#2580 — thanks @alltomatos) - i18n(all): translate freeTier provider strings across 41 locales — replaces
__MISSING__:Free Tier Providersplaceholders with proper translations in bothcommonandprovidersnamespaces. (#2609 — thanks @leninejunior) - i18n(pt-BR): eliminate all 1270 remaining
__MISSING__markers — completes pt-BR translation across 41 namespaces to true 100% coverage. (#2610 — thanks @leninejunior)
📝 Maintenance
- chore: remove Akamai VPS deploy from release workflow and skills.
- chore(deps): bump
actions/setup-nodefrom v4 to v6 +randomBytessecurity fix for cloud agent task IDs. (#2589) - chore(deps): bump
actions/upload-artifactfrom v4 to v7. (#2588) - chore: ignore
.claude/worktreesfrom git tracking. - chore(ci): auto-lock release branch on version publish — new CI workflow applies
lock_branchprotection when a GitHub Release is published. (#2542) - docs: redesign README — marketing-first layout with accurate provider counts. (#2490)
[3.8.1] — 2026-05-21
✨ New Features
- feat(settings): Feature Flags Settings Page (Card Grid + DB overrides) — fully implements the feature flags UI dashboard using Variant A (Card Grid) with Glassmorphism, complete with global
GET/PUT/DELETEAPI routes, Zod validation, debounced search, category filters, and full 30+ locale i18n support. Resolves priority hierarchy to DB > ENV > Defaults. (#2457) - feat(db): multi-driver SQLite abstraction layer — new
SqliteAdapterinterface with 3 concrete adapters (betterSqliteAdapter,nodeSqliteAdapter,sqljsAdapter) and adriverFactorythat cascadesbetter-sqlite3→node:sqlite→sql.js (WASM). Enables OmniRoute to run on any JavaScript runtime (Node.js, Bun, Deno, Cloudflare Workers) without native binary dependencies.better-sqlite3moved tooptionalDependencies. (#2447) - feat(settings): Claude Fast Mode toggle in Settings › AI — opt-in toggle that forwards
X-CPA-Force-Fast-Modeheader so a paired CLIProxyAPI build can reach Anthropic Fast Mode (speed:"fast"). Model-gated to Opus models matching Anthropic's binary KT() check. (#2449 — thanks @NomenAK) - feat(settings): Codex Fast Tier — tier dropdown (
default/priority/flex) + per-model gate preventing 400 errors from OpenAI when the tier toggle was on for non-Fast-eligible models. (#2451 — thanks @NomenAK) - feat: align Antigravity 2.0.1 support — updated client profile, upstream headers, and model aliases. (#2443 — thanks @dhaern)
- feat: enhance
extractBearerto supportx-api-keyfor Anthropic API style auth. (#2436 — thanks @thedtvn) - feat(memory): wire
createMemorytoupsertSemanticMemoryPoint(Qdrant). (#2439 — thanks @NomenAK)
🔧 Bug Fixes & Refactors
- fix(deepseek-web): rewrite auth to userToken Bearer + WASM PoW solver. (#2452 — thanks @ovehbe)
- chore: update node dependencies and runtime support. (#2453 — thanks @backryun)
- fix(translator): fix 3 Kiro
tool_resultdefects causing 400 on follow-up turns — missingtool_use_idmapping, orphan result blocks, and conversation ID collision on assistant-first turns. (#2447) - fix(translator): treat
developerrole as system in OpenAI → Claude translation —openAIToClaudenow extractsdeveloper-role messages intosystemParts(same assystem) and filters them from the non-system message list, preventing identity context injected via the Responses APIdeveloperrole from silently becoming an assistant turn when routing to a Claude-format provider. (#2407) - fix(antigravity): deduplicate
removeHeaderCaseInsensitive— export canonical implementation fromantigravityClientProfile.tsand remove the local copy inantigravity.ts; exportAntigravityCredentialsLiketype for cross-module use. (#2433 — thanks @Gi99lin) - refactor(docs): enhance frontmatter handling in DocPage — gray-matter Date object parsing bug fix. (#2448 — thanks @ovehbe)
- fix(jules): Jules API parity and cloud-agent provider registration. (#2438)
- fix(i18n): harden diff key extraction tag sanitization in
extract-keys-from-diff.mjs. - chore(i18n): refresh fr/es/de locales + add missing
settings.updatekey. (#2437) - fix(dashboard): allow bracketed combo names — align dashboard combo-name validator regex with the shared/server schema updated in PR #2354; names like
Claude [1m]are now accepted in the create/edit form. (#2458 — thanks @congvc-dev) - docs(agentrouter): recommend native provider as the simple path — guide now prefers the built-in AgentRouter provider instead of manual OpenAI-compatible configuration. (#2429 — thanks @leninejunior)
- feat(settings): surface Codex Fast Tier toggle in Settings › AI — companion UI toggle for the Codex Fast Tier feature. (#2440 — thanks @NomenAK)
🔒 Security Fixes
- fix(security): replace
execSyncstring-template withspawnSyncarg-array inplugin.mjs— eliminates shell command injection via malicious plugin names. - fix(security): gate Electron CSP
unsafe-evalon!app.isPackagedinstead of URL substring match — was leakingunsafe-evalinto production builds; merged duplicateconnect-srcdirectives. - fix(api): add
requireManagementAuthto/api/usage/budget/bulkand/api/resilience/reset— both endpoints exposed spend data and circuit-breaker controls without auth. - fix(security): route catch-block error messages through
sanitizeErrorMessage()ingemini-web,claude-web,copilot-webexecutors,oauthroute, and cloud-agent task routes — prevents stack traces and internal paths leaking into HTTP responses. - fix(codex):
refreshCredentialsreturnsnull(not error-object) on token refresh failure — prevents base executor from spreading{error}onto active credentials. - fix(tokenRefresh): safe
unknown-error access incatchblock (error instanceof Error ? error.message : String(error)). - fix(combo): reset
exhaustedProvidersset at start of each set-retry iteration — providers excluded in a failing pass now get a second chance on retry. - fix(circuitBreaker): persist and restore
lastFailureKindvia theoptionsJSON column — kind-based cooldown overrides (cooldownByKind) now survive server restarts.
[3.8.0] — 2026-05-06
🚀 Post-release hotfixes e contribuições (2026-05-06 → 2026-05-20)
2026-05-20
- feat(batch): implement 10 feature requests harvested from issues — T3 Chat Web executor (cookie-based), per-request exhausted-provider tracking (#1731) to skip quota-drained providers mid-combo, Zed Docker detection, API key rotator health dashboard, Kiro multi-account isolation, context-window model filtering, cost blending in combos, combo config tests, provider validation branches, and postinstall support scripts. (#2414)
- feat(combos): add
falloverBeforeRetrystrategy — combo routing now falls over to the next target before retrying the same model, eliminating the tail-latency spike from exhausting all per-model retries on a failing endpoint. Also wraps the retry loop in asetTryouter loop for per-target retry coordination. (#2417 — thanks @hartmark) - fix(gamification): resolve 6 implementation gaps — missing
SELECTincheckActionCountBadgesSQL (was silently skipping 8 badges), federation leaderboard auth enforcement, paginationoffsetparameter no longer silently discarded, admin anomaly view now computes real z-scores,addXpcorrectly calculates initial level from XP amount, and barrelindex.tsfor clean module exports. 72-test suite covering all fixes. (#2421 — thanks @oyi77) - docs: add AgentRouter provider setup guide — step-by-step instructions for connecting OmniRoute to AgentRouter.org's Claude-compatible relay endpoint, covering API key configuration and wire-image headers. (#2422 — thanks @leninejunior)
- fix(claude): drop orphan
tool_resultblocks left behind whenfixToolAdjacencystrips a danglingtool_use— resolves HTTP 400 "unexpected tool_use_id in tool_result blocks" from the Anthropic API on truncated histories.fixToolPairsnow re-runs after everyfixToolAdjacencypass across all three call sites (contextManager.ts,base.ts,claudeCodeCompatible.ts). (discussion #2410) - fix(playground): guard against
null/non-string model IDs in Playground dropdowns —typeof m?.id !== "string"check prevents a silent crash in the provider discovery loop andfilteredModelscomputation that was leaving all Playground dropdowns empty when/v1/modelsreturned entries withid: null; adds deduplication viaSetto eliminate duplicate React key warnings. - fix(mitm): point MITM runtime manager re-export to the compiled
.jsentrypoint — fixes module resolution after build when the.tssource is no longer present. - fix(storage): persist
STORAGE_ENCRYPTION_KEYacross upgrades (closes #1622) — ensures that SQLite encryption keys are preserved during version upgrades. (#2428 — thanks @Chewji9875) - fix(auth): auto-reset credential
apiKeyHealthstatus on successful connection test. (#2427 — thanks @clousky2020) - fix(mitm): drop
.jsextension onmanager.runtimere-export to fix webpack packaging issue. (#2425 — thanks @NomenAK) - fix(image): support Antigravity image generation and add Gemini 3.5 Flash support. (#2423 — thanks @backryun)
2026-05-19
- chore(i18n): comprehensive dashboard i18n coverage — 6 rounds of parallel refactoring replacing hardcoded English/Portuguese text with
t()calls across 57+ dashboard pages; 420+ new keys added toen.jsoncoveringsettings,playground,analytics,apiManager,providers,skills,memory,agents, and 15 other namespaces (coverage: ~88%, up from ~20%). - fix(offline): avoid SSR/CSR hydration mismatch on the offline status page — switches from a
useStatelazy initializer (which accessednavigator.onLineon the server) touseSyncExternalStorewith a distinctfalseserver snapshot, eliminating the React hydration warning. - fix(cli-tools): guard
modelIdtype before calling.indexOf()— prevents aTypeErrorwhen a model entry without a stringidreaches the comparison logic in CLI Tools. - fix(providers): add missing
isLocalProviderimport and update changelog. - fix(resilience): add API Key health tracking with automatic rotation and UI toast alerts. (#2412 — thanks @clousky2020)
- feat(providers): support Gemini API keys for Gemini CLI executor. (#2408 — thanks @benzntech)
- feat(gamification): implement Gamification & Leaderboard System with non-blocking event-driven updates. (#2405 — thanks @oyi77)
- fix(providers): Kilo Code provider no longer blocks on a missing local
kilocodeCLI binary — the provider uses OAuth device flow + direct HTTPS toapi.kilo.aiand never required the CLI at runtime; the connection test was hard-failing with "Local CLI runtime is not installed" even when the OAuth token was valid. CLI Tools integration (/api/cli-tools/kilo-settings) keeps its own runtime check. (#2404 — thanks @Flexible78) - fix(db):
bun add -g omniroute(and other runtimes that skip postinstall) no longer surfaces a generic 500 —isNativeSqliteLoadErrornow also detects "Could not locate the bindings file" /MODULE_NOT_FOUND, so the user gets the friendly rebuild guide instead. (#2358 — thanks @yamansin) - fix(kiro): enable Google OAuth login option in the Kiro auth modal — surfaces the Google SSO button alongside the existing identity providers. (#2392 — thanks @congvc-dev)
- fix(security): drop hashing layer in
sessionPoolKeyafter switching to a non-cryptographic key derivation strategy that clears CodeQL alert #247. (#2396) - feat(providers): Gemini Web cookie-based provider — proxies google.com chat through a session cookie, allowing free Gemini access without API keys. (#2380 — thanks @oyi77)
- model: add Composer 2.5 to the Cursor provider catalog. (#2381 — thanks @backryun)
- fix:
tool_usewithout adjacenttool_resultcauses Claude 400 — adjacency guard now also applies insidecompressContext. (#2383 — thanks @oyi77) - build(deps): bump
electronfrom 42.0.1 to 42.1.0 in/electron. (#2397) - build(deps): production group bumps — 4 updates. (#2398)
- build(deps): development group bumps — 4 updates. (#2399)
- chore: sync
release/v3.8.0withmain(CodeQL hotfixes + Dependabot bumps) via merge commit.
2026-05-18
- fix(security): resolve CodeQL alerts #243/#244/#245 — incomplete URL substring sanitization and weak crypto signal hardening. (#2391)
- fix(security): switch
sessionPoolKeyderivation to HMAC-SHA256 to clear CodeQL alert #246 (insecure hash for sensitive data). (#2394) - docs(readme): restore the 9router acknowledgment that was inadvertently dropped during the v3.8.0 README rework. (#2393)
- refactor(dashboard): comprehensive nav, providers, endpoint, runtime, quota, pricing, budget redesign + quota sharing preview (sidebar restructure → 12 collapsible sections, 22 new routes). (#2384)
- fix(dashboard): PR #2384 follow-up review fixes — Runtime quota i18n, Budget projection logic, QuotaShare i18n externalization, Provider Limits semantic markup, bulk endpoint usage. (#2389)
- feat(content): add Haiper, Leonardo, Ideogram, Suno, and Udio as content/media providers. (#2377 — thanks @oyi77)
- feat(@omniroute/opencode-provider): expand config helpers, add MCP entry, live model fetch, and combo builder. (#2375 — thanks @mrmm)
- fix(claude-oauth): enable system-transforms pipeline for the native Claude executor (closes 400 billing-gate). (#2370 — thanks @thepigdestroyer)
- feat(content): extend providers with video, audio, TTS, music capabilities — Pollinations, MiniMax, Together, Replicate across Audio TTS and Transcription registries. (#2369 — thanks @oyi77)
- feat(providers): add Veo AI Free as a web-wrapper provider for generating video, image, and TTS without an API key. (#2366 — thanks @oyi77)
- feat(providers): add Replicate as a free provider for OpenAI-compatible inference with community models. (#2364 — thanks @oyi77)
- fix(claude): avoided redundant deep cloning of Claude Code messages during semantic passthrough preparation, improving memory/CPU efficiency for large histories. (#2362 — thanks @terence71-glitch)
- fix(providers): register
llm7in the executor registry and route Cohere via OpenAI-compatible layer. (#2361, #2360) - fix(rate-limiter): Redis is now opt-in — when
REDIS_URLis unset, the rate limiter falls back to the in-memory store instead of spammingECONNREFUSED. (#2357) - fix(streaming): emit protocol-aware stream errors —
createDisconnectAwareStream()now emits native Responses API or Claude API SSE error blocks based on the client protocol. (#2355 — thanks @dhaern) - fix(combos): allow bracketed combo names (e.g.
Claude [1m]) by updating validation schemas. (#2354 — thanks @congvc-dev) - fix(claude-code): semantic passthrough — preserve Claude Code
messages[]structure for native Claude OAuth and relay routes. (#2351 — thanks @terence71-glitch) - fix(usage): extract flat
cached_tokensandreasoning_tokensfrom OpenAI-compatible usage objects. (#2350 — thanks @TF0rd) - fix(translator): DeepSeek tool-call response lookup reads cached reasoning before falling back to empty string. (#2349 — thanks @herjarsa)
- fix(ui/tooltip): render in portal + clamp to viewport so tooltips aren't clipped in modal dialogs. (#2352 — thanks @slider23)
- fix(auto-routing): replace bare
getSettings()withgetCachedSettingsto stop 500 onauto/*requests. (#2346) - fix(docker): ship Dashboard Docs markdown in the container image. (#2348)
- fix(combo/validator): treat upstream responses carrying a non-empty
reasoning_contentas valid output. (#2341) - fix(account-fallback): classify Anthropic
Usage Limit ReachedasQUOTA_EXHAUSTEDwith a 1h cooldown. (#2321) - feat(providers): add GitHub Models as a free provider — GPT-5, o-series, DeepSeek-R1, Llama 4, Grok 3. (#2344 — thanks @oyi77)
- feat(providers): add Hackclub AI as a free provider — 30+ models, no credit card required. (#2339 — thanks @oyi77)
- feat(providers): add Microsoft Copilot Web executor — WebSocket-based provider. (#2340 — thanks @oyi77)
- feat(routing): LKGP stores last known good account
connectionIdalongside provider. (#2338 — thanks @oyi77) - feat(dashboard): add Claude Code auth import/export UI + i18n (three-PR series: libs, API routes, dashboard UI).
- feat(dashboard): add Gemini CLI auth import/export UI + i18n (three-PR series: libs, API routes, dashboard UI).
- fix(routing): implement embedding combos, local provider validation bypass, and resolve migration collisions.
- fix(build): import Monaco ESM API to fix webpack
nls.messages-loadererror. - fix(ui): v3.8.0 polish — connections border, sticky tabs, EN translations, save toasts, auto-combo catalog. (#2305 — thanks @mrmm)
- fix(auth+build): Bearer manage scope on management routes + lazy-load deepseek PoW solver. (#2308 — thanks @mrmm)
- fix(claude): guard orphan tool_use/tool_result pairs before upstream send. (#2312 — thanks @mrmm)
- fix(ui): remove count from batch removal button. (#2309 — thanks @hartmark)
- fix: remove implicit API key request caps — removes default 1K/5K/20K rate caps. (#2289 — thanks @josephvoxone)
- fix(sse): strip stale
Content-Encoding,Content-Length, andTransfer-Encodingheaders on non-streaming forward. (#2264 — thanks @gleber) - chore(providers): refresh provider model metadata and ordering. (#2318 — thanks @backryun)
- chore(providers): consolidate Alibaba provider entries. (#2319 — thanks @backryun)
- fix(streaming): harden stream readiness detection. (#2317 — thanks @dhaern)
- fix(v1/messages): default to non-streaming when
streamfield is absent for Anthropic format. (#2326 — thanks @thepigdestroyer) - fix(claude):
fitThinkingToMaxTokenscaps thinking budget to model's output ceiling. (#2327 — thanks @thepigdestroyer) - fix(codex): Codex reasoning priority resolves
modelEffortbeforeexplicitReasoning. (#2335 — thanks @terence71-glitch) - fix(providers): providers page no longer deadlocks when no providers are configured. (#2329 — thanks @slider23)
- chore(providers): update HuggingFace to use the new
/v1/router endpoint. (#2322 — thanks @backryun) - fix(security): resolve CodeQL ReDoS + URL sanitization alerts.
- fix(auth): stop retrying unrecoverable token refresh failures and include connection id in token health check credentials.
- fix(auth): return synthetic credentials for noAuth free providers and show no-auth card in dashboard instead of OAuth modal.
- fix(endpoint): replace nested
<button>with<div role=button>in tunnel toggle rows to fix hydration warnings. - fix(migrations): resolve version collision at migration slot 056 and add batch deletion API. (#2294 — thanks @hartmark)
- feat(batch): global rate-limit header cache with 60s TTL + 24h retry window. (#2299 — thanks @hartmark)
- feat(cc-bridge): config-driven per-provider system-block transform DSL. (#2286, closes #2260 — thanks @mrmm)
- feat(deepseek-web): full DeepSeek web API executor with Keccak PoW solver. (#2295 — thanks @oyi77)
- feat(i18n): add Azerbaijani (az / 🇦🇿) language support — new locale in
config/i18n.json, 42 total supported languages. - build(deps): bump
actions/checkoutfrom 4 to 6 in CI workflows. (#2288)
2026-05-17
- fix(codex): bulk import Codex
auth.json— multi-file upload, paste-from-clipboard, and ZIP archive support. (#2343) - feat(codex): import single Codex
auth.jsonas an OAuth connection (one-click migration from Codex Desktop). (#2336) - feat(codex-auth): rename
exportaction + gate "Apply Local" behind a confirmation modal to prevent accidental local config overwrite. (#2332) - fix(providers): providers page empty-state — missing i18n keys and "Add Provider" CTA so first-time users can add a provider. (#2333, #2337)
- fix(providers): Fix Providers empty state blocking first provider setup. (thanks @slider23)
- feat(providers): bulk add API keys with Single/Bulk tabs.
- feat(ui): comprehensive dashboard UX rework including simple/advanced modes for RTK/Caveman, human-readable error badges, InfoTooltip/PresetSlider shared components, sidebar subtitles, and provider category filters. (#2315, #2316 — thanks @oyi77)
- feat(provider): add Gitlawb Opengateway provider (xiaomi-mimo + gmi-cloud) with hasFree flag support. (#2314 — thanks @oyi77)
- feat(i18n): add simple/advanced mode keys and missing provider filter keys (
allProviders,audioProviders,showFreeOnly).
2026-05-16
- feat(deepseek-web): full DeepSeek web API executor with PoW solver — also landed via PR #2295. (thanks @oyi77)
- feat(batch): global rate-limit header cache with 60s TTL — also via #2299.
- feat(cc-bridge): config-driven per-provider system-block transform DSL — also via #2286.
- feat(dashboard): provider summary card, free test button, sidebar order, i18n fix.
- feat(dashboard): A2A audit page, stats bar on MCP audit, sidebar deduplication.
- feat(skills): add 5 CLI skill manifests + AgentSkills / OmniSkills dashboard pages. (#2284)
- fix(translator): map
developer→systemby default for non-OpenAI-family providers. (#2281) - fix(api/combos): add API-key-safe
GET /v1/combosendpoint. (#2300) - fix(embeddings/registry): add DeepInfra to the embedding provider registry. (#2298)
- fix(opencode-zen): flag
qwen3.6-plusandqwen3.6-plus-freewithtargetFormat: "claude". (#2292) - fix(settings): default
debugModetotrueon fresh installations. - fix(sse): remove dead-code flag leak in
claudeCodeToolRemapper. (#2290 — thanks @thepigdestroyer) - fix(sse): strip stale
Content-Encoding,Content-Length,Transfer-Encodingfrom upstream responses. (#2291 — thanks @thepigdestroyer) - fix(migrations): resolve version collisions and add schema repair for quota thresholds.
2026-05-15
- feat(cli): CLI v4 — Commander.js architecture, 50+ commands, interactive TUI, full i18n (42 locales), plugin system (Fases 0–9). (#2280)
- feat(skills): publish 3 operational SKILL.md manifests + AI Skills dashboard entry. (#2276)
- feat(termux): Android/Termux headless support — auto-detect Android platform for headless mode. (#2273 — thanks @t-way666)
- feat(limits): per-window quota cutoffs across all providers with usage data. (#2267 — thanks @payne0420)
- feat(api-keys): configurable default rate limits via
DEFAULT_RATE_LIMIT_PER_DAYenv var. (#2266 — thanks @gleber) - feat(authz):
managementPolicyaccepts API keys withmanagescope. (#2265 — thanks @gleber) - feat(mcp): MCP accessibility-tree smart filter engine — collapses ≥30 repeated sibling lines, 60-80% token savings.
- feat(auth): CLI machine-ID HMAC-SHA256 token for zero-friction local auth without JWT/password.
- feat(security): route protection tiers — 5 tiers: public/read-only/protected/always/local-only.
- feat(compression): Caveman
SHARED_BOUNDARIES— all 6 languages × 3 intensities embed boundary clause. - feat(runtime): dynamic SQLite 5-step fallback chain — bundled → runtime-installed → lazy-install → node:sqlite → sql.js.
- feat(cli): standalone system tray with PowerShell fallback on Windows (
omniroute --tray). - fix(providers/command-code): send required
skillsandstreampayload fields. (#2271 — thanks @ddarkr) - chore: ignore
.playwright-mcp/generated artifacts. (#2269 — thanks @backryun) - chore: tidy up deprecated models from Windsurf provider registry. (#2279 — thanks @backryun)
- chore(deps): node dependency updates. (#2259 — thanks @backryun)
- build(deps): bump
mermaidfrom 11.14.0 to 11.15.0. (#2178)
2026-05-08 a 2026-05-14
- feat(guardrails/vision-bridge): add
VISION_BRIDGE_BASE_URL+VISION_BRIDGE_API_KEYenv overrides for non-Anthropic vision-bridge routing. (#2232) - feat(claude-web): implement session-based Claude Web executor with auto-refresh authentication. (#2283 — thanks @oyi77)
- refactor(@omniroute/opencode-provider): complete rewrite of the npm helper — tsup build (CJS + ESM +
.d.ts), schema-correct output,baseURLdeduplication, input validation, 13 unit tests. Versioned as0.1.0. - BREAKING: dropped Node 20.x support. Minimum Node version is now 22.22.2 (or 24.0.0+).
- fix(auth): accept
x-api-keyheader inextractApiKeyso Anthropic-native clients hit the same per-key policy enforcement. (#2225) - fix(translator/claude-to-openai): stop including
cache_creation_input_tokensinprompt_tokens. (#2215) - fix(kiro): harden OpenAI-to-Kiro translator for API compliance. (#2251 — thanks @8mbe)
- fix(models): sync managed model aliases with provider model visibility. (#2250 — thanks @InkshadeWoods)
- fix(models/cleanup): align managed model cleanup for imported models. (#2261 — thanks @InkshadeWoods)
- fix(executor/claude-code): store tool-name round-trip metadata in non-enumerable
_toolNameMap. (#2254 — thanks @Rikonorus) - fix(streaming): strip upstream
Content-Encoding,Content-Length,Transfer-Encodingheaders from SSE responses. (#2253 — thanks @Rikonorus) - fix(security): remediate CodeQL vulnerabilities (ReDoS, cryptographic bias, stack trace exposure, weak password hashing). (#216, #215, #211, #208, #206, #210)
- fix(providers/blackbox-web): add
BLACKBOX_WEB_VALIDATED_TOKENenv override and 403 token-error disambiguation. (#2252) - fix(auth):
REQUIRE_API_KEY=falseinvalid Bearer no longer 401s the whole request. (#2257) - feat(resilience): add model cooldowns dashboard card with real-time list, individual/bulk re-enable, and auto-refresh.
- feat(resilience):
useUpstream429BreakerHintstoggle. (#2133 — thanks @eleata) - feat(auto): zero-config auto-routing with
auto/prefix — dynamic virtual combo from connected providers with 6 variant profiles. (#2131 — thanks @oyi77) - feat(kiro): headless auth via kiro-cli SQLite, image support, tool overflow handling, model list sync. (#2129 — thanks @christlau)
- feat(cursor): surface Cursor Pro plan usage on provider-limits dashboard. (#2128 — thanks @payne0420)
- feat(mitm): dynamic Linux certificate path detection for multi-distro MITM cert trust. (#2134 — thanks @flyingmongoose)
- feat(1proxy): add dedicated settings tab with proxy rotation support. (#2135 — thanks @oyi77)
- feat(responses): degrade
background: trueto synchronous execution with a warning. (#2164 — thanks @Yosee11) - feat(api): aggregate combo model metadata in catalog endpoint. (#2166 — thanks @faisalill)
- feat(oauth): complete Windsurf and Devin CLI OAuth + API-token flows. (#2168 — thanks @Zhaba1337228)
- feat(antigravity): support custom Google Cloud project ID. (#2227 — thanks @nickwizard)
- feat(cli): CLI Integration Suite — 5 new management commands, 3 API endpoints, config generators for 6 tools. (#2240 — thanks @oyi77)
- fix(sanitizer): preserve
reasoning_contenton assistant messages withtool_calls. (#2140 — thanks @DavyMassoneto) - fix(catalog): ensure individual models expose
context_lengthviagetTokenLimit()fallback chain. (#2136 — thanks @herjarsa) - fix(docker): remove docs directory from
.dockerignore. (#2137, #2120 — thanks @hartmark) - fix(providers): restore cloud agent provider exports and logger import. (#2138 — thanks @backryun)
- fix(providers): remove duplicate
CLOUD_AGENT_PROVIDERSdeclaration. (#2141 — thanks @backryun) - fix(translator): preserve
body.systemin openai→claude when Claude Code sends native format. (#2130) - fix(authz): classify
/dashboard/onboardingas PUBLIC to unblock setup wizard. (#2127) - fix(i18n): complete Simplified Chinese translations. (#2115 — thanks @boa-z)
- fix(sse): classify hour quota errors as QUOTA_EXHAUSTED. (#2119 — thanks @clousky2020)
- fix(sse): fix CC-compatible streaming bridge. (#2118 — thanks @rdself)
- fix(cliproxyapi): detect Anthropic-shaped request bodies and route to
/v1/messages. (#2165 — thanks @Brkic-Nikola) - fix(claudeHelper): preserve latest assistant thinking blocks verbatim. (#2224 — thanks @NomenAK)
- fix(deepseek): preserve
reasoning_contentthrough full pipeline for DeepSeek V4 models. (#2231 — thanks @kang-heewon) - fix(chatcore): stop leaking provider credentials in response headers.
- fix(export): exclude telemetry/usage-history tables from JSON config backups by default. (#2125)
- build(deps): regenerate
package-lock.jsonto matchhttp-proxy-middleware4.x bump. (#2228 — thanks @NomenAK)
2026-05-06 a 2026-05-07 (lançamento inicial v3.8.0)
- feat(zed): Zed IDE Docker support — when OmniRoute runs in Docker and Zed is on the host, the Import flow now returns a 422 with
zedDockerEnvironment: trueand the dashboard auto-expands a Manual Token Import panel (newPOST /api/providers/zed/manual-importendpoint with Zod validation). Includes Docker detection utility (/.dockerenv+ cgroup heuristics) and a setup guide atdocs/providers/ZED-DOCKER.md. ([#2306]) - feat(workflow):
/implement-featuresgains pre-flight triage script (scripts/features/feature-triage.mjs) classifying open feature requests into 8 buckets — fresh issues (<14d) stay dormant to give the community time to react, engagement override (≥5 👍 or ≥3 unique non-bot commenters) absorbs early, already-delivered detection via merged PRs + CHANGELOG + git log closes issues with version + PR reference, staleneed_details/(>30d) is closed politely, ageddefer/(>90d) is re-evaluated, and externally-closed issues clean up_ideia/automatically. Idea files now carry a YAML frontmatter snapshot enabling incremental comment re-sync. 53 unit tests cover the new logic. - feat(providers): add GitHub Models as a free provider — GPT-5, o-series, DeepSeek-R1, Llama 4, Grok 3 with GitHub PAT auth and dynamic model fetch from
api.github.com. (#2344 — thanks @oyi77) - feat(providers): add Hackclub AI as a free provider — 30+ models, no credit card required, optional API key auth with passthrough model support. (#2339 — thanks @oyi77)
- feat(providers): add Microsoft Copilot Web executor — WebSocket-based provider translating OpenAI chat completions to Copilot's proprietary event protocol with per-token session pool isolation. (#2340 — thanks @oyi77)
- feat(routing): LKGP stores last known good account
connectionIdalongside provider — combo routing now prioritizes the exact connection that last succeeded, with graceful provider-level fallback for old records. (#2338 — thanks @oyi77) - feat(i18n): add Azerbaijani (az / 🇦🇿) language support — new locale in
config/i18n.json(source of truth),src/i18n/messages/az.json(UI strings),docs/i18n/az/(full documentation set), README language bar, docs i18n index, and both translation pipeline scripts (generate-multilang.mjs,i18n_autotranslate.py). Total supported languages: 42. - feat(limits): per-window quota cutoffs across all providers with usage data — operators can set per-quota-window thresholds (e.g.
session=95%, weekly=80%) with cascading resolver (connection → provider default → global 98%) and zero-latency gate when nothing is configured. New migration 056, newGET /api/providers/quota-windowsendpoint, and Dashboard › Limits cutoff modal. (#2267 — thanks @payne0420) - feat(api-keys): configurable default rate limits via
DEFAULT_RATE_LIMIT_PER_DAYenv var — replaces hardcoded 1000/day fallback with Zod-validated configuration while preserving secure defaults for existing deployments. (#2266 — thanks @gleber) - feat(authz):
managementPolicyaccepts API keys withmanagescope — enables headless/programmatic management (provisioning providers, setting rate limits) without a browser session. (#2265 — thanks @gleber) - feat(termux): Android/Termux headless support — auto-detect Android platform for headless mode (no browser open), move
wreq-jsandtls-client-nodetooptionalDependenciesfor ARM compatibility, lazy-load WS proxy with graceful 503 when unavailable, setGYP_DEFINESforbetter-sqlite3ARM build, extended build timeout to 600s. (#2273 — thanks @t-way666) - feat(deepseek-web): full DeepSeek web API executor with Keccak PoW solver (
DeepSeekHashV1), SSE streaming, and auto-refresh session management viads_session_id. (#2295 — thanks @oyi77) - feat(cc-bridge): config-driven per-provider system-block transform DSL — operators can now configure system prompt transformations per-provider via Dashboard settings UI. (#2286, closes #2260 — thanks @mrmm)
- feat(batch): global rate-limit header cache with 60s TTL + 24h time-based retry window — shares rate-limit throttle state across sequential batches and uses time-based retry limits for robust large-batch processing. (#2299 — thanks @hartmark)
- feat(providers): improve Cohere provider support, expanding models and accurately updating OpenAI context limits. (#2313 — thanks @backryun)
- feat(claude-web): implement session-based Claude Web executor with auto-refresh authentication — enables direct Claude Web API access without an API key. (#2283 — thanks @oyi77)
- feat(skills): add 5 CLI skill manifests + AgentSkills / OmniSkills dashboard pages — enables external AI agents to discover and invoke OmniRoute capabilities. (#2284)
- feat(providers): add llama.cpp as local provider —
llama-cpp(aliasllamacpp) added toLOCAL_PROVIDERSandSELF_HOSTED_CHAT_PROVIDER_IDS; default base URLhttp://127.0.0.1:8080/v1; no API key required; uses the default OpenAI-compatible executor (#1980) - feat(providers): bulk add API keys with Single/Bulk tabs.
- feat(provider): add Gitlawb Opengateway provider (xiaomi-mimo + gmi-cloud) with hasFree flag support. (#2314 — thanks @oyi77)
- feat(ui): comprehensive dashboard UX rework including simple/advanced modes for RTK/Caveman, human-readable error badges, InfoTooltip/PresetSlider shared components, sidebar subtitles, and provider category filters. (#2315, #2316 — thanks @dhaern, @oyi77)
- feat(i18n): add simple/advanced mode keys and missing provider filter keys (
allProviders,audioProviders,showFreeOnly). - feat(cli): full i18n support — 42 locales,
--langflag,config lang get/set/listcommands for CLI language selection. (#2285) - feat(claude-code): semantic passthrough for Claude Code
/v1/messagespayloads — preserves clientmessages[]structure (document blocks, tool_use/tool_result chains, cache_control, unknown content types) for native Claude OAuth andanthropic-compatible-cc-*relay routes, skipping broad normalization that could rewrite valid Claude Code semantics. (#2351 — thanks @terence71-glitch)
Changed
- CLI: Refactored architecture to use Commander.js as framework. Monolith
bin/cli-commands.mjs(2853 lines) removed — commands now live individually inbin/cli/commands/. No breaking changes in normal usage; all previously listed subcommands continue working.
Removed
-
bin/cli-commands.mjs— replaced by modular structure inbin/cli/commands/. -
bin/cli/index.mjs— replaced bybin/cli/program.mjs+bin/cli/commands/registry.mjs. -
bin/cli/args.mjs— replaced by Commander.js native parsing support. -
refactor(@omniroute/opencode-provider): complete rewrite of the npm helper. The
1.0.0artifact was non-functional —index.jsre-exported from.ts(unrunnable at install time) and the emitted shape didn't match the OpenCodehttps://opencode.ai/config.jsonschema. The new release ships a realtsupbuild (CJS + ESM +.d.ts), schema-correct output (npm: "@ai-sdk/openai-compatible", withmodelscatalog),baseURLdeduplication (no more/v1/v1), input validation, 13 unit tests, and full documentation indocs/frameworks/OPENCODE.md. Versioned as0.1.0to signal the pre-1.0 reset. -
chore(npm):
@omniroute/opencode-provider@0.1.0published to npmjs.com under the new@omnirouteorg. Install withnpm install --save-dev @omniroute/opencode-provider. -
BREAKING: dropped Node 20.x support. Minimum Node version is now 22.22.2 (or 24.0.0+). Required because http-proxy-middleware 4.x requires
node >=22.15.0. Users on Node 20 must upgrade — seepackage.jsonengines field and the README Node badge.
Security
- fix(oauth/windsurf): Windsurf Firebase token refresh now reads
WINDSURF_CONFIG.firebaseApiKeyinstead ofprocess.env.WINDSURF_FIREBASE_API_KEYdirectly. - fix(kiro/translator): assistant-first conversations no longer collide on a single
conversationId. - fix(utils/publicCreds):
decodePublicCred()no longer silently mangles raw credential overrides that don't matchRAW_VALUE_PATTERN. - fix(auth/extractApiKey):
x-api-keyfallback now only triggers when the request also carries ananthropic-versionheader. - fix(providers/qoder): the OAuth+PAT disambiguation message now actually surfaces.
- fix(authz/clientApi): when
REQUIRE_API_KEY=false, an invalid Bearer no longer 401s the whole request — falls through to anonymous (matching the "no auth required" semantics of the flag) with a single warning log carrying the masked key id. Fixes the surprise 401s that hit CLI integrations (Codex Desktop auto-config, Hermes Agent) that ship a stale Bearer in their saved config. (#2257)
Fixed
- fix(providers/llm7): add
llm7to the executor registry (open-sse/config/providerRegistry.ts). The provider was advertised in the dashboard catalog but missing from the executor table, so every connection test failed with a credential error. Now routes through the standard OpenAI-compatiblehttps://api.llm7.io/v1/chat/completionsendpoint with optional bearer auth. (#2361) - fix(providers/cohere): switch the Cohere upstream from
https://api.cohere.com/v2/chat(native shape) tohttps://api.cohere.com/compatibility/v1/chat/completions(OpenAI-compatible). The native endpoint returned{ message: { content: [...] } }which the combo test validator could not read, surfacing asProvider returned HTTP 200 but no text content.(#2360) - fix(combo/dispatch): add defensive
typeof target.modelStr === "string"guards around the LKGP fallback findIndex and the combo test target builder. Combo entries whosemodelStrfailed to resolve at routing time (regression after #2338 added per-account LKGP) used to crash the request withTypeError: e.startsWith is not a function; we now surface a clean error instead. (#2359) - fix(rate-limiter): Redis is now opt-in. When
REDIS_URLis unset, the rate limiter and API-key auth cache fall back silently to the in-memory store instead of spammingconnect ECONNREFUSED 127.0.0.1:6379for every request. Connection-error logging is also deduped so docker logs no longer flood under sustained outage. Single-instance deployments work out of the box; multi-instance deployments continue to use Redis whenREDIS_URLis provided. (#2357) - fix(auto-routing): stop the
ReferenceError: getSettings is not defined500 that everyauto/auto/*request raised.src/sse/handlers/chat.tscalled the baregetSettingssymbol without importing it; replaced with the already-importedgetCachedSettings(same shape, plus the auto-routing hot path benefits from the cache). (#2346) - fix(combo/validator): treat upstream responses carrying a non-empty
reasoning_content(orreasoning) field as valid output, even whencontentis null. Reasoning models likemoonshotai/Kimi-K2.5-TEE,zai-org/GLM-5-TEE, and the QwQ family put their answer inreasoning_contentonly — the quality validator was rejecting them with502: empty contentand triggering unnecessary combo fallbacks. (#2341) - fix(docker): the Dashboard Docs viewer now actually has documents to show.
.dockerignorewas hiding every file underdocs/exceptopenapi.yaml, so the in-product/docs/*viewer threwENOENT: no such file or directory, open '/app/docs/...'for every page. We now ship the ~5 MB English markdown tree and still exclude the ~45 MB of translations/screenshots/raster diagrams that were the original optimization target. (#2348) - fix(account-fallback): Anthropic OAuth (Claude Code Pro/Team) 429 responses carrying phrases like
Usage Limit Reached,Claude Pro usage limit reached, oryou've reached your usage limitare now classified asQUOTA_EXHAUSTEDwith a 1h cooldown instead ofRATE_LIMIT_EXCEEDEDwith a ~5s transient backoff. Previously every Claude Pro account cascaded into a tight retry loop until the 5h subscription window genuinely reset. Also honors absolute ISO timestamps embedded in the error body (Try again at 2026-05-17T10:00:00Z) so the cooldown matches the upstream's stated recovery time. (#2321) - fix(ui/tooltip): the shared
<Tooltip>component now renders into a React portal anchored todocument.bodyby default, so tooltips in modal dialogs (combo editor, etc.) are no longer clipped byoverflow:hiddenancestors. Adds an optionalmultilineprop that swaps the legacywhitespace-nowrapclamp formax-w-xs whitespace-normal break-wordswhen the label is long. Coordinates are clamped to the viewport so triggers near the right edge don't bleed off-screen. (#2352) - fix(claude): avoided redundant deep cloning of Claude Code messages during semantic passthrough preparation, improving memory/CPU efficiency for large histories. (#2362 — thanks @terence71-glitch)
- fix(streaming): emit protocol-aware stream errors —
createDisconnectAwareStream()now emits native Responses API (response.failed) or Claude API (event: error) SSE error blocks based on the client protocol instead of falling back to raw Chat Completions chunks, resolving upstream client parse failures on mid-stream disconnects. (#2355 — thanks @dhaern) - fix(combos): allow bracketed combo names (e.g.
Claude [1m]) by updating validation schemas and pinning exact combo lookup behavior before model suffix parsing. (#2354 — thanks @congvc-dev) - fix(v1/messages):
POST /v1/messagesnow defaults to non-streaming when thestreamfield is absent and the Anthropic source format is detected — preventsSTREAM_EARLY_EOFerrors from Anthropic SDK clients that omit the field per spec. (#2326 — thanks @thepigdestroyer) - fix(claude):
fitThinkingToMaxTokenscaps thinking budget to the model's output ceiling — eliminates HTTP 400 from Anthropic whenmax_tokens + budgetexceeds model limits (e.g. Opus 4.7's 128K ceiling). (#2327 — thanks @thepigdestroyer) - fix(codex): Codex reasoning priority now resolves
modelEffortbeforeexplicitReasoning— aligns with expected precedence and fixes suffix alias mismatches. (#2335 — thanks @terence71-glitch) - fix(translator): DeepSeek tool-call response lookup reads cached reasoning before falling back to empty string — preserves reasoning content in multi-turn tool-call flows. (#2349 — thanks @herjarsa)
- fix(providers): providers page no longer deadlocks when no providers are configured — setup hint is shown instead of an empty filtered list, allowing the first provider to be added. (#2329 — thanks @slider23)
- fix(usage): extract flat
cached_tokensandreasoning_tokensfrom OpenAI-compatible usage objects — providers like Xiaomi MiMo that return these as top-level fields instead of nesting inprompt_tokens_details/completion_tokens_detailsnow properly surface in call logs and dashboard. (#2350 — thanks @TF0rd) - chore(providers): update HuggingFace to use the new
/v1/router endpoint with dynamic model list (router.huggingface.co/v1/), removing the stale static model list. (#2322 — thanks @backryun) - fix(security): resolve CodeQL ReDoS + URL sanitization alerts.
- fix(auth): stop retrying unrecoverable token refresh failures and include connection id in token health check credentials.
- fix(auth): return synthetic credentials for noAuth free providers and show no-auth card in dashboard instead of OAuth modal.
- fix(endpoint): replace nested
<button>with<div role=button>in tunnel toggle rows to fix hydration warnings. - fix(claude): guard orphan tool_use/tool_result pairs before upstream send, resolving a critical Anthropic 400 error on truncated histories. (#2312 — thanks @mrmm)
- fix(ui): remove count from batch removal button for cleaner interface. (#2309 — thanks @hartmark)
- fix(sse): strip stale
Content-Encoding,Content-Length, andTransfer-Encodingheaders on non-streaming forward — fixes JSON truncation on gzipped Gemini responses where clients honoringContent-Lengthread only the compressed byte count of the decompressed payload, causing"Unterminated string in JSON"parse failures. RFC 7230 §6.1 compliant. (#2264 — thanks @gleber) - fix(executor/claude-code): store tool-name round-trip metadata in non-enumerable
_toolNameMapso it survives in-memory but is stripped byJSON.stringify()— prevents internal OmniRoute metadata from leaking to upstream providers. (#2254 — thanks @Rikonorus) - fix(streaming): strip upstream
Content-Encoding,Content-Length, andTransfer-Encodingheaders from SSE responses — prevents client-side decompression corruption when the proxy serves plain-text event streams through nginx/caddy. (#2253 — thanks @Rikonorus) - fix(kiro): harden OpenAI-to-Kiro translator for API compliance: recursively strip
additionalPropertiesand emptyrequired: []from tool schemas; merge consecutive assistant messages; prepend synthetic user for assistant-first conversations; convert orphaned tool results to inline text; enforceorigin: "AI_EDITOR"on all history user messages; deterministicuuidv5session caching. Closes #2213. (#2251 — thanks @8mbe) - fix(models): sync managed model aliases with provider model visibility — remove aliases when models are hidden/deleted, skip alias creation for hidden models during sync, restore aliases when unhidden, cross-connection safety guard prevents pruning aliases still valid from another connection. (#2250 — thanks @InkshadeWoods)
- fix(models/cleanup): align managed model cleanup for imported models — provider-level "Delete All" now also removes synced available model storage; delete-alias button only shown for alias-source rows; compatible models section uses proper 3-way source-aware delete logic. (#2261 — thanks @InkshadeWoods)
- fix(auth): accept
x-api-keyheader inextractApiKeyso Anthropic-native clients (Claude Code,@anthropic-ai/sdk) hit the same per-key policy enforcement as Bearer clients. Previously these requests were treated as anonymous, bypassing model/budget/rate-limit policies and showing up asNULLinusage_history.api_key_id(~50% of traffic invisible in Costs/Analytics).Authorization: Bearerstill wins when both are present (back-compat). (#2225) - fix(translator/claude-to-openai): stop including
cache_creation_input_tokensinprompt_tokens. Anthropic pads short prompts up to a 1024-token minimum on cache creation, so a 2-token"hi"could be reported as ~2008prompt_tokensand inflate downstream billing (Sub2API/NewAPI/OneAPI) ~250x.prompt_tokensnow matches the dashboard "Total In" (input + cache_read);cache_creation_tokensis exposed separately inprompt_tokens_details.cache_creation_tokensfor auditing. (#2215) - fix(ui/claude-extra-usage): clarify the toggle-success notification text to spell out the toggle→effect relationship ("Claude extra-usage blocking enabled/disabled" instead of the ambiguous "blocked/allowed"). (#2157)
- fix(providers/qoder): disambiguate the "Local CLI runtime is not installed" error when a user pastes a Personal Access Token but the connection is in OAuth/CLI-flavored mode. The test route now surfaces a single actionable message ("switch this connection to API Key auth") instead of cascading CLI + 401 errors. (#2247)
- fix(dashboard/api-manager): route custom OpenAI-/Anthropic-compatible provider IDs through
getProviderDisplayNameso the model grouping label showsCompatible (openai)instead of leaking the raw syntheticopenai-compatible-chat-<uuid>value. (#2021) - fix(providers/blackbox-web): add
BLACKBOX_WEB_VALIDATED_TOKENenv override and 403 token-error disambiguation. Blackbox/api/chatstarted rejecting requests whosevalidatedfield didn't match the frontendtktoken, even with a valid cookie + active subscription. Operators with the real token can now set the env var; otherwise the previous random-UUID fallback still ships, and a 403 with a token-specific body now surfaces a one-line "set BLACKBOX_WEB_VALIDATED_TOKEN" hint instead of the generic "cookie expired" message. (#2252) - fix(guardrails/vision-bridge): add
VISION_BRIDGE_BASE_URL+VISION_BRIDGE_API_KEYenv overrides so non-Anthropic vision-bridge calls can be routed through OmniRoute's own/v1self-loop, Google's Gemini OpenAI-compat endpoint, OpenRouter, or any other OpenAI-compatible URL — instead of being hardcoded tohttps://api.openai.com/v1(which failed with 401 for users without an OpenAI key, even when they configuredvisionBridgeModel: "google/gemini-2.0-flash"). Anthropic models keep their dedicated path. (#2232) - docs(security): document the ToS-violation hot spot of
ANTIGRAVITY_CREDITS=alwaysinSTEALTH_GUIDE.md, including why it draws Google abuse detection more aggressively than free-tier-only usage and the recommended posture (=retry, Auto-Combo spread, per-connection RPM caps). (#2246) - fix(translator/developer-role): convert OpenAI
developerrole →systemby default for non-OpenAI-family providers. Codex/Responses API clients hitting DeepSeek (and other OpenAI-compatible gateways: MiniMax, Mimo, GLM, Fireworks, Together, etc.) were getting400: unknown variant 'developer'because the previous default preserveddeveloperfor anytargetFormat=openaiupstream. New default: preserve only foropenai/azure-openai/azure/github(and any id containing"openai"); convert everywhere else. Operators can still force preservation per-model via the dashboard "Compatibility → preserveOpenAIDeveloperRole = true" toggle. (#2281) - fix(api/combos): add API-key-safe
GET /v1/combosendpoint that mirrors the/v1/modelsauth model. Previously/api/comboswas management-gated, blocking read-only integrations (e.g.opencode-omniroute-authplugin) that need to enrich combo capabilities from a normal Bearer API key. The new endpoint projects only public metadata (name, strategy, model ids, providerId, description) — internal routing details likeconnectionId, weights, and labels are stripped./api/combos(management) is unchanged. (#2300) - fix(embeddings/registry): add DeepInfra to the embedding provider registry. Custom embedding models on the DeepInfra provider (e.g.
Qwen/Qwen3-Embedding-8B,BAAI/bge-large-en-v1.5) were failing withUnknown embedding provider: deepinfrabecause the registry only included Nebius/OpenAI/Together/Fireworks/NVIDIA/etc. Now ships 8 popular DeepInfra embedding models out of the box and routes throughhttps://api.deepinfra.com/v1/openai/embeddings. (#2298) - fix(opencode-zen): flag
qwen3.6-plusandqwen3.6-plus-freewithtargetFormat: "claude". The opencode-zen upstream returns Claude-format SSE bodies (type: "message_start", nochoicesarray) for these Qwen3.6 models even when the request hits the OpenAI-compatible/chat/completionsendpoint, causing client-side Zod failures (expected "choices" (array), received undefined). Routing them through the Claude/messagesendpoint + translator fixes the format mismatch. (#2292) - fix(settings): default
debugModetotrueon fresh installations — the Debug sidebar section (Translator, Playground, Search Tools) was hidden on new installs becausedebugModewas not in the settings defaults object, makingdata?.debugMode === trueevaluate tofalse. The toggle in System & Storage appeared active but had no effect until manually set. Now all sidebar sections are visible out of the box. - fix(providers/command-code): send required
skillsandstreampayload fields — Command Code upstream wrapper now includesskills: ""and forcesparams.stream: trueto align with upstream API requirements. Validation probe defaults todeepseek/deepseek-v4-flash. (#2271 — thanks @ddarkr) - fix(sse): strip stale
Content-Encoding,Content-Length, andTransfer-Encodingfrom upstream responses — prevents JSON truncation andZlibErroron gzipped provider responses forwarded through the proxy. (#2291 — thanks @thepigdestroyer) - fix(sse): remove dead-code flag leak in
claudeCodeToolRemapper— eliminates a stale boolean flag that could cause incorrect tool remapping behavior on subsequent requests. (#2290 — thanks @thepigdestroyer) - fix(ui): v3.8.0 polish — connections border, sticky tabs, EN translations, save toasts, auto-combo catalog. (#2305 — thanks @mrmm)
- fix: remove implicit API key request caps — removes the default daily/weekly/monthly rate caps (1K/5K/20K) that silently applied 429s to API keys without explicit limits configured, causing unexpected throttling for operators who hadn't set custom rate policies. (#2289 — thanks @josephvoxone)
- fix(auth+build): Bearer manage scope on management routes + lazy-load deepseek PoW solver — unblocks MCP remote usage and Docker Next.js standalone builds. (#2308 — thanks @mrmm)
- fix(migrations): resolve version collision at migration slot 056 by renaming the quota thresholds migration to 057, and add batch deletion API with bulk cleanup support and batch/file management UI. (#2294 — thanks @hartmark)
- chore: ignore
.playwright-mcp/generated artifacts (CSP error logs, accessibility tree snapshots) — removes tracked test artifacts and adds the directory to.gitignore. (#2269 — thanks @backryun) - chore: tidy up deprecated models from Windsurf provider registry. (#2279 — thanks @backryun)
- build(deps): bump
actions/checkoutfrom 4 to 6 in CI workflows. (#2288) - build(deps): regenerate
package-lock.jsonto matchhttp-proxy-middleware4.x bump. (#2228 — thanks @NomenAK) - fix(streaming): harden stream readiness detection — recognize OpenAI Responses API lifecycle events (
response.created,response.in_progress,response.output_item.added) and Chat Completions start chunks as readiness signals; switch GLM from idle timeout to readiness timeout; compact Provider Limits cutoff UI with i18n fallback labels; fix DeepSeek PoW dynamic import warning; static locale for docs prerender. (#2317 — thanks @dhaern) - chore(providers): refresh provider model metadata, sort dashboard entries by display name, fix docs generator relative links and frontmatter. (#2318 — thanks @backryun)
- chore(providers): consolidate Alibaba provider entries — merge
alicode/alicode-intlinto sharedALIBABA_DASHSCOPE_MODELSarray, update 42 i18n llm.txt files. (#2319 — thanks @backryun) - chore: narrow
.claude/gitignore to runtime files only and untrackscheduled_tasks.lock. - Docs: 270 broken internal markdown links repaired.
🏆 v3.8.0 Hall of Fame — extended credits (post-release)
The following contributions landed after the initial v3.8.0 cut and supplement the 55+ community hall of fame below. Updated tallies:
| Contributor | New PRs in this cycle | Full v3.8.0 PR list |
|---|---|---|
| @oyi77 | #2338, #2339, #2340, #2344, #2364, #2366, #2369, #2377, #2380, #2383 | (+ already listed: #2010, #2014, #2041, #2052, #2061, #2074, #2091, #2094, #2096, #2131, #2135, #2240, #2283, #2295) |
| @backryun | #2269, #2279, #2313, #2318, #2319, #2381 | (+ already listed: #1992, #2033, #2088, #2123, #2138, #2141, #2150, #2177) |
| @thepigdestroyer | #2326, #2327, #2370 | (+ already listed: #2290, #2291) |
| @mrmm | #2375, #2286 (closes #2260), #2305, #2308, #2312 | (consolidates the row) |
| @dhaern | #2315, #2316, #2317, #2355 | (+ already listed: #2028, #2039, #2087, #2090) |
| @hartmark | #2294, #2299, #2309 | (+ already listed: #2045, #2137) |
| @gleber | #2264, #2265, #2266 | (+ already listed: #2103) |
| @herjarsa | #2349 | (+ already listed: #2030, #2136, #2152) |
| @congvc-dev | #2354, #2392 | (+ already listed: #2004) |
| @terence71-glitch | #2335, #2351, #2362 | (new contributor — 3 PRs) |
| @TF0rd | #2350 | (new contributor — 1 PR) |
| @slider23 | #2329, #2352 | (new contributor — 2 PRs) |
| @t-way666 | #2273 | (new contributor — 1 PR) |
| @payne0420 | #2267 | (+ already listed: #2082, #2128) |
| @Rikonorus | #2253, #2254 | (new contributor — 2 PRs) |
| @8mbe | #2251 | (new contributor — 1 PR) |
| @InkshadeWoods | #2250, #2261 | (+ already listed: #2202) |
| @clousky2020 | #2412 | (+ already listed: 15 PRs) |
| @benzntech | #2408 | (+ already listed: 8 PRs) |
Thanks also to @app/dependabot for keeping our dependency tree current via #2178, #2228, #2288, #2397, #2398, #2399.
Detalhes completos — features do release (2026-05-15)
- feat(providers): expanded capabilities for Pollinations, MiniMax, Together, and Replicate across Video, Audio TTS, and Transcription registries. (#2369 — thanks @oyi77)
- feat(providers): added Veo AI Free as a web-wrapper provider for generating video, image, and TTS without an API key. (#2366 — thanks @oyi77)
- feat(providers): added Replicate as a free provider for OpenAI-compatible inference with community models. (#2364 — thanks @oyi77)
feat(mcp): MCP accessibility-tree smart filter engine— collapses ≥30 repeated sibling lines, preserves[ref=eXX]anchors, 60-80% savings on browser snapshot outputs (Task 1)docs(skills): publish 10 SKILL.md manifests for external AI agents— zero-friction onboarding for Claude Desktop, ChatGPT, Cursor, Cline (Task 2)feat(cli): standalone system tray with PowerShell fallback on Windows— no Electron required;omniroute --tray; autostart via LaunchAgent/.desktop/registry (Task 3)feat(auth): CLI machine-ID HMAC-SHA256 token— zero-friction local auth without JWT/password; loopback-only; constant-time compare (Task 4)feat(security): route protection tiers— 5 tiers: public/read-only/protected/always/local-only; spawn-capable routes enforce loopback even with valid JWT (Task 5)feat(compression): Caveman SHARED_BOUNDARIES— all 6 languages × 3 intensities embed boundary clause;alreadyAppliedcheck order fixed (Task 6)feat(runtime): dynamic SQLite 5-step fallback chain— bundled → runtime-installed → lazy-install → node:sqlite → sql.js; magic-byte validation (ELF/Mach-O/PE) (Task 7)docs/ux: tier 1/2/3 marketing, onboarding tour, dashboard widget— README tier diagram,docs/marketing/TIERS.md, TierTour onboarding step, Tier Coverage widget (Task 8)docs(comparison): OMNIROUTE_VS_ALTERNATIVES.md— objective comparison vs LiteLLM, OpenRouter, Portkey
Changed
getDbInstance()requires priorensureDbInitialized()call — server startup awaits it automatically (see release notes for migration)- Caveman prompts embed
SHARED_BOUNDARIESverbatim (LITE/FULL/ULTRA × 6 languages) - README "Why OmniRoute?" enhanced with 3-tier ASCII diagram and comparison table
- Onboarding wizard gains "How It Works" tier tour step (after Welcome, before Security)
- Home dashboard shows "Tier coverage" widget (configured + active counts per tier)
Security
- Hard Rule #15: spawn-capable routes must call
assertRouteAllowed(req)(CLAUDE.md) - CLI token rejected on non-loopback hosts even when the HMAC is correct
always-protected routes (shutdown, db export) reject CLI tokens unconditionally
Documentation
docs/security/CLI_TOKEN.mddocs/security/ROUTE_GUARD_TIERS.mddocs/ops/SQLITE_RUNTIME.mddocs/marketing/TIERS.mddocs/comparison/OMNIROUTE_VS_ALTERNATIVES.mddocs/releases/v3.8.0.md
Dependencies
- chore(deps): node dependency updates — bump multiple runtime and dev dependencies to latest patch/minor versions. (#2259 — thanks @backryun)
Detalhes completos — features e fixes do lançamento (2026-05-06 a 2026-05-14)
✨ New Features
- feat(providers): add Command Code provider (#2199 — thanks @ddarkr)
- feat(providers): add ModelScope provider-specific 429 handling and retry logic (#2202 — thanks @InkshadeWoods)
- feat(providers): update Gemini CLI provider models catalog (#2196 — thanks @nickwizard)
- feat(antigravity): integrate Antigravity provider with dynamic
maxOutputTokenscalculation, identity fingerprinting overhaul, and Cloud Code envelope payload sanitization (#2055, #2063) - feat(gemini-cli): add custom projectId support for Gemini CLI transport (UI, DB, executor) (#1991)
- feat(providers): add KIE media provider support with dynamic polling, text models, and expanded video models catalog (#2009 — thanks @wauputr4)
- feat(providers): add Z.AI provider support with GLM quota handling and new quota labels — thanks @JxnLexn
- feat(providers): add 9 new free AI providers — LLM7, Lepton, Kluster, UncloseAI, BazaarLink, Completions, Enally, FreeTheAi (#2096 — thanks @oyi77)
- feat(providers): batch delete provider connections via checkbox multi-select (#2094 — thanks @oyi77)
- feat(cursor): full OpenAI parity — tool calls, streaming, and session management (#2082 — thanks @payne0420)
- feat(cursor): surface Cursor Pro plan usage on provider-limits dashboard (#2128 — thanks @payne0420)
- feat(cli): comprehensive CLI enhancement suite with 20+ new commands including
omniroute providers,omniroute combos,omniroute doctor(#2074 — thanks @oyi77) - feat(cli): add modular CLI setup and provider management commands (#2046 — thanks @wauputr4)
- feat(mcp): add DeepSeek quota and limit monitoring feature (#2089 — thanks @HoaPham98)
- feat(circuit-breaker): classify 429 errors and apply per-kind cooldowns (#2116 — thanks @eleata)
- feat(multi): manifest-aware tier routing — W1-W4 complete (#2014 — thanks @oyi77)
- feat(combos): add reset-aware routing strategy for quota-based providers — thanks @JxnLexn
- feat(combo): add context_length input field to combo edit form (#2047 — thanks @ddarkr)
- feat(combo): add
fallbackDelayMsto combo configuration and related settings — thanks @JxnLexn - feat(chat): dynamic tool limit detection with proactive truncation (#2061 — thanks @oyi77)
- feat(chat): add
STREAM_READINESS_TIMEOUT_MSand integrate into chat handling — thanks @JxnLexn - feat(chat): enhance error handling for semaphore capacity with fallback logic — thanks @JxnLexn
- feat(sse): refresh Claude OAuth wire image to claude-cli/2.1.131 (#2011 — thanks @Tentoxa)
- feat(github): add
targetFormat: openai-responsesto all GitHub models (#2122 — thanks @abhinavjnu) - feat(api): allow configuration via API calls — open management routes to Bearer keys with manage scope (#2103 — thanks @gleber)
- feat(api): update API bridge proxy timeout to 600,000ms (#2019 — thanks @JxnLexn)
- feat(api): aggregate combo model metadata in catalog endpoint —
buildComboCatalogMetadata()inlines contextLength, strategy, and target count for combo entries (#2166 — thanks @faisalill) - feat(usage): add service tier breakdown, codex fast service tier analytics, and account for fast tier — thanks @JxnLexn
- feat(qdrant): embedding model discovery (#2086 — thanks @rafacpti23)
- feat(auth): per-session sticky routing for Codex (#1887)
- feat(oauth): complete Windsurf and Devin CLI OAuth + API-token flows — WindsurfExecutor (gRPC-web/protobuf), DevinCliExecutor (ACP JSON-RPC 2.0 over stdio), model alias map, OAuth provider config (#2168 — thanks @Zhaba1337228)
- feat(inworld): enhance Inworld TTS support (#2123 — thanks @backryun)
- feat(kiro): headless auth via kiro-cli SQLite, image support, tool overflow handling, and model list sync (#2129 — thanks @christlau)
- feat(auto): zero-config auto-routing with
auto/prefix — dynamic virtual combo from connected providers with 6 variant profiles (coding, fast, cheap, offline, smart, lkgp), analytics tab, and settings UI (#2131 — thanks @oyi77) - feat(resilience): add model cooldowns dashboard card with real-time list, individual/bulk re-enable, and auto-refresh (#2146 — thanks @rafacpti23)
- feat(resilience):
useUpstream429BreakerHintstoggle — per-provider default policy for upstream 429 hint trust at the circuit-breaker cooldown layer with tri-state PATCH semantics (#2133 — thanks @eleata) - feat(search): add Ollama Search as a web search provider with registry integration and validation (#2176 — thanks @andrewmunsell)
- feat(search): add Z.AI Coding Plan Search via MCP protocol integration (#2238 — thanks @andrewmunsell)
- feat(debug): configurable chat log truncation limits via environment variables (
CHAT_LOG_TEXT_LIMIT,CHAT_LOG_ARRAY_TAIL_ITEMS,CHAT_LOG_MAX_DEPTH,CHAT_LOG_MAX_OBJECT_KEYS) andCHAT_DEBUG_FILEmode for untruncated JSON payloads (#2156 — thanks @bypanghu) - feat(responses): degrade
background: trueto synchronous execution with a warning instead of throwingunsupportedFeature(#2164 — thanks @Yosee11) - feat(mitm): dynamic Linux certificate path detection for multi-distro MITM cert trust (Debian, Arch/CachyOS, Fedora/RHEL, openSUSE) with NSS browser database injection (#2134 — thanks @flyingmongoose)
- feat(1proxy): add dedicated settings tab with proxy rotation support (#2135 — thanks @oyi77)
- feat(antigravity): support custom Google Cloud project ID for Antigravity provider (#2227 — thanks @nickwizard)
- feat(cli): CLI Integration Suite — 5 new management commands (
config,status,logs,update,provider), 3 API endpoints, config generators for 6 tools (Claude, Cline, Codex, Continue, KiloCode, OpenCode), zero-configauto/routing, and@omniroute/opencode-providernpm package (#2240 — thanks @oyi77)
🐛 Bug Fixes
- fix(pricing): make
getPricingForModelfully case-insensitive to ensure custom prices correctly reflect in new incoming requests cost calculations - fix(gemini): prevent
functionDeclarationsfrom being dropped by the sanitizer whengoogleSearchtool is present (#2077) - fix(pollinations): add
jsonMode: trueflag in the request transformation to enforce correct JSON structure from Pollinations API (#2109) - fix(docker): update Dockerfile to copy
/docsdirectory during build ensuring API catalog availability at runtime (#2083) - fix(docker): include OpenAPI spec in runtime image (#2007 — thanks @tatsster)
- fix(providers): strip OpenAI-specific fields in Kiro translator to prevent 400 errors (#2037)
- fix(kiro): normalize tool-use payloads to prevent 400 errors from agents (#2104 — thanks @rilham97)
- fix(kiro): merge adjacent user history turns after role normalization (#2105 — thanks @Gioxaa)
- fix(ui): resolve text contrast issues for zero-config warning banner in light mode (#2050)
- fix(core): inject global system prompt correctly into downstream chat completions pipeline (#2080)
- fix(core): restore Claude Code adaptive thinking defaults and resolve audio transcription CORS regression
- fix(routing): add missing v1beta rewrites to next.config to resolve 404 on Gemini models endpoint (#2102)
- fix(routing): fix bare GPT-5.5 routing for Codex-only installations (#2054 — thanks @guanbear)
- fix(routing): add fuzzy auto-combo routing for
auto/*model prefix (#2010 — thanks @oyi77) - fix(cache): optimize cache_control preservation logic and explicitly align tool schema with upstream Claude Code expectations
- fix(db): preserve legacy SQLite database path on Windows to prevent data loss (#1973)
- fix(db): reduce hot-path persistence overhead (#2039 — thanks @dhaern)
- fix(db): resolve migration conflict by renumbering overlapping migration entries (#2041 — thanks @oyi77)
- fix(settings): resolve model alias persistence double stringification preventing UI updates (#2018)
- fix(routing): dynamically filter bare model auto-resolution by active provider connections to prevent dead-routing (#2029)
- fix(embeddings): add Google Gemini embeddings compatibility via OpenAI-compatible endpoint mapping (#2006)
- fix(sse): prevent Claude OAuth multi-account correlation via metadata.user_id (#2053 — thanks @Tentoxa)
- fix(sse): prevent Claude Code identity cloak overrides and fix fallback resilience (#2053 — thanks @Tentoxa)
- fix(sse): classify hour quota errors as QUOTA_EXHAUSTED (#2119 — thanks @clousky2020)
- fix(sse): fix CC-compatible streaming bridge (#2118 — thanks @rdself)
- fix(antigravity): sanitize Claude Cloud Code payloads (#2090 — thanks @dhaern)
- fix(antigravity): add duplex half for streaming bodies — thanks @Gi99lin
- fix(antigravity): align identity protocol and behavior with official AM — thanks @Gi99lin
- fix(chatgpt-web): plumb proxy through to native tls-client (#2022, #2023 — thanks @xssdem)
- fix(codex): expose native model IDs in catalog (#2012 — thanks @Tr0sT)
- fix(glm): add dedicated coding transport (#2087 — thanks @dhaern)
- fix(compression): support Responses input and expand Spanish compression rules (#2028 — thanks @dhaern)
- fix(catalog): auto-calculate combo context_length from target model limits (#2030 — thanks @herjarsa)
- fix(api): fix usage analytics and API key identity (#2008, #2092 — thanks @AveryanAlex, @yoviarpauzi)
- fix(api-key): allow Unicode letters in API key name validation (#1996 — thanks @rodrigogbbr-stack)
- fix(auth): allow bootstrap without password (#2048 — thanks @tces1)
- fix(proxy): clean up proxy page redundancy and fix 1proxy sync empty body error (#2052 — thanks @oyi77)
- fix(dashboard): resolve Unknown plan display in Provider Limits — thanks @congvc-dev
- fix(usage): add extensible CURRENCY_SYMBOLS mapping for deepseek currencies
- fix(runtime): harden timer handling and model pricing fallback
- fix(i18n): complete Simplified Chinese translations (#2115 — thanks @boa-z)
- fix(mitm): add Linux cert install and skip sudo password when root (#1999 — thanks @NekoMonci12)
- fix(mitm): prevent stub from loading at runtime via bypass module — thanks @NekoMonci12
- fix: remove Anthropic-Beta header from non-Anthropic providers to fix identity contamination (#1989)
- fix(cli): resolve .env loading failure for global npm installations
- fix(authz): classify
/dashboard/onboardingas PUBLIC to unblock setup wizard (#2127) - fix(chatcore): stop leaking provider credentials in response headers
- fix(analytics): precise SQL matching for
auto/prefix models - fix(export): exclude telemetry/usage-history tables from JSON config backups by default to prevent unbounded file growth (#2125)
- fix(translator): preserve
body.systemin openai→claude translator when Claude Code sends native Anthropic system array through /chat/completions — fixes v3.7.9 regression where system prompt was silently dropped, triggering Anthropic 429 (#2130) - fix(sanitizer): preserve
reasoning_contenton assistant messages withtool_callsorfunction_call— fixes Kimi and other thinking-enabled providers returning 400 errors when reasoning_content was incorrectly stripped (#2140 — thanks @DavyMassoneto) - fix(catalog): ensure individual (non-combo) models expose
context_lengthviagetTokenLimit()fallback chain — prevents OpenCode and other clients from falling back to conservative ~4000 token limit (#2136 — thanks @herjarsa) - fix(docker): remove docs directory from
.dockerignoreso API catalog documentation is available at runtime inside containers (#2137, #2120 — thanks @hartmark) - fix(types): systematic
anytype elimination across 8 core files —antigravity.ts,accountFallback.ts,usage.ts,geminiHelper.ts,error.ts,apiKeys.ts,settings.ts,logger.ts(#2137 — thanks @hartmark) - fix(providers): restore cloud agent provider exports and logger import (#2138 — thanks @backryun)
- fix(providers): remove duplicate
CLOUD_AGENT_PROVIDERSdeclaration, move Kiro dash→dot Claude model aliases toPROVIDER_MODEL_ALIASES, and trim deprecated Kiro registry entries (#2141 — thanks @backryun) - fix: Follow OpenAI specification, handle throttling in batch and fix UI (#2045)
- fix(cliproxyapi): probe
/v1/modelsfor health when CPA 6.x has no/healthendpoint (#2189 — thanks @Brkic-Nikola) - fix(cliproxyapi): detect Anthropic-shaped request bodies and route to
/v1/messages, strip Capy extras, and round-tripmcp_*tool name rewrites toMcp_*(#2165 — thanks @Brkic-Nikola) - fix(cliproxyapi): detect Anthropic shape on minimal Capy bodies (#2192 — thanks @Brkic-Nikola)
- fix(stream): skip
[DONE]terminator for Claude SSE clients (#2190 — thanks @Brkic-Nikola) - fix(claudeHelper): emit
datafield onredacted_thinking, drop bogus signature (#2191 — thanks @Brkic-Nikola) - fix(modelSpecs): cap thinking budget for Claude Opus 4.6 / 4.7 / Sonnet 4.6 (#2197 — thanks @Brkic-Nikola)
- fix(reasoning-cache): include xiaomi-mimo in replay provider/model detection (#2198 — thanks @Brkic-Nikola)
- fix(kiro): synthesize minimal tools schema when
body.toolsis omitted but message history containstool_calls, preventing 400 errors from Claude Code and OpenCode (#2149 — thanks @Gioxaa) - fix(kiro): avoid treating high-traffic 429s as quota exhaustion — use
classify429FromErrorto prevent premature account deactivation (#2153 — thanks @Gioxaa) - fix(responses): propagate
includearray (e.g.reasoning.encrypted_content) during Chat→Responses API translation, fixing broken thinking panel in Codex/OpenCode (#2154 — thanks @Gioxaa) - fix(responses): emit reasoning summary as
delta.reasoning_content(flat) instead ofdelta.reasoning.summary(nested) for Chat Completions client compatibility (#2159 — thanks @Gioxaa) - fix(cloudflare): add state file write serialization lock to prevent race conditions in
cloudflaredTunnel.ts(#2156 — thanks @bypanghu) - fix(providers): allow optional-key providers to pass connection test (#2169 — thanks @andrewmunsell)
- fix(providers): correct pollinations requests and provider dashboard state
- fix(providers): fix Azure AI Foundry provider connection handling (#2236 — thanks @one-vs)
- fix(providers/command-code): fix validation request format for Command Code API (#2243 — thanks @ddarkr)
- fix(antigravity): strip
generationConfig.thinkingConfigfor Claude models routed through Antigravity to prevent upstream errors (#2217 — thanks @NomenAK) - fix(antigravity): bootstrap project via
loadCodeAssist+fetchAvailableModelsfallback for robust startup (#2219 — thanks @NomenAK) - fix(rateLimit): never
.stop()during runtime reset, evict cache instead to prevent stale rate-limit state (#2218 — thanks @NomenAK) - fix(ModelSync): shared loopback readiness gate + IPv4 force to prevent model sync failures on dual-stack hosts (#2221 — thanks @NomenAK)
- fix(proxyFetch): retry once on undici dispatcher failure before native fallback (#2222 — thanks @NomenAK)
- fix(model): local aliases override cross-proxy provider inference to prevent incorrect model resolution (#2223 — thanks @NomenAK)
- fix(claudeHelper): preserve latest assistant thinking blocks verbatim to prevent Anthropic HTTP 400 errors (#2224 — thanks @NomenAK)
- fix(deepseek): preserve
reasoning_contentthrough full pipeline for DeepSeek V4 models — prevents reasoning context loss on multi-turn conversations (#2231 — thanks @kang-heewon) - fix(sse-heartbeat): shape-aware keepalives keep streams alive through stricter proxies (#2233 — thanks @NomenAK)
- fix(translator): coerce
submit_pr_reviewfunctionalChanges/findingsto arrays to prevent upstream schema errors (#2242 — thanks @NomenAK) - fix(api): validate model cooldown delete payload
- fix(ci): run coverage gate serially, align resilience and thinking checks, align cloud code thinking and model catalog tests
🔒 Security
- fix(security): remediate CodeQL vulnerabilities (ReDoS, cryptographic bias, stack trace exposure, and weak password hashing) (#216, #215, #211, #208, #206, #210)
- fix(security): sanitize error messages in API routes to prevent stack trace exposure (CodeQL js/stack-trace-exposure) (#2209)
- fix(security): remediate regex validation backtracking path in core compression cleanup (#1990)
- fix(core): harden input handling and stabilization for prompt compression edge cases
📝 Documentation
- docs: add competitive marketing tables and SEO/AEO optimizations to README (#2091)
- docs: refresh providers, model catalogs, and docs for v3.8.0 (#2088)
- docs: update Claude MD and update GLM-CN max context to 200k (#2027)
- docs(env): add
GITLAB_DUO_OAUTH_CLIENT_IDto.env.example(#2031) - docs: add Brazilian WhatsApp group link to README (#2201 — thanks @rafacpti23)
🔧 Improvements
- refactor(executor):
sanitizeReasoningEffortForProvider()hook inBaseExecutor.execute()— downgradesxhigh→highfor unsupporting providers, strips effort for mistral/devstral and github claude models (#2162 — thanks @hachimed) - refactor(translator): remove redundant provider guard from Claude thinking placeholder injection — applies to all
targetFormat === FORMATS.CLAUDEbodies (#2161 — thanks @JohnDoe-oss) - refactor(catalog): remove 11
.tsextension imports, eliminate allas anycasts, addCustomModelEntryinterface andComboModelSteptype predicate, normalize alias resolution withresolveCanonicalProviderId()(#2152 — thanks @herjarsa) - feat(resilience):
useUpstream429BreakerHintstri-state PATCH field —true/falsepersists,nullresets to undefined (omitted from JSON) (#2146 tests — thanks @rafacpti23)
🧹 Chores & Maintenance
- chore(providers): prune redundant local provider icon assets in favor of
@lobehub/iconsweb fonts (#1992) - chore(providers): remove deprecated models (#2033)
- chore(providers): improve BazaarLink and Completions.me support (#2177 — thanks @backryun)
- chore(registry): refresh
contextLengthandmaxOutputTokensfor claude, kiro, github, kimi-coding, xiaomi-mimo, codex/gpt-5.5 models (#2163 — thanks @brucevoin) - chore(models): tidy up Alibaba Coding Plan base URL, reorganize Cursor model list by family, fix
gpt-4omodel ID, update OpenCode Zen model (#2150 — thanks @backryun) - chore(deps): resolve npm audit moderate vulnerability (hono)
- chore(deps): move
gray-matterfrom devDependencies to dependencies (runtime requirement) (#2156 — thanks @bypanghu) - deps: bump
fast-urifrom 3.1.0 to 3.1.2 (#2078) - deps: bump
honofrom 4.12.14 to 4.12.18 (#2065, #2079) - deps: bump the development group with 6 updates (#2184)
- deps: bump
electron-builderfrom 26.9.1 to 26.10.0 (#2183) - ci: update build-fork workflow to build from main branch (#2055)
- ci: skip SonarCloud scan on main pushes to optimize CI time
- test: stabilize cooldown abort coverage case in integration testing
- build(deps): regenerate
package-lock.jsonto matchhttp-proxy-middleware4.x bump (#2228 — thanks @NomenAK) - fix(requestLogger): exempt tools field from array truncation for full debug visibility (#2234 — thanks @NomenAK)
🏆 v3.8.0 Community Contributors
Thank you to all 55+ community contributors who made v3.8.0 possible! 🎉
| Contributor | PRs | Contributions |
|---|---|---|
| @NomenAK | 12 | #2217, #2218, #2219, #2221, #2222, #2223, #2224, #2228, #2233, #2234, #2242, #2192 |
| @oyi77 | 14 | #2010, #2014, #2041, #2052, #2061, #2074, #2091, #2094, #2096, #2131, #2135, #2240, #2283, #2295 |
| @backryun | 9 | #1992, #2033, #2088, #2123, #2138, #2141, #2150, #2177, #2279 |
| @Brkic-Nikola | 6 | #2165, #2189, #2190, #2191, #2192, #2197 |
| @Gioxaa | 5 | #2105, #2149, #2153, #2154, #2159 |
| @dhaern | 4 | #2028, #2039, #2087, #2090 |
| @andrewmunsell | 3 | #2169, #2176, #2238 |
| @ddarkr | 4 | #2047, #2199, #2243, #2271 |
| @nickwizard | 3 | #1991, #2196, #2227 |
| @herjarsa | 3 | #2030, #2136, #2152 |
| @rafacpti23 | 3 | #2086, #2146, #2201 |
| @Tentoxa | 2 | #2011, #2053 |
| @wauputr4 | 2 | #2009, #2046 |
| @hartmark | 4 | #2045, #2137, #2294, #2299 |
| @payne0420 | 2 | #2082, #2128 |
| @bypanghu | 2 | #2027, #2156 |
| @eleata | 2 | #2116, #2133 |
| @Tr0sT | 1 | #2012 |
| @AveryanAlex | 1 | #2008 |
| @rodrigogbbr-stack | 1 | #1996 |
| @NekoMonci12 | 1 | #1999 |
| @congvc-dev | 1 | #2004 |
| @tatsster | 1 | #2007 |
| @xssdem | 1 | #2023 |
| @wucm667 | 1 | #2031 |
| @tces1 | 1 | #2048 |
| @guanbear | 1 | #2054 |
| @Gi99lin | 1 | #2055 |
| @ivan-mezentsev | 1 | #2063 |
| @JxnLexn | 1 | #2019 |
| @yoviarpauzi | 1 | #2092 |
| @gleber | 1 | #2103 |
| @rilham97 | 1 | #2104 |
| @boa-z | 1 | #2115 |
| @rdself | 1 | #2118 |
| @clousky2020 | 1 | #2119 |
| @abhinavjnu | 1 | #2122 |
| @HoaPham98 | 1 | #2089 |
| @christlau | 1 | #2129 |
| @flyingmongoose | 1 | #2134 |
| @05dunski | 1 | #1978 (cherry-picked) |
| @DavyMassoneto | 1 | #2140 |
| @Zhaba1337228 | 1 | #2168 |
| @faisalill | 1 | #2166 |
| @Yosee11 | 1 | #2164 |
| @hachimed | 1 | #2162 |
| @JohnDoe-oss | 1 | #2161 |
| @brucevoin | 1 | #2163 |
| @InkshadeWoods | 1 | #2202 |
| @kang-heewon | 1 | #2231 |
| @one-vs | 1 | #2236 |
| @thepigdestroyer | 2 | #2290, #2291 |
| @josephvoxone | 1 | #2289 |
| @mrmm | 3 | #2286, #2305, #2308 |
[3.7.9] — 2026-05-03
✨ New Features
-
feat(docs): integrate multi-page documentation into OmniRoute dashboard (#1969)
-
feat(settings): add request body limit setting (#1968)
-
feat(auth): add Gemini CLI OAuth client secret default (#1974)
-
feat(models): expose models.dev context windows in /v1/models (#1972)
-
feat(compression): major upgrade to Caveman and RTK compression pipelines (#1876, #1889):
- Add RTK tool-output compression, stacked Caveman + RTK pipelines, compression combo assignments, dashboard context pages, MCP management tools, and language-aware Caveman rule packs.
- Expand RTK parity with a 39-filter catalog, RTK-style JSON DSL stages, inline verify/benchmark coverage, trust-gated custom filters, expanded command detection, and redacted raw-output recovery.
- Expose rule intensities, track USD savings, unify config validation, and persist MCP savings.
- Expand Caveman parity and MCP metadata compression.
-
feat(provider): update Jina AI model catalog to support Embeddings and Rerank natively (#1874 — thanks @backryun)
-
feat(provider): add NanoGPT image generation provider (#1899 — thanks @Aculeasis)
-
feat(ui): move proxy configuration to dedicated System → Proxy page (#1907 — thanks @oyi77)
-
feat(ui): add K/M/B/T cost shortener utility (#1902 — thanks @oyi77)
-
feat(providers): implement bulk paste for extra API keys (#1916 — thanks @0xtbug)
-
feat(analytics): usage history API key backfill + dark mode pricing (#1896 — thanks @Gi99lin)
-
feat(logs): show RTK and Caveman compression token savings accurately in request log UI (#1923 — thanks @emdash)
-
feat(routing): auto-skip exhausted quota accounts (Issue #1952)
-
feat(docs): docs site overhaul (#1976 — thanks @oyi77)
-
feat(db): consolidate all database settings into SystemStorageTab (closes #1935) (#1947 — thanks @oyi77)
-
feat(sse): codex 429 mid-task failover with account rotation (#1888 — thanks @smartenok-ops)
-
feat(auto-assessment): add auto-assessment engine for combo self-healing (#1918 — thanks @oyi77)
-
feat(usage): DeepSeek V4 native cache token extraction (#1930 — thanks @smartenok-ops)
-
feat(cost): enhance cost formatting and add Codex GPT-5.5 pricing support (#1944 — thanks @JxnLexn)
🐛 Bug Fixes
- fix(auth): implement session affinity sticky routing logic
- fix(dashboard): derive display base URL from origin instead of hardcoding localhost (#1960 — thanks @jeanfbrito)
- fix(proxy): use credentials.connectionId instead of non-existent credentials.id for image proxy resolution (#1929 — thanks @Aculeasis)
- fix(routing): codex bare-name disambiguation + family-native fallback (#1933 — thanks @smartenok-ops)
- fix(infrastructure): move wreq-js to optionalDependencies and add Node 25/26 to secure runtime policy (#1924)
- fix(providers): resolve ChatGPT Web authentication failure by aligning TLS fingerprint User-Agent strings (#1925)
- fix(mitm): support root user for MITM sudo handling (#1948 — thanks @NekoMonci12)
- fix(db): resolve legacy encryption fallback causing re-encryption loops (#1941, #1945)
- fix(auth): fix Codex assistant final_answer response sanitization (#1965)
- fix(mcp): reclassify MCP endpoints to ensure API key authentication works even when dashboard auth is enabled (#1970)
- fix(providers): allow local OpenAI-compatible endpoints (like Ollama) to be added without an API key (fixes #1893)
- fix(providers): bypass AgentRouter unauthorized_client_error by spoofing Claude CLI headers via Anthropic endpoints (fixes #1921)
- fix(copilot): emit compatible reasoning text deltas (#1919 — thanks @ivan-mezentsev)
- fix(api-manager): show validation errors inline in modals, not behind (#1920 — thanks @andrewmunsell)
- fix(compression): align seeded standard savings combo with stacked default, preserve stacked defaults, and secure metadata routes.
- fix(gemini-cli): separate Cloud Code transport from Antigravity (#1869 — thanks @dhaern)
- fix(codex): map prompt field to input array for Cursor compatibility (fixes #1872)
- fix(core): align stream parameter default to false per strict OpenAI spec (fixes #1873)
- fix(ui): restore Next.js CSP
unsafe-evalin productionscript-srcto fix unresponsive Onboarding button (fixes #1883) - fix(proxy): globally strip
prompt_cache_retentioninBaseExecutorto prevent upstream 400 errors from strict endpoints like droid/gemini-2-pro (fixes #1884) - fix(ui): include
isOpendependency inEditConnectionModalstate sync to ensuremaxConcurrentis properly hydrated when reopening the modal (fixes #1859) - fix(security): remediate 4 polynomial-redos CodeQL alerts in compression regexes by bounding repetitions and removing overlapping quantifiers
- fix(codex): flatten Chat Completions tool format to Codex Responses format in
normalizeCodexTools— preventsMissing required parameter: tools[0].nameupstream errors (#1914 — thanks @tranduykhanh030) - fix(proxy): add proxy-aware execution context to image generation route — proxy settings are now correctly applied for image providers behind restricted networks (#1904 — thanks @Aculeasis)
- fix(translator): inject
properties: {}into zero-argument MCP tool schemas during Anthropic→OpenAI translation — prevents 400 errors from OpenAI strict schema validation (#1898 — thanks @bryceIT) - fix(codex): sanitize raw responses input (#1895 — thanks @dhaern)
- fix(combos): align strategy contracts (#1892 — thanks @dhaern)
- fix(combos): fix combo provider breaker profile handling (#1891 — thanks @rdself)
- fix(migrations): duplicate-column no-op fix (#1886 — thanks @smartenok-ops)
- fix(auth): per-connection OAuth refresh mutex (#1885 — thanks @smartenok-ops)
- fix(auth): require dashboard management auth for compression preview
🔄 Updates
- chore(provider): Add reka models list (#1956 — thanks @backryun)
- chore(model): Update new models, Delete Deprecated models (#1949 — thanks @backryun)
📝 Documentation
- docs(compression): document RTK+Caveman stacked savings ranges
🏆 Release Attribution & Retroactive Credits
- @payne0420 (PR #1828 / #1839) — Implementation of the Rate Limit Watchdog and environment overrides. (This feature was manually backported to v3.7.8, causing the automatic GitHub Release notes to omit the author's credit).
[3.7.8] — 2026-05-01
✨ New Features
-
feat(docs): integrate multi-page documentation into OmniRoute dashboard (#1969)
-
feat(settings): add request body limit setting (#1968)
-
feat(auth): add Gemini CLI OAuth client secret default (#1974)
-
feat(models): expose models.dev context windows in /v1/models (#1972)
-
fix(db): resolve legacy encryption fallback causing re-encryption loops (#1941)
-
fix(auth): fix Codex assistant final_answer response sanitization (#1965)
-
feat(providers): add Grok 4.3 and Xiaomi Mimo TTS provider (#1837)
-
feat(core): implement Rate Limit Watchdog with environment override capability to detect and reset stalled queues (#1839)
-
feat(providers): add muse-spark-web provider with multiple models and reasoning support (#1843)
-
feat(1proxy): integrate 1proxy free proxy marketplace with dashboard management and new MCP tools (closes #1788) (#1847)
🐛 Bug Fixes
- fix(codex): sanitize Responses replay state to prevent internal assistant commentary from leaking (#1868 — thanks @dhaern)
- fix(cli): add capture-backed Gemini CLI fingerprint (#1866)
- fix(ui): hide combo compression controls when the global setting is disabled (#1840)
- fix(db): tolerate missing request_detail_logs table for legacy deployments (#1848)
- fix(core): remove unneeded `store` payload parameter for providers lacking support (closes #1841)
- fix(core): ensure safeOutboundFetch and A2A routers return 503 Service Unavailable when security guardrails are triggered
- fix(usage): correct Unix seconds vs milliseconds parsing logic for Kiro AI quota reset (closes #1849)
- fix(ui): apply robust NaN handling, ensure 24h consistency, and fix missing hour slots in Compression Analytics (closes #1844)
- fix(ui): implement short number formatting for token consumption metrics on cache pages to prevent overflow (closes #1842)
- fix(combo): stabilize provider routing at 500+ connections by bounding semaphore queues and adjusting circuit breaker tracking (closes #1846) (#1854)
- fix(maritalk): update Maritalk model list, use Authorization Key header, and align with latest API endpoints (#1856)
- fix(grok-web): stabilize tool calling (bash, readFile, webSearch) and response parsing by mapping native Grok intents to standard OpenAI payloads (#1857)
- fix(providers): correctly map and expose the Upstage embedding and chat model catalogs (#1855)
- fix(executor): apply proper urlSuffix and custom authHeaders for unknown registry-based providers in DefaultExecutor (closes #1846) (#1861)
🛠️ Maintenance
- fix(workflow): build docker images on version tags (#1838)
[3.7.7] — 2026-04-30
✨ New Features
-
feat(docs): integrate multi-page documentation into OmniRoute dashboard (#1969)
-
feat(settings): add request body limit setting (#1968)
-
feat(auth): add Gemini CLI OAuth client secret default (#1974)
-
feat(models): expose models.dev context windows in /v1/models (#1972)
-
fix(db): resolve legacy encryption fallback causing re-encryption loops (#1941)
-
fix(auth): fix Codex assistant final_answer response sanitization (#1965)
-
Prompt Compression Pipeline: Implemented a multi-phase prompt compression engine including
lite(whitespace/duplication collapse),aggressive(summarization, tool compression), andultramodes (heuristic pruning and SLM stub) (#1633, #1738, #1739, #1741) -
Compression Dashboard & Analytics: Added a compression settings UI, real-time log viewer, pipeline statistics tracking, and interactive playground preview (#1756)
-
Compression Caching & MCP: Added caching-aware strategy adjustments to the compression pipeline, alongside new MCP tools for status and configuration (#1758)
-
Analytics Custom Filters: Added custom date range selection, API key filtering, and NULL key analytics backfilling to the Costs Dashboard (#1830)
🐛 Bug Fixes
- Combo Routing: Fixed an issue where Gemini
-previewmodels were incorrectly normalized to their canonical names, causing 404 errors during combo routing (#1834) - Codex Native Passthrough: Added support for Cursor 5.5 sending
messagesarrays to theresponses/compactendpoint, preventing upstream rejections with empty requests (#1832) - Rate-limit Watchdog: Implemented a new rate-limit watchdog with environment override capabilities and Stage Tracing to prevent and diagnose silent wedges (#1828)
- Encryption Resiliency: Prevent sending encrypted tokens to providers by returning null on decryption failure (#763d353)
- i18n & Locales: Fixed OpenCode baseUrl locale placeholders and added compression keys across 32 languages
- Startup Stability: Hardened resilience integration server startup logic (#9aa89b17)
🛠️ Maintenance
- Tests & Docs: Expanded the test suite with 61 unit/integration tests for the compression pipeline and updated
AGENTS.md - Workflow: Fixed the changelog extraction logic to accurately capture GitHub release descriptions
[3.7.6] — 2026-04-30
✨ New Features
-
feat(docs): integrate multi-page documentation into OmniRoute dashboard (#1969)
-
feat(settings): add request body limit setting (#1968)
-
feat(auth): add Gemini CLI OAuth client secret default (#1974)
-
feat(models): expose models.dev context windows in /v1/models (#1972)
-
fix(db): resolve legacy encryption fallback causing re-encryption loops (#1941)
-
fix(auth): fix Codex assistant final_answer response sanitization (#1965)
-
feat(api-keys): add rename support in the permissions modal — editable key name field with validation (#1796)
-
feat(chatgpt-web): support
thinking_effortparameter (Standard/Extended) for thinking-capable models (#1821) -
feat(dashboard): implement remaining v3.7.6 dashboard features — Costs overview, Translator pipeline, and Endpoint tabs improvements
-
feat(tools): inject fallback tool names to prevent upstream 400 errors on providers that require tool names (#1775)
-
feat(db): auto-restore probe-failed database on startup to prevent data loss after failed upgrades (#1810)
-
feat(analytics): add cost-based usage insights and activity streaks in the analytics dashboard
🔒 Security
- fix(security): resolve ReDoS vulnerability in Codex executor regex patterns (#1797, #1789)
🐛 Bug Fixes
- fix(stability): resolve codex input validation, enable combo circuit breaker, and fix broken unit tests (#1804, #1805)
- fix(stability): safely cast inputs to strings before calling
.trim()to avoid crashes on numeric fields in proxy modal (#1825) - fix(stability): clear active requests and recover providers after connection failures (#1824)
- fix(xiaomi-mimo): update models to V2.5, fix Token Plan validation and default region (#1823)
- fix(codex): omit compact client metadata to prevent upstream rejections (#1822)
- fix(dashboard): fix endpoint visibility, A2A status display, and API catalog consistency (#1806)
- fix(analytics): use pure SQL aggregations — no history rows loaded into memory (#1802)
- fix(dashboard): correct
loadPresetsReferenceError in CostOverviewTab - fix(mitm): enforce transparent interception on port 443 only
🧹 Chores
- chore(workflow): mandate implementation plan generation in
/resolve-issuesworkflow before coding - chore(release): expand contributor credits to 155 PRs across full project history
🏆 Community Contributors Acknowledgment
We identified that 155 community PRs across the entire project history (from inception through v3.7.5) were manually integrated into release branches but closed instead of properly merged through GitHub, preventing contributors from receiving merge credit on their profiles. We sincerely apologize for this oversight and have since updated our workflows to ensure this never happens again.
The following contributors had their code and ideas integrated across multiple releases without proper merge credit. Thank you for your invaluable contributions to OmniRoute:
| Contributor | PRs (Total) | All Contributions |
|---|---|---|
| @rdself | 28 | #542, #705, #717, #737, #738, #841, #851, #853, #875, #880, #888, #891, #903, #904, #974, #1069, #1089, #1196, #1267, #1272, #1299, #1300, #1356, #1357, #1441, #1443, #1549, #1742 |
| @oyi77 | 27 | #644, #672, #700, #850, #859, #862, #868, #874, #881, #883, #908, #926, #931, #983, #990, #1019, #1020, #1021, #1103, #1281, #1286, #1363, #1368, #1377, #1411, #1689, #1717 |
| @clousky2020 | 15 | #1244, #1323, #1365, #1366, #1408, #1442, #1484, #1595, #1598, #1599, #1611, #1618, #1620, #1621, #1644 |
| @benzntech | 8 | #158, #1264, #1435, #1436, #1437, #1440, #1444, #1677 |
| @kang-heewon | 5 | #530, #854, #884, #1235, #1574 |
| @herjarsa | 4 | #1472, #1474, #1477, #1480 |
| @backryun | 4 | #1358, #1609, #1627, #1722 |
| @tombii | 4 | #708, #856, #900, #1013 |
| @christopher-s | 3 | #868, #885, #992 |
| @zen0bit | 3 | #561, #650, #912 |
| @k0valik | 3 | #554, #587, #596 |
| @zhangqiang8vip | 2 | #470, #575 |
| @wlfonseca | 2 | #997, #1016 |
| @RaviTharuma | 2 | #1188, #1277 |
| @prakersh | 2 | #419, #480 |
| @payne0420 | 2 | #1593, #1670 |
| @only4copilot | 2 | #855, #1039 |
| @jay77721 | 2 | #581, #582 |
| @hijak | 2 | #295, #578 |
| @hartmark | 2 | #1494, #1500 |
| @defhouse | 2 | #906, #946 |
| @xiaoge1688 | 1 | #1304 |
| @xandr0s | 1 | #1376 |
| @willbnu | 1 | #882 |
| @slewis3600 | 1 | #1624 |
| @sergey-v9 | 1 | #594 |
| @razllivan | 1 | #987 |
| @nmime | 1 | #1271 |
| @Moutia-Ben-Yahia | 1 | #1663 |
| @Mind-Dragon | 1 | #467 |
| @mercs2910 | 1 | #1001 |
| @MAINER4IK | 1 | #196 |
| @luandiasrj | 1 | #996 |
| @knopki | 1 | #1434 |
| @kfiramar | 1 | #389 |
| @ken2190 | 1 | #166 |
| @keith8496 | 1 | #569 |
| @jonesfernandess | 1 | #1118 |
| @JasonLandbridge | 1 | #1626 |
| @i1hwan | 1 | #1386 |
| @Gorchakov-Pressure | 1 | #754 |
| @foxy1402 | 1 | #934 |
| @dt418 | 1 | #896 |
| @dhaern | 1 | #1647 |
| @DavyMassoneto | 1 | #211 |
| @dail45 | 1 | #1413 |
| @congvc-dev | 1 | #1569 |
| @be0hhh | 1 | #1581 |
| @andruwa13 | 1 | #1457 |
| @AndrewDragonIV | 1 | #898 |
| @AndersonFirmino | 1 | #362 |
| @alexsvdk | 1 | #1280 |
| @abhinavjnu | 1 | #550 |
[3.7.5] — 2026-04-29
✨ New Features
-
feat(docs): integrate multi-page documentation into OmniRoute dashboard (#1969)
-
feat(settings): add request body limit setting (#1968)
-
feat(auth): add Gemini CLI OAuth client secret default (#1974)
-
feat(models): expose models.dev context windows in /v1/models (#1972)
-
fix(db): resolve legacy encryption fallback causing re-encryption loops (#1941)
-
fix(auth): fix Codex assistant final_answer response sanitization (#1965)
-
feat(tunnels): integrate native ngrok tunnel support with dashboard UI parity (#1753)
🐛 Bug Fixes
- fix(dashboard): add manual 'Clear All' button to terminate stalled long-running requests in Active Requests panel (#1799)
- fix(schema): remove empty string values from optional tool parameters to prevent upstream validation errors (#1674)
- fix(providers): ensure proper streaming cleanup and semaphore release to prevent stalls with nanoGPT (#1781)
- fix(db): wrap quota_snapshots access in try/catch to gracefully handle pending database migrations (#1784)
- feat(providers): add support for glm-cn (BigModel) provider (#1770)
- fix(grok-web): fix Grok validator and cookie parsing (#1793)
- fix(antigravity): scrub internal OmniRoute headers (#1794)
- fix(chatgpt-web): restore validator + expand model catalog to ChatGPT Plus tier (#1792)
- fix(codex): stabilize Copilot responses replay state (#1791)
- fix(antigravity): cap Claude bridge output tokens (#1785)
- fix(schema): strip
defaultproperties from tool-call JSON schemas during egress to prevent injection errors (#1782) - fix(db): add
quota_snapshotstable to core DB schema initialization to prevent startup failures on fresh installs - fix(models): apply blocked providers filter to non-chat catalog models (image, embedding, audio, etc.) (#1752)
- fix(antigravity): stabilize streaming payload parsing and deduplicate usage/model metadata refreshes (#1748)
- fix(antigravity): normalize Gemini bridge payloads — sanitize tool names, cap output tokens, and fix thinking budget (#1769)
- fix(sse): propagate AbortSignal to pre-fetch semaphore and rate-limit awaits to prevent memory leaks (#1771)
- fix(models): fix model sync import handling — separate synced models from custom models to prevent data loss (#1755)
- fix(codex): improve VS Code Copilot /responses reasoning and tool follow-ups (#1750)
- fix(memory): resolve build issues and implement memory UPSERT logic to prevent duplicate entries (#1763)
- fix(kiro): support organization IDC OAuth with regional endpoints and refresh (#1754)
- fix(combo): include 429 in provider circuit breaker to stop infinite retry loops on exhausted quotas (#1767)
- fix(claude): respect client-set thinking/effort params — only inject adaptive thinking and high effort when the client hasn't explicitly set them, preventing forced quota drain on Claude Max accounts (#1761)
- fix(blackbox-web): correct cookie name and populate session/subscription fields (#1776)
- fix(codex): align client identity metadata (#1778)
- fix(claude): fix support for claude-cli using Gemini provider (#1779)
- test(reasoning-cache): isolate DB state using mkdtempSync to prevent 401 middleware errors
🛠️ Maintenance
- chore(docs): add MseeP.ai security assessment badge to README (#1727)
- chore(xiaomi): update Xiaomi provider model list (#1759)
- chore(db): move DB health endpoint to management API (#1757)
- chore(ui): speed up endpoint initial render with background task loading (#1760)
- chore(workflows): add strict PR contributor credit policy to prevent future merge credit loss
[3.7.4] — 2026-04-28
✨ New Features
-
feat(docs): integrate multi-page documentation into OmniRoute dashboard (#1969)
-
feat(settings): add request body limit setting (#1968)
-
feat(auth): add Gemini CLI OAuth client secret default (#1974)
-
feat(models): expose models.dev context windows in /v1/models (#1972)
-
fix(db): resolve legacy encryption fallback causing re-encryption loops (#1941)
-
fix(auth): fix Codex assistant final_answer response sanitization (#1965)
-
feat(ui): add endpoint tunnel visibility settings (#1743)
-
feat(cli): refresh CLI fingerprint provider profiles (#1746)
-
feat(proxy): implement bulk proxy import via pipe-delimited parser with update-or-create (upsert) logic and real-time preview table
-
feat(pwa): add fullscreen installable PWA with manifest, service worker, and cross-platform app icons (#1728)
🔒 Security
- security: replace insecure
Math.randomwithcrypto.getRandomValuesfor fallback UUID generation to resolve CodeQL CWE-338 finding (#182)
🐛 Bug Fixes
-
fix(cc-compatible): fix CC-compatible relay format and UI copy (#1742)
-
fix(codex): normalize max reasoning effort for Codex routing (#1744)
-
fix(claude-code): fix Claude Code gateway config helper (#1745)
-
fix(db): reconcile legacy
create_reasoning_cachemigration tracking to prevent version shadowing on032and resolve startup warnings (#1734) -
fix(db): intercept
007migration to use idempotentIF NOT EXISTSlogic viaPRAGMA table_info, preventing syntax crashes on fresh installs (#1733) -
fix(cc-compatible): preserve Claude Code system skeleton to prevent request rejection by strict compatible upstream providers (#1740)
-
fix(providers): add API key validation for image-only providers and fix Stability AI requests to use
multipart/form-datainstead of JSON (#1726) -
fix(codex): preserve
previous_response_idandconversation_idfields when input array is empty to prevent schema validation errors (#1729) -
fix(searxng): bypass UI validation block when
apiKeyOptionalis true and fix typing errors in provider dashboard to allow saving search providers without credentials (#1721) -
fix(proxy): disable HTTP keep-alive and pipelining in Undici proxy dispatcher to prevent "Socket hang up" rotation failures
-
stream: correctly identify
thoughtanderrorblocks in Antigravity/Gemini SSE streams to prevent premature 502 timeouts (#1725, #1705)
🛠️ Maintenance
- workflow: add phase 4 release monitoring instructions to
/generate-releaseworkflow - test: fix typescript compilation errors in unit tests to keep CI typecheck pipeline fully green
- test: update responses store expectations for empty input arrays
[3.7.3] — 2026-04-28
🐛 Bug Fixes
- fix(claude): strip existing billing headers from system array before injecting to prevent Anthropic prompt cache misses — stacked
x-anthropic-billing-headerblocks invalidated prefix matching, causing ~100% cache_create instead of cache_read (#1712) - fix(claude): strip
output_config.formatfor non-Anthropic Claude-compatible providers during passthrough — third-party Claude endpoints (MiniMax, DeepSeek via aggregators) reject structured output fields with 400 errors (#1719) - fix(combo): set terminal error state on response quality validation failure — prevents misleading
ALL_ACCOUNTS_INACTIVE503 when the real issue is response quality validation (#1707, #1710) - fix(combo): treat combo fallback as target-level orchestration — all non-ok responses (including generic 400s) now fall through to the next target instead of being terminal; removes complex bad-request allowlist regex (#1713)
- fix(codex): restore namespace MCP tools and hosted-tool whitelist — regression from #1581 that silently dropped all MCP tool groups and Responses-API hosted tools (#1715)
- fix(codex): add neutral instructions for bare chat requests — Codex Responses backend rejects requests without
instructions, making Codex unusable for normal chat (#1709) - fix(proxy): wrap proxy assignment queries in try-catch for missing
proxy_assignmentstable — Electron installs where migration 004 hasn't run no longer crash withno such tableerror (#1706) - fix(migration): improve Windows file URL path resolution in migration runner — adds direct URL path extraction and
process.cwd()fallback for CI-built bundles with leaked build-time paths (#1704) - fix(ui): fix light mode active request payload modal — add missing
--color-cardtheme token, use opaquebg-surfaceinstead of translucentbg-card/70, add backdrop blur (#1714)
🔄 Updates
- chore(image-models): refresh image generation model registry — replace stale FLUX aliases with FLUX Kontext / FLUX.2 mappings, remove deprecated FLUX Redux/Depth/Canny variants (#1722)
[3.7.2] — 2026-04-28
✨ New Features
-
feat(docs): integrate multi-page documentation into OmniRoute dashboard (#1969)
-
feat(settings): add request body limit setting (#1968)
-
feat(auth): add Gemini CLI OAuth client secret default (#1974)
-
feat(models): expose models.dev context windows in /v1/models (#1972)
-
fix(db): resolve legacy encryption fallback causing re-encryption loops (#1941)
-
fix(auth): fix Codex assistant final_answer response sanitization (#1965)
-
feat(authz): introduce centralized proxy-based authz pipeline and lifecycle policy (#1632)
-
feat(logs): configure call log pipeline artifacts (#1650)
-
feat(network): add guarded remote image fetch utility
-
feat(codex): enable native Codex websocket responses on beta-gated models (#1658)
-
feat(muse-spark-web): continue the same meta.ai conversation across turns (#1673)
🐛 Bug Fixes
- fix(responses): sanitize empty string placeholders from tool-call optional arguments in stream delta accumulation to avoid breaking strict clients (#1674)
- fix(codex): prevent unexpected protocol leakage and fabricated instructions on bare chat completion requests without tools (#1686)
- fix(executors): truncate tools array to 128 items max in GitHub Copilot and OpenCode executors to mitigate 400 Bad Request errors from upstream (#1687)
- fix: add body-read timeout to prevent stuck pending requests (#1680)
- fix(rate-limit): replace unsupported Bottleneck
maxWaitoption with job-levelexpirationto prevent indefinite queue stalls (#1694) - fix(sse): sanitize OpenAI tool schemas for strict upstream validators — strips null from enum arrays, normalizes tuple items, filters invalid required keys (#1692)
- fix(stream): fail zombie SSE streams before accepting response — returns 504 instead of hanging indefinitely, enables combo fallback (#1693)
- fix(combo): complete context truncation hotfix — cache getCombos() with 10s TTL, pass allCombosData to resolveComboTargets() for nested combo resolution, consolidate duplicated context overflow regex patterns (#1685)
- fix(codex): raise default quota threshold from 90% to 99% to avoid premature account blocking when usable quota remains (#1697)
- fix(memory): use
userrole for GLM/ZAI/Qianfan providers — providers with strict role constraints (nosystemrole) now correctly receive memory context as ausermessage instead of asystemmessage, preventing 422 validation errors (#1701) - fix(oauth): target specific connection by ID on re-auth token exchange — prevents duplicate account creation when re-authenticating an existing OAuth connection (#1702 — thanks @namhhitvn)
- feat(email-privacy): integrate email visibility toggle in RequestLoggerV2 — log detail modal now respects global email privacy state, hiding email addresses by default (#1700 — thanks @namhhitvn)
- fix(combo): trigger fallback on Anthropic
Invalid signature in thinking blockerrors instead of returning 400 directly (#1696) - fix: combo retry loop stops immediately on client disconnect (499) (#1681)
- fix(search): support optional bearer auth for SearXNG (#1683)
- fix(vision): respect native GPT vision support — prevents VisionBridge from intercepting models that already handle images natively (#1678)
- fix(qwen): use
security.authformat instead ofmodelProvidersfor Qwen Code config generation (#1677) - fix(codex): remove stale websocket transport lookup that caused fallback errors (#1676)
- fix(chatgpt-web): bound tls-client native deadlocks so requests never hang forever (#1664)
- fix(codex): default gpt-5.5 to HTTP transport instead of WebSocket (#1660)
- fix(codex): [urgent] fix gpt-5.5 websocket transport and model labels (#1656)
- fix(grokweb): update Request and Response Specifications (#1655)
- fix(blackbox-web): set isPremium flag to true to enable premium model access (#1661)
- fix(core): avoid OpenAI stream options for Anthropic-compatible providers (#1654)
- fix(electron): resolve MCP server start failure on Windows (#1662)
- fix(electron): make Windows smoke test non-blocking (continue-on-error), pre-create userData dir for Windows + stream logs in CI, and add --no-sandbox and sandbox env for CI smoke tests
- fix(codex): fix
getWreqWebsocketReferenceError causing 502 on all Codex requests (#1652, #1653) - fix(codex): default
storetofalse— Codex OAuth backend rejectsstore=true(#1635) - fix(db): add post-migration guards for missing
batchestable andcombos.sort_ordercolumn on DB upgrades (#1648, #1657) - fix(db): renumber duplicate migration
032to prevent collision - fix(perplexity-web): update API version and user-agent to match upstream requirements (#1666)
- fix(docker): copy SQLite migration files and explicitly trace in standalone build (#1665)
- fix(muse-spark-web): update to Meta's Ecto-era persisted query — fixes 502
Unknown type "RewriteOptionsInput"after Meta retired the Abra mutation (#1668) - fix(dev): enable Turbopack by default and repair Codex CORS headers (#1669)
- fix(authz): restore
REQUIRE_API_KEYsupport in clientApi policy - fix(auth): align fallback API key format with test setup
🛠️ Maintenance
- build(prepublish): make Next.js build bundler configurable (webpack/turbopack)
- ci: align sonar analysis scope
- ci: stabilize release branch checks
- ci: remove expired advanced security scans job
🧪 Tests
- test: fix TypeScript configuration errors in plan3-p0.test.ts
- test: fix implicit any types across test suites
- test: disable type checking in flaky unit tests
- test: fix failing tests due to recent refactors
- fix(tests): align integration tests with authz pipeline refactor
- fix(tests): align test assertions with v3.7.2 source code changes
- fix(tests): CORS test now checks object body instead of entire file
- fix(e2e): fix E2E flakiness and implicit any type errors
[3.7.1] — 2026-04-26
✨ New Features
-
feat(docs): integrate multi-page documentation into OmniRoute dashboard (#1969)
-
feat(settings): add request body limit setting (#1968)
-
feat(auth): add Gemini CLI OAuth client secret default (#1974)
-
feat(models): expose models.dev context windows in /v1/models (#1972)
-
fix(db): resolve legacy encryption fallback causing re-encryption loops (#1941)
-
fix(auth): fix Codex assistant final_answer response sanitization (#1965)
-
feat(providers): Add GPT-5.5 support to the Codex provider — includes 1.05M context window, tool calling, vision, and reasoning capabilities with proper pricing entries across
cxandopenaiproviders. RefactorssplitCodexReasoningSuffix()into a shared helper for cleaner effort-level parsing (#1617 — thanks @Zhaba1337228). -
feat(cli): Add
omniroute reset-encrypted-columnsrecovery command — nulls encrypted credential columns (api_key,access_token,refresh_token,id_token) inprovider_connectionswhile preserving provider metadata, giving users affected by #1622 a clean recovery path without losing configurations. -
feat(i18n): Expand locale coverage with nine new language packs (Bengali, Farsi, Gujarati, Indonesian, Marathi, Swahili, Tamil, Telugu, Urdu), bringing total language support from 32 to 41 locales.
🐛 Bug Fixes
- fix(rate-limit): Add per-model rate limiting for GitHub Copilot provider — a 429 on one model (e.g.
gpt-5.1-codex-max) no longer locks the entire connection, matching the existing Gemini per-model quota pattern (#1624 — thanks @slewis3600). - fix(cli-tools): Preserve existing OpenCode configuration (MCP servers, custom providers, comments) when saving OmniRoute settings — uses
jsonc-parserfor tree-preserving edits instead of destructive JSON roundtrip. Fix API key clipboard copy to use raw keys instead of masked placeholders. Add theme-aware OpenCode light/dark SVG logos (#1626 — thanks @JasonLandbridge). - fix(cli-tools): Fix OpenCode guide step 3
{{baseUrl}}double-brace placeholder to use ICU-style{baseUrl}across all 41 locales, restoring next-intl interpolation (#1626). - fix(codex): Make
wreq-jsnative module import lazy and optional to prevent server crash on startup when the platform-specific binary is missing — affects pnpm installs, Docker Alpine, macOS ARM, and Windows (#1612, #1613, #1616). - fix(i18n): Add 14 missing translation keys (
logs.runningRequests,logs.model,logs.provider,logs.account,logs.elapsed,logs.count,logs.payloads, etc.) for the Active Requests panel across all locales. Replace 83 placeholder values in usage/evals namespace. Add 5 missing health namespace keys for rate limit status. - fix(encryption): Prevent
STORAGE_ENCRYPTION_KEYfrom being silently regenerated duringnpm install -gupgrades, which made all previously-encrypted provider credentials permanently unrecoverable due to AES-GCM auth-tag mismatch (#1622). - fix(startup): Add decrypt-probe diagnostic at server bootstrap — if
STORAGE_ENCRYPTION_KEYdoesn't match encrypted credentials in the database, a prominent warning is logged directing users to restore the key or use the new recovery command. - fix(cli-tools): Allow
nullAPI key values incliModelConfigSchemato prevent 400 Bad Request errors when saving cloud-based CLI tool configurations. Fix error handling across all 10 ToolCard components to safely extract messages from structured error objects, preventing React Error #31 crashes. - fix(docker): Set
NPM_CONFIG_LEGACY_PEER_DEPS=truein the Docker builder layer beforenpm ciand remove duplicatepostinstallSupport.mjsCOPY instruction — fixes container image build failures introduced in v3.7.0 (#1630 — thanks @rdself). - fix(antigravity): Hide deprecated Gemini-routed Claude 4.5 models from public catalogs and model lists. Legacy
gemini-claude-*aliases now silently resolve to current Claude 4.6 equivalents. Replace dynamic reverse-alias generation with an explicit allowlist for predictable model visibility (#1631 — thanks @backryun). - fix(types): Add explicit type annotations to sync-env test helpers and dynamic import casts to satisfy
typecheck:noimplicit:coreCI gate. - fix(reasoning): Implement Reasoning Replay Cache — hybrid memory/SQLite persistence for
reasoning_contentin multi-turn tool-calling flows. Automatically captures reasoning from DeepSeek V4, Kimi K2, Qwen-Thinking, and GLM models and re-injects it on follow-up turns to prevent HTTP 400 errors from strict reasoning-content validation. Includes dashboard telemetry tab, REST API, and 21 unit tests (#1628 — thanks @JasonLandbridge). - fix(postinstall): Extend postinstall native module repair to cover
wreq-js— detects missing platform-specific.nodebinaries insideapp/node_modules/wreq-js/rust/and copies them from the root install. Fixes globalpnpminstalls on macOS arm64 where the standalone app directory only contained Linux binaries (#1634 — thanks @MarcosT96). - fix(migration): Prevent compat-renamed migration slots from shadowing new migrations at the same version number. After rewriting
028_provider_connection_max_concurrent→029, the runner now verifies the old version slot is clear, ensuring028_create_files_and_batchesruns on v3.6.x → v3.7.x upgrades. Addsbatchestable as a physical schema sentinel for upgrade recovery (#1637 — thanks @V8-Software). - fix(registry): Route GitHub Copilot GPT 5.4/5.5 models through the Responses API (
targetFormat: "openai-responses"). Fixesgpt-5.4-miniandgpt-5.4being rejected on/chat/completionsby GitHub (#1641 — thanks @dhaern). - fix(usage): Correct MiniMax token plan quota display — the newer
/v1/token_plan/remainsendpoint reports used counts, not remaining counts. Rounds floating-point percentage artifacts in Provider Limits UI (#1642 — thanks @CruxExperts). - fix(codex): Lazy-load
wreq-jsWebSocket transport viacreateRequireinstead of top-level import. Server boots cleanly when native module is unavailable and returns 503 only when Codex WebSocket is actually requested. Fixes #1612 (#1640 — thanks @dendyadinirwana). - fix(electron): Package Electron runtime dependencies into
resources/app/node_modules/via separateextraResourcesFileSet. Adds cross-platform packaged app smoke test script and CI integration to prevent future regressions. Closes #1636 (#1639 — thanks @prateek). - feat(account-fallback): Add model-level daily quota lockout. When a provider returns 429 with
quota_exhausted, cooldown is set to tomorrow 00:00 instead of exponential backoff. Detects daily quota patterns viaisDailyQuotaExhausted()in chat handler (#1644 — thanks @clousky2020). - fix(codex): Use per-conversation
session_id/conversation_idfrom client body asprompt_cache_keyinstead of account-wideworkspaceId. The official Codex CLI usesconversation_id(a unique UUID per session); using the sharedworkspaceIdcapped cache hit-rate at ~49%. Includes 10 unit tests (#1643). - fix(claude): Stabilize billing header fingerprint to prevent Anthropic prompt-cache prefix invalidation. The fingerprint was derived from the first user message text, which changes every turn, mutating
system[]and forcing ~100%cache_create. Now uses a stable per-day hash, preserving ~96%cache_readhit rate (#1638). - fix(transport): Harden GitHub and Kiro streaming — thread
clientHeadersthroughBaseExecutor.buildHeaders()to eliminate mutable singleton state race condition on concurrent requests. Remove redundant[DONE]stripping TransformStream from GitHub executor. Add defensiveparseToolInput()for malformed Kiro tool call arguments. HoistTextEncoder/TextDecoderto module singletons and use zero-copysubarray()(#1645 — thanks @dhaern). - fix(transport): Prevent memory bloat and database exhaustion from large, fragmented streaming responses. Implemented
ByteQueueinkiro.tsfor zero-copy binary accumulation, refactoredantigravity.tsfor incremental SSE parsing, and enforced a strict 512KB tiered truncation limit (MAX_CALL_LOG_ARTIFACT_BYTES) on stream request logs and call artifacts (#1647). - chore(ci): Update build environment dependencies — bump Node to
24.15.0,actions/checkout@v6,docker/build-push-action@v7, pinactions/setup-pythonto major tag (#1646 — thanks @backryun).
📝 Documentation
- docs(env): Add
OMNIROUTE_ALLOW_PRIVATE_PROVIDER_URLSto.env.examplewith documentation for LM Studio and other local provider use cases (#1623).
[3.7.0] — 2026-04-26
✨ New Features
-
feat(docs): integrate multi-page documentation into OmniRoute dashboard (#1969)
-
feat(settings): add request body limit setting (#1968)
-
feat(auth): add Gemini CLI OAuth client secret default (#1974)
-
feat(models): expose models.dev context windows in /v1/models (#1972)
-
fix(db): resolve legacy encryption fallback causing re-encryption loops (#1941)
-
fix(auth): fix Codex assistant final_answer response sanitization (#1965)
-
feat(providers): Implement Image Generation and Editing capabilities for ChatGPT Web, including in-band chat image generation and caching (#1606).
-
feat(ui): Integrate OpenCode Zen/Go API tool logo SVG and polish API key copy-to-clipboard interactions (#1607).
-
feat(providers): Add CrofAI as a built-in API-key provider with quota/usage monitoring wired into the dashboard Limits page (#1604, #1606).
-
feat(skills): Add workspace-scoped built-in skills (
file_read,file_write,http_request,eval_code,execute_command) with real sandbox execution via Docker, replacing stub responses. Browser skills now fail explicitly when runtime is not configured. -
feat(providers): Integrate AgentRouter as a new OpenAI-compatible passthrough provider with $200 free credits via sign-up (Issue #1572).
-
feat(ui): Implement on-demand per-model testing in the provider dashboard, allowing single-token diagnostic checks without triggering rate-limits (Issue #1532).
-
feat(provider): add ChatGPT Web (Plus/Pro) session provider (#1593)
-
feat(provider): add Baidu Qianfan chat provider (#1582)
-
feat(codex): support GPT-5.5 responses websocket (#1573)
-
feat(sse): Codex CLI image_generation + DALL-E-style image route (#1544)
-
feat(dashboard): Complete the reconciled v3.7.0 dashboard task set: MCP cache tools and count, video endpoint visibility, provider taxonomy, upstream proxy visibility, provider count badges, costs overview, eval suite management, Custom CLI builder, ACP-focused Agents copy, Translator stream transformer, logs convergence, learned rate-limit health cards, docs expansion, and active request payload inspection.
-
feat(mcp): Register
omniroute_cache_statsandomniroute_cache_flushacross MCP schemas, server registration, handlers, docs, and tests. -
feat(providers): Complete the v3.7.0 provider onboarding wave with self-hosted/local providers (
lm-studio,vllm,lemonade,llamafile,triton,docker-model-runner,xinference,oobabooga), OpenAI-compatible gateways (glhf,cablyai,thebai,fenayai,empower,poe), enterprise providers (datarobot,azure-openai,azure-ai,bedrock,watsonx,oci,sap), specialty providers (clarifai,modal,reka,nous-research,nlpcloud,petals,vertex-partner),amazon-q, GitLab/GitLab Duo, and Chutes.ai. -
feat(providers): Add Cloudflare Workers AI integration and UI support for robust backend execution.
-
feat(telemetry): Implement proactive public IP capture from client headers (
x-forwarded-for,x-real-ip, etc.) withinsafeLogEventsfor accurate database observability. -
feat(audio): Add AWS Polly as an audio speech provider with SigV4 request signing, static engine catalog, provider validation, managed-provider UI coverage, and sanitization for AWS secret/session fields.
-
feat(search): Add You.com search provider support with dashboard discovery, validation, livecrawl option handling, and search handler normalization.
-
feat(video): Add RunwayML task-based video generation support, task polling, provider catalog metadata, validation, and dashboard/model-list coverage.
-
feat(providers): Add search functionality to the providers dashboard with i18n support. (#1511 — thanks @th-ch)
-
feat(providers): Register 6 new models in the opencode-go provider catalog. (#1510 — thanks @kang-heewon)
-
feat(providers): Add ModelScope provider (Chinese AI marketplace) with Kimi K2.5, GLM-5, and Step-3.5-Flash integration. (#1430 — thanks @clousky2020)
-
feat(providers): Add LM Studio as an OpenAI-compatible local provider for self-hosted model inference.
-
feat(providers): Add Grok 4.3 thinking model support for xAI web executor requests.
-
feat(core): Implement provider-level Circuit Breaker to prevent cascading failures across connections, enforcing a 10-minute cooldown after 5 consecutive transient failures. (#1430)
-
feat(core): Add daily quota exhaustion lock to detect "quota exceeded" signals and lock the specific model until midnight. (#1430)
-
feat(core): Auto-inject
stream_options.include_usage = truefor OpenAI format streams to guarantee token usage is reported correctly during streaming. (#1423) -
feat(core): Add OpenAI Batch Processing API support — submit, monitor, and manage batch jobs through the proxy with full lifecycle tracking.
-
feat(vision-bridge): Add automatic image description fallback for non-vision models via
VisionBridgeGuardrail(priority 5). Intercepts image-bearing requests to non-vision models, extracts descriptions via a configurable vision model (default: gpt-4o-mini), and replaces images with text before forwarding. Fails open on any error. (#1476) -
feat(dashboard): Introduce real-time model status badges with countdown timers in the provider detail and combo panel interfaces. (#1430)
-
feat(dashboard): Add Batch/File management data grid with full i18n translations for batch processing workflows. (#1479)
-
feat(usage): MiniMax + MiniMax-CN quota tracking in provider limits dashboard. (#1516)
-
feat(providers): Fix OpenRouter remote discovery and unify managed model sync. (#1521)
-
feat(providers): Implement provider and account-level concurrency cap enforcement (
maxConcurrent) using robust semaphore mechanisms. (#1524) -
feat(core): Implement Hermes CLI config generation and message content stripping. (#1475)
-
feat(combos): Add expert combo configuration mode for advanced routing controls. (#1547)
-
feat(providers): Register Codex auto review and expand icon coverage.
-
feat(tunnels): Add Tailscale tunnel management routes and runtime helpers for install, login, daemon start, enable/disable, and health checks.
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
fix(chatgpt-web): Fix empty-file race in
tlsFetchStreamingwherewaitForFileaccepted zero-byte files, silently degrading streaming requests to buffered mode. Replaced withwaitForContentrequiringfile.size > 0with early exit on request settlement. (#1597 — thanks @trader-payne) -
fix(chatgpt-web): Fix stale NextAuth session-token cookies surviving rotation shape changes (unchunked↔chunked).
mergeRefreshedCookienow drops all session-token family members viaSESSION_TOKEN_FAMILY_REbefore appending the refreshed set, preventing auth failures from dual cookie submission. (#1597 — thanks @trader-payne) -
fix(codex): WebSocket memory retention and weekly limit handling (#1581)
-
fix(providers): Default models list logic (#1577)
-
fix(ui): Dashboard endpoint URL hydration respects
NEXT_PUBLIC_BASE_URLwhen behind a reverse proxy (#1579) -
fix(providers): Restore strict PascalCase header masquerading for Claude Code to resolve HTTP 429 upstream errors (#1556)
-
fix(sse): make Responses passthrough robust for size-sensitive clients (#1580)
-
fix(codex): update client version for gpt-5.5 (#1578)
-
fix(vision-bridge): force GPT-family image fallback (#1571)
-
fix(claude): skip adaptive thinking defaults for unsupported models (#1563)
-
fix(claude): preserve tool_result adjacency in native and CC-compatible paths (#1555)
-
fix(reasoning): Preserve OpenAI Chat Completions
reasoning_effortthrough assistant-prefill requests and label OpenAI request protocols explicitly asOpenAI-ChatorOpenAI-Responses. (#1550) -
fix(codex): Fix Codex auto-review model routing so review traffic resolves to the intended configured model. (#1551)
-
fix(resilience): Route HTTP 429 cooldowns through runtime settings so cooldown behavior follows the configured resilience profile. (#1548)
-
fix(providers): Normalize Anthropic header keys to lowercase in the provider registry to avoid duplicate or case-variant upstream headers. (#1527)
-
fix(providers): Preserve audio, embedding, rerank, image, video, and OpenAI-compatible alias metadata when
/v1/modelsmerges static and discovered catalogs. -
fix(providers): Discover Azure OpenAI deployments from resource endpoints using
api-keyauth and configurable API versions. -
fix(providers): Keep local OpenAI-style providers authless when no API key is configured, including the Lemonade Server default endpoint.
-
fix(translator): Preserve Antigravity default system instructions and caller-provided system prompts as separate Gemini
systemInstructionparts instead of concatenating them. -
fix(security): Sanitize provider-specific AWS secrets and session tokens from provider management API responses.
-
fix(release): Resolve combo prefixing, Electron packaging, CLI auth, and release-branch integration regressions. (#1471, #1492, #1496, #1497, #1486)
-
fix(providers): Resolve 400 errors for GLM and Antigravity Claude adapter during request translation by scoping prompt caching to compatible Anthropic endpoints and flattening system instructions. (#1514, #1520, #1522)
-
fix(core): Strip
reasoning_contentfrom OpenAI format messages for non-reasoning models to prevent upstream HTTP 400 validation errors. (#1505) -
fix(sse): Map Claude
output_config/thinkingto OpenAIreasoning_effortfor proper Antigravity tool translation. (#1528) -
fix(combo): Fallback to next model on all-accounts-rate-limited (HTTP 503/429) to maintain high availability. (#1523)
-
fix(api): Harden batch and file endpoints for auth and recovery to prevent schema state collisions.
-
fix(ui): Add missing UI wiring for "Add Memory" and "Import" buttons on the
/dashboard/memorypage. (#1506) -
fix(ui): Prevent Dark Mode FOUC (Flash of Unstyled Content) by injecting a synchronous theme initialization script into the root
layout.tsx. -
fix(ui): Fix mobile layout text overflow in provider and combo cards, and enable touch-friendly reordering arrows across all combo strategies.
-
fix(core): Add periodic runtime log rotation checks to prevent disk exhaustion in long-running instances. (#1504 — thanks @ether-btc)
-
fix(build): Resolve missing
processmodule in webpack client build for pino-abstract-transport. (#1509 — thanks @hartmark) -
fix(ui): Add dark mode support for native dropdown
<option>elements on Linux/Windows, resolving invisible text in settings and combo builders (#1488) -
fix(batch): Add batch item dispatching to specific handlers based on URL to support embeddings and other modalities (#1495 — thanks @hartmark)
-
fix(dashboard): Correct TOML round-trip corruption in Codex config serializer by dequoting keys and preserving array/boolean structures properly. (#1438 — thanks @benzntech)
-
fix(security): Resolve CodeQL alert 164 (ReDoS in extraction) and 163 (incomplete URL sanitization). (#163, #164)
-
fix(providers): Add optional chaining to connection object before accessing
providerSpecificData, preventing runtime errors when the connection is null/undefined. -
fix(codex): Preserve namespace MCP tools forwarded to Codex Responses API, preventing tool name stripping during translation. (#1483)
-
fix(codex): Deduplicate case-variant
anthropic-versionheader in Claude Code patch to prevent duplicate header injection. (#1481) -
fix(fallback): Use shared
CircuitBreakerinstead of undefined constants, fixing runtime errors in provider failure handling. (#1485) -
fix(fallback): Merge new provider failure threshold fields (
providerFailureThreshold,providerFailureWindowMs,providerCooldownMs) into resilience profiles. -
fix(fallback): Remove 429 from
PROVIDER_FAILURE_ERROR_CODES— rate limits are already handled by model-level and account-level locks; including them in the provider-wide circuit breaker caused premature cooldown. -
fix(sse): Enable tool calling for GPT OSS and DeepSeek Reasoner models. (#1455)
-
fix(encryption): Return null on decryption failure to prevent sending encrypted tokens to providers. (#1462)
-
fix(combo): Resolve cross-provider thinking 400 errors and HTTP clipboard issues during combo routing. (#1444)
-
fix(core): Resolve skills, memory, and encryption system issues affecting startup and runtime stability. (#1456)
-
fix(core): Fix model ID parsing for providers with slashes in model names — use
indexOf/substringinstead ofsplitto handle models likemodelscope/moonshotai/Kimi-K2.5. -
fix(core): Fix reference counting in
ModelStatusContext— changedregisteredModelsfromSettoMap<string, number>to prevent polling stop when one component unmounts while others still track the same model. -
fix(security): Prompt injection guard failures now return an explicit 500 response instead of silently passing through (fail-closed policy).
-
fix(security): Encryption now derives new keys from a secret-based salt while falling back to the legacy static-salt key during decryption, preserving existing stored credentials.
-
fix(combo): Resolve context truncation bug in combo routing to prevent incomplete execution states. (#1517)
-
fix(compression): Implement bidirectional tool_pair cleaning for anthropic inputs (fixes #1592).
-
fix: Resolve v3.7.0 stabilization issues including dashboard navigation routing, ProxyRegistryManager component layout, and models API response merging (#1566, #1560, #1559).
-
fix(cli): Preserve TOML integer/boolean types in Codex config round-trip to prevent
tui.model_availability_nuxvalidation errors. -
fix(tailscale): Support sudo auth prompts and live daemon socket detection for non-root tunnel management.
-
fix(dashboard): Stabilize usage tab loading and refresh behavior to prevent empty state flashes.
-
fix(i18n): Translate 519 untranslated pt-BR keys and add missing Windsurf/Cline/Kimi docs keys.
-
fix(i18n): Add missing dashboard message keys across all 30 locales.
-
fix(cli): Align OpenCode config preview and add multi-model selection (#1602).
-
fix(security): Harden management API auth and OpenAPI try-proxy endpoint.
-
fix(security): Resolve vulnerability scan findings for auth-guarded routes.
♻️ Refactoring
- refactor(fallback): Make provider failure thresholds configurable via
PROVIDER_PROFILESinstead of hardcoded constants, supporting different failure tolerance per provider type. (#1449) - refactor(resilience): Unify resilience controls across the codebase for consistent circuit breaker and fallback behavior. (#1449)
- refactor(core): Implement shared path utilities, add custom date formatting, improve type safety, and unify database imports across modules.
- refactor(security): Harden backup archive creation by switching to
execFileSync, validate ACP agent IDs, expand shared CORS handling. - refactor(release): Remove obsolete agent workflow playbooks and the stale compiled
src/lib/dataPaths.jsartifact. (#1541)
🧪 Tests
- test(providers): Add targeted coverage for AWS Polly SigV4 speech/validation, Azure OpenAI deployment discovery, Lemonade local discovery, provider dashboard taxonomy, managed provider catalog behavior, and merged
/v1/modelsalias metadata. - test(catalog): Add v3.7.0 catalog coverage for Pollinations text models, Perplexity Sonar via Puter, and NVIDIA free-model alias resolution.
- test(vision-bridge): Add 51 unit tests covering all VisionBridge spec scenarios (VB-S01 through VB-S10), including helper functions for
callVisionModel,extractImageParts,replaceImageParts, andresolveImageAsDataUri. - test(batch-api): Isolate batch API unit tests with temp
DATA_DIRto prevent schema state collisions. - test(settings-api): Add test harness with
createSettingsApiHarnessfunction for proper temp directory setup and storage reset between tests. - test(security): Update prompt injection test for fail-closed policy alignment.
- test(core): Restore local test fixes for encryption and resilience modules.
- test(next): Align transpile package expectations for the Next.js standalone build.
- test(ci): Fix CI-only test failures from environment differences — clear
INITIAL_PASSWORDandJWT_SECRETin integration tests, handleXDG_CONFIG_HOMEfor guide-settings tests.
📚 Documentation
- docs: Update the root changelog with all release-branch changes through 2026-04-24, including PRs #1544, #1555, #1551, #1550, #1548, #1547, #1541, #1538, #1536, and #1527.
- docs: Fix broken README and localized documentation links. (#1536)
- docs: Add dashboard docs coverage for current API endpoints, management APIs, ACP, MCP tools, provider onboarding, and v3.7.0 task reconciliation.
- docs: Add Arch Linux AUR install notes for community package support. (#1478)
- docs(i18n): Improve Ukrainian (uk-UA) translation quality — full Ukrainian translation for README, SECURITY, A2A-SERVER, API_REFERENCE, AUTO-COMBO, and USER_GUIDE documents. Fix mixed Latin/Cyrillic typos, translate model table entries, and standardize section headers.
🛠️ Maintenance
- chore: Add
.tmp/to.gitignoreto keep local build/test artifacts out of release diffs. (#1538) - chore(release): Clarify release version parity and changelog segregation rules for generated release workflows.
📦 Dependencies
- deps: Bump the development group with 4 updates. (#1464)
- deps: Bump the production group with 4 updates. (#1463)
- deps: Update
@lobehub/iconsto5.5.4, add explicitreact-is@19.2.5for Recharts, pin npm installs to skip unused peer auto-installs, and override Electron's transitive@xmldom/xmldomto0.9.10so audit findings stay closed.
[3.6.9] — 2026-04-19
✨ New Features
-
feat(docs): integrate multi-page documentation into OmniRoute dashboard (#1969)
-
feat(settings): add request body limit setting (#1968)
-
feat(auth): add Gemini CLI OAuth client secret default (#1974)
-
feat(models): expose models.dev context windows in /v1/models (#1972)
-
fix(db): resolve legacy encryption fallback causing re-encryption loops (#1941)
-
fix(auth): fix Codex assistant final_answer response sanitization (#1965)
-
feat(providers): Implement Image Generation and Editing capabilities for ChatGPT Web, including in-band chat image generation and caching (#1606).
-
feat(ui): Integrate OpenCode Zen/Go API tool logo SVG and polish API key copy-to-clipboard interactions (#1607).
-
feat(providers): Mark Qwen OAuth provider as deprecated following the upstream free tier shutdown on 2026-04-15. Adds deprecation warning to CLI tool UI and rewrites
saveQwenConfigto inject OmniRoute as a multi-provider (openai, anthropic, gemini) via.qwen/settings.jsonand.qwen/.env(#1437) -
feat(cc-compatible): Align Claude Code-compatible request shape with the official Claude CLI protocol, including proper system skeleton and request normalization (#1411)
-
feat(skills): Provider-aware marketplace UX with scored AUTO injection and memory pipeline hardening. Skills now show relevance scores and can automatically inject context into requests (#1411)
-
feat(claude-code): Update Claude Code obfuscation to version 2.1.114, centralize hardcoded version strings, and use standard logger (#1403)
-
feat(cli-tools): Add direct configuration file generation and override support for Qwen Code local settings (#1394)
-
feat(providers): Derive Claude CLI model defaults dynamically from provider registry to stay current with upstream API changes (#1393)
-
feat(core): Implement persistent API key, backup pruning, and GPU optimization (#1350, #1367, #1369)
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
fix(cli-tools): Prevent masked API keys (
sk-31c4****8600) from being written to CLI tool config files. The dashboard UI now passeskey.idto the backend, which resolves the unmasked key from the database via a newresolveApiKey()helper. Fixes auth failures across all CLI tools (Claude, Codex, Cline, Kilo, Droid, OpenClaw, Antigravity) (#1435) -
fix(cc-compatible): Trim the default Claude Code-compatible system prompt skeleton from a multi-paragraph instruction set down to a single identifier line, reducing redundant token usage since Claude Code already injects its own extensive system context (#1433)
-
fix(security): Resolve SSRF environment static evaluation bug where the outbound URL guard could be bypassed via computed expressions (#1427)
-
fix(auth): Reload fresh token state and unify expiry persistence to prevent stale credentials from causing cascading auth failures
-
fix(core): Stabilization fixes for token refresh, usage translation, and testing infrastructure
-
fix(api): Stop sending unsupported parameters to Gemini and Codex upstream APIs, preventing 400 Bad Request errors
-
fix(skills): Optimize AUTO scoring algorithm and include Responses API input context for more accurate skill relevance matching (#1418)
-
fix(responses): Preserve reasoning content when translating Chat Completions format to Responses API format, preventing loss of chain-of-thought data (#1414)
-
fix(cc-compatible): Add Claude CLI system skeleton for OpenAI-format inputs to ensure consistent behavior when CC-compatible providers receive OpenAI-style payloads
-
fix(providers): Add
reftoGEMINI_UNSUPPORTED_SCHEMA_KEYSto fix 400 errors from Gemini CLI when tool schemas contain JSON Schema$reffields -
fix(codex): Prevent proactive token refresh from consuming valid tokens and strip the unsupported
backgroundparameter from upstream requests -
fix(providers): Fix
usage.prompt_tokensunder-reporting when translating Claude caching responses to OpenAI format (#1426) -
fix(core): Fix token refresh resilience for Codex providers. Unrecoverable OAuth refresh errors (
token_expiredandinvalid_token) now correctly mark the connection as invalid to prompt user re-authentication, rather than silently failing (#1415) -
fix(providers): Fix Gemini tool calling by removing the unsupported
additionalPropertiesschema field, resolving 400 errors during complex tool invocations (#1421) -
fix(providers): Remove arbitrary user thought signature injection in Gemini responses to comply with updated API constraints (#1410)
-
fix(providers): Fix Gemini API part count mismatch for streaming responses (#1412)
-
fix(codex): Respect
openaiStoreEnabledsetting during native passthrough for Responses API to prevent unsupported upstream arguments (#1432) -
fix(ui): Makes dropdown text visible in dark mode within the Combo Builder modal (#1409)
-
fix(chatcore): Apply proactive compression before provider translation to prevent token limit errors in combo routes (#1406)
-
fix(claude-code): Scope thinking stripping to executor boundaries to prevent issues with normal API requests (#1401)
-
fix(claude-code): Scope obfuscation logic to CLI clients only and fix associated test assertions
-
fix(mitm): Resolve MITM not working when connecting Antigravity (#1399)
-
fix(security): Resolve CodeQL password hash alert and fix TruffleHog CI failure (#161)
-
fix(combo): Fallback to the next model when all provider accounts return a 503 rate-limited signal instead of aborting the routing sequence (#1398)
-
fix(codex): Strip server-generated IDs from response items in input to prevent 404 lookup errors in multi-turn Codex Conversations (#1397)
-
fix(codex): Optimize Chat Completions paths by converting
systemtodeveloperroles instead of hoisting them into instructions, enabling prompt caching for system messages on GPT-5 models (#1400) -
fix(providers): Resolve Claude passthrough corruption (#1359), Kimi-k2 reasoning header rejections (#1360), thinking parameter leaks (#1361), and Ollama proxy redirect drops (#1381)
-
fix(core): Proxy lookup in key validation respects the new ProxyRegistry environments, and proxy contexts correctly inherit downwards during token refresh preventing expiration loops (#1384, #1390)
-
fix(providers): Treat upstream legacy validation HTTP 5xx responses as a valid bypass for Qoder PAT tokens to prevent false negative invalidation (#1391)
-
fix(electron): Resolve type error in Header electronAPI properties
-
fix(security): Resolve CodeQL security alerts including safe prototype bindings (#151, #152, #154, #155-159)
-
fix(tsc): Silence
baseUrldeprecation warnings for TypeScript 5.5+ configurations
🧪 Tests
- test(core): Resolve typescript strictness complaints and fix combo-routing-engine test regression
- test(core): Resolve remaining strict type errors across all unit test files
- test(providers): Fix provider service assertion for anthropic-compatible header format
- test(codex): Align codex passthrough assertions with explicit store retention policy
- test(codex): Fix store assertion for codex responses
- test(cli): Resolve strict null checks in Qoder unit tests
🛠️ Maintenance
- chore: Sync infrastructure with docker postinstall components and secondary CodeQL analysis rules
- chore: Enforce contributor credit rule in review-prs workflow
- chore: Fix TS errors and update review-prs workflow for improved automation
- ci: Allow manual CI dispatch for release branches
- ci: Shard long-running test suites and relax timeouts for stability
- ci: Restore release v3.6.9 build pipeline and fix flaky tests
- docs: Update generate-release workflow to use full changelog for PR body
- docs: Enforce PR merge instead of manual close in workflows
[3.6.8] — 2026-04-17
✨ New Features
-
feat(docs): integrate multi-page documentation into OmniRoute dashboard (#1969)
-
feat(settings): add request body limit setting (#1968)
-
feat(auth): add Gemini CLI OAuth client secret default (#1974)
-
feat(models): expose models.dev context windows in /v1/models (#1972)
-
fix(db): resolve legacy encryption fallback causing re-encryption loops (#1941)
-
fix(auth): fix Codex assistant final_answer response sanitization (#1965)
-
feat(providers): Implement Image Generation and Editing capabilities for ChatGPT Web, including in-band chat image generation and caching (#1606).
-
feat(ui): Integrate OpenCode Zen/Go API tool logo SVG and polish API key copy-to-clipboard interactions (#1607).
-
feat(providers): Integrate AgentRouter as a new OpenAI-compatible passthrough provider with $200 free credits via sign-up (Issue #1572).
-
feat(ui): Implement on-demand per-model testing in the provider dashboard, allowing single-token diagnostic checks without triggering rate-limits (Issue #1532).
-
feat(providers): Support
xhighreasoning tier exclusively on Claude models that expose it (#1356) -
feat(providers): Add CC Compatible connection-level 1M context toggle (#1357)
-
feat(core): Add full support for Node.js 24 LTS (Krypton) environments with continuous integration coverage (#1340)
-
feat(dashboard): Display Antigravity credit balance in dashboard Limits & Quotas (#1338)
-
feat(i18n): Add internationalization support for combo features and dashboard components; sync translations across 31 keys (#1318)
-
feat(providers): Add Claude Opus 4.7 to Claude Code OAuth models natively with extended context and caching (#1347)
-
feat(core): Add stopSequences support and expand tool definitions to include Google Search capabilities
-
feat(auth): Enforce dashboard session authentication on all management API routes, preventing unauthenticated access to configuration endpoints
-
feat(runtime): Add hot-reloadable guardrails and model diagnostics for real-time rule evaluation without restarts
-
feat(core): Add payload rules, tag-based routing, and scheduled budget systems for fine-grained request governance
-
feat(providers): Expose Antigravity preview model aliases and Gemini CLI onboarding flow for first-time setup
-
feat(antigravity): Add client model aliases and thoughtSignature bypass modes for Antigravity OAuth connections
-
feat(providers): Expand image provider registry with extended model support including SD3.5, FLUX, and DALL-E 3 HD configurations
-
feat(combos): Add new routing strategies and full i18n support for agent features section across 31 languages
🔒 Security
- security: Resolve 18 GitHub CodeQL scan alerts including ReDoS, incomplete sanitization, and bad HTML filtering regexp patterns
- fix(auth): Seal privilege escalation vector by enforcing JWT session checking exclusively on
/api/keysmanagement endpoints (#1353) - fix(providers): Resolve Codex token refresh race condition via mutex
getAccessTokenpreventingrefresh_token_reusedAuth0 revocations
🔧 Maintenance & Architecture
- refactor(core): Split CLI runner and decouple migration engine for extensibility (#1358)
- refactor(audit): Rewire audit dashboard from dead in-memory
configAuditstore to live SQLiteaudit_logtable — 331+ hidden compliance entries now visible in/dashboard/audit - build(deps): Bump
softprops/action-gh-releasefrom v2 to v3 - ci: Bump GitHub Actions CI node-version to Node.js 24 natively
- fix(types): Resolve TypeScript compilation errors in
claudeCodeCompatible.ts(type predicates,cache_controlindex access) andproxyFetch.ts(signalnullability)
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
fix(context): Scale reserved context tokens dynamically using a 15% sliding window for smaller models
-
test(core): Replace unit test with integration test for proactive context compression to align with isolated runner rules (#1378)
-
fix(services): Pass origin provider to refreshWithRetry to avoid tripping the generic "unknown" circuit breaker (fixes Codex accounts erroneously disabling)
-
fix(db): Prevent native module ABI load crashes from assuming database corruption and skipping databases
-
fix(db): Increase mass-migration threshold from 5 to 50 pending migrations to protect legacy users upgrading node
-
fix(db): Prevent migration runner safety aborts from triggering on fresh
DATA_DIRinstallations by detecting new databases (#1328) -
fix(mcp): Checkpoint and close MCP audit SQLite database safely on process signals and shutdown (#1348)
-
fix(mcp): Fully decouple MCP audit SQLite connection caching via globalThis to fix unhandled teardown in standalone Next.js chunks (#1349)
-
fix(cli): Avoid creating app router directory during postinstall initialization on non-built source trees (#1351)
-
fix(codex): Correctly translate
systemrole todeveloperin input array to unlock GPT-5 automatic prompt caching (#1346) -
fix(core): Pass client headers to executor in chatCore (#1335)
-
fix(providers): Separate test batch calls and ignore unknown connections
-
fix(providers): Add grok-web SSO cookie validation handler (#1334)
-
fix(db): Preserve key_value settings (dashboard passwords, saved aliases) across DB heuristic recreation cycles (#1333)
-
fix(routing): Allow combo fallback to cascade context overflow 400 errors instead of immediate aborts (#1331)
-
fix(core): Resolve thinking leaks, consecutive roles, and missing thoughtSignatures for Antigravity translator (#1316)
-
fix(translator): Only apply thoughtSignature to the first
functionCallpart in Gemini parallel tool calls, preventing duplicate signatures -
fix(providers): Default to batch testing execution blocks for web, search, and audio modalities to prevent connection timeouts
-
fix(cli): Resolve Node 22 TS entrypoint incompatibility by using esbuild compilation (#1315)
-
fix(chat): Preserve max_output_tokens for Responses API targets in chatCore sanitization (#1313)
-
fix(api): API Manager usage stats showing 0 for all registered keys (#1310)
-
fix(api): Support image-only models in catalog and allow authless search providers to bypass validation requirements
-
fix(routes): Require prompts for media generation requests (
/images,/videos,/music), returning 400 on missing payloads -
fix(dashboard): Auto-scroll ActivityHeatmap to show current date (#1309)
-
fix(dashboard): Restore horizontal layout with
w-maxwrapper in heatmap components -
fix(i18n): Update
nodeIncompatibleHintto recommend Node 24 LTS across all 31 languages -
fix(i18n): Add Chinese i18n support to remaining dashboard components (
Loading.tsx,DataTable, etc.) -
fix(requestLogger): Add missing
cacheSourceandtpscolumns to i18n log detail views
[3.6.6] — 2026-04-15
✨ New Features
-
feat(docs): integrate multi-page documentation into OmniRoute dashboard (#1969)
-
feat(settings): add request body limit setting (#1968)
-
feat(auth): add Gemini CLI OAuth client secret default (#1974)
-
feat(models): expose models.dev context windows in /v1/models (#1972)
-
fix(db): resolve legacy encryption fallback causing re-encryption loops (#1941)
-
fix(auth): fix Codex assistant final_answer response sanitization (#1965)
-
feat(providers): Implement Image Generation and Editing capabilities for ChatGPT Web, including in-band chat image generation and caching (#1606).
-
feat(ui): Integrate OpenCode Zen/Go API tool logo SVG and polish API key copy-to-clipboard interactions (#1607).
-
feat(providers): Integrate AgentRouter as a new OpenAI-compatible passthrough provider with $200 free credits via sign-up (Issue #1572).
-
feat(ui): Implement on-demand per-model testing in the provider dashboard, allowing single-token diagnostic checks without triggering rate-limits (Issue #1532).
-
feat(storage): Add database backup cleanup controls, UI management, and customizable retention period env vars (#1304)
-
feat(providers): Add Freepik Pikaso image generation provider with support for cookie/subscription-based auth modes (#1277)
-
feat(providers): Add Perplexity Web (Session) Provider — Routes through Perplexity's internal SSE API using a session cookie, giving native proxy access without separate API costs to GPT-5.4, Claude Opus, Gemini 3.1 Pro, and Nemotron via preferences mapping (#1289)
-
feat(api): Sync Tokens & V1 WebSocket Bridge — Dedicated sync token storage, issuance, revocation, and bundle download routes backed by stable config bundle versioning with ETag support. Exposes
/v1/wsWebSocket upgrade route and a custom Next.js server bridge (scripts/v1-ws-bridge.mjs) so OpenAI-compatible WebSocket traffic can be proxied through the gateway. Compliance auditing expanded with structured metadata, pagination, request context, auth/provider credential events, and SSRF-blocked validation logging. New migrations:024_create_sync_tokens.sql. New modules:syncTokens.ts,src/lib/sync/bundle.ts,src/lib/sync/tokens.ts,src/lib/ws/handshake.ts,src/lib/apiBridgeServer.ts,src/lib/compliance/providerAudit.ts. -
feat(models): GLM Thinking Preset & Hybrid Token Counting — GLM Thinking (
glmt) registered as a first-class provider preset with shared GLM model metadata, pricing, per-connection usage sync, dashboard support, andmaxTokens: 65536 / thinkingBudgetTokens: 24576request defaults with 900s extended timeout. Provider-side/messages/count_tokensendpoint used when a Claude-compatible upstream supports it; gracefully falls back to estimation on missing models, missing credentials, or upstream failures. Startup seeding of default model aliases (src/lib/modelAliasSeed.ts) normalizes common cross-proxy model dialects so canonical slash-based model IDs are not misrouted. New fileopen-sse/config/glmProvider.ts. -
feat(core): Hardened Outbound Provider Calls & Cooldown Retries — Guarded outbound fetch helpers (
src/shared/network/safeOutboundFetch.ts,src/shared/network/outboundUrlGuard.ts) blocking private/local URLs with configurable retry, timeout normalisation, and route-level status propagation for provider validation and model discovery. Cooldown-aware chat retries (src/sse/services/cooldownAwareRetry.ts) with configurablerequestRetryandmaxRetryIntervalSecsettings and model-scoped cooldown responses. Improved rate-limit learning from headers and error bodies so short upstream lockouts can recover automatically. Runtime environment validation (src/lib/env/runtimeEnv.ts) checks env at startup. Pollinations now requires an API key. Antigravity and Codex header handling aligned viaopen-sse/config/antigravityUpstream.tsandopen-sse/config/codexClient.ts. Gemini tool names restored in translated responses; synthetic Claude text block injected when upstream SSE completes empty. -
feat(logs): Add TPS (Tokens Per Second) metric to log details modal metadata grid (#1182)
-
feat(memory+skills): Full-featured Memory & Skills systems with FTS5 SQLite search, dynamic UI pagination, backend observability, and extensive test coverage (#1228)
-
feat(bailian-quota): Add Alibaba Coding Plan quota monitoring, multi-window quota extraction, and UI credential validation (#1235)
-
feat(storage): Call Log Storage Refactor — Extracted heavy request/response JSON payloads from the core SQLite database (
storage.sqlite) into filesystem artifacts stored withinDATA_DIR/call_logs. This massively reduces WAL bloat and eliminatesSQLITE_FULLcrashes on high-traffic nodes (#1307). -
feat(providers): Add Grok Web (Subscription) Provider — Routes through the xAI web interface for subscription users via cookie session mapping (#1295).
-
feat(api): Advanced Media Support — Extends OpenAI generic proxy layer to natively support
image,embeddings,audio-transcriptions, andaudio-speechworkflows (#1297). -
feat(cli-tools): Qwen Code CLI Integration — Full integration for Qwen Code local execution mapping, model resolution, and dynamic API key fetching (#1266, #1263).
-
feat(oauth): Supports
cursor-agentCLI as a native Cursor credential source alongside the standard configuration (#1258). -
feat(models): Custom and imported models now merge correctly into filter lists for all available global providers (#1191).
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
fix(providers): match correct endpoint api.xiaomimimo.com for Xiaomi MiMo (#1303)
-
fix(core): strip provider alias routing prefix from payload for custom endpoints to fix Azure OpenAI 400 errors (#1261)
-
fix(core): ProxyFetch Undici dispatcher automatically bypasses LAN/local addresses, preventing fetch failures on internal OpenRouter requests (#1254)
-
fix(core): Gemini thought stream signature detection upgraded to use native part.thought boolean, preventing reasoning text leaks (#1298)
-
deps: bump hono from 4.12.12 to 4.12.14 to resolve CVE SSR HTML injection vulnerability (#1306, #59)
-
deps: update dompurify to 3.4.0 in frontend overrides mitigating XSS HTML Injection (CVE-XYZ / Dependabot #60)
-
test: Disable SQLite automatic backups during continuous integration (CI) tests to resolve E2E timeout issues limiting runner scaling (#24481475058)
-
feat(core): Proactive Context Compression —
chatCorenow proactively compresses oversized message contexts before hitting upstream providers to dramatically reducecontext_length_exceedederrors. Employs binary-search message pruning with structural integrity guarantees tracking explicittool_useboundaries ensuring truncated tool inputs drop paired outputs appropriately (#1292, #1293) -
fix(cli): Resolve codex routing config parsing by strictly quoting section keys array, enforcing responses wire_api with fallback, and standardizing select-model button positioning mirroring Claude UI
-
fix(providers): Correct Lobehub provider icons rendering by removing unsupported local references ensuring local SVG/PNG fallback mechanism invokes natively
-
fix(db): Implement Database migration tracking safety abort safeguards (pre-migration backups via
VACUUM INTOand mass renumbering warnings) to protect existing database structures on startup upgrades (#1281) -
fix(dashboard): Cleaned up target codex
config.tomlstructure preventing recursive section rendering by enforcing quotes on section dot paths and mapping correct UIOMNIROUTE_API_KEYnames. -
fix(mcp): Add dedicated explicit timeout constraint overrides for search handlers (#1280)
-
fix(crypto): Add validation guard to encryption layer to surface clear UI errors when cryptographic environment variables are missing, replacing raw Node.js TypeErrors. Legacy env vars
OMNIROUTE_CRYPT_KEYandOMNIROUTE_API_KEY_BASE64now also accepted as fallbacks (#1165) -
fix(providers): Update Pollinations provider definition to require API keys and specify their new limited pollen/hour free tier (#1177)
-
Streaming
\n\nArtifact Fix (#1211): Changed<omniModel>tag-stripping regex from?to*quantifier acrosscombo.ts,comboAgentMiddleware.ts, andcontextHandoff.tsto greedily strip all accumulated JSON-escaped newline sequences surrounding the tag. This prevents literal\n\nprefix artifacts from appearing in consumer streaming responses -
E2E Combo Test Locator: Fixed Playwright strict-mode violation in
combo-unification.spec.tsby replacing ambiguousgetByRolelocator with a compound filter locator for the "All" strategy tab -
fix(cc-compatible): Trim beta flags and preserve cache passthrough for third-party HTTP proxy compatibility (#1230)
-
fix(providers): Update Xiaomi MiMo endpoints to the live token-plan, migrating away from dead API URLs (#1238)
-
fix: Forward client
x-initiatorheader to GitHub Copilot upstream to accurately distinguish agent vs user turns (#1227) -
fix: Resolve backlog bugs including streaming edge cases, unhandled rejections, and quota parse failures (#1206, #1220, #1231, #1175, #1187, #1218, #1202)
-
fix(tests): Resolve memory migration and skills route pagination bugs arising from PR overlaps
-
fix(i18n): Add missing Chinese i18n support to dashboard components (
DataTable,EmptyState, etc), updateen.json/zh-CN.jsonrouting keys, and natively resolve JSX defaults vianext-intl(#1274)
🔧 Internal Improvements
- Compliance Audit Expansion:
src/lib/compliance/index.tsexpanded with structured metadata, pagination support, request context enrichment, and newproviderAudit.tsmodule logging auth and provider credential events, SSRF-blocked validation attempts, and provider CRUD operations - Config Sync Bundle:
src/lib/sync/bundle.tsexportsbuildConfigBundle()generating a versioned JSON snapshot of settings, provider connections, nodes, model aliases, combos, and API keys (passwords redacted) with ETag support for bandwidth-efficient polling - Codex Client Constants: Centralized
CODEX_CLIENT_VERSION,CODEX_USER_AGENT_PLATFORM, and pattern-validated env overrides (CODEX_CLIENT_VERSION,CODEX_USER_AGENT) inopen-sse/config/codexClient.ts - Antigravity Upstream Constants:
open-sse/config/antigravityUpstream.tsconsolidates all Antigravity base URLs and model/fetchAvailableModels discovery path builders - Model Alias Seed:
src/lib/modelAliasSeed.tsseeds 30+ cross-proxy model dialect aliases (e.g.openai/gpt-5→gpt-5,anthropic/claude-opus-4-6→cc/claude-opus-4-6) at startup via idempotentupsert - Test Coverage: 15+ new unit test suites covering sync routes, WebSocket bridge, compliance index, GLM provider config, cooldown-aware retry, safe outbound fetch, stream utilities, Codex executor, provider validation branches, model cross-proxy compatibility, and model alias seeding
- TypeScript Migration: Finalized migration of remaining JS tests (
proxy-loadandtestFromFile) to TypeScript ES modules, ensuring a fully synchronized TS stack. - Reliability & Resilience: Added exponential backoff to
models.devauto-sync to combat transient network failures, raised interval floor to 1 hour, and added LKGP debug logging for enhanced observability during routing. (#1286)
[3.6.5] — 2026-04-13
✨ New Features
-
feat(docs): integrate multi-page documentation into OmniRoute dashboard (#1969)
-
feat(settings): add request body limit setting (#1968)
-
feat(auth): add Gemini CLI OAuth client secret default (#1974)
-
feat(models): expose models.dev context windows in /v1/models (#1972)
-
fix(db): resolve legacy encryption fallback causing re-encryption loops (#1941)
-
fix(auth): fix Codex assistant final_answer response sanitization (#1965)
-
feat(providers): Implement Image Generation and Editing capabilities for ChatGPT Web, including in-band chat image generation and caching (#1606).
-
feat(ui): Integrate OpenCode Zen/Go API tool logo SVG and polish API key copy-to-clipboard interactions (#1607).
-
feat(providers): Integrate AgentRouter as a new OpenAI-compatible passthrough provider with $200 free credits via sign-up (Issue #1572).
-
feat(ui): Implement on-demand per-model testing in the provider dashboard, allowing single-token diagnostic checks without triggering rate-limits (Issue #1532).
-
Antigravity AI Credits Fallback: Automatically retries with
GOOGLE_ONE_AIcredit injection when free-tier quota is exhausted. Per-account credit balance (5-hour TTL) is cached from SSEremainingCreditsand exposed as a numeric badge in the Provider Usage dashboard (#1190 — thanks @sFaxsy) -
Claude Code Native Parity: Full header/body signing parity with the Claude Code 2.1.87 OAuth client — CCH xxHash64 body signing with singleton WASM initialization promise (fixing race conditions), dynamic per-request fingerprint, bidirectional TitleCase ↔ lowercase tool name remapping (14 tools), API constraint enforcement (
temperature=1for thinking, max 4cache_controlblocks, auto-inject ephemeral on last user message), and optional ZWJ obfuscation. Wired intoBaseExecutorfor automatic CCH signing on allanthropic-compatible-cc-*providers and intochatCorefor synchronous parity pipeline steps (#1188 — thanks @RaviTharuma) -
Per-Connection Codex Defaults: Codex Fast Service Tier and Reasoning Effort settings are now per-connection instead of a single global toggle. Existing connections are migrated automatically on startup via an idempotent backfill migration (#1176 — thanks @rdself)
-
Cursor Usage Dashboard: New
getCursorUsage()fetches quotas from Cursor's/api/usage,/api/auth/me, and/api/subscriptionendpoints. Displays standard requests, on-demand usage, and per-plan limits (Free/Pro/Business/Team). Client version bumped to3.1.0andx-cursor-user-agentheader added for parity -
Database Health Check System: Automated periodic SQLite integrity monitoring via
runDbHealthCheck()— detects orphan quota/domain rows, broken combo references, stale snapshots, and invalid JSON state. Runs every 6 hours (configurable viaOMNIROUTE_DB_HEALTHCHECK_INTERVAL_MS), with auto-repair and pre-repair backup. Exposed as MCP tool #18 (omniroute_db_health_check) with Zod schemas andautoRepairoption. Dashboard panel in Health page with status card, issue count, repaired count, and one-click repair button -
OpenAI Responses API Store Opt-In: Per-connection
openaiStoreEnabledflag controls whether thestorefield is preserved or forced tofalseon Codex Responses API requests. When enabled,previous_response_id,prompt_cache_key,session_id, andconversation_idfields are round-tripped through the Chat Completions → Responses translation, enabling multi-turn context caching on supported providers -
Email Privacy Toggle (Combos Page): Global email visibility toggle (
EmailPrivacyToggle) added to the Combos page header with responsive layout, tooltip guidance, and per-connection label masking viapickDisplayValue(). All combo builder options, provider connection lists, and quota screens now respect the global privacy state fromemailPrivacyStore -
skills.sh Integration: Added
skills.shas an external skill provider. Users can now search, browse, and install agent skills directly from a new "skills.sh" tab in the Skills dashboard. Includes backend API resolvers, frontend implementation with search/install states, and a dedicated unit test suite (#1223 — thanks @RaviTharuma) -
Stabilization Settings: Added persistence support for
lkgpEnabledandbackgroundDegradationsettings, integrated intoinstrumentation-node.tsfor improved lifecycle awareness (#1212) -
xxhash-wasm dependency: Added
xxhash-wasm@^1.1.0for CCH signing (xxHash64 with seed0x6E52736AC806831E)
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
Codex
stream: falsevia Combo (ALL_ACCOUNTS_INACTIVE): Fixed a critical bug where Codex combos returnedALL_ACCOUNTS_INACTIVEor empty content when the client sentstream: false. Root cause was triple: (1)CodexExecutor.transformRequest()mutatedbody.streamin-place totrue, contaminating the combo's quality check which skipped validation thinking it was streaming; (2) the non-stream SSE parser used the wrong format (Chat Completions instead of Responses API) for Codex SSE output; (3) combo quality validation read the mutatedbody.streaminstead of the client's original intent. Fixed by: cloning the body viastructuredClone()in CodexExecutor, detecting Codex/Responses SSE format in the non-stream fallback path (with auto-translation back to Chat Completions), and capturingclientRequestedStreambefore the combo loop -
Gemini CLI Tool Schema Rejection: Fixed 400 Bad Request errors from the Google API by strictly filtering non-standard vendor extensions (starting with
x-) anddeprecatedfields from tool parameter schemas (#1206) -
SOCKS5 Proxy Interop (Node.js 22): Resolved
invalid onRequestStart methodcrashes caused byundiciversion mismatches between dispatchers and the built-in fetch. HardenedproxyFetch.tsto strictly use the library's fetch implementation for custom dispatchers (#1219) -
Search Cache Coalescing with TTL=0: Fixed a bug where providers configured with
cacheTTLMs: 0(caching explicitly disabled) still had concurrent requests coalesced and returned{ cached: true }. Now each call gets its own independent upstream fetch (#1178 — thanks @sjhddh) -
Antigravity Credit Cache Alignment (PR #1190): Reconciled
accountIdderivation betweenAntigravityExecutor.collectStreamToResponseandgetAntigravityUsageto use consistent cache keys (email || sub || "unknown"). Previously, SSE-parsed credit balances could be written under a different key than the one read by the usage dashboard, causing stale/missing credit badges -
Non-streaming reasoning_content Duplication: Fixed clients rendering duplicated reasoning panels when both
reasoning_contentand visiblecontentwere present in non-streaming responses.responseSanitizernow stripsreasoning_contentfrom messages that already have visible text content, preserving it only for reasoning-only messages -
Streaming Regression Fix: Hardened the
sanitizeTransformStream in the combo engine to strip both literal and JSON-escaped newline sequences, eliminating leading\n\nprefixes in assistant responses (#1211) -
Gemini Empty Choice Fix: Ensured initial assistant deltas always include an empty
content: ""string to satisfy strict OpenAI client requirements and prevent empty choice responses in tools (#1209) -
Gemini Tools Sanitizer Deduplication: Extracted shared tool conversion logic into
buildGeminiTools()helper (geminiToolsSanitizer.ts), eliminating duplicate implementations betweenopenai-to-gemini.tsandclaude-to-gemini.ts. The new helper correctly handlesweb_search/web_search_previewtool types by emittinggoogleSearchtools with priority over function declarations -
Qwen/Qoder Thinking+Tool_Choice Conflict: Added
sanitizeQwenThinkingToolChoice()to bothDefaultExecutor(for Qwen provider) andQoderExecutorto prevent provider-side 400 errors when clients sendtool_choicealongside thinking/reasoning parameters that are mutually exclusive upstream -
API Key Deletion Orphan Cleanup: Deleting an API key now also removes associated
domain_budgetsanddomain_cost_historyrows, preventing orphan data accumulation -
CC-compatible test assertion: Fixed pre-existing test that expected no
cache_controlon system blocks — the billing header system block now carriescache_control: { type: "ephemeral" }per PR #1188 design -
Codex Combo Smoke Test False Positives: Fixed combo tests incorrectly reporting
ERRORfor valid Codex streaming responses whenresponse.outputis empty but text deltas were emitted. The summary now falls back to accumulated delta text (#1176 — thanks @rdself) -
Electron Builder Version Mismatch: Fixed Electron desktop startup failures on Windows packaged builds caused by native modules (
better-sqlite3) being underapp.asar.unpackedwhile helpers were inapp/node_modules.resolveServerNodePath()now merges both locations with deduplication and existence checks (#1172 — thanks @backryun)
🔧 Internal Improvements
- SSE Parser: Responses API Non-Stream Conversion: Added full
parseSSEToResponsesOutput()implementation insseParser.ts(255+ lines) — reconstructs complete Responses API objects from SSE event streams, handlingresponse.output_text.delta/done,response.reasoning_summary_text.delta/done,response.function_call_arguments.delta/done, and terminal events. Used by the new chatCore non-stream fallback path for Codex - Cursor Executor Version Sync: Updated Cursor client User-Agent to
3.1.0and centralized version constants (CURSOR_CLIENT_VERSION,CURSOR_USER_AGENT) for consistent fingerprinting across executor, usage fetcher, and OAuth flows - Responses API Translator Parity:
convertResponsesApiFormat()now accepts credentials and passes them through to the translator, enabling store-aware field propagation. Round-trip preservation ofprevious_response_id,prompt_cache_key,session_id, andconversation_idfields - Provider Schema Validation: Added
openaiStoreEnabledboolean validation toproviderSpecificDataZod schema - Combo Error Response Normalization: Empty combo targets now return 404 (
comboModelNotFoundResponse) instead of generic 503, improving client-side error differentiation - Dependency Updates: Bumps
typescript-eslintto8.58.2(dev),axiosto1.15.0(prod), andnextto16.2.2(prod) (#1224, #1225)
⚠️ Breaking Changes
DELETE /api/settings/codex-service-tierremoved: This endpoint no longer exists. Codex Service Tier configuration has moved to per-connectionproviderSpecificData.requestDefaults. Existing connections are migrated automatically on first startup after upgrade. Any external scripts or integrations that call this endpoint should be updated — usePUT /api/providers/:idwithproviderSpecificData.requestDefaults.serviceTierinstead (#1176).- CCH signing on CC-compatible providers: All requests to
anthropic-compatible-cc-*providers now include an xxHash64 integrity token (cch=...) in the billing header. Providers that do not validate CCH will ignore it (no behavioral change), but any custom middleware inspecting the billing header should expect a 5-character hex token instead of the00000placeholder
[3.6.4] — 2026-04-12
✨ New Features
-
feat(docs): integrate multi-page documentation into OmniRoute dashboard (#1969)
-
feat(settings): add request body limit setting (#1968)
-
feat(auth): add Gemini CLI OAuth client secret default (#1974)
-
feat(models): expose models.dev context windows in /v1/models (#1972)
-
fix(db): resolve legacy encryption fallback causing re-encryption loops (#1941)
-
fix(auth): fix Codex assistant final_answer response sanitization (#1965)
-
feat(providers): Implement Image Generation and Editing capabilities for ChatGPT Web, including in-band chat image generation and caching (#1606).
-
feat(ui): Integrate OpenCode Zen/Go API tool logo SVG and polish API key copy-to-clipboard interactions (#1607).
-
feat(providers): Integrate AgentRouter as a new OpenAI-compatible passthrough provider with $200 free credits via sign-up (Issue #1572).
-
feat(ui): Implement on-demand per-model testing in the provider dashboard, allowing single-token diagnostic checks without triggering rate-limits (Issue #1532).
-
Combo Builder v2 (Wizard UI): Completely redesigned the combo creation/editing interface as a multi-stage wizard with stages: Basics → Steps → Strategy → Review. The builder fetches provider, model, and connection metadata via a new
GET /api/combos/builder/optionsendpoint, enabling precise provider/model/account selection with duplicate detection and automatic next-connection suggestion. Heavy UI components (ModelSelectModal,ProxyConfigModal,ModelRoutingSection) are now lazily loaded vianext/dynamicfor faster initial page render -
Combo Step Architecture (Schema v2): Introduced a structured step model (
ComboModelStep,ComboRefStep) replacing the legacy flat string/object combo entries. Steps carry explicitid,kind,providerId,connectionId,weight, andlabelfields, enabling pinned-account routing, cross-combo references, and per-step metrics. All combo CRUD operations normalize entries through the newsrc/lib/combos/steps.tsmodule. Zod schemas updated withcomboModelStepInputSchemaandcomboRefStepInputSchemaunions -
Composite Tiers System: Added tiered model routing via
config.compositeTiers— each tier maps a named stage to a specific combo step with optional fallback chains. Includes comprehensive validation (src/lib/combos/compositeTiers.ts) ensuring step existence, preventing circular fallback, and validating default tier references. Zod schema enforcement blocks composite tiers on global defaults (concrete combos only) -
Model Capabilities Registry: Created
src/lib/modelCapabilities.tsprovidinggetResolvedModelCapabilities()— a unified resolver that merges static specs, provider registry data, and live-synced capabilities into a singleResolvedModelCapabilitiesobject covering tool calling, reasoning, vision, context window, thinking budget, modalities, and model lifecycle metadata -
Observability Module: Extracted health and telemetry payload construction into
src/lib/monitoring/observability.tswithbuildHealthPayload(),buildTelemetryPayload(), andbuildSessionsSummary()builders. The health endpoint now returns session activity, quota monitor status, and per-provider breakdowns alongside existing system metrics -
Session & Quota Monitor Dashboard: Added live Session Activity and Quota Monitors panels to the Health dashboard, showing active session counts, sticky-bound sessions, per-API-key breakdowns, and top session details alongside quota monitor alerting/exhausted/error status with per-provider drill-down
-
Combo Health Per-Target Analytics: The combo-health API now resolves per-target metrics using the new
resolveNestedComboTargets()function, providing step-level success rates, latency, and historical usage breakdowns per execution key — enabling per-account, per-connection health visibility -
Auto-Combo → Combos Unification: Merged the separate
/dashboard/auto-combopage into the main/dashboard/combospage. Auto/LKGP combos are now managed alongside all other combos with a new strategy filter tabs system (All / Intelligent / Deterministic). The old auto-combo route redirects to/dashboard/combos?filter=intelligent. Removed theauto-combosidebar entry, consolidating navigation into the singleCombositem -
Intelligent Routing Panel (
IntelligentComboPanel): New inline panel (371 lines) within the combos page that shows real-time provider scores, 6-factor scoring breakdown (quota, health, cost, latency, task fitness, stability), mode pack selector, incident mode status, and excluded providers forauto/lkgpcombos — replacing the former standalone auto-combo dashboard -
Builder Intelligent Step (
BuilderIntelligentStep): New conditional wizard step (280 lines) that appears in the Builder v2 flow only whenstrategy=autoorstrategy=lkgpis selected. Exposes candidate pool selection, mode pack presets, router sub-strategy selector, exploration rate slider, budget cap, and collapsible advanced scoring weights configuration -
Intelligent Routing Module (
intelligentRouting.ts): Extracted strategy categorization and filtering logic into a dedicated shared module (210 lines) withgetStrategyCategory(),isIntelligentStrategy(),filterCombosByStrategyCategory(),normalizeIntelligentRoutingFilter(), andnormalizeIntelligentRoutingConfig()utility functions -
LKGP Standalone Strategy: Implemented
lkgp(Last Known Good Provider) as a fully functional standalone combo strategy. Previously,lkgpas a combo strategy silently fell through topriorityordering — the LKGP lookup only ran inside theautoengine. Nowstrategy: "lkgp"correctly queries the LKGP state, moves the last successful provider to the top of the target list, and saves the LKGP state after each successful request. Falls back to priority ordering when no LKGP state exists -
Unified Routing Rules & Model Aliases: Consolidated the routing rules and model alias management controls into the Settings page, reducing fragmentation across the dashboard
⚡ Performance
- Middleware Lazy Loading: Refactored
src/proxy.tsto lazy-importapiAuth,db/settings, andmodelSyncSchedulermodules, reducing middleware cold-start overhead. Added inlineisPublicApiRoute()to avoid loading the full auth module for public routes - E2E Auth Bypass: Added
NEXT_PUBLIC_OMNIROUTE_E2E_MODEenvironment flag to bypass authentication gates for dashboard and management API routes during Playwright E2E test runs
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
P2C Credential Selection: Implemented Power-of-Two-Choices (P2C) connection scoring in
src/sse/services/auth.tswith quota headroom awareness, error/recency penalties, and forced/excluded connection support. The newgetProviderCredentialsWithQuotaPreflight()function integrates quota preflight checks directly into credential selection, eliminating the separate Codex-only preflight path -
Fixed-Account Combo Steps: Combo steps with explicit
connectionIdnow correctly bypass provider-level model cooldowns and circuit breakers, preventing a single account failure from blocking pinned-connection routing for the same model -
Combo Metrics Per-Target Tracking: Extended
comboMetrics.tsto trackbyTargetmetrics keyed by execution path, recording per-stepprovider,providerId,connectionId, andlabelalongside existing per-model aggregates -
Call Logs Schema Expansion: Added
requested_model,request_type,tokens_cache_read,tokens_cache_creation,tokens_reasoning,combo_step_id, andcombo_execution_keycolumns tocall_logswith auto-migration. Added composite indexidx_cl_combo_targetfor efficient per-target historical queries -
Quota Monitor Enrichment: Expanded
quotaMonitor.tswith full lifecycle state tracking (status,startedAt,lastPolledAt,consecutiveFailures,totalPolls,totalAlerts), ISO-formatted snapshots viagetQuotaMonitorSnapshots(), and sorted summary viagetQuotaMonitorSummary() -
Codex Quota Fetcher Hardening: Improved
codexQuotaFetcher.tswith safer connection registration and quota fetch error handling -
LKGP Save Refactored to Async/Await: Replaced fire-and-forget
.then()chain for LKGP persistence after successful combo routing with properasync/await+try/catch, preventing unhandled promise rejections and ensuring LKGP state is reliably saved before the response is returned -
Duplicate
autoin Combo Strategy Schema: Removed duplicate"auto"entry fromcomboStrategySchema(was listed on both line 104 and 108). Harmless to Zod runtime but cleaned up to avoid confusion. Schema now has exactly 13 unique strategy values -
Legacy Combo Refs Normalization: Fixed combo step normalization to preserve legacy string combo references during CRUD operations, preventing data loss when editing combos created before the v2 step architecture
🔒 Security
- Auth Bypass on Backup Routes (Critical): Added
isAuthenticatedguards to/api/db-backups/exportAll(full database export) and/api/db-backups(list, create, and restore backups) — both were previously accessible without authentication - Auth Guard on Translator Save: Added
isAuthenticatedguard to/api/translator/savefor defense-in-depth consistency - API Key Secret Hardening: Removed the hardcoded
"omniroute-default-insecure-api-key-secret"fallback fromapiKey.ts— the function now fails fast ifAPI_KEY_SECRETis unset, relying on the startup validator to auto-generate it - NPM Tarball Leak Fix: Added
app/.env*to.npmignoreto prevent the working.envfile from being shipped inside the npm tarball distribution - Electron Builder CVE Fix: Bumped
electron-builderto 26.8.1 to resolvetarCVEs in the desktop build pipeline
🔧 Maintenance & Infrastructure
- DB Migration 021: Added
combo_call_log_targetsmigration forcombo_step_idandcombo_execution_keycolumns in call_logs - Combo CRUD Normalization:
db/combos.tsnow normalizes all stored combo entries through the step normalization pipeline on read, ensuring consistent step IDs and kind annotations regardless of when the combo was created - Playwright Config: Updated Playwright configuration and
run-next-playwright.mjsscript for improved E2E test orchestration - Build Script: Updated
build-next-isolated.mjswith additional reliability improvements - Auto-Combo UI Cleanup: Deleted
AutoComboModal.tsx(161 lines), replacedauto-combo/page.tsx(478→5 lines) with a server-side redirect to/dashboard/combos?filter=intelligent - Sidebar Consolidation: Removed
"auto-combo"fromHIDEABLE_SIDEBAR_ITEM_IDSandPRIMARY_SIDEBAR_ITEMS—normalizeHiddenSidebarItems()silently discards any stale"auto-combo"entries in user settings - Schema Cleanup: Removed obsolete
createAutoComboSchemafromschemas.ts. ExportedcomboStrategySchemafor direct use in test and filter modules - A2A Agent Card Update: Renamed skill ID from
auto-combotointelligent-routingwith updated description referencing the unified combos dashboard - Builder Draft Refactor: Extended
builderDraft.tswith dynamic stage list generation viagetComboBuilderStages()andisIntelligentBuilderStrategy(). Stage navigation (getNextComboBuilderStage,getPreviousComboBuilderStage,canAccessComboBuilderStage) now accepts options to conditionally include/skip theintelligentwizard step - i18n Consolidation: Removed the standalone
"autoCombo"i18n block (22 keys) from all 30 language files. Migrated keys into the"combos"block with new additions for filter tabs, intelligent panel, and builder step labels
🧪 Tests
- 16 New Test Suites: Added comprehensive test coverage including:
combo-builder-draft.test.mjs(186 lines) — Builder draft step construction and validationcombo-builder-options-route.test.mjs(228 lines) — Builder options API endpointcombo-health-route.test.mjs(266 lines) — Combo health analytics with per-target metricscombo-routes-composite-tiers.test.mjs(157 lines) — Composite tiers API integrationcomposite-tiers-validation.test.mjs(131 lines) — Composite tier validation rulesdb-combos-crud.test.mjs— Combo CRUD with step normalizationdb-core-init.test.mjs(129 lines) — DB initialization and column migrationsmodel-capabilities-registry.test.mjs(105 lines) — Model capabilities resolutionobservability-payloads.test.mjs(165 lines) — Health/telemetry payload constructionopenapi-spec-route.test.mjs— OpenAPI spec generationproxy-e2e-mode.test.mjs(74 lines) — E2E mode auth bypassquota-monitor.test.mjs— Quota monitor lifecycle staterun-next-playwright.test.mjs(119 lines) — Playwright runner scriptsse-auth.test.mjs(154 lines) — P2C credential selection and quota preflighttelemetry-summary-route.test.mjs(35 lines) — Telemetry summary endpoint- Plus updates to 12 existing test files for compatibility with new step architecture
- Auto-Combo Unification Tests:
autocombo-unification.test.mjs(156 lines) — Strategy categorization, schema deduplication, sidebar cleanup, and routing strategies metadata validationcombo-unification.spec.ts(189 lines) — Playwright E2E tests for filter tabs, intelligent panel rendering, redirect from old route, sidebar entry removal, and Builder v2 intelligent step flow- 3 new LKGP standalone tests in
combo-routing-engine.test.mjs— Validates LKGP provider prioritization, fallback to priority when no state exists, and LKGP state persistence after successful requests - Updated
combo-builder-draft.test.mjswith intelligent stage navigation tests - Updated
sidebar-visibility.test.mjsto reflectauto-comboremoval
[3.6.3] — 2026-04-11
✨ New Features
-
feat(docs): integrate multi-page documentation into OmniRoute dashboard (#1969)
-
feat(settings): add request body limit setting (#1968)
-
feat(auth): add Gemini CLI OAuth client secret default (#1974)
-
feat(models): expose models.dev context windows in /v1/models (#1972)
-
fix(db): resolve legacy encryption fallback causing re-encryption loops (#1941)
-
fix(auth): fix Codex assistant final_answer response sanitization (#1965)
-
feat(providers): Implement Image Generation and Editing capabilities for ChatGPT Web, including in-band chat image generation and caching (#1606).
-
feat(ui): Integrate OpenCode Zen/Go API tool logo SVG and polish API key copy-to-clipboard interactions (#1607).
-
feat(providers): Integrate AgentRouter as a new OpenAI-compatible passthrough provider with $200 free credits via sign-up (Issue #1572).
-
feat(ui): Implement on-demand per-model testing in the provider dashboard, allowing single-token diagnostic checks without triggering rate-limits (Issue #1532).
-
OpenAI-Compatible Loose Validation: Empty API keys can now be naturally submitted and saved for any
openai-compatible-*providers (e.g. Pollinations, localized routes) directly in the UI instead of blocking save actions (#1152) -
Cloudflare Configuration: Updated the provider schema and UI integration for Cloudflare AI to officially expose and support the backend
accountIdfield securely without overrides (#1150)
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
Vertex JSON Validation Crash: Prevented
invalid character in headercrashes inside the/validateendpoint by creating a native authentication parser that correctly handles Google Identity Service Account JSON flows prior to pinging endpoints (#1153) -
Extraneous Payload Rejection: Globally prevented upstream
400 Bad Requestexecution crashes by stripping the non-standardprompt_cache_retentionattribute forcibly attached by Cursor/Cline IDE engines when targeting strict OpenAI/Anthropic routes (#1154) -
Reasoning Content Drop: Prevented pure reasoning packets, common in advanced fallback models like DeepSeek, from being aborted mid-stream by explicitly adjusting the
Empty Content (502)circuit breakers to acknowledgereasoning_contentstates as valid (#1155) -
Desktop Windows Build Crash: Fixed
better_sqlite3.node is not a valid Win32 applicationpreventing OmniRoute Desktop from launching on Windows by properly removing the ABI-mismatched sqlite cache from Next.js standalone and falling back to the cross-compiled Electron equivalent during packager build steps (#1163) -
Login Visual Security: Removed the raw fallback hash dump that artificially rendered underneath the login modal in Docker instances missing
OMNIROUTE_API_KEY_BASE64flags (#1148)
🔧 Maintenance & Dependencies
- Dependabot Updates: Safely bumped GitHub Actions
docker/build-push-actionto v7 andactions/download-artifactto v8 - Electron Updates: Upgraded desktop wrapper core to Electron
41.2.0andelectron-builderto26.8.1, incorporating essential V8/Chromium security patches - NPM Package Groups: Updated
productionanddevelopmentNPM groups to securely handle minor audit warnings and keep toolchains modern - CI/CD Reliability: Fixed persistent
Snyktoken-absence failures on automated pull requests by appropriately bypassing on dependabot actions
[3.6.2] — 2026-04-11
✨ New Features
-
feat(docs): integrate multi-page documentation into OmniRoute dashboard (#1969)
-
feat(settings): add request body limit setting (#1968)
-
feat(auth): add Gemini CLI OAuth client secret default (#1974)
-
feat(models): expose models.dev context windows in /v1/models (#1972)
-
fix(db): resolve legacy encryption fallback causing re-encryption loops (#1941)
-
fix(auth): fix Codex assistant final_answer response sanitization (#1965)
-
feat(providers): Implement Image Generation and Editing capabilities for ChatGPT Web, including in-band chat image generation and caching (#1606).
-
feat(ui): Integrate OpenCode Zen/Go API tool logo SVG and polish API key copy-to-clipboard interactions (#1607).
-
feat(providers): Integrate AgentRouter as a new OpenAI-compatible passthrough provider with $200 free credits via sign-up (Issue #1572).
-
feat(ui): Implement on-demand per-model testing in the provider dashboard, allowing single-token diagnostic checks without triggering rate-limits (Issue #1532).
-
33 New API Key Providers: Massive provider expansion adding DeepInfra, Vercel AI Gateway, Lambda AI, SambaNova, nScale, OVHcloud AI, Baseten, PublicAI, Moonshot AI, Meta Llama API, v0 (Vercel), Morph, Featherless AI, FriendliAI, LlamaGate, Galadriel, Weights & Biases Inference, Volcengine, AI21 Labs, Venice.ai, Codestral, Upstage, Maritalk, Xiaomi MiMo, Inference.net, NanoGPT, Predibase, Bytez, Heroku AI, Databricks, Snowflake Cortex, and GigaChat (Sber). OmniRoute now supports 100+ providers (4 Free + 8 OAuth + 91 API Key + Custom compatible)
-
Global Email Privacy Toggle: Added a persistent eye-icon toggle button across all dashboard pages (Providers, Usage Limits, Playground) that reveals or hides masked email addresses. Toggle state is stored in localStorage and synced globally via Zustand store
-
Documentation Refresh: Updated README, ARCHITECTURE, FEATURES, AGENTS.md, and API_REFERENCE for v3.6.2 with accurate provider counts (100+), new executor list, and system API documentation
-
Uninstall Guide: Created comprehensive
docs/guides/UNINSTALL.mdcovering clean uninstallation for all deployment methods (npm, Docker, Electron, source)
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
PDF Attachments: Unlocked deep string object parsing (
geminiHelper) ensuring Gemini translation successfully passes complex PDF payloads from OpenAI-compatible streams without dropping them silently (#993) -
SkillsMP Engine: Corrected object extraction path mappings inside the API router to fix UI marketplace rendering under Docker/Standalone Node isolated deployments (#988)
[3.6.1] — 2026-04-10
✨ New Features
-
feat(docs): integrate multi-page documentation into OmniRoute dashboard (#1969)
-
feat(settings): add request body limit setting (#1968)
-
feat(auth): add Gemini CLI OAuth client secret default (#1974)
-
feat(models): expose models.dev context windows in /v1/models (#1972)
-
fix(db): resolve legacy encryption fallback causing re-encryption loops (#1941)
-
fix(auth): fix Codex assistant final_answer response sanitization (#1965)
-
feat(providers): Implement Image Generation and Editing capabilities for ChatGPT Web, including in-band chat image generation and caching (#1606).
-
feat(ui): Integrate OpenCode Zen/Go API tool logo SVG and polish API key copy-to-clipboard interactions (#1607).
-
feat(providers): Integrate AgentRouter as a new OpenAI-compatible passthrough provider with $200 free credits via sign-up (Issue #1572).
-
feat(ui): Implement on-demand per-model testing in the provider dashboard, allowing single-token diagnostic checks without triggering rate-limits (Issue #1532).
-
OAuth Env Repair Action: Added a "Repair env" button to the OAuth Providers dashboard that detects and restores missing OAuth client IDs from
.env.example— with timestamped backup and append-only safety. Includes full 33-language i18n support and sanitized API responses (#1116, by @yart)
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
i18n: Missing Provider Keys: Added missing
filterModels,modelsActive,showModel,hideModelkeys across all 32 locale files, fixing runtimeMISSING_MESSAGEerrors in the providers UI. Also cleaned up duplicate keys inen.json(#1111, by @rilham97) -
GPT-5.4 Routing: Added missing
targetFormat: "openai-responses"togpt-5.4andgpt-5.4-minimodels in both the Codex and GitHub Copilot providers, fixing[400]: model not accessible via /chat/completionserrors (#1114, by @ask33r)
[3.6.0] — 2026-04-10
✨ New Features & Analytics
-
feat(docs): integrate multi-page documentation into OmniRoute dashboard (#1969)
-
feat(settings): add request body limit setting (#1968)
-
feat(auth): add Gemini CLI OAuth client secret default (#1974)
-
feat(models): expose models.dev context windows in /v1/models (#1972)
-
fix(db): resolve legacy encryption fallback causing re-encryption loops (#1941)
-
fix(auth): fix Codex assistant final_answer response sanitization (#1965)
-
feat(providers): Implement Image Generation and Editing capabilities for ChatGPT Web, including in-band chat image generation and caching (#1606).
-
feat(ui): Integrate OpenCode Zen/Go API tool logo SVG and polish API key copy-to-clipboard interactions (#1607).
-
feat(providers): Integrate AgentRouter as a new OpenAI-compatible passthrough provider with $200 free credits via sign-up (Issue #1572).
-
feat(ui): Implement on-demand per-model testing in the provider dashboard, allowing single-token diagnostic checks without triggering rate-limits (Issue #1532).
-
Combo Smoke Test: Raised the default token budget to 2048 to prevent truncation of thinking models during preflight checks, and fully randomized the arithmetic probe prompt to bypass deterministic caching from upstream relays (#1105)
🐛 Bug Fixes & Compliance
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
DB Bloat / Row Limits: Added
CALL_LOGS_TABLE_MAX_ROWSandPROXY_LOGS_TABLE_MAX_ROWS(default: 100,000) to the backend DB compliance cleaner to prevent runaway SQLite growth. Limits are enforced automatically on the TTL cycle (#1104, fixes #1101) -
HTML Error Handling: The router now correctly identifies unexpected HTML responses (e.g.
<!DOCTYPE html>) sent by upstream providers (like Azure/Copilot) instead of throwing obscureUnexpected token '<'JSON parse errors, bubbling up a clean 502 Bad Gateway (#1104, fixes #1066) -
Android/Termux SQLite Native Support:
better-sqlite3is now correctly built from source with cross-compilation flags in ARM64 local Termux deployments without failing on missing prebuilt binaries (#1107)
[3.5.9] — 2026-04-09
✨ New Features
-
feat(docs): integrate multi-page documentation into OmniRoute dashboard (#1969)
-
feat(settings): add request body limit setting (#1968)
-
feat(auth): add Gemini CLI OAuth client secret default (#1974)
-
feat(models): expose models.dev context windows in /v1/models (#1972)
-
fix(db): resolve legacy encryption fallback causing re-encryption loops (#1941)
-
fix(auth): fix Codex assistant final_answer response sanitization (#1965)
-
feat(providers): Implement Image Generation and Editing capabilities for ChatGPT Web, including in-band chat image generation and caching (#1606).
-
feat(ui): Integrate OpenCode Zen/Go API tool logo SVG and polish API key copy-to-clipboard interactions (#1607).
-
feat(providers): Integrate AgentRouter as a new OpenAI-compatible passthrough provider with $200 free credits via sign-up (Issue #1572).
-
feat(ui): Implement on-demand per-model testing in the provider dashboard, allowing single-token diagnostic checks without triggering rate-limits (Issue #1532).
-
Persistent Combo Ordering: Drag combo cards by handle to reorder them in the dashboard; order is persisted to SQLite via a new
sort_ordercolumn andPOST /api/combos/reorderendpoint. Includes DB migration020_combo_sort_order.sqland JSON import preservation (#1095) -
Sidebar Group Reorder: Moved "Logs" before "Health" in the System section and "Limits & Quotas" after "Cache" in the Primary section for a more logical navigation flow (#1095)
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
Stream Failure Surfacing: Upstream
response.failedevents (e.g. Codex rate-limit errors) are now properly surfaced as non-200 errors instead of being silently swallowed as empty 200 OK streams. Rate-limit failures return HTTP 429 (#1098, closes #1093) -
Upstream Model Preservation: The Responses-to-OpenAI stream translator now preserves the actual upstream model (e.g.
gpt-5.4) instead of hardcoding agpt-4fallback (#1098, closes #1094) -
Docker EXDEV Fix:
build-next-isolated.mjsnow falls back fromfs.rename()tocp/rmwhen Docker buildx raisesEXDEV(cross-device link), unblocking the Docker image publish workflow (#1097) -
macOS CLI Path Resolution:
cliRuntime.tsresolves symlink parents withfs.realpath()to handle macOS/var→/private/varchains, preventing falsesymlink_escaperejections (#1097) -
Request Log Token Layout: Split token badges into separate Input (Total In, Cache Read, Cache Write) and Output (Total Out, Reasoning) groups for clearer readability; renamed "Time" label to "Completed Time" (#1096)
[3.5.8] — 2026-04-09
✨ New Features & Analytics
-
feat(docs): integrate multi-page documentation into OmniRoute dashboard (#1969)
-
feat(settings): add request body limit setting (#1968)
-
feat(auth): add Gemini CLI OAuth client secret default (#1974)
-
feat(models): expose models.dev context windows in /v1/models (#1972)
-
fix(db): resolve legacy encryption fallback causing re-encryption loops (#1941)
-
fix(auth): fix Codex assistant final_answer response sanitization (#1965)
-
feat(providers): Implement Image Generation and Editing capabilities for ChatGPT Web, including in-band chat image generation and caching (#1606).
-
feat(ui): Integrate OpenCode Zen/Go API tool logo SVG and polish API key copy-to-clipboard interactions (#1607).
-
feat(providers): Integrate AgentRouter as a new OpenAI-compatible passthrough provider with $200 free credits via sign-up (Issue #1572).
-
feat(ui): Implement on-demand per-model testing in the provider dashboard, allowing single-token diagnostic checks without triggering rate-limits (Issue #1532).
-
Analytics Layout Redesign: Replaced flat metrics with a responsive
CompactStatGrid, grouping data visually across sections (#1089)
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
Build Core: Force Turbopack cleanup via Prepbulish script to prevent Next.js 16 app/ routing conflicts on runtime.
-
Provider Quarantine: Introduces model/provider circuit-breakers with adaptive TTL exponential backoff for recurring upstream errors (#1090)
-
Oauth Keep-Alive: Safely protects authenticated active accounts against spontaneous dropping from router due to transient token refresh failures (#1085)
🔒 Security & Maintenance
- Dependabot: bumped axios from 1.14.0 to 1.15.0 addressing SSRF flags (#1088)
[3.5.7] — 2026-04-09
🐛 Bug Fixes & Security
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
Turbopack Standalone Chunks: Fixed a critical bug in
scripts/prepublish.mjswhere Turbopack chunks missing from the.next/standalonetrace resulted in a500 ChunkLoadError(e.g.,_not-foundpage crash) during production deployments via NPM or Docker. Standalone chunks are now explicitly copied and correctly stripped of Turbopack hashes.
[3.5.6] — 2026-04-09
✨ New Features
-
feat(docs): integrate multi-page documentation into OmniRoute dashboard (#1969)
-
feat(settings): add request body limit setting (#1968)
-
feat(auth): add Gemini CLI OAuth client secret default (#1974)
-
feat(models): expose models.dev context windows in /v1/models (#1972)
-
fix(db): resolve legacy encryption fallback causing re-encryption loops (#1941)
-
fix(auth): fix Codex assistant final_answer response sanitization (#1965)
-
feat(providers): Implement Image Generation and Editing capabilities for ChatGPT Web, including in-band chat image generation and caching (#1606).
-
feat(ui): Integrate OpenCode Zen/Go API tool logo SVG and polish API key copy-to-clipboard interactions (#1607).
-
feat(providers): Integrate AgentRouter as a new OpenAI-compatible passthrough provider with $200 free credits via sign-up (Issue #1572).
-
feat(ui): Implement on-demand per-model testing in the provider dashboard, allowing single-token diagnostic checks without triggering rate-limits (Issue #1532).
-
Email Privacy Masking: OAuth account emails are now masked in the provider dashboard (e.g.
di*****@g****.com) to prevent accidental exposure when sharing screenshots. Full address visible on hover viatitleattribute (#1025). -
OpenRouter & GitHub in Embedding/Image Registries: OpenRouter (3 embedding models, 4 image models) and GitHub Models (2 embedding models via Azure inference) are now first-class entries in the provider registries, enabling their use for
/v1/embeddingsand/v1/images/generations(#960). -
Model Visibility Toggle & Search Filter: The provider page model list now includes a real-time search/filter bar and a per-model visibility toggle (👁 icon). Hidden models are grayed out and excluded from the
/v1/modelscatalog. An active-count badge (N/M active) shows at a glance how many models are enabled (#750). -
Chinese Localization (zh-CN): Added missing translations for Context Relay, Memory, LKGP, and Models.dev sync features, while standardizing terminology across the application (#1079).
-
Environment Auto-Sync: Added
sync-env.mjsto auto-generate and append.envfrom.env.exampleduring installation, automatically generating cryptographic secrets on first run. -
Source Mode Dashboard Update: Fixed real-time Source (git-checkout) updating in the dashboard, enabling secure, real-time update pipelines for non-NPM installations.
🐛 Bug Fixes & Security
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
Hardcoded Secret Cleanup: Removed 12 hardcoded OAuth credential fallbacks from the source code, forcing secure reliance on environment variables and resolving static analysis security alerts.
-
Next.js Security Patch: Bumped
nextfrom 16.2.2 to 16.2.3 to resolve critical RSC deserialization RCE vulnerability (SNYK-JS-NEXT-15954202). -
Memory/Cache UI Crash: Added null-safety guards (
?? 0) to.toLocaleString()calls in Memory and Cache dashboard pages, preventingTypeErrorcrashes when database tables are empty or contain null numeric values (#1083). -
WebSearch tool_choice Translation: Fixed OpenAI-to-Claude translator dropping
tool_choiceobjects withtype: "function"as-is, which Claude rejects. Now properly maps all OpenAItool_choicevariants (function,required,none) to Claude-compatible format (tool,any,auto), fixing "Did 0 searches" in Claude Code WebSearch (#1072). -
Provider Validation baseUrl Override: Added
baseUrlpassthrough from frontend validation requests to the backend validation endpoint. Chinese-site users of Alibaba Coding Plan (bailian-coding-plan) can now validate API keys against their custom Base URL instead of always hitting the international endpoint (#1078). -
Minimax Auth Header: Switched Minimax provider from
x-api-keytoAuthorization: Bearerheader format, matching the current API spec (#1076). -
Native Fetch Fallback: Added graceful fallback to native
fetchwhen theundicidispatcher fails, improving resilience in environments where undici is unavailable (#1054). -
EPIPE Flood Fix: Added circuit-breaker logic to prevent EPIPE errors from creating a feedback loop that fills logs at GB/s (#1006).
-
Qoder PAT Validation: Improved Qoder Personal Access Token validation with actionable error messages that guide users to the correct token format (#966).
-
CI/CD Pipeline: Fixed
check:docs-syncfailure by syncing OpenAPI version to 3.5.6 and finalizing CHANGELOG release heading. Commented outDATA_DIRin.env.exampleto prevent E2E test failures in CI runners lacking root permissions.
🌍 i18n
- Auto Language Generation (CI): Added CI pipeline to auto-generate missing language files and strings via
feat(CI,i18n)workflow, covering 30+ locales (#1071).
[3.5.5] — 2026-04-08
✨ New Features
-
feat(docs): integrate multi-page documentation into OmniRoute dashboard (#1969)
-
feat(settings): add request body limit setting (#1968)
-
feat(auth): add Gemini CLI OAuth client secret default (#1974)
-
feat(models): expose models.dev context windows in /v1/models (#1972)
-
fix(db): resolve legacy encryption fallback causing re-encryption loops (#1941)
-
fix(auth): fix Codex assistant final_answer response sanitization (#1965)
-
feat(providers): Implement Image Generation and Editing capabilities for ChatGPT Web, including in-band chat image generation and caching (#1606).
-
feat(ui): Integrate OpenCode Zen/Go API tool logo SVG and polish API key copy-to-clipboard interactions (#1607).
-
feat(providers): Integrate AgentRouter as a new OpenAI-compatible passthrough provider with $200 free credits via sign-up (Issue #1572).
-
feat(ui): Implement on-demand per-model testing in the provider dashboard, allowing single-token diagnostic checks without triggering rate-limits (Issue #1532).
-
Node.js 24 Compatibility Warning: Added a proactive version incompatibility warning on the login page to guide users to the stable Node.js 22 LTS, preventing native sqlite binding crashes.
-
Context Relay Combo Strategy: Added the new
context-relaycombo strategy with priority-style routing, structured handoff summary generation once quota usage reaches the warning threshold, and handoff injection after the next real account switch. -
Global Context Relay Defaults: Added global Settings defaults plus combo-level configuration for
handoffThreshold,handoffModel, andhandoffProviders, so new or unconfigured combos can inherit the feature consistently.
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
Proxy Connection Healthchecks: Applied proxy resolution per connection in the sweeping loop (
tokenHealthCheck.ts) and global provider validation sweeps, resolving Node 22 bypass and improving proxy stability (#1051, #1056, #1061). -
Security Vulnerability Remediation: Resolved multiple CodeQL scanning alerts including SSRF in model sync, insecure randomness in web crypto (
generateSessionId), and incomplete URL sanitization. -
Context Relay Typing & Synchronization: Reverted out-of-scope test breakages and resolved
handoffProviderand responseinputextraction payload typing. -
Legacy OpenAI-Compatible Responses Routing: Fixed legacy/imported OpenAI-compatible providers (for example
openai-compatible-sp-openai) incorrectly routing Chat Completions traffic to/chat/completionswhen the real provider node was configured asapiType: "responses". OmniRoute now treatsproviderSpecificData.apiTypeas authoritative across routing, executors, and translator tools, avoiding false empty-content failures during combo/provider smoke tests (#1069). -
Gemini PDF Attachment Integration: Fixed payload generation and format for parsing
inline_dataand generic base64 sources for deep Gemini PDF routing (#993, #1021). -
Vercel AI SDK Fallbacks: Mapped
max_output_tokenstomax_tokensfor strict OpenAI-compatible providers, resolving errors from standard AI agents and frameworks (#994). -
External Auth & UI Reliability: Handled null
statefailures in Cline OAuth exchange (#1016), added 3rd-party 400 error patterns to combo fallback (#1024), and resolved desktop sidebar layout and popover overflows (#1039, #1001). -
Context Relay In-Flight Deduplication: Prevented duplicate handoff generation for the same session/combo while an earlier summary request is still in flight.
-
Context Relay Provider Gating: Aligned runtime behavior with configuration so explicit
handoffProvidersexclusions, including an empty array, now disable handoff generation as expected.
🛠️ Maintenance & Dependabot
- Updated Sub-dependencies: Bumped
honoto4.12.12and@hono/node-serverto1.19.13to patch critical security gaps (#1063, #1064, #1067, #1068).
📚 Documentation
- Documentation Synchronization: Updated system documentation (README, Architecture, Features, Tools, Troubleshooting) and synced
i18nconfigurations to match the v3.5.5 context relay patterns and proxy troubleshooting steps. - Context Relay Delivery Notes: Documented the current architecture, runtime flow, and Codex-focused scope in the feature docs, changelog, and agent guidance.
[3.5.4] — 2026-04-07
✨ New Features
-
feat(docs): integrate multi-page documentation into OmniRoute dashboard (#1969)
-
feat(settings): add request body limit setting (#1968)
-
feat(auth): add Gemini CLI OAuth client secret default (#1974)
-
feat(models): expose models.dev context windows in /v1/models (#1972)
-
fix(db): resolve legacy encryption fallback causing re-encryption loops (#1941)
-
fix(auth): fix Codex assistant final_answer response sanitization (#1965)
-
feat(providers): Implement Image Generation and Editing capabilities for ChatGPT Web, including in-band chat image generation and caching (#1606).
-
feat(ui): Integrate OpenCode Zen/Go API tool logo SVG and polish API key copy-to-clipboard interactions (#1607).
-
feat(providers): Integrate AgentRouter as a new OpenAI-compatible passthrough provider with $200 free credits via sign-up (Issue #1572).
-
feat(ui): Implement on-demand per-model testing in the provider dashboard, allowing single-token diagnostic checks without triggering rate-limits (Issue #1532).
-
Detailed Token Tracking: Added granular token breakdown columns (cache read, cache write, reasoning) to call logs with proper null vs zero distinction. Includes DB migration 018 and 5-label UI display per provider capability (#1017 — thanks @rdself).
-
Legacy JSON Config Import/Export: Restored JSON-based settings export and import for migration from legacy configurations. Security-hardened with Zero-Trust redaction of passwords and
requireLoginfields, and automatic pre-import database backups (#1012 — thanks @luandiasrj). -
Non-Stream Aliases: Added API support for explicit non-streaming aliases (
non_stream,disable_stream,disable_streaming,streaming=false), normalized at the boundary before provider translation (#1036 — thanks @wlfonseca). -
Russian Dashboard Localization: Comprehensive Russian translation for the dashboard UI, including fixes for 2 Ukrainian locale keys (#1003 — thanks @mercs2910).
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
Anthropic Streaming Input Undercount: Fixed a critical bug where Anthropic streaming
prompt_tokensonly reported non-cached tokens (e.g.,in=3when actual total was 113,616). Cache tokens are now summed into prompt_tokens during streaming (#1017). -
Built-in Responses API Tool Types: Preserved built-in Responses API tools (
web_search,file_search,computer,code_interpreter,image_generation) from being silently stripped by the empty-name tool filter — these tools carry no.namefield (#1014 — thanks @rdself). -
Cursor/Codex Responses Compatibility: Fixed empty output in Cursor when using Codex models by hoisting system input items to
instructions, sanitizing invalid tool names, and detecting Responses-format payloads on chat/completions endpoint (#1002 — thanks @mercs2910). -
OAuth Token Expiry Display: Fixed OAuth connections showing "expired" badge even with valid tokens by reading
tokenExpiresAt(updated on refresh) instead ofexpiresAt(original grant timestamp) (#1032 — thanks @tombii). -
Codex Fast-Tier Copy: Corrected dashboard settings copy from
service_tier=fasttoservice_tier=priority, matching the actual Codex wire format (#1045 — thanks @kfiramar). -
macOS Desktop App Startup: Stabilized packaged macOS app launch by excluding desktop artifacts from the standalone bundle and improving launch path detection (#1004 — thanks @mercs2910).
-
macOS Sidebar Layout: Fixed macOS traffic light overlap, sidebar spacing, and button overflow in the Electron desktop app (#1001 — thanks @mercs2910).
⚡ Performance
- Analytics Page Load: Dramatically reduced analytics page load times (30s→1-2s for 50K entries) via date-filtered DB queries, parallel
Promise.all()cost calculations, and merged 6 COUNT queries into a single CASE WHEN aggregate (#1038 — thanks @oyi77).
🔒 Security & Dependencies
- Node Base Image: Upgraded Docker base from
22-bookworm-slimto22.22.2-trixie-slim(#1011 — Snyk). - Production Dependencies: Bumped 5 production dependencies (#1044 — Dependabot).
- Vite: Bumped from 8.0.3 to 8.0.5 (#1031 — Dependabot).
- Development Dependencies: Bumped 4 development dependencies (#1030 — Dependabot).
🧪 Tests
- Token Accounting Tests: Added 18 new unit tests covering detailed token breakdown, null vs zero semantics, per-provider token extraction, and Anthropic streaming input fix (#1017).
- Built-in Tool Tests: Added 3 new test cases for built-in Responses API tool type preservation (#1014).
- ChatCore Sanitization: Updated sanitization tests to accommodate Responses format detection (PR #1002) and built-in tool preservation (PR #1014).
🛠️ Maintenance
- PR Workflow: Updated
/review-prsworkflow to merge PRs into the release branch (release/vX.Y.Z) instead of directly intomain, ensuring proper pre-release staging.
Coverage
- 2537 tests, 2532 passing — Statement coverage: 91.95%, Branch coverage: 78.79%, Function coverage: 93.19%
[3.5.3] - 2026-04-07
Security
- Vulnerabilities: Fully remediated 12 High-Severity CodeQL vulnerabilities by migrating from Math.random to
crypto.randomUUID(), wrapping SSE injection points with aggressive backslash escaping, sanitizing trailing HTTP fragments, and enforcing rigid SSRF HTTP verification schemes across internal routes. - Dependencies: Upgraded Next.js to
^16.2.2and Vite to>=8.0.5resolving critical DoS, arbitrary file reads and CSRF vectors in the build/server environments.
Fixed
- E2E Stability: Eliminated extreme CI unreliability and transient test timeouts (Playwright) by propagating internal standalone
_next/staticassets properly and refactoring deep UI interactions inside defensiveexpect().toPass()loops. - Middleware: Resolved infinite redirect loop on dashboard for fresh instances when requireLogin is disabled.
- Core Fallbacks: Preserved primary failure contexts and enhanced Edge-case error handling pipelines across chat and fallback loops.
- Proxy/Hooks: Optimized local git hooks, normalized token coverage endpoints into
/coverage, and guarded GLM region lookups.
🛠️ Maintenance
- CI/CD Stabilization: Prevented random GitHub Runner freezes by decoupling sharded processes, adjusting test concurrencies, unref-ing active connections on server teardown, and strictly capping job timeout durations.
Documentation
- I18n Engine: Synchronized and pushed deep Machine Translation updates across all 32 natively-supported languages (682 translation nodes aligned).
Coverage
- Testing: Consolidated the workspace test coverage framework hitting 92.1% statement line coverage, with new rigid unit-tests matching API key policies and tool scopes.
[3.5.2] — 2026-04-05
✨ New Features
-
feat(docs): integrate multi-page documentation into OmniRoute dashboard (#1969)
-
feat(settings): add request body limit setting (#1968)
-
feat(auth): add Gemini CLI OAuth client secret default (#1974)
-
feat(models): expose models.dev context windows in /v1/models (#1972)
-
fix(db): resolve legacy encryption fallback causing re-encryption loops (#1941)
-
fix(auth): fix Codex assistant final_answer response sanitization (#1965)
-
feat(providers): Implement Image Generation and Editing capabilities for ChatGPT Web, including in-band chat image generation and caching (#1606).
-
feat(ui): Integrate OpenCode Zen/Go API tool logo SVG and polish API key copy-to-clipboard interactions (#1607).
-
feat(providers): Integrate AgentRouter as a new OpenAI-compatible passthrough provider with $200 free credits via sign-up (Issue #1572).
-
feat(ui): Implement on-demand per-model testing in the provider dashboard, allowing single-token diagnostic checks without triggering rate-limits (Issue #1532).
-
Qoder API Native Integration: Completely refactored the Qoder Executor to bypass the legacy COSY AES/RSA encryption algorithm, routing directly into the native DashScope OpenAi-compatible URL. Eliminates complex dependencies on Node
cryptomodules while improving stream fidelity. -
Resilience Engine Overhaul: Integrated context overflow graceful fallbacks, proactive OAuth token detection, and empty-content emission prevention (#990).
-
Context-Optimized Routing Strategy: Added new intelligent routing capability to natively maximize context windows in automated combo deployments (#990).
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
Responses API Stream Corruption: Fixed deep-cloning corruption where Anthropic/OpenAI translation boundaries stripped
response.specific SSE prefixes from streaming boundaries (#992). -
Claude Cache Passthrough Alignment: Aligned CC-Compatible cache markers consistently with upstream Client Pass-Through mode preserving prompt caching.
-
Turbopack Memory Leak: Pinned Next.js to strict
16.0.10preventing memory leaks and build staleness from recent upstream Turbopack hashed module regressions (#987).
[3.5.1] — 2026-04-04
✨ New Features
-
feat(docs): integrate multi-page documentation into OmniRoute dashboard (#1969)
-
feat(settings): add request body limit setting (#1968)
-
feat(auth): add Gemini CLI OAuth client secret default (#1974)
-
feat(models): expose models.dev context windows in /v1/models (#1972)
-
fix(db): resolve legacy encryption fallback causing re-encryption loops (#1941)
-
fix(auth): fix Codex assistant final_answer response sanitization (#1965)
-
feat(providers): Implement Image Generation and Editing capabilities for ChatGPT Web, including in-band chat image generation and caching (#1606).
-
feat(ui): Integrate OpenCode Zen/Go API tool logo SVG and polish API key copy-to-clipboard interactions (#1607).
-
feat(providers): Integrate AgentRouter as a new OpenAI-compatible passthrough provider with $200 free credits via sign-up (Issue #1572).
-
feat(ui): Implement on-demand per-model testing in the provider dashboard, allowing single-token diagnostic checks without triggering rate-limits (Issue #1532).
-
Models.dev Integration: Integrated models.dev as the authoritative runtime source for model pricing, capabilities, and specifications, overriding hardcoded prices. Includes a settings UI to manage sync intervals, translation strings for all 30 languages, and robust test coverage.
-
Provider Native Capabilities: Added support for declaring and checking native API features (e.g.
systemInstructions_supported) preventing failures by sanitizing invalid roles. Currently configured for Gemini Base and Antigravity OAuth providers. -
API Provider Advanced Settings: Added per-connection custom
User-Agentoverrides for API-key provider connections. The override is stored inproviderSpecificData.customUserAgentand now applies to validation probes and upstream execution requests.
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
Qwen OAuth Reliability: Resolved a series of OAuth integration issues including a 400 Bad Request blocker on expired tokens, fallback generation for parsing OIDC
access_tokenproperties whenid_tokenis omitted, model catalog discovery errors, and strict filtering ofX-Dashscope-*headers to avoid 400 rejection from OpenAI-compatible endpoints.
[3.5.0] — 2026-04-03
✨ New Features
-
feat(docs): integrate multi-page documentation into OmniRoute dashboard (#1969)
-
feat(settings): add request body limit setting (#1968)
-
feat(auth): add Gemini CLI OAuth client secret default (#1974)
-
feat(models): expose models.dev context windows in /v1/models (#1972)
-
fix(db): resolve legacy encryption fallback causing re-encryption loops (#1941)
-
fix(auth): fix Codex assistant final_answer response sanitization (#1965)
-
feat(providers): Implement Image Generation and Editing capabilities for ChatGPT Web, including in-band chat image generation and caching (#1606).
-
feat(ui): Integrate OpenCode Zen/Go API tool logo SVG and polish API key copy-to-clipboard interactions (#1607).
-
feat(providers): Integrate AgentRouter as a new OpenAI-compatible passthrough provider with $200 free credits via sign-up (Issue #1572).
-
feat(ui): Implement on-demand per-model testing in the provider dashboard, allowing single-token diagnostic checks without triggering rate-limits (Issue #1532).
-
Auto-Combo & Routing: Completed native CRUD lifecycle integration for the advanced Auto-Combo engine (#955).
-
Core Operations: Fixed missing translations for new native Auto-Combos options (#955).
-
Security Validation: Disabled SQLite auto-backup tasks natively during unit test CI execution to explicitly resolve Node 22 Event Loop hanging memory leaks (#956).
-
Ecosystem Proxies: Completed explicit integration mapping model synchronization schedulers, OAuth cycles, and Token Check refreshes safely through OmniRoute's native system upstream proxies (#953).
-
MCP Extensibility: Added and successfully registered the new
omniroute_web_searchMCP framework tool out of beta into production schemas (#951). -
Tokens Buffer Logic: Added runtime configuration limits extending configurable input/output token buffers for precise Usage Tracking metrics (#959).
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
CodeQL Remediation: Fully resolved and secured critical string indexing operations preventing Server-Side Request Forgery (SSRF) arrays indexing heuristics alongside polynomial algorithmic backtracking (ReDoS) inside deep proxy dispatcher modules.
-
Crypto Hashes: Replaced weak unverified legacy OAuth 1.0 hashes with robust HMAC-SHA-256 standard validation primitives ensuring tight access controls.
-
API Boundary Protection: Correctly verified and mapped structural route protections enforcing strict
isAuthenticated()middleware logic covering newer dynamic endpoints targeting settings manipulation and native skills loading. -
CLI Ecosystem Compat: Resolved broken native runtime parser bindings crashing
whereenvironment detectors strictly over.cmd/.exeedge cases gracefully for external plugins (#969). -
Cache Architecture: Refactored exact Analytics and System Settings dashboard parameters layout structure caching to maintain stable re-hydration persistence cycles resolving visual unaligned state flashes (#952).
-
Claude Caching Standards: Normalized and accurately strictly preserved critical ephemeral block markers
ephemeralcaching TTL orders for downstream nodes enforcing standard compatible CC requests mapping cleanly without dropped metrics (#948). -
Internal Aliases Auth: Simplified internal runtime mappings normalizing Codex credential payload lookups inside global translation parameters resolving 401 unauthenticated drops (#958).
🛠️ Maintenance
- UI Discoverability: Correctly adjusted layout categorizations explicitly separating free tier providers logic improving UX sorting flows inside the general API registry pages (#950).
- Deployment Topology: Unified Docker deployment artifacts ensuring the root
fly.tomlmatches expected cloud instance parameters out-of-the-box natively handling automated deployments scaling properly. - Development Tooling: Decoupled
LKGPruntime parameters into explicit DB layer abstraction caching utilities ensuring strict test isolation coverage for core caching layers safely.
[3.4.9] — 2026-04-03
Features & Refactoring
- Dashboard Auto-Combo Panel: Completely refactored the
/dashboard/auto-comboUI to seamlessly integrate with native Dashboard Cards and standardized visual padding/headers. Added dynamic visual progress bars mapping model selection weight mechanisms. - Settings Routing Sync: Fully exposed advanced routing
priorityandweightedschema targets internally inside global settings fallback lists.
Bug Fixes
- Memory & Skills Locale Nodes: Resolved empty rendering tags for Memory and Skills options directly inside global settings views by wiring all
settings.*mapping values internally intoen.json(also mapped implicitly for cross-translation tools).
Internal Integrations
- Integrated PR #946 — fix: preserve Claude Code compatibility in responses conversion
- Integrated PR #944 — fix(gemini): preserve thought signatures across antigravity tool calls
- Integrated PR #943 — fix: restore GitHub Copilot body
- Integrated PR #942 — Fix cc-compatible cache markers
- Integrated PR #941 — refactor(auth): improve NVIDIA alias lookup + add LKGP error logging
- Integrated PR #939 — Restore Claude OAuth localhost callback handling
- (Note: PR #934 was omitted from 3.4.9 cycle to prevent core conflict regressions)
[3.4.8] — 2026-04-03
Security
- Fully remediated all outstanding Github Advanced Security (CodeQL) findings and Dependabot alerts.
- Fixed insecure randomness vulnerabilities by migrating from
Math.randomtocrypto.randomUUID(). - Secured shell commands in automated scripts from string injection.
- Migrated vulnerable catastrophic backtracking RegEx parsing patterns in chat/translation pipelines.
- Enhanced output sanitization controls inside React UI components and Server Sent Events (SSE) tag injection.
[3.4.7] — 2026-04-03
Features
- Added
Cryptographynode to Monitoring and MCP health checks (#798) - Hardened model-catalog route permissions mapping (
/models) (#781)
Bug Fixes
- Fixed Claude OAuth token refreshes failing to preserve cache contexts (#937)
- Fixed CC-Compatible provider errors rendering cached models unreachable (#937)
- Fixed GitHub Executor errors related to invalid context arrays (#937)
- Fixed NPM-installed CLI tools healthcheck failures on Windows (#935)
- Fixed payload translation dropping valid content due to invalid API fields (#927)
- Fixed runtime crash in Node 25 regarding API key execution (#867)
- Fixed MCP standalone module-resolution (
ERR_MODULE_NOT_FOUND) viaesbuild(#936) - Fixed NVIDIA NIM routing credential resolution alias mismatch (#931)
Security
- Added safe strict input boundary protection against raw
shell: trueremote-code execution injections.
[3.4.6] - 2026-04-02
✨ New Features
-
feat(docs): integrate multi-page documentation into OmniRoute dashboard (#1969)
-
feat(settings): add request body limit setting (#1968)
-
feat(auth): add Gemini CLI OAuth client secret default (#1974)
-
feat(models): expose models.dev context windows in /v1/models (#1972)
-
fix(db): resolve legacy encryption fallback causing re-encryption loops (#1941)
-
fix(auth): fix Codex assistant final_answer response sanitization (#1965)
-
feat(providers): Implement Image Generation and Editing capabilities for ChatGPT Web, including in-band chat image generation and caching (#1606).
-
feat(ui): Integrate OpenCode Zen/Go API tool logo SVG and polish API key copy-to-clipboard interactions (#1607).
-
feat(providers): Integrate AgentRouter as a new OpenAI-compatible passthrough provider with $200 free credits via sign-up (Issue #1572).
-
feat(ui): Implement on-demand per-model testing in the provider dashboard, allowing single-token diagnostic checks without triggering rate-limits (Issue #1532).
-
Providers: Registered new image, video, and audio generation providers from the community-requested list (#926).
-
Dashboard UI: Added standalone sidebar navigation for the new Memory and Skills modules (#926).
-
i18n: Added translation strings and layout mappings across 30 languages for the Memory and Skills namespaces.
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
Resilience: Prevented the proxy Circuit Breaker from becoming stuck in an OPEN state indefinitely by handling direct transitions to CLOSED state inside fallback combo paths (#930).
-
Protocol Translation: Patched the streaming transformer to sanitize response blocks based on the expected source protocol rather than the provider target protocol, fixing Anthropics models wrapped in OpenAI payloads crashing Claude Code (#929).
-
API Specs & Gemini: Fixed
thought_signatureparsing inopenai-to-geminiandclaude-to-geminitranslators, preventing HTTP 400 errors across all Gemini 3 API tool-calls. -
Providers: Cleaned up non-OpenAI-compatible endpoints preventing valid upstream connections (#926).
-
Cache Trends: Fixed an invalid property mapping data mismatch causing Cache Trends UI charts to crash, and extracted redundant cache metric widgets (#926).
[3.4.5] - 2026-04-02
✨ New Features
-
feat(docs): integrate multi-page documentation into OmniRoute dashboard (#1969)
-
feat(settings): add request body limit setting (#1968)
-
feat(auth): add Gemini CLI OAuth client secret default (#1974)
-
feat(models): expose models.dev context windows in /v1/models (#1972)
-
fix(db): resolve legacy encryption fallback causing re-encryption loops (#1941)
-
fix(auth): fix Codex assistant final_answer response sanitization (#1965)
-
feat(providers): Implement Image Generation and Editing capabilities for ChatGPT Web, including in-band chat image generation and caching (#1606).
-
feat(ui): Integrate OpenCode Zen/Go API tool logo SVG and polish API key copy-to-clipboard interactions (#1607).
-
feat(providers): Integrate AgentRouter as a new OpenAI-compatible passthrough provider with $200 free credits via sign-up (Issue #1572).
-
feat(ui): Implement on-demand per-model testing in the provider dashboard, allowing single-token diagnostic checks without triggering rate-limits (Issue #1532).
-
CLIProxyAPI Ecosystem Integration: Added the
cliproxyapiexecutor with built-in module-level caching and proxy routing. Introduced a comprehensive Version Manager service to automatically test health, download binaries from GitHub, spawn isolated background processes, and cleanly manage the lifecycle of external CLI tools directly through the UI. Includes DB tables for proxy configuration to enable automatic SSRF-gated cross-routing of external OpenAI requests via the local CLI tool layer (#914, #915, #916). -
Qoder PAT Support: Integrated Personal Access Tokens (PAT) support directly via the local
qoderclitransport instead of legacy remote.cnbrowser configurations (#913). -
Gemini 3.1 Pro Preview (GitHub): Added
gemini-3.1-pro-previewcanonical explicit model support natively into the GitHub Copilot provider while preserving older routing aliases (#924).
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
GitHub Copilot Token Stability: Repaired the Copilot token refresh loop where stale tokens weren't deep-merged into DB, and removed
reasoning_textfields that were fatally breaking downstream Anthropic block conversions for multi-turn chats (#923). -
Global Timeout Matrix: Centralized and parameterized request timeouts explicitly from
REQUEST_TIMEOUT_MSto prevent hidden (~300s) default fetch buffers prematurely cutting off long-lived SSE streaming responses from heavy reasoning models (#918). -
Cloudflare Quick Tunnels State: Fixed a severe state inconsistency where restarted OmniRoute instances erroneously showed destroyed tunnels as active, and defaulted cloudflared tunneling to
HTTP/2to eliminate UDP receive buffer log spam (#925). -
i18n Translation Overhaul (Czech & Hindi): Fixed Hindi code from DEPRECATED
in.jsonto canonicalhi.json, overhauled Czech text mappings, extracteduntranslatable-keys.jsonto fix CI/CD false-positive validations, and generated comprehensiveI18N.mddocs to guide translators (#912). -
Tokens Provider Recovery: Fixed Qwen losing specific
resourceUrlendpoints after automatic health-check token refreshes because of missing DB deep merges (#917). -
CC Compatible UX & Streaming: Unified the Add CC/OpenAI/Anthropic compatible actions around the Anthropic UI treatment, forced CC-compatible upstream requests to use SSE while still returning streaming or non-streaming responses based on the client request, removed CC model-list configuration/import support in favor of an explicit unsupported-model-listing error, and made CC-compatible Available Models mirror the OAuth Claude Code registry list (#921).
[3.4.4] - 2026-04-02
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
Responses API Token Reporting: Emit
response.completedwith correctinput_tokens/output_tokensfields for Codex CLI clients, fixing token usage display (#909 — thanks @christopher-s). -
SQLite WAL Checkpoint on Shutdown: Flush WAL changes into the primary database file during graceful shutdown/restart, preventing data loss on Docker container stops (#905 — thanks @rdself).
-
Graceful Shutdown Signal: Changed
/api/restartand/api/shutdownroutes fromprocess.exit(0)toprocess.kill(SIGTERM), ensuring the shutdown handler runs before exit. -
Docker Stop Grace Period: Added
stop_grace_period: 40sto Docker Compose files and--stop-timeout 40to Docker run examples.
🛠️ Maintenance
- Closed 5 resolved/not-a-bug issues (#872, #814, #816, #890, #877).
- Triaged 6 issues with needs-info requests (#892, #887, #886, #865, #895, #870).
- Responded to CLI detection tracking issue (#863) with contributor guidance.
[3.4.3] - 2026-04-02
✨ New Features
-
feat(docs): integrate multi-page documentation into OmniRoute dashboard (#1969)
-
feat(settings): add request body limit setting (#1968)
-
feat(auth): add Gemini CLI OAuth client secret default (#1974)
-
feat(models): expose models.dev context windows in /v1/models (#1972)
-
fix(db): resolve legacy encryption fallback causing re-encryption loops (#1941)
-
fix(auth): fix Codex assistant final_answer response sanitization (#1965)
-
feat(providers): Implement Image Generation and Editing capabilities for ChatGPT Web, including in-band chat image generation and caching (#1606).
-
feat(ui): Integrate OpenCode Zen/Go API tool logo SVG and polish API key copy-to-clipboard interactions (#1607).
-
feat(providers): Integrate AgentRouter as a new OpenAI-compatible passthrough provider with $200 free credits via sign-up (Issue #1572).
-
feat(ui): Implement on-demand per-model testing in the provider dashboard, allowing single-token diagnostic checks without triggering rate-limits (Issue #1532).
-
Antigravity Memory & Skills: Completed remote memory and skills injection for the Antigravity provider at the proxy network level.
-
Claude Code Compatibility: Built a natively hidden compatibility bridge for Claude Code, passing tools and formatting through cleanly.
-
Web Search MCP: Added the
omniroute_web_searchtool with theexecute:searchscope. -
Cache Components: Implemented dynamic cache components utilizing TDD.
-
UI & Customization: Added custom favicon support, appearance tabs, wired whitelabeling to the sidebar, and added Windsurf guide steps across all 33 languages.
-
Log Retention: Unified request log retention and artifacts natively.
-
Model Enhancements: Added explicit
contextLengthfor all opencode-zen models. -
i18n & translations: Integrated 33 language translations natively, including placeholder CI validations and Chinese documentation updates (#873, #869).
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
Qwen OAuth Mapping: Reverted
id_tokenreliance toaccess_tokenand enabled dynamicresource_urlAPI endpoint injection for proper regional routing (#900). -
Model Sync Engine: Stored the strict internal Provider ID in
getCustomModels()sync routines instead of the UI Channel Alias format, preventing SQLite catalog insertion failures (#903). -
Claude Code & Codex: Standardized non-streaming blank responses to Anthropic-formatted
(empty response)to prevent CLI proxy crashes (#866). -
CC Compatible Routing: Resolved duplicate
/v1endpoint collision during path concatenation for generic Claude Code gateways (#904). -
Antigravity Dashboards: Blocked unlimited quota models from falsely registering as exhausted
100% Usagelimit states in the Provider Usage UI (#857). -
Claude Image Passthrough: Fixed Claude models missing image block passthroughs (#898).
-
Gemini CLI Routing: Resolved 403 authorization lockouts and content accumulation issues by refreshing the project ID via
loadCodeAssist(#868). -
Antigravity Stability: Corrected model access lists, enforced 404 lockouts, fixed 429 cascades locking out standard connections, and capped
gemini-3.1-prooutput tokens (#885). -
Provider Sync Cadence: Repaired the provider limits synchronization cadence via the internal scheduler (#888).
-
Dashboard Optimization: Resolved
/dashboard/limitsUI freezing when processing 70+ accounts via chunk parallelization (#784). -
SSRF Hardening: Enforced strict SSRF IP range filtering and blocked the
::1loopback interface. -
MIME Types: Standardized
mime_typeto snake_case to match Gemini API specifications. -
CI Stabilization: Fixed failing analytics/settings Playwright selectors and request assertions so GitHub Actions E2E runs pass reliably across localized UIs and switch-based controls.
-
Deterministic Tests: Removed date-sensitive quota fixtures from Copilot usage tests and aligned idempotency/model catalog tests with the merged runtime behavior.
-
MCP Type Hardening: Removed zero-budget explicit
anyregressions from the MCP server tool registration path. -
Model Sync Engine: Bypassed destructive
replaceoverrides when the provider's auto-sync yields an empty model list, maintaining stability for dynamic catalogs (#899).
🛠️ Maintenance
- Pipeline Logging: Refined pipeline logging artifacts and enforce retention caps (#880).
- AGENTS.md Overhaul: Condensed from 297→153 lines. Added build/test/style guidelines, code workflows (Prettier, TypeScript, ESLint), and trimmed verbose tables (#882).
- Release Branch Integration: Consolidated the active feature branches into
release/v3.4.2on top of currentmainand validated the branch with lint, unit, coverage, build, and CI-mode E2E runs. - Testing: Added vitest configuration for component testing and Playwright specs for settings toggles.
- Doc Updates: Expanded root readmes, translated chinese documents natively, and cleaned up obsolete files.
[3.4.1] - 2026-03-31
Warning
BREAKING CHANGE: request logging, retention, and logging environment variables have been redesigned. On the first startup after upgrading, OmniRoute archives legacy request logs from
DATA_DIR/logs/, legacyDATA_DIR/call_logs/, andDATA_DIR/log.txtintoDATA_DIR/log_archives/*.zip, then removes the deprecated layout and switches to the new unified artifact format underDATA_DIR/call_logs/.
✨ New Features
-
feat(docs): integrate multi-page documentation into OmniRoute dashboard (#1969)
-
feat(settings): add request body limit setting (#1968)
-
feat(auth): add Gemini CLI OAuth client secret default (#1974)
-
feat(models): expose models.dev context windows in /v1/models (#1972)
-
fix(db): resolve legacy encryption fallback causing re-encryption loops (#1941)
-
fix(auth): fix Codex assistant final_answer response sanitization (#1965)
-
feat(providers): Implement Image Generation and Editing capabilities for ChatGPT Web, including in-band chat image generation and caching (#1606).
-
feat(ui): Integrate OpenCode Zen/Go API tool logo SVG and polish API key copy-to-clipboard interactions (#1607).
-
feat(providers): Integrate AgentRouter as a new OpenAI-compatible passthrough provider with $200 free credits via sign-up (Issue #1572).
-
feat(ui): Implement on-demand per-model testing in the provider dashboard, allowing single-token diagnostic checks without triggering rate-limits (Issue #1532).
-
.ENV Migration Utility: Included
scripts/migrate-env.mjsto seamlessly migrate<v3.3configurations tov3.4.xstrict security validation constraints (FASE-01), repairing startup crashes caused by shortJWT_SECRETinstances. -
Kiro AI Cache Optimization: Implemented deterministic
conversationIdgeneration (uuidv5) to enable AWS Builder ID Prompt Caching properly across invocations (#814). -
Dashboard UI Restoration & Consolidation: Resolved sidebar logic omitting the Debug section, and cleared Nextjs routing warnings by moving standalone
/dashboard/mcpand/dashboard/a2apages explicitly into embedded Endpoint Proxy UI components. -
Unified Request Log Artifacts: Request logging now stores one SQLite index row plus one JSON artifact per request under
DATA_DIR/call_logs/, with optional pipeline capture embedded in the same file. -
Language: Improved the Chinese translation (#855)
-
Opencode-Zen Models: Added 4 free models to opencode-zen registry (#854)
-
Tests: Added unit and E2E tests for settings toggles and bug fixes (#850)
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
429 Quota Parsing: Parsed long quota reset times from error bodies to honor correct backoffs and prevent rate-limited account bans (#859)
-
Prompt Caching: Preserved client
cache_controlheaders for all Claude-protocol providers (like Minimax, GLM, and Bailian), correctly recognizing caching support (#856) -
Model Sync Logs: Reduced log spam by recording
sync-modelsonly when the channel actually modifies the list (#853) -
Provider Quota & Token Parsing: Switched Antigravity limits to use
retrieveUserQuotanatively and correctly mapped Claude token refresh payloads to URL-encoded forms (#862) -
Rate-Limiting Stability: Universalized the 429 Retry-After parsing architecture to cap provider-induced cooldowns at 24 hours max (#862)
-
Dashboard Limit Rendering: Re-architected
/dashboard/limitsquota mapping to render immediately inside chunks, fixing a major UI freezing delay on accounts exceeding 70 active connections (#784) -
QWEN OAuth Authorization: Mapped the OIDC
id_tokenas the primary API Bearer token for Dashscope requests, fixing immediate 401 Unauthorized errors after connecting accounts or refreshing tokens (#864) -
ZAI API Stability: Hardened Server-Sent Events compiler to gracefully fallback to empty strings when DeepSeek providers stream mathematically null content during reasoning phases (#871)
-
Claude Code/Codex Translations: Protected non-streaming payload conversions against empty responses from upstream Codex tools, avoiding catastrophic TypeErrors (#866)
-
NVIDIA NIM Rendering: Conditionally stripped identical provider prefixes dynamically pushed by audio models, eliminating duplicate
nim/nimtag structures throwing 404 on the Media Playground (#872)
⚠️ Breaking Changes
- Request Log Layout: Removed the old multi-file
DATA_DIR/logs/request log sessions and theDATA_DIR/log.txtsummary file. New requests are written as single JSON artifacts inDATA_DIR/call_logs/YYYY-MM-DD/. - Logging Environment Variables: Replaced
LOG_*,ENABLE_REQUEST_LOGS,CALL_LOGS_MAX,CALL_LOG_PAYLOAD_MODE, andPROXY_LOG_MAX_ENTRIESwith the newAPP_LOG_*andCALL_LOG_RETENTION_DAYSconfiguration model. - Pipeline Toggle Setting: Replaced the legacy
detailed_logs_enabledsetting withcall_log_pipeline_enabled. New pipeline details are embedded inside the request artifact instead of being stored as separaterequest_detail_logsrecords.
🛠️ Maintenance
- Legacy Request Log Upgrade Backup: Upgrades now archive old
data/logs/, legacydata/call_logs/, anddata/log.txtlayouts intoDATA_DIR/log_archives/*.zipbefore removing the deprecated structure. - Streaming Usage Persistence: Streaming requests now write a single
usage_historyrow on completion instead of emitting a duplicate in-progress usage row with empty status metadata. - Logging Follow-up Cleanup: Pipeline logs no longer capture
SOURCE REQUEST, request artifact entries now honorCALL_LOG_MAX_ENTRIES, and application log archives now honorAPP_LOG_MAX_FILES.
[3.4.0] - 2026-03-31
🚀 Features
- Subscription Utilization Analytics: Added quota snapshot time-series tracking, Provider Utilization and Combo Health tabs with recharts visualizations, and corresponding API endpoints (#847)
- SQLite Backup Control: New
OMNIROUTE_DISABLE_AUTO_BACKUPenv flag to disable automatic SQLite backups (#846) - Model Registry Update: Injected
gpt-5.4-miniinto the Codex provider's array of models (#756) - Provider Limit Tracking: Track and display when provider rate limits were last refreshed per account (#843)
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
Qwen Auth Routing: Re-routed Qwen OAuth completions from the DashScope API to the Web Inference API (
chat.qwen.ai), resolving authorization failures (#844, #807, #832) -
Qwen Auto-Retry Loop: Added targeted 429 Quota Exceeded backoff handling inside
chatCoreprotecting burst requests -
Codex OAuth Fallback: Modern browser popup blocking no longer traps the user; it automatically falls back to manual URL entry (#808)
-
Claude Token Refresh: Anthropic's strict
application/jsonboundaries are now respected during token generation instead of encoded URLs (#836) -
Codex Messages Schema: Stripped purist
messagesinjects from native passthrough requests to avoid structural rejections from the ChatGPT upstream (#806) -
CLI Detection Size Limit: Safely bumped the Node binary scanning upper bound from 100MB to 350MB, allowing heavy standalone tools like Claude Code (229MB) and OpenCode (153MB) to be correctly detected by the VPS runtime (#809)
-
CLI Runtime Environment: Restored ability for CLI configurations to respect user override paths (
CLI_{PROVIDER}_BIN) bypassing strict path-bound discovery rules -
Nvidia Header Conflicts: Removed
prompt_cache_keyproperties from upstream headers when calling non-Anthropic providers (#848) -
Codex Fast Tier Toggle: Restored Codex service tier toggle contrast in light mode (#842)
-
Test Infrastructure: Updated
t28-model-catalog-updatestest that incorrectly expected the outdated DashScope endpoint for the Qwen native registry
[3.3.9] - 2026-03-31
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
Custom Provider Rotation: Integrated
getRotatingApiKeyinternally inside DefaultExecutor, ensuringextraApiKeysrotation triggers correctly for custom and compatible upstream providers (#815)
[3.3.8] - 2026-03-30
🚀 Features
- Models API Filtering: Endpoint
/v1/modelsnow dynamically filters its list based on the permissions tied to theAuthorization: Bearer <token>when restricted access is on (#781) - Qoder Integration: Native integration for Qoder AI natively replacing the legacy iFlow platform mappings (#660)
- Prompt Cache Tracking: Added tracking capabilities and frontend visualization (Stats card) for semantic and prompt caching in the Dashboard UI
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
Cache Dashboard Sizing: Improved the UI layout sizes and context headers for the advanced cache pages (#835)
-
Debug Sidebar Visibility: Fixed an issue where the debug toggle wouldn't correctly show/hide sidebar debug details (#834)
-
Gemini Model Prefixing: Modified the namespace fallback to properly route via
gemini-cli/instead ofgc/to respect upstream specs (#831) -
OpenRouter Sync: Improved compatibility synchronization to automatically ingest the available models catalog correctly from OpenRouter (#830)
-
Streaming Payloads Mapping: Reserialization of reasoning fields natively resolves conflict alias paths when output is streaming to edge devices
[3.3.7] - 2026-03-30
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
OpenCode Config: Restructured generated
opencode.jsonto use the@ai-sdk/openai-compatiblerecord-based schema withoptionsandmodelsas object maps instead of flat arrays, fixing config validation failures (#816) -
i18n Missing Keys: Added missing
cloudflaredUrlNoticetranslation key across all 30 language files to preventMISSING_MESSAGEconsole errors in the Endpoint page (#823)
[3.3.6] - 2026-03-30
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
Token Accounting: Included prompt cache tokens safely in historical usage inputs calculations for correct quota deductions (PR #822)
-
Combo Test Probes: Fixed combo testing logic false negatives by resolving parsing for reasoning-only responses and enabled massive parallelization via Promise.all (PR #828)
-
Docker Quick Tunnels: Embedded required ca-certificates inside the base runtime container to resolve Cloudflared TLS startup failures, and surfaced stdout network errors replacing generic exit codes (PR #829)
[3.3.5] - 2026-03-30
✨ New Features
-
feat(docs): integrate multi-page documentation into OmniRoute dashboard (#1969)
-
feat(settings): add request body limit setting (#1968)
-
feat(auth): add Gemini CLI OAuth client secret default (#1974)
-
feat(models): expose models.dev context windows in /v1/models (#1972)
-
fix(db): resolve legacy encryption fallback causing re-encryption loops (#1941)
-
fix(auth): fix Codex assistant final_answer response sanitization (#1965)
-
feat(providers): Implement Image Generation and Editing capabilities for ChatGPT Web, including in-band chat image generation and caching (#1606).
-
feat(ui): Integrate OpenCode Zen/Go API tool logo SVG and polish API key copy-to-clipboard interactions (#1607).
-
feat(providers): Integrate AgentRouter as a new OpenAI-compatible passthrough provider with $200 free credits via sign-up (Issue #1572).
-
feat(ui): Implement on-demand per-model testing in the provider dashboard, allowing single-token diagnostic checks without triggering rate-limits (Issue #1532).
-
Gemini Quota Tracking: Added real-time Gemini CLI quota tracking via the
retrieveUserQuotaAPI (PR #825) -
Cache Dashboard: Enhanced the Cache Dashboard to display prompt cache metrics, 24h trends, and estimated cost savings (PR #824)
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
User Experience: Removed invasive auto-opening OAuth modal loops on barren provider detailed pages (PR #820)
-
Dependency Updates: Bumped and locked down dependencies for development and production trees including Next.js 16.2.1, Recharts, and TailwindCSS 4.2.2 (PR #826, #827)
[3.3.4] - 2026-03-30
✨ New Features
-
feat(docs): integrate multi-page documentation into OmniRoute dashboard (#1969)
-
feat(settings): add request body limit setting (#1968)
-
feat(auth): add Gemini CLI OAuth client secret default (#1974)
-
feat(models): expose models.dev context windows in /v1/models (#1972)
-
fix(db): resolve legacy encryption fallback causing re-encryption loops (#1941)
-
fix(auth): fix Codex assistant final_answer response sanitization (#1965)
-
feat(providers): Implement Image Generation and Editing capabilities for ChatGPT Web, including in-band chat image generation and caching (#1606).
-
feat(ui): Integrate OpenCode Zen/Go API tool logo SVG and polish API key copy-to-clipboard interactions (#1607).
-
feat(providers): Integrate AgentRouter as a new OpenAI-compatible passthrough provider with $200 free credits via sign-up (Issue #1572).
-
feat(ui): Implement on-demand per-model testing in the provider dashboard, allowing single-token diagnostic checks without triggering rate-limits (Issue #1532).
-
A2A Workflows: Added deterministic FSM orchestrator for multi-step agent workflows.
-
Graceful Degradation: Added a new multi-layer fallback framework to preserve core functionality during partial system outages.
-
Config Audit: Added an audit trail with diff detection to track changes and enable configuration rollbacks.
-
Provider Health: Added provider expiration tracking with proactive UI alerts for expiring API keys.
-
Adaptive Routing: Added an adaptive volume and complexity detector to override routing strategies dynamically based on load.
-
Provider Diversity: Implemented provider diversity scoring via Shannon entropy to improve load distribution.
-
Auto-Disable Bounds: Added an Auto-Disable Banned Accounts setting toggle to the Resilience dashboard.
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
Codex & Claude Compatibility: Fixed UI fallbacks, patched Codex non-streaming integration issues, and resolved CLI runtime detection on Windows.
-
Release Automation: Expanded permissions required for the Electron App build in GitHub Actions.
-
Cloudflare Runtime: Addressed correct runtime isolation exit codes for Cloudflared tunnel components.
🧪 Tests
- Test Suite Updates: Expanded test coverage for volume detectors, provider diversity, configuration audit, and FSM.
[3.3.3] - 2026-03-29
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
CI/CD Reliability: Patched GitHub Actions to stable dependency versions (
actions/checkout@v4,actions/upload-artifact@v4) to mitigate unannounced builder environment deprecations. -
Image Fallbacks: Replaced arbitrary fallback chains in
ProviderIcon.tsxwith explicit asset validation to prevent UI loading<Image>components for files that don't exist, eliminating404errors in dashboard console logs (#745). -
Admin Updater: Dynamic source-installation detection for the dashboard Updater. Safely disables the
Update Nowbutton when OmniRoute is built locally rather than through npm, prompting forgit pull(#743). -
Update ERESOLVE Error: Injected
package.jsonoverrides forreact/react-domand enabled--legacy-peer-depswithin the internal automatic updater scripts to resolve breaking dependency tree conflicts with@lobehub/ui.
[3.3.2] - 2026-03-29
✨ New Features
-
feat(docs): integrate multi-page documentation into OmniRoute dashboard (#1969)
-
feat(settings): add request body limit setting (#1968)
-
feat(auth): add Gemini CLI OAuth client secret default (#1974)
-
feat(models): expose models.dev context windows in /v1/models (#1972)
-
fix(db): resolve legacy encryption fallback causing re-encryption loops (#1941)
-
fix(auth): fix Codex assistant final_answer response sanitization (#1965)
-
feat(providers): Implement Image Generation and Editing capabilities for ChatGPT Web, including in-band chat image generation and caching (#1606).
-
feat(ui): Integrate OpenCode Zen/Go API tool logo SVG and polish API key copy-to-clipboard interactions (#1607).
-
feat(providers): Integrate AgentRouter as a new OpenAI-compatible passthrough provider with $200 free credits via sign-up (Issue #1572).
-
feat(ui): Implement on-demand per-model testing in the provider dashboard, allowing single-token diagnostic checks without triggering rate-limits (Issue #1532).
-
Cloudflare Tunnels: Cloudflare Quick Tunnel integration with dashboard controls (PR #772).
-
Diagnostics: Semantic cache bypass for combo live tests (PR #773).
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
Streaming Stability: Apply
FETCH_TIMEOUT_MSto streaming requests' initialfetch()call to prevent 300s Node.js TCP timeout causing silent task failures (#769). -
i18n: Add missing
windsurfandcopilotentries totoolDescriptionsacross all 33 locale files (#748). -
GLM Coding Audit: Complete provider audit fixing ReDoS vulnerabilities, context window sizing (128k/16k), and model registry syncing (PR #778).
[3.3.1] - 2026-03-29
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
OpenAI Codex: Fallback processing fix for
type: "text"elements carrying null or empty datasets that caused 400 rejection (#742). -
Opencode: Update schema alignment to singular
providerto match official spec (#774). -
Gemini CLI: Inject missing end-user quota headers preventing 403 authorization lockouts (#775).
-
DB Recovery: Refactor multipart payload imports into raw binary buffered arrays to bypass reverse proxy max body limits (#770).
[3.3.0] - 2026-03-29
✨ Enhancements & Refactoring
- Release Stabilization — Finalized v3.2.9 release (combo diagnostics, quality gates, Gemini tool fix) and created missing git tag. Consolidated all staged changes into a single atomic release commit.
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
Auto-Update Test — Fixed
buildDockerComposeUpdateScripttest assertion to match unexpanded shell variable references ($TARGET_TAG,${TARGET_TAG#v}) in the generated deploy script, aligning with the refactored template from v3.2.8. -
Circuit Breaker Test — Hardened
combo-circuit-breaker.test.mjsby injectingmaxRetries: 0to prevent retry inflation from skewing failure count assertions during breaker state transitions.
[3.2.9] - 2026-03-29
✨ Enhancements & Refactoring
- Combo Diagnostics — Introduced a live test bypass flag (
forceLiveComboTest) allowing administrators to execute real upstream health checks that bypass all local circuit-breaker and cooldown state mechanisms, enabling precise diagnostics during rolling outages (PR #759) - Quality Gates — Added automated response quality validation for combos and officially integrated
claude-4.6model support into the core routing schemas (PR #762)
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
Tool Definition Validation — Repaired Gemini API integration by normalizing enum types inside tool definitions, preventing upstream HTTP 400 parameter errors (PR #760)
[3.2.8] - 2026-03-29
✨ Enhancements & Refactoring
- Docker Auto-Update UI — Integrated a detached background update process for Docker Compose deployments. The Dashboard UI now seamlessly tracks update lifecycle events combining JSON REST responses with SSE streaming progress overlays for robust cross-environment reliability.
- Cache Analytics — Repaired zero-metrics visualization mapping by migrating Semantic Cache telemetry logs directly into the centralized tracking SQLite module.
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
Authentication Logic — Fixed a bug where saving dashboard settings or adding models failed with a 401 Unauthorized error when
requireLoginwas disabled. API endpoints now correctly evaluate the global authentication toggle. Resolved global redirection by reactivatingsrc/middleware.ts. -
CLI Tool Detection (Windows) — Prevented fatal initialization exceptions during CLI environment detection by catching
cross-spawnENOENT errors correctly. Adds explicit detection paths for\AppData\Local\droid\droid.exe. -
Codex Native Passthrough — Normalized model translation parameters preventing context poisoning in proxy pass-through mode, enforcing generic
store: falseconstraints explicitly for all Codex-originated requests. -
SSE Token Reporting — Normalized provider tool-call chunk
finish_reasondetection, fixing 0% Usage analytics for stream-only responses missing strict<DONE>indicators. -
DeepSeek Tags — Implemented an explicit
<think>extraction mapping insideresponsesHandler.ts, ensuring DeepSeek reasoning streams map equivalently to native Anthropic<thinking>structures.
[3.2.7] - 2026-03-29
Fixed
- Seamless UI Updates: The "Update Now" feature on the Dashboard now provides live, transparent feedback using Server-Sent Events (SSE). It performs package installation, native module rebuilds (better-sqlite3), and PM2 restarts reliably while showing real-time loaders instead of silently hanging.
[3.2.6] — 2026-03-29
✨ Enhancements & Refactoring
- API Key Reveal (#740) — Added a scoped API key copy flow in the Api Manager, protected by the
ALLOW_API_KEY_REVEALenvironment variable. - Sidebar Visibility Controls (#739) — Admins can now hide any sidebar navigation link via the Appearance settings to reduce visual clutter.
- Strict Combo Testing (#735) — Hardened the combo health check endpoint to require live text responses from models instead of just soft reachability signals.
- Streamed Detailed Logs (#734) — Switched detailed request logging for SSE streams to reconstruct the final payload, saving immense amounts of SQLite database size and significantly cleaning up the UI.
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
OpenCode Go MiniMax Auth (#733) — Corrected the authentication header logic for
minimaxmodels on OpenCode Go to usex-api-keyinstead of standard bearer tokens across the/messagesprotocol.
[3.2.5] — 2026-03-29
✨ Enhancements & Refactoring
- Void Linux Deployment Support (#732) — Integrated
xbps-srcpackaging template and instructions to natively compile and install OmniRoute withbetter-sqlite3bindings via cross-compilation target.
[3.2.4] — 2026-03-29
✨ Enhancements & Refactoring
- Qoder AI Migration (#660) — Completely migrated the legacy
iFlowcore provider ontoQoder AImaintaining stable API routing capabilities.
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
Gemini Tools HTTP 400 Payload Invalid Argument (#731) — Prevented
thoughtSignaturearray injections inside standard GeminifunctionCallsequences blocking agentic routing flows.
[3.2.3] — 2026-03-29
✨ Enhancements & Refactoring
- Provider Limits Quota UI (#728) — Normalized quota limit logic and data labeling inside the Limits interface.
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
Core Routing Schemas & Leaks — Expanded
comboStrategySchemato natively supportfill-firstandp2cstrategies to unblock complex combo editing natively. -
Thinking Tags Extraction (CLI) — Restructured CLI token responses sanitizer RegEx capturing model reasoning structures inside streams avoiding broken
<thinking>extractions breaking response text output format. -
Strict Format Enforcements — Hardened pipeline sanitization execution making it universally apply to translation mode targets.
[3.2.2] — 2026-03-29
✨ New Features
-
feat(docs): integrate multi-page documentation into OmniRoute dashboard (#1969)
-
feat(settings): add request body limit setting (#1968)
-
feat(auth): add Gemini CLI OAuth client secret default (#1974)
-
feat(models): expose models.dev context windows in /v1/models (#1972)
-
fix(db): resolve legacy encryption fallback causing re-encryption loops (#1941)
-
fix(auth): fix Codex assistant final_answer response sanitization (#1965)
-
feat(providers): Implement Image Generation and Editing capabilities for ChatGPT Web, including in-band chat image generation and caching (#1606).
-
feat(ui): Integrate OpenCode Zen/Go API tool logo SVG and polish API key copy-to-clipboard interactions (#1607).
-
feat(providers): Integrate AgentRouter as a new OpenAI-compatible passthrough provider with $200 free credits via sign-up (Issue #1572).
-
feat(ui): Implement on-demand per-model testing in the provider dashboard, allowing single-token diagnostic checks without triggering rate-limits (Issue #1532).
-
Four-Stage Request Log Pipeline (#705) — Refactored log persistence to save comprehensive payloads at four distinct pipeline stages: Client Request, Translated Provider Request, Provider Response, and Translated Client Response. Introduced
streamPayloadCollectorfor robust SSE stream truncation and payload serialization.
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
Mobile UI Fixes (#659) — Prevented table components on the dashboard from breaking the layout on narrow viewports by adding proper horizontal scrolling and overflow containment to
DashboardLayout. -
Claude Prompt Cache Fixes (#708) — Ensured
cache_controlblocks in Claude-to-Claude fallback loops are faithfully preserved and passed safely back to Anthropic models. -
Gemini Tool Definitions (#725) — Fixed schema translation errors when declaring simple
objectparameter types for Gemini function calling.
[3.2.1] — 2026-03-29
✨ New Features
-
feat(docs): integrate multi-page documentation into OmniRoute dashboard (#1969)
-
feat(settings): add request body limit setting (#1968)
-
feat(auth): add Gemini CLI OAuth client secret default (#1974)
-
feat(models): expose models.dev context windows in /v1/models (#1972)
-
fix(db): resolve legacy encryption fallback causing re-encryption loops (#1941)
-
fix(auth): fix Codex assistant final_answer response sanitization (#1965)
-
feat(providers): Implement Image Generation and Editing capabilities for ChatGPT Web, including in-band chat image generation and caching (#1606).
-
feat(ui): Integrate OpenCode Zen/Go API tool logo SVG and polish API key copy-to-clipboard interactions (#1607).
-
feat(providers): Integrate AgentRouter as a new OpenAI-compatible passthrough provider with $200 free credits via sign-up (Issue #1572).
-
feat(ui): Implement on-demand per-model testing in the provider dashboard, allowing single-token diagnostic checks without triggering rate-limits (Issue #1532).
-
Global Fallback Provider (#689) — When all combo models are exhausted (502/503), OmniRoute now attempts a configurable global fallback model before returning the error. Set
globalFallbackModelin settings to enable.
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
Fix #721 — Fixed context pinning bypass during tool-call responses. Non-streaming tagging used wrong JSON path (
json.messages→json.choices[0].message). Streaming injection now triggers onfinish_reasonchunks for tool-call-only streams.injectModelTag()now appends synthetic pin messages for non-string content. -
Fix #709 — Confirmed already fixed (v3.1.9) —
system-info.mjscreates directories recursively. Closed. -
Fix #707 — Confirmed already fixed (v3.1.9) — empty tool name sanitization in
chatCore.ts. Closed.
🧪 Tests
- Added 6 unit tests for context pinning with tool-call responses (null content, array content, roundtrip, re-injection)
[3.2.0] — 2026-03-28
✨ New Features
-
feat(docs): integrate multi-page documentation into OmniRoute dashboard (#1969)
-
feat(settings): add request body limit setting (#1968)
-
feat(auth): add Gemini CLI OAuth client secret default (#1974)
-
feat(models): expose models.dev context windows in /v1/models (#1972)
-
fix(db): resolve legacy encryption fallback causing re-encryption loops (#1941)
-
fix(auth): fix Codex assistant final_answer response sanitization (#1965)
-
feat(providers): Implement Image Generation and Editing capabilities for ChatGPT Web, including in-band chat image generation and caching (#1606).
-
feat(ui): Integrate OpenCode Zen/Go API tool logo SVG and polish API key copy-to-clipboard interactions (#1607).
-
feat(providers): Integrate AgentRouter as a new OpenAI-compatible passthrough provider with $200 free credits via sign-up (Issue #1572).
-
feat(ui): Implement on-demand per-model testing in the provider dashboard, allowing single-token diagnostic checks without triggering rate-limits (Issue #1532).
-
Cache Management UI — Added a dedicated semantic caching dashboard at `/dashboard/cache` with targeted API invalidation and 31-language i18n support (PR #701 by @oyi77)
-
GLM Quota Tracking — Added real-time usage and session quota tracking for the GLM Coding (Z.AI) provider (PR #698 by @christopher-s)
-
Detailed Log Payloads — Wired full four-stage pipeline payload capturing (original, translated, provider-response, streamed-deltas) directly into the UI (PR #705 by @rdself)
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
Fix #708 — Prevented token bleeding for Claude Code users routing through OmniRoute by correctly preserving native `cache_control` headers during Claude-to-Claude passthrough (PR #708 by @tombii)
-
Fix #719 — Setup internal auth boundaries for `ModelSyncScheduler` to prevent unauthenticated daemon failures on startup (PR #719 by @rdself)
-
Fix #718 — Rebuilt badge rendering in Provider Limits UI preventing bad quota boundaries overlap (PR #718 by @rdself)
-
Fix #704 — Fixed Combo Fallbacks breaking on HTTP 400 content-policy errors preventing model-rotation dead-routing (PR #704 by @rdself)
🔒 Security & Dependencies
- Bumped `path-to-regexp` to `8.4.0` resolving dependabot vulnerabilities (PR #715)
[3.1.10] — 2026-03-28
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
Fix #706 — Fixed icon fallback rendering caused by Tailwind V4
font-sansoverride by applying!importantto.material-symbols-outlined. -
Fix #703 — Fixed GitHub Copilot broken streams by enabling
responsestoopenaiformat translation for any custom models leveragingapiFormat: "responses". -
Fix #702 — Replaced flat-rate usage tracking with accurate DB pricing calculations for both streaming and non-streaming responses.
-
Fix #716 — Cleaned up Claude tool-call translation state, correctly parsing streaming arguments and preventing OpenAI
tool_callschunks from repeating theidfield.
[3.1.9] — 2026-03-28
✨ New Features
-
feat(docs): integrate multi-page documentation into OmniRoute dashboard (#1969)
-
feat(settings): add request body limit setting (#1968)
-
feat(auth): add Gemini CLI OAuth client secret default (#1974)
-
feat(models): expose models.dev context windows in /v1/models (#1972)
-
fix(db): resolve legacy encryption fallback causing re-encryption loops (#1941)
-
fix(auth): fix Codex assistant final_answer response sanitization (#1965)
-
feat(providers): Implement Image Generation and Editing capabilities for ChatGPT Web, including in-band chat image generation and caching (#1606).
-
feat(ui): Integrate OpenCode Zen/Go API tool logo SVG and polish API key copy-to-clipboard interactions (#1607).
-
feat(providers): Integrate AgentRouter as a new OpenAI-compatible passthrough provider with $200 free credits via sign-up (Issue #1572).
-
feat(ui): Implement on-demand per-model testing in the provider dashboard, allowing single-token diagnostic checks without triggering rate-limits (Issue #1532).
-
Schema Coercion — Auto-coerce string-encoded numeric JSON Schema constraints (e.g.
"minimum": "1") to proper types, preventing 400 errors from Cursor, Cline, and other clients sending malformed tool schemas. -
Tool Description Sanitization — Ensure tool descriptions are always strings; converts
null,undefined, or numeric descriptions to empty strings before sending to providers. -
Clear All Models Button — Added i18n translations for the "Clear All Models" provider action across all 30 languages.
-
Codex Auth Export — Added Codex
auth.jsonexport and apply-local buttons for seamless CLI integration. -
Windsurf BYOK Notes — Added official limitation warnings to the Windsurf CLI tool card documenting BYOK constraints.
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
Fix #709 —
system-info.mjsno longer crashes when the output directory doesn't exist (addedmkdirSyncwith recursive flag). -
Fix #710 — A2A
TaskManagersingleton now usesglobalThisto prevent state leakage across Next.js API route recompilations in dev mode. E2E test suite updated to handle 401 gracefully. -
Fix #711 — Added provider-specific
max_tokenscap enforcement for upstream requests. -
Fix #605 / #592 — Strip
proxy_prefix from tool names in non-streaming Claude responses; fixed LongCat validation URL. -
Call Logs Max Cap — Upgraded
getMaxCallLogs()with caching layer, env var support (CALL_LOGS_MAX), and DB settings integration.
🧪 Tests
- Test suite expanded from 964 → 1027 tests (63 new tests)
- Added
schema-coercion.test.mjs— 9 tests for numeric field coercion and tool description sanitization - Added
t40-opencode-cli-tools-integration.test.mjs— OpenCode/Windsurf CLI integration tests - Enhanced feature-tests branch with comprehensive coverage tooling
📁 New Files
| File | Purpose |
|---|---|
open-sse/translator/helpers/schemaCoercion.ts |
Schema coercion and tool description sanitization utilities |
tests/unit/schema-coercion.test.mjs |
Unit tests for schema coercion |
tests/unit/t40-opencode-cli-tools-integration.test.mjs |
CLI tool integration tests |
COVERAGE_PLAN.md |
Test coverage planning document |
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
Claude Prompt Caching Passthrough — Fixed cache_control markers being stripped in Claude passthrough mode (Claude → OmniRoute → Claude), which caused Claude Code users to deplete their Anthropic API quota 5-10x faster than direct connections. OmniRoute now preserves client's cache_control markers when sourceFormat and targetFormat are both Claude, ensuring prompt caching works correctly and dramatically reducing token consumption.
[3.1.8] - 2026-03-27
🐛 Bug Fixes & Features
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
Platform Core: Implemented global state handling for Hidden Models & Combos preventing them from cluttering the catalog or leaking into connected MCP agents (#681).
-
Stability: Patched streaming crashes related to the native Antigravity provider integration failing due to unhandled undefined state arrays (#684).
-
Localization Sync: Deployed a fully overhauled
i18nsynchronizer detecting missing nested JSON properties and retro-fitting 30 locales sequentially (#685).## [3.1.7] - 2026-03-27
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
Streaming Stability: Fixed
hasValuableContentreturningundefinedfor empty chunks in SSE streams (#676). -
Tool Calling: Fixed an issue in
sseParser.tswhere non-streaming Claude responses with multiple tool calls dropped theidof subsequent tool calls due to incorrect index-based deduplication (#671).
[3.1.6] — 2026-03-27
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
Claude Native Tool Name Restoration — Tool names like
TodoWriteare no longer prefixed withproxy_in Claude passthrough responses (both streaming and non-streaming). Includes unit test coverage (PR #663 by @coobabm) -
Clear All Models Alias Cleanup — "Clear All Models" button now also removes associated model aliases, preventing ghost models in the UI (PR #664 by @rdself)
[3.1.5] — 2026-03-27
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
Backoff Auto-Decay — Rate-limited accounts now auto-recover when their cooldown window expires, fixing a deadlock where high
backoffLevelpermanently deprioritized accounts (PR #657 by @brendandebeasi)
🌍 i18n
- Chinese translation overhaul — Comprehensive rewrite of
zh-CN.jsonwith improved accuracy (PR #658 by @only4copilot)
[3.1.4] — 2026-03-27
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
Streaming Override Fix — Explicit
stream: truein request body now takes priority overAccept: application/jsonheader. Clients sending both will correctly receive SSE streaming responses (#656)
🌍 i18n
- Czech string improvements — Refined terminology across
cs.json(PR #655 by @zen0bit)
[3.1.3] — 2026-03-26
🌍 i18n & Community
- ~70 missing translation keys added to
en.jsonand 12 languages (PR #652 by @zen0bit) - Czech documentation updated — CLI-TOOLS, API_REFERENCE, VM_DEPLOYMENT guides (PR #652)
- Translation validation scripts —
check_translations.pyandvalidate_translation.pyfor CI/QA (PR #651 by @zen0bit)
[3.1.2] — 2026-03-26
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
Critical: Tool Calling Regression — Fixed
proxy_Basherrors by disabling theproxy_tool name prefix in the Claude passthrough path. Tools likeBash,Read,Writewere being renamed toproxy_Bash,proxy_Read, etc., causing Claude to reject them (#618) -
Kiro Account Ban Documentation — Documented as upstream AWS anti-fraud false positive, not an OmniRoute issue (#649)
🧪 Tests
- 936 tests, 0 failures
[3.1.1] — 2026-03-26
✨ New Features
-
feat(docs): integrate multi-page documentation into OmniRoute dashboard (#1969)
-
feat(settings): add request body limit setting (#1968)
-
feat(auth): add Gemini CLI OAuth client secret default (#1974)
-
feat(models): expose models.dev context windows in /v1/models (#1972)
-
fix(db): resolve legacy encryption fallback causing re-encryption loops (#1941)
-
fix(auth): fix Codex assistant final_answer response sanitization (#1965)
-
feat(providers): Implement Image Generation and Editing capabilities for ChatGPT Web, including in-band chat image generation and caching (#1606).
-
feat(ui): Integrate OpenCode Zen/Go API tool logo SVG and polish API key copy-to-clipboard interactions (#1607).
-
feat(providers): Integrate AgentRouter as a new OpenAI-compatible passthrough provider with $200 free credits via sign-up (Issue #1572).
-
feat(ui): Implement on-demand per-model testing in the provider dashboard, allowing single-token diagnostic checks without triggering rate-limits (Issue #1532).
-
Vision Capability Metadata: Added
capabilities.vision,input_modalities, andoutput_modalitiesto/v1/modelsentries for vision-capable models (PR #646) -
Gemini 3.1 Models: Added
gemini-3.1-pro-previewandgemini-3.1-flash-lite-previewto the Antigravity provider (#645)
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
Ollama Cloud 401 Error: Fixed incorrect API base URL — changed from
api.ollama.comto officialollama.com/v1/chat/completions(#643) -
Expired Token Retry: Added bounded retry with exponential backoff (5→10→20 min) for expired OAuth connections instead of permanently skipping them (PR #647)
🧪 Tests
- 936 tests, 0 failures
[3.1.0] — 2026-03-26
✨ New Features
-
feat(docs): integrate multi-page documentation into OmniRoute dashboard (#1969)
-
feat(settings): add request body limit setting (#1968)
-
feat(auth): add Gemini CLI OAuth client secret default (#1974)
-
feat(models): expose models.dev context windows in /v1/models (#1972)
-
fix(db): resolve legacy encryption fallback causing re-encryption loops (#1941)
-
fix(auth): fix Codex assistant final_answer response sanitization (#1965)
-
feat(providers): Implement Image Generation and Editing capabilities for ChatGPT Web, including in-band chat image generation and caching (#1606).
-
feat(ui): Integrate OpenCode Zen/Go API tool logo SVG and polish API key copy-to-clipboard interactions (#1607).
-
feat(providers): Integrate AgentRouter as a new OpenAI-compatible passthrough provider with $200 free credits via sign-up (Issue #1572).
-
feat(ui): Implement on-demand per-model testing in the provider dashboard, allowing single-token diagnostic checks without triggering rate-limits (Issue #1532).
-
GitHub Issue Templates: Added standardized bug report, feature request, and config/proxy issue templates (#641)
-
Clear All Models: Added a "Clear All Models" button to the provider detail page with i18n support in 29 languages (#634)
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
Locale Conflict (
in.json): Renamed the Hindi locale file fromin.json(Indonesian ISO code) tohi.jsonto fix translation conflicts in Weblate (#642) -
Codex Empty Tool Names: Moved tool name sanitization before the native Codex passthrough, fixing 400 errors from upstream providers when tools had empty names (#637)
-
Streaming Newline Artifacts: Added
collapseExcessiveNewlinesto the response sanitizer, collapsing runs of 3+ consecutive newlines from thinking models into a standard double newline (#638) -
Claude Reasoning Effort: Converted OpenAI
reasoning_effortparam to Claude's nativethinkingbudget block across all request paths, including automaticmax_tokensadjustment (#627) -
Qwen Token Refresh: Implemented proactive pre-expiry OAuth token refreshes (5-minute buffer) to prevent requests from failing when using short-lived tokens (#631)
🧪 Tests
- 936 tests, 0 failures (+10 tests since 3.0.9)
[3.0.9] — 2026-03-26
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
NaN tokens in Claude Code / client responses (#617):
sanitizeUsage()now cross-mapsinput_tokens→prompt_tokensandoutput_tokens→completion_tokensbefore the whitelist filter, fixing responses showing NaN/0 token counts when providers return Claude-style usage field names
🔒 Security
- Updated
yamlpackage to fix stack overflow vulnerability (GHSA-48c2-rrv3-qjmp)
📋 Issue Triage
- Closed #613 (Codestral — resolved with Custom Provider workaround)
- Commented on #615 (OpenCode dual-endpoint — workaround provided, tracked as feature request)
- Commented on #618 (tool call visibility — requesting v3.0.9 test)
- Commented on #627 (effort level — already supported)
[3.0.8] — 2026-03-25
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
Translation Failures for OpenAI-format Providers in Claude CLI (#632):
- Handle
reasoning_details[]array format from StepFun/OpenRouter — converts toreasoning_content - Handle
reasoningfield alias from some providers → normalized toreasoning_content - Cross-map usage field names:
input_tokens↔prompt_tokens,output_tokens↔completion_tokensinfilterUsageForFormat - Fix
extractUsageto accept bothinput_tokens/output_tokensandprompt_tokens/completion_tokensas valid usage fields - Applied to both streaming (
sanitizeStreamingChunk,openai-to-claude.tstranslator) and non-streaming (sanitizeMessage) paths
- Handle
[3.0.7] — 2026-03-25
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
Antigravity Token Refresh: Fixed
client_secret is missingerror for npm-installed users — theclientSecretDefaultwas empty in providerRegistry, causing Google to reject token refresh requests (#588) -
OpenCode Zen Models: Added
modelsUrlto the OpenCode Zen registry entry so "Import from /models" works correctly (#612) -
Streaming Artifacts: Fixed excessive newlines left in responses after thinking-tag signature stripping (#626)
-
Proxy Fallback: Added automatic retry without proxy when SOCKS5 relay fails
-
Proxy Test: Test endpoint now resolves real credentials from DB via proxyId
✨ New Features
-
feat(docs): integrate multi-page documentation into OmniRoute dashboard (#1969)
-
feat(settings): add request body limit setting (#1968)
-
feat(auth): add Gemini CLI OAuth client secret default (#1974)
-
feat(models): expose models.dev context windows in /v1/models (#1972)
-
fix(db): resolve legacy encryption fallback causing re-encryption loops (#1941)
-
fix(auth): fix Codex assistant final_answer response sanitization (#1965)
-
feat(providers): Implement Image Generation and Editing capabilities for ChatGPT Web, including in-band chat image generation and caching (#1606).
-
feat(ui): Integrate OpenCode Zen/Go API tool logo SVG and polish API key copy-to-clipboard interactions (#1607).
-
feat(providers): Integrate AgentRouter as a new OpenAI-compatible passthrough provider with $200 free credits via sign-up (Issue #1572).
-
feat(ui): Implement on-demand per-model testing in the provider dashboard, allowing single-token diagnostic checks without triggering rate-limits (Issue #1532).
-
Playground Account/Key Selector: Persistent, always-visible dropdown to select specific provider accounts/keys for testing — fetches all connections at startup and filters by selected provider
-
CLI Tools Dynamic Models: Model selection now dynamically fetches from
/v1/modelsAPI — providers like Kiro now show their full model catalog -
Antigravity Model List: Updated with Claude Sonnet 4.5, Claude Sonnet 4, GPT 5, GPT 5 Mini; enabled
passthroughModelsfor dynamic model access (#628)
🔧 Maintenance
- Merged PR #625 — Provider Limits light mode background fix
[3.0.6] — 2026-03-25
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
Limits/Proxy: Fixed Codex limit fetching for accounts behind SOCKS5 proxies — token refresh now runs inside proxy context
-
CI: Fixed integration test
v1/modelsassertion failure in CI environments without provider connections -
Settings: Proxy test button now shows success/failure results immediately (previously hidden behind health data)
✨ New Features
-
feat(docs): integrate multi-page documentation into OmniRoute dashboard (#1969)
-
feat(settings): add request body limit setting (#1968)
-
feat(auth): add Gemini CLI OAuth client secret default (#1974)
-
feat(models): expose models.dev context windows in /v1/models (#1972)
-
fix(db): resolve legacy encryption fallback causing re-encryption loops (#1941)
-
fix(auth): fix Codex assistant final_answer response sanitization (#1965)
-
feat(providers): Implement Image Generation and Editing capabilities for ChatGPT Web, including in-band chat image generation and caching (#1606).
-
feat(ui): Integrate OpenCode Zen/Go API tool logo SVG and polish API key copy-to-clipboard interactions (#1607).
-
feat(providers): Integrate AgentRouter as a new OpenAI-compatible passthrough provider with $200 free credits via sign-up (Issue #1572).
-
feat(ui): Implement on-demand per-model testing in the provider dashboard, allowing single-token diagnostic checks without triggering rate-limits (Issue #1532).
-
Playground: Added Account selector dropdown — test specific connections individually when a provider has multiple accounts
🔧 Maintenance
- Merged PR #623 — LongCat API base URL path correction
[3.0.5] — 2026-03-25
✨ New Features
-
feat(docs): integrate multi-page documentation into OmniRoute dashboard (#1969)
-
feat(settings): add request body limit setting (#1968)
-
feat(auth): add Gemini CLI OAuth client secret default (#1974)
-
feat(models): expose models.dev context windows in /v1/models (#1972)
-
fix(db): resolve legacy encryption fallback causing re-encryption loops (#1941)
-
fix(auth): fix Codex assistant final_answer response sanitization (#1965)
-
feat(providers): Implement Image Generation and Editing capabilities for ChatGPT Web, including in-band chat image generation and caching (#1606).
-
feat(ui): Integrate OpenCode Zen/Go API tool logo SVG and polish API key copy-to-clipboard interactions (#1607).
-
feat(providers): Integrate AgentRouter as a new OpenAI-compatible passthrough provider with $200 free credits via sign-up (Issue #1572).
-
feat(ui): Implement on-demand per-model testing in the provider dashboard, allowing single-token diagnostic checks without triggering rate-limits (Issue #1532).
-
Limits UI: Added tag grouping feature to the connections dashboard to improve visual organization for accounts with custom tags.
[3.0.4] — 2026-03-25
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
Streaming: Fixed
TextDecoderstate corruption inside combosanitizeTransformStream which caused SSE garbled output matching multibyte characters (PR #614) -
Providers UI: Safely render HTML tags inside provider connection error tooltips using
dangerouslySetInnerHTML -
Proxy Settings: Added missing
usernameandpasswordpayload body properties allowing authenticated proxies to be successfully verified from the Dashboard. -
Provider API: Bound soft exception returns to
getCodexUsagepreventing API HTTP 500 failures when token fetch fails
[3.0.3] — 2026-03-25
✨ New Features
-
feat(docs): integrate multi-page documentation into OmniRoute dashboard (#1969)
-
feat(settings): add request body limit setting (#1968)
-
feat(auth): add Gemini CLI OAuth client secret default (#1974)
-
feat(models): expose models.dev context windows in /v1/models (#1972)
-
fix(db): resolve legacy encryption fallback causing re-encryption loops (#1941)
-
fix(auth): fix Codex assistant final_answer response sanitization (#1965)
-
feat(providers): Implement Image Generation and Editing capabilities for ChatGPT Web, including in-band chat image generation and caching (#1606).
-
feat(ui): Integrate OpenCode Zen/Go API tool logo SVG and polish API key copy-to-clipboard interactions (#1607).
-
feat(providers): Integrate AgentRouter as a new OpenAI-compatible passthrough provider with $200 free credits via sign-up (Issue #1572).
-
feat(ui): Implement on-demand per-model testing in the provider dashboard, allowing single-token diagnostic checks without triggering rate-limits (Issue #1532).
-
Auto-Sync Models: Added a UI toggle and
sync-modelsendpoint to automatically synchronise model lists per provider using a scheduled interval scheduler (PR #597)
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
Timeouts: Elevated default proxies
FETCH_TIMEOUT_MSandSTREAM_IDLE_TIMEOUT_MSto 10 minutes to properly support deep reasoning models (like o1) without aborting requests (Fixes #609) -
CLI Tool Detection: Improved cross-platform detection handling NVM paths, Windows
PATHEXT(preventing.cmdwrappers issue), and custom NPM prefixes (PR #598) -
Streaming Logs: Implemented
tool_callsdelta accumulation in streaming response logs so function calls are tracked and persisted accurately in DB (PR #603) -
Model Catalog: Removed auth exemption, properly hiding
comfyuiandsdwebuimodels when no provider is explicitly configured (PR #599)
🌐 Translations
- cs: Improved Czech translation strings across the app (PR #601)
[3.0.2] — 2026-03-25
🚀 Enhancements & Features
feat(ui): Connection Tag Grouping
- Added a Tag/Group field to
EditConnectionModal(stored inproviderSpecificData.tag) without requiring DB schema migrations. - Connections in the provider view now dynamically group by tag with visual dividers.
- Untagged connections appear first without a header, followed by tagged groups in alphabetical order.
- The tag grouping automatically applies to the Codex/Copilot/Antigravity Limits section since toggles exist inside connection rows.
🐛 Bug Fixes
- fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
- fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. - fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. - fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. - fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. - fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
- fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. - fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. - fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard.
fix(ui): Proxy Management UI Stabilization
- Missing badges on connection cards: Fixed by using
resolveProxyForConnection()rather than static mapping. - Test Connection disabled in saved mode: Enabled the Test button by resolving proxy config from the saved list.
- Config Modal freezing: Added
onClose()calls after save/clear to prevent the UI from freezing. - Double usage counting:
ProxyRegistryManagernow loads usage eagerly on mount with deduplication byscope+scopeId. Usage counts were replaced with a Test button displaying IP/latency inline.
fix(translator): function_call prefix stripping
- Repaired an incomplete fix from PR #607 where only
tool_useblocks stripped Claude'sproxy_tool prefix. Now, clients using the OpenAI Responses API format will also correctly receive tool tools without theproxy_prefix.
[3.0.1] — 2026-03-25
🔧 Hotfix Patch — Critical Bug Fixes
Three critical regressions reported by users after the v3.0.0 launch have been resolved.
fix(translator): strip proxy_ prefix in non-streaming Claude responses (#605)
The proxy_ prefix added by Claude OAuth was only stripped from streaming responses. In non-streaming mode, translateNonStreamingResponse had no access to the toolNameMap, causing clients to receive mangled tool names like proxy_read_file instead of read_file.
Fix: Added optional toolNameMap parameter to translateNonStreamingResponse and applied prefix stripping in the Claude tool_use block handler. chatCore.ts now passes the map through.
fix(validation): add LongCat specialty validator to skip /models probe (#592)
LongCat AI does not expose GET /v1/models. The generic validateOpenAICompatibleProvider validator fell through to a chat-completions fallback only if validationModelId was set, which LongCat doesn't configure. This caused provider validation to fail with a misleading error on add/save.
Fix: Added longcat to the specialty validators map, probing /chat/completions directly and treating any non-auth response as a pass.
fix(translator): normalize object tool schemas for Anthropic (#595)
MCP tools (e.g. pencil, computer_use) forward tool definitions with {type:"object"} but without a properties field. Anthropic's API rejects these with: object schema missing properties.
Fix: In openai-to-claude.ts, inject properties: {} as a safe default when type is "object" and properties is absent.
🔀 Community PRs Merged (2)
| PR | Author | Summary |
|---|---|---|
| #589 | @flobo3 | docs(i18n): fix Russian translation for Playground and Testbed |
| #591 | @rdself | fix(ui): improve Provider Limits light mode contrast and plan tier display |
✅ Issues Resolved
#592 #595 #605
🧪 Tests
- 926 tests, 0 failures (unchanged from v3.0.0)
[3.0.0] — 2026-03-24
🎉 OmniRoute v3.0.0 — The Free AI Gateway, Now with 67+ Providers
The biggest release ever. From 36 providers in v2.9.5 to 67+ providers in v3.0.0 — with MCP Server, A2A Protocol, auto-combo engine, Provider Icons, Registered Keys API, 926 tests, and contributions from 12 community members across 10 merged PRs.
Consolidated from v3.0.0-rc.1 through rc.17 (17 release candidates over 3 days of intense development).
🆕 New Providers (+31 since v2.9.5)
| Provider | Alias | Tier | Notes |
|---|---|---|---|
| OpenCode Zen | opencode-zen |
Free | 3 models via opencode.ai/zen/v1 (PR #530 by @kang-heewon) |
| OpenCode Go | opencode-go |
Paid | 4 models via opencode.ai/zen/go/v1 (PR #530 by @kang-heewon) |
| LongCat AI | lc |
Free | 50M tokens/day (Flash-Lite) + 500K/day (Chat/Thinking) during public beta |
| Pollinations AI | pol |
Free | No API key needed — GPT-5, Claude, Gemini, DeepSeek V3, Llama 4 (1 req/15s) |
| Cloudflare Workers AI | cf |
Free | 10K Neurons/day — ~150 LLM responses or 500s Whisper audio, edge inference |
| Scaleway AI | scw |
Free | 1M free tokens for new accounts — EU/GDPR compliant (Paris) |
| AI/ML API | aiml |
Free | $0.025/day free credits — 200+ models via single endpoint |
| Puter AI | pu |
Free | 500+ models (GPT-5, Claude Opus 4, Gemini 3 Pro, Grok 4, DeepSeek V3) |
| Alibaba Cloud (DashScope) | ali |
Paid | International + China endpoints via alicode/alicode-intl |
| Alibaba Coding Plan | bcp |
Paid | Alibaba Model Studio with Anthropic-compatible API |
| Kimi Coding (API Key) | kmca |
Paid | Dedicated API-key-based Kimi access (separate from OAuth) |
| MiniMax Coding | minimax |
Paid | International endpoint |
| MiniMax (China) | minimax-cn |
Paid | China-specific endpoint |
| Z.AI (GLM-5) | zai |
Paid | Zhipu AI next-gen GLM models |
| Vertex AI | vertex |
Paid | Google Cloud — Service Account JSON or OAuth access_token |
| Ollama Cloud | ollamacloud |
Paid | Ollama's hosted API service |
| Synthetic | synthetic |
Paid | Passthrough models gateway |
| Kilo Gateway | kg |
Paid | Passthrough models gateway |
| Perplexity Search | pplx-search |
Paid | Dedicated search-grounded endpoint |
| Serper Search | serper-search |
Paid | Web search API integration |
| Brave Search | brave-search |
Paid | Brave Search API integration |
| Exa Search | exa-search |
Paid | Neural search API integration |
| Tavily Search | tavily-search |
Paid | AI search API integration |
| NanoBanana | nb |
Paid | Image generation API |
| ElevenLabs | el |
Paid | Text-to-speech voice synthesis |
| Cartesia | cartesia |
Paid | Ultra-fast TTS voice synthesis |
| PlayHT | playht |
Paid | Voice cloning and TTS |
| Inworld | inworld |
Paid | AI character voice chat |
| SD WebUI | sdwebui |
Self-hosted | Stable Diffusion local image generation |
| ComfyUI | comfyui |
Self-hosted | ComfyUI local workflow node-based generation |
| GLM Coding | glm |
Paid | BigModel/Zhipu coding-specific endpoint |
Total: 67+ providers (4 Free, 8 OAuth, 55 API Key) + unlimited OpenAI/Anthropic-Compatible custom providers.
✨ Major Features
🔑 Registered Keys Provisioning API (#464)
Auto-generate and issue OmniRoute API keys programmatically with per-provider and per-account quota enforcement.
| Endpoint | Method | Description |
|---|---|---|
/api/v1/registered-keys |
POST |
Issue a new key — raw key returned once only |
/api/v1/registered-keys |
GET |
List registered keys (masked) |
/api/v1/registered-keys/{id} |
GET/DELETE |
Get metadata / Revoke |
/api/v1/quotas/check |
GET |
Pre-validate quota before issuing |
/api/v1/providers/{id}/limits |
GET/PUT |
Configure per-provider issuance limits |
/api/v1/accounts/{id}/limits |
GET/PUT |
Configure per-account issuance limits |
/api/v1/issues/report |
POST |
Report quota events to GitHub Issues |
Security: Keys stored as SHA-256 hashes. Raw key shown once on creation, never retrievable again.
🎨 Provider Icons via @lobehub/icons (#529)
130+ provider logos using @lobehub/icons React components (SVG). Fallback chain: Lobehub SVG → existing PNG → generic icon. Applied across Dashboard, Providers, and Agents pages with standardized ProviderIcon component.
🔄 Model Auto-Sync Scheduler (#488)
Auto-refreshes model lists for connected providers every 24 hours. Runs on server startup. Configurable via MODEL_SYNC_INTERVAL_HOURS.
🔀 Per-Model Combo Routing (#563)
Map model name patterns (glob) to specific combos for automatic routing:
claude-sonnet*→ code-combo,gpt-4o*→ openai-combo,gemini-*→ google-combo- New
model_combo_mappingstable with glob-to-regex matching - Dashboard UI section: "Model Routing Rules" with inline add/edit/toggle/delete
🧭 API Endpoints Dashboard
Interactive catalog, webhooks management, OpenAPI viewer — all in one tabbed page at /dashboard/endpoint.
🔍 Web Search Providers
5 new search provider integrations: Perplexity Search, Serper, Brave Search, Exa, Tavily — enabling grounded AI responses with real-time web data.
📊 Search Analytics
New tab in /dashboard/analytics — provider breakdown, cache hit rate, cost tracking. API: GET /api/v1/search/analytics.
🛡️ Per-API-Key Rate Limits (#452)
max_requests_per_day and max_requests_per_minute columns with in-memory sliding-window enforcement returning HTTP 429.
🎵 Media Playground
Full media generation playground at /dashboard/media: Image Generation, Video, Music, Audio Transcription (2GB upload limit), and Text-to-Speech.
🔒 Security & CI/CD
- CodeQL remediation — Fixed 10+ alerts: 6 polynomial-redos, 1 insecure-randomness (
Math.random()→crypto.randomUUID()), 1 shell-command-injection - Route validation — Zod schemas +
validateBody()on 176/176 API routes — CI enforced - CVE fix — dompurify XSS vulnerability (GHSA-v2wj-7wpq-c8vv) resolved via npm overrides
- Flatted — Bumped 3.3.3 → 3.4.2 (CWE-1321 prototype pollution)
- Docker — Upgraded
docker/setup-buildx-actionv3 → v4
🐛 Bug Fixes (40+)
- fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
- fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. - fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. - fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. - fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. - fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
- fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. - fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. - fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard.
OAuth & Auth
- #537 — Gemini CLI OAuth: clear actionable error when
GEMINI_OAUTH_CLIENT_SECRETmissing in Docker - #549 — CLI settings routes now resolve real API key from
keyId(not masked strings) - #574 — Login no longer freezes after skipping wizard password setup
- #506 — Cross-platform
machineIdrewritten (Windows REG.exe → macOS ioreg → Linux → hostname fallback)
Providers & Routing
- #536 — LongCat AI: fixed
baseUrlandauthHeader - #535 — Pinned model override:
body.modelcorrectly set topinnedModel - #570 — Unprefixed Claude models now resolve to Anthropic provider
- #585 —
<omniModel>internal tags no longer leak to clients in SSE streaming - #493 — Custom provider model naming no longer mangled by prefix stripping
- #490 — Streaming + context cache protection via
TransformStreaminjection - #511 —
<omniModel>tag injected into first content chunk (not after[DONE])
CLI & Tools
- #527 — Claude Code + Codex loop:
tool_resultblocks now converted to text - #524 — OpenCode config saved correctly (XDG_CONFIG_HOME, TOML format)
- #522 — API Manager: removed misleading "Copy masked key" button
- #546 —
--versionreturningunknownon Windows (PR by @k0valik) - #544 — Secure CLI tool detection via known installation paths (PR by @k0valik)
- #510 — Windows MSYS2/Git-Bash paths normalized automatically
- #492 — CLI detects
mise/nvm-managed Node whenapp/server.jsmissing
Streaming & SSE
- PR #587 — Revert
resolveDataDirimport in responsesTransformer for Cloudflare Workers compat (@k0valik) - PR #495 — Bottleneck 429 infinite wait: drop waiting jobs on rate limit (@xandr0s)
- #483 — Stop trailing
data: nullafter[DONE]signal - #473 — Zombie SSE streams: timeout reduced 300s → 120s for faster fallback
Media & Transcription
- Transcription — Deepgram
video/mp4→audio/mp4MIME mapping, auto language detection, punctuation - TTS —
[object Object]error display fixed for ElevenLabs-style nested errors - Upload limits — Media transcription increased to 2GB (nginx
client_max_body_size 2g+maxDuration=300)
🔧 Infrastructure & Improvements
Sub2api Gap Analysis (T01–T15 + T23–T42)
- T01 —
requested_modelcolumn in call logs (migration 009) - T02 — Strip empty text blocks from nested
tool_result.content - T03 — Parse
x-codex-5h-*/x-codex-7d-*quota headers - T04 —
X-Session-Idheader for external sticky routing - T05 — Rate-limit DB persistence with dedicated API
- T06 — Account deactivated → permanent block (1-year cooldown)
- T07 — X-Forwarded-For IP validation (
extractClientIp()) - T08 — Per-API-key session limits with sliding-window enforcement
- T09 — Codex vs Spark rate-limit scopes (separate pools)
- T10 — Credits exhausted → distinct 1h cooldown fallback
- T11 —
maxreasoning effort → 131072 budget tokens - T12 — MiniMax M2.7 pricing entries
- T13 — Stale quota display fix (reset window awareness)
- T14 — Proxy fast-fail TCP check (≤2s, cached 30s)
- T15 — Array content normalization for Anthropic
- T23 — Intelligent quota reset fallback (header extraction)
- T24 —
503cooldown +406mapping - T25 — Provider validation fallback
- T29 — Vertex AI Service Account JWT auth
- T33 — Thinking level to budget conversion
- T36 —
403vs429error classification - T38 — Centralized model specifications (
modelSpecs.ts) - T39 — Endpoint fallback for
fetchAvailableModels - T41 — Background task auto-redirect to flash models
- T42 — Image generation aspect ratio mapping
Other Improvements
- Per-model upstream custom headers — via configuration UI (PR #575 by @zhangqiang8vip)
- Model context length — configurable in model metadata (PR #578 by @hijak)
- Model prefix stripping — option to remove provider prefix from model names (PR #582 by @jay77721)
- Gemini CLI deprecation — marked deprecated with Google OAuth restriction warning
- YAML parser — replaced custom parser with
js-yamlfor correct OpenAPI spec parsing - ZWS v5 — HMR leak fix (485 DB connections → 1, memory 2.4GB → 195MB)
- Log export — New JSON export button on dashboard with time range dropdown
- Update notification banner — dashboard homepage shows when new versions are available
🌐 i18n & Documentation
- 30 languages at 100% parity — 2,788 missing keys synced
- Czech — Full translation: 22 docs, 2,606 UI strings (PR by @zen0bit)
- Chinese (zh-CN) — Complete retranslation (PR by @only4copilot)
- VM Deployment Guide — Translated to English as source document
- API Reference — Added
/v1/embeddingsand/v1/audio/speechendpoints - Provider count — Updated from 36+/40+/44+ to 67+ across README and all 30 i18n READMEs
🔀 Community PRs Merged (10)
| PR | Author | Summary |
|---|---|---|
| #587 | @k0valik | fix(sse): revert resolveDataDir import for Cloudflare Workers compat |
| #582 | @jay77721 | feat(proxy): model name prefix stripping option |
| #581 | @jay77721 | fix(npm): link electron-release to npm-publish workflow |
| #578 | @hijak | feat: configurable context length in model metadata |
| #575 | @zhangqiang8vip | feat: per-model upstream headers, compat PATCH, chat alignment |
| #562 | @coobabm | fix: MCP session management, Claude passthrough, detectFormat |
| #561 | @zen0bit | fix(i18n): Czech translation corrections |
| #555 | @k0valik | fix(sse): centralized resolveDataDir() for path resolution |
| #546 | @k0valik | fix(cli): --version returning unknown on Windows |
| #544 | @k0valik | fix(cli): secure CLI tool detection via installation paths |
| #542 | @rdself | fix(ui): light mode contrast CSS theme variables |
| #530 | @kang-heewon | feat: OpenCode Zen + Go providers with OpencodeExecutor |
| #512 | @zhangqiang8vip | feat: per-protocol model compatibility (compatByProtocol) |
| #497 | @zhangqiang8vip | fix: dev-mode HMR resource leaks (ZWS v5) |
| #495 | @xandr0s | fix: Bottleneck 429 infinite wait (drop waiting jobs) |
| #494 | @zhangqiang8vip | feat: MiniMax developer→system role fix |
| #480 | @prakersh | fix: stream flush usage extraction |
| #479 | @prakersh | feat: Codex 5.3/5.4 and Anthropic pricing entries |
| #475 | @only4copilot | feat(i18n): improved Chinese translation |
Thank you to all contributors! 🙏
📋 Issues Resolved (50+)
#452 #458 #462 #464 #466 #473 #474 #481 #483 #487 #488 #489 #490 #491 #492 #493 #506 #508 #509 #510 #511 #513 #520 #521 #522 #524 #525 #527 #529 #531 #532 #535 #536 #537 #541 #546 #549 #563 #570 #574 #585
🧪 Tests
- 926 tests, 0 failures (up from 821 in v2.9.5)
- +105 new tests covering: model-combo mappings, registered keys, OpencodeExecutor, Bailian provider, route validation, error classification, aspect ratio mapping, and more
📦 Database Migrations
| Migration | Description |
|---|---|
| 008 | registered_keys, provider_key_limits, account_key_limits tables |
| 009 | requested_model column in call_logs |
| 010 | model_combo_mappings table for per-model combo routing |
⬆️ Upgrading from v2.9.5
# npm
npm install -g omniroute@3.0.0
# Docker
docker pull diegosouzapw/omniroute:3.0.0
# Migrations run automatically on first startup
Breaking changes: None. All existing configurations, combos, and API keys are preserved. Database migrations 008-010 run automatically on startup.
[3.0.0-rc.17] — 2026-03-24
🔒 Security & CI/CD
- CodeQL remediation — Fixed 10+ alerts:
- 6 polynomial-redos in
provider.ts/chatCore.ts(replaced(?:^|/)alternation patterns with segment-based matching) - 1 insecure-randomness in
acp/manager.ts(Math.random()→crypto.randomUUID()) - 1 shell-command-injection in
prepublish.mjs(JSON.stringify()path escaping)
- 6 polynomial-redos in
- Route validation — Added Zod schemas +
validateBody()to 5 routes missing validation:model-combo-mappings(POST, PUT),webhooks(POST, PUT),openapi/try(POST)- CI
check:route-validation:t06now passes: 176/176 routes validated
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
#585 —
<omniModel>internal tags no longer leak to clients in SSE responses. Added outbound sanitizationTransformStreamincombo.ts
⚙️ Infrastructure
- Docker — Upgraded
docker/setup-buildx-actionfrom v3 → v4 (Node.js 20 deprecation fix) - CI cleanup — Deleted 150+ failed/cancelled workflow runs
🧪 Tests
- Test suite: 926 tests, 0 failures (+3 new)
[3.0.0-rc.16] — 2026-03-24
✨ New Features
-
feat(docs): integrate multi-page documentation into OmniRoute dashboard (#1969)
-
feat(settings): add request body limit setting (#1968)
-
feat(auth): add Gemini CLI OAuth client secret default (#1974)
-
feat(models): expose models.dev context windows in /v1/models (#1972)
-
fix(db): resolve legacy encryption fallback causing re-encryption loops (#1941)
-
fix(auth): fix Codex assistant final_answer response sanitization (#1965)
-
feat(providers): Implement Image Generation and Editing capabilities for ChatGPT Web, including in-band chat image generation and caching (#1606).
-
feat(ui): Integrate OpenCode Zen/Go API tool logo SVG and polish API key copy-to-clipboard interactions (#1607).
-
feat(providers): Integrate AgentRouter as a new OpenAI-compatible passthrough provider with $200 free credits via sign-up (Issue #1572).
-
feat(ui): Implement on-demand per-model testing in the provider dashboard, allowing single-token diagnostic checks without triggering rate-limits (Issue #1532).
-
Increased media transcription limits
-
Added Model Context Length to registry metadata
-
Added per-model upstream custom headers via configuration UI
-
Fixed multiple bugs, Zod valiadation for patches, and resolved various community issues.
[3.0.0-rc.15] — 2026-03-24
✨ New Features
-
feat(docs): integrate multi-page documentation into OmniRoute dashboard (#1969)
-
feat(settings): add request body limit setting (#1968)
-
feat(auth): add Gemini CLI OAuth client secret default (#1974)
-
feat(models): expose models.dev context windows in /v1/models (#1972)
-
fix(db): resolve legacy encryption fallback causing re-encryption loops (#1941)
-
fix(auth): fix Codex assistant final_answer response sanitization (#1965)
-
feat(providers): Implement Image Generation and Editing capabilities for ChatGPT Web, including in-band chat image generation and caching (#1606).
-
feat(ui): Integrate OpenCode Zen/Go API tool logo SVG and polish API key copy-to-clipboard interactions (#1607).
-
feat(providers): Integrate AgentRouter as a new OpenAI-compatible passthrough provider with $200 free credits via sign-up (Issue #1572).
-
feat(ui): Implement on-demand per-model testing in the provider dashboard, allowing single-token diagnostic checks without triggering rate-limits (Issue #1532).
-
#563 — Per-model Combo Routing: map model name patterns (glob) to specific combos for automatic routing
- New
model_combo_mappingstable (migration 010) with pattern, combo_id, priority, enabled resolveComboForModel()DB function with glob-to-regex matching (case-insensitive,*and?wildcards)getComboForModel()inmodel.ts: augmentsgetCombo()with model-pattern fallbackchat.ts: routing decision now checks model-combo mappings before single-model handling- API:
GET/POST /api/model-combo-mappings,GET/PUT/DELETE /api/model-combo-mappings/:id - Dashboard: "Model Routing Rules" section added to Combos page with inline add/edit/toggle/delete
- Examples:
claude-sonnet*→ code-combo,gpt-4o*→ openai-combo,gemini-*→ google-combo
- New
🌐 i18n
- Full i18n Sync: 2,788 missing keys added across 30 language files — all languages now at 100% parity with
en.json - Agents page i18n: OpenCode Integration section fully internationalized (title, description, scanning, download labels)
- 6 new keys added to
agentsnamespace for OpenCode section
🎨 UI/UX
- Provider Icons: 16 missing provider icons added (3 copied, 2 downloaded, 11 SVG created)
- SVG fallback:
ProviderIconcomponent updated with 4-tier strategy: Lobehub → PNG → SVG → Generic icon - Agents fingerprinting: Synced with CLI tools — added droid, openclaw, copilot, opencode to fingerprint list (14 total)
🔒 Security
- CVE fix: Resolved dompurify XSS vulnerability (GHSA-v2wj-7wpq-c8vv) via npm overrides forcing
dompurify@^3.3.2 npm auditnow reports 0 vulnerabilities
🧪 Tests
- Test suite: 923 tests, 0 failures (+15 new model-combo mapping tests)
[3.0.0-rc.14] — 2026-03-23
🔀 Community PRs Merged
| PR | Author | Summary |
|---|---|---|
| #562 | @coobabm | fix(ux): MCP session management, Claude passthrough normalization, OAuth modal, detectFormat |
| #561 | @zen0bit | fix(i18n): Czech translation corrections — HTTP method names and documentation updates |
🧪 Tests
- Test suite: 908 tests, 0 failures
[3.0.0-rc.13] — 2026-03-23
🔧 Bug Fixes
- config: resolve real API key from
keyIdin CLI settings routes (codex-settings,droid-settings,kilo-settings) to prevent writing masked strings (#549)
[3.0.0-rc.12] — 2026-03-23
🔀 Community PRs Merged
| PR | Author | Summary |
|---|---|---|
| #546 | @k0valik | fix(cli): --version returning unknown on Windows — use JSON.parse(readFileSync) instead of ESM import |
| #555 | @k0valik | fix(sse): centralized resolveDataDir() for path resolution in credentials, autoCombo, responses logger, and request logger |
| #544 | @k0valik | fix(cli): secure CLI tool detection via known installation paths (8 tools) with symlink validation, file-type checks, size bounds, minimal env in healthcheck |
| #542 | @rdself | fix(ui): improve light mode contrast — add missing CSS theme variables (bg-primary, bg-subtle, text-primary) and fix dark-only colors in log detail |
🔧 Bug Fixes
- TDZ fix in
cliRuntime.ts—validateEnvPathwas used before initialization at module startup bygetExpectedParentPaths(). Reordered declarations to fixReferenceError. - Build fixes — Added
pinoandpino-prettytoserverExternalPackagesto prevent Turbopack from breaking Pino's internal worker loading.
🧪 Tests
- Test suite: 905 tests, 0 failures
[3.0.0-rc.10] — 2026-03-23
🔧 Bug Fixes
- #509 / #508 — Electron build regression: downgraded Next.js from
16.1.xto16.0.10to eliminate Turbopack module-hashing instability that caused blank screens in the Electron desktop bundle. - Unit test fixes — Corrected two stale test assertions (
nanobanana-image-handleraspect ratio/resolution,thinking-budgetGeminithinkingConfigfield mapping) that had drifted after recent implementation changes. - #541 — Responded to user feedback about installation complexity; no code changes required.
[3.0.0-rc.9] — 2026-03-23
✨ New Features
-
feat(docs): integrate multi-page documentation into OmniRoute dashboard (#1969)
-
feat(settings): add request body limit setting (#1968)
-
feat(auth): add Gemini CLI OAuth client secret default (#1974)
-
feat(models): expose models.dev context windows in /v1/models (#1972)
-
fix(db): resolve legacy encryption fallback causing re-encryption loops (#1941)
-
fix(auth): fix Codex assistant final_answer response sanitization (#1965)
-
feat(providers): Implement Image Generation and Editing capabilities for ChatGPT Web, including in-band chat image generation and caching (#1606).
-
feat(ui): Integrate OpenCode Zen/Go API tool logo SVG and polish API key copy-to-clipboard interactions (#1607).
-
feat(providers): Integrate AgentRouter as a new OpenAI-compatible passthrough provider with $200 free credits via sign-up (Issue #1572).
-
feat(ui): Implement on-demand per-model testing in the provider dashboard, allowing single-token diagnostic checks without triggering rate-limits (Issue #1532).
-
T29 — Vertex AI SA JSON Executor: implemented using the
joselibrary to handle JWT/Service Account auth, along with configurable regions in the UI and automatic partner model URL building. -
T42 — Image generation aspect ratio mapping: created
sizeMapperlogic for generic OpenAI formats (size), added nativeimagen3handling, and updated NanoBanana endpoints to utilize mapped aspect ratios automatically. -
T38 — Centralized model specifications:
modelSpecs.tscreated for limits and parameters per model.
🔧 Improvements
- T40 — OpenCode CLI tools integration: native
opencode-zenandopencode-gointegration completed in earlier PR.
[3.0.0-rc.8] — 2026-03-23
🔧 Bug Fixes & Improvements (Fallback, Quota & Budget)
- T24 —
503cooldown await fix +406mapping: mapped406 Not Acceptableto503 Service Unavailablewith proper cooldown intervals. - T25 — Provider validation fallback: graceful fallback to standard validation models when a specific
validationModelIdis not present. - T36 —
403vs429provider handling refinement: extracted intoerrorClassifier.tsto properly segregate hard permissions failures (403) from rate limits (429). - T39 — Endpoint Fallback for
fetchAvailableModels: implemented a tri-tier mechanism (/models->/v1/models-> local generic catalog) +list_models_catalogMCP tool updates to reflectsourceandwarning. - T33 — Thinking level to budget conversion: translates qualitative thinking levels into precise budget allocations.
- T41 — Background task auto redirect: routes heavy background evaluation tasks to flash/efficient models automatically.
- T23 — Intelligent quota reset fallback: accurately extracts
x-ratelimit-reset/retry-afterheader values or maps static cooldowns.
[3.0.0-rc.7] — 2026-03-23 (What's New vs v2.9.5 — will be released as v3.0.0)
Upgrade from v2.9.5: 16 issues resolved · 2 community PRs merged · 2 new providers · 7 new API endpoints · 3 new features · DB migration 008+009 · 832 tests passing · 15 sub2api gap improvements (T01–T15 complete).
🆕 New Providers
| Provider | Alias | Tier | Notes |
|---|---|---|---|
| OpenCode Zen | opencode-zen |
Free | 3 models via opencode.ai/zen/v1 (PR #530 by @kang-heewon) |
| OpenCode Go | opencode-go |
Paid | 4 models via opencode.ai/zen/go/v1 (PR #530 by @kang-heewon) |
Both providers use the new OpencodeExecutor with multi-format routing (/chat/completions, /messages, /responses, /models/{model}:generateContent).
✨ New Features
-
feat(docs): integrate multi-page documentation into OmniRoute dashboard (#1969)
-
feat(settings): add request body limit setting (#1968)
-
feat(auth): add Gemini CLI OAuth client secret default (#1974)
-
feat(models): expose models.dev context windows in /v1/models (#1972)
-
fix(db): resolve legacy encryption fallback causing re-encryption loops (#1941)
-
fix(auth): fix Codex assistant final_answer response sanitization (#1965)
-
feat(providers): Implement Image Generation and Editing capabilities for ChatGPT Web, including in-band chat image generation and caching (#1606).
-
feat(ui): Integrate OpenCode Zen/Go API tool logo SVG and polish API key copy-to-clipboard interactions (#1607).
-
feat(providers): Integrate AgentRouter as a new OpenAI-compatible passthrough provider with $200 free credits via sign-up (Issue #1572).
-
feat(ui): Implement on-demand per-model testing in the provider dashboard, allowing single-token diagnostic checks without triggering rate-limits (Issue #1532).
🔑 Registered Keys Provisioning API (#464)
Auto-generate and issue OmniRoute API keys programmatically with per-provider and per-account quota enforcement.
| Endpoint | Method | Description |
|---|---|---|
/api/v1/registered-keys |
POST |
Issue a new key — raw key returned once only |
/api/v1/registered-keys |
GET |
List registered keys (masked) |
/api/v1/registered-keys/{id} |
GET |
Get key metadata |
/api/v1/registered-keys/{id} |
DELETE |
Revoke a key |
/api/v1/registered-keys/{id}/revoke |
POST |
Revoke (for clients without DELETE support) |
/api/v1/quotas/check |
GET |
Pre-validate quota before issuing |
/api/v1/providers/{id}/limits |
GET/PUT |
Configure per-provider issuance limits |
/api/v1/accounts/{id}/limits |
GET/PUT |
Configure per-account issuance limits |
/api/v1/issues/report |
POST |
Report quota events to GitHub Issues |
DB — Migration 008: Three new tables: registered_keys, provider_key_limits, account_key_limits.
Security: Keys stored as SHA-256 hashes. Raw key shown once on creation, never retrievable again.
Quota types: maxActiveKeys, dailyIssueLimit, hourlyIssueLimit per provider and per account.
Idempotency: idempotency_key field prevents duplicate issuance. Returns 409 IDEMPOTENCY_CONFLICT if key was already used.
Budget per key: dailyBudget / hourlyBudget — limits how many requests a key can route per window.
GitHub reporting: Optional. Set GITHUB_ISSUES_REPO + GITHUB_ISSUES_TOKEN to auto-create GitHub issues on quota exceeded or issuance failures.
🎨 Provider Icons — @lobehub/icons (#529)
All provider icons in the dashboard now use @lobehub/icons React components (130+ providers with SVG).
Fallback chain: Lobehub SVG → existing /providers/{id}.png → generic icon. Uses a proper React ErrorBoundary pattern.
🔄 Model Auto-Sync Scheduler (#488)
OmniRoute now automatically refreshes model lists for connected providers every 24 hours.
- Runs on server startup via the existing
/api/sync/initializehook - Configurable via
MODEL_SYNC_INTERVAL_HOURSenvironment variable - Covers 16 major providers
- Records last sync time in the settings database
🔧 Bug Fixes
OAuth & Auth
- #537 — Gemini CLI OAuth: Clear actionable error when
GEMINI_OAUTH_CLIENT_SECRETis missing in Docker/self-hosted deployments. Previously showed crypticclient_secret is missingfrom Google. Now provides specificdocker-compose.ymland~/.omniroute/.envinstructions.
Providers & Routing
- #536 — LongCat AI: Fixed
baseUrl(api.longcat.chat/openai) andauthHeader(Authorization: Bearer). - #535 — Pinned model override:
body.modelis now correctly set topinnedModelwhen context-cache protection is active. - #532 — OpenCode Go key validation: Now uses the
zen/v1test endpoint (testKeyBaseUrl) — same key works for both tiers.
CLI & Tools
- #527 — Claude Code + Codex loop:
tool_resultblocks are now converted to text instead of dropped, stopping infinite tool-result loops. - #524 — OpenCode config save: Added
saveOpenCodeConfig()handler (XDG_CONFIG_HOME aware, writes TOML). - #521 — Login stuck: Login no longer freezes after skipping password setup — redirects correctly to onboarding.
- #522 — API Manager: Removed misleading "Copy masked key" button (replaced with a lock icon tooltip).
- #532 — OpenCode Go config: Guide settings handler now handles
opencodetoolId.
Developer Experience
- #489 — Antigravity: Missing
googleProjectIdreturns a structured 422 error with reconnect guidance instead of a cryptic crash. - #510 — Windows paths: MSYS2/Git-Bash paths (
/c/Program Files/...) are now normalized toC:\Program Files\...automatically. - #492 — CLI startup:
omnirouteCLI now detectsmise/nvm-managed Node whenapp/server.jsis missing and shows targeted fix instructions.
📖 Documentation Updates
- #513 — Docker password reset:
INITIAL_PASSWORDenv var workaround documented - #520 — pnpm:
pnpm approve-builds better-sqlite3step documented
✅ Issues Resolved in v3.0.0
#464 #488 #489 #492 #510 #513 #520 #521 #522 #524 #527 #529 #532 #535 #536 #537
🔀 Community PRs Merged
| PR | Author | Summary |
|---|---|---|
| #530 | @kang-heewon | OpenCode Zen + Go providers with OpencodeExecutor and improved tests |
[3.0.0-rc.7] - 2026-03-23
🔧 Improvements (sub2api Gap Analysis — T05, T08, T09, T13, T14)
- T05 — Rate-limit DB persistence:
setConnectionRateLimitUntil(),isConnectionRateLimited(),getRateLimitedConnections()inproviders.ts. The existingrate_limited_untilcolumn is now exposed as a dedicated API — OAuth token refresh must NOT touch this field to prevent rate-limit loops. - T08 — Per-API-key session limit:
max_sessions INTEGER DEFAULT 0added toapi_keysvia auto-migration.sessionManager.tsgainsregisterKeySession(),unregisterKeySession(),checkSessionLimit(), andgetActiveSessionCountForKey(). Callers inchatCore.jscan enforce the limit and decrement onreq.close. - T09 — Codex vs Spark rate-limit scopes:
getCodexModelScope()andgetCodexRateLimitKey()incodex.ts. Standard models (gpt-5.x-codex,codex-mini) get scope"codex"; spark models (codex-spark*) get scope"spark". Rate-limit keys should be${accountId}:${scope}so exhausting one pool doesn't block the other. - T13 — Stale quota display fix:
getEffectiveQuotaUsage(used, resetAt)returns0when the reset window has passed;formatResetCountdown(resetAt)returns a human-readable countdown string (e.g."2h 35m"). Both exported fromproviders.ts+localDb.tsfor dashboard consumption. - T14 — Proxy fast-fail: new
src/lib/proxyHealth.tswithisProxyReachable(proxyUrl, timeoutMs=2000)(TCP check, ≤2s instead of 30s timeout),getCachedProxyHealth(),invalidateProxyHealth(), andgetAllProxyHealthStatuses(). Results cached 30s by default; configurable viaPROXY_FAST_FAIL_TIMEOUT_MS/PROXY_HEALTH_CACHE_TTL_MS.
🧪 Tests
- Test suite: 832 tests, 0 failures
[3.0.0-rc.6] - 2026-03-23
🔧 Bug Fixes & Improvements (sub2api Gap Analysis — T01–T15)
- T01 —
requested_modelcolumn incall_logs(migration 009): track which model the client originally requested vs the actual routed model. Enables fallback rate analytics. - T02 — Strip empty text blocks from nested
tool_result.content: prevents Anthropic 400 errors (text content blocks must be non-empty) when Claude Code chains tool results. - T03 — Parse
x-codex-5h-*/x-codex-7d-*headers:parseCodexQuotaHeaders()+getCodexResetTime()extract Codex quota windows for precise cooldown scheduling instead of generic 5-min fallback. - T04 —
X-Session-Idheader for external sticky routing:extractExternalSessionId()insessionManager.tsreadsx-session-id/x-omniroute-sessionheaders withext:prefix to avoid collision with internal SHA-256 session IDs. Nginx-compatible (hyphenated header). - T06 — Account deactivated → permanent block:
isAccountDeactivated()inaccountFallback.tsdetects 401 deactivation signals and applies a 1-year cooldown to prevent retrying permanently dead accounts. - T07 — X-Forwarded-For IP validation: new
src/lib/ipUtils.tswithextractClientIp()andgetClientIpFromRequest()— skipsunknown/non-IP entries inX-Forwarded-Forchains (Nginx/proxy-forwarded requests). - T10 — Credits exhausted → distinct fallback:
isCreditsExhausted()inaccountFallback.tsreturns 1h cooldown withcreditsExhaustedflag, distinct from generic 429 rate limiting. - T11 —
maxreasoning effort → 131072 budget tokens:EFFORT_BUDGETSandTHINKING_LEVEL_MAPupdated; reverse mapping now returns"max"for full-budget responses. Unit test updated. - T12 — MiniMax M2.7 pricing entries added:
minimax-m2.7,MiniMax-M2.7,minimax-m2.7-highspeedadded to pricing table (sub2api PR #1120). M2.5/GLM-4.7/GLM-5/Kimi pricing already existed. - T15 — Array content normalization:
normalizeContentToString()helper inopenai-to-claude.tscorrectly collapses array-formatted system/tool messages to string before sending to Anthropic.
🧪 Tests
- Test suite: 832 tests, 0 failures (unchanged from rc.5)
[3.0.0-rc.5] - 2026-03-22
✨ New Features
-
feat(docs): integrate multi-page documentation into OmniRoute dashboard (#1969)
-
feat(settings): add request body limit setting (#1968)
-
feat(auth): add Gemini CLI OAuth client secret default (#1974)
-
feat(models): expose models.dev context windows in /v1/models (#1972)
-
fix(db): resolve legacy encryption fallback causing re-encryption loops (#1941)
-
fix(auth): fix Codex assistant final_answer response sanitization (#1965)
-
feat(providers): Implement Image Generation and Editing capabilities for ChatGPT Web, including in-band chat image generation and caching (#1606).
-
feat(ui): Integrate OpenCode Zen/Go API tool logo SVG and polish API key copy-to-clipboard interactions (#1607).
-
feat(providers): Integrate AgentRouter as a new OpenAI-compatible passthrough provider with $200 free credits via sign-up (Issue #1572).
-
feat(ui): Implement on-demand per-model testing in the provider dashboard, allowing single-token diagnostic checks without triggering rate-limits (Issue #1532).
-
#464 — Registered Keys Provisioning API: auto-issue API keys with per-provider & per-account quota enforcement
POST /api/v1/registered-keys— issue keys with idempotency supportGET /api/v1/registered-keys— list (masked) registered keysGET /api/v1/registered-keys/{id}— get key metadataDELETE /api/v1/registered-keys/{id}/POST ../{id}/revoke— revoke keysGET /api/v1/quotas/check— pre-validate before issuingPUT /api/v1/providers/{id}/limits— set provider issuance limitsPUT /api/v1/accounts/{id}/limits— set account issuance limitsPOST /api/v1/issues/report— optional GitHub issue reporting- DB migration 008:
registered_keys,provider_key_limits,account_key_limitstables
[3.0.0-rc.4] - 2026-03-22
✨ New Features
-
feat(docs): integrate multi-page documentation into OmniRoute dashboard (#1969)
-
feat(settings): add request body limit setting (#1968)
-
feat(auth): add Gemini CLI OAuth client secret default (#1974)
-
feat(models): expose models.dev context windows in /v1/models (#1972)
-
fix(db): resolve legacy encryption fallback causing re-encryption loops (#1941)
-
fix(auth): fix Codex assistant final_answer response sanitization (#1965)
-
feat(providers): Implement Image Generation and Editing capabilities for ChatGPT Web, including in-band chat image generation and caching (#1606).
-
feat(ui): Integrate OpenCode Zen/Go API tool logo SVG and polish API key copy-to-clipboard interactions (#1607).
-
feat(providers): Integrate AgentRouter as a new OpenAI-compatible passthrough provider with $200 free credits via sign-up (Issue #1572).
-
feat(ui): Implement on-demand per-model testing in the provider dashboard, allowing single-token diagnostic checks without triggering rate-limits (Issue #1532).
-
#530 (PR) — OpenCode Zen and OpenCode Go providers added (by @kang-heewon)
- New
OpencodeExecutorwith multi-format routing (/chat/completions,/messages,/responses) - 7 models across both tiers
- New
[3.0.0-rc.3] - 2026-03-22
✨ New Features
-
feat(docs): integrate multi-page documentation into OmniRoute dashboard (#1969)
-
feat(settings): add request body limit setting (#1968)
-
feat(auth): add Gemini CLI OAuth client secret default (#1974)
-
feat(models): expose models.dev context windows in /v1/models (#1972)
-
fix(db): resolve legacy encryption fallback causing re-encryption loops (#1941)
-
fix(auth): fix Codex assistant final_answer response sanitization (#1965)
-
feat(providers): Implement Image Generation and Editing capabilities for ChatGPT Web, including in-band chat image generation and caching (#1606).
-
feat(ui): Integrate OpenCode Zen/Go API tool logo SVG and polish API key copy-to-clipboard interactions (#1607).
-
feat(providers): Integrate AgentRouter as a new OpenAI-compatible passthrough provider with $200 free credits via sign-up (Issue #1572).
-
feat(ui): Implement on-demand per-model testing in the provider dashboard, allowing single-token diagnostic checks without triggering rate-limits (Issue #1532).
-
#529 — Provider icons now use @lobehub/icons with graceful PNG fallback and a
ProviderIconcomponent (130+ providers supported) -
#488 — Auto-update model lists every 24h via
modelSyncScheduler(configurable viaMODEL_SYNC_INTERVAL_HOURS)
🔧 Bug Fixes
- #537 — Gemini CLI OAuth: now shows clear actionable error when
GEMINI_OAUTH_CLIENT_SECRETis missing in Docker/self-hosted deployments
[3.0.0-rc.2] - 2026-03-22
🔧 Bug Fixes
- #536 — LongCat AI key validation: fixed baseUrl (
api.longcat.chat/openai) and authHeader (Authorization: Bearer) - #535 — Pinned model override:
body.modelis now set topinnedModelwhen context-cache protection detects a pinned model - #524 — OpenCode config now saved correctly: added
saveOpenCodeConfig()handler (XDG_CONFIG_HOME aware, writes TOML)
[3.0.0-rc.1] - 2026-03-22
🔧 Bug Fixes
- #521 — Login no longer gets stuck after skipping password setup (redirects to onboarding)
- #522 — API Manager: Removed misleading "Copy masked key" button (replaced with lock icon tooltip)
- #527 — Claude Code + Codex superpowers loop:
tool_resultblocks now converted to text instead of dropped - #532 — OpenCode GO API key validation now uses the correct
zen/v1endpoint (testKeyBaseUrl) - #489 — Antigravity: missing
googleProjectIdreturns structured 422 error with reconnect guidance - #510 — Windows: MSYS2/Git-Bash paths (
/c/Program Files/...) are now normalized toC:\Program Files\... - #492 —
omnirouteCLI now detectsmise/nvmwhenapp/server.jsis missing and shows targeted fix
📖 Documentation
- #513 — Docker password reset:
INITIAL_PASSWORDenv var workaround documented - #520 — pnpm:
pnpm approve-builds better-sqlite3documented
✅ Closed Issues
#489, #492, #510, #513, #520, #521, #522, #525, #527, #532
[2.9.5] — 2026-03-22
Sprint: New OpenCode providers, embedding credentials fix, CLI masked key bug, CACHE_TAG_PATTERN fix.
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
CLI tools save masked API key to config files —
claude-settings,cline-settings, andopenclaw-settingsPOST routes now accept akeyIdparam and resolve the real API key from DB before writing to disk.ClaudeToolCardupdated to sendkeyIdinstead of the masked display string. Fixes #523, #526. -
Custom embedding providers:
No credentialserror —/v1/embeddingsnow trackscredentialsProviderIdseparately from the routing prefix, so credentials are fetched from the matching provider node ID rather than the public prefix string. Fixes a regression wheregoogle/gemini-embedding-001and similar custom-provider models would always fail with a credentials error. Fixes #532-related. (PR #528 by @jacob2826) -
Context cache protection regex misses
prefix —CACHE_TAG_PATTERNincomboAgentMiddleware.tsupdated to match both literal(backslash-n) and actual newline U+000A thatcombo.tsstreaming injects around the<omniModel>tag after fix #515. Fixes #531.
✨ New Providers
- OpenCode Zen — Free tier gateway at
opencode.ai/zen/v1with 3 models:minimax-m2.5-free,big-pickle,gpt-5-nano - OpenCode Go — Subscription service at
opencode.ai/zen/go/v1with 4 models:glm-5,kimi-k2.5,minimax-m2.7(Claude format),minimax-m2.5(Claude format) - Both providers use the new
OpencodeExecutorwhich routes dynamically to/chat/completions,/messages,/responses, or/models/{model}:generateContentbased on the requested model. (PR #530 by @kang-heewon)
[2.9.4] — 2026-03-21
Sprint: Bug fixes — preserve Codex prompt cache key, fix tagContent JSON escaping, sync expired token status to DB.
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
fix(translator): Preserve
prompt_cache_keyin Responses API → Chat Completions translation (#517) — The field is a cache-affinity signal used by Codex; stripping it was preventing prompt cache hits. Fixed inopenai-responses.tsandresponsesApiHelper.ts. -
fix(combo): Escape
intagContentso injected JSON string is valid (#515) — Template literal newlines (U+000A) are not allowed unescaped inside JSON string values. Replaced with\nliteral sequences inopen-sse/services/combo.ts. -
fix(usage): Sync expired token status back to DB on live auth failure (#491) — When the Limits & Quotas live check returns 401/403, the connection
testStatusis now updated to"expired"in the database so the Providers page reflects the same degraded state. Fixed insrc/app/api/usage/[connectionId]/route.ts.
[2.9.3] — 2026-03-21
Sprint: Add 5 new free AI providers — LongCat, Pollinations, Cloudflare AI, Scaleway, AI/ML API.
✨ New Providers
- feat(providers/longcat): Add LongCat AI (
lc/) — 50M tokens/day free (Flash-Lite) + 500K/day (Chat/Thinking) during public beta. OpenAI-compatible, standard Bearer auth. - feat(providers/pollinations): Add Pollinations AI (
pol/) — no API key required. Proxies GPT-5, Claude, Gemini, DeepSeek V3, Llama 4 (1 req/15s free). Custom executor handles optional auth. - feat(providers/cloudflare-ai): Add Cloudflare Workers AI (
cf/) — 10K Neurons/day free (~150 LLM responses or 500s Whisper audio). 50+ models on global edge. Custom executor builds dynamic URL withaccountIdfrom credentials. - feat(providers/scaleway): Add Scaleway Generative APIs (
scw/) — 1M free tokens for new accounts. EU/GDPR compliant (Paris). Qwen3 235B, Llama 3.1 70B, Mistral Small 3.2. - feat(providers/aimlapi): Add AI/ML API (
aiml/) — $0.025/day free credit, 200+ models (GPT-4o, Claude, Gemini, Llama) via single aggregator endpoint.
🔄 Provider Updates
- feat(providers/together): Add
hasFree: true+ 3 permanently free model IDs:Llama-3.3-70B-Instruct-Turbo-Free,Llama-Vision-Free,DeepSeek-R1-Distill-Llama-70B-Free - feat(providers/gemini): Add
hasFree: true+freeNote(1,500 req/day, no credit card needed, aistudio.google.com) - chore(providers/gemini): Rename display name to
Gemini (Google AI Studio)for clarity
⚙️ Infrastructure
- feat(executors/pollinations): New
PollinationsExecutor— omitsAuthorizationheader when no API key provided - feat(executors/cloudflare-ai): New
CloudflareAIExecutor— dynamic URL construction requiresaccountIdin provider credentials - feat(executors): Register
pollinations,pol,cloudflare-ai,cfexecutor mappings
📝 Documentation
- docs(readme): Expanded free combo stack to 11 providers ($0 forever)
- docs(readme): Added 4 new free provider sections (LongCat, Pollinations, Cloudflare AI, Scaleway) with model tables
- docs(readme): Updated pricing table with 4 new free tier rows
- docs(i18n/pt-BR): Updated pricing table + added LongCat/Pollinations/Cloudflare AI/Scaleway sections in Portuguese
- docs(new-features/ai): 10 task spec files + master implementation plan in
docs/new-features/ai/
🧪 Tests
- Test suite: 821 tests, 0 failures (unchanged)
[2.9.2] — 2026-03-21
Sprint: Fix media transcription (Deepgram/HuggingFace Content-Type, language detection) and TTS error display.
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
fix(transcription): Deepgram and HuggingFace audio transcription now correctly map
video/mp4→audio/mp4and other media MIME types via newresolveAudioContentType()helper. Previously, uploading.mp4files consistently returned "No speech detected" because Deepgram was receivingContent-Type: video/mp4. -
fix(transcription): Added
detect_language=trueto Deepgram requests — auto-detects audio language (Portuguese, Spanish, etc.) instead of defaulting to English. Fixes non-English transcriptions returning empty or garbage results. -
fix(transcription): Added
punctuate=trueto Deepgram requests for higher-quality transcription output with correct punctuation. -
fix(tts):
[object Object]error display in Text-to-Speech responses fixed in bothaudioSpeech.tsandaudioTranscription.ts. TheupstreamErrorResponse()function now correctly extracts nested string messages from providers like ElevenLabs that return{ error: { message: "...", status_code: 401 } }instead of a flat error string.
🧪 Tests
- Test suite: 821 tests, 0 failures (unchanged)
Triaged Issues
- #508 — Tool call format regression: requested proxy logs and provider chain info (
needs-info) - #510 — Windows CLI healthcheck path: requested shell/Node version info (
needs-info) - #485 — Kiro MCP tool calls: closed as external Kiro issue (not OmniRoute)
- #442 — Baseten /models endpoint: closed (documented manual workaround)
- #464 — Key provisioning API: acknowledged as roadmap item
[2.9.1] — 2026-03-21
Sprint: Fix SSE omniModel data loss, merge per-protocol model compatibility.
Bug Fixes
- #511 — Critical:
<omniModel>tag was sent afterfinish_reason:stopin SSE streams, causing data loss. Tag is now injected into the first non-empty content chunk, guaranteeing delivery before SDKs close the connection.
Merged PRs
- PR #512 (@zhangqiang8vip): Per-protocol model compatibility —
normalizeToolCallIdandpreserveOpenAIDeveloperRolecan now be configured per client protocol (OpenAI, Claude, Responses API). NewcompatByProtocolfield in model config with Zod validation.
Triaged Issues
- #510 — Windows CLI healthcheck_failed: requested PATH/version info
- #509 — Turbopack Electron regression: upstream Next.js bug, documented workarounds
- #508 — macOS black screen: suggested
--disable-gpuworkaround
[2.9.0] — 2026-03-20
Sprint: Cross-platform machineId fix, per-API-key rate limits, streaming context cache, Alibaba DashScope, search analytics, ZWS v5, and 8 issues closed.
✨ New Features
-
feat(docs): integrate multi-page documentation into OmniRoute dashboard (#1969)
-
feat(settings): add request body limit setting (#1968)
-
feat(auth): add Gemini CLI OAuth client secret default (#1974)
-
feat(models): expose models.dev context windows in /v1/models (#1972)
-
fix(db): resolve legacy encryption fallback causing re-encryption loops (#1941)
-
fix(auth): fix Codex assistant final_answer response sanitization (#1965)
-
feat(providers): Implement Image Generation and Editing capabilities for ChatGPT Web, including in-band chat image generation and caching (#1606).
-
feat(ui): Integrate OpenCode Zen/Go API tool logo SVG and polish API key copy-to-clipboard interactions (#1607).
-
feat(providers): Integrate AgentRouter as a new OpenAI-compatible passthrough provider with $200 free credits via sign-up (Issue #1572).
-
feat(ui): Implement on-demand per-model testing in the provider dashboard, allowing single-token diagnostic checks without triggering rate-limits (Issue #1532).
-
feat(search): Search Analytics tab in
/dashboard/analytics— provider breakdown, cache hit rate, cost tracking. New API:GET /api/v1/search/analytics(#feat/search-provider-routing) -
feat(provider): Alibaba Cloud DashScope added with custom endpoint path validation — configurable
chatPathandmodelsPathper node (#feat/custom-endpoint-paths) -
feat(api): Per-API-key request-count limits —
max_requests_per_dayandmax_requests_per_minutecolumns with in-memory sliding-window enforcement returning HTTP 429 (#452) -
feat(dev): ZWS v5 — HMR leak fix (485 DB connections → 1), memory 2.4GB → 195MB,
globalThissingletons, Edge Runtime warning fix (@zhangqiang8vip)
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
fix(#506): Cross-platform
machineId—getMachineIdRaw()rewritten with try/catch waterfall (Windows REG.exe → macOS ioreg → Linux file read → hostname →os.hostname()). Eliminatesprocess.platformbranching that Next.js bundler dead-code-eliminated, fixing'head' is not recognizedon Windows. Also fixes #466. -
fix(#493): Custom provider model naming — removed incorrect prefix stripping in
DefaultExecutor.transformRequest()that mangled org-scoped model IDs likezai-org/GLM-5-FP8. -
fix(#490): Streaming + context cache protection —
TransformStreamintercepts SSE to inject<omniModel>tag before[DONE]marker, enabling context cache protection for streaming responses. -
fix(#458): Combo schema validation —
system_message,tool_filter_regex,context_cache_protectionfields now pass Zod validation on save. -
fix(#487): KIRO MITM card cleanup — removed ZWS_README, generified
AntigravityToolCardto use dynamic tool metadata.
🧪 Tests
- Added Anthropic-format tools filter unit tests (PR #397) — 8 regression tests for
tool.namewithout.functionwrapper - Test suite: 821 tests, 0 failures (up from 813)
📋 Issues Closed (8)
- #506 — Windows machineId
headnot recognized (fixed) - #493 — Custom provider model naming (fixed)
- #490 — Streaming context cache (fixed)
- #452 — Per-API-key request limits (implemented)
- #466 — Windows login failure (same root cause as #506)
- #504 — MITM inactive (expected behavior)
- #462 — Gemini CLI PSA (resolved)
- #434 — Electron app crash (duplicate of #402)
[2.8.9] — 2026-03-20
Sprint: Merge community PRs, fix KIRO MITM card, dependency updates.
Merged PRs
- PR #498 (@Sajid11194): Fix Windows machine ID crash (
undefined\REG.exe). Replacesnode-machine-idwith native OS registry queries. Closes #486. - PR #497 (@zhangqiang8vip): Fix dev-mode HMR resource leaks — 485 leaked DB connections → 1, memory 2.4GB → 195MB.
globalThissingletons, Edge Runtime warning fix, Windows test stability. (+1168/-338 across 22 files) - PRs #499-503 (Dependabot): GitHub Actions updates —
docker/build-push-action@7,actions/checkout@6,peter-evans/dockerhub-description@5,docker/setup-qemu-action@4,docker/login-action@4.
Bug Fixes
- #505 — KIRO MITM card now displays tool-specific instructions (
api.anthropic.com) instead of Antigravity-specific text. - #504 — Responded with UX clarification (MITM "Inactive" is expected behavior when proxy is not running).
[2.8.8] — 2026-03-20
Sprint: Fix OAuth batch test crash, add "Test All" button to individual provider pages.
Bug Fixes
- OAuth batch test crash (ERR_CONNECTION_REFUSED): Replaced sequential for-loop with 5-connection concurrency limit + 30s per-connection timeout via
Promise.race()+Promise.allSettled(). Prevents server crash when testing large OAuth provider groups (~30+ connections).
Features
- "Test All" button on provider pages: Individual provider pages (e.g.,
/providers/codex) now show a "Test All" button in the Connections header when there are 2+ connections. UsesPOST /api/providers/test-batchwith{mode: "provider", providerId}. Results displayed in a modal with pass/fail summary and per-connection diagnosis.
[2.8.7] — 2026-03-20
Sprint: Merge PR #495 (Bottleneck 429 drop), fix #496 (custom embedding providers), triage features.
Bug Fixes
- Bottleneck 429 infinite wait (PR #495 by @xandr0s): On 429,
limiter.stop({ dropWaitingJobs: true })immediately fails all queued requests so upstream callers can trigger fallback. Limiter is deleted from Map so next request creates a fresh instance. - Custom embedding models unresolvable (#496):
POST /v1/embeddingsnow resolves custom embedding models from ALL provider_nodes (not just localhost). Enables models likegoogle/gemini-embedding-001added via dashboard.
Issues Responded
- #452 — Per-API-key request-count limits (acknowledged, on roadmap)
- #464 — Auto-issue API keys with provider/account limits (needs more detail)
- #488 — Auto-update model lists (acknowledged, on roadmap)
- #496 — Custom embedding provider resolution (fixed)
[2.8.6] — 2026-03-20
Sprint: Merge PR #494 (MiniMax role fix), fix KIRO MITM dashboard, triage 8 issues.
Features
- MiniMax developer→system role fix (PR #494 by @zhangqiang8vip): Per-model
preserveDeveloperRoletoggle. Adds "Compatibility" UI in providers page. Fixes 422 "role param error" for MiniMax and similar gateways. - roleNormalizer:
normalizeDeveloperRole()now acceptspreserveDeveloperRoleparameter with tri-state behavior (undefined=keep, true=keep, false=convert). - DB: New
getModelPreserveOpenAIDeveloperRole()andmergeModelCompatOverride()inmodels.ts.
Bug Fixes
- KIRO MITM dashboard (#481/#487):
CLIToolsPageClientnow routes anyconfigType: "mitm"tool toAntigravityToolCard(MITM Start/Stop controls). Previously only Antigravity was hardcoded. - AntigravityToolCard generic: Uses
tool.image,tool.description,tool.idinstead of hardcoded Antigravity values. Guards against missingdefaultModels.
Cleanup
- Removed
ZWS_README_V2.md(development-only docs from PR #494).
Issues Triaged (8)
- #487 — Closed (KIRO MITM fixed in this release)
- #486 — needs-info (Windows REG.exe PATH issue)
- #489 — needs-info (Antigravity projectId missing, OAuth reconnect needed)
- #492 — needs-info (missing app/server.js on mise-managed Node)
- #490 — Acknowledged (streaming + context cache blocking, fix planned)
- #491 — Acknowledged (Codex auth state inconsistency)
- #493 — Acknowledged (Modal provider model name prefix, workaround provided)
- #488 — Feature request backlog (auto-update model lists)
[2.8.5] — 2026-03-19
Sprint: Fix zombie SSE streams, context cache first-turn, KIRO MITM, and triage 5 external issues.
Bug Fixes
- Zombie SSE Streams (#473): Reduce
STREAM_IDLE_TIMEOUT_MSfrom 300s → 120s for faster combo fallback when providers hang mid-stream. Configurable via env var. - Context Cache Tag (#474): Fix
injectModelTag()to handle first-turn requests (no assistant messages) — context cache protection now works from the very first response. - KIRO MITM (#481): Change KIRO
configTypefromguide→mitmso the dashboard renders MITM Start/Stop controls. - E2E Test (CI): Fix
providers-bailian-coding-plan.spec.ts— dismiss pre-existing modal overlay before clicking Add API Key button.
Closed Issues
- #473 — Zombie SSE streams bypass combo fallback
- #474 — Context cache
<omniModel>tag missing on first turn - #481 — MITM for KIRO not activatable from dashboard
- #468 — Gemini CLI remote server (superseded by #462 deprecation)
- #438 — Claude unable to write files (external CLI issue)
- #439 — AppImage doesn't work (documented libfuse2 workaround)
- #402 — ARM64 DMG "damaged" (documented xattr -cr workaround)
- #460 — CLI not runnable on Windows (documented PATH fix)
[2.8.4] — 2026-03-19
Sprint: Gemini CLI deprecation, VM guide i18n fix, dependabot security fix, provider schema expansion.
Features
- Gemini CLI Deprecation (#462): Mark
gemini-cliprovider as deprecated with warning — Google restricts third-party OAuth usage from March 2026 - Provider Schema (#462): Expand Zod validation with
deprecated,deprecationReason,hasFree,freeNote,authHint,apiHintoptional fields
Bug Fixes
- VM Guide i18n (#471): Add
VM_DEPLOYMENT_GUIDE.mdto i18n translation pipeline, regenerate all 30 locale translations from English source (were stuck in Portuguese)
Security
- deps: Bump
flatted3.3.3 → 3.4.2 — fixes CWE-1321 prototype pollution (#484, @dependabot)
Closed Issues
- #472 — Model Aliases regression (fixed in v2.8.2)
- #471 — VM guide translations broken
- #483 — Trailing
data: nullafter[DONE](fixed in v2.8.3)
Merged PRs
- #484 — deps: bump flatted from 3.3.3 to 3.4.2 (@dependabot)
[2.8.3] — 2026-03-19
Sprint: Czech i18n, SSE protocol fix, VM guide translation.
Features
- Czech Language (#482): Full Czech (cs) i18n — 22 docs, 2606 UI strings, language switcher updates (@zen0bit)
- VM Deployment Guide: Translated from Portuguese to English as the source document (@zen0bit)
Bug Fixes
- SSE Protocol (#483): Stop sending trailing
data: nullafter[DONE]signal — fixesAI_TypeValidationErrorin strict AI SDK clients (Zod-based validators)
Merged PRs
- #482 — Add Czech language + Fix VM_DEPLOYMENT_GUIDE.md English source (@zen0bit)
[2.8.2] — 2026-03-19
Sprint: 2 merged PRs, model aliases routing fix, log export, and issue triage.
Features
- Log Export: New Export button on
/dashboard/logswith time range dropdown (1h, 6h, 12h, 24h). Downloads JSON of request/proxy/call logs via/api/logs/exportAPI (#user-request)
Bug Fixes
- Model Aliases Routing (#472): Settings → Model Aliases now correctly affect provider routing, not just format detection. Previously
resolveModelAlias()output was only used forgetModelTargetFormat()but the original model ID was sent to the provider - Stream Flush Usage (#480): Usage data from the last SSE event in the buffer is now correctly extracted during stream flush (merged from @prakersh)
Merged PRs
- #480 — Extract usage from remaining buffer in flush handler (@prakersh)
- #479 — Add missing Codex 5.3/5.4 and Anthropic model ID pricing entries (@prakersh)
[2.8.1] — 2026-03-19
Sprint: Five community PRs — streaming call log fixes, Kiro compatibility, cache token analytics, Chinese translation, and configurable tool call IDs.
✨ Features
- feat(logs): Call log response content now correctly accumulated from raw provider chunks (OpenAI/Claude/Gemini) before translation, fixing empty response payloads in streaming mode (#470, @zhangqiang8vip)
- feat(providers): Per-model configurable 9-char tool call ID normalization (Mistral-style) — only models with the option enabled get truncated IDs (#470)
- feat(api): Key PATCH API expanded to support
allowedConnections,name,autoResolve,isActive, andaccessSchedulefields (#470) - feat(dashboard): Response-first layout in request log detail UI (#470)
- feat(i18n): Improved Chinese (zh-CN) translation — complete retranslation (#475, @only4copilot)
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
fix(kiro): Strip injected
modelfield from request body — Kiro API rejects unknown top-level fields (#478, @prakersh) -
fix(usage): Include cache read + cache creation tokens in usage history input totals for accurate analytics (#477, @prakersh)
-
fix(callLogs): Support Claude format usage fields (
input_tokens/output_tokens) alongside OpenAI format, include all cache token variants (#476, @prakersh)
[2.8.0] — 2026-03-19
Sprint: Bailian Coding Plan provider with editable base URLs, plus community contributions for Alibaba Cloud and Kimi Coding.
✨ Features
- feat(providers): Added Bailian Coding Plan (
bailian-coding-plan) — Alibaba Model Studio with Anthropic-compatible API. Static catalog of 8 models including Qwen3.5 Plus, Qwen3 Coder, MiniMax M2.5, GLM 5, and Kimi K2.5. Includes custom auth validation (400=valid, 401/403=invalid) (#467, @Mind-Dragon) - feat(admin): Editable default URL in Provider Admin create/edit flows — users can configure custom base URLs per connection. Persisted in
providerSpecificData.baseUrlwith Zod schema validation rejecting non-http(s) schemes (#467)
🧪 Tests
- Added 30+ unit tests and 2 e2e scenarios for Bailian Coding Plan provider covering auth validation, schema hardening, route-level behavior, and cross-layer integration
[2.7.10] — 2026-03-19
Sprint: Two new community-contributed providers (Alibaba Cloud Coding, Kimi Coding API-key) and Docker pino fix.
✨ Features
- feat(providers): Added Alibaba Cloud Coding Plan support with two OpenAI-compatible endpoints —
alicode(China) andalicode-intl(International), each with 8 models (#465, @dtk1985) - feat(providers): Added dedicated
kimi-coding-apikeyprovider path — API-key-based Kimi Coding access is no longer forced through OAuth-onlykimi-codingroute. Includes registry, constants, models API, config, and validation test (#463, @Mind-Dragon)
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
fix(docker): Added missing
split2dependency to Docker image —pino-abstract-transportrequires it at runtime but it was not being copied into the standalone container, causingCannot find module 'split2'crashes (#459)
[2.7.9] — 2026-03-18
Sprint: Codex responses subpath passthrough natively supported, Windows MITM crash fixed, and Combos agent schemas adjusted.
✨ Features
- feat(codex): Native responses subpath passthrough for Codex — natively routes
POST /v1/responses/compactto Codex upstream, maintaining Claude Code compatibility without stripping the/compactsuffix (#457)
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
fix(combos): Zod schemas (
updateComboSchemaandcreateComboSchema) now includesystem_message,tool_filter_regex, andcontext_cache_protection. Fixes bug where agent-specific settings created via the dashboard were silently discarded by the backend validation layer (#458) -
fix(mitm): Kiro MITM profile crash on Windows fixed —
node-machine-idfailed due to missingREG.exeenv, and the fallback threw a fatalcrypto is not definederror. Fallback now safely and correctly imports crypto (#456)
[2.7.8] — 2026-03-18
Sprint: Budget save bug + combo agent features UI + omniModel tag security fix.
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
fix(budget): "Save Limits" no longer returns 422 —
warningThresholdis now correctly sent as fraction (0–1) instead of percentage (0–100) (#451) -
fix(combos):
<omniModel>internal cache tag is now stripped before forwarding requests to providers, preventing cache session breaks (#454)
✨ Features
- feat(combos): Agent Features section added to combo create/edit modal — expose
system_messageoverride,tool_filter_regex, andcontext_cache_protectiondirectly from the dashboard (#454)
[2.7.7] — 2026-03-18
Sprint: Docker pino crash, Codex CLI responses worker fix, package-lock sync.
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
fix(docker):
pino-abstract-transportandpino-prettynow explicitly copied in Docker runner stage — Next.js standalone trace misses these peer deps, causingCannot find module pino-abstract-transportcrash on startup (#449) -
fix(responses): Remove
initTranslators()from/v1/responsesroute — was crashing Next.js worker withthe worker has exiteduncaughtException on Codex CLI requests (#450)
🔧 Maintenance
- chore(deps):
package-lock.jsonnow committed on every version bump to ensure Dockernpm ciuses exact dependency versions
[2.7.5] — 2026-03-18
Sprint: UX improvements and Windows CLI healthcheck fix.
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
fix(ux): Show default password hint on login page — new users now see
"Default password: 123456"below the password input (#437) -
fix(cli): Claude CLI and other npm-installed tools now correctly detected as runnable on Windows — spawn uses
shell:trueto resolve.cmdwrappers via PATHEXT (#447)
[2.7.4] — 2026-03-18
Sprint: Search Tools dashboard, i18n fixes, Copilot limits, Serper validation fix.
🚀 Features
- feat(search): Add Search Playground (10th endpoint), Search Tools page with Compare Providers/Rerank Pipeline/Search History, local rerank routing, auth guards on search API (#443 by @Regis-RCR)
- New route:
/dashboard/search-tools - Sidebar entry under Debug section
GET /api/search/providersandGET /api/search/statswith auth guards- Local provider_nodes routing for
/v1/rerank - 30+ i18n keys in search namespace
- New route:
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
fix(search): Fix Brave news normalizer (was returning 0 results), enforce max_results truncation post-normalization, fix Endpoints page fetch URL (#443 by @Regis-RCR)
-
fix(analytics): Localize analytics day/date labels — replace hardcoded Portuguese strings with
Intl.DateTimeFormat(locale)(#444 by @hijak) -
fix(copilot): Correct GitHub Copilot account type display, filter misleading unlimited quota rows from limits dashboard (#445 by @hijak)
-
fix(providers): Stop rejecting valid Serper API keys — treat non-4xx responses as valid authentication (#446 by @hijak)
[2.7.3] — 2026-03-18
Sprint: Codex direct API quota fallback fix.
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
fix(codex): Block weekly-exhausted accounts in direct API fallback (#440)
resolveQuotaWindow()prefix matching:"weekly"now matches"weekly (7d)"cache keysapplyCodexWindowPolicy()enforcesuseWeekly/use5htoggles correctly- 4 new regression tests (766 total)
[2.7.2] — 2026-03-18
Sprint: Light mode UI contrast fixes.
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
fix(logs): Fix light mode contrast in request logs filter buttons and combo badge (#378)
- Error/Success/Combo filter buttons now readable in light mode
- Combo row badge uses stronger violet in light mode
[2.7.1] — 2026-03-17
Sprint: Unified web search routing (POST /v1/search) with 5 providers + Next.js 16.1.7 security fixes (6 CVEs).
✨ New Features
-
feat(docs): integrate multi-page documentation into OmniRoute dashboard (#1969)
-
feat(settings): add request body limit setting (#1968)
-
feat(auth): add Gemini CLI OAuth client secret default (#1974)
-
feat(models): expose models.dev context windows in /v1/models (#1972)
-
fix(db): resolve legacy encryption fallback causing re-encryption loops (#1941)
-
fix(auth): fix Codex assistant final_answer response sanitization (#1965)
-
feat(providers): Implement Image Generation and Editing capabilities for ChatGPT Web, including in-band chat image generation and caching (#1606).
-
feat(ui): Integrate OpenCode Zen/Go API tool logo SVG and polish API key copy-to-clipboard interactions (#1607).
-
feat(providers): Integrate AgentRouter as a new OpenAI-compatible passthrough provider with $200 free credits via sign-up (Issue #1572).
-
feat(ui): Implement on-demand per-model testing in the provider dashboard, allowing single-token diagnostic checks without triggering rate-limits (Issue #1532).
-
feat(search): Unified web search routing —
POST /v1/searchwith 5 providers (Serper, Brave, Perplexity, Exa, Tavily)- Auto-failover across providers, 6,500+ free searches/month
- In-memory cache with request coalescing (configurable TTL)
- Dashboard: Search Analytics tab in
/dashboard/analyticswith provider breakdown, cache hit rate, cost tracking - New API:
GET /api/v1/search/analyticsfor search request statistics - DB migration:
request_typecolumn oncall_logsfor non-chat request tracking - Zod validation (
v1SearchSchema), auth-gated, cost recorded viarecordCost()
🔒 Security
- deps: Next.js 16.1.6 → 16.1.7 — fixes 6 CVEs:
- Critical: CVE-2026-29057 (HTTP request smuggling via http-proxy)
- High: CVE-2026-27977, CVE-2026-27978 (WebSocket + Server Actions)
- Medium: CVE-2026-27979, CVE-2026-27980, CVE-2026-jcc7
📁 New Files
| File | Purpose |
|---|---|
open-sse/handlers/search.ts |
Search handler with 5-provider routing |
open-sse/config/searchRegistry.ts |
Provider registry (auth, cost, quota, TTL) |
open-sse/services/searchCache.ts |
In-memory cache with request coalescing |
src/app/api/v1/search/route.ts |
Next.js route (POST + GET) |
src/app/api/v1/search/analytics/route.ts |
Search stats API |
src/app/(dashboard)/dashboard/analytics/SearchAnalyticsTab.tsx |
Analytics dashboard tab |
src/lib/db/migrations/007_search_request_type.sql |
DB migration |
tests/unit/search-registry.test.mjs |
277 lines of unit tests |
[2.7.0] — 2026-03-17
Sprint: ClawRouter-inspired features — toolCalling flag, multilingual intent detection, benchmark-driven fallback, request deduplication, pluggable RouterStrategy, Grok-4 Fast + GLM-5 + MiniMax M2.5 + Kimi K2.5 pricing.
✨ New Models & Pricing
- feat(pricing): xAI Grok-4 Fast —
$0.20/$0.50 per 1M tokens, 1143ms p50 latency, tool calling supported - feat(pricing): xAI Grok-4 (standard) —
$0.20/$1.50 per 1M tokens, reasoning flagship - feat(pricing): GLM-5 via Z.AI —
$0.5/1M, 128K output context - feat(pricing): MiniMax M2.5 —
$0.30/1M input, reasoning + agentic tasks - feat(pricing): DeepSeek V3.2 — updated pricing
$0.27/$1.10 per 1M - feat(pricing): Kimi K2.5 via Moonshot API — direct Moonshot API access
- feat(providers): Z.AI provider added (
zaialias) — GLM-5 family with 128K output
🧠 Routing Intelligence
- feat(registry):
toolCallingflag per model in provider registry — combos can now prefer/require tool-calling capable models - feat(scoring): Multilingual intent detection for AutoCombo scoring — PT/ZH/ES/AR script/language patterns influence model selection per request context
- feat(fallback): Benchmark-driven fallback chains — real latency data (p50 from
comboMetrics) used to re-order fallback priority dynamically - feat(dedup): Request deduplication via content-hash — 5-second idempotency window prevents duplicate provider calls from retrying clients
- feat(router): Pluggable
RouterStrategyinterface inautoCombo/routerStrategy.ts— custom routing logic can be injected without modifying core
🔧 MCP Server Improvements
- feat(mcp): 2 new advanced tool schemas:
omniroute_get_provider_metrics(p50/p95/p99 per provider) andomniroute_explain_route(routing decision explanation) - feat(mcp): MCP tool auth scopes updated —
metrics:readscope added for provider metrics tools - feat(mcp):
omniroute_best_combo_for_tasknow acceptslanguageHintparameter for multilingual routing
📊 Observability
- feat(metrics):
comboMetrics.tsextended with real-time latency percentile tracking per provider/account - feat(health): Health API (
/api/monitoring/health) now returns per-providerp50LatencyanderrorRatefields - feat(usage): Usage history migration for per-model latency tracking
🗄️ DB Migrations
- feat(migrations): New column
latency_p50incombo_metricstable — zero-breaking, safe for existing users
🐛 Bug Fixes / Closures
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
close(#411): better-sqlite3 hashed module resolution on Windows — fixed in v2.6.10 (
f02c5b5) -
close(#409): GitHub Copilot chat completions fail with Claude models when files attached — fixed in v2.6.9 (
838f1d6) -
close(#405): Duplicate of #411 — resolved
[2.6.10] — 2026-03-17
Windows fix: better-sqlite3 prebuilt download without node-gyp/Python/MSVC (#426).
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
fix(install/#426): On Windows,
npm install -g omnirouteused to fail withbetter_sqlite3.node is not a valid Win32 applicationbecause the bundled native binary was compiled for Linux. Adds Strategy 1.5 toscripts/postinstall.mjs: uses@mapbox/node-pre-gyp install --fallback-to-build=false(bundled withinbetter-sqlite3) to download the correct prebuilt binary for the current OS/arch without requiring any build tools (no node-gyp, no Python, no MSVC). Falls back tonpm rebuildonly if the download fails. Adds platform-specific error messages with clear manual fix instructions.
[2.6.9] — 2026-03-17
CI fixes (t11 any-budget), bug fix #409 (file attachments via Copilot+Claude), release workflow correction.
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
fix(ci): Remove word "any" from comments in
openai-responses.tsandchatCore.tsthat were failing the t11anybudget check (false positive from regex counting comments) -
fix(chatCore): Normalize unsupported content part types before forwarding to providers (#409 — Cursor sends
{type:"file"}when.mdfiles are attached; Copilot and other OpenAI-compat providers reject with "type has to be either 'image_url' or 'text'"; fix convertsfile/documentblocks totextand drops unknown types)
🔧 Workflow
- chore(generate-release): Add ATOMIC COMMIT RULE — version bump (
npm version patch) MUST happen before committing feature files to ensure tag always points to a commit containing all version changes together
[2.6.8] — 2026-03-17
Sprint: Combo as Agent (system prompt + tool filter), Context Caching Protection, Auto-Update, Detailed Logs, MITM Kiro IDE.
🗄️ DB Migrations (zero-breaking — safe for existing users)
- 005_combo_agent_fields.sql:
ALTER TABLE combos ADD COLUMN system_message TEXT DEFAULT NULL,tool_filter_regex TEXT DEFAULT NULL,context_cache_protection INTEGER DEFAULT 0 - 006_detailed_request_logs.sql: New
request_detail_logstable with 500-entry ring-buffer trigger, opt-in via settings toggle
✨ Features
- feat(combo): System Message Override per Combo (#399 —
system_messagefield replaces or injects system prompt before forwarding to provider) - feat(combo): Tool Filter Regex per Combo (#399 —
tool_filter_regexkeeps only tools matching pattern; supports OpenAI + Anthropic formats) - feat(combo): Context Caching Protection (#401 —
context_cache_protectiontags responses with<omniModel>provider/model</omniModel>and pins model for session continuity) - feat(settings): Auto-Update via Settings (#320 —
GET /api/system/version+POST /api/system/update— checks npm registry and updates in background with pm2 restart) - feat(logs): Detailed Request Logs (#378 — captures full pipeline bodies at 4 stages: client request, translated request, provider response, client response — opt-in toggle, 64KB trim, 500-entry ring-buffer)
- feat(mitm): MITM Kiro IDE profile (#336 —
src/mitm/targets/kiro.tstargets api.anthropic.com, reuses existing MITM infrastructure)
[2.6.7] — 2026-03-17
Sprint: SSE improvements, local provider_nodes extensions, proxy registry, Claude passthrough fixes.
✨ Features
- feat(health): Background health check for local
provider_nodeswith exponential backoff (30s→300s) andPromise.allSettledto avoid blocking (#423, @Regis-RCR) - feat(embeddings): Route
/v1/embeddingsto localprovider_nodes—buildDynamicEmbeddingProvider()with hostname validation (#422, @Regis-RCR) - feat(audio): Route TTS/STT to local
provider_nodes—buildDynamicAudioProvider()with SSRF protection (#416, @Regis-RCR) - feat(proxy): Proxy registry, management APIs, and quota-limit generalization (#429, @Regis-RCR)
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
fix(sse): Strip Claude-specific fields (
metadata,anthropic_version) when target is OpenAI-compat (#421, @prakersh) -
fix(sse): Extract Claude SSE usage (
input_tokens,output_tokens, cache tokens) in passthrough stream mode (#420, @prakersh) -
fix(sse): Generate fallback
call_idfor tool calls with missing/empty IDs (#419, @prakersh) -
fix(sse): Claude-to-Claude passthrough — forward body completely untouched, no re-translation (#418, @prakersh)
-
fix(sse): Filter orphaned
tool_resultitems after Claude Code context compaction to avoid 400 errors (#417, @prakersh) -
fix(sse): Skip empty-name tool calls in Responses API translator to prevent
placeholder_toolinfinite loops (#415, @prakersh) -
fix(sse): Strip empty text content blocks before translation (#427, @prakersh)
-
fix(api): Add
refreshable: trueto Claude OAuth test config (#428, @prakersh)
📦 Dependencies
- Bump
vitest,@vitest/*and related devDependencies (#414, @dependabot)
[2.6.6] — 2026-03-17
Hotfix: Turbopack/Docker compatibility — remove
node:protocol from allsrc/imports.
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
fix(build): Removed
node:protocol prefix fromimportstatements in 17 files undersrc/. Thenode:fs,node:path,node:url,node:osetc. imports causedEcmascript file had an erroron Turbopack builds (Next.js 15 Docker) and on upgrades from older npm global installs. Affected files:migrationRunner.ts,core.ts,backup.ts,prompts.ts,dataPaths.ts, and 12 others insrc/app/api/andsrc/lib/. -
chore(workflow): Updated
generate-release.mdto make Docker Hub sync and dual-VPS deploy mandatory steps in every release.
[2.6.5] — 2026-03-17
Sprint: reasoning model param filtering, local provider 404 fix, Kilo Gateway provider, dependency bumps.
✨ New Features
-
feat(docs): integrate multi-page documentation into OmniRoute dashboard (#1969)
-
feat(settings): add request body limit setting (#1968)
-
feat(auth): add Gemini CLI OAuth client secret default (#1974)
-
feat(models): expose models.dev context windows in /v1/models (#1972)
-
fix(db): resolve legacy encryption fallback causing re-encryption loops (#1941)
-
fix(auth): fix Codex assistant final_answer response sanitization (#1965)
-
feat(providers): Implement Image Generation and Editing capabilities for ChatGPT Web, including in-band chat image generation and caching (#1606).
-
feat(ui): Integrate OpenCode Zen/Go API tool logo SVG and polish API key copy-to-clipboard interactions (#1607).
-
feat(providers): Integrate AgentRouter as a new OpenAI-compatible passthrough provider with $200 free credits via sign-up (Issue #1572).
-
feat(ui): Implement on-demand per-model testing in the provider dashboard, allowing single-token diagnostic checks without triggering rate-limits (Issue #1532).
-
feat(api): Added Kilo Gateway (
api.kilo.ai) as a new API Key provider (aliaskg) — 335+ models, 6 free models, 3 auto-routing models (kilo-auto/frontier,kilo-auto/balanced,kilo-auto/free). Passthrough models supported via/api/gateway/modelsendpoint. (PR #408 by @Regis-RCR)
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
fix(sse): Strip unsupported parameters for reasoning models (o1, o1-mini, o1-pro, o3, o3-mini). Models in the
o1/o3family rejecttemperature,top_p,frequency_penalty,presence_penalty,logprobs,top_logprobs, andnwith HTTP 400. Parameters are now stripped at thechatCorelayer before forwarding. Uses a declarativeunsupportedParamsfield per model and a precomputed O(1) Map for lookup. (PR #412 by @Regis-RCR) -
fix(sse): Local provider 404 now results in a model-only lockout (5 seconds) instead of a connection-level lockout (2 minutes). When a local inference backend (Ollama, LM Studio, oMLX) returns 404 for an unknown model, the connection remains active and other models continue working immediately. Also fixes a pre-existing bug where
modelwas not passed tomarkAccountUnavailable(). Local providers detected via hostname (localhost,127.0.0.1,::1, extensible viaLOCAL_HOSTNAMESenv var). (PR #410 by @Regis-RCR)
📦 Dependencies
better-sqlite312.6.2 → 12.8.0undici7.24.2 → 7.24.4https-proxy-agent7 → 8agent-base7 → 8
[2.6.4] — 2026-03-17
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
fix(providers): Removed non-existent model names across 5 providers:
- gemini / gemini-cli: removed
gemini-3.1-pro/flashandgemini-3-*-preview(don't exist in Google API v1beta); replaced withgemini-2.5-pro,gemini-2.5-flash,gemini-2.0-flash,gemini-1.5-pro/flash - antigravity: removed
gemini-3.1-pro-high/lowandgemini-3-flash(invalid internal aliases); replaced with real 2.x models - github (Copilot): removed
gemini-3-flash-previewandgemini-3-pro-preview; replaced withgemini-2.5-flash - nvidia: corrected
nvidia/llama-3.3-70b-instruct→meta/llama-3.3-70b-instruct(NVIDIA NIM usesmeta/namespace for Meta models); addednvidia/llama-3.1-70b-instructandnvidia/llama-3.1-405b-instruct
- gemini / gemini-cli: removed
-
fix(db/combo): Updated
free-stackcombo on remote DB: removedqw/qwen3-coder-plus(expired refresh token), correctednvidia/llama-3.3-70b-instruct→nvidia/meta/llama-3.3-70b-instruct, correctedgemini/gemini-3.1-flash→gemini/gemini-2.5-flash, addedif/deepseek-v3.2
[2.6.3] — 2026-03-16
Sprint: zod/pino hash-strip baked into build pipeline, Synthetic provider added, VPS PM2 path corrected.
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
fix(build): Turbopack hash-strip now runs at compile time for ALL packages — not just
better-sqlite3. Step 5.6 inprepublish.mjswalks every.jsinapp/.next/server/and strips the 16-char hex suffix from any hashedrequire(). Fixeszod-dcb22c...,pino-..., etc. MODULE_NOT_FOUND on global npm installs. Closes #398 -
fix(deploy): PM2 on both VPS was pointing to stale git-clone directories. Reconfigured to
app/server.jsin the npm global package. Updated/deploy-vpsworkflow to usenpm pack + scp(npm registry rejects 299MB packages).
✨ Features
- feat(provider): Synthetic (synthetic.new) — privacy-focused OpenAI-compatible inference.
passthroughModels: truefor dynamic HuggingFace model catalog. Initial models: Kimi K2.5, MiniMax M2.5, GLM 4.7, DeepSeek V3.2. (PR #404 by @Regis-RCR)
📋 Issues Closed
- close #398: npm hash regression — fixed by compile-time hash-strip in prepublish
- triage #324: Bug screenshot without steps — requested reproduction details
[2.6.2] — 2026-03-16
Sprint: module hashing fully fixed, 2 PRs merged (Anthropic tools filter + custom endpoint paths), Alibaba Cloud DashScope provider added, 3 stale issues closed.
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
fix(build): Extended webpack
externalshash-strip to cover ALLserverExternalPackages, not justbetter-sqlite3. Next.js 16 Turbopack hasheszod,pino, and every other server-external package into names likezod-dcb22c6336e0bc69that don't exist innode_modulesat runtime. A HASH_PATTERN regex catch-all now strips the 16-char suffix and falls back to the base package name. Also addedNEXT_PRIVATE_BUILD_WORKER=0inprepublish.mjsto reinforce webpack mode, plus a post-build scan that reports any remaining hashed refs. (#396, #398, PR #403) -
fix(chat): Anthropic-format tool names (
tool.namewithout.functionwrapper) were silently dropped by the empty-name filter introduced in #346. LiteLLM proxies requests withanthropic/prefix in Anthropic Messages API format, causing all tools to be filtered and Anthropic to return400: tool_choice.any may only be specified while providing tools. Fixed by falling back totool.namewhentool.function.nameis absent. Added 8 regression unit tests. (PR #397)
✨ Features
- feat(api): Custom endpoint paths for OpenAI-compatible provider nodes — configure
chatPathandmodelsPathper node (e.g./v4/chat/completions) in the provider connection UI. Includes a DB migration (003_provider_node_custom_paths.sql) and URL path sanitization (no..traversal, must start with/). (PR #400) - feat(provider): Alibaba Cloud DashScope added as OpenAI-compatible provider. International endpoint:
dashscope-intl.aliyuncs.com/compatible-mode/v1. 12 models:qwen-max,qwen-plus,qwen-turbo,qwen3-coder-plus/flash,qwq-plus,qwq-32b,qwen3-32b,qwen3-235b-a22b. Auth: Bearer API key.
📋 Issues Closed
- close #323: Cline connection error
[object Object]— fixed in v2.3.7; instructed user to upgrade from v2.2.9 - close #337: Kiro credit tracking — implemented in v2.5.5 (#381); pointed user to Dashboard → Usage
- triage #402: ARM64 macOS DMG damaged — requested macOS version, exact error, and advised
xattr -d com.apple.quarantineworkaround
[2.6.1] — 2026-03-15
Critical startup fix: v2.6.0 global npm installs crashed with a 500 error due to a Turbopack/webpack module-name hashing bug in the Next.js 16 instrumentation hook.
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
fix(build): Force
better-sqlite3to always be required by its exact package name in the webpack server bundle. Next.js 16 compiled the instrumentation hook into a separate chunk and emittedrequire('better-sqlite3-<hash>')— a hashed module name that doesn't exist innode_modules— even though the package was listed inserverExternalPackages. Added an explicitexternalsfunction to the server webpack config so the bundler always emitsrequire('better-sqlite3'), resolving the startup500 Internal Server Erroron clean global installs. (#394, PR #395)
🔧 CI
- ci: Added
workflow_dispatchtonpm-publish.ymlwith version sync safeguard for manual triggers (#392) - ci: Added
workflow_dispatchtodocker-publish.yml, updated GitHub Actions to latest versions (#392)
[2.6.0] - 2026-03-15
Issue resolution sprint: 4 bugs fixed, logs UX improved, Kiro credit tracking added.
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
fix(media): ComfyUI and SD WebUI no longer appear in the Media page provider list when unconfigured — fetches
/api/providerson mount and hides local providers with no connections (#390) -
fix(auth): Round-robin no longer re-selects rate-limited accounts immediately after cooldown —
backoffLevelis now used as primary sort key in the LRU rotation (#340) -
fix(oauth): Qoder (and other providers that redirect to their own UI) no longer leave the OAuth modal stuck at "Waiting for Authorization" — popup-closed detector auto-transitions to manual URL input mode (#344)
-
fix(logs): Request log table is now readable in light mode — status badges, token counts, and combo tags use adaptive
dark:color classes (#378)
✨ Features
- feat(kiro): Kiro credit tracking added to usage fetcher — queries
getUserCreditsfrom AWS CodeWhisperer endpoint (#337)
🛠 Chores
- chore(tests): Aligned
test:plan3,test:fixes,test:securityto use sametsx/esmloader asnpm test— eliminates module resolution false negatives in targeted runs (PR #386)
[2.5.9] - 2026-03-15
Codex native passthrough fix + route body validation hardening.
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
fix(codex): Preserve native Responses API passthrough for Codex clients — avoids unnecessary translation mutations (PR #387)
-
fix(api): Validate request bodies on pricing/sync and task-routing routes — prevents crashes from malformed inputs (PR #388)
-
fix(auth): JWT secrets persist across restarts via
src/lib/db/secrets.ts— eliminates 401 errors after pm2 restart (PR #388)
[2.5.8] - 2026-03-15
Build fix: restore VPS connectivity broken by v2.5.7 incomplete publish.
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
fix(build):
scripts/prepublish.mjsstill used deprecated--webpackflag causing Next.js standalone build to fail silently — npm publish completed withoutapp/server.js, breaking VPS deployment
[2.5.7] - 2026-03-15
Media playground error handling fixes.
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
fix(media): Transcription "API Key Required" false positive when audio contains no speech (music, silence) — now shows "No speech detected" instead
-
fix(media):
upstreamErrorResponseinaudioTranscription.tsandaudioSpeech.tsnow returns proper JSON ({error:{message}}), enabling correct 401/403 credential error detection in the MediaPageClient -
fix(media):
parseApiErrornow handles Deepgram'serr_msgfield and detects"api key"in error messages for accurate credential error classification
[2.5.6] - 2026-03-15
Critical security/auth fixes: Antigravity OAuth broken + JWT sessions lost after restart.
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
fix(oauth) #384: Antigravity Google OAuth now correctly sends
client_secretto the token endpoint. The fallback forANTIGRAVITY_OAUTH_CLIENT_SECRETwas an empty string, which is falsy — soclient_secretwas never included in the request, causing"client_secret is missing"errors for all users without a custom env var. Closes #383. -
fix(auth) #385:
JWT_SECRETis now persisted to SQLite (namespace='secrets') on first generation and reloaded on subsequent starts. Previously, a new random secret was generated each process startup, invalidating all existing cookies/sessions after any restart or upgrade. Affects bothJWT_SECRETandAPI_KEY_SECRET. Closes #382.
[2.5.5] - 2026-03-15
Model list dedup fix, Electron standalone build hardening, and Kiro credit tracking.
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
fix(models) #380:
GET /api/modelsnow includes provider aliases when building the active-provider filter — models forclaude(aliascc) andgithub(aliasgh) were always shown regardless of whether a connection was configured, becausePROVIDER_MODELSkeys are aliases but DB connections are stored under provider IDs. Fixed by expanding each active provider ID to also include its alias viaPROVIDER_ID_TO_ALIAS. Closes #353. -
fix(electron) #379: New
scripts/prepare-electron-standalone.mjsstages a dedicated/.next/electron-standalonebundle before Electron packaging. Aborts with a clear error ifnode_modulesis a symlink (electron-builder would ship a runtime dependency on the build machine). Cross-platform path sanitization viapath.basename. By @kfiramar.
✨ New Features
-
feat(docs): integrate multi-page documentation into OmniRoute dashboard (#1969)
-
feat(settings): add request body limit setting (#1968)
-
feat(auth): add Gemini CLI OAuth client secret default (#1974)
-
feat(models): expose models.dev context windows in /v1/models (#1972)
-
fix(db): resolve legacy encryption fallback causing re-encryption loops (#1941)
-
fix(auth): fix Codex assistant final_answer response sanitization (#1965)
-
feat(providers): Implement Image Generation and Editing capabilities for ChatGPT Web, including in-band chat image generation and caching (#1606).
-
feat(ui): Integrate OpenCode Zen/Go API tool logo SVG and polish API key copy-to-clipboard interactions (#1607).
-
feat(providers): Integrate AgentRouter as a new OpenAI-compatible passthrough provider with $200 free credits via sign-up (Issue #1572).
-
feat(ui): Implement on-demand per-model testing in the provider dashboard, allowing single-token diagnostic checks without triggering rate-limits (Issue #1532).
-
feat(kiro) #381: Kiro credit balance tracking — usage endpoint now returns credit data for Kiro accounts by calling
codewhisperer.us-east-1.amazonaws.com/getUserCredits(same endpoint Kiro IDE uses internally). Returns remaining credits, total allowance, renewal date, and subscription tier. Closes #337.
[2.5.4] - 2026-03-15
Logger startup fix, login bootstrap security fix, and dev HMR reliability improvement. CI infrastructure hardened.
🐛 Bug Fixes (PRs #374, #375, #376 by @kfiramar)
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
fix(logger) #376: Restore pino transport logger path —
formatters.levelcombined withtransport.targetsis rejected by pino. Transport-backed configs now strip the level formatter viagetTransportCompatibleConfig(). Also corrects numeric level mapping in/api/logs/console:30→info, 40→warn, 50→error(was shifted by one). -
fix(login) #375: Login page now bootstraps from the public
/api/settings/require-loginendpoint instead of the protected/api/settings. In password-protected setups, the pre-auth page was receiving a 401 and falling back to safe defaults unnecessarily. The public route now returns all bootstrap metadata (requireLogin,hasPassword,setupComplete) with a conservative 200 fallback on error. -
fix(dev) #374: Add
localhostand127.0.0.1toallowedDevOriginsinnext.config.mjs— HMR websocket was blocked when accessing the app via loopback address, producing repeated cross-origin warnings.
🔧 CI & Infrastructure
- ESLint OOM fix:
eslint.config.mjsnow ignoresvscode-extension/**,electron/**,docs/**,app/.next/**, andclipr/**— ESLint was crashing with a JS heap OOM by scanning VS Code binary blobs and compiled chunks. - Unit test fix: Removed stale
ALTER TABLE provider_connections ADD COLUMN "group"from 2 test files — column is now part of the base schema (added in #373), causingSQLITE_ERROR: duplicate column nameon every CI run. - Pre-commit hook: Added
npm run test:unitto.husky/pre-commit— unit tests now block broken commits before they reach CI.
[2.5.3] - 2026-03-14
Critical bugfixes: DB schema migration, startup env loading, provider error state clearing, and i18n tooltip fix. Code quality improvements on top of each PR.
🐛 Bug Fixes (PRs #369, #371, #372, #373 by @kfiramar)
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
fix(db) #373: Add
provider_connections.groupcolumn to base schema + backfill migration for existing databases — column was used in all queries but missing from schema definition -
fix(i18n) #371: Replace non-existent
t("deleteConnection")key with existingproviders.deletekey — fixesMISSING_MESSAGE: providers.deleteConnectionruntime error on provider detail page -
fix(auth) #372: Clear stale error metadata (
errorCode,lastErrorType,lastErrorSource) from provider accounts after genuine recovery — previously, recovered accounts kept appearing as failed -
fix(startup) #369: Unify env loading across
npm run start,run-standalone.mjs, and Electron to respectDATA_DIR/.env → ~/.omniroute/.env → ./.envpriority — prevents generating a newSTORAGE_ENCRYPTION_KEYover an existing encrypted database
🔧 Code Quality
- Documented
result.successvsresponse?.okpatterns inauth.ts(both intentional, now explained) - Normalized
overridePath?.trim()inelectron/main.jsto matchbootstrap-env.mjs - Added
preferredEnvmerge order comment in Electron startup
Codex account quota policy with auto-rotation, fast tier toggle, gpt-5.4 model, and analytics label fix.
✨ New Features (PRs #366, #367, #368)
-
feat(docs): integrate multi-page documentation into OmniRoute dashboard (#1969)
-
feat(settings): add request body limit setting (#1968)
-
feat(auth): add Gemini CLI OAuth client secret default (#1974)
-
feat(models): expose models.dev context windows in /v1/models (#1972)
-
fix(db): resolve legacy encryption fallback causing re-encryption loops (#1941)
-
fix(auth): fix Codex assistant final_answer response sanitization (#1965)
-
feat(providers): Implement Image Generation and Editing capabilities for ChatGPT Web, including in-band chat image generation and caching (#1606).
-
feat(ui): Integrate OpenCode Zen/Go API tool logo SVG and polish API key copy-to-clipboard interactions (#1607).
-
feat(providers): Integrate AgentRouter as a new OpenAI-compatible passthrough provider with $200 free credits via sign-up (Issue #1572).
-
feat(ui): Implement on-demand per-model testing in the provider dashboard, allowing single-token diagnostic checks without triggering rate-limits (Issue #1532).
-
Codex Quota Policy (PR #366): Per-account 5h/weekly quota window toggles in Provider dashboard. Accounts are automatically skipped when enabled windows reach 90% threshold and re-admitted after
resetAt. IncludesquotaCache.tswith side-effect free status getter. -
Codex Fast Tier Toggle (PR #367): Dashboard → Settings → Codex Service Tier. Default-off toggle injects
service_tier: "flex"only for Codex requests, reducing cost ~80%. Full stack: UI tab + API endpoint + executor + translator + startup restore. -
gpt-5.4 Model (PR #368): Adds
cx/gpt-5.4andcodex/gpt-5.4to the Codex model registry. Regression test included.
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
fix #356: Analytics charts (Top Provider, By Account, Provider Breakdown) now display human-readable provider names/labels instead of raw internal IDs for OpenAI-compatible providers.
Major release: strict-random routing strategy, API key access controls, connection groups, external pricing sync, and critical bug fixes for thinking models, combo testing, and tool name validation.
✨ New Features (PRs #363 & #365)
-
feat(docs): integrate multi-page documentation into OmniRoute dashboard (#1969)
-
feat(settings): add request body limit setting (#1968)
-
feat(auth): add Gemini CLI OAuth client secret default (#1974)
-
feat(models): expose models.dev context windows in /v1/models (#1972)
-
fix(db): resolve legacy encryption fallback causing re-encryption loops (#1941)
-
fix(auth): fix Codex assistant final_answer response sanitization (#1965)
-
feat(providers): Implement Image Generation and Editing capabilities for ChatGPT Web, including in-band chat image generation and caching (#1606).
-
feat(ui): Integrate OpenCode Zen/Go API tool logo SVG and polish API key copy-to-clipboard interactions (#1607).
-
feat(providers): Integrate AgentRouter as a new OpenAI-compatible passthrough provider with $200 free credits via sign-up (Issue #1572).
-
feat(ui): Implement on-demand per-model testing in the provider dashboard, allowing single-token diagnostic checks without triggering rate-limits (Issue #1532).
-
Strict-Random Routing Strategy: Fisher-Yates shuffle deck with anti-repeat guarantee and mutex serialization for concurrent requests. Independent decks per combo and per provider.
-
API Key Access Controls:
allowedConnections(restrict which connections a key can use),is_active(enable/disable key with 403),accessSchedule(time-based access control),autoResolvetoggle, rename keys via PATCH. -
Connection Groups: Group provider connections by environment. Accordion view in Limits page with localStorage persistence and smart auto-switch.
-
External Pricing Sync (LiteLLM): 3-tier pricing resolution (user overrides → synced → defaults). Opt-in via
PRICING_SYNC_ENABLED=true. MCP toolomniroute_sync_pricing. 23 new tests. -
i18n: 30 languages updated with strict-random strategy, API key management strings. pt-BR fully translated.
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
fix #355: Stream idle timeout increased from 60s to 300s — prevents aborting extended-thinking models (claude-opus-4-6, o3, etc.) during long reasoning phases. Configurable via
STREAM_IDLE_TIMEOUT_MS. -
fix #350: Combo test now bypasses
REQUIRE_API_KEY=trueusing internal header, and uses OpenAI-compatible format universally. Timeout extended from 15s to 20s. -
fix #346: Tools with empty
function.name(forwarded by Claude Code) are now filtered before upstream providers receive them, preventing "Invalid input[N].name: empty string" errors.
🗑️ Closed Issues
- #341: Debug section removed — replacement is
/dashboard/logsand/dashboard/health.
API Key Round-Robin support for multi-key provider setups, and confirmation of wildcard routing and quota window rolling already in place.
✨ New Features
-
feat(docs): integrate multi-page documentation into OmniRoute dashboard (#1969)
-
feat(settings): add request body limit setting (#1968)
-
feat(auth): add Gemini CLI OAuth client secret default (#1974)
-
feat(models): expose models.dev context windows in /v1/models (#1972)
-
fix(db): resolve legacy encryption fallback causing re-encryption loops (#1941)
-
fix(auth): fix Codex assistant final_answer response sanitization (#1965)
-
feat(providers): Implement Image Generation and Editing capabilities for ChatGPT Web, including in-band chat image generation and caching (#1606).
-
feat(ui): Integrate OpenCode Zen/Go API tool logo SVG and polish API key copy-to-clipboard interactions (#1607).
-
feat(providers): Integrate AgentRouter as a new OpenAI-compatible passthrough provider with $200 free credits via sign-up (Issue #1572).
-
feat(ui): Implement on-demand per-model testing in the provider dashboard, allowing single-token diagnostic checks without triggering rate-limits (Issue #1532).
-
API Key Round-Robin (T07): Provider connections can now hold multiple API keys (Edit Connection → Extra API Keys). Requests rotate round-robin between primary + extra keys via
providerSpecificData.extraApiKeys[]. Keys are held in-memory indexed per connection — no DB schema changes required.
📝 Already Implemented (confirmed in audit)
- Wildcard Model Routing (T13):
wildcardRouter.tswith glob-style wildcard matching (gpt*,claude-?-sonnet, etc.) is already integrated intomodel.tswith specificity ranking. - Quota Window Rolling (T08):
accountFallback.ts:isModelLocked()already auto-advances the window — ifDate.now() > entry.until, lock is deleted immediately (no stale blocking).
UI polish, routing strategy additions, and graceful error handling for usage limits.
✨ New Features
-
feat(docs): integrate multi-page documentation into OmniRoute dashboard (#1969)
-
feat(settings): add request body limit setting (#1968)
-
feat(auth): add Gemini CLI OAuth client secret default (#1974)
-
feat(models): expose models.dev context windows in /v1/models (#1972)
-
fix(db): resolve legacy encryption fallback causing re-encryption loops (#1941)
-
fix(auth): fix Codex assistant final_answer response sanitization (#1965)
-
feat(providers): Implement Image Generation and Editing capabilities for ChatGPT Web, including in-band chat image generation and caching (#1606).
-
feat(ui): Integrate OpenCode Zen/Go API tool logo SVG and polish API key copy-to-clipboard interactions (#1607).
-
feat(providers): Integrate AgentRouter as a new OpenAI-compatible passthrough provider with $200 free credits via sign-up (Issue #1572).
-
feat(ui): Implement on-demand per-model testing in the provider dashboard, allowing single-token diagnostic checks without triggering rate-limits (Issue #1532).
-
Fill-First & P2C Routing Strategies: Added
fill-first(drain quota before moving on) andp2c(Power-of-Two-Choices low-latency selection) to combo strategy picker, with full guidance panels and color-coded badges. -
Free Stack Preset Models: Creating a combo with the Free Stack template now auto-fills 7 best-in-class free provider models (Gemini CLI, Kiro, Qoder×2, Qwen, NVIDIA NIM, Groq). Users just activate the providers and get a $0/month combo out-of-the-box.
-
Wider Combo Modal: Create/Edit combo modal now uses
max-w-4xlfor comfortable editing of large combos.
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
Limits page HTTP 500 for Codex & GitHub:
getCodexUsage()andgetGitHubUsage()now return a user-friendly message when the provider returns 401/403 (expired token), instead of throwing and causing a 500 error on the Limits page. -
MaintenanceBanner false-positive: Banner no longer shows "Server is unreachable" spuriously on page load. Fixed by calling
checkHealth()immediately on mount and removing staleshow-state closure. -
Provider icon tooltips: Edit (pencil) and delete icon buttons in the provider connection row now have native HTML tooltips — all 6 action icons are now self-documented.
Multiple improvements from community issue analysis, new provider support, bug fixes for token tracking, model routing, and streaming reliability.
✨ New Features
-
feat(docs): integrate multi-page documentation into OmniRoute dashboard (#1969)
-
feat(settings): add request body limit setting (#1968)
-
feat(auth): add Gemini CLI OAuth client secret default (#1974)
-
feat(models): expose models.dev context windows in /v1/models (#1972)
-
fix(db): resolve legacy encryption fallback causing re-encryption loops (#1941)
-
fix(auth): fix Codex assistant final_answer response sanitization (#1965)
-
feat(providers): Implement Image Generation and Editing capabilities for ChatGPT Web, including in-band chat image generation and caching (#1606).
-
feat(ui): Integrate OpenCode Zen/Go API tool logo SVG and polish API key copy-to-clipboard interactions (#1607).
-
feat(providers): Integrate AgentRouter as a new OpenAI-compatible passthrough provider with $200 free credits via sign-up (Issue #1572).
-
feat(ui): Implement on-demand per-model testing in the provider dashboard, allowing single-token diagnostic checks without triggering rate-limits (Issue #1532).
-
Task-Aware Smart Routing (T05): Automatic model selection based on request content type — coding → deepseek-chat, analysis → gemini-2.5-pro, vision → gpt-4o, summarization → gemini-2.5-flash. Configurable via Settings. New
GET/PUT/POST /api/settings/task-routingAPI. -
HuggingFace Provider: Added HuggingFace Router as an OpenAI-compatible provider with Llama 3.1 70B/8B, Qwen 2.5 72B, Mistral 7B, Phi-3.5 Mini.
-
Vertex AI Provider: Added Vertex AI (Google Cloud) provider with Gemini 2.5 Pro/Flash, Gemma 2 27B, Claude via Vertex.
-
Playground File Uploads: Audio upload for transcription, image upload for vision models (auto-detect by model name), inline image rendering for image generation results.
-
Model Select Visual Feedback: Already-added models in combo picker now show ✓ green badge — prevents duplicate confusion.
-
Qwen Compatibility (PR #352): Updated User-Agent and CLI fingerprint settings for Qwen provider compatibility.
-
Round-Robin State Management (PR #349): Enhanced round-robin logic to handle excluded accounts and maintain rotation state correctly.
-
Clipboard UX (PR #360): Hardened clipboard operations with fallback for non-secure contexts; Claude tool normalization improvements.
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
Fix #302 — OpenAI SDK stream=False drops tool_calls: T01 Accept header negotiation no longer forces streaming when
body.streamis explicitlyfalse. Was causing tool_calls to be silently dropped when using the OpenAI Python SDK in non-streaming mode. -
Fix #73 — Claude Haiku routed to OpenAI without provider prefix:
claude-*models sent without a provider prefix now correctly route to theantigravity(Anthropic) provider. Addedgemini-*/gemma-*→geminiheuristic as well. -
Fix #74 — Token counts always 0 for Antigravity/Claude streaming: The
message_startSSE event which carriesinput_tokenswas not being parsed byextractUsage(), causing all input token counts to drop. Input/output token tracking now works correctly for streaming responses. -
Fix #180 — Model import duplicates with no feedback:
ModelSelectModalnow shows ✓ green highlight for models already in the combo, making it obvious they're already added. -
Media page generation errors: Image results now render as
<img>tags instead of raw JSON. Transcription results shown as readable text. Credential errors show an amber banner instead of silent failure. -
Token refresh button on provider page: Manual token refresh UI added for OAuth providers.
🔧 Improvements
- Provider Registry: HuggingFace and Vertex AI added to
providerRegistry.tsandproviders.ts(frontend). - Read Cache: New
src/lib/db/readCache.tsfor efficient DB read caching. - Quota Cache: Improved quota cache with TTL-based eviction.
📦 Dependencies
dompurify→ 3.3.3 (PR #347)undici→ 7.24.2 (PR #348, #361)docker/setup-qemu-action→ v4 (PR #342)docker/setup-buildx-action→ v4 (PR #343)
📁 New Files
| File | Purpose |
|---|---|
open-sse/services/taskAwareRouter.ts |
Task-aware routing logic (7 task types) |
src/app/api/settings/task-routing/route.ts |
Task routing config API |
src/app/api/providers/[id]/refresh/route.ts |
Manual OAuth token refresh |
src/lib/db/readCache.ts |
Efficient DB read cache |
src/shared/utils/clipboard.ts |
Hardened clipboard with fallback |
[2.4.1] - 2026-03-13
🐛 Fix
- Combos modal: Free Stack visible and prominent — Free Stack template was hidden (4th in 3-column grid). Fixed: moved to position 1, switched to 2x2 grid so all 4 templates are visible, green border + FREE badge highlight.
[2.4.0] - 2026-03-13
Major release — Free Stack ecosystem, transcription playground overhaul, 44+ providers, comprehensive free tier documentation, and UI improvements across the board.
✨ Features
- Combos: Free Stack template — New 4th template "Free Stack ($0)" using round-robin across Kiro + Qoder + Qwen + Gemini CLI. Suggests the pre-built zero-cost combo on first use.
- Media/Transcription: Deepgram as default — Deepgram (Nova 3, $200 free) is now the default transcription provider. AssemblyAI ($50 free) and Groq Whisper (free forever) shown with free credit badges.
- README: "Start Free" section — New early-README 5-step table showing how to set up zero-cost AI in minutes.
- README: Free Transcription Combo — New section with Deepgram/AssemblyAI/Groq combo suggestion and per-provider free credit details.
- providers.ts: hasFree flag — NVIDIA NIM, Cerebras, and Groq marked with hasFree badge and freeNote for the providers UI.
- i18n: templateFreeStack keys — Free Stack combo template translated and synced to all 30 languages.
[2.3.16] - 2026-03-13
📖 Documentation
- README: 44+ Providers — Updated all 3 occurrences of "36+ providers" to "44+" reflecting the actual codebase count (44 providers in providers.ts)
- README: New Section "🆓 Free Models — What You Actually Get" — Added 7-provider table with per-model rate limits for: Kiro (Claude unlimited via AWS Builder ID), Qoder (5 models unlimited), Qwen (4 models unlimited), Gemini CLI (180K/mo), NVIDIA NIM (~40 RPM dev-forever), Cerebras (1M tok/day / 60K TPM), Groq (30 RPM / 14.4K RPD). Includes the /usr/bin/bash Ultimate Free Stack combo recommendation.
- README: Pricing Table Updated — Added Cerebras to API KEY tier, fixed NVIDIA from "1000 credits" to "dev-forever free", updated Qoder/Qwen model counts and names
- README: Qoder 8→5 models (named: kimi-k2-thinking, qwen3-coder-plus, deepseek-r1, minimax-m2, kimi-k2)
- README: Qwen 3→4 models (named: qwen3-coder-plus, qwen3-coder-flash, qwen3-coder-next, vision-model)
[2.3.15] - 2026-03-13
✨ Features
- Auto-Combo Dashboard (Tier Priority): Added
🏷️ Tieras the 7th scoring factor label in the/dashboard/auto-combofactor breakdown display — all 7 Auto-Combo scoring factors are now visible. - i18n — autoCombo section: Added 20 new translation keys for the Auto-Combo dashboard (
title,status,modePack,providerScores,factorTierPriority, etc.) to all 30 language files.
[2.3.14] - 2026-03-13
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
Qoder OAuth (#339): Restored the valid default
clientSecret— was previously an empty string, causing "Bad client credentials" on every connect attempt. The public credential is now the default fallback (overridable viaQODER_OAUTH_CLIENT_SECRETenv var). -
MITM server not found (#335):
prepublish.mjsnow compilessrc/mitm/*.tsto JavaScript usingtscbefore copying to the npm bundle. Previously only raw.tsfiles were copied — meaningserver.jsnever existed in npm/Volta global installs. -
GeminiCLI missing projectId (#338): Instead of throwing a hard 500 error when
projectIdis missing from stored credentials (e.g. after Docker restart), OmniRoute now logs a warning and attempts the request — returning a meaningful provider-side error instead of an OmniRoute crash. -
Electron version mismatch (#323): Synced
electron/package.jsonversion to2.3.13(was2.0.13) so the desktop binary version matches the npm package.
✨ New Models (#334)
- Kiro:
claude-sonnet-4,claude-opus-4.6,deepseek-v3.2,minimax-m2.1,qwen3-coder-next,auto - Codex:
gpt5.4
🔧 Improvements
- Tier Scoring (API + Validation): Added
tierPriority(weight0.05) to theScoringWeightsZod schema and thecombos/autoAPI route — the 7th scoring factor is now fully accepted by the REST API and validated on input.stabilityweight adjusted from0.10to0.05to keep total sum =1.0.
✨ New Features
-
feat(docs): integrate multi-page documentation into OmniRoute dashboard (#1969)
-
feat(settings): add request body limit setting (#1968)
-
feat(auth): add Gemini CLI OAuth client secret default (#1974)
-
feat(models): expose models.dev context windows in /v1/models (#1972)
-
fix(db): resolve legacy encryption fallback causing re-encryption loops (#1941)
-
fix(auth): fix Codex assistant final_answer response sanitization (#1965)
-
feat(providers): Implement Image Generation and Editing capabilities for ChatGPT Web, including in-band chat image generation and caching (#1606).
-
feat(ui): Integrate OpenCode Zen/Go API tool logo SVG and polish API key copy-to-clipboard interactions (#1607).
-
feat(providers): Integrate AgentRouter as a new OpenAI-compatible passthrough provider with $200 free credits via sign-up (Issue #1572).
-
feat(ui): Implement on-demand per-model testing in the provider dashboard, allowing single-token diagnostic checks without triggering rate-limits (Issue #1532).
-
Tiered Quota Scoring (Auto-Combo): Added
tierPriorityas a 7th scoring factor — accounts with Ultra/Pro tiers are now preferred over Free tiers when other factors are equal. New optional fieldsaccountTierandquotaResetIntervalSecsonProviderCandidate. All 4 mode packs updated (ship-fast,cost-saver,quality-first,offline-friendly). -
Intra-Family Model Fallback (T5): When a model is unavailable (404/400/403), OmniRoute now automatically falls back to sibling models from the same family before returning an error (
modelFamilyFallback.ts). -
Configurable API Bridge Timeout:
API_BRIDGE_PROXY_TIMEOUT_MSenv var lets operators tune the proxy timeout (default 30s). Fixes 504 errors on slow upstream responses. (#332) -
Star History: Replaced star-history.com widget with starchart.cc (
?variant=adaptive) in all 30 READMEs — adapts to light/dark theme, real-time updates.
🐛 Bug Fixes
-
fix(mitm): Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime.
-
fix(build): Move the local
.tmp/wine32Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot triggerEACCESscans during Node 24 builds. -
fix(build): Copy the
wreq-jsnative runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. -
fix(api): Validate the Codex Responses websocket bridge and
/v1/batchesJSON payloads with Zod before use, keepingrequest.json()route validation green and returning explicit 400 responses for invalid bodies. -
fix(providers): Add explicit typing to provider alias and category helpers so the strict
typecheck:noimplicit:coreCI gate passes. -
fix(ui): Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable.
-
fix(electron): Harden the production desktop CSP by removing
unsafe-evaloutside development and adding object, base URI, form action, frame ancestor, and worker restrictions. -
fix(cli): Replace shell-interpolated setup and privileged command execution paths with argument-based
spawn/execFilehelpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. -
fix(ui): Keep provider icons resilient by using direct
@lobehub/iconscomponents first, then local PNG/SVG fallbacks, avoiding the@lobehub/uipeer runtime in the dashboard. -
Auth — First-time password:
INITIAL_PASSWORDenv var is now accepted when setting the first dashboard password. UsestimingSafeEqualfor constant-time comparison, preventing timing attacks. (#333) -
README Truncation: Fixed a missing
</details>closing tag in the Troubleshooting section that caused GitHub to stop rendering everything below it (Tech Stack, Docs, Roadmap, Contributors). -
pnpm install: Removed redundant
@swc/helpersoverride frompackage.jsonthat conflicted with the direct dependency, causingEOVERRIDEerrors on pnpm. Addedpnpm.onlyBuiltDependenciesconfig. -
CLI Path Injection (T12): Added
isSafePath()validator incliRuntime.tsto block path traversal and shell metacharacters inCLI_*_BINenv vars. -
CI: Regenerated
package-lock.jsonafter override removal to fixnpm cifailures on GitHub Actions.
🔧 Improvements
- Response Format (T1):
response_format(json_schema/json_object) now injected as a system prompt for Claude, enabling structured output compatibility. - 429 Retry (T2): Intra-URL retry for 429 responses (2× attempts with 2s delay) before falling back to next URL.
- Gemini CLI Headers (T3): Added
User-AgentandX-Goog-Api-Clientfingerprint headers for Gemini CLI compatibility. - Pricing Catalog (T9): Added
deepseek-3.1,deepseek-3.2, andqwen3-coder-nextpricing entries.
📁 New Files
| File | Purpose |
|---|---|
open-sse/services/modelFamilyFallback.ts |
Model family definitions and intra-family fallback logic |
Fixed
- KiloCode: kilocode healthcheck timeout already fixed in v2.3.11
- OpenCode: Add opencode to cliRuntime registry with 15s healthcheck timeout
- OpenClaw / Cursor: Increase healthcheck timeout to 15s for slow-start variants
- VPS: Install droid and openclaw npm packages; activate CLI_EXTRA_PATHS for kiro-cli
- cliRuntime: Add opencode tool registration and increase timeout for continue
[2.3.11] - 2026-03-12
Fixed
- KiloCode healthcheck: Increase
healthcheckTimeoutMsfrom 4000ms to 15000ms — kilocode renders an ASCII logo banner on startup causing falsehealthcheck_failedon slow/cold-start environments
[2.3.10] - 2026-03-12
Fixed
- Lint: Fix
check:any-budget:t11failure — replaceas anywithas Record<string, unknown>in OAuthModal.tsx (3 occurrences)
Docs
- CLI-TOOLS.md: Complete guide for all 11 CLI tools (claude, codex, gemini, opencode, cline, kilocode, continue, kiro-cli, cursor, droid, openclaw)
- i18n: CLI-TOOLS.md synced to 30 languages with translated title + intro
[2.3.8] - 2026-03-12
[2.3.9] - 2026-03-12
Added
- /v1/completions: New legacy OpenAI completions endpoint — accepts both
promptstring andmessagesarray, normalizes to chat format automatically - EndpointPage: Now shows all 3 OpenAI-compatible endpoint types: Chat Completions, Responses API, and Legacy Completions
- i18n: Added
completionsLegacy/completionsLegacyDescto 30 language files
Fixed
- OAuthModal: Fix
[object Object]displayed on all OAuth connection errors — properly extract.messagefrom error response objects in all 3throw new Error(data.error)calls (exchange, device-code, authorize) - Affects Cline, Codex, GitHub, Qwen, Kiro, and all other OAuth providers
[2.3.7] - 2026-03-12
Fixed
- Cline OAuth: Add
decodeURIComponentbefore base64 decode so URL-encoded auth codes from the callback URL are parsed correctly, fixing "invalid or expired authorization code" errors on remote (LAN IP) setups - Cline OAuth:
mapTokensnow populatesname = firstName + lastName || emailso Cline accounts show real user names instead of "Account #ID" - OAuth account names: All OAuth exchange flows (exchange, poll, poll-callback) now normalize
name = emailwhen name is missing, so every OAuth account shows its email as the display label in the Providers dashboard - OAuth account names: Removed sequential "Account N" fallback in
db/providers.ts— accounts with no email/name now use a stable ID-based label viagetAccountDisplayName()instead of a sequential number that changes when accounts are deleted
[2.3.6] - 2026-03-12
Fixed
- Provider test batch: Fixed Zod schema to accept
providerId: null(frontend sends null for non-provider modes); was incorrectly returning "Invalid request" for all batch tests - Provider test modal: Fixed
[object Object]display by normalizing API error objects to strings before rendering insetTestResultsandProviderTestResultsView - i18n: Added missing keys
cliTools.toolDescriptions.opencode,cliTools.toolDescriptions.kiro,cliTools.guides.opencode,cliTools.guides.kirotoen.json - i18n: Synchronized 1111 missing keys across all 29 non-English language files using English values as fallbacks
[2.3.5] - 2026-03-11
Fixed
- @swc/helpers: Added permanent
postinstallfix to copy@swc/helpersinto the standalone app'snode_modules— prevents MODULE_NOT_FOUND crash on global npm installs
[2.3.4] - 2026-03-10
Added
- Multiple provider integrations and dashboard improvements