Compare commits

...

116 Commits

Author SHA1 Message Date
Xiangzhe
79478a6676 test(vitest): pay heavy module imports at collection time, not out of the per-test budget
The remaining ui-shard reds were one class, not six bugs: every one of them did
`await import(<heavy component>)` INSIDE an `it()`, so Vite's transform of the
dependency graph was billed to that test's timeout. Measured costs against the
budgets they had to fit in:

  ProxyRegistryManager   86s import vs 30s / 60s / 5s budgets (render itself: 567ms)
  claudeTlsClient       ~12s import vs 5s default
  useProviderConnections  1050-line hook, whole dashboard graph, vs 5s default

That is why they looked like cross-file pollution: on an idle box the import
squeaked under the limit, and under the ui suite's 20 parallel workers it did not.
Running claudeTlsClient ALONE on a loaded box reproduces it — the trigger is CPU
contention, not a neighbouring file. The sibling chatgptTlsClient/grokTlsClient
tests import the same graph and never fail, because they import statically at
module scope, where the cost falls on the collection phase which has no per-test
budget. Every fix here does the same: static import or a beforeAll with its own
budget.

AutoComboCatalog also explains its own blast radius: the timeout aborted inside an
open act(), leaking an unbalanced act scope that then failed the file's three
remaining tests in ~20ms with 'overlapping act() calls'. One slow import, four reds.

CoolingConnectionsPanel is the one production change. It imported providerText from
the ../providerPageHelpers barrel, but that symbol is DEFINED in the
../providerCredentialText leaf and only re-exported by the barrel — which drags
providerRegistry (352 providers) and the rest of the provider-page graph into a
"use client" component for one string helper. Verified before accepting: the
component used nothing else from the barrel, the barrel has no top-level
side-effect to lose (the empty-registry hazard this repo has hit before does not
apply), typecheck:core is clean, and the panel's first test drops from ~4s to 95ms.
The import was suboptimal, never broken — the screen was not failing for users.

No assertion was weakened anywhere. expect() counts are unchanged (25/25, 4/4) or
up by one (AutoComboCatalog 11 -> 12); the #8855 autofill sentinels, the
data-1p-ignore / data-lpignore guards and the dead-status round-trip are intact.

The #5918 TDZ guard was proven still live by mutation, not by absence of red:
moving useProxyBatchOperations(load) above its const reproduced
'ReferenceError: Cannot access load before initialization' in 207ms, then the
production file was restored (diff empty).

tests/unit/ui under load: 17 failed files / 45 failed tests -> 4 failed files /
4 failed tests, none of them these. The four left are compression-guidance-7530,
compressionPanel, compressionUltraTier and lobe-provider-icons-stepfun, untouched
and uninvestigated.

Refs #10692
2026-08-25 10:27:32 -03:00
Xiangzhe
486a2650b9 fix(i18n): unstale trafficInspectorSubtitle across 32 locales and regenerate the omni-inference skill
Two gates in the Lint job, both inherited — each was hidden behind the one before it.

i18n value drift: #11283 rewrote sidebar.trafficInspectorSubtitle in en.json without
touching the 42 translations, so 32 locales kept serving a sentence the English no
longer says. Most take the documented __MISSING__: placeholder, which makes the
runtime serve the corrected English until the translation pipeline catches up.
Three do not:

  - vi cannot take a placeholder at all — tests/unit/i18n-vi-completeness.test.ts
    bans any __MISSING__/__TODO__ value outright, so it needs a real translation.
  - pt and pt-BR are translated for real rather than placeheld, because a placeholder
    there means this project's own maintainer reads the sidebar in English.

Each file changed by exactly one line; the JSON was not reserialised wholesale.

agent-skills-sync: skills/omni-inference/SKILL.md was missing the ElevenLabs voices
and speech-to-text routes added by #11312, so the generator reported one file out of
date and the gate exited 2. Regenerated — purely additive, 48 lines, no deletions.

Verified: check-ui-value-drift PASS, i18n:check-ui-coverage PASS (42 locales),
i18n-vi-completeness 5/5, check:agent-skills-sync 46 unchanged.

Refs #10692
2026-08-25 09:57:26 -03:00
Xiangzhe
dd3a0f0189 test(vitest): realign five sibling-test contracts unmasked by the green mcp shard
None of these are cycle regressions. The Vitest job runs test:vitest (mcp shard)
then test:vitest:ui; the mcp shard was failing on a missing glm-5.3-max and aborted
the job before the ui shard ever ran. Fixing that shard this cycle unmasked 34 ui
failures that had been broken since 18-23 Aug — four separate PRs that moved a
contract and updated their own tests but not their siblings.

- ProviderCard gained useRouter() in #10448; four test files render it without
  mocking next/navigation and died on 'invariant expected app router to be mounted'.
  The sibling created alongside #10448 already had the mock — it just was not
  applied to the other four consumers.

- SkillCoverage gained a required config category. The four fixtures in
  agent-skills-page still described only api/cli, so the component read
  config.have off undefined. Values were chosen per scenario rather than pasted:
  full coverage gets 2/2 so its bar stays emerald, the amber fixture gets 3/4 so it
  stays amber. CoverageBar renders api -> config -> cli, so the new bar lands in the
  MIDDLE and the cli assertions moved from index [1] to [2]; without that the cli
  checks would have passed while measuring the config bar. The aria test now pins
  all three bars.

- CliAgentsPage hardcoded AGENT_IDS, which had already drifted once (6 -> 8 with
  omp/letta, per its own comment) and drifted again with prime-agent (#11166). It is
  now derived from CLI_TOOLS. This is why an agent missing from that list is not
  cosmetic: it never enters the status map, defaults to not_installed, and adds a
  phantom card to the filter and count tests. Deriving keeps the fixture in sync by
  construction instead of waiting for the next agent.

- claudeTlsClient asserted proxyUrl was undefined inside a test literally named
  'falls back to env var when per-call proxyUrl not provided' — it pinned the old
  behaviour where testOverride bypassed proxy resolution. #10910 moved resolution
  ahead of the override on purpose ('so test overrides and the real path both see
  it'), so the assertion now checks the fallback the test name promises.

test:vitest:ui goes from 34 failures to 14. The remaining 14 sit in six files none
of this commit touches (AutoComboCatalog, CoolingConnectionsPanel, ProxyRegistryManager
x2, connectionsSearchFilter) plus one claudeTlsClient case that passes in isolation
and only fails in the full run — i.e. cross-file pollution. They need a clean
environment to judge: this devbox resolves part of its tree through a stray pnpm
store and has already produced one phantom failure count this cycle.

Refs #10692
2026-08-25 09:48:12 -03:00
Xiangzhe
5eccbfac19 fix(ci): drain four inherited base-reds in packaging, electron and integration tests
All four predate this session — each reproduces identically on f95b03d70 (2026-08-24),
so none is a cycle regression. Draining them here because the release pre-flight is
where inherited reds get resolved.

1. Package Artifact: the job runs `build:cli`, which assembles dist/ but never writes
   dist/BUILD_SHA — only `build:release` does, via write-build-sha.mjs. The #10427
   provenance guard inside check:pack-artifact then rejects the artifact as
   untraceable, and rejects it even under OMNIROUTE_ALLOW_CANARY_BUILD. The job's
   build+validate pair was structurally incompatible and failed 100% of the time.
   Stamps the SHA between the two steps.

2. Electron Package Smoke: electron/package.json's build.files allowlist enumerates
   each lib/*.js by hand and never got lib/loginHeaderCapture.js, added alongside its
   require() in #9984. The file therefore stayed out of app.asar and the packaged app
   died at startup on 'Cannot find module ./lib/loginHeaderCapture'.

3. proxy-pipeline: the breaker assertion grepped chat.ts for executeChatWithBreaker(,
   but that call moved behind the chatDispatch.ts seam. Rather than drop the check,
   it now pins both hops — chat.ts dispatches through the seam and the seam calls the
   breaker — so the extraction cannot silently take the breaker off the path.

4. skills-pipeline: #9058 began encoding skill tool names as omr_skill_<base64url>
   because providers require ^[a-zA-Z0-9_-]+$, and these assertions still expected the
   raw name@version. They now derive the expected name from encodeSkillToolName(), the
   same helper production uses, so the test tracks the contract instead of duplicating
   it. Only the assertions about names on the wire were converted; the identifiers
   passed straight to skillExecutor.execute() stay raw, because those are not encoded.

Integration suite for these two files: 54/55. The one still red —
'web_search fallback preserves Responses API output' — is a separate pre-existing
defect, deliberately left failing rather than papered over: on the /v1/responses path
resolveSearchCredentials() returns null for the seeded serper-search connection, so
executeWebSearch.ts:185-200 falls through to the cheapest fallbackOnly provider
(duckduckgo-free) and the results come back empty. The sibling chat-path test seeds
identically and does resolve serper-search. Needs its own investigation.

Refs #10692
2026-08-25 09:24:40 -03:00
Xiangzhe
b6a4739b1f fix(ci): set NODE_OPTIONS on the unit-shard step and prune stale eslint suppressions
The previous heap bump only touched test:unit:ci:shard, i.e. the node the shard
script spawns. The process that actually runs out of memory is the `c8` wrapper
around it — it aggregates ~577 MB of raw V8 coverage JSON — so the ceiling stayed
at the V8 default (~4 GB) and the shards kept aborting at ~4083 MB, byte for byte
the same failure. Setting NODE_OPTIONS on the step covers c8 and every child,
which is the pattern the coverage-merge job already uses.

Also prunes three eslint suppression entries whose violations no longer exist:
videoBridgeContactSheet.ts and videoBridgeRuntime.ts (no-unused-vars, fixed
during this cycle) and cli-oneproxy-commands.test.ts (no-explicit-any 14 -> 13,
a consequence of restoring the real mock in that test). Stale entries make
`npm run lint` exit 2 with 'There are suppressions left that do not occur
anymore'. Pruned and verified on an uncontaminated checkout, not the devbox.

Refs #10692
2026-08-25 08:41:17 -03:00
Xiangzhe
3d59c661de fix(ci): raise the unit-shard heap ceiling to 8192 MB to stop the SIGABRT OOM
The 8 unit shards run under V8 coverage instrumentation, which retains far more
memory than the bare suite. With the 4096 MB ceiling they began aborting with
exit 134 ("Ineffective mark-compacts near heap limit") at ~4086 MB as the
provider catalogue grew during the v3.8.50 cycle: every test in the shard passed
and the process died at the end, which reads as a test failure without being one.

Aligns test:unit:ci:shard and test:unit:serial with the 8192 MB the non-sharded
variants already use. GitHub-hosted runners have 16 GB, so the headroom is real.
Validated by the CI run on this commit — the shards are the gate.

Refs #10692
2026-08-25 07:37:06 -03:00
Xiangzhe
5e370c2ca7 docs(changelog): date the 3.8.50 header, inject contributors and sync the 42 i18n mirrors 2026-08-25 05:50:19 -03:00
Xiangzhe
1e705fd54f docs(changelog): reconcile the v3.8.50 section with the cycle's uncovered commits
Adds 131 consolidated bullets (45 features, 71 fixes, 15 maintenance)
covering the ~490 user-facing commits and the ~100 chore/ci/test/refactor/docs
commits that landed in the cycle without a CHANGELOG entry, grouped by
subsystem and citing their PR references.

Uncovered report: 594 -> 175 (the remainder are commits carrying no #N in
their subject, which the matcher can never resolve; they are covered in
prose).
2026-08-25 05:46:48 -03:00
Xiangzhe
588c683e9b docs(changelog): aggregate the 363 changelog.d fragments into [3.8.50]
Release reconciliation (Phase 0a.1). `scripts/release/aggregate-changelog.mjs`
folds each changelog.d/<section>/*.md fragment into its heading in the living
[3.8.50] section and deletes the fragment, which is the whole point of the
fragment convention: two PRs never touch the same file, so the CHANGELOG never
conflicts mid-cycle and no bullet is eaten by a merge auto-resolve.

Section bullets 731 -> 1041. The remaining uncovered commits (mostly merges from
#11397 onward, which landed without a fragment) are reconciled separately.
2026-08-25 05:30:52 -03:00
Xiangzhe
543bf9949a chore(docs): commit the next-dev agent-rules block into AGENTS.md
`next dev` writes and re-adds this block (see
node_modules/next/dist/server/lib/generate-agent-files.js), so leaving it out of
a diff only recreates the uncommitted change on the next dev run. Committing it
keeps the working tree clean, which is what the block's own note prescribes.
2026-08-25 05:29:32 -03:00
diegosouzapw
1249f714fe fix(cli): do not treat a tmpfs mount as proof a config path reaches the host
hasBindMountAt() accepted ANY mount as evidence that a would-be CLI config
write reaches the operator's host: it matched on the mount point alone and
never looked at the filesystem type. An in-memory mount therefore cleared the
ephemeral flag, so guardCliConfigWrite() let the write through and both
POST /api/cli-tools/apply and the dashboard's guide-settings writer answered
200 instead of the safe 422 that #10057 added.

That is the exact case the guard exists to refuse, and the worst one: a
container running with `--tmpfs /tmp` (or a home on tmpfs) loses the file even
before the container is recreated, while the UI reports success.

Parse the filesystem type from mountinfo (the field after the lone "-"
separator) and skip mounts backed by RAM or kernel state. Real bind mounts
(ext4/xfs/nfs/virtiofs/fuse.*) still count, including one nested under a tmpfs
path, so the compose `host` profile is unaffected. A line carrying no
separator proves nothing and is skipped too.

Regression cover added to tests/unit/container-env-detect.test.ts; this also
un-reds tests/unit/cli-tools-apply-container-422.test.ts and
tests/unit/api/cli-tools/apply-container-guard.test.ts, which were failing on
any box whose /tmp is a tmpfs.
2026-08-25 08:12:51 +00:00
Xiangzhe
e4f730a2b2 fix(release): drain two v3.8.50 base-reds (volcengine vision metadata, antigravity BYOP contract)
Two unrelated real reds on the release tip:

* fix(providers): flag MiniMax M3 as multimodal on the Volcengine Ark plans.
  d732cf615 ("feat(volcengine): add Ark plan providers") added
  volcengine-agent-plan/minimax-m3 and volcengine-coding-plan/minimax-m3
  without supportsVision, breaking the LEDGER-4 invariant that every
  minimax-m3 registry entry except PromptQL (text-only upstream) is flagged
  multimodal. Every other provider carrying the model (opencode-zen,
  opencode-go, bazaarlink, ollama-cloud, codebuddy-cn, trae) sets it.
  Registry metadata defect, not a stale test.

* test(antigravity): align the empty-projectId onboarding test to the
  contract shipped by #11284/#11358 (6de542b9b). That change made an
  onboardUser 200 whose body carries NO cloudaicompanionProject mean Google
  BYOP — no project was created and none ever will be — so it short-circuits
  before the retry loadCodeAssist. The older test still mocked onboardUser
  with the bare { done: true } BYOP shape while asserting the retry path, so
  it pinned a contract that was deliberately moved. The mock now returns a
  real onboarding-success body; every assertion is kept, and the id in the
  onboard body deliberately differs from the expected one so the test still
  proves the projectId came from the retry discovery.

Refs #11284
2026-08-25 08:11:23 +00:00
diegosouzapw
ae126dadcb fix(quality): drain the three v3.8.50 inventory/coverage base-reds
All three guards were drifting behind legitimate cycle growth, not catching a
defect. Nothing was weakened: no assertion removed, no floor lowered, no
blanket-allow added.

providers-constants-split: APIKEY_PROVIDERS 231 -> 233. The delta is exactly the
two Volcano Ark plan providers (volcengine-agent-plan, volcengine-coding-plan)
added to the regional family in d732cf615. The invariant the guard exists for
still holds, measured on the tip: 233 merged keys, 233 unique, family sum 233
(gateways 92 + frontier-labs 25 + inference-hosts 29 + enterprise-cloud 17 +
regional 43 + specialty-media 27) with an empty cross-family duplicate set and
an empty symmetric difference between the merged object and the family union -
so the six files are still a strict partition, no loss and no dup.

openapi-coverage: the operation floor (34.6%) is untouched. The cycle grew the
denominator 985 -> 1002 while covered only moved 343 -> 345 (34.4%). Fixed by
DOCUMENTING five real public operations rather than moving the floor, taking it
to 350/1002 = 34.9%: GET /api/health, GET /api/v1/voices, POST
/api/v1/speech-to-text, POST /api/v1/text-to-speech/{voiceId} and GET
/api/v1/explain/routing. Each entry was written from the route source (auth
mode, path-param pattern, limit clamp, upstream relay behaviour and the 400 /
401 / 429 branches), not from memory.

hard-session-lease-bypass-inventory: three new connection-query sites
classified, none silenced. open-sse/services/combo.ts
(readConnectionForCooldownGate) reads the row backing the pre-dispatch
persisted-cooldown gate, so it sits on the routing path and joins the class-B
list next to combo/providerWildcard.ts and autoComboCandidates.ts.
src/lib/providers/volcenginePlanBinding.ts and
src/lib/providers/volcPlanAutoSyncBackfill.ts are connection persistence, not
dispatch - the first resolves update-vs-create during connect, the second is a
one-shot boot backfill of a providerSpecificData flag with no upstream call -
so both stay class C alongside oauth/connectionPersistence.ts.
2026-08-25 08:11:23 +00:00
diegosouzapw
b4a636cf1e test(providers): use a non-reserved prefix in the CC-compatible node create cases
93da24cd7 ("fix(providers): reject reserved provider prefixes on
compatible-node create/update") made createProviderNodeSchema reject any
prefix that is a built-in REGISTRY id or alias. "cc" is the alias of the
built-in `claude` provider, so the two provider-nodes create cases in
cc-compatible-provider.test.ts started getting a 400 schema rejection
before the route ever reached its feature-flag gate (403) or the create
path (201) — the guard PR updated its own tests but missed this sibling
file, leaving a base-red on release/v3.8.50.

The operator-chosen prefix is incidental to what these cases assert (the
ENABLE_CC_COMPATIBLE_PROVIDER gate, the dedicated
anthropic-compatible-cc- id prefix, baseUrl sanitization and the nulled
modelsPath), so switch it to a non-reserved "cc-proxy". No assertion was
removed or loosened.
2026-08-25 08:07:11 +00:00
diegosouzapw
80f107d5a0 test(quota): freeze the clock in the GLM absolute-ISO-reset regression test
The #11353 regression test pins an ABSOLUTE upstream reset instant
(2026-08-29 21:01:21) in its production fixture body but measured the
resulting cooldown against the real wall clock. The remaining window
therefore shrank every day: from 2026-08-25 it dropped under the
5-day floor the two assertions use, and past 2026-08-29 it would
parse to null and collapse onto the 24h WEEKLY_QUOTA_COOLDOWN_MS
default - a guaranteed future red.

The shipped parser (parseIsoDateTimeResetMs / parseDayGranularityResetMs
/ buildWeeklyQuotaFallback / checkFallbackError) is correct: it returned
the real multi-day reset, just measured from today instead of the
fixtures NOW. Freeze Date at NOW via node:test mock timers in the two
time-dependent cases so they assert the parser rather than the calendar.
No assertion weakened, no production code touched.
2026-08-25 08:07:11 +00:00
diegosouzapw
7de2643243 test(router-eval): give spawned CLI children an explicit DATA_DIR
The router-eval CLI test spawns the CLI with spawnSync and asserts stderr stays empty. NODE_TEST_CONTEXT is inherited by those children, so since #10432 (guard #10428) resolveWritableDataDir() detects a test context with no DATA_DIR and warns on stderr before falling back to a throwaway dir - 194 chars that broke three cases. Pass an isolated DATA_DIR in the child env (the resolution the guard message itself prescribes) instead of loosening the assertions.
2026-08-25 08:07:10 +00:00
diegosouzapw
93c798c694 test(a2a): call the agent-card route handlers with a NextRequest
PR #11418 (S2 topology sanitisation) removed the hardcoded
localhost:20128 from both well-known agent-card routes and made them
derive the base URL from `request.nextUrl.origin` via
`getBaseUrl(request)` (src/lib/wellKnown.ts). That changed the handler
contract: `GET` now requires the request Next.js always passes it.

Three sibling test files were never aligned and still invoked the
handler as a bare `GET()`, so every case blew up with
`TypeError: Cannot read properties of undefined (reading nextUrl)`
before reaching a single assertion — 8 base-reds from one moved
contract, not from a skill-count drift.

Align the callers to the shipped contract with a local
`makeCardRequest()` helper mirroring tests/unit/security-s1-s2-s4.test.ts
(a Request with a defined `nextUrl`). No assertion was removed,
loosened or skipped; the assert counts are unchanged and the cases now
actually execute.

Refs #11418
2026-08-25 08:07:10 +00:00
diegosouzapw
fc0d61950b fix(release): drain the v3.8.50 docs/golden/GLM base-reds and restore a masked assert
Second base-red batch from the release pre-flight, measured on the .113 with a
clean npm ci (the devbox tree resolves eslint-plugin-react-hooks 7.1.1 from a
stray pnpm store instead of the lockfile 7.0.1 and reports 925 phantom errors).

Provider count 350 -> 352, one root cause behind three reds. Two providers
landed this cycle (volcengine-agent-plan, volcengine-coding-plan) without
regenerating the artifacts that quote the count:

  - docs/reference/PROVIDER_REFERENCE.md regenerated (gen:provider-reference).
  - README / AGENTS / llm.txt (+42 mirrors) / package.json description / 4 SVG
    diagrams updated, including the section heading AND the anchor that links to
    it, so the link does not break.
  - tests/snapshots/provider/translate-path.json regenerated. The diff is purely
    additive: 46 insertions, 0 deletions, exactly the two new providers.

GLM effort tiers. #11415 added the explicit glm-5.3-max tier and left two
sibling vitest specs pinning the old 16-model inventory and an empty tier list
for it. Aligned to the shipped contract (inventory order matches glmProvider.ts;
glm-5.3-max declares ["max"]).

Test-masking. Four assert reductions surfaced once the deleted-file signal was
resolved. Three are legitimate and are allowlisted with their reasoning:
#11355 inverted the startup-cooldown contract (preserve future quota cooldowns),
#11280 replaced two unrolled hops with a 3-hop loop that asserts MORE, and the
Gemini 3.5 Flash retirement removed the models those capability asserts described.

The fourth was real masking: #10960 rewrote the oneproxy status test to install a
stream mock, immediately overwrite it with a passthrough to the real fetch, and
assert `calls.length >= 0` — always true. Restored to assert what the test name
claims (the JSON-RPC tools/call carries omniroute_oneproxy_stats and its result
reaches the caller), with a scope note that it pins the MCP client contract
rather than the commander wiring.

Also allowlists the Gemini 3.5 Flash test deletion as _deletedWithReplacement
(the model was retired by 2764812ee4; gemini-models-parser.test.ts pins the new
"excluded from the parsed list" contract), and rebaselines bundleSize
8045 -> 8461 with per-entry measurements — every entrypoint stays far below its
absolute budget.
2026-08-25 07:47:27 +00:00
diegosouzapw
e3e188e993 fix(release): restore #10534 quota recovery and validate the volcengine connect bodies
Two base-reds on the v3.8.50 tip, found by the release pre-flight.

1. #11355 regressed #10534. It replaced the per-window recovery check with an
   unconditional `hasActiveCooldown()` stop, which is right for an
   upstream-derived cooldown but also blocks the case #10534 exists for: a
   Claude-subscription 429 persists a SYNTHETIC 1h rateLimitedUntil because the
   upstream sends no parseable reset. When the later poll shows every governing
   window has really reset with quota left, holding that synthetic cooldown just
   deadlocks the connection for an hour.

   The orphaned `windowStillExhaustedAfterRealReset()` helper and the three
   unused claudeExtraUsage imports that ESLint flagged were the fingerprint of
   this regression, not dead code: they are the two halves of the original gate.
   Re-wired as `isQuotaExhaustedCooldownReleasable()`, deliberately narrow —
   only lastErrorType "quota_exhausted" is eligible, one still-exhausted or
   unknown-reset window keeps the lock, and an extra-usage POLICY block stays
   locked even though its quota windows do look recovered in the same fetch.
   #11277/#11355 semantics are untouched (both guards still pass).

   Regression guard: tests/unit/provider-limits-recovery.test.ts already pinned
   this contract and was red on the tip. 15/15 now.

2. The three volcengine-plan connect routes read `request.json()` and handed the
   raw fields to a headless-browser login service after ad-hoc typeof checks
   (`check:route-validation:t06`, Hard Rule #7). `String(body.code ?? "")` turned
   123 into "123" and an absent code into "", both reaching the service as a
   plausible SMS code. Now parsed with Zod schemas, before the session lookup, so
   a malformed body answers 400 instead of a misleading 404.

   New: tests/unit/volcengine-plan-connect-validation.test.ts (8 cases, red
   before the fix). Gate: 687 route files scanned, PASS.

Also drops a genuinely dead import (formatVideoTimestamp in videoBridge.ts —
only used inside the helpers module that defines it).
2026-08-25 06:49:18 +00:00
Xiangzhe
f95b03d709 chore(release): back-merge main into release/v3.8.50 (ours) to unblock the release PR
The living release PR #8875 was CONFLICTING, which makes GitHub skip EVERY
pull_request workflow silently (no ci.yml, no semgrep, no DAST). Back-merging
main restores a computable merge ref.

Strategy `-s ours`: main is a stale snapshot of the release line (PR #11088 was
merged into main from a release-tip base, dragging ~5094 files). All 7 main-only
commits were verified as already represented on this branch:

- #11088 ollama capability routing  -> ported here as #11271 (6d4c4843e9)
- #11075 shared passthrough providers -> ported here as #11165 (92ef3c71ea)
- #10055 getModelsDevPricing memoization -> present (modelsDevSync.ts)
- #10026 hide health-check excluded models -> present and extended (catalog.ts)
- /_tasks anchored gitignore hardening -> present (.gitignore:288)
- nanoid/dompurify Dependabot bumps -> identical versions

main-only files intentionally NOT carried over:

- changelog.d/fixes/10286-gemini-3-5-flash-thinking.md + its regression test:
  the fix landed here as #10450 and was then deliberately superseded by
  2764812ee4 "eliminate Gemini 3.5 Flash". The test fails on this branch by
  design.
- public/providers/hackclub.svg: provider removed here (migration 162).
- docs/superpowers/**/2026-08-23-qdrant-*: planning artifacts belong in _tasks/
  (AGENTS.md), never under docs/.
2026-08-24 19:55:00 -03:00
Diego Rodrigues de Sa e Souza
1d0c5a36db fix(security): match cookie domains by suffix, not substring (#11429)
CodeQL js/incomplete-url-substring-sanitization, alerts #860 and #861:
volcengineConsoleAutoLogin accepted any cookie whose `domain` merely *contained*
"volcengine.com".

That check is an authorization decision, not a string test. The console
auto-login harvests `digest`, `AccountID`, `csrfToken` and `userInfo` out of the
Playwright context and persists them as the operator's Volcengine credentials,
so a cookie set by `volcengine.com.attacker.tld` — or `notvolcengine.com` — was
captured and stored as a provider connection.

Add `matchesCookieDomain()` (open-sse/utils/cookieDomain.ts): exact host or
dot-boundary suffix, leading dots and case normalized on both sides, failing
closed on an empty expected domain. Same shape as the existing
`isAdobeCookieDomain` in adobeFireflyBrowserLogin.ts, which already got this
right.

While sweeping the class, inAppLoginService's cookie capture had the identical
weakness — `c.domain.includes(domain.replace(/^\./, ""))` — with the identical
consequence: a look-alike host's cookie stored as the operator's credential.
CodeQL did not flag it because the expected domain comes from
TOKEN_EXTRACTION_CONFIGS rather than a literal. Both callsites now share the
helper.

tests/unit/volcengine-cookie-domain-suffix.test.ts — 5 tests, red before the
fix, covering the real domains, seven look-alikes, empty/missing input, and the
config-supplied path.

Co-authored-by: Xiangzhe <bakryun0718@proton.me>
2026-08-24 19:44:12 -03:00
Diego Rodrigues de Sa e Souza
b24cc53d54 fix(quality): drain the two Fast Quality Gates base-reds (#11438)
`Fast Quality Gates` has been failing on every open PR against
release/v3.8.50 with "2 gate(s) failed: mutation-test-coverage lockfile".
Neither belongs to any feature branch, so they are drained here.

check:lockfile — a transitive dev/optional entry
(libxmljs2 → brace-expansion@2.1.4) landed with a `resolved` URL pointing at
registry.npmmirror.com instead of registry.npmjs.org, which lockfile-lint
rejects as a supply-chain policy violation. Verified before touching it: the
recorded `integrity`
(sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==)
is byte-identical to the official npmjs tarball's, so the package content is the
same and this is a provenance slip — someone's install ran behind the mirror
registry — not a tampered package. Repointed the URL; `integrity` untouched.
It was the only non-npmjs host in the lockfile (2690 npmjs entries).

check:mutation-test-coverage — two covering unit tests were missing from
stryker.conf.json's tap.testFiles, so their mutant kills did not count:
repro-glm-iso-reset-24h-cap (accountFallback.ts) and
repro-combo-persisted-cooldown-preskip (comboPredicates.ts). Inserted in place.

Both gates verified green locally. The diff is three lines: re-serializing
either file would have reordered a curated list for no reason.

Co-authored-by: Xiangzhe <bakryun0718@proton.me>
2026-08-24 19:43:47 -03:00
Bob.Hou
59dccdd9e1 fix(security): sanitize agent-card topology, anti-spoof login rate-limit peer IP, and add 429 Retry-After (#S1 #S2 #S4) (#11418)
Validado em lote combinado (batch-0824g) contra o tip de release/v3.8.50: typecheck:core limpo, gates estáticos OK, 62/62 testes focados passando (S1/S2/S4, tests/unit/security-s1-s2-s4.test.ts, 9/9).

Boa integração com o padrão já existente de peer IP stamped por HMAC (resolveStampedPeer/OMNIROUTE_PEER_STAMP_TOKEN) — reusa em vez de reimplementar, e o header confiável só é honrado quando o stamp token está configurado. S2 remove corretamente a disclosure de topologia hardcoded do agent-card. Obrigado pela contribuição!
2026-08-24 17:24:08 -03:00
PhuongDoan
9464792cfc feat(sse): add glm-5.3-max explicit effort tier (#11415)
Validado em lote combinado (batch-0824g) contra o tip de release/v3.8.50: typecheck:core limpo, gates estáticos OK, 62/62 testes focados passando (23/23 do PR entre glm-5.3-catalog-and-effort-tiers.test.ts e zai-catalog-glm52.test.ts).

Aditivo, espelha exatamente o padrão já existente glm-5.2-max. Obrigado pela contribuição, primeira PR bem-vinda!
2026-08-24 17:23:58 -03:00
Marcelo Karval
11cbd7d4e0 fix(models): normalize media endpoint metadata (#11397)
Validado em lote combinado (batch-0824g) contra o tip de release/v3.8.50: typecheck:core limpo, gates estáticos OK, 62/62 testes focados passando (endpoint/parser/schema/static-model + catálogo).

Canonicaliza metadados de endpoint legados (video/audio) para IDs específicos por operação, mantendo compatibilidade retroativa via `normalizeModelSupportedEndpoints` (valores antigos `audio`/`video` continuam válidos como entrada e são normalizados na escrita). Obrigado pela contribuição, primeira PR bem-vinda!
2026-08-24 17:23:48 -03:00
Nguyen Thanh Dat
f93fecd86b fix(dashboard): honour the live WebSocket port the handshake reports (#11331) (#11388)
Validado em lote combinado (batch-0824g) contra o tip de release/v3.8.50: typecheck:core limpo, gates estáticos OK, 62/62 testes focados passando (incluindo tests/unit/live-ws-url-11331.test.ts, 11 casos + mutation-check).

Resolve o incidente real do #11331: o handshake já reportava a porta live real, mas o cliente descartava esse campo e ficava preso na porta compilada no bundle. Precedência clara (wsUrl explícito > publicUrl completo > porta/path do handshake aplicados ao default). Obrigado pela contribuição!
2026-08-24 17:23:38 -03:00
Nguyen Thanh Dat
d5d730c845 test(kimi): stop drawing the refresh window inside the assertion (#11380)
Validado em lote combinado (batch-0824g, junto de #11388/#11397/#11415/#11418) contra o tip de release/v3.8.50: typecheck:core limpo, file-size/changelog/complexity/cognitive-complexity OK, 62/62 testes focados passando.

Diagnóstico correto e bem documentado: a falha do nightly Node 26 era um teste que sorteia um número e depende do resultado, não uma quebra de compatibilidade. Comportamento de produção inalterado (a janela de jitter continua aleatória; só o teste ganhou controle sobre ela). Obrigado pela investigação detalhada!
2026-08-24 17:23:27 -03:00
Diego Rodrigues de Sa e Souza
8bbe92c692 fix(docker): size the Next build worker pool for a 16 GB runner (#11419)
Every "Publish to Docker Hub" run has failed since 2026-08-22 23:14 UTC — 96 of
the last 100. The builder stage dies with:

  ERROR: failed to solve: ResourceExhausted: process "/bin/sh -c ... npm run
  build ..." did not complete successfully: cannot allocate memory

That is the kernel, not V8. The log puts it precisely: the compile phase always
finishes ("✓ Compiled successfully in 4.2min") and the build is killed right
after "Collecting page data using 7 workers".

Each page-data worker is its own process and inherits NODE_OPTIONS, so the
--max-old-space-size ceiling is per PROCESS, not per build. CIRCLE_NODE_TOTAL=8
means 7 workers, and 7 of them alongside the parent no longer fit the 16 GB /
4 vCPU GitHub-hosted runners the pipeline builds on. It was intermittent for a
while before going 100%, which is what a threshold crossed by ordinary codebase
growth looks like — 7 was also oversubscribing a 4 vCPU runner.

Lower the pool to 3 (2 workers) and make it a build arg, so a big builder can
raise it back with `--build-arg OMNIROUTE_BUILD_WORKERS=8`.

tests/unit/docker-build-memory-budget.test.ts pins the budget: it reads the two
ARG defaults out of the Dockerfile and fails if `parent heap + workers × peak`
outgrows the runner, or if the pool oversubscribes its CPUs. Red on the base
(3/3), green here (3/3). The per-worker peak it budgets with is documented as an
inference from this failure, not a measurement.

DOCKER_GUIDE's build-arg table was stale (it still listed the pre-#10060 4096 MB
default); updated and given the new knob plus the symptom to recognize.
CIRCLE_NODE_TOTAL and OMNIROUTE_BUILD_WORKERS are allowlisted in the
fabricated-docs gate with the reason: neither is read via process.env here — one
is a Dockerfile ARG, the other is read by Next itself.

Note: the real proof is the next publish run. This failure mode only reproduces
on a memory-constrained host, so it cannot be reproduced by the unit suite; the
test guards the arithmetic, not the outcome.

Co-authored-by: Xiangzhe <bakryun0718@proton.me>
2026-08-24 15:47:47 -03:00
Diego Rodrigues de Sa e Souza
bbc7bf4351 fix(authz): match exact public routes exactly, not as prefixes (#11417)
`isPublicApiRoute()` matched every entry of PUBLIC_API_ROUTE_PREFIXES with
`startsWith()`, but 11 of the 15 entries name ONE route, not a subtree. As a
prefix each also marked every adjacent path sharing its leading characters as
PUBLIC, which skips the MANAGEMENT auth gate.

That is reachable today: Next resolves `/api/usage/om-usage<anything>` to the
dynamic route `/api/usage/[connectionId]`, and that handler carries no auth of
its own — it relies entirely on being classified MANAGEMENT. An unauthenticated
caller therefore reaches `fetchAndPersistProviderLimits()`, which is an
existence oracle over connection ids (409/404/400/200) and, for a connection id
actually starting with `om-usage`, discloses live quota JSON and can drive an
OAuth token refresh (a write side effect) with no credentials.

Split the allowlist by shape:

- PUBLIC_API_ROUTE_PREFIXES keeps only genuine subtrees, every entry ending in
  "/" (asserted by a unit test, so the class cannot come back silently).
- PUBLIC_API_ROUTES_EXACT holds the single routes, matched exactly in both
  spellings.
- The three read-only "prefixes" were single routes too and move to
  PUBLIC_READONLY_CORS_API_ROUTES, matched exactly. classify.ts now asks
  `isPublicReadonlyCorsRoute()` instead of scanning the raw list, so the CORS
  origin relaxation pipeline.ts keys on cannot be inherited by a sibling either
  (`/api/monitoring/health-detail` was taking it).
- `/api/health` deliberately stays in its own set so it keeps classifying as
  `public_prefix`; folding it into the read-only set would widen CORS on it.

dashboardCsrf.ts had a second copy of the prefix scan; it now shares
`isPublicApiRoute()` so the client CSRF exemption and the server classification
cannot disagree. Side effect in the safe direction: the three LOCAL_ONLY oauth
auto-import routes were CSRF-exempt on the client while the server already
required the token — the client now attaches it.

Reported by @ntdat812 (GHSA-74g9-q8f6-793h), with the shape of the fix and the
two gotchas above called out in the report.

Closes GHSA-74g9-q8f6-793h

Co-authored-by: Xiangzhe <bakryun0718@proton.me>
Co-authored-by: Nguyen Thanh Dat <ntdat812.dev@gmail.com>
2026-08-24 15:47:34 -03:00
Jacob Stoner
56d64e29a4 fix(dashboard): expose custom mode-pack option (#11407)
Validado em lote combinado (batch-0824f, junto de #11399/#11400/#11402) contra o tip de release/v3.8.50: typecheck:core limpo, file-size/changelog/complexity/cognitive-complexity OK, 56/56 testes focados passando incluindo os deste PR (tests/unit/autocombo-unification.test.ts).

Baixo risco: expõe a opção "custom" já suportada em runtime (`getModePack("custom") === undefined`, cai de volta para os pesos explícitos dos sliders) no seletor compartilhado de mode-pack da UI. Obrigado pela contribuição!
2026-08-24 14:16:37 -03:00
Jacob Stoner
7b36e45df8 fix(dashboard): normalize explicit auto weights (#11402)
Validado em lote combinado (batch-0824f, junto de #11399/#11400/#11407) contra o tip de release/v3.8.50: typecheck:core limpo, file-size/changelog/complexity/cognitive-complexity OK, 56/56 testes focados passando incluindo os deste PR (tests/unit/combo-scoring-inspector.test.ts).

Baixo risco: normaliza pesos parciais/não-unitários no inspector de diagnóstico (`comboScoringInspector.ts`) reutilizando o normalizador já existente do motor real de scoring, mantendo diagnósticos consistentes com o runtime. Obrigado pela contribuição!
2026-08-24 14:16:20 -03:00
Jacob Stoner
ddee064f1b fix(sse): preserve auto scoring order (#11400)
Validado em lote combinado (batch-0824f, junto de #11399/#11402/#11407) contra o tip de release/v3.8.50: typecheck:core limpo, file-size/changelog/complexity/cognitive-complexity OK, 56/56 testes focados passando incluindo os deste PR (tests/unit/combo-task-aware.test.ts).

Remove `auto` da lista de estratégias task-routing genéricas — coerente com o #11399, que também protege a ordem já computada pelo `auto` contra reordenação por outro pós-processamento. Obrigado pela contribuição!
2026-08-24 14:16:04 -03:00
Jacob Stoner
b1fdfd5ea4 fix(sse): preserve auto-selected first target (#11399)
Validado em lote combinado (batch-0824f, junto de #11400/#11402/#11407) contra o tip de release/v3.8.50: typecheck:core limpo, file-size/changelog/complexity/cognitive-complexity OK (abaixo do baseline), 56/56 testes focados passando incluindo os deste PR (tests/unit/8370-priority-affinity-reorder.test.ts).

Aditivo e coerente: protege a ordem já decidida pelo `auto` contra reordenação pelo pós-processamento de prompt-cache-affinity — mesma linha do #11400. Obrigado pela contribuição!
2026-08-24 14:15:52 -03:00
Markus Hartung
71eeaf293c fix(combo): reconcile #11360 retry-loop persisted-cooldown recheck return shape
The retry-loop recheck returned a non-conforming {ok:false, reason} object
that breaks typecheck against the established {ok, response?} contract used
everywhere else in this function. Aligns with the pre-dispatch skip pattern
(return null after fallbackCount++), matching the PR's own intent: skip this
target and move to the next, not error the whole attempt.

This is a live fix — the broken shape reached origin/release/v3.8.50 via
#11360's own squash-merge and was breaking typecheck:core until now.
2026-08-24 12:32:09 -03:00
Markus Hartung
406f4524ff chore(quality): rebaseline file-size for #11355/#11344/#11381/#11362/#11382/#11383 growth
These entries were already validated in an earlier merge-batch worktree but
never reached origin (worktree discarded before pushing). Re-adding them
here since #11355's test/route.ts growth (1215->1237) is now live on
origin/release/v3.8.50 and fails the frozen cap otherwise.
2026-08-24 12:24:40 -03:00
Markus Hartung
dfc5b5eec4 perf(providers): lazy validate provider schema on demand to reduce startup heap 2026-08-24 12:23:50 -03:00
ggdayup
0a53c8a2ce test(providers): update reserved-prefix count fixture to 391 after upstream merge
Upstream 65e81158a added new providers to the registry; the reserved set
is a full REGISTRY walk, so the pinned count moves 329 -> 391. The
tracked-artifacts pre-commit gate fails on this branch because the same
upstream commit force-tracked two docs/superpowers/ files that its own
.gitignore excludes — an inherited upstream issue unrelated to this fix,
so hooks are skipped for this fixture-only commit with operator approval.
2026-08-24 12:22:38 -03:00
ggdayup
93da24cd79 fix(providers): reject reserved provider prefixes on compatible-node create/update
A compatible node created with prefix "tokenrouter" was silently
unreachable: the runtime model resolver (src/sse/services/model.ts)
skips compatible-node lookup for built-in registry ids/aliases, so
"tokenrouter/qwen/..." routed to the built-in tokenrouter provider and
failed with "No active credentials for provider: tokenrouter" even
though the node itself worked when addressed by its internal id.

Reject reserved prefixes at the write path instead:

- new shared module src/shared/constants/reservedProviderPrefixes.ts
  (REGISTRY ids + aliases, case-sensitive, built lazily) — single
  source of truth consumed by both the runtime guard and the
  validation schemas so they can never drift apart
- createProviderNodeSchema / updateProviderNodeSchema now reject
  reserved prefixes with a clear message naming the colliding prefix
- src/sse/services/model.ts consumes the shared module; runtime
  behavior is byte-for-byte unchanged (verified e2e)

Set semantics mirror the old inline guard exactly: manual alias ids
outside REGISTRY (xiaomi/llamacpp/aq) do not intercept nodes at
runtime and stay allowed; mixed-case input (TokenRouter) does not
collide with the exact-match runtime lookup either.
2026-08-24 12:22:38 -03:00
杨思源
815c7c2864 fix(volcengine): exempt volcengine-console from the -web naming convention
Upstream added a lint test requiring every web-cookie provider ID to end
with -web. volcengine-console extracts a console session (not a chat-web
credential), so it is exempted explicitly.
2026-08-24 12:22:00 -03:00
deploy
8f15b79a84 feat(volcengine): phone/SMS auto-login for console with MFA + identity selection
- Session-based headless login service (volcengineConsoleAutoLogin)
- API: POST /connect {phone} + /code /status /cancel /resend /identity sub-routes
- Dashboard modal: phone → SMS code → MFA step-up → identity selection
- Falls back to the legacy headful manual flow on risk-control/TOTP-binding
- Route guard: connect subtree stays LOCAL_ONLY + spawn-capable
2026-08-24 12:22:00 -03:00
yangsiyuan.rengar
07a378c86c feat(volcengine): switch Agent Plan discovery to ListAgentPlanLatestModel 2026-08-24 12:21:59 -03:00
yangsiyuan.rengar
34150506f2 fix(volcengine): retain API-callable Agent Plan models 2026-08-24 12:21:59 -03:00
yangsiyuan.rengar
76ac1c8b7e feat(volcengine): live model discovery for Ark plan providers
Replace the static curated model lists for volcengine-agent-plan and
volcengine-coding-plan with live discovery from the console APIs
(GetAgentPlanModelMappingMeta / ListArkCodeLatestModel), authenticated by
the console cookie+csrf already captured at plan binding time.

- Add volcenginePlanModelDiscovery.ts: fetch + parse + capability enrichment
  (family->contextLength/vision/reasoning map, conservative default fallback).
  Console calls go through a dynamic undici import to bypass OmniRoute's
  global fetch patch (built for LLM provider traffic, reroutes console hits).
  Coding plan's ListArkCodeLatestModel needs {AccountId:<number>} extracted
  from the console cookie; agent plan's GetAgentPlanModelMappingMeta filters
  PlatformAllowStatus===true && Type==='llm'.
- Remove both plan ids from CURATED_MODEL_ONLY_PROVIDERS so synced models
  merge into /v1/models and the dashboard Sync Models button works.
- sync-models route: short-circuit to console discovery for plan providers
  (the chat API has no /models endpoint); persist via
  replaceSyncedAvailableModelsForConnection.
- volcenginePlanBinding: set autoSync:true on new plan connections so the
  24h modelSyncScheduler refreshes them automatically.
- volcPlanAutoSyncBackfill: idempotent boot-time backfill so pre-existing
  plan connections also enter the scheduler.

Verified end-to-end on local OmniRoute build against live Volcano console:
agent plan synced 7 LLMs, coding plan synced 11 models, /v1/models exposes
all of them (incl. new glm-5-3-260801 / deepseek-v4-flash-260801).
2026-08-24 12:21:59 -03:00
yangsiyuan.rengar
d732cf615d feat(volcengine): add Ark plan providers 2026-08-24 12:21:59 -03:00
Yao Lu
f58e8bef6f fix(opencode): close Muse Responses streams at completion 2026-08-24 12:21:51 -03:00
Nicolas Duran Garces
243445f210 docs(changelog): record Codex tool call fix 2026-08-24 12:21:44 -03:00
Nicolas Duran Garces
13e29f2f39 fix(translator): preserve Claude tool call state 2026-08-24 12:21:44 -03:00
Zius
2544ee9498 feat: enable Linux PATH inheritance for autostart & extend loginShellPath to Linux (#11372)
Merged via consolidated batch validation. Fixes autostart on Linux failing to inherit the user's shell PATH (CLI-dependent features like Kiro's Google OAuth broke). Resolved a conflict against a batch sibling in bin/cli/commands/doctor.mjs (kept the more complete prebuilds-aware candidate list) and setup-claude.mjs (formatting only). Own test (login-shell-path-3321.test.ts, 10/10) passes + typecheck:core clean. Thanks!
2026-08-24 12:20:02 -03:00
Prabhudutt Dash
440113c8e8 fix(dashboard): align sync interval slider ticks via magnetic checkpoints (#11394)
Merged via consolidated batch validation. Model Database sync-interval slider used two incompatible coordinate systems (evenly spaced labels vs a linear 1-168h scale); moves the slider to checkpoint-space so the thumb and labels agree. Own test passes.
2026-08-24 12:13:45 -03:00
Bob.Hou
3c2906a80e fix(sse): kill entire process tree on Linux for adobe firefly sign-in to prevent orphan browser instances (#11387)
Merged via consolidated batch validation. Fixes orphaned browser processes on Linux for Adobe Firefly sign-in: spawns Chrome as a process-group leader (detached:true) and kills -pid instead of the single PID, with self-termination guards. Own test passes.
2026-08-24 12:13:40 -03:00
Bob.Hou
095f424658 fix(sse): spare live user message across all aggressive compression sub-paths (#11386)
Merged via consolidated batch validation. Aggressive compression could collapse the live user's active prompt into a [COMPRESSED:summary] marker; now spares the last user message across all sub-paths (applyAging, fallback summarizer, caveman/lite). Own test passes.
2026-08-24 12:13:36 -03:00
Mr White
20de0d9c79 fix(usage): parse CREDIT_LIMIT rows from z.ai coding-plan quota API (#11378)
Merged via consolidated batch validation. Z.ai's quota API now returns CREDIT_LIMIT rows for GLM Coding Plan subscription keys instead of TOKENS_LIMIT, breaking the dashboard quota card. Own test passes.
2026-08-24 12:13:19 -03:00
Nguyen Thanh Dat
9f30b76057 fix(live-ws): resolve the public socket URL at runtime (#11377)
Merged via consolidated batch validation. Fixes live-ws public socket URL resolution for prebuilt Docker/npm images, where NEXT_PUBLIC_* is inlined at build time and can never carry an operator's runtime value. Own test passes.
2026-08-24 12:13:15 -03:00
Nguyen Thanh Dat
019ad33a61 fix(auth): keep the real upstream reason in lastError (#11376)
Merged via consolidated batch validation. markAccountUnavailable collapsed every non-string upstream error reason to a generic 'Provider error' literal, hiding the actual upstream detail operators need in lastError. Own test passes.
2026-08-24 12:13:11 -03:00
Nguyen Thanh Dat
dfc9257b07 fix(cli): spawn npm the way Windows needs in omniroute update (#11374)
Merged via consolidated batch validation. Fixes omniroute update on Windows (npm.cmd cannot be execFile'd without a shell on Node >=24, nodejs/node#52554). Extracts a shared bin/cli/npm-exec.mjs (also handles Bun, windowsHide) mirroring the existing server-side pattern in src/lib/services/installers/utils.ts. Own tests pass. Note: #11336 fixed the same underlying bug (#11335) with a narrower inline change; closed as duplicate crediting this more complete fix.
2026-08-24 12:13:06 -03:00
MSiva
37e71915db fix(translator): preserve functionCall id in Gemini to OpenAI request translation (#11365)
Merged via consolidated batch validation. Fixes geminiToOpenAIRequest discarding functionCall.id in favor of a random generated id, causing multi-turn tool-call id mismatches against OpenAI-compatible upstreams. Own test passes.
2026-08-24 12:13:02 -03:00
Nguyễn Viết Tuấn
077bc1a8a2 fix(compression): use pathToFileURL for workerUrl to prevent bundler resolution failure (#11364)
Merged via consolidated batch validation. Fixes Webpack/Turbopack production build failure (Module not found: compressionWorker.js) by using pathToFileURL(join(...)) instead of new URL(..., import.meta.url), which static bundler scanning misidentifies as an asset import.
2026-08-24 12:12:43 -03:00
sprintberlin
378eff0f75 fix(combo): pre-skip targets with persisted connection cooldown and re-check on retry (#11360)
Merged via consolidated batch validation, with one fix applied during batch validation: the retry-loop persisted-cooldown recheck returned a non-conforming {ok:false, reason} shape that failed typecheck against the established {ok, response?} contract — aligned it with the pre-dispatch skip pattern (return null after fallbackCount++), matching this PR's own intent (skip the target, don't error the whole attempt). Pre-skips combo targets with a persisted connection cooldown and re-checks fresh before transient retries. Own regression suite (13/13, including the fixed retry-recheck path) passes.
2026-08-24 12:12:38 -03:00
Rouzbeh†
6de542b9b6 fix(providers): mark Antigravity connects with no Cloud Code projectId as degraded (#11284) (#11358)
Merged via consolidated batch validation. Production evidence (VPS docker instance): Antigravity OAuth connects ending without a Cloud Code projectId were persisted as silently active while every model call failed; now persisted as degraded. Own tests pass.
2026-08-24 12:12:33 -03:00
sprintberlin
315b0a94e1 fix(resilience): preserve active cooldowns during recovery and probes (#11355)
Merged via consolidated batch validation (fix applied for a cross-PR interaction with #11360, both boarded in the same batch — see combo.ts reconciliation commit). Startup crash recovery cleared every non-terminal transient cooldown unconditionally, erasing legitimate multi-day weekly quota cooldowns on restart. Now only clears expired/unparseable ones. Own repro tests pass.
2026-08-24 12:12:30 -03:00
sprintberlin
e1c2b347f9 fix(quota): parse absolute ISO datetime reset timestamps in weekly quota fallback (#11353)
Merged via consolidated batch validation. Fixes GLM/Z.AI weekly quota fallback: parseDayGranularityResetMs only recognized 'reset in N days', dropping the real multi-day cooldown when upstream returns a full absolute ISO datetime. Own repro test passes.
2026-08-24 12:12:26 -03:00
Paco Cartones
f88aa48847 test(db): make exclusive-connection-lease uniqueness test self-contained (#11341)
Merged via consolidated batch validation. Test-only fix: exclusive-connection-lease uniqueness test implicitly depended on lease state from an earlier test in the same file (shared DB instance, reset only in test.after) — now self-contained. No production change.
2026-08-24 12:12:04 -03:00
Paco Cartones
8301984734 fix(i18n): complete zh-CN/zh-TW CLI locales and guard their parity (#11339)
Merged via consolidated batch validation. Completes 45 missing zh-CN/zh-TW CLI locale keys and adds a parity guard so future gaps fail CI. Own tests pass.
2026-08-24 12:12:00 -03:00
Paco Cartones
028f1b91e4 fix(release): count sweep-stale matches by their real category in the summary (#11338)
Merged via consolidated batch validation. Fixes sweep-stale-fragments.mjs miscounting: classifyFragments never actually produces matchedBy==="ref" (only "pr-number"/"text"), so the pr-number bucket was permanently 0 in the release captain's report. Own test passes.
2026-08-24 12:11:56 -03:00
stanley
2af1326adf fix(catalog): add Stealth Ox Alpha (stealth/ox-alpha) to the openrouter free roster (#11337)
Merged via consolidated batch validation. Data fix so stealth/ox-alpha becomes visible in /v1/models under hidePaidModels (synced-provider-row filter drops pricing metadata before isFreeModel; adds :free suffix handling). Own test passes.
2026-08-24 12:11:52 -03:00
Paco Cartones
644dd32d3f fix(cli): resolve tray runtime import to a file:// URL so --tray works on Windows (#11332)
Merged via consolidated batch validation. Fixes omniroute server --tray on Windows: absolute paths passed to dynamic import() are parsed as URLs, and a Windows drive letter (C:) isn't a supported URL scheme. Resolves via pathToFileURL. Own regression test passes.
2026-08-24 12:11:49 -03:00
Diego Rodrigues de Sa e Souza
9df3f8923d fix(build): stop bundling the better-sqlite3 stub at runtime (#11343) (#11391)
Merged via consolidated batch validation (worktree `.claude/worktrees/batch-0824e`, 27-PR batch). Critical fix: next.config.mjs unconditionally aliased better-sqlite3 to its build-time stub, but Turbopack's resolveAlias applies at RUNTIME too — every request on any build from the release/v3.8.50 tip answered HTTP 500 because the real driver was never loaded. Gates green; own tests pass.
2026-08-24 12:11:36 -03:00
Markus Hartung
0b7ac870ef sync with tip before push 2026-08-24 09:55:31 -03:00
Markus Hartung
9fedc1c411 merge #11381 onto updated tip 2026-08-24 09:50:48 -03:00
Markus Hartung
e589831952 sync with tip before push 2026-08-24 09:46:03 -03:00
Diego Rodrigues de Sa e Souza
04d2a60331 fix(video): make one-frame scene sampling deterministic (#11344)
Merged via consolidated batch validation. Makes scene_aware Video Bridge sampling deterministic for a one-frame budget: falls back to the midpoint of the active full-video/focus window and reports policyEffective: uniform (a single scene candidate can't preserve both temporal ends). Adds opt-in real-FFmpeg fixture matrix (rapid edge cuts, one-frame budget, static/gradual scenes, sub-second clips, detector failure). Static gates green; own regression suite (videoBridgeSampler.test.ts, video-bridge-sampler-ffmpeg.test.ts) passed in the combined-batch run. Related to #9760. Thanks!
2026-08-24 09:44:50 -03:00
Markus Hartung
d23bfefec0 merge #11383 onto updated tip 2026-08-24 09:44:36 -03:00
Markus Hartung
c8ad44e018 merge #11350 onto updated tip 2026-08-24 09:41:55 -03:00
Diego Rodrigues de Sa e Souza
c83116e634 fix(video): isolate drill-down cache by principal (#11369)
Merged via consolidated batch validation (worktree `.claude/worktrees/batch-0824d`). Video Bridge FU-08 drill-down cache substrate hardening (explicitly PARTIAL per the PR body — no production producer/callsite feeds this cache yet): canonical isolation by principalId+sessionId+videoRef, loopback broker auth, strict Zod contracts, per-principal + global LRU quotas, full JPEG decode/re-encode with truncated-scan and polyglot-tail rejection, cancellation-safe atomic replacement. Static gates green; own regression suite (videoBridgeDrilldown.test.ts, video-bridge-drilldown-authz.test.ts, video-bridge-drilldown-route.test.ts) passed in the combined-batch run. Thanks!
2026-08-24 09:39:40 -03:00
Diego Rodrigues de Sa e Souza
7715825cb8 fix(video): harden visual frame deduplication (#11382)
Merged via consolidated batch validation (worktree `.claude/worktrees/batch-0824d`), stacked on the just-merged #11362 as documented. Moves the Video Bridge frame cap to post-dedup, bounds the perceptual candidate pool to at most 2x budget (max 16), includes the dedup policy/version in result-cache identity, adds cooperative abort checks to the comparator loop. Static gates green; own dedup/cache-version regression suite passed in the combined-batch run (grayscale-16x16-mean-cells-v2 policy, real fixtures). Thanks!
2026-08-24 09:31:00 -03:00
Diego Rodrigues de Sa e Souza
761d38f433 fix(video): harden result cache identity and coalescing (#11362)
Merged via consolidated batch validation (worktree `.claude/worktrees/batch-0824d`). Completes the Video Bridge FU-01 cache-hardening slice: fingerprints authorized video bytes + result-affecting dimensions before a persistent cache hit, strict metadata validation with corrupt-entry recompute, TTL/LRU bounds by count/entry-bytes/aggregate-bytes, coalesced protected HTTPS downloads isolated by tenant, deadline/abort-bounded model selection. Static gates green; own regression suite (tests/unit/guardrails/videoBridgeResultCache.test.ts) passed in the combined-batch run. Thanks!
2026-08-24 09:26:06 -03:00
Diego Rodrigues de Sa e Souza
c6963ca5dd fix(changelog): require verified reconciliation ledger (#11345)
Merged via consolidated batch validation (worktree `.claude/worktrees/batch-0824d`). Removes the broad ALLOW_CHANGELOG_REMOVALS bypass from the anti-CHANGELOG-eat gate and requires a reviewed, SHA-256-bound reconciliation ledger for intentional release-note rewrites (fails closed on malformed/stale/partial ledgers, retired bypass usage). Static gates green; own regression suite (tests/unit/check-changelog-integrity.test.ts, tests/unit/merge-train-plan.test.ts) passed in the combined-batch run — 15/15 CLI/ledger cases. Related to #9985. Thanks!
2026-08-24 09:25:40 -03:00
Diego Rodrigues de Sa e Souza
b010d8bf86 docs(readme): reconcile v3.8.50 metrics and contributors (#11356)
Merged via consolidated batch validation (worktree `.claude/worktrees/batch-0824d`). Reconciles README/diagram claims against the live release branch with explicit, non-conflated denominators (merged-PR ranking vs GitHub Contributors REST vs normalized Git census) and adds a repository-local SVG validator. Static gates green; own SVG-validator + render-pipeline tests (tests/unit/docs-validate-svg.test.ts) passed in the combined-batch run, docs:check-all clean per the PR's own evidence. Thanks!
2026-08-24 09:25:29 -03:00
Diego Rodrigues de Sa e Souza
fdcd15e6a9 docs(openapi): document try proxy operation (#11363)
Merged via consolidated batch validation (worktree `.claude/worktrees/batch-0824d`). Restores the OpenAPI operation-coverage ratchet by documenting POST /api/openapi/try (allowlist, verbs, header denylist, auth, response envelope). Static gates green (typecheck:core, file-size, changelog-integrity, complexity, cognitive-complexity); own contract test (tests/unit/openapi-security-tiers.test.ts) passed in the combined-batch run. Thanks!
2026-08-24 09:25:18 -03:00
Diego Rodrigues de Sa e Souza
12b8df02dd fix(catalog): keep large builds event-loop responsive (#11367)
Merged via consolidated batch validation (worktree `.claude/worktrees/batch-0824d`, 11-PR video-bridge/catalog/ops batch, tip `dafb4ae8`). Fixes the #9147 catalog-scale event-loop regression: reuses one build-local capability snapshot, yields cooperatively during catalog/virtual-pool construction, reads only persisted TTL settings. Static gates: typecheck:core, file-size, changelog-integrity, complexity, cognitive-complexity all green. Own regression test (tests/unit/9147-catalog-eventloop-yield.test.ts) reproduced the RED→GREEN transition in isolated runs per the PR's own evidence; under current shared-devbox load (10-15, multiple parallel sessions) the test intermittently reports INFRA-RED exactly as the PR body pre-disclosed (documented starvation signature, not a code defect). Thanks for the careful RED/GREEN + INFRA-RED discipline.
2026-08-24 09:24:59 -03:00
Diego Rodrigues de Sa e Souza
38d21afc2d docs(changelog): link FU-04 pull request 2026-08-24 08:40:33 -03:00
Diego Rodrigues de Sa e Souza
05e76d6e76 docs(changelog): link FU-07 pull request 2026-08-24 08:31:59 -03:00
Diego Rodrigues de Sa e Souza
93135f8e18 feat(guardrails): add focused video analysis mode 2026-08-24 07:30:28 -03:00
Diego Rodrigues de Sa e Souza
22086a73fa fix(video-bridge): validate structural segment sampling 2026-08-24 06:35:58 -03:00
Diego Rodrigues de Sa e Souza
d4ade9d1d3 fix(video): separate tenant scope from download hash 2026-08-24 05:55:00 -03:00
Diego Rodrigues de Sa e Souza
f54c93c879 fix(video): key download flights with process HMAC 2026-08-24 05:16:13 -03:00
Diego Rodrigues de Sa e Souza
e2e48fdab8 docs(changelog): link Video Bridge cache fix PR 2026-08-24 04:37:54 -03:00
Diego Rodrigues de Sa e Souza
d2cea0811a fix(video): harden result cache identity and bounds 2026-08-24 04:31:27 -03:00
Diego Rodrigues de Sa e Souza
2f18a85310 docs(changelog): link Video Bridge contact-sheet fix 2026-08-24 03:35:10 -03:00
Diego Rodrigues de Sa e Souza
38969ad16b fix(video-bridge): render timestamped contact sheets 2026-08-24 03:20:43 -03:00
Diego Rodrigues de Sa e Souza
dafb4ae808 fix(deps): keep unused pnpm peers out of production (#11342)
* fix(deps): keep unused pnpm peers out of production

* docs(changelog): link dependency policy fix to PR 11342
2026-08-24 02:26:02 -03:00
Erick Kinnee
338c05dc6a fix(models): expose Ollama Cloud native effort tiers (#11307)
Validated on a 17-PR combined board: models-catalog-combo-metadata + ollama-cloud-reasoning-effort-tiers-10788 within the board's 287/287, typecheck:core clean, check:open-sse-typecheck clean, vitest 405/405. Publishes Ollama Cloud's native none/low/medium/high/max effort vocabulary for reasoning-capable passthrough/tagged models with no exact registry declaration, adds none to DeepSeek V4/GLM 5.x, and preserves narrower exact-model vocabularies (GPT-OSS) via intersection. Refs #10788. Thank you @ekinnee!
2026-08-24 01:55:47 -03:00
Nguyen Thanh Dat
6945bbaaba fix(db): escape regex metacharacters in group model patterns (#11311)
Validated on a 17-PR combined board: group-model-pattern-regex-escape within the board's 287/287, typecheck:core clean. matchesModelPattern() only substituted * before compiling to RegExp — every other metacharacter kept its regex meaning, so a malformed group pattern (unbalanced parens/brackets) threw uncaught and broke EVERY request for keys in that group, not just the malformed rule (isModelAllowedForKey has no try/catch and runs on the chat completion path and the /v1/models catalog). Thank you @ntdat812!
2026-08-24 01:55:42 -03:00
Ravi Tharuma
690f684bfc feat(audio): add native ElevenLabs HTTP compatibility routes (#11312)
Validated on a 17-PR combined board: elevenlabs-native-routes + hard-session-lease-bypass-inventory (9/9) within the board's 287/287, typecheck:core clean. Native ElevenLabs compatibility routes (voices, TTS, STT) reusing the stored credential via quota-preflight, sent only as xi-api-key; client authorization headers never forwarded. Closes #10556. Thank you @RaviTharuma!
2026-08-24 01:55:36 -03:00
Ravi Tharuma
c3cd1f94c0 feat(services): expose sanitized CLIProxyAPI account health (#11314)
Validated on a 17-PR combined board: cliproxy-accounts + cliproxy-tab + cliproxy-account-health + cliproxy-resolve-spawn-args-6877 (16/16) within the board's 287/287, typecheck:core clean, env-doc-sync clean. Exposes a sanitized read-only CLIProxyAPI account health view (5s-bounded client, explicit allowlist excluding names/paths/emails/tokens/status messages) through a management-authenticated API + dashboard card. Closes #6342. Thank you @RaviTharuma!
2026-08-24 01:55:31 -03:00
Webman
c21460f22a fix(lint): drain release-green hard failures on release/v3.8.50 (#9985) (#11317)
Validated on the resolved merge against the current release tip: pack-artifact-policy + cli-mcp-call-commands + cli-resilience-commands + cli-skills-commands + model-hide-multikey-11300 39/39, typecheck:core clean, eslint clean. Resolved a pt-BR.json wording conflict against #11322 (kept the tip's wording, semantically identical). Drains the real lint-fallout from the wave that was blocking the release-green verdict — dead code + newly-enforced React-Compiler hook rules. Thank you @jonlwheat2-gif!
2026-08-24 01:54:31 -03:00
Ravi Tharuma
9b14896a6c feat(api): add Google AI Studio Gemini TTS (#11315)
Validated on a 17-PR combined board: gemini-tts + vertex-media + audio-speech-handler (41/41) within the board's 287/287, typecheck:core clean. Registers public google/gemini-*-tts speech models and translates OpenAI-compatible /v1/audio/speech to the AI Studio generateContent audio contract, reusing the Vertex inline-audio/PCM/WAV conversion path. Batch TTS only, Gemini Live is out of scope. Thank you @RaviTharuma!
2026-08-24 01:50:44 -03:00
Ravi Tharuma
29f26293c3 feat(compression): isolate sync engines in bounded worker pool (#11318)
Validated on a 17-PR combined board: compression-worker + colocate-standalone-esm-scope within the board's 287/287, typecheck:core clean, env-doc-sync clean. Offloads eligible sync compression engines into a bounded worker_threads pool with a strict serializable DTO boundary and fail-open on spawn/worker/timeout failure. Closes #11023. Thank you @RaviTharuma!
2026-08-24 01:50:39 -03:00
Nguyen Thanh Dat
cb11592441 fix(db): judge the proxy URL host by address, not by spelling (#11319)
Validated on a 17-PR combined board: upstream-proxy-host-spelling 8/8 within the board's 287/287, typecheck:core clean. Routes src/lib/db/upstreamProxy.ts through the shared outbound-guard helpers instead of a private dotted-quad regex copy that had drifted since #10843 — closes the IPv4-mapped IPv6, ULA, link-local and CGNAT bypasses while preserving the deliberate loopback allow (CLIProxyAPI on localhost:8317). Multicast widened from /224\. to the full 224.0.0.0/4, called out explicitly. Thank you @ntdat812!
2026-08-24 01:50:35 -03:00
Ravi Tharuma
5ee646e68e fix(github): verify access tokens during health checks (#11320)
Validated on a 17-PR combined board: token-health-check + token-health-no-refresh-token-expired-5326 + token-refresh-service within the board's 287/287, typecheck:core clean. GitHub access-token-only connections are now actively verified on each due health interval (via the existing Copilot token exchange); the parent credential is marked expired only on a confirmed 401, never on 403/429/5xx/network failures; response bodies and transport messages no longer enter token-refresh logs. Closes #10352. Thank you @RaviTharuma!
2026-08-24 01:50:30 -03:00
Paco Cartones
6984676d95 fix(quality): report the real failure line and stop double-counting ci.yml gates (#11321)
Validated on a 17-PR combined board: validate-release-green within the board's 287/287, typecheck:core clean. Two accuracy bugs in the release-green verdict tool: an unanchored regex blamed a passing test line (matching a filename containing 'fail'), and 6 gates were double-recorded as both hard-failure and drift due to an id-format mismatch (ci.yml script name vs curated id). Found while reading the #9985 verdict — good catch.
2026-08-24 01:50:26 -03:00
Paco Cartones
79f8ae9d1e fix(i18n): add the 3 pt-BR CLI keys that break the locale parity test (#11322)
Validated on a 17-PR combined board: typecheck:core clean, gates within baseline. Restores 3 missing pt-BR CLI keys (setup.opencode, serve.tls_cert, serve.tls_key) — parity restored, 823/823. Thank you @pacocartones!
2026-08-24 01:49:52 -03:00
Nguyen Thanh Dat
04b2c47940 fix(i18n): restore three placeholders dropped from the pt catalogue (#11325)
Validated on a 17-PR combined board: i18n-placeholder-parity within the board's 287/287, typecheck:core clean. Restores 3 dropped placeholders in pt.json (the visible one: the cache tile's subtitle was repeating its own label instead of showing the total) and adds a 42-locale placeholder-set gate so this class of drift can't recur silently. Thank you @ntdat812!
2026-08-24 01:49:48 -03:00
Paco Cartones
24ac71465e test(db): make singleton reset survive the full suite and un-skip the 3 DB-state tests (#11327)
Validated on a 17-PR combined board: capture-critical-db-state 7/7 (all three previously-skipped tests now run) within the board's 287/287, typecheck:core clean. Fixes the racy DATA_DIR-after-dynamic-import isolation and removes a duplicate type declaration. Thank you @pacocartones!
2026-08-24 01:49:42 -03:00
Nguyen Thanh Dat
8d6f91b558 fix(security): refuse proxy-authorization and proxy-authenticate upstream (#11328)
Validated on a 17-PR combined board: upstream-headers-proxy-auth within the board's 287/287, typecheck:core clean, gates within baseline. proxy-authorization and proxy-authenticate join the FORBIDDEN denylist — forwarding proxy-authorization to a model provider would hand that provider the operator's own proxy credential. Thank you @ntdat812!
2026-08-24 01:49:37 -03:00
Diego Rodrigues de Sa e Souza
c3698eedcb fix(dashboard): route the Adapta tutorial CTA through the branded shortener (#11329)
Validated on a 17-PR combined board: TSX parses clean, eslint clean. Adapta tutorial CTA href now points at the branded shortener (link.omniroute.online/adapta) while keeping the visible link text as the real domain. Completes #11196's shortener rollout.
2026-08-24 01:49:32 -03:00
Diego Rodrigues de Sa e Souza
adca3b881c fix(kie): map remaining google-imagen Market ids to their real KIE upstream ids (#11326)
Merging --admin with red discrimination (merge-gates §4). Fails: ESLint warnings ratchet drift (inherited base-red), Unit Tests shards containing stream-timing.test.ts (CPU-contention timing flake, assert.ok(total >= 15)ms — unrelated to this PR's scope, open-sse/handlers/imageGeneration.ts), and dast-smoke (advisory, isRequired:null).
2026-08-24 01:10:23 -03:00
Diego Rodrigues de Sa e Souza
ac02c5b42f fix(resilience): don't clear an active rate-limit cooldown for non-quota_exhausted errors (#11277) (#11310)
Merging --admin: only fails are ESLint warnings ratchet drift (inherited base-red) and dast-smoke (advisory, isRequired:null). Zero overlap with this PR's scope (src/lib/usage/providerLimits.ts).
2026-08-23 22:55:33 -03:00
Diego Rodrigues de Sa e Souza
07d1816a45 fix(providers): hidden models leak into GET /v1/models (#11300) (#11309)
Merging --admin: only fails are ESLint warnings ratchet drift (inherited) and dast-smoke (advisory, isRequired:null). Zero overlap with this PR's file scope (src/app/api/v1/models/catalog.ts).
2026-08-23 22:35:29 -03:00
Praveen K Palaniswamy
65e81158ab fix(ollama): route models by advertised capability (#11088)
Landed with the design call resolved per the owner's pick — **option 1**: the synced store is now endpoint-agnostic (persistDiscoveredModels and managedModelImport no longer drop non-chat models at write time), and chat selectability moved to read time (auto-pool expansion in autoStrategy applies filterChatSelectableModels; the models-route projection already had its chatOnly filter). Your discovery test now passes end-to-end (3/3): /api/show capabilities persist per connection and image/embedding requests route through the advertising host.

Reconciliation notes: conflicted areas merged onto the current tip (adobe discovery import, requestedModel preflight signature, resolvedProvider fast-path coexists with the synced-route override — explicit resolution wins); carried base-red drains (#10055 memoization, #11071 test variants) dropped as already-landed; the managed-model-import exclusion test was propagated to the new contract (image/video models persist; the read filter still hides them from chat pickers — pinned by a new assertion). Full battery: 205/206 focused (the one red is a confirmed periodic-timer timing flake on the loaded devbox — 20/20 isolated), autoCombo vitest 30/30, combo suites 46/46, gates + typecheck clean.

Thank you @yourspraveen — the capability probe + routing design was right; it just needed the store contract opened up. Fixes #11087.
2026-08-23 11:45:01 -03:00
Praveen K Palaniswamy
c68cda7dfb fix(resilience): honor shared passthrough providers (#11075)
5 — Fornecedores locais compartilhados (ollama-local, LM Studio, vLLM) declaram passthroughModels:true no registry, mas hasPerModelQuota() não consultava o registry compartilhado — fallha de modelo faltante virava cooldown de conexão inteira. Broadens a classificação de model-lockout. TDD + 78/291 testes + typecheck + lint verdes. Fecha #11071.
2026-08-21 22:06:21 -03:00
Ravi Tharuma
ca23eed77c fix(models): memoize getModelsDevPricing (event loop / healthz) (#10055)
* fix(models): memoize getModelsDevPricing for /v1/models catalog

resolveCatalogPricing called getModelsDevPricing once per model while
building GET /v1/models. Each call re-scanned models_dev_pricing and
JSON.parsed every row (~10k SQL scans + multi-GB parse work), pegging
the event loop so even /healthz timed out (#9685, #10052).

Memoize the parsed map until saveModelsDevPricing / clearModelsDevPricing
and add a unit test for invalidation.

Signed-off-by: Ravi Tharuma <RaviTharuma@users.noreply.github.com>

* fix(db): invalidate modelsDevPricing cache on DB reset (#10055)

Copilot review fixes:
1. Register invalidateModelsDevPricingCache() with DB state reset system
   so resetDbInstance() clears the process-local memo, preventing stale
   pricing data from surviving across DB reset/restore operations.
2. Add test assertion verifying DB reset bypasses the memo (Copilot #10055).

The process-local memo at modelsDevSync.ts:204 caches getModelsDevPricing()
results until saveModelsDevPricing()/clearModelsDevPricing() to avoid
re-scanning all pricing rows on every /v1/models request. Without this hook,
backup restore and test DB resets would serve stale cached data from the
previous connection.

Tests: npm run test:unit:serial -- tests/unit/modelsDevSync-extended.test.ts

---------

Signed-off-by: Ravi Tharuma <RaviTharuma@users.noreply.github.com>
Co-authored-by: Ravi Tharuma <RaviTharuma@users.noreply.github.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2026-08-13 00:45:27 -03:00
ritheshcn25
5f0a394091 Hide health-check excluded models from /v1/models catalog (#10026)
Mirror the request-time exclusion rule (provider_specific_data.excludedModels)
in the unified catalog builder: a model is hidden when its provider has
connections but none of them is eligible for it. Applied across the
PROVIDER_MODELS, synced, custom, alias-backed, and managed-fallback loops
so ghost models no longer appear as available.

Co-authored-by: ritheshcn25 <ritheshcn25@users.noreply.github.com>
2026-08-13 00:43:38 -03:00
diegosouzapw
918fba5e39 fix(repo): harden .gitignore to also ignore a _tasks symlink (/_tasks)
_tasks is a SEPARATE nested git repo (gitignored). The pattern _tasks/ (trailing
slash) ignores only a directory, not a SYMLINK named _tasks. A self-referential
_tasks symlink can slip in via git add -A and, once pulled, checkout materializes
it over the real _tasks repo (destroying plans/specs/hands-off). Anchored /_tasks
ignores the symlink too, preventing re-capture.
2026-08-08 01:18:49 -03:00
Diego Rodrigues de Sa e Souza
026e1cadaa fix(deps): bump nanoid, dompurify for Dependabot #189, #190
Closes Dependabot #189 (dompurify 3.4.13) and #190 (nanoid 3.3.17). npm audit → 0.
2026-08-08 00:10:31 -03:00
diegosouzapw
b090b601a5 fix(deps): bump nanoid, dompurify for 2 new Dependabot alerts (#189, #190)
Bumps: nanoid ^3.3.17 (was transitive, now overridden), dompurify ^3.4.13
(with monaco-editor scoped override). Closes Dependabot #189, #190.

Remaining #182-#188 (js-yaml + mermaid) already closed by #9651 merge —
awaiting Dependabot re-scan.

npm audit → 0 vulnerabilities.
2026-08-08 00:08:57 -03:00
785 changed files with 56550 additions and 4361 deletions

View File

@@ -1027,6 +1027,16 @@ PROVIDER_LIMITS_SYNC_SPACING_MS=1500
# Used by: open-sse/services/compression/engines/rtk/filterLoader.ts. Default: 0.
#OMNIROUTE_RTK_TRUST_PROJECT_FILTERS=0
# Maximum concurrent synchronous compression workers. Excess jobs wait FIFO.
# Used by: open-sse/services/compression/compressionWorkerPool.ts. Default: 2.
#OMNI_COMPRESSION_WORKERS=2
# Per-job worker timeout (ms). A timed-out worker is terminated and the request fails open.
# Used by: open-sse/services/compression/compressionWorkerPool.ts. Default: 120000.
#OMNI_COMPRESSION_WORKER_TIMEOUT_MS=120000
# Terminate idle compression workers after this many milliseconds.
# Used by: open-sse/services/compression/compressionWorkerPool.ts. Default: 60000.
#OMNI_COMPRESSION_WORKER_IDLE_MS=60000
# T02 stacked-pipeline engine circuit-breaker (OPT-IN, default off). When enabled, a compression
# engine that throws repeatedly across requests is skipped (fail-open) for a cooldown.
# Used by: open-sse/services/compression/pipelineEngineBreaker.ts.
@@ -2011,6 +2021,9 @@ APP_LOG_TO_FILE=true
# CLIPROXYAPI_HOST=127.0.0.1
# CLIPROXYAPI_PORT=5544
# CLIPROXYAPI_CONFIG_DIR=~/.cli-proxy-api
# Management key for an externally managed instance. Embedded instances use
# OmniRoute's encrypted service key.
# CLIPROXYAPI_MANAGEMENT_KEY=
# ── Mux embedded service ──
# Override the port where the embedded Mux (coder/mux) agent-orchestration
@@ -2420,10 +2433,10 @@ APP_LOG_TO_FILE=true
# test suite must NEVER mutate the OS trust store (a fake test PEM installed via
# update-ca-certificates broke all system TLS on a persistent runner, 2026-07-05).
# OMNIROUTE_SKIP_SYSTEM_TRUST=1
# check-changelog-integrity.mjs (anti CHANGELOG-eat gate): explicit base ref
# override, and the justified-removal escape hatch for intentional bullet removals.
# check-changelog-integrity.mjs (anti CHANGELOG-eat gate): explicit base ref override.
# Intentional transformations require an exact reviewed entry in
# config/release/changelog-reconciliations.json; there is no runtime bypass.
# CHANGELOG_BASE_REF=origin/release/v0.0.0
# ALLOW_CHANGELOG_REMOVALS=1
# ── Remote audio provider nodes ──
# Used by: src/app/api/v1/_shared/audioProviderNodes.ts — lets the /v1/audio/*

View File

@@ -685,6 +685,14 @@ jobs:
- run: npm run build:cli
- name: Assert dist/server.js exists
run: test -f dist/server.js || (echo "dist/server.js missing — build:cli did not assemble correctly" && exit 1)
# `build:cli` monta dist/ mas NAO grava dist/BUILD_SHA — so `build:release` faz
# isso, chamando write-build-sha.mjs. O guard de proveniencia do #10427, dentro
# de check:pack-artifact, rejeita um artefato sem SHA (e rejeita mesmo com
# OMNIROUTE_ALLOW_CANARY_BUILD=1: o que nao da para identificar nao da para
# vouchear). Sem este passo o par build+validate deste job e estruturalmente
# incompativel e falha 100% das vezes.
- name: Stamp dist/BUILD_SHA for the provenance guard (#10427)
run: node scripts/build/write-build-sha.mjs
- run: npm run check:pack-artifact
# WS1.2 (#7065 class): pack the real tarball, install it into a clean prefix and
# BOOT it to a healthy /api/monitoring/health — the gate that structure checks
@@ -792,9 +800,23 @@ jobs:
# D3 (plano mestre): a coverage é coletada NESTE mesmo run (c8/NODE_V8_COVERAGE propaga
# aos filhos através do npm) — elimina a matrix Coverage Shard ×8, que re-executava a
# suíte inteira só para medir o gate. Padrão usado pelo CI do próprio nodejs/node.
# Heap: os shards rodam sob instrumentacao de cobertura do V8, que retem muito
# mais memoria que a suite crua. Com o teto antigo de 4096 MB os shards passaram
# a abortar com SIGABRT (exit 134, "Ineffective mark-compacts near heap limit")
# ao redor de 4086 MB conforme o catalogo de providers cresceu no ciclo v3.8.50 —
# todos os testes passavam e o processo morria no fim, o que le como falha de
# teste sem ser. O teto vive em `test:unit:ci:shard` (package.json) e agora
# acompanha os 8192 MB ja usados pelas variantes nao-shardadas; os runners
# GitHub-hosted tem 16 GB.
- name: Unit tests (shard ${{ matrix.shard }}/8) with V8 coverage
env:
TEST_SHARD: ${{ matrix.shard }}/8
# NODE_OPTIONS (nao so o flag em test:unit:ci:shard) porque quem estoura o
# heap e o processo `c8` que embrulha a suite — ele agrega ~577 MB de JSON
# de cobertura bruta. Subir o teto so no node filho deixa o pai no default
# do V8 (~4 GB) e o OOM continua igual, em ~4083 MB. Mesmo padrao ja usado
# pelo job de merge de cobertura mais abaixo.
NODE_OPTIONS: --max-old-space-size=8192
run: |
rm -rf coverage-shard coverage-shard-report
npx c8 \

1
.gitignore vendored
View File

@@ -14,6 +14,7 @@ _tasks/
.agents/**
.claude/**
.gemini/**
.code-forge/**
.config/**
.data/**
.logs/**

View File

@@ -46,7 +46,7 @@ Repository map and Reference Documentation sections below.
## Project at a Glance
**OmniRoute** — unified AI proxy/router. One endpoint, 350 LLM providers, auto-fallback.
**OmniRoute** — unified AI proxy/router. One endpoint, 352 LLM providers, auto-fallback.
| Layer | Location | Purpose |
| ------------- | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
@@ -718,3 +718,13 @@ The dashboard is reachable at the operator's chosen URL/port (default `http://lo
- **Local VPS / shared dev environments**: ask the operator for the URL and current credentials — they live in their personal vault, NOT in this repo.
> Any credential observed in a previous version of this file was a non-production demo value; treat it as compromised and do not reuse it.
<!-- BEGIN:nextjs-agent-rules -->
# This is NOT the Next.js you know
This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` (resolved from this file's directory; in monorepos the `next` package may not be visible from the repo root) before writing any code. Heed deprecation notices.
This block is written and re-added by `next dev` — verify at `node_modules/next/dist/server/lib/generate-agent-files.js`. Removing it from a diff only re-creates the uncommitted change; committing it with your work keeps the tree clean.
<!-- END:nextjs-agent-rules -->

View File

@@ -15,8 +15,81 @@
`docs/routing/STRICT_ZERO_COST.md`.
---
- feat(services): show sanitized CLIProxyAPI account health from its authenticated management API without exposing credentials, file paths, or raw account metadata (#6342)
- **feat(credential-health):** pace the credential health sweep per connection via `provider_connections.healthCheckInterval` (minutes, 0 = never), with `CREDENTIAL_HEALTH_CHECK_INTERVAL` as the global default ([#8443](https://github.com/diegosouzapw/OmniRoute/issues/8443))
- **behavior change:** `healthCheckInterval` is a shared column — it paces both the OAuth token refresh and the credential health sweep, and `0` disables both. The connection editor defaults it to 60, so configured OAuth connections are now credential-checked at 60min instead of the previous ~10min (aligned with the probe-volume goal of #8443)
- **feat(providers):** publish Poolside's Laguna Preview catalog statically — `poolside/laguna-xs-2.1` and `poolside/laguna-s-2.1` (262144 context, 32768 max completion, tools + reasoning, text-only), so the models are routable and visible before a key is configured instead of only after live discovery. Pins the catalog form of the XS id against the `laguna-xs.2` variant carried by third-party listings. ([#9085](https://github.com/diegosouzapw/OmniRoute/issues/9085))
- feat(modality-bridge): bridge Chat and Responses video parts through a strict trusted-loopback, quota-bounded FFmpeg broker; enforce HTTPS redirects/SSRF plus format, protocol, stream, pixel, frame, 50 MiB broker/remote, 36 MiB inline, and 120-second limits; propagate caller aborts; preserve the actual successful fallback model through cache/meta/headers; expose sampled latency and honest success telemetry; and ship the localized Video settings UI (#9760)
- **feat(radar):** Persist local model display-name/enabled overrides and hide/restore tombstones, with authenticated catalog controls and feed safety precedence ([#9830](https://github.com/diegosouzapw/OmniRoute/pull/9830))
- **feat(radar):** add curated-family combo suggestions, a guided combo page, and the read-only Radar MCP catalog tool ([#9836](https://github.com/diegosouzapw/OmniRoute/pull/9836))
- **feat(radar):** add a signed live offers feed and supporter offers dashboard ([#9912](https://github.com/diegosouzapw/OmniRoute/pull/9912))
- **feat(radar):** add signed Intel insights, supporter recognition, and local Radar CLI commands ([#9923](https://github.com/diegosouzapw/OmniRoute/pull/9923))
- **feat(radar):** add a localized public news feed and dismissible dashboard launch banner, with the Radar announcement staged inactive for a separately authorized launch ([#9926](https://github.com/diegosouzapw/OmniRoute/pull/9926))
- **feat(admission):** add lane-aware admission probes for combo/fusion/chaos fan-out (fail-open, queueing disabled), an env-wins `OMNIROUTE_CHAT_VIRTUAL_LANES` activation flag applied at boot, and adaptive-lane visibility in the `omniroute_get_health` MCP tool (related to #9654)
- **docs(mcp):** complete the MCP server README tool reference so the `schemas/` catalog is fully covered (agent-skills, oneproxy, web, tool-search, combo/routing, pricing and DB-health tools were previously only discoverable via `omniroute_tool_search`)
- **feat(cli):** container-aware auto-config — `setup-*`, `omniroute configure`, `omniroute config set` and the CLI-tool config APIs now refuse to write into a containerised OmniRoute's ephemeral home (CLI exits `2`, API returns `422` with `containerEphemeralTarget`) and point at the host-CLI or bind-mount setup instead; `--allow-container-write` / `OMNIROUTE_ALLOW_CONTAINER_CONFIG_WRITE=true` opt back in. Also fixes `CLI_CONFIG_HOME` so the Compose `host` profile's `/host-home` bind mounts are honoured instead of silently falling back to the container home. (#10057)
- feat(dashboard): opt-in `DASHBOARD_ALLOW_EMBED=vscode` relaxes CSP `frame-ancestors` to `'self' vscode-webview:` and drops `X-Frame-Options` for HTML pages only, so the dashboard renders inside the VS Code Simple Browser (OmniCopilot). Default posture unchanged — API routes stay unframable (#10273)
- **feat(resilience):** warn when `/healthz` is served under event-loop lag ≥200ms so a slow 200 is visible as sick, not healthy ([#10303](https://github.com/diegosouzapw/OmniRoute/issues/10303))
- **feat(docker):** add `GET`/`HEAD` `/livez` as a process-alive probe, distinct from `/healthz` readiness ([#10316](https://github.com/diegosouzapw/OmniRoute/issues/10316))
- feat(providers): add **Cloudflare AI Playground** as a No Auth provider (`cloudflare-playground`, alias `cfp`) — free anonymous chat over the reverse-engineered `cf_agent` WebSocket protocol (PartySocket transport, no account/API key/cookies) with GLM 5.2, Kimi K2.7 Code, DeepSeek V4 Pro, gpt-oss-120B, Llama 3.3 70B, Qwen2.5 Coder 32B and 14 more curated models. The executor drives a headless Chromium via Playwright (the WS upgrade is TLS-fingerprint-gated), translates the `cf_agent` frame stream into OpenAI SSE, and surfaces upstream rate limits (3021) as HTTP 429. Fixes #10389
- **feat(providers):** AI Horde accepts an optional registered API key and advertises only live image models that currently have workers ([#10542](https://github.com/diegosouzapw/OmniRoute/pull/10542))
- **fix(providers):** AI Horde Check validates keys via `/v2/find_user` instead of the unauthenticated OpenAI models list ([#10542](https://github.com/diegosouzapw/OmniRoute/pull/10542))
- **feat(audio):** proxy native ElevenLabs voices, text-to-speech, and speech-to-text HTTP routes through stored OmniRoute credentials, preserving query strings, multipart uploads, binary responses, and upstream errors (#10556).
- **feat(providers):** complete Jina AI as one credential pool — dashboard `jina-ai` / `jina-reader` share a token, `JINA_AI_API_KEY` is a real fallback, Test probes `GET https://api.jina.ai/v1/models` (embeddings fallback hits `jina-embeddings-v5-omni-small`), embed/rerank logs keep `connection_id`, catalog adds `jina-reranker-v3.5`, Omni v5 multimodal `{text}`/`{image}`/`{content}` docs pass through intact, and OmniRoute proxies classify / segment / `jina-search` (`s.jina.ai`). Reader stays a separate `r.jina.ai` card with an explicit label. Gemini Embedding 2 (`gemini/gemini-embedding-2`, alias `google/gemini-embedding-2`) uses dashboard `gemini` keys (or `GEMINI_API_KEY` / `GOOGLE_API_KEY` only when none exist), forwards native multimodal parts, and maps N OpenAI `input` items to N `:batchEmbedContents` vectors instead of one aggregated `:embedContent`. ([#10581](https://github.com/diegosouzapw/OmniRoute/pull/10581))
- **feat(providers):** accept `response_format=ogg` on `/v1/audio/speech` as an alias for the existing Opus/Ogg encoder ([#10587](https://github.com/diegosouzapw/OmniRoute/issues/10587))
- Added Google AI Studio Gemini batch text-to-speech support through `POST /v1/audio/speech`.
- **feat(settings):** add `autoDisableBannedScope` so permanent-ban auto-disable can target subscription/OAuth accounts only, leaving prepaid API keys in the routing pool ([#10617](https://github.com/diegosouzapw/OmniRoute/pull/10617))
- feat(server): emit systemd sd_notify READY/WATCHDOG/STOPPING (generated unit becomes Type=notify with WatchdogSec=180) so a frozen server process is killed and restarted by systemd instead of lingering undetected
- **feat(providers):** add the TabiToken NewAPI gateway (`tabitoken`) and teach the existing HCNSec entry (`hcnsec`) the three further protocols it actually serves. TabiToken leaves the NewAPI pricing endpoint public, so its catalog is read from the host rather than guessed: four Claude models, each reporting the Anthropic and OpenAI protocols. HCNSec shipped OpenAI-only; probing the host showed `/v1/messages`, `/v1/responses` and the Gemini `/v1beta` path all reach its token layer, so each is now declared as an alternate format — with its default format, base URL, auth scheme and regional catalog classification untouched. ([#10668](https://github.com/diegosouzapw/OmniRoute/pull/10668)) — thanks @yawar-aquil
- **feat(sse):** allow an alternate protocol to build its own upstream URL. `AlternateFormat` gained an optional `urlBuilder`, because the Gemini protocol carries the model inside the path (`{base}/{model}:generateContent`) and the existing `chatPath`/`urlSuffix` fields are constants that cannot express it. The route builder is extracted as `buildGeminiGenerateContentUrl` and shared with the native `gemini` provider so the two consumers cannot drift on the `?alt=sse` streaming suffix. ([#10668](https://github.com/diegosouzapw/OmniRoute/pull/10668)) — thanks @yawar-aquil
- **feat(call_logs):** persist the per-call error family in `call_logs.error_type` and expose a failure breakdown (`errorBreakdown`) in the usage analytics endpoint, reusing the existing production classifier ([#10670](https://github.com/diegosouzapw/OmniRoute/issues/10670))
- **feat(proxy):** the proxy-health sweep and `GET /api/settings/proxies/egress` now report an anonymous summary of egress-IP sharing — how many rotation groups share an egress IP and the largest number of accounts behind one IP — computed from persisted `proxy_logs` over a 24h window. No IPs and no account identities by default; `PROXY_LOG_INCLUDE_IPS=true` restores raw details. ([#10677](https://github.com/diegosouzapw/OmniRoute/issues/10677))
- **docs(guides):** OmniRoute now serves VS Code's **native Copilot Chat model picker** through the [OmniCopilot](https://github.com/diegosouzapw/OmniCopilot) extension ([Marketplace](https://marketplace.visualstudio.com/items?itemName=diegosouzapw.omnicopilot) · [Open VSX](https://open-vsx.org/extension/diegosouzapw/omnicopilot) — Cursor, Windsurf, VSCodium, Theia…) — no Copilot subscription needed since VS Code 1.122. New [`docs/guides/VSCODE-COPILOT.md`](docs/guides/VSCODE-COPILOT.md) covers setup, how the picker collapses the `dual`-prefix catalog via `GET /v1/models?prefix=alias`, and the **build-time** `DASHBOARD_ALLOW_EMBED=vscode` flag that renders the dashboard in an editor tab ([#10697](https://github.com/diegosouzapw/OmniRoute/pull/10697))
- **feat(docker):** `DASHBOARD_ALLOW_EMBED` is now a Docker build argument — `docker build --build-arg DASHBOARD_ALLOW_EMBED=vscode` produces an image whose dashboard renders inside the VS Code Simple Browser (OmniCopilot's `dashboardOpen: "editor"`). Previously the flag was only reachable from a source build: Docker silently drops a `--build-arg` with no matching `ARG`, so the operator got the default image and no error. Builder-stage only and empty by default — the runtime stages deliberately do not carry it, and the unframable default posture is unchanged ([#10701](https://github.com/diegosouzapw/OmniRoute/pull/10701))
- **feat(providers):** new `cursor-api` provider (card "Cursor API", alias `cua`): connect a Cursor user API key (`crsr_…`) and route `cursor-api/<model>` through the existing Cursor agent executor (the key is exchanged for a 1h session token and cached), plus a `/api/cursor-cli/*` passthrough so the Cursor CLI itself runs through OmniRoute (`CURSOR_API_ENDPOINT=http://<omniroute>/api/cursor-cli`, `CURSOR_API_KEY=<OmniRoute key>`) with every RPC attributed and logged. The IDE `cursor` provider is unchanged. (#10729)
- **feat(api):** `GET /api/health` now answers `{ status, timestamp }` without a key. Until now the path had no route, so the management-auth boundary answered first with a 401 — indistinguishable from a wrong key or an unknown route, which left Docker HEALTHCHECKs and Kubernetes probes unable to tell "down" from "misconfigured". Kept deliberately minimal: version, uptime and memory stay behind the authenticated `/api/monitoring/health` ([#PRNUM](https://github.com/diegosouzapw/OmniRoute/pull/10771)).
- feat(routing): make Task-Aware Smart Routing's detection patterns operator-configurable via `settings.taskRouting.patternOverrides` (`PUT /api/settings/task-routing`) — the built-in patterns are English-only, so a non-English dashboard had no recourse short of turning detection off entirely; an override now replaces the pattern list for one task type without touching the rest (#10783)
- feat(api): accept PATCH on /api/combos/[id], the verb the OpenAPI spec already documents (#10869)
- **feat(sse):** add GLM-5.3 support (`glm-5.3`, `glm-5.3-high`, `glm-5.3-low`) across the z.ai first-party providers, mapping the upstream `reasoning_effort` request parameter to the existing 5.2 tier UX ([#10896](https://github.com/diegosouzapw/OmniRoute/pull/10896)) — thanks @phuongddx
- **feat(home):** add a live **Recent Requests** panel beside the home Provider Topology (polls `GET /api/usage/call-logs?excludeTests=1` every ~3s, gated by the topology appearance toggle + page visibility). `excludeTests` is now an allowlist of real provider inference (`/v1/%` or `/api/v1/%`), applied before `LIMIT`, so connection-test/model-sync/management rows can never leak into the feed ([#10897](https://github.com/diegosouzapw/OmniRoute/pull/10897), extracted from [#8450](https://github.com/diegosouzapw/OmniRoute/pull/8450)) — thanks @nguyenha935
- **feat(rankings):** free provider rankings now expose a `reliability` field (raw `testStatus`/`rateLimitedUntil` per connection plus a `healthy`/`degraded`/`down` state, reusing the `ProviderHealthState` vocabulary of the provider health matrix) when the configured/available filters are active — derived from already-loaded data, without touching the ranking order ([#10909](https://github.com/diegosouzapw/OmniRoute/pull/10909))
- `feat(resilience)`: when an allowlisted provider (opencode family) answers
429 classified `quota_exhausted` or `rate_limit_exceeded` and its free-tier
quota is bucketed by egress IP (#9611), every connection of that family
sharing the IP is cooled down together before the rotation tries them — one
guaranteed-failed upstream call per episode instead of N, on the combo path
as well. For the allowlisted family a 429 now cools the connection instead
of locking a single model. Exclusive allowlist, never terminal, best-effort
when the egress IP is unknown (#10920).
- **feat(rankings):** free provider rankings can now report what each provider actually served — `reliability.usage` (requests, successes, success rate over a window) behind the opt-in `withUsage`/`usageRange` query parameters, so a provider that answers every call with an error is no longer described as healthy ([#10926](https://github.com/diegosouzapw/OmniRoute/pull/10926))
- **feat(providers):** add Logfare as a free OpenAI-compatible provider — dashboard card with a Free badge and request-logging disclosure (every prompt/completion is logged for research; opt out at logfare.ai/consent), live model discovery from `https://logfare.ai/v1/models` (20 models, 11 chat-capable: kimi-k3, deepseek-v4-pro, glm-5.2, gpt-5.6-luna, minimax-m3…), full chat/streaming through the existing OpenAI-compatible path, the real Logfare logo on the card, and a listing in the free-tiers guide. ([#10987](https://github.com/diegosouzapw/OmniRoute/pull/10987))
- Run synchronous RTK and Caveman request compression in a bounded worker-thread pool, keeping
large `/v1/responses` compression heaps outside the HTTP isolate while preserving strict
fail-open behavior and per-engine telemetry.
- **feat(providers):** let operators declare per-provider error rules through `settings.providerErrorRules` instead of patching the catalog — an operator-supplied rule for a provider is consulted before the built-in `providerRuleRegistry`, receives the raw error text, and has its declared scope/cooldown/reason actually honored end to end, for any provider (declaring the rule is the opt-in — no extra allowlist entry needed). Matches are plain case-insensitive substrings (never RegExp) and bounded to 50 rules to keep the hot path safe ([#11104](https://github.com/diegosouzapw/OmniRoute/pull/11104))
- **feat(combo):** the shared per-request combo attempt budget is now operator-configurable via `maxGlobalAttempts` (combo config / `comboDefaults` cascade), instead of the hardcoded 30. Lower it to fail fast on a dead target pool, raise it for large combos; clamped to `[1, 200]` so an unbounded budget can never cause runaway background requests ([#11134](https://github.com/diegosouzapw/OmniRoute/issues/11134))
- **feat(api):** `/api/usage/om-usage` gains a structured form — `?format=json` returns the key's own usage as `ApiKeyUsageLimitStatus` + `UsageSnapshot` instead of `text/plain`. This is the surface a UI (the OmniCopilot panel) consumes to show a key holder their daily/weekly spend and quota reset. The route is self-service (the caller's own key, gated by `allowUsageCommand`), not the management surface; refusals come back as a discriminated `{ "allowed": false, "error": … }` so a UI can tell "not allowed" apart from "allowed but nothing cached yet". The endpoint was previously undocumented in `API_REFERENCE.md`; it now has a section ([#11190](https://github.com/diegosouzapw/OmniRoute/pull/11190))
- **feat(api):** `/api/usage/om-usage?format=json` now returns `providers[]` — every connection's quota snapshot, not just the single selected one — so a panel can render Codex / Claude / OpenCode side by side. The collector already gathered all of them; the single-pick `provider` field (kept) is a terminal presentation choice. Closes the per-connection gap from OmniCopilot #8 ([#11192](https://github.com/diegosouzapw/OmniRoute/pull/11192))
- **feat(providers):** allow overriding the rate-limit queue wait timeout (`maxWaitMs`) per connection, alongside the existing `rpm`/`tpm`/`tpd`/`minTime`/`maxConcurrent` overrides — a single slow provider no longer has to lower the global wait budget for every other provider (#11251)
- **feat(dashboard):** replace the hard Home → onboarding redirect with a dismissable first-run readiness card so returning users can stay on Home while new users still get a clear 4-step path ([#11282](https://github.com/diegosouzapw/OmniRoute/pull/11282))
- **feat(dashboard):** lead Traffic Inspector with a purpose-first header that separates "what happened" from "how it happened", so beginners can read request outcomes without drowning in protocol detail ([#11283](https://github.com/diegosouzapw/OmniRoute/pull/11283))
- **feat(dashboard):** add an Essentials sidebar preset that shows only the beginner core path (Home → Endpoints → API Keys → Providers → Health → Settings) while keeping Advanced tools reachable via Command Palette search ([#11286](https://github.com/diegosouzapw/OmniRoute/pull/11286))
- **feat(video bridge):** harden the optional drill-down cache substrate with exact-path broker policy, canonical principal/session/media isolation, independent retained-byte quotas, cancellation-safe commits, rejection of excess or non-canonical Base64 padding and non-JPEG/truncated media, warning-sensitive full JPEG canonicalization that strips trailing polyglot bytes, server-derived dimensions, and auditable derivation metadata; production tenant binding and multi-resolution selection remain follow-up work ([#11369](https://github.com/diegosouzapw/OmniRoute/pull/11369))
- **feat(video):** add an opt-in focused analysis mode that safely uses a normalized, 500-code-point latest-user hint for task-aware frame captions while preserving full-mode prompts, temporal-window isolation, and cache identity without storing raw task text ([#11383](https://github.com/diegosouzapw/OmniRoute/pull/11383)).
- feat(command-code): advertise low/medium/high/xhigh/max reasoning-effort suffixes for reasoning-capable models in the catalog and Combo Builder, with request-time resolution to reasoning_effort
- feat(crof): advertise reasoning-effort tiers (none/low/medium/high/max) for live-discovered and seed models, so the catalog, Playground, and Combo Builder surface <model>-<tier> aliases and requests resolve max upstream
- feat(sse): add Cursor plan image generation via Agent CLI (`IMAGE_PROVIDERS.cursor`, format `cursor-agent-image`), reusing the chat Cursor OAuth connection
- feat(routing): add the default-off `DISABLE_CONTEXT_WINDOW_CHECKS` feature flag to let operators bypass OmniRoute's local context-window and max-input-token check for direct single-model requests, leaving upstream limits, prompt compression, and output-token caps intact.
- **feat(catalog):** surface runtime-learned `reasoning_effort` tiers in `/v1/models` `capabilities.effort_tiers` (learned set replaces synced metadata when present), map them to OpenCode `ModelV2.variants` in the OmniRoute plugin, and align dispatch `-<tier>` suffix validation to the effective (learned ?? synced) set — so the UI offers exactly the tiers the upstream accepts (e.g. `{low, high, max}` for `oc/x-preview-f-free`) and each advertised variant completes. Excludes codex/glm/kimi, which keep their own dedicated `-{effort}` suffix mechanism and never gain `effort_tiers` from this path (related to #7694, builds on #11232)
- **feat(usage):** show Kimi Coding's fixed-order Code 5-hour/7-day quota windows plus Extra Usage status, balance, monthly spend/limit, and the official Additional Credits link on Dashboard → Quota cards.
- **feat(providers):** copilot-m365-web now supports OpenAI tool calling — a router planning turn asks the substrate model (as a tool-selection assistant emitting `CALL_TOOL: name({...})` / `NO_TOOL_NEEDED` text, which bypasses its plugin-registry refusal) and validated decisions surface as `tool_calls` with `finish_reason: "tool_calls"` in both stream and non-stream modes; also flattens the full message history (assistant `tool_calls` + compacted tool results) so multi-turn agent loops keep context, replies to SignalR `type:6` keepalives, surfaces `type:3` error frames instead of a silent empty `stop`, and suppresses `writeAtCursor` text from tool-progress frames
- **feat(api):** add `GET`/`POST` `/v1/multimodal-embeddings` as an alias of `/v1/embeddings` so Jina-compatible clients do not receive HTTP 404 `unknown_route` — thanks @RaviTharuma
- feat(opencode-go): expose Muse Spark 1.2 Contributor reasoning-effort aliases (minimal/low/medium/high/xhigh) in the Combo Builder
- **feat(providers):** restore the operator-owned upstream timeout tier per connection via `providerSpecificData.timeoutMs` (preempts the maintainer-only model/provider registry tiers and the global `FETCH_TIMEOUT_MS`), and make the combo per-target timeout ceiling follow the selected connection
- **feat(cli):** run `omniroute serve --tray` as a detached desktop process after server and tray readiness, with graphical login auto-start support.
- **feat(routing):** add client-, provider-, and model-neutral exclusive managed session connection leases with API-key-bound generation fencing, durable SQLite ownership, explicit allowlist policy, and bounded 429 capacity retry semantics.
## [3.8.50] — TBD
## [3.8.50] — 2026-08-25
_Living section — regenerated 2026-08-12 from all cycle commits (cycle open `ed2db6cb19` → tip). Bullets carry the merged PR and its author; direct pushes listed separately._
@@ -178,8 +251,57 @@ _Living section — regenerated 2026-08-12 from all cycle commits (cycle open `e
- **feat(dashboard):** Kimi 15% first-top-up campaign — dedicated tracked link + discount-first banner copy ([#10240](https://github.com/diegosouzapw/OmniRoute/pull/10240))
- **feat(providers):** integrate audited free-tier gateways ([#9210](https://github.com/diegosouzapw/OmniRoute/pull/9210))
<!-- reconciliation pass 2 (Phase 0a.1): commits that landed in the cycle without a bullet -->
- **feat(radar):** **OmniRoute Radar** — the signed free-model catalog overlay — landed end to end this cycle: local catalog state is persisted, guided combo suggestions feed a guided combos page, guided provider setup walks a new operator through connecting, verified model metadata and an owner-only admin link are surfaced, signed supporter offers get their own dashboard and sync path, signed Intel insights arrive with a supporter-recognition UI, a launch-news surface was added, and catalog export runs through a stable workflow that carries provenance ([#10826](https://github.com/diegosouzapw/OmniRoute/issues/10826)). The catalog is also reachable as an MCP tool and through the new `radar status` / `radar sync` CLI commands.
- **feat(video):** Video Bridge gained a full sampling and caching stack — scene-aware and segment-aware sampling with a structural fallback, conservative frame deduplication, validated focus windows, timestamped contact sheets, and an optional audio-fusion timeline that preserves transcript provenance, reports fusion telemetry, and degrades to *partial* on invalid audio. Results are cached with metadata (the key includes the audio transcript and focus window), and drill-downs use an isolated cache bounded by a global byte budget; a sampler/contact-sheet benchmark script ships alongside ([#10483](https://github.com/diegosouzapw/OmniRoute/issues/10483)) — thanks @backryun
- **feat(volcengine):** Volcengine **Ark plan providers** — live model discovery through `ListAgentPlanLatestModel` (retaining only API-callable Agent Plan models) and phone/SMS console auto-login with MFA and identity selection ([#11333](https://github.com/diegosouzapw/OmniRoute/issues/11333) — thanks @rengaryang)
- **feat(providers):** new upstreams this cycle — **Token Kiosk** as an OpenAI-compatible provider ([#10722](https://github.com/diegosouzapw/OmniRoute/issues/10722) — thanks @hgaib), a local **ZCode ACP** backend ([#10184](https://github.com/diegosouzapw/OmniRoute/issues/10184) — thanks @megamen32), and the **tencent-aistudio-web** cookie provider (`tasw`) ([#10174](https://github.com/diegosouzapw/OmniRoute/issues/10174) — thanks @MeRezaRezaei)
- **feat(providers):** Cursor PKCE login with Bearer quota reporting, automatic router selection and explicit empty-turn errors ([#9909](https://github.com/diegosouzapw/OmniRoute/issues/9909) — thanks @yansigit); `cursor` also discovers the account's Agent endpoint instead of assuming one ([#10804](https://github.com/diegosouzapw/OmniRoute/issues/10804) — thanks @tuandinh0801)
- **feat(providers):** tool calling for `copilot-m365-web` via router planning ([#10948](https://github.com/diegosouzapw/OmniRoute/issues/10948) — thanks @acc0mplish)
- **feat(providers):** per-connection upstream timeout tier is back — a connection can again override the provider-wide request timeout ([#10885](https://github.com/diegosouzapw/OmniRoute/issues/10885) — thanks @maxmad64bis)
- **feat(providers):** catalog refreshes — Qwen3.8 model catalogs ([#10226](https://github.com/diegosouzapw/OmniRoute/issues/10226)), Grok 4.6 plus a DeepSeek V4 refresh ([#10195](https://github.com/diegosouzapw/OmniRoute/issues/10195)) — thanks @backryun — and the `agnes` chat catalog moved to the 2026-07-30 listing ([#10942](https://github.com/diegosouzapw/OmniRoute/issues/10942) — thanks @oyi77)
- **feat(providers):** the web-session credential guide gained a Cookie Editor fast path, so cookie-based providers can be connected without hand-copying headers — thanks @benzntech
- **feat(sse):** explicit `glm-5.3-max` reasoning-effort tier ([#11415](https://github.com/diegosouzapw/OmniRoute/issues/11415) — thanks @phuongddx)
- **feat(sse):** discover Anthropic partner models served through Vertex AI ([#11279](https://github.com/diegosouzapw/OmniRoute/issues/11279) — thanks @maci0)
- **feat(sse):** opt-in `STRICT_ZERO_COST` free-access policy — refuses any target that is not verifiably zero-cost ([#10965](https://github.com/diegosouzapw/OmniRoute/issues/10965) — thanks @mymusicmyspace)
- **feat(sse):** Kimi Web token lifecycle manager — rolling auto-refresh and 401 recovery, so `kimi-web` connections stop expiring silently ([#10944](https://github.com/diegosouzapw/OmniRoute/issues/10944) — thanks @MeRezaRezaei)
- **feat(sse):** Cursor plan images through the Agent CLI (`IMAGE_PROVIDERS.cursor`) ([#10842](https://github.com/diegosouzapw/OmniRoute/issues/10842)), and the `i-have-adhd` output style reached vi/ja/id parity behind a style × language guard matrix ([#10425](https://github.com/diegosouzapw/OmniRoute/issues/10425))
- **feat(api):** health and liveness surface — `GET /livez` as a process-alive probe ([#10819](https://github.com/diegosouzapw/OmniRoute/issues/10819)), `GET/HEAD /readyz` aliased to `/healthz` ([#10977](https://github.com/diegosouzapw/OmniRoute/issues/10977)), and `GET /api/health` answering without a key ([#10771](https://github.com/diegosouzapw/OmniRoute/issues/10771)) — thanks @RaviTharuma and @maxmad64bis
- **feat(api):** `/v1/multimodal-embeddings` is now an alias of `/v1/embeddings` ([#10568](https://github.com/diegosouzapw/OmniRoute/issues/10568) — thanks @RaviTharuma), and `/v1/combos` steps flag a pinned account without leaking its id ([#11076](https://github.com/diegosouzapw/OmniRoute/issues/11076) — thanks @ntdatt812)
- **feat(audio):** native ElevenLabs HTTP compatibility routes ([#11312](https://github.com/diegosouzapw/OmniRoute/issues/11312)), Google AI Studio Gemini TTS ([#11315](https://github.com/diegosouzapw/OmniRoute/issues/11315)), and `response_format=ogg` accepted as an opus alias on `/v1/audio/speech` ([#10822](https://github.com/diegosouzapw/OmniRoute/issues/10822)) — thanks @RaviTharuma
- **feat(gemini-web):** image generation exposed through `/v1/images/generations` ([#10494](https://github.com/diegosouzapw/OmniRoute/issues/10494) — thanks @Abhishek4512009)
- **feat(cli):** native Bun backend support plus `Dockerfile.bun` ([#11039](https://github.com/diegosouzapw/OmniRoute/issues/11039) — thanks @rqzbeh), with matching `-bun` / `-web-bun` container images published by the Docker workflow ([#11168](https://github.com/diegosouzapw/OmniRoute/issues/11168))
- **feat(cli):** tray mode detaches from the terminal ([#11230](https://github.com/diegosouzapw/OmniRoute/issues/11230) — thanks @tuandinh0801), Grok Build accepts a custom host ([#10830](https://github.com/diegosouzapw/OmniRoute/issues/10830) — thanks @tuandinh0801), and Linux autostart inherits the login shell `PATH` ([#11372](https://github.com/diegosouzapw/OmniRoute/issues/11372) — thanks @ziuus)
- **feat(cli):** relay-like CLI closure — a target manifest, Codex TOML generation, a Gemini launcher and drift guards, so `omniroute run <cli>` covers the documented agent surface — thanks @backryun
- **feat(dashboard):** beginner-oriented UX pass across the app — a guided endpoint-connection header ([#11228](https://github.com/diegosouzapw/OmniRoute/issues/11228)), a stable outcome header on the batch page ([#11227](https://github.com/diegosouzapw/OmniRoute/issues/11227)), plain-language verdicts for health status ([#11224](https://github.com/diegosouzapw/OmniRoute/issues/11224)) and for the resilience page ([#11215](https://github.com/diegosouzapw/OmniRoute/issues/11215)), orientation before API-key management ([#11195](https://github.com/diegosouzapw/OmniRoute/issues/11195)), and ACP framed as optional advanced setup ([#11206](https://github.com/diegosouzapw/OmniRoute/issues/11206)) — thanks @ignamiranda
- **feat(dashboard):** agentic conversation tracking ([#10263](https://github.com/diegosouzapw/OmniRoute/issues/10263) — thanks @hartmark), a Recent Requests panel on the home page ([#10900](https://github.com/diegosouzapw/OmniRoute/issues/10900)), a VS Code Copilot Chat home banner replacing the Provider Quota card ([#10520](https://github.com/diegosouzapw/OmniRoute/issues/10520)), a CheaperInference sponsor banner with its links routed through the branded shortener ([#11196](https://github.com/diegosouzapw/OmniRoute/issues/11196), [#11329](https://github.com/diegosouzapw/OmniRoute/issues/11329)), and Auto-Combo snapshot generation/duplication in the UX ([#10354](https://github.com/diegosouzapw/OmniRoute/issues/10354) — thanks @swingtempo)
- **feat(routing):** quota-aware provider scheduling, phase 2 ([#10126](https://github.com/diegosouzapw/OmniRoute/issues/10126) — thanks @benzntech); exclusive managed-session connection leases so two sessions cannot claim the same account ([#10362](https://github.com/diegosouzapw/OmniRoute/issues/10362) — thanks @KaspaPulse); an adaptive feedback loop v2 scoring operational and semantic quality, confidence and TTFT/ITL ([#10881](https://github.com/diegosouzapw/OmniRoute/issues/10881) — thanks @Egorich-print); and a `DISABLE_CONTEXT_WINDOW_CHECKS` bypass for the direct-request input/context check ([#10927](https://github.com/diegosouzapw/OmniRoute/issues/10927))
- **feat(combo):** an opaque per-invocation decision trace for priority fallbacks, so an operator can see why a combo picked the target it picked without exposing account identities ([#10730](https://github.com/diegosouzapw/OmniRoute/issues/10730) — thanks @stanleytejakusuma)
- **feat(admission):** adaptive overload and pressure controls wired across the LLM routes, with a monitoring snapshot exposing the structural chat-admission state and shed counters ([#11268](https://github.com/diegosouzapw/OmniRoute/issues/11268)); Responses and Messages bodies are reserved before clone ([#10814](https://github.com/diegosouzapw/OmniRoute/issues/10814) — thanks @RaviTharuma) and structural chat-admission shedding is gated on real heap pressure ([#10437](https://github.com/diegosouzapw/OmniRoute/issues/10437)) — thanks @xz-dev
- **feat(compression):** adopts omniglyph 1.4.0 with semantic profiles and evidence-backed accounting ([#10647](https://github.com/diegosouzapw/OmniRoute/issues/10647)), and the sync engines now run in a bounded worker pool instead of on the request thread ([#11318](https://github.com/diegosouzapw/OmniRoute/issues/11318) — thanks @RaviTharuma)
- **feat(models):** learned `reasoning_effort` sets are surfaced in the catalog, in model variants and at dispatch ([#11252](https://github.com/diegosouzapw/OmniRoute/issues/11252) — thanks @maxmad64bis); `opencode-go` and `command-code` expose Muse Spark reasoning-effort aliases/suffixes ([#10883](https://github.com/diegosouzapw/OmniRoute/issues/10883), [#10884](https://github.com/diegosouzapw/OmniRoute/issues/10884) — thanks @excessivechaos)
- **feat(search):** `context7` added as a library-docs search and fetch provider ([#11140](https://github.com/diegosouzapw/OmniRoute/issues/11140) — thanks @HouMinXi)
- **feat(mcp):** dynamic runtime tool-schema plumbing so blocked providers are removed from the advertised enums instead of failing at call time ([#11155](https://github.com/diegosouzapw/OmniRoute/issues/11155) — thanks @rqzbeh), plus a Radar catalog tool
- **feat(a2a):** A2A v1.0 client compatibility — a `SendMessage` alias and a v1.0-shaped agent card ([#10839](https://github.com/diegosouzapw/OmniRoute/issues/10839) — thanks @wpec)
- **feat(server):** native systemd `sd_notify` watchdog support (`Type=notify`), so a stalled process is restarted by the supervisor rather than hanging ([#10662](https://github.com/diegosouzapw/OmniRoute/issues/10662) — thanks @maxmad64bis)
- **feat(redis):** configurable key namespace prefix, so several OmniRoute instances can share one Redis ([#11042](https://github.com/diegosouzapw/OmniRoute/issues/11042) — thanks @MeRezaRezaei)
- **feat(docker):** hardened Linux VPS deployment recipe ([#10623](https://github.com/diegosouzapw/OmniRoute/issues/10623) — thanks @freudantunes)
- **feat(proxy):** anonymous egress-IP sharing surfaced in the health sweep and the egress API ([#10876](https://github.com/diegosouzapw/OmniRoute/issues/10876) — thanks @maxmad64bis), and a non-destructive auto-disable mode for the proxy health scheduler ([#10342](https://github.com/diegosouzapw/OmniRoute/issues/10342) — thanks @Gi99lin)
- **feat(services):** sanitized CLIProxyAPI account health exposed to the dashboard ([#11314](https://github.com/diegosouzapw/OmniRoute/issues/11314) — thanks @RaviTharuma)
- **feat(db):** the DB health check now reports which SQLite driver is active and what durability it gives you ([#10652](https://github.com/diegosouzapw/OmniRoute/issues/10652) — thanks @maxmad64bis)
- **feat(codex):** sync with Codex v178 identity mechanisms — turn-state relay, persisted seeds and identity faces ([#10716](https://github.com/diegosouzapw/OmniRoute/issues/10716) — thanks @xz-dev)
- **feat(usage):** Kimi Coding "Extra Usage" is shown on the provider card ([#10712](https://github.com/diegosouzapw/OmniRoute/issues/10712) — thanks @xz-dev)
- **feat(oauth):** `gemini-3.7-flash` models added for the `antigravity` and `agy` providers ([#10305](https://github.com/diegosouzapw/OmniRoute/issues/10305) — thanks @Chewji9875)
- **feat(cli-tools):** Prime Agent added to the CLI agents catalog ([#11166](https://github.com/diegosouzapw/OmniRoute/issues/11166) — thanks @arminanton)
- **feat(guardrails):** a focused video-analysis mode for the vision guardrail, so long videos are judged on the requested window instead of the whole timeline
- **feat(ops):** canary deploy path with a provenance gate, a real smoke run and a rollback anchor ([#10446](https://github.com/diegosouzapw/OmniRoute/issues/10446)), backed by artifact provenance verification and a `buildSha` on the health endpoint ([#10444](https://github.com/diegosouzapw/OmniRoute/issues/10444))
- **feat(api):** list embeddings models from the configured providers ([#11249](https://github.com/diegosouzapw/OmniRoute/issues/11249)) and guided Qdrant memory configuration ([#11213](https://github.com/diegosouzapw/OmniRoute/issues/11213)) — thanks @rafacpti23
- **feat(responses):** `previous_response_id` continuation is virtualized even when the upstream does not support it ([#10262](https://github.com/diegosouzapw/OmniRoute/issues/10262) — thanks @hartmark)
### 🐛 Bug Fixes
- **fix(build):** every route no longer answers HTTP 500 on artifacts built from the release tip ([#11343](https://github.com/diegosouzapw/OmniRoute/issues/11343)) — `next.config.mjs` aliased `better-sqlite3` to its build-time stub **unconditionally**, on the premise that `serverExternalPackages` still won at runtime. It does not: a Turbopack `resolveAlias` rewrites the request *before* the externals check, so the request stopped matching the `better-sqlite3` external entry and the stub was baked into the shipped bundle. The sync driver then failed with `r(...) is not a constructor`, fell through `node:sqlite` and sql.js, and the instrumentation hook aborted at boot. Same failure shape as [#6344](https://github.com/diegosouzapw/OmniRoute/issues/6344), so it gets the same treatment: the alias is opt-in via `OMNIROUTE_BETTER_SQLITE3_STUB=1` through the shared `scripts/build/better-sqlite3-stub-flag.mjs` helper — set it only on a build host that actually hits the SIGABRT build-worker teardown ([#10060](https://github.com/diegosouzapw/OmniRoute/issues/10060)); default builds externalize the real native addon. Regression guards: `tests/unit/better-sqlite3-stub-alias-11343.test.mjs` (5) and the env matrix in `tests/unit/next-config.test.ts`.
- **security(search)**: block SSRF via `/v1/search` `provider_options.baseUrl` for the Firecrawl search provider — the client-controlled override is now validated as a public URL before it is used to build the server-side fetch target, so a caller with a valid API key can no longer redirect search requests at loopback, RFC1918, or cloud-metadata hosts — thanks @zmf963
- **providers**: honor `PATCH /api/providers/[id]` so `omniroute providers rotate` stops 405ing (the OpenAPI spec and CLI already use PATCH) (PR #10366)
- **cli**: route provider test commands through configured connection test endpoints (#10570)
@@ -642,6 +764,353 @@ _Living section — regenerated 2026-08-12 from all cycle commits (cycle open `e
- **fix(security):** resolve open CodeQL alerts ([#10188](https://github.com/diegosouzapw/OmniRoute/pull/10188))
- **fix(dashboard):** retarget Kimi promo CTA to the API platform aff link ([#10200](https://github.com/diegosouzapw/OmniRoute/pull/10200))
- **fix(build):** repair broken production build, red lint gate and SWR crash ([#10198](https://github.com/diegosouzapw/OmniRoute/pull/10198))
- fix(cli): repair hollow externalized package dirs in the nested `<distDir>/node_modules` bundle location too, not just the top-level one, fixing macOS/Linux Electron `ERR_MODULE_NOT_FOUND` on Turbopack-externalized packages (#7346)
- **Electron packaged smoke test:** add a cold-restart mode (`ELECTRON_SMOKE_COLD_RESTART=1`, wired blocking on the Linux release leg) that relaunches the packaged app against its own persisted `DATA_DIR` and asserts a native SQLite driver was selected instead of the sql.js WASM fallback, closing the regression-test gap flagged in the stale-ABI `better-sqlite3` investigation ([#7592](https://github.com/diegosouzapw/OmniRoute/issues/7592)).
- **fix(usage):** keep session/weekly/monthly quota windows in chronological order on every provider card. The order is now derived from the quota keys themselves instead of a provider whitelist, so Claude, MiniMax, Z.ai and Command Code stop rendering the two bars in opposite positions across sibling accounts ([#7764](https://github.com/diegosouzapw/OmniRoute/issues/7764))
- **fix(images):** retry Codex image generation on a sibling ChatGPT account when the requested model isn't entitled on the current account, instead of failing the request outright ([#8307](https://github.com/diegosouzapw/OmniRoute/pull/8307)).
- fix(dashboard): treat UncloseAI as a no-auth provider so the connect form no longer forces a fake API key (#8864)
- **fix(dashboard):** model-level allowed/blocked param edits now persist when the compatibility popover is closed by clicking outside, and a failed save no longer clears the edit or reports success ([#9013](https://github.com/diegosouzapw/OmniRoute/pull/9013))
- fix(ssrf): make `getProviderOutboundGuard()` (used for search-provider connection validation, image generation and remote image fetch) honor the local-first default `OMNIROUTE_ALLOW_LOCAL_PROVIDER_URLS` the same way the chat validation guard already does, so a LAN-hosted SearXNG/Brave search provider works with only the LOCAL flag set instead of silently requiring `OMNIROUTE_ALLOW_PRIVATE_PROVIDER_URLS` ([#9123](https://github.com/diegosouzapw/OmniRoute/issues/9123)).
- fix(compression): preserve unfenced raw code (e.g. Copilot #file references) from Caveman's prose recapitalization/whitespace cleanup, which was corrupting keyword casing and indentation (#9144)
- fix(api): yield the event loop during catalog builds and bulk-load override/hidden-model tables (#9147)
- fix(combo): recovery hint for all_targets_skipped now points at provider quota/availability instead of 'transient, just retry' (#9303)
- fix(providers): strip uniqueItems from Gemini tool schemas (Gemini rejects it with 400 'Unknown name uniqueItems') (#9617)
- **fix(translator):** convert OpenAI `image_url` blocks nested in `role: "tool"` / `tool_result` content to Claude `image` source blocks so OpenAI-compatible clients (Kimi Code CLI `ReadMediaFile`, and any other tool that returns media) no longer 400 the next Claude-format upstream turn ([#9692](https://github.com/diegosouzapw/OmniRoute/issues/9692))
- **fix(resilience):** retry a retryable Codex pre-output 502/503/504/507 once on the same account (23s jitter) before cooling the connection, and stop translating that mixed pool into an all-accounts quota `429` ([#9708](https://github.com/diegosouzapw/OmniRoute/issues/9708))
- **fix(ratelimit):** respect operator `minTimeBetweenRequestsMs` floor when relaxing the limiter on headroom — the adaptive rate-limit learning no longer silently erases a configured minimum gap between requests when the upstream reports plenty of remaining capacity ([#9763](https://github.com/diegosouzapw/OmniRoute/issues/9763)).
- **fix(test):** remove live `npm pack` from MCP files unit test (it stalled concurrent `test:unit` via prepare→husky + monorepo pack walk); keep the static #3578 `files` allowlist + negation guards in unit and fold #3821 pack assertions into `check:pack-artifact` / `check:pack-policy` (already `--ignore-scripts`).
- fix(dashboard): media mini-playgrounds authenticate via session instead of sending the masked API key as Bearer, fixing 401s under REQUIRE_API_KEY (#9935)
- fix(sse): exclude search providers from credential-health scheduler sweep to stop burning billed API queries (#9970)
- **Passthrough streaming:** stop leaking upstream SSE control lines (`id:`/`event:`/`retry:`/`:` comments) to plain OpenAI Chat-Completions-format clients, while preserving `event:` framing for OpenAI Responses API and Claude Messages API passthrough ([#10017](https://github.com/diegosouzapw/OmniRoute/issues/10017)).
- fix(cli): stop diagnosing every Next.js instrumentation-hook failure as the Android/Termux cache bug — only the Android "Unsupported platform: android" signal now triggers the Android hint, so a win32/desktop instrumentation error surfaces its real cause instead of a useless `mkdir -p ~/.cache` (#10028)
- **fix(build):** stop the native `better-sqlite3` addon from loading during the Next.js production build (#10060). Its `Statement` destructor aborts with `SIGABRT` when a build worker thread exits (assertion in `node::RemoveEnvironmentCleanupHook`, `env == nullptr`), which can leave the build with no standalone bundle. Every DB entry point now keys off a reliable `OMNIROUTE_BUILDING=1` signal (set by `build-next-isolated.mjs` and inherited by every spawned build worker, because Next.js workers sometimes drop `NEXT_PHASE`): `getDbInstance()` returns a no-op SQLite stub during build, `driverFactory` skips the native driver and falls through to `node:sqlite`, and the `codegraph`/`kiro-import` lazy loaders fail closed. A build-time `better-sqlite3` alias to a stub (`next.config.mjs`, turbopack) backs this up without changing runtime behaviour (the real package is still `require()`d natively via `serverExternalPackages`). Also raises the default build heap 4096→6144 MB and caps Next build worker pools (`CIRCLE_NODE_TOTAL=8`) to avoid the many-core page-data-collection SIGSEGV, and adds `.gitattributes` (`*.sh text eol=lf`) so kernel-exec'd shell scripts never ship with CRLF shebangs. Deliberately does NOT downgrade the Node base image: per the maintainer's review on #10060, `release/v3.8.50` moved to `node:26-trixie-slim` through several considered commits, so the `OMNIROUTE_BUILDING` guard is re-derived against the current base rather than reverting the FROM line; the npm pin and binary-hide dance from the original PR are dropped because our build already rebuilds `better-sqlite3` deterministically via `node-gyp` and floats `npm@latest` for the CVE overlay.
- **fix(providers):** the five g4f.space sub-providers (Groq, Gemini, Pollinations, Ollama, NVIDIA) no longer advertise a free tier — a keyless `POST /v1/chat/completions` now returns `402 insufficient_credits` behind a proof-of-work "cake" wall (re-verified live 2026-08-22), so `hasFree` is `false` and the notes point at `g4f.dev/members.html`. The gateway still works with a member key, so its registry wiring and `authType: "optional"` are unchanged ([#10071](https://github.com/diegosouzapw/OmniRoute/issues/10071)) — thanks @chirag127
- **fix(chatgpt-web):** Preserve native `max` thinking effort through ChatGPT Web routing ([#10077](https://github.com/diegosouzapw/OmniRoute/pull/10077)) — thanks @zannen7
- Fix: wire AgentRouter's existing console balance fetcher into the Dashboard Quota UI (visibility gate + provider-limits data path + background sync) so its wallet balance renders instead of falling back to "Usage API not implemented" (#10078)
- Fix: AgentRouter's dollar balance now renders as a currency-formatted "$X.XX" credits row in the Dashboard Quota UI instead of a bare percentage, and an exhausted wallet always shows exactly $0.00 (#10078)
- fix(sse): bridge generic openai-compatible/anthropic-compatible provider type ids to their concrete uuid node id in credential lookup (#10085)
- fix(domain): stop treating an unreported Antigravity quota fraction (`fractionReported:false`) as 0% remaining in `quotaCache.ts`, which was falsely marking every fresh/newly-connected account as exhausted and blocking multi-account rotation (#10095)
- fix(dashboard): remap unified Kimi Code card API-key save to the admitted `kimi-coding-apikey` connection id, fixing 400 "Invalid provider" on Save (#10096)
- fix(antigravity): strip trailing model turn for native Gemini requests too, not just Claude (#10104)
- **fix(admission):** stop the adaptive latency-gradient collapse from permanently locking out ordinary requests — individually valid requests now make solo progress when the system is idle and normal pressure, and the collapsed limit actively recovers on sustained idle windows instead of being stuck; the critical-pressure fuse still wins over solo progress (#10111)
- fix(sse): downgrade client-supplied `thinking:{type:"adaptive"}` to `enabled` and gate the `context-1m-2025-08-07` beta on model eligibility when a combo/fallback re-routes a request to a non-adaptive/non-1M model like claude-haiku-4-5 (avoids "adaptive thinking is not supported on this model" and "long context beta is not yet available" 400s, #10119)
- **fix(logging):** move call-log artifact serialization and filesystem writes to a bounded singleton worker to keep request handling responsive (#10123)
- **perf(logging):** bound each scheduled call-log rotation pass to incremental database and filesystem work (#10125)
- **fix(streaming):** start early SSE heartbeats when Responses or Messages requests opt into streaming through the request body (#10127)
- **fix(combo):** scope session-stickiness bindings to their owning Combo so identical first messages cannot carry a successful target into another priority chain and bypass its configured order (fixes #10136)
- **fix(translator):** resolve the Claude thinking output cap with the routed provider so a provider-scoped-only `max_output_tokens` override is no longer invisible to `fitThinkingToMaxTokens()`, which previously let the synthesized `max_tokens` (caller room + thinking budget) go out unbounded and 400 upstream ([#10139](https://github.com/diegosouzapw/OmniRoute/issues/10139))
- fix(providers): correct the conol-web registry fallback-models import depth, which pointed at a
non-existent `open-sse/config/services/` and made any suite loading the provider registry fail to
resolve (#10140)
- **fix(oauth):** Claude connections created via `claude-auth/import` now send required CLI headers on the bootstrap identity call and persist a `cliUserID` device identity, fixing intermittent "Third-party apps now draw from your extra usage" 400s on otherwise valid imported subscription tokens ([#10144](https://github.com/diegosouzapw/OmniRoute/pull/10144), fixes [#10143](https://github.com/diegosouzapw/OmniRoute/issues/10143))
- **fix(sse):** Responses-passthrough `response.completed` snapshots now drop `phase:"commentary"` items the same way live SSE frames already do, so the terminal `response.output` array no longer echoes internal commentary text that was already suppressed from the stream (#10156).
- fix(proxy-subscriptions): allow local/loopback proxy-subscription fetch URLs (local-first, cloud-metadata still blocked) (#10158)
- **fix(routing):** keep approximate Combo context estimates advisory so requests reach concrete targets instead of returning a pre-dispatch 400 ([#10162](https://github.com/diegosouzapw/OmniRoute/pull/10162)) — thanks @xz-dev
- **docs(settings):** document Thinking Budget modes (passthrough vs auto-strip); fix dashboard i18n key collision that showed Auto Combo routing copy on the thinking tab; clarify independence from compression/cache ([#10169](https://github.com/diegosouzapw/OmniRoute/pull/10169))
- fix(cli): guarantee a non-empty `[STARTUP] Fatal:` log line for any instrumentation-hook boot throw, not just DB-driver init failures (#10171)
- fix(sse): gate structural chat admission shedding on real heap pressure instead of unconditional capacity, with a bounded headroom budget so a healthy heap can no longer bypass admission control indefinitely (#10183, #10268)
- **fix(guardrails):** Vision Bridge handles OpenAI Responses `input`/`input_image` requests before combo vision filtering ([#10202](https://github.com/diegosouzapw/OmniRoute/pull/10202)) — thanks @Zartharas
- **fix(cursor):** Stop truncating pending tool calls on non-composer models when a KV checkpoint arrives after text but before the `exec_mcp` frame — the KV short-circuit is now gated to the composer family where it was verified ([#10215](https://github.com/diegosouzapw/OmniRoute/issues/10215)).
- **fix(responses):** repair corrupted SSE deltas for non-ASCII streams by keeping a single stream-aware `TextDecoder` (`{ stream: true }`) across `transform()` calls instead of recreating it per chunk and decoding without the `stream` flag. When a multi-byte UTF-8 character (CJK/emoji) was split across two TCP chunks — common in Chinese streaming text — the per-chunk decoder truncated it to `U+FFFD`, corrupting every delta while the rebuilt `*.done` snapshot stayed internally identical ([#10223](https://github.com/diegosouzapw/OmniRoute/issues/10223))
- **fix(combo):** defer the known-context-overflow hard rejection for compressible requests so compression runs before the final context gate, instead of a raw-body estimate 400'ing generic Responses clients targeting a large model before OmniRoute can shrink it ([#10225](https://github.com/diegosouzapw/OmniRoute/issues/10225))
- **fix(api):** deleting a manually-added custom model no longer tombstones a provider-synced model that shares its id. `DELETE /api/provider-models` is addressed by `provider` + `model` alone, so when both a custom row and a synced row existed for one id it removed both and wrote `isDeleted:true`. `replaceSyncedAvailableModelsForConnection` then filtered that id out of every subsequent re-import, so the provider could never resync — model sync kept reporting `added: N` while the catalog stayed empty and `/v1/models` never listed the model again, even though routing to it still worked. The custom row is now removed first and its presence is treated as the operator's intent, leaving the synced sibling importable; a synced-only delete still tombstones as before (#3199, #3782 unaffected) ([#10228](https://github.com/diegosouzapw/OmniRoute/pull/10228)) — thanks @Neuron-Mr-White
- **Audio Bridge:** fix production transcription self-loop uploads so real audio reaches the configured STT provider instead of falling back to an unavailable-provider stub ([#10229](https://github.com/diegosouzapw/OmniRoute/pull/10229)).
- **fix(api):** DeepSeek V4's native `max` reasoning tier is now reachable. DeepSeek accepts `reasoning_effort` `low`/`high`/`max` and maps `medium`/`xhigh` down to `high`, while OmniRoute's canonical vocabulary collapses `max` onto `xhigh` — so `{"effort":"max"}` silently resolved to `high` and the catalog never advertised a `max` tier (or its `<model>-max` variant). Following the existing `extendCodexGpt56EffortValues` precedent, the native tier is now preserved for `deepseek`/`ds` V4 models only; the global effort vocabulary is unchanged, routed namespaces (`openrouter/deepseek/…`, `tllm/deepseek_v4`, `oc/deepseek-v4-flash-free`) keep the canonical behavior, and an explicit client `reasoning_effort` still wins ([#10230](https://github.com/diegosouzapw/OmniRoute/pull/10230)) — thanks @Neuron-Mr-White
- **fix(providers):** FreeAIAPIKey now targets `api.freeaiapikey.com`, the host upstream names in its `410 endpoint_moved` response — every request through the provider was failing — and its catalog is resynced to the 10 models the live `/v1/models` actually serves ([#10233](https://github.com/diegosouzapw/OmniRoute/pull/10233))
- **fix(providers):** MonsterAPI's deprecation now actually applies — the flag was written as `isDeprecated`, a key no consumer or schema reads, so the provider kept rendering as healthy in the dashboard, the onboarding wizard and the generated provider reference ([#10234](https://github.com/diegosouzapw/OmniRoute/pull/10234))
- **fix(cliproxy):** read platform/arch at runtime via `os.platform()`/`os.arch()` in `binaryManager` so the embedded installer selects the Windows/ARM assets even when the release bundle is built on a Linux runner (fixes #10244)
- **fix(providers):** compatible/custom providers now save valid Data URL icons and show Add/Edit save failures instead of silently doing nothing ([#10247](https://github.com/diegosouzapw/OmniRoute/pull/10247)) — thanks @xz-dev
- **fix(models):** custom model metadata and compatible-provider context overrides now take precedence over discovered metadata, while deleting a synced model no longer creates a permanent tombstone so a later provider sync can restore it ([#10248](https://github.com/diegosouzapw/OmniRoute/pull/10248)) — thanks @jackjinke
- fix(open-sse): stop concurrent requests colliding on the same dedup hash for non-OpenAI target formats (#10249)
- **fix(translator):** Text-format tool calls emitted inline by some models are now converted to proper `tool_use` blocks. Certain models (DeepSeek, Qwen) return tool invocations as `<tool_call>{"name":"Bash","arguments":{…}}</tool_call>` or `TOOL_CALL Read: {"file_path":"…"}` inside the text stream instead of the structured `tool_calls` field. Both formats leaked through the Claude translators as plain text, so Claude Code rendered the raw block and stalled instead of executing the tool. `extractXmlInvokeBlocks` (previously `<invoke>`-only) now scans for all three shapes in a single pass and emits `content_block_start`/`input_json_delta`/`content_block_stop` events, in both `openai-to-claude` and `gemini-to-claude` (Antigravity) paths ([#10251](https://github.com/diegosouzapw/OmniRoute/pull/10251))
- fix(dashboard): make provider card warning indicators expose the interaction they advertise (#10261)
- fix(command-code): route chat to the documented /provider/v1/chat/completions endpoint instead of the CLI-only /alpha/generate, which Command Code gates/blocks for external callers (#10265)
- **fix(providers):** preserve validator HTTP status codes in API-key and web connection-test results so callers can distinguish authentication, rate-limit, and upstream failures ([#10272](https://github.com/diegosouzapw/OmniRoute/pull/10272)) — thanks @Zartharas
- **fix(sse):** tiny-budget reasoning probes (e.g. Claude Code's `/model` check sends `max_tokens: 1`) are answered with a valid truncated 200 instead of relaying the upstream 5xx "empty response content" — which previously also marked the connection unavailable and poisoned fallback/cooldown bookkeeping for a request that is only a probe ([#10281](https://github.com/diegosouzapw/OmniRoute/issues/10281)) — thanks @harkaranbrar7
- fix(video): stop advertising the googleflow (Veo) video provider as working and fail fast with a clear diagnostic — its submit/poll endpoints 404 and no server-side OAuth transport can satisfy the working endpoint (#10285)
- **fix(build):** stop Turbopack from dead-code-eliminating the Windows Tailscale branches of `src/lib/tailscaleTunnel.ts` in the published build (#10293). The release `dist` is bundled on a Linux runner, and the bundler constant-folds `process.platform`, pruning every non-Linux branch — the Windows installers shipped with no `where` lookup, an always-injected `--socket`, and a lost `net start Tailscale`/windows-default-binary path. The module now reads the platform at runtime via `os.platform()` (a function call a bundler cannot fold), so the Windows branches survive on any build machine; a vitest regression test mocking `os.platform()``win32` guards the anti-fold invariant (RED before, GREEN after).
- **fix(ops):** Docker HEALTHCHECK defaults to the lightweight `/healthz` lifecycle probe instead of the heavy `/api/monitoring/health` path, with an `OMNIROUTE_HEALTHCHECK_PATH` opt-in override ([#10311](https://github.com/diegosouzapw/OmniRoute/pull/10311))
- fix(api): hash the API key before using it as the model-catalog cache Map key (no raw credentials in process heap) (#10313)
- fix(resilience): keep combo quality and auth failure reasons separate and redact connection labels in terminal errors (#10314)
- fix(dashboard): send periodic WS heartbeat pings so live dashboard connections stop dropping every ~35s (#10319)
- **fix(chat-body-admission):** restore a single process-wide admission budget — heavyweight leases and queued bytes are now bounded once for the whole process instead of per session, so one session can no longer mint extra capacity or starve others; per-session fairness is preserved via round-robin dispatch ([#10110](https://github.com/diegosouzapw/OmniRoute/issues/10110))
- **fix(providers):** validate Z.ai web Local Storage sessions against the authenticated user-settings endpoint and preserve exact upstream status codes ([#10329](https://github.com/diegosouzapw/OmniRoute/pull/10329)) — thanks @Zartharas
- **fix(opencode-plugin):** publish bare combo model ids without the plugin provider prefix so OpenCode can select them ([#10345](https://github.com/diegosouzapw/OmniRoute/issues/10345))
- **fix(backend):** log `auto/<family> matched no connected models` once per process per label instead of every minute ([#10346](https://github.com/diegosouzapw/OmniRoute/issues/10346))
- fix(backend): redact client IPs and account prefixes from default proxy logs (#10348)
- **fix(github):** proactive credential health now verifies GitHub access tokens through the existing Copilot token exchange, marks only a confirmed `401 Unauthorized` as expired, and leaves rate limits, permission failures, upstream failures, and network errors routable ([#10352](https://github.com/diegosouzapw/OmniRoute/issues/10352)) — thanks @RaviTharuma
- **fix(docker):** warn at boot when `OMNIROUTE_MEMORY_MB` disagrees with `NODE_OPTIONS --max-old-space-size`, and document that the standalone/Docker launcher appends `OMNIROUTE_MEMORY_MB` last ([#10353](https://github.com/diegosouzapw/OmniRoute/issues/10353))
- fix(providers): GitLab Duo falls back to the public Code Suggestions endpoint when direct_access returns 401 (#10365)
- **fix(db):** `getSettings()` defaults `debugMode` to `false` — fresh installs no longer run in debug mode (persisted `debugMode: true` is preserved) ([#10372](https://github.com/diegosouzapw/OmniRoute/pull/10372) — thanks @lamchun1110)
- **fix(translator):** Consolidate tool-name casing normalization into a single `restoreClaudeToolName` helper reused across every response path (`openai-to-claude`, `gemini-to-claude`, `stream` passthrough, xAI and Antigravity handlers), replacing six hand-copied 7-entry casing maps. The shared helper resolves via the request-side `toolNameMap` first (preserving declared PascalCase and MCP/alias names), then the complete `TOOL_RENAME_MAP` (which already covers `glob`/`grep`/`task`/`todowrite`/`skill`/`askuserquestion`/etc.), then the `#7926` TitleCase→lowercase fallback for map-less clients. This closes the coverage gap that left `TodoWrite` and other tools failing with `Error: No such tool available: todowrite`, fixes a `ReferenceError` in `remapToolNamesInResponse`, and preserves the Gemini thought-signature persistence (`#8979`) and OpenAI→Claude `toolNameMap` restoration that must not regress ([#10374](https://github.com/diegosouzapw/OmniRoute/issues/10374))
- **fix(responses):** preserve native tool definitions for custom OpenAI-compatible providers when using the Responses API (`/v1/responses`). When `apiType` is set to `"responses"` (or `_omnirouteForceResponsesUpstream` is enabled), OmniRoute passes native tool shapes (`custom` with lark grammars, `namespace`, `local_shell`) directly upstream without running a lossy Responses→Chat→Responses conversion ([#10374](https://github.com/diegosouzapw/OmniRoute/issues/10374))
- fix(dashboard): Free Tier 'used this month' now includes live usage_history rows, not just the rolled-up daily summary (#10381)
- **fix(executors):** OpencodeExecutor and MimocodeExecutor now rotate to the next account on network exceptions (timeout, connection refused/reset) when the failed account has a dedicated proxy, not only on 429 — a throw on one account no longer fails the whole request when other accounts remain. Accounts sharing the default egress (no proxy) fail fast instead of retrying the same outage against every account. The shared rotation mechanics (`pickAccount`/`markCooldown`/`markSuccess`) are now extracted into `accountRotation.ts`, fixing an identical unconditional-cooldown gap that pre-dated this PR in MimocodeExecutor ([#10393](https://github.com/diegosouzapw/OmniRoute/pull/10393))
- **fix(sse):** the header-budget drop warning fires once per unique dropped-header set instead of on every SSE response (warn-storm fix) ([#10397](https://github.com/diegosouzapw/OmniRoute/pull/10397) — thanks @lamchun1110)
- fix(sse): fail over combo streaming responses that reach `finish_reason` with zero content, reasoning, or tool_calls instead of forwarding a terminated-but-empty completion (#10404)
- **fix(guardrails):** Vision Bridge now reroutes whole requests for named combos whose targets have zero vision-capable models (previously such image requests died with `capability_mismatch` when the describe path could not run), and when the fallback describe path also fails for every image the request degrades to explicit `(unavailable)` stub text instead of preserving images the combo cannot consume ([#10415](https://github.com/diegosouzapw/OmniRoute/pull/10415)) — thanks @rqzbeh
- **fix(antigravity):** geo-blocked egress (Google "User location is not supported") is now classified (scoped to the Google AI surfaces that emit it: Cloud Code/Gemini Code Assist, Gemini API, Vertex), cached as a 24h per-account exclusion so routing continues with other accounts, and surfaced with an actionable message; the dashboard connection test now probes the real `streamGenerateContent` model surface instead of the non-geo-restricted OAuth userinfo endpoint ([#10420](https://github.com/diegosouzapw/OmniRoute/pull/10420)) — thanks @rqzbeh
- **fix(antigravity):** strip competing-agent identity sentences from system prompts (e.g. "You are a Claude agent, built on Anthropic's Claude Agent SDK.") that Antigravity flags and answers with 429 RESOURCE_EXHAUSTED (port of decolua/9router b566b20) ([#10420](https://github.com/diegosouzapw/OmniRoute/pull/10420)) — thanks @rqzbeh
- **fix(antigravity):** accounts with an empty Cloud Code `projectId` now heal themselves — failed auto-onboarding (`onboardUser`) attempts are retried after a short backoff instead of being memoized forever, so the missing Google project is created without user action on a later request or token refresh ([#10424](https://github.com/diegosouzapw/OmniRoute/pull/10424)) — thanks @rqzbeh
- **fix(antigravity):** Google deprecated automatic project creation for standard-tier (personal) accounts — when `onboardUser` completes without a project id the account now fails fast with a clear `403 GCP_PROJECT_REQUIRED` message (no more generic 422 or delayed 429 RESOURCE_EXHAUSTED), and a manual GCP Project ID override is available in the connection editor so operators can enter their own project id ([#10424](https://github.com/diegosouzapw/OmniRoute/pull/10424)) — thanks @rqzbeh
- **fix(usage):** read Gemini `usageMetadata` out of the antigravity `{ response: {...} }` envelope so non-streaming requests log real token usage instead of `IN 0 | OUT 0` (port of decolua/9router#59d858b) ([#10430](https://github.com/diegosouzapw/OmniRoute/pull/10430)) — thanks @rqzbeh
- **fix(usage):** surface Gemini `cachedContentTokenCount` into `cached_tokens` for non-streaming requests so cache-hit accounting matches the OpenAI/Claude/Responses branches and the streaming path (follow-up to the #10430 envelope fix) ([#10465](https://github.com/diegosouzapw/OmniRoute/pull/10465)) — thanks @rqzbeh
- **fix(antigravity):** automatically rotate to a sibling account when one is BYOP (GCP Project ID required, `gcp_project_required` 422) — the account is excluded from selection for 24h and the request succeeds via another account instead of failing fast; the actionable 422 is surfaced only when no sibling exists (follow-up to the #10424 BYOP fast-fail) ([#10470](https://github.com/diegosouzapw/OmniRoute/pull/10470)) — thanks @rqzbeh
- fix(mitm): forward passthrough traffic to the actual requested Host instead of misrouting every non-TARGET_HOSTS request to the hardcoded Antigravity sandbox host (#10479)
- **fix(docker):** point the bifrost sidecar at the real `ghcr.io/maximhq/bifrost:v1.6.11` tag and the cliproxyapi sidecar at the official `docker.io/eceasy/cli-proxy-api:v6.9.7` image (the previously pinned tags never existed), and complete the runtime `OMNIROUTE_BASE_PATH` subpath patch for Next 16 standalone (assetPrefix + client env + baked asset URLs) so prebuilt images respect the webpath env var ([#10482](https://github.com/diegosouzapw/OmniRoute/pull/10482))
- fix(sse): stop ZWJ-obfuscating the substring "hermes" in user messages and hostnames (#10484)
- **fix(memory):** auto-check Qdrant health on mount and stop the false-red status badge on `/dashboard/memory?tab=engine` — the badge treated "not yet checked" (`health === null`) as a failure, so a healthy Qdrant showed red after every page refresh until "Test connection" was clicked; settings changes now also invalidate the stale result and re-check after the save persists, so a health check racing the settings PUT can no longer keep the badge red until a manual re-test ([#10489](https://github.com/diegosouzapw/OmniRoute/pull/10489))
- **test(compression):** align source-contract tests with the merged `release/v3.8.50` base (`aa912c42a`) — accept the multi-line `providerTransport` shape in `omniglyph-chatcore-plumbing` and give the pipeline-circuit-breaker fixture a `metadata.executionStages` (both structural changes landed in the base merge) ([#10489](https://github.com/diegosouzapw/OmniRoute/pull/10489))
- fix(cli): use 127.0.0.1 for the readiness health-check poll instead of localhost, avoiding Windows DNS-resolution delays that made a healthy server report as never-ready (#10508)
- **fix(providers):** zed-hosted OAuth now redirects the browser back to the dashboard's own loopback port (auto-completing the login), and the manual paste path accepts Zed's user_id/access_token callback URL instead of erroring with "No authorization code found" ([#10517](https://github.com/diegosouzapw/OmniRoute/pull/10517)) - thanks @phatchau036
- **fix(providers):** allow token-backed web sessions stored with `authType: "cookie"` to refresh their token through the provider update API ([#10518](https://github.com/diegosouzapw/OmniRoute/pull/10518)) — thanks @Zartharas
- **fix(providers):** test token-backed web sessions through their provider validator instead of the OAuth path ([#10519](https://github.com/diegosouzapw/OmniRoute/pull/10519)) — thanks @Zartharas
- **fix(compliance):** redact additional provider API keys from audit-log payloads ([#10521](https://github.com/diegosouzapw/OmniRoute/pull/10521)) — thanks @Zartharas
- fix(providers): register a real Firefly auth probe under both the `firefly` alias and the `adobe-firefly` canonical id, and normalize the provider id before the generic web-cookie fallback, so a Firefly connection stops always reporting "Provider validation not supported" (#10522)
- fix(services): isolate probeBeforeSpawn adoption tests on distinct ports to stop the order-dependent flake (#10523)
- fix(sse): auto-replay a bounded multi-turn trajectory in the DeepSeek Web prompt builder for clients that never send `tools[]`, so agentic clients like Cline stop losing the original task after a couple of turns (#10527)
- **fix(network):** direct (no-proxy) egress now bounds each attempt's response-start window (default 30s, `OMNIROUTE_DIRECT_HEADERS_TIMEOUT_MS`) and retries once on a fresh no-keep-alive socket, so a silently-dropped pooled keep-alive connection can no longer stall direct providers (opencode-go, command-code) until a service restart ([#10214](https://github.com/diegosouzapw/OmniRoute/issues/10214))
- **fix(models):** align Codex GPT-5.6 context limits with the Codex catalog and honor model context overrides when advertising combos ([#10530](https://github.com/diegosouzapw/OmniRoute/issues/10530))
- **fix(deps):** upgrade `@atjsh/llmlingua-2` from 2.0.3 to 2.0.5 and remove `@tensorflow/tfjs` from the LLMLingua SLM stack — 2.0.5 adds official Transformers.js v4 support (peers `@huggingface/transformers` at `^3.5.2 || ^4.0.0`) and 2.0.4+ no longer requires TensorFlow.js, restoring compatibility with OmniRoute's Transformers.js v4 while dropping the largest single contributor to the optional runtime footprint ([#10536](https://github.com/diegosouzapw/OmniRoute/issues/10536))
- **fix(deepseek):** Advertise `none`, `low`, `high`, and `max` for V4 Pro and Flash, derive OpenCode Go effort aliases from base-model metadata, and route those models through native Responses ([#10540](https://github.com/diegosouzapw/OmniRoute/pull/10540)) — thanks @jackjinke
- **fix(a2a):** use a constant-time bearer compare in `/api/a2a/tasks` via `crypto.timingSafeEqual`, matching the `tokensMatch` helper already used in `src/app/a2a/route.ts` and removing the last non-constant secret comparison in the repo ([#10544](https://github.com/diegosouzapw/OmniRoute/pull/10544))
- Preserve portable plaintext reasoning by default across streaming and non-streaming Chat Completions and Responses routes while keeping provider-bound opaque state target-compatible. Direct requests drop incompatible continuation reasoning by default; combos can explicitly skip incompatible targets without mutating the request. Known providers no longer show redundant encrypted-reasoning controls. (#10550, #10959)
- fix(dashboard): show the real model count on the "List Models" endpoint card instead of a permanent "—" (#10553)
- **fix(cli):** ignore the operating system `HOSTNAME` when choosing the server bind address on Linux and macOS, preventing startup failures when the shell hostname differs from `os.hostname()`; use `OMNIROUTE_SERVER_HOST` for explicit non-Windows configuration while preserving the legacy `HOSTNAME` fallback on Windows ([#10557](https://github.com/diegosouzapw/OmniRoute/pull/10557), closes [#10492](https://github.com/diegosouzapw/OmniRoute/issues/10492)) — thanks @redzrush101
- **fix(providers):** OpenCode `x-opencode-session` now derives a stable, conversation-scoped fingerprint via `generateSessionId()` instead of a fresh random UUID per request, so upstream prompt caching can hit across requests in the same conversation; bare `big-pickle`/`*-free` model ids now keep routing to an active opencode-family connection even when its synced catalog is temporarily stale; and bare requests to no-auth catalog providers (e.g. `opencode`) now echo the listing-valid `<alias>/<model>` form in `response.model` so clients validating against `/v1/models` don't warn ([#10571](https://github.com/diegosouzapw/OmniRoute/pull/10571))
- **fix(mcp):** make GitHub skill tools discoverable through `omniroute_tool_search`
- fix(providers): remove 10 retired model ids from the crof seed catalog so /v1/models stops advertising models crof.ai no longer serves (#10577)
- **fix(audio):** when a prefix-matched STT provider has no credentials, retry gateways that list the same nested model id (e.g. `deepgram/nova-3``openrouter/deepgram/nova-3`) and mention those ids in the 400; stop documenting bare `deepgram/nova-3` as the default example ([#10583](https://github.com/diegosouzapw/OmniRoute/issues/10583))
- fix(sse): resolve the short provider-alias prefix (e.g. `el/`) advertised by GET /v1/models for audio speech, transcription and translation model ids (#10586)
- fix(sse): map OpenAI-compat voice names to real ElevenLabs voice_ids in direct TTS (#10589)
- fix(dashboard): route the Playground's ChatTab "Send" through the endpoint actually selected in StudioConfigPane (`search`, `web.fetch`, etc.) instead of always POSTing to `/api/v1/chat/completions`, fixing the false "No active credentials for provider" 404 when testing search-only providers (#10592)
- **fix(providers):** Magnific Mystic is now the canonical provider (`/dashboard/providers/magnific`, `magnific/<model>`). It uses the Magnific API (`api.magnific.com` + `x-magnific-api-key`), dashboard Test Connection validates keys without starting a paid generation, and the old `freepik` slug remains a legacy alias ([#10594](https://github.com/diegosouzapw/OmniRoute/pull/10594))
- **fix(sse):** Include the redacted upstream error body in the per-target COMBO failure log (`Model X failed, trying next`) so operators can triage a 400/500 without reproducing the request ([#10597](https://github.com/diegosouzapw/OmniRoute/issues/10597))
- **fix(xai):** trim Chat Completions `messages` and Responses `input` to xAI's 800-item history cap before dispatch, so long tool loops no longer die on `413 Chat history exceeds the 800-message limit` ([#10601](https://github.com/diegosouzapw/OmniRoute/pull/10601))
- **fix(cli):** derive the machine-id token correctly under plain Node — `await import("node-machine-id")` puts the CJS exports on `.default`, so the destructured `machineIdSync` was `undefined` and the catch blanked the token, sending every management request unauthenticated; `OMNIROUTE_CLI_SALT` rotation is now honored too ([#10612](https://github.com/diegosouzapw/OmniRoute/pull/10612))
- **fix(cli):** `omniroute setup --add-provider --api-key <key>` no longer aborts with "Provider API key is required" — Commander bound the value to the program-level `--api-key` (the OmniRoute server key), leaving the subcommand's own option undefined; `OMNIROUTE_API_KEY` now works as the error message advertised ([#10613](https://github.com/diegosouzapw/OmniRoute/pull/10613))
- fix(dashboard): make /api/models agree with /v1/models on synced-catalog coverage instead of reporting stale models as available (#10615)
- **Combo routing:** await each connection's token limit before reserving quota. The old lookup treated the `Promise` as a connection and dropped `rateLimitOverrides.tpm` ([#10686](https://github.com/diegosouzapw/OmniRoute/pull/10686)).
- fix(guardrails): resolve the public provider alias before querying credentials in the Vision Bridge router, so command-code/opencode (and any alias!=id provider) are no longer reported as "unusable" despite active connections (#10702)
- fix(dashboard): filter the Modality Bridge Vision model picker to vision-capable models, matching the sibling Video/Audio tabs (#10703)
- fix(usage): repair provider-reported input_tokens: 0 on non-trivial requests instead of passing it through unrepaired (#10705)
- fix(cli): distinguish a CLI-probe timeout from a genuinely absent binary in locateCommand, and resolve the Hermes Agent Apply flow's `keyId` server-side instead of writing the `YOUR_OMNIROUTE_API_KEY_HERE` placeholder (#10710, #10711)
- fix(cli): pass --allow-scripts for the runtime's own npm-installed dependencies, so npm 12+'s default install-scripts block no longer silently skips better-sqlite3's native build (#10713)
- fix(db): filter `getProviderMetrics()` to providers with a live `provider_connections` row so a deleted provider stops permanently ghost-haunting the Home "Provider Topology" widget (#10714)
- fix(proxy): keep password-only proxy credentials instead of dropping them when no username is set (#10720)
- **fix(executors):** the Meta AI (muse-spark-web) WebSocket send-message timeout now reports the socket's `readyState` at the moment it fires, so a "Meta AI WS timed out" failure can be told apart as either the connection never opening (`readyState=0`) or opening successfully and then going silent (`readyState=1`) — the exact ambiguity that made #10727 undiagnosable from logs alone (#10727).
- **fix(providers):** copilot-m365-web chat turns no longer surface as `(empty response)` — the type:4 invocation is aligned with the 2026-08 wire shape and now carries its type:1 Metrics follow-up in the same socket write, and the access token pre-flight-refreshes from a stored refresh_token instead of requiring a DevTools re-capture every ~75 minutes ([#10732](https://github.com/diegosouzapw/OmniRoute/pull/10732) — thanks @acc0mplish)
- **fix(catalog):** stop counting `getTokenLimit()`'s generic 128k catch-all as a known combo window, so `/v1/models` advertises the min of sourced member contexts instead of collapsing a 500k combo to 128k ([#10734](https://github.com/diegosouzapw/OmniRoute/issues/10734))
- **fix(search):** name `/v1/search` 502s with provider id and sanitized Node cause code, without hostnames ([#10735](https://github.com/diegosouzapw/OmniRoute/issues/10735))
- **fix(db):** pause call-log rotation and record SQLITE_CORRUPT on `/api/db/health` instead of retrying writes against a malformed pager ([#10736](https://github.com/diegosouzapw/OmniRoute/issues/10736))
- fix(compression): skip the expensive `createCompressionStats()` pass in RTK when no message was actually compressed, matching every sibling stacked engine (#10765)
- **fix(api):** `/api/cache/stats` reported the prompt-cache LRU, which no request path ever writes to — it answered `0 hit / 0 miss, size 0` while the semantic cache served real traffic, and the Health and Usage dashboards rendered that as fact. It now reports the semantic cache's in-memory entries, with the same response shape ([#PRNUM](https://github.com/diegosouzapw/OmniRoute/pull/10769)) — thanks @Poid-ZA, who first fixed this in #9446.
- **fix(logging):** the app log is filterable and readable again. Entries from the tagged logger (`[LEVEL] [TAG] message`) were filed under the level instead of the component, and printf format strings were never applied, so `%s`/`%d` stayed literal with the values trailing behind them unlabelled — including every LiveWS connection line, where the format is deliberate hardening against injected format specifiers ([#PRNUM](https://github.com/diegosouzapw/OmniRoute/pull/10770)).
- **fix(analytics):** Claude Code (`claude`/`cc`) is a flat-rate subscription, so cost analytics reports `$0` for it instead of estimating Anthropic list prices — the metered `anthropic` API keeps its real cost, and budget/quota/routing still estimate as before ([#10774](https://github.com/diegosouzapw/OmniRoute/pull/10774)) — thanks @electrumguy
- fix(db): periodically run `wal_checkpoint(TRUNCATE)` so the SQLite WAL file shrinks on long-running servers (default 6h, override with `OMNIROUTE_WAL_TRUNCATE_INTERVAL_MS`, `0` disables) (#10781)
- fix(sse): replace LiveWS's application-only liveness check with a protocol-level `ws.ping()`/`pong` heartbeat (RFC 6455 §5.5.2) alongside the existing one, so a read-only dashboard subscriber that never sends anything survives the connection timeout — a socket that stops reading frames entirely is still reaped exactly as before (#10782)
- **fix(open-sse):** declare `supportedThinkingEfforts` (`low`/`medium`/`high`/`max`) on Ollama Cloud's `glm-5.1`, `glm-5.2`, `deepseek-v4-pro` and `deepseek-v4-flash` registry entries so the catalog's `appendSyncedEffortVariants()` pass — which only synthesizes selectable `-low`/`-high`/`-max` model ids from an already-populated `capabilities.effort_tiers` — can expose an effort selector for these reasoning-capable models, matching what `gpt-oss:20b`/`gpt-oss:120b` already had (#10788)
- **fix(resilience):** scope the same-account transport retry (#9708) out of emergency-fallback and combo hops — it was retrying the free fallback model and combo targets too, doubling upstream calls and corrupting the terminal error status on those paths.
- **fix(opencode-plugin):** respect log level in provider.models() catalog path so debug/info/warn messages are suppressed when `features.logLevel` is set to `"error"` ([#10798](https://github.com/diegosouzapw/OmniRoute/pull/10798)) — thanks @tientien17
- **fix(providers):** Keep NVIDIA timeout probes and generic Antigravity/AGY HTTP 400 probes from poisoning credential health while preserving explicit Google geo-block handling ([#10799](https://github.com/diegosouzapw/OmniRoute/pull/10799)) — thanks @Zartharas
- fix(db): disambiguate `createProviderConnection()`'s OAuth email dedup by `providerSpecificData.profileArn` in addition to `username`, so adding a second Kiro/AWS profile with the same email creates a new connection instead of silently merging into the first (#10815)
- fix(oauth): stop treating the Kiro profile ARN as an account identity in `findKiroConnectionByIdentity()`, so a second Google/GitHub social login creates a new connection instead of overwriting the first — distinct Builder ID accounts share the same CodeWhisperer profile ARN, and the social token is not a JWT, so no e-mail was available to disambiguate them (#10815)
- **fix(images):** register OpenAI `dall-e-3` in the image registry so unprefixed `dall-e-3` (and `openai/dall-e-3`) route to OpenAI Images instead of Microsoft Designer Web, and so the chat catalog no longer lists `openai/dall-e-3` as a 128k chat model ([#10832](https://github.com/diegosouzapw/OmniRoute/issues/10832))
- **fix(security):** Outbound URL guard now resolves IPv4-mapped IPv6 literals to their embedded address, so `[::ffff:169.254.169.254]` is refused by the unconditional cloud-metadata block like its dotted spelling; `[::]` is refused alongside `0.0.0.0` ([#10843](https://github.com/diegosouzapw/OmniRoute/pull/10843)) — thanks @ntdat812
- fix(config): exclude cookie-auth image bridges (chatgpt-web, gemini-web) from the unprefixed model scan so a bare id never silently binds to an unofficial web bridge (#10848)
- fix(api): POST /v1/search now replies with a named `Unknown search provider: <id>` error (and field-named validation messages) instead of an opaque `Invalid request` for unrecognized or short-alias provider ids like `brave`/`serper` (#10849)
- **fix(api):** alias `GET`/`HEAD` `/readyz` to `/healthz` so Kubernetes readiness probes do not 404 ([#10850](https://github.com/diegosouzapw/OmniRoute/issues/10850))
- Document the conditional management authentication and 401/403 responses for `GET /api/openapi/spec`.
- **fix(i18n):** The "Disabled" status no longer renders as the noun for a person with a disability in Japanese, Spanish, Hindi, Polish, Telugu, Urdu and both Chinese locales — 24 strings now use each catalog's existing wording (ja 無効, es Deshabilitado, hi अक्षम, pl Wyłączone, te నిలిపివేయబడింది, ur غیر فعال, zh-CN 已禁用, zh-TW 已停用) ([#10812](https://github.com/diegosouzapw/OmniRoute/issues/10812), [#10853](https://github.com/diegosouzapw/OmniRoute/pull/10853)) — thanks @ntdat812
- **fix(skills):** Marketplace-installed skills are available to API-key-scoped requests, including existing SkillsMP and skills.sh installs ([#10854](https://github.com/diegosouzapw/OmniRoute/pull/10854)) — thanks @kriptoburak
- **fix(catalog):** `/v1/models` no longer advertises the built-in `auto/*` ids while auto routing is disabled — they were listed but rejected at request time with `Auto routing is disabled` ([#10831](https://github.com/diegosouzapw/OmniRoute/issues/10831), [#10857](https://github.com/diegosouzapw/OmniRoute/pull/10857)) — thanks @ntdat812
- **fix(context):** Base64 file payloads (OpenAI `file` parts, Responses `input_file`, Claude `document` blocks) are budgeted like the Gemini `inlineData` path instead of being counted as prompt text — a ~1MB PDF estimated at 350k tokens and was rejected on the context limit before reaching the provider's document pipeline ([#10840](https://github.com/diegosouzapw/OmniRoute/issues/10840), [#10858](https://github.com/diegosouzapw/OmniRoute/pull/10858)) — thanks @ntdat812
- **fix(mcp):** MCP tool calls that wait on a model provider no longer abort after 10 seconds. `omniRouteFetch` applied a single hardcoded `AbortSignal.timeout(10000)` to every internal hop, and `omniroute_route_request` — which posts to `/v1/chat/completions` and waits on the upstream provider, plus auto-combo candidate probing before a provider is even chosen — passed no signal of its own, so it inherited it. Any route slower than 10s failed from the MCP side while the identical request succeeded through the REST API. `omniroute_web_search` and `omniroute_web_fetch` in the same file already carried an explicit 60s signal, so that value is now shared by all three provider-bound calls instead of being repeated as a literal, while management reads (health, resilience, rate limits, combos, quota, usage) keep their fast-fail 10s budget so a stalled local endpoint still cannot hold a tool call open. Both budgets are overridable through `OMNIROUTE_MCP_FETCH_TIMEOUT_MS` and `OMNIROUTE_MCP_UPSTREAM_TIMEOUT_MS`, replacing the reported workaround of patching the compiled `dist/.build/next/server/chunks/*.js`; a malformed or non-positive override falls back to the default rather than disabling the timeout
- **fix(providers):** importing models with an expired API key now surfaces the credential error instead of reporting "No new models were added". The Import button posts to `/api/providers/{id}/sync-models`, which self-fetches the models route; that route does not fail on an upstream 401 but degrades to a catalog it already has, preferring the cache and using the local catalog only when there is no cache. A provider that imported successfully once therefore has a cache, so an expired key produced `{ source: "cache", warning: "Models probe failed (401) — using cached catalog" }` with HTTP 200 — and the #5460/#5465 degradation guard only recognised the `local_catalog` branch, so model-sync accepted it as a successful discovery, found every cached model already imported, and returned the empty-diff result. Retest does not go through this path, which is why it failed correctly and made the import look like a genuine "nothing to do". The existing rule — a degraded discovery must not be persisted as the synced catalog — is now applied to the branch it missed rather than special-casing 401/403, discriminating on the warning the fallback builder always attaches (an ordinary non-refresh cache hit attaches none, and model-sync always requests `refresh=true`). `isDegradedLocalCatalog` keeps its exact meaning and its existing tests
- fix(api): reject a combo update that removes every model, and store the copilot's combo targets where the router reads them (#10866)
- **fix(proxy):** proxy "Test connection" no longer reports an IPv4-only SOCKS5/SSH proxy as dead. #1255 moved every egress probe from `api.ipify.org` to `api64.ipify.org` so proxies with IPv6 egress could be tested, but `api64` is IPv6-first: a tunnel with no IPv6 route has nothing to connect to, so the probe hung until the caller's deadline and a proxy that was carrying live LLM traffic came back as a failure. Swapping the target to `api4` fixes that case and re-breaks the one #1255 fixed, so the probe now tries the targets in order instead — `api64` first, so a proxy with working IPv6 answers on the first attempt and keeps the exact behaviour #1255 introduced, including which of its addresses is reported (the egress IP is used as an identity to detect accounts of one rotation group sharing an address, so the attempts are sequential rather than raced). The attempts split the budget each call site already enforced, so no probe can take longer than it could before, and each attempt gets its own `AbortController` so exhausting the budget on an unreachable target does not abort the next one. `OMNIROUTE_PROXY_ECHO_URL` pins a single target — including a self-hosted echo — replacing the workaround of rewriting the compiled bundle after every upgrade. The relay branch of the test route still targets `api64` through `x-relay-target`, since that request egresses from the relay worker rather than the operator's tunnel
- fix(cli): warn when a .env line never takes effect, and stop swallowing an unreadable .env (#10870)
- **fix(db):** Remove stale MiMoCode provider configuration, including the legacy `mcode` alias, left after provider retirement while preserving historical usage and call logs ([#10873](https://github.com/diegosouzapw/OmniRoute/pull/10873)) — thanks @Zartharas
- **fix(sse):** `getResetAwareProvider()` and the auto-combo quota lookup in `combo.ts` now canonicalize the provider id via `resolveProviderId()` before calling `getQuotaFetcher()`, so a fetcher registered under a provider's canonical id (e.g. `ollama-cloud`, `codex`) is found for combo targets stored under an alias spelling (e.g. `ollamacloud`, `cx`) instead of silently degrading reset-aware/reset-window/auto quota-aware routing to plain priority ordering (#10877)
- **fix(provider-health):** Keep unsupported 404/405 validation probes neutral so they do not poison stored credential health or scheduler failure state, while still honoring per-connection health-check pacing ([#10878](https://github.com/diegosouzapw/OmniRoute/pull/10878)) — thanks @Zartharas
- **fix(antigravity):** map Gemini 3.7 Flash tier ids (`gemini-3.7-flash-high/medium/low`, bare `gemini-3.7-flash`) to the upstream `gemini-3.7-flash-tiered` model id Google's Cloud Code endpoint expects, and configure per-tier thinking budgets ([#10882](https://github.com/diegosouzapw/OmniRoute/pull/10882)) — thanks @adevwithpurpose
- **fix(memory):** enable agent memory save/update via MCP tools (`memory_save`/`update`/`search`/`delete` builtins with per-provider schemas, `apiKeyId` optional with caller-principal fallback) and gate server-side memory builtin injection to non-stream requests only ([#10887](https://github.com/diegosouzapw/OmniRoute/pull/10887)) — thanks @Egorich-print
- **fix(perplexity-web):** make the built-in-search hint appended to every system message opt-in via `OMNIROUTE_PPLX_SEARCH_HINT` (off by default) — Perplexity's answer engine searches anyway, and the hint leaked into replies as meta-commentary for coding clients ([#10902](https://github.com/diegosouzapw/OmniRoute/pull/10902), extracted from [#8634](https://github.com/diegosouzapw/OmniRoute/pull/8634)) — thanks @danscMax
- **fix(providers):** the loopback readiness gate no longer memorizes a failed probe — the next caller after 30s starts a fresh probe, and a readiness failure is logged once per probe instead of once per caller ([#10903](https://github.com/diegosouzapw/OmniRoute/pull/10903))
- **fix(relay):** the Cloudflare proxy-relay worker now resolves `x-relay-path` through the shared `resolveRelayTarget()` guard instead of concatenating it onto the validated target. PR #4643 and its follow-up applied that guard to the Deno and Vercel workers; the Cloudflare generator, ported separately from upstream `decolua/9router` PR #1360, kept `fetch(targetBase + relayPath)`. Validating `x-relay-target` and then concatenating is not sufficient — the path re-points the request past the host that was just checked, through userinfo (`/x@evil.com`), a backslash (`\evil.com`), or a protocol-relative path (`//evil.com/x`). The guard is embedded verbatim under a literal `const resolveRelayTarget =` binding so the hardcoded call site still resolves when the SWC-minified standalone build mangles the source function's own name (#6149), and the new regression test pins that property for this worker by renaming the embedded function and re-evaluating the emitted source. The auth check and the private/loopback target guard are unchanged
- **fix(build):** the `next` Docker image no longer crashes on boot with `ReferenceError: require is not defined in ES module scope`. The standalone `server.js` is CommonJS, but the `postbuild` colocate step was re-adding `"type":"module"` to the standalone root `package.json` (undoing `assembleStandalone`'s strip) to make its ESM worker bundles load. The `type:module` scope is now written per-worker-directory instead of on the root, so `server.js` stays CommonJS while the workers stay ESM ([#10936](https://github.com/diegosouzapw/OmniRoute/pull/10936), fixes [#10933](https://github.com/diegosouzapw/OmniRoute/issues/10933)) — thanks @arminanton
- fix(cli): always emit limit.output in generated OpenCode config so schema validation passes for metadata-less models (#10940)
- **fix(relay):** the private/loopback guard the three proxy-relay workers embed no longer misses four host spellings, and now lives in one place instead of three byte-identical inline copies. Driving `new URL(target).hostname` the way the workers do, the previous guard allowed `::` (the unspecified address, which reaches a service bound to the IPv6 loopback), `localhost.` (the FQDN root dot defeated the exact match and every `.localhost`/`.local`/`.internal` suffix rule, so `svc.internal.` slipped too), `::127.0.0.1` (the deprecated IPv4-compatible form — only `::ffff:` was checked), and `feb0::1` (link-local is `fe80::/10`, spanning `fe80``febf`, but only the literal `fe80:` spelling matched). The policy moved to `src/lib/proxyRelay/privateHostname.ts` and is embedded verbatim via `Function#toString` under a literal const name, the same mechanism `resolveRelayTarget` already uses for these workers, so a minified standalone build cannot break the call site (#6149). Nothing previously blocked is now allowed. Severity is low — reaching a worker needs the `x-relay-auth` secret and these are edge runtimes where loopback has nothing listening — but the suffix-rule bypass held regardless of runtime
- **Account rotation:** make `fallbackStrategy: "least-used"` actually rotate. The strategy sorts on `lastUsedAt` but never wrote it — only the round-robin branch committed — so on a pool where every `last_used_at` was still `NULL` the tie-break fell through to `priority` and returned the same connection on every dispatch ([#10945](https://github.com/diegosouzapw/OmniRoute/issues/10945)).
- **Desktop auto-update (Windows):** stop the in-app updater 404ing on every release. NSIS used electron-builder's default artifact name, whose spaces GitHub rewrites to `.` on upload while `latest.yml` keeps `-`, so the manifest pointed at `OmniRoute-Setup-X.Y.Z.exe` while the published asset was `OmniRoute.Setup.X.Y.Z.exe`. The name is now set explicitly to the dot form the asset already has, so nothing published changes name ([#10947](https://github.com/diegosouzapw/OmniRoute/issues/10947)).
- Preserve explicit plaintext reasoning when a Responses reasoning item also carries opaque provider state (rare OpenCode Go `deepseek-v4-flash` responses). Mixed plaintext + opaque input is projected onto the target transport: plaintext targets keep portable text, opaque targets keep provider state. Opaque-only reasoning is dropped when the selected target cannot replay it, allowing cross-model conversations to continue. (#10949, #10959)
- **fix(catalog):** preserve provider-declared reasoning effort tiers instead of replacing them with generic defaults ([#10953](https://github.com/diegosouzapw/OmniRoute/pull/10953)) — thanks @xz-dev
- fix(cli): combo create accepts --models and no longer creates empty combos (#10954)
- fix(cli): resolve $ref path params and add PATCH combos requestBody in generated API commands (#10955)
- fix(sse): default single-target incompatible reasoning to drop for agentic replay — single-target requests to opaque reasoning targets now gracefully strip incompatible plaintext reasoning history instead of returning HTTP 400, matching combo default behavior while preserving operator and per-request overrides ([#10959](https://github.com/diegosouzapw/OmniRoute/issues/10959))
- fix(sse): combo diagnostics no longer truncate `exhausted_connection` entries to a hardcoded `provider: "unknown"` with the provider prefix eaten by an 8-char slice — the real provider id is preserved and only the connection id is truncated (#10967)
- fix(sse): combo terminal failures caused entirely by quota/account-balance exhaustion (including a durable HTTP 403 `insufficient_quota` / `AUTHZ_INSUFFICIENT_BALANCE`) now stamp a stable `quota_exhausted` diagnostics reason with a `switch-combo` recovery hint instead of the misleading default `retry` action (#10966)
- **fix(search):** skip catalog-default SearXNG `http://localhost:8888/search` so Docker/K8s search does not ECONNREFUSED then 502 into the next provider ([#10976](https://github.com/diegosouzapw/OmniRoute/issues/10976))
- fix(command-code): surface reasoning-only output as content when a model emits no text-delta (#10986)
- **fix(ci):** clear inherited `release/v3.8.50` quality-gate reds on the X Search PR: drop the stale `copilot-m365-web.ts:330` public-creds allowlist, document six missing env vars, register four covering Stryker tap tests, prune leftover ESLint suppressions, replace the phantom `@/lib/db/connections` Utilization import with `getProviderConnectionById`, and fix open-sse/dashboard typecheck regressions in freebuff, browser-backed chat, auth, health matrix, and Monaco ([#10988](https://github.com/diegosouzapw/OmniRoute/pull/10988)).
- **fix(ci):** clear remaining `release/v3.8.50` unit-shard reds on the X Search PR: pin `onnxruntime-node` to the transformers 1.24.3 copy, rebaseline OpenAPI coverage, sync goldens/i18n, honor eye-hidden no-auth models across provider aliases, await rejected-request call-log writes, absorb catalog event-loop shard contention in #9147, and align inherited tests with advisory context estimates, #10501 combo terminal-status aggregation, and current catalog/auth behavior ([#10988](https://github.com/diegosouzapw/OmniRoute/pull/10988)).
- **Static model catalog for v0-vercel-web:** seed a static catalog for the v0-vercel-web web-cookie provider (v0-1.0-md, v0-1.5-lg, v0-1.5-md) so its dashboard "Available Models" / "Import from /models" UI serves a usable list instead of falling through to the route's 400 "does not support models listing" ([#10990](https://github.com/diegosouzapw/OmniRoute/issues/10990)).
- fix(providers): mark the blackbox provider deprecated — api.blackbox.ai returns HTTP 404 on every path variant (sweep 2026-08-21), so the public inference surface is dead and the catalog entry now carries a deprecation notice. ([#10997](https://github.com/diegosouzapw/OmniRoute/issues/10997))
- fix(providers): validate Dify keys against its native /v1/chat-messages endpoint (#11002)
- **fix(accounts):** `markCooldown` now carries the failure origin (`transient` vs `terminal`) — transient 429/network only cools down, repeated terminal failures evict and are skipped by `pickAccount` until a success or operator clear ([#11008](https://github.com/diegosouzapw/OmniRoute/pull/11008)) — thanks @maxmad64bis
- **fix(providers):** route terminal `testStatus` writes (`banned`, `deactivated`, `credits_exhausted`) through a single origin-aware passage — probe failures are recorded but never deactivate the connection ([#11009](https://github.com/diegosouzapw/OmniRoute/pull/11009)) — thanks @maxmad64bis
- **fix(codex):** drop non-standard `codex.*` SSE events by default so OpenAI SDK / Codex CLI `/v1/responses` clients are not 502'd by `event: codex.rate_limits` ([#11014](https://github.com/diegosouzapw/OmniRoute/issues/11014)) — thanks @RaviTharuma
- **fix(resilience):** count heavyweight `/v1` admission leases in the SIGTERM drain and send `Retry-After` on shutdown 503s so Recreate no longer looks like an empty 502 ([#11015](https://github.com/diegosouzapw/OmniRoute/issues/11015)) — thanks @RaviTharuma
- **fix(startup):** log `Credential health scheduler disabled` when `OMNIROUTE_DISABLE_CREDENTIAL_HEALTH_CHECK` is set instead of lying with `started` ([#11016](https://github.com/diegosouzapw/OmniRoute/issues/11016)) — thanks @RaviTharuma
- **docs(api-keys):** document that unset `DEFAULT_RATE_LIMIT_PER_DAY` is unlimited (#2289), not a hidden 1000/day cap ([#11017](https://github.com/diegosouzapw/OmniRoute/issues/11017)) — thanks @RaviTharuma
- **fix(webhooks):** remove 3 declared-but-never-emitted events (`provider.error`, `provider.recovered`, `combo.switched`) from `WebhookEvent` — catalog now `request.completed | request.failed | quota.exceeded | test.ping`; `POST /api/webhooks` and `PUT /api/webhooks/[id]` reject ghost values with 400; OpenAPI webhook description updated across 43 locales ([11050](https://github.com/diegosouzapw/OmniRoute/pull/11050))
- fix(providers): filter Perplexity model import to the Sonar family so Agent-API catalog ids stop surfacing as routable chat models (#11060)
- **fix(claude):** restore canonical tool names (`bash``Bash`, `croncreate``CronCreate`) on non-streaming OpenAI→Claude conversion and through identity-echo alias maps, so Claude Code stops rejecting tool calls with "No such tool available" ([#11085](https://github.com/diegosouzapw/OmniRoute/pull/11085)) — thanks @linhdmn
- **fix(resilience):** filter chat connection selection by each connection's *synced* model inventory on multi-host self-hosted providers (`ollama-local`, `lm-studio`, `vllm`, …), so a request for a model only one host advertises is pinned to that host instead of failing over onto a host that never had it ([#11089](https://github.com/diegosouzapw/OmniRoute/issues/11089))
- fix(install): make the ONNX dependency chain optional so Termux/Android installs succeed again (#11095)
- **fix(providers):** Reject silent validation degradation on provider connection patch — unknown `rateLimitOverrides` keys (e.g. a typo'd `tpm`) and empty/non-numeric values now return `400` with the rejected key list instead of being silently dropped ([#11101](https://github.com/diegosouzapw/OmniRoute/pull/11101))
- **Autopilot suggestion counter:** the combo health autopilot summary now reports `suggestionCount` (the real number of suggested actions across all issues) instead of conflating it with link counts, while keeping `actionableCount` as a deprecated alias for backward compatibility. The `run_combo_test` action now links to the dashboard with the combo id (`/dashboard/combos?test=<comboId>`) rather than the read-only API route, so operators can actually trigger a test from the UI ([#11102](https://github.com/diegosouzapw/OmniRoute/pull/11102)).
- **Config audit persistence:** persist the configuration audit trail to SQLite (`config_audit_log`) instead of an in-memory buffer capped at 1000 volatile entries, and bound its growth with `cleanupConfigAudit()` driven by the `retention.configAudit` setting (default 30 days), wired into `runAutoCleanup` ([#11103](https://github.com/diegosouzapw/OmniRoute/pull/11103)).
- fix(sse): resume mid-stream recovery after a _completed_ tool call — `finish_reason: "tool_calls"` is now tracked per-call instead of as a general terminal marker, so truncation of trailing prose after a fully-delivered tool call is recoverable while in-flight calls stay blocked ([#11109](https://github.com/diegosouzapw/OmniRoute/pull/11109))
- **fix(providers):** `reasoning_effort` now learns the accepted values from a provider's own 400/422 response and clamps to the highest one instead of forwarding an unsupported `xhigh`/`max` (or a hardcoded `"high"` fallback) — fixes custom OpenAI-compatible connections and registered providers with no reasoning metadata ([#11116](https://github.com/diegosouzapw/OmniRoute/pull/11116)) — thanks @maxmad64bis
- **fix(sse):** parallel `function_call` items in a Responses API stream (e.g. several tool calls dispatched in the same turn) now each get a stable, distinct `index`/`id` when translated to Chat Completions streaming deltas, instead of colliding on index 0 and tripping strict stream parsers with `Expected 'id' to be a string.` ([#11144](https://github.com/diegosouzapw/OmniRoute/pull/11144))
- **fix(analytics):** `opencode-go` is now classified as a flat-rate subscription, so cost analytics shows $0 for it instead of billing every call at the underlying models metered rate — it resells GLM, Kimi, Grok, DeepSeek, MiniMax, Qwen and GPT-5.x under one flat monthly fee, which made the overstatement large rather than marginal ([#11149](https://github.com/diegosouzapw/OmniRoute/pull/11149)) — thanks @electrumguy
- fix(dashboard): keep `open-sse/config/providerRegistry.ts` free of `node:net` so the provider detail client bundle builds again — the host classification moved to a platform-free `src/shared/network/privateHost.ts` with a pure-JS `isIP` equivalent, leaving the #11122 routing behaviour unchanged (#11154)
- **Combo create:** creating a routing combo without any model is now refused (`400`) — the CLI requires `--models`/`--model` on `combo create`, matching the dashboard which already rejected empty combos.
- **fix(resilience):** a missing-model `404` on a provider that declares `passthroughModels: true` in the shared registry (novita, uncloseai, orcarouter and 37 others) now locks out only that model instead of cooling the entire connection — `hasPerModelQuota()` previously read only the open-sse registry and the local/self-hosted families ([#11165](https://github.com/diegosouzapw/OmniRoute/pull/11165)) — thanks @yourspraveen
- **fix(routing):** a custom `openai-compatible-*` / `anthropic-compatible-*` connection pointing at a keyless self-hosted backend (llama.cpp, Ollama, vLLM started without an API key) now stays in the `auto/*` candidate pool instead of being silently dropped by the credential gate — for those IDs "no credential" is the normal configuration, not an unconfigured connection ([#11180](https://github.com/diegosouzapw/OmniRoute/pull/11180)) — thanks @marcs7
- **fix(routing):** the Routing tab's "last known good provider" toggle now actually takes effect — `lkgpEnabled` was persisted and the `lkgp` strategy guarded on it, but the setting was never forwarded into the `RoutingContext` built in `resolveAutoStrategyOrder()`, so `context.lkgpEnabled` was always `undefined` and the off-switch was unreachable ([#11181](https://github.com/diegosouzapw/OmniRoute/issues/11181))
- **fix(ollama):** Ollama Local models are no longer flattened to `chat` at sync time — the synced store persists every advertised capability and chat filtering moves to read time, so `/v1/embeddings` and `/v1/images/generations` stop rejecting models the daemon reports as capable ([#11271](https://github.com/diegosouzapw/OmniRoute/pull/11271)) — thanks @yourspraveen
- **fix(providers):** Antigravity OAuth marks connects with no Cloud Code projectId as degraded instead of a false "Connected"; BYOP detection at connect time, auto-disable of confirmed-missing accounts, and selection-side rotation ([#11284](https://github.com/diegosouzapw/OmniRoute/issues/11284))
- **fix(translator):** preserve omitted OpenCode `subagent.sessionID` values — optional default-less plain strings now use the Responses `null = omit` sentinel and are stripped before the client sees the tool call, so Codex/Responses no longer invent filler session IDs ([#11297](https://github.com/diegosouzapw/OmniRoute/pull/11297)) — thanks @ofonseca-pyming
- **fix(db):** group model patterns escape regex metacharacters, so `gpt-4.1*` no longer matches `gpt-4o1-preview` and a pattern like `gpt-4(*` no longer throws `SyntaxError` out of the completion and `/v1/models` paths ([#11311](https://github.com/diegosouzapw/OmniRoute/pull/11311))
- **fix(db):** the upstream proxy URL check judges the host by address instead of by spelling, so `http://[::ffff:169.254.169.254]`, `[::ffff:10.0.0.5]`, ULA/link-local and CGNAT targets are refused like their dotted equivalents ([#11319](https://github.com/diegosouzapw/OmniRoute/pull/11319))
- **fix(i18n):** three `pt` strings had dropped their placeholders — the cache tile's subtitle repeated its own label instead of showing `{total}` — and a unit test now enforces placeholder parity with `en` across all locales ([#11325](https://github.com/diegosouzapw/OmniRoute/pull/11325))
- **fix(kie):** map the remaining `google-imagen/*` KIE Market catalog ids (`nano-banana`, `nano-banana-pro`, `nano-banana-edit`) to their real, KIE-documented upstream `model` values — `#11225`'s fix only covered `nano-banana-2` ([#11326](https://github.com/diegosouzapw/OmniRoute/pull/11326)).
- **fix(security):** `proxy-authorization` and `proxy-authenticate` are refused as upstream/custom headers, so a proxy credential is no longer forwarded to the model provider — the canonical denylist now matches the RFC 7230 §6.1 set the rest of the codebase already strips ([#11328](https://github.com/diegosouzapw/OmniRoute/pull/11328))
- **fix(video-bridge):** fall back to the deterministic active-window midpoint when a one-frame scene-aware budget cannot preserve both timeline ends; a real FFmpeg fixture matrix now covers rapid cuts, gradual changes, static and short clips, and detector failure ([#11344](https://github.com/diegosouzapw/OmniRoute/pull/11344)).
- **fix(translator):** Codex Responses tool calls translated for Claude clients no longer emit a duplicate `tool_use` block with the same ID and an empty name, preventing Claude Code from terminating with `No such tool available` ([#11347](https://github.com/diegosouzapw/OmniRoute/pull/11347))
- **fix(video-bridge):** burn high-contrast timestamps into every bounded contact-sheet cell and add a real-model A/B harness whose promotion verdict stays `HOLD` until token, latency, and quality evidence is actually executed ([#11350](https://github.com/diegosouzapw/OmniRoute/pull/11350))
- **fix(video):** fingerprint protected Video Bridge bytes, coalesce concurrent work, and fail open when the bounded TTL/LRU result cache is unavailable or corrupt ([#11362](https://github.com/diegosouzapw/OmniRoute/pull/11362))
- **fix(catalog):** keep large `/v1/models` builds responsive by reusing the build-local capability snapshot throughout enrichment and Auto-Combo preparation, yielding cooperatively while constructing virtual candidate pools, and avoiding unrelated synchronous database diagnostics on the cache-TTL read path ([#11367](https://github.com/diegosouzapw/OmniRoute/pull/11367))
- **fix(video):** apply the caption-frame cap after bounded visual deduplication, preserve first/final candidates plus small high-contrast motion and text changes, and version the dedup policy in result-cache identity ([#11382](https://github.com/diegosouzapw/OmniRoute/pull/11382)).
- **Live dashboard:** honour the WebSocket port reported by `/api/v1/ws?handshake=1` instead of the port compiled into the bundle, so a `LIVE_WS_PORT` override reaches prebuilt Docker/npm images and Combo Studio Live connects behind a reverse proxy ([#11331](https://github.com/diegosouzapw/OmniRoute/issues/11331)).
- **fix(dashboard):** Model Database sync interval slider ticks now match the thumb position — checkpoint-space slider with magnetic snap on release ([#11394](https://github.com/diegosouzapw/OmniRoute/pull/11394)) — thanks @An0nym0us92
- **fix(api-manager):** Allowed Combos can now be restricted to zero entries: **All** is stored explicitly as `combo/*`, while **Restrict** with no selection saves an empty allowlist that denies Combo routes without blocking direct models. Existing keys are migrated to preserve their previous allow-all behavior.
- fix(build): tolerate a same-realpath symlink or stale-typed dest in the standalone bundle assembler, fixing non-deterministic `ERR_FS_CP_EINVAL`/`ERR_FS_CP_DIR_TO_NON_DIR` crashes under heavy concurrent build I/O
- **fix(auto):** rate-limit `auto/<family> matched no connected models` warnings to once per minute per label (`open-sse/services/autoCombo/virtualFactory.ts`)
- fix(cli): drop the orphaned `resolveOpencodeConfigDir` re-export from `cliRuntime` — it lost its last consumer in #10246 and diverged from the canonical resolver by one directory level (#9985)
- fix(ci): make `Build (advisory)` produce a signal again — pinned to a hosted runner with the swap/heap provisioning `Fast Production Build` proves sufficient, and scoped to fork PRs, which are the only ones `build.yml` cannot cover (72 of the last 100 PRs into `release/**`)
- **fix(api):** hash API keys in the `/v1/models` catalog cache Map key so heap dumps cannot leak bearer tokens (`src/app/api/v1/models/catalogCache.ts`)
- **fix(providers):** register live OpenRouter Gemini Embedding 2 ids (`google/gemini-embedding-2` and `google/gemini-embedding-2-preview`, 3072-d) in the curated embeddings catalog so `GET /v1/models` and `GET /v1/embeddings` list the ids that already serve — thanks @RaviTharuma
- **fix(translator):** merge consecutive same-role contents in direct Claude to Gemini request translation to prevent upstream HTTP 400 errors
- **fix(cli):** `omniroute update` now finds npm on Windows. It called `execFile("npm", …)` with no shell, and on Node ≥ 24 a `.cmd` wrapper cannot be spawned that way (nodejs/node#52554) — while a bare `npm` can also resolve to an extensionless shim `CreateProcess` refuses. The result was `✖ Could not check latest version. Is npm available?` in a terminal where `npm view omniroute version` worked fine, so the updater was unusable on Windows even though nothing was wrong with the install. This is the same class as #5379/#5542, which fixed the server-side calls; the CLI entry points were missed because they are plain `.mjs` and cannot import the TypeScript helper. `bin/cli/npm-exec.mjs` now states the same rule for them: `npm.cmd` plus a shell on win32, no shell anywhere else. Both npm lookups in `update.mjs` (version and changelog) pass a literal argv array, so enabling the shell cannot splice a runtime value into the command line — a test asserts that and fails if a future edit interpolates one. (#11335)
- **fix(cline):** Preserve client-supplied Cline task IDs and omit the header when clients provide none, preventing request-scoped proxy IDs from being reported as tasks.
- Hardened the Codex app-server transport after the post-merge security review of #11205: approval prompts from the app-server (its own command/file/permission execution — not the harness tool passthrough) are now auto-denied by default, with opt-in auto-approval via `providerSpecificData.codexAppServerAutoApprove` / `OMNIROUTE_CODEX_APPSERVER_AUTO_APPROVE`; the default codex sandbox changed from `danger-full-access` to `workspace-write` (override per connection or env); env-sourced capability tokens are now only sent to env-sourced URLs or operator-local hosts (loopback/RFC1918/link-local/ULA/localhost/single-label LAN names/*.local/*.ts.net/*.internal), so a connection's providerSpecificData URL can no longer exfiltrate the operator's env token; and the `/readyz` health probe no longer follows redirects while carrying the bearer token.
- fix(codex): prefer `max_context_window` over the `context_window` pricing tier as the usable input limit in discovery, and raise the static Codex OAuth catalog to the same usable window so the conservative discovery merge no longer caps live values at the 272K pricing tier
- **fix(catalog):** derive combo reasoning-effort tiers from the exact runtime-selectable connection scope, intersecting dynamic, pinned, allowlisted, and compatible provider-node evidence while failing closed on unknown capabilities.
- fix(combo): evict in-memory session-stickiness bindings when a combo disables stickiness, so stale pins stop overriding the declared priority order until TTL/restart
- fix(combo): resolve effort-suffixed command-code variants (e.g. `deepseek-v4-flash-max`) to their base model for capability lookups, so tool-bearing combo requests keep the declared priority order instead of reordering behind models with confirmed capabilities
- **fix(db):** the `compression_run_telemetry` retention sweep now actually deletes expired rows. Its cutoff was computed in epoch seconds while the column stores epoch milliseconds, so `WHERE timestamp < cutoff` never matched and the table added by #6848 to bound `storage.sqlite` growth was unbounded in practice. Same unit mismatch as #9625, which corrected the sibling `domain_cost_history` sweep and missed this call site
- **fix(compression):** use `pathToFileURL` in `compressionWorkerPool` so bundlers (Webpack / Turbopack) do not attempt static asset resolution of missing `compressionWorker.js` during build
- **fix(db):** database settings API no longer returns HTTP 500 on SQLite builds compiled without the optional `dbstat` virtual table (sql.js/WASM); per-table sizes degrade to 0 instead of failing the whole stats call
- fix(discovery): parse upstream reasoning tiers nested under metadata.reasoning.supported_efforts (neuralwatt /v1/models shape) so synced openai-compatible models advertise effort aliases
- **fix(ops):** Docker HEALTHCHECK probes lightweight `/healthz` instead of `/api/monitoring/health` so a busy event loop does not mark the container Unhealthy (`scripts/dev/healthcheck.mjs`)
- **fix(api):** `/v1/embeddings` 400s for native `gemini-embedding-2` now name the working OpenRouter ids (`openrouter/google/gemini-embedding-2` and the preview alias) instead of only `No credentials for embedding provider: gemini` — thanks @RaviTharuma
- **fix(sse):** keep Codex/Anthropic quota headers under the upstream forwarding budget; drop `x-codex-turn-state` and raise the 768-byte cap (`open-sse/handlers/chatCore/responseHeaders.ts`)
- **fix(usage):** z.ai/GLM coding-plan subscription keys now render their quota cards again, with absolute credits. Z.ai's `/api/monitor/usage/quota/limit` switched these keys from `TOKENS_LIMIT` to `CREDIT_LIMIT` rows (same `unit`/`number` semantics: unit=3/number=5 → 5-hour window, unit=6/number=1 → weekly), and the parser only matched `TOKENS_LIMIT`/`TIME_LIMIT`, so both rows were dropped and the subscription card rendered empty. `CREDIT_LIMIT` is now accepted alongside `TOKENS_LIMIT`, and when the row carries absolute credit fields (`usage`/`currentValue`/`remaining`) they are preferred over the percent-only scale, so the card shows `3341 / 28000` like z.ai's own dashboard instead of `11 / 100`
- **fix(auth):** a connection's `lastError` now names the real upstream failure instead of the bare string `Provider error`. `markAccountUnavailable` kept the reason only when it was already a string, so every other shape collapsed to that literal — and the shape that matters most is not a string: a failed `fetch` arrives as `TypeError: fetch failed` with the actionable part on `error.cause.code`, which means a wrong port, a firewall, a DNS failure and a blocked proxy all looked identical in the dashboard and in the console line. `describeUpstreamFailure` (in `src/shared/utils/upstreamError.ts`, reusing the `extractErrorMessage` that already parsed provider bodies) reads Error messages and appends the transport code when the message does not already carry it, reads the usual provider JSON shapes (`error.message`, `message`, string `error`, `detail`, `errors[]`), and falls back to the code alone before giving up. It never serializes the error object wholesale, so a request body or header attached to an error cannot leak into the stored reason — pinned by a test.
- **fix(live-ws):** the Live dashboard socket can now be pointed at a reverse proxy without rebuilding the image. `NEXT_PUBLIC_*` is inlined at BUILD time, so a prebuilt Docker or npm image never carries an operator's `NEXT_PUBLIC_LIVE_WS_PUBLIC_URL` — which is exactly why the browser discovers the socket through `/api/v1/ws?handshake=1` instead. The server side of that handshake, however, read only the `NEXT_PUBLIC_`-prefixed name, so it had nothing to echo: behind Traefik the dashboard kept dialling `wss://<host>:20132/live-ws` and sat on "Live disabled — WebSocket disconnected. Showing last known state." `LIVE_WS_PUBLIC_URL` is now read at runtime alongside the existing `LIVE_WS_HOST` / `LIVE_WS_PORT`, and the prefixed name stays supported as the fallback, so deployments that already set it are unaffected. Only `ws://` and `wss://` values are accepted, matching the guard the client already applies. (#11331)
- **fix(sse):** MiniMax music models now generate audio instead of failing with `Unsupported music format: minimax-music` — the provider entry was registered in the music registry (and advertised by `/v1/models`), but `handleMusicGeneration` had no branch for its format, so every `minimax/*` music request fell through the dispatch chain to a 400. Adds the missing dispatch: a single synchronous POST with the `base_resp` envelope check (a non-zero `status_code` arrives on HTTP 200 too), `data.status` handling (an unfinished generation is reported instead of polled — the operation has no task id and no query endpoint), `url` and `hex` output formats (hex normalized to base64), `mp3`/`wav`/`pcm` containers via `audio_setting`, and the regional endpoint through the per-connection base-URL override, which is also the only host that accepts `aigc_watermark`. The registry entry gains the generation and cover model ids it was missing and drops a query URL that does not exist for this operation. Regression guard: `tests/unit/minimax-music-generation.test.ts` (9 tests).
- **fix(models):** honor `MODELS_DEV_SYNC_ENABLED=0` as a hard kill switch over the dashboard setting so a wedged `/healthz` / UI can be recovered without HTTP (`src/lib/modelsDevSync.ts`)
- **fix(providers):** when `OPENCODE_SYNTHESIZE_CLI_HEADERS=true`, a non-CLI client User-Agent (e.g. `curl/8.5.0`, SDKs) on opencode-go/opencode-zen/opencode-free requests is now REPLACED with the synthesized `opencode-cli/1.0.0` instead of being honored — opencode.ai's free tier (`/zen/v1`) returns `FreeUsageLimitError` 429 for generic client UAs egressing from datacenter IPs, which made the #5997 CLI-identity synthesis ineffective for non-CLI clients. Client UAs already matching `opencode-cli/…` are preserved (the real CLI's versioned identity stays intact); all other client-supplied `x-opencode-*` headers keep client-wins. Regression guard: `tests/unit/opencode-cli-headers-synthesis-5997.test.ts` (7, incl. non-CLI UA replaced + CLI UA preserved). (#5997 follow-up)
- **OpenCode config merge:** stop `mergeOpenCodeConfig` splaying a malformed `provider` block into index keys. The root was already guarded against a non-object; the `provider` branch it spreads one level down was not, so an existing `"provider": ["a", "b"]` merged to `{"0": "a", "1": "b", …}`. Its sibling `mergeOpenCodeConfigText` already refuses the same input.
- **fix(models):** a model synced from a provider's own `/models` discovery is now enforced at its real context window immediately, instead of waiting up to 24h for the Feature 5004 reconciler's next tick. The request-time token-limit chain resolves the window from `auto:discovery` overrides, which previously were only written at startup and on a 24h interval — so any model synced mid-cycle (models.dev not indexing it yet, no static registry entry) fell through to the provider's static `defaultContextLength` (128K for OpenRouter) while `/v1/models` simultaneously advertised the real window from the same discovery data. Measured: `openrouter/stealth/ox-alpha` advertised `context_length: 1048576` but rejected requests over 128K with `context_length_exceeded` for a full day after its sync. The reconcile now also runs opportunistically (debounced, fire-and-forget) right after a synced catalog write changes. Companion fix: discovery now captures the vendor-declared `reasoning.default_effort` (e.g. OpenRouter `stealth/ox-alpha` declares `max`, normalized to `xhigh`) as `defaultThinkingEffort`, and the OpenAI dispatch path injects it when a request carries no reasoning field of any shape — the lowest-priority default behind a `-{effort}` suffix alias and a static `ModelSpec.defaultReasoningEffort` — so a reasoning model that returns an empty response without an explicit effort gets the vendor default instead of `upstream_empty_response`.
- **fix(providers):** Claude Code / CC-protocol-compatible clients sending `cache_control` with no `ttl` on the native Claude OAuth path (`claude`/`cc`) now default to the 1h extended cache TTL instead of silently falling back to Anthropic's 5-minute default, even though the 1h beta is always negotiated on this path — thanks @jeff-alves
- **fix(electron):** desktop window stays hidden on Windows because the embedded Next.js server binds to the machine hostname instead of loopback ([#PENDING](https://github.com/diegosouzapw/OmniRoute/pull/PENDING))
- **fix(executors):** OpencodeExecutor rotates (or retries once on a single-account direct path) on upstream 400 empty-body rejections — malformed completion envelopes with no error field were propagated as success and killed client sessions. Bounded +1 attempt per request; body reads are conditioned on status 400 so successful/streaming responses are never buffered. 400s carrying an error field keep propagating immediately.
- **fix(cli):** recognize native `opencode.jsonc` files in OpenCode detection, generated-provider setup, and dashboard save/apply flows; preserve unrelated JSONC comments and provider settings, write updates back to the selected file, and refuse to overwrite invalid config ([#10227](https://github.com/diegosouzapw/OmniRoute/issues/10227)) — thanks @tito13kfm
- fix(i18n): complete Vietnamese translations for recently added UI strings (#9985)
- fix(api): repair broken `@/lib/db/connections` import in the usage utilization route that failed the production build (#10939 follow-up)
- chore(docs): regenerate PROVIDER_REFERENCE and refresh README diagram SVGs to the real provider count (347)
- chore(lint): prune ESLint suppressions orphaned on the release branch
- **fix(build):** repair the broken Turbopack production build, the red lint gate and a runtime crash on `release/v3.8.50`. Six independent module-level defects, each from a different PR, had accumulated because the `Build` CI job is advisory rather than blocking: a lost closing brace in `modelSelectModalHelpers.ts` that swallowed `PROVIDER_TEST_CHUNK_SIZE` into a function body (#9011); `handleFalVideoGeneration` imported twice in `videoGeneration.ts` after the provider-neutral Fal module superseded the standalone handler (#9982 over #9969); `catalog.ts` still re-exporting and calling the injectable stale-while-revalidate policy that #9199 deliberately replaced with a fixed 30 s bound when it landed on top of #8728 — the consumer and the #8728 test suite were never realigned; two dangling statements left in `catalogCache.ts::scheduleBackgroundRefresh` referencing undeclared `inFlight`/`promise`, which made **every** stale-while-revalidate read throw a `ReferenceError` at runtime (a defect the build never caught, surfaced here by the realigned test); a generated wasm-bindgen sidecar URL in `tinycmsSigner.ts` that Turbopack resolves at build time even though the WASM module ships inlined as base64 (#8736/#10087); `conolDiscovery.ts` importing `getProviderOutboundGuard` from `outboundUrlGuard` instead of the sibling `outboundUrlGuardPolicy` module that actually exports it (#8974) — fixed on the consumer side, since re-exporting it would put a `@/`-aliased import into the module the packaged CLI loads without a tsconfig (#7682); and an unbalanced brace in `tests/unit/db-adapters/driverFactory.test.ts` where a new case was inserted between the preceding test's `finally` block and its `});`, so the whole file stopped parsing and the SQLite driver-cascade coverage silently stopped running since 2026-08-11 (#9173).
- **fix(security):** harden three secret-leak paths surfaced by an audit of the error/log surface. (1) `upstreamErrorPassthrough` relays an upstream provider's 4xx body verbatim to Claude-Code-format clients (the capability-recovery contract needs the exact wording); it now refuses passthrough when the body actually carries a credential pattern (`Bearer`/`Basic` token, `sk-…`, or an `api_key`/`token`/`authorization`/`cookie`/`secret` assignment) so a provider that echoes the offending request can't relay a key to the client, falling back to the sanitized error path. The credential regex is bounded (ReDoS-safe, verified linear at 60k chars). (2) The OCR and moderations handlers no longer forward an upstream error body byte-for-byte; they run it through the (now exported) structure-preserving `redactSensitiveErrorText` first. (3) `protectPayloadForLog`'s sensitive-key set gains `cookie`/`storageState`/`runtimeKey`/`capability` so web-impersonation credentials (Meta AI `ecto_1_sess`, chatgpt-web `storageState`) that land in a request/response body field are redacted before the call-log artifact is written to disk. No behavior change for secret-free error bodies; the Claude Code verbatim-wording contract is preserved.
- **fix(db):** the sql.js fallback now publishes the database atomically — temp file in the same directory, `fsync`, then `rename()` — instead of rewriting it in place with `writeFileSync`. sql.js has no incremental write path, so every save rewrote the whole image through an `O_TRUNC` open: for the duration of the write the on-disk database was 0 bytes and then partial, a window that scales with database size and recurs on every save. Unlike better-sqlite3 / node:sqlite, that window is not covered by SQLite's locking protocol, so it was visible to every OTHER process reading the same file (a backup job, a metrics exporter, an operator running `sqlite3`), which got `SQLITE_CORRUPT` — "database disk image is malformed" — while `PRAGMA integrity_check` passed moments later. It also closes a total-loss window: a crash mid-write used to leave the real database truncated, and now only leaves a stale temp file
<!-- reconciliation pass 2 (Phase 0a.1): commits that landed in the cycle without a bullet -->
- **fix(sse):** streaming-integrity cluster — a split `<think` open tag can no longer leak into content ([#10441](https://github.com/diegosouzapw/OmniRoute/issues/10441) — thanks @geek007git); a low-overlap stream-recovery continuation is rejected instead of being concatenated raw ([#11152](https://github.com/diegosouzapw/OmniRoute/issues/11152)) and recovery resumes after a clean stop that produced only reasoning ([#11151](https://github.com/diegosouzapw/OmniRoute/issues/11151)) — thanks @maxmad64bis; OpenAI streams that close with content but no terminal marker are flagged ([#10475](https://github.com/diegosouzapw/OmniRoute/issues/10475) — thanks @HouMinXi); a bare upstream close is surfaced to Responses clients as `response.failed` ([#10980](https://github.com/diegosouzapw/OmniRoute/issues/10980) — thanks @linhdmn); and a graceful silent-close no longer reads as a truncation ([#10805](https://github.com/diegosouzapw/OmniRoute/issues/10805) — thanks @minhlongs)
- **fix(sse):** tool-call and Responses-item correctness — concatenated `tool_call` arguments are split apart when two calls collide on the same name/index ([#11043](https://github.com/diegosouzapw/OmniRoute/issues/11043)), kept Responses input items get a default summary and lose a malformed id ([#11110](https://github.com/diegosouzapw/OmniRoute/issues/11110)), freshly built Chat→Responses reasoning items get a default summary ([#11129](https://github.com/diegosouzapw/OmniRoute/issues/11129)) — thanks @maxmad64bis — and the synthetic keepalive reasoning item is closed properly with hardened `output_index` allocation ([#10330](https://github.com/diegosouzapw/OmniRoute/issues/10330) — thanks @hartmark), with the synthetic keepalive itself replaced ([#10806](https://github.com/diegosouzapw/OmniRoute/issues/10806) — thanks @xz-dev)
- **fix(sse):** prompt-shaping fixes for strict upstreams — a synthetic user turn is appended for GLM-family upstreams that reject the shape with `400 [1214]` ([#11209](https://github.com/diegosouzapw/OmniRoute/issues/11209)), the muse-spark output budget is floored so it stops returning empty-content 502s ([#11214](https://github.com/diegosouzapw/OmniRoute/issues/11214)) — thanks @linhdmn; the strict system hoist re-runs after format translation ([#10803](https://github.com/diegosouzapw/OmniRoute/issues/10803) — thanks @Kizuno18); directive-only messages are relocated off `messages[0]` ([#10457](https://github.com/diegosouzapw/OmniRoute/issues/10457) — thanks @HouMinXi); and the `purify_history` compression notice is merged into the leading system message instead of being injected as its own turn ([#11113](https://github.com/diegosouzapw/OmniRoute/issues/11113) — thanks @ggdayup)
- **fix(sse):** routing and credential edge cases — keyless Pollinations 401s stop poisoning the no-auth pool ([#11194](https://github.com/diegosouzapw/OmniRoute/issues/11194) — thanks @jonlwheat2-gif); `:free` OpenRouter models bypass a connection-wide `credits_exhausted` lock ([#10445](https://github.com/diegosouzapw/OmniRoute/issues/10445) — thanks @killmonger2317-coder); the `OpencodeExecutor` target format resolves through the provider alias ([#11047](https://github.com/diegosouzapw/OmniRoute/issues/11047) — thanks @maxmad64bis); search providers are excluded from the credential-health scheduler sweep ([#10435](https://github.com/diegosouzapw/OmniRoute/issues/10435)); bare `qwen3.8-max` routes to the canonical `-preview` id ([#10632](https://github.com/diegosouzapw/OmniRoute/issues/10632)); the missing `minimax-music` dispatch was added to music generation ([#10650](https://github.com/diegosouzapw/OmniRoute/issues/10650) — thanks @octo-patch); `gemini-3.5-flash` is marked thinking-capable ([#10450](https://github.com/diegosouzapw/OmniRoute/issues/10450)); a generic compatible-provider type id is bridged to the concrete node id during credential lookup ([#10434](https://github.com/diegosouzapw/OmniRoute/issues/10434)); and `localDb` is imported through its real `.ts` extension so the release build stops breaking ([#10691](https://github.com/diegosouzapw/OmniRoute/issues/10691), [#10674](https://github.com/diegosouzapw/OmniRoute/issues/10674))
- **fix(sse):** Antigravity's static `sessionId` is no longer pinned and DNS failures are classified as retryable ([#11177](https://github.com/diegosouzapw/OmniRoute/issues/11177) — thanks @rqzbeh); prompt-cache usage fields are included on the `message_stop` fallback path ([#10545](https://github.com/diegosouzapw/OmniRoute/issues/10545) — thanks @NahuSaruf); Claude cache breakpoints advance on growing tails ([#10684](https://github.com/diegosouzapw/OmniRoute/issues/10684) — thanks @cryptiklemur); the reasoning-cache write is guarded by the same predicate its readers use ([#10978](https://github.com/diegosouzapw/OmniRoute/issues/10978) — thanks @maxmad64bis); Codex quota headers stay under the forwarding budget ([#10306](https://github.com/diegosouzapw/OmniRoute/issues/10306)) and the substring "hermes" is no longer ZWJ-obfuscated in user text ([#10488](https://github.com/diegosouzapw/OmniRoute/issues/10488)) — thanks @RaviTharuma
- **fix(sse):** the Adobe Firefly sign-in helper kills the whole process tree on Linux so no orphan browser is left behind ([#11387](https://github.com/diegosouzapw/OmniRoute/issues/11387)), and every aggressive-compression sub-path spares the live user message ([#11386](https://github.com/diegosouzapw/OmniRoute/issues/11386)) — thanks @HouMinXi
- **fix(providers):** catalog and lifecycle corrections — reserved provider prefixes are rejected on compatible-node create/update ([#11375](https://github.com/diegosouzapw/OmniRoute/issues/11375) — thanks @ggdayup); the **Hack Club AI** provider was removed and purged from the shared catalog ([#11123](https://github.com/diegosouzapw/OmniRoute/issues/11123) — thanks @rqzbeh, [#11262](https://github.com/diegosouzapw/OmniRoute/issues/11262)); Pollinations now requires an API key with corrected optional-key i18n labels ([#11117](https://github.com/diegosouzapw/OmniRoute/issues/11117) — thanks @rqzbeh); `hailuo-web` moved to `chat.minimax.io` ([#11055](https://github.com/diegosouzapw/OmniRoute/issues/11055) — thanks @rqzbeh); the invalid CodeBuddy CN `glm-4.7` entry was dropped and `hy3` added ([#10356](https://github.com/diegosouzapw/OmniRoute/issues/10356) — thanks @rizxfrog); the phantom Gemini 3.5 Flash entry was eliminated (thanks @backryun); OpenRouter Gemini Embedding 2 ids were catalogued ([#10566](https://github.com/diegosouzapw/OmniRoute/issues/10566) — thanks @RaviTharuma); and `bailian-coding-plan` is validated against the Token Plan host ([#10634](https://github.com/diegosouzapw/OmniRoute/issues/10634))
- **fix(providers):** reasoning-effort handling is consistent — `reasoning_effort` is clamped to the vocabulary a model actually declares ([#11274](https://github.com/diegosouzapw/OmniRoute/issues/11274) — thanks @linhdmn) and learned and declared clamps now share nearest-tier semantics ([#11305](https://github.com/diegosouzapw/OmniRoute/issues/11305))
- **fix(providers):** hidden models no longer leak into `GET /v1/models` ([#11309](https://github.com/diegosouzapw/OmniRoute/issues/11309)); rate-limit protection is not silently enabled by a `PATCH` unless it is actually persisted ([#11302](https://github.com/diegosouzapw/OmniRoute/issues/11302)); a degraded cached catalog counts as a failed model sync rather than a success ([#10862](https://github.com/diegosouzapw/OmniRoute/issues/10862) — thanks @ntdat812); expired terminal `grok-cli` credentials answer 401 ([#10971](https://github.com/diegosouzapw/OmniRoute/issues/10971) — thanks @RaviTharuma); model target formats are scoped to their provider ([#10072](https://github.com/diegosouzapw/OmniRoute/issues/10072) — thanks @xz-dev); combo names resolve on `/v1/audio/speech` and `/v1/videos/generations` ([#10471](https://github.com/diegosouzapw/OmniRoute/issues/10471) — thanks @sha367); Muse Spark is routed to the Responses API on `opencode-zen` too ([#11049](https://github.com/diegosouzapw/OmniRoute/issues/11049)); and two dead knobs were removed — the `existingConnections` lookups on connection creation ([#10973](https://github.com/diegosouzapw/OmniRoute/issues/10973)) and `ALLOW_MULTI_CONNECTIONS_PER_COMPAT_NODE`, a flag that governed nothing ([#10974](https://github.com/diegosouzapw/OmniRoute/issues/10974)) — thanks @maxmad64bis
- **fix(resilience):** cooldown and quota-recovery hardening across the cycle — active cooldowns are preserved during recovery and probes ([#11355](https://github.com/diegosouzapw/OmniRoute/issues/11355) — thanks @sprintberlin); an active rate-limit cooldown is not cleared for non-`quota_exhausted` errors ([#11310](https://github.com/diegosouzapw/OmniRoute/issues/11310)); the `quota_exhausted` cooldown is cleared when the real window recovers ([#10534](https://github.com/diegosouzapw/OmniRoute/issues/10534) — thanks @SnCr90); Ollama model-not-found failures stay scoped to per-model lockout instead of disabling the connection ([#11078](https://github.com/diegosouzapw/OmniRoute/issues/11078) — thanks @rqzbeh); dashboard quota snapshots are honored in the `opencode-go` preflight ([#11267](https://github.com/diegosouzapw/OmniRoute/issues/11267)); shared passthrough providers are honored ([#11075](https://github.com/diegosouzapw/OmniRoute/issues/11075) — thanks @yourspraveen); an embed connection is marked terminal on a hard upstream failure so dead accounts stop being re-hit ([#10506](https://github.com/diegosouzapw/OmniRoute/issues/10506)); same-account transport retry is scoped out of emergency fallback and combo hops (thanks @hartmark); and heavyweight SSE is drained on `SIGTERM` ([#11020](https://github.com/diegosouzapw/OmniRoute/issues/11020) — thanks @RaviTharuma)
- **fix(quota):** absolute ISO datetime reset timestamps are parsed in the weekly quota fallback ([#11353](https://github.com/diegosouzapw/OmniRoute/issues/11353) — thanks @sprintberlin), and `CREDIT_LIMIT` rows are parsed from the z.ai coding-plan quota API ([#11378](https://github.com/diegosouzapw/OmniRoute/issues/11378) — thanks @Neuron-Mr-White)
- **fix(combo):** targets carrying a persisted connection cooldown are pre-skipped and re-checked on retry ([#11360](https://github.com/diegosouzapw/OmniRoute/issues/11360) — thanks @sprintberlin); combo loops always terminate with an actionable error instead of hanging silently ([#10463](https://github.com/diegosouzapw/OmniRoute/issues/10463)) and auto-combo pools are restricted to user-visible models ([#10456](https://github.com/diegosouzapw/OmniRoute/issues/10456)) — thanks @herjarsa; SSE comment lines (OpenRouter keep-alives) are accepted by response-quality validation ([#11036](https://github.com/diegosouzapw/OmniRoute/issues/11036) — thanks @asorourx); stale sticky pins are cleared when stickiness is disabled ([#10907](https://github.com/diegosouzapw/OmniRoute/issues/10907) — thanks @excessivechaos); an `unhandledRejection` from the per-model-timeout abort is prevented ([#10846](https://github.com/diegosouzapw/OmniRoute/issues/10846) — thanks @HouMinXi); a combo per-model timeout evicts the sticky session pin ([#10016](https://github.com/diegosouzapw/OmniRoute/issues/10016) — thanks @fenix007); local target timeouts are classified as gateway timeouts; and benign empty error fields no longer fail streaming quality validation
- **fix(auto):** empty auto-family pools warn once per process instead of on every request ([#10820](https://github.com/diegosouzapw/OmniRoute/issues/10820), [#10344](https://github.com/diegosouzapw/OmniRoute/issues/10344) — thanks @RaviTharuma); auto scoring order and the auto-selected first target are preserved ([#11400](https://github.com/diegosouzapw/OmniRoute/issues/11400), [#11399](https://github.com/diegosouzapw/OmniRoute/issues/11399) — thanks @jacobsparts)
- **fix(routing):** combo precedence is preserved and hidden models are skipped in the alias resolver ([#11107](https://github.com/diegosouzapw/OmniRoute/issues/11107) — thanks @SCys); keyless custom-compatible connections stay in the `auto/*` pool ([#11198](https://github.com/diegosouzapw/OmniRoute/issues/11198)) and `lkgpEnabled` is forwarded into `RoutingContext` so the LKGP toggle actually works ([#11193](https://github.com/diegosouzapw/OmniRoute/issues/11193)) — thanks @pacocartones; unmapped model aliases fall back to the default seeds ([#10124](https://github.com/diegosouzapw/OmniRoute/issues/10124) — thanks @benzntech)
- **fix(models):** a synced model's real context window and default effort take effect immediately ([#10957](https://github.com/diegosouzapw/OmniRoute/issues/10957) — thanks @Neuron-Mr-White); `apiFormat`, `targetFormat` and `supportsVision` overrides persist for catalog models ([#10898](https://github.com/diegosouzapw/OmniRoute/issues/10898) — thanks @rqzbeh); media endpoint metadata is normalized ([#11397](https://github.com/diegosouzapw/OmniRoute/issues/11397) — thanks @marcelokarval); Ollama Cloud native effort tiers are exposed ([#11307](https://github.com/diegosouzapw/OmniRoute/issues/11307) — thanks @ekinnee); Codex context and combo limit resolution were corrected ([#10533](https://github.com/diegosouzapw/OmniRoute/issues/10533) — thanks @jackjinke); `MODELS_DEV_SYNC_ENABLED=0` wins over the dashboard setting ([#10299](https://github.com/diegosouzapw/OmniRoute/issues/10299)) and `getModelsDevPricing` is memoized so it stops stalling the event loop and `/healthz` ([#10055](https://github.com/diegosouzapw/OmniRoute/issues/10055)) — thanks @RaviTharuma
- **fix(catalog):** GLM reasoning-effort tiers are declared ([#10963](https://github.com/diegosouzapw/OmniRoute/issues/10963)) and combo reasoning efforts are scoped by connection ([#10723](https://github.com/diegosouzapw/OmniRoute/issues/10723)) — thanks @xz-dev; Stealth Ox Alpha (`stealth/ox-alpha`) was added to the OpenRouter free roster ([#11337](https://github.com/diegosouzapw/OmniRoute/issues/11337) — thanks @stanleytejakusuma)
- **fix(pricing):** DeepSeek V4 static defaults were stale by four days and off by ~1.62.4× ([#10635](https://github.com/diegosouzapw/OmniRoute/issues/10635)), and three dead entries in `LITELLM_PROVIDER_MAP` were silently dropping synced pricing ([#10636](https://github.com/diegosouzapw/OmniRoute/issues/10636)) — thanks @stanleytejakusuma. DeepSeek off-peak pricing is documented as weekdays-only ([#11210](https://github.com/diegosouzapw/OmniRoute/issues/11210) — thanks @xyzs996)
- **fix(api):** proxy and probe correctness — both proxy health checks share one probe-target resolution ([#10657](https://github.com/diegosouzapw/OmniRoute/issues/10657)), a target that refuses the egress IP is no longer reported as a healthy proxy ([#10654](https://github.com/diegosouzapw/OmniRoute/issues/10654)), `/api/cache/stats` reports the cache requests actually use ([#10769](https://github.com/diegosouzapw/OmniRoute/issues/10769)) — thanks @maxmad64bis; an OAuth probe timeout is classified as `network_error` ([#10663](https://github.com/diegosouzapw/OmniRoute/issues/10663) — thanks @HouMinXi); pool usage snapshot limits scale by pool member count ([#10253](https://github.com/diegosouzapw/OmniRoute/issues/10253) — thanks @dpozimski); the MCP SSE singleton resets on a new client `initialize` ([#10772](https://github.com/diegosouzapw/OmniRoute/issues/10772) — thanks @sadSanta-07); `.opus` uploads are accepted on `/v1/audio/transcriptions` ([#10607](https://github.com/diegosouzapw/OmniRoute/issues/10607) — thanks @pucedoteth); working OpenRouter ids are named when Gemini embed credentials are missing ([#10565](https://github.com/diegosouzapw/OmniRoute/issues/10565) — thanks @RaviTharuma); a routing combo can no longer be created with no model at all ([#11162](https://github.com/diegosouzapw/OmniRoute/issues/11162) — thanks @maxmad64bis); call logs are saved and an endpoint fallback added for local rerank providers ([#11081](https://github.com/diegosouzapw/OmniRoute/issues/11081) — thanks @AndrianBalanescu); and API keys are hashed in the `/v1/models` catalog cache key
- **fix(security):** four rounds of CodeQL and advisory remediation landed this cycle — cookie domains are matched by suffix rather than substring ([#11429](https://github.com/diegosouzapw/OmniRoute/issues/11429)); the agent-card topology is sanitized, the login rate limiter uses an anti-spoofed peer IP and 429s carry `Retry-After` ([#11418](https://github.com/diegosouzapw/OmniRoute/issues/11418) — thanks @HouMinXi); round-3 advisories were closed ([#11261](https://github.com/diegosouzapw/OmniRoute/issues/11261)) along with rounds 2 and 4 of the code-scanning alerts ([#10888](https://github.com/diegosouzapw/OmniRoute/issues/10888), [#11293](https://github.com/diegosouzapw/OmniRoute/issues/11293)); an eleven-finding advisory batch ([#11040](https://github.com/diegosouzapw/OmniRoute/issues/11040)) and four still-real findings — ACP RCE hardening, the db-backups route tier, an uppercase authz bypass and spawn-veto drift ([#11028](https://github.com/diegosouzapw/OmniRoute/issues/11028)) — were fixed; Adobe Firefly login compares a parsed hostname instead of a substring and its credential-parsing gates were hardened; an SSRF through the `/v1/search` Firecrawl `provider_options.baseUrl` was blocked ([#10738](https://github.com/diegosouzapw/OmniRoute/issues/10738)) and the open CodeQL code-scanning alerts were zeroed out ([#10739](https://github.com/diegosouzapw/OmniRoute/issues/10739)) with the test regex sanitized and the hash false-positives annotated ([#10380](https://github.com/diegosouzapw/OmniRoute/issues/10380)); and Tier 1 local-only route-guard process-spawning endpoints gained test coverage ([#11189](https://github.com/diegosouzapw/OmniRoute/issues/11189) — thanks @rqzbeh)
- **fix(authz):** exact public routes are matched exactly instead of as prefixes, so `/api/healthz-something` no longer inherits `/api/healthz`'s public tier ([#11417](https://github.com/diegosouzapw/OmniRoute/issues/11417))
- **fix(auth):** the real upstream reason is kept in `lastError` instead of a generic string ([#11376](https://github.com/diegosouzapw/OmniRoute/issues/11376) — thanks @ntdat812); login navigates with `window.location` instead of `router.push` so the session cookie is picked up ([#11175](https://github.com/diegosouzapw/OmniRoute/issues/11175)) and `opencode`/`opencode-zen` were added to the credential-resolution search pairs ([#10899](https://github.com/diegosouzapw/OmniRoute/issues/10899)) — thanks @rqzbeh; the OIDC authorization URL carries its missing `state` parameter ([#10614](https://github.com/diegosouzapw/OmniRoute/issues/10614) — thanks @MeRezaRezaei)
- **fix(oauth):** connections stuck on an upstream 400 after token staleness recover ([#11141](https://github.com/diegosouzapw/OmniRoute/issues/11141) — thanks @HouMinXi); the Kiro `profileArn` survives IAM Identity Center logins ([#10725](https://github.com/diegosouzapw/OmniRoute/issues/10725) — thanks @MichaelYcJo) and a Kiro social poll status is treated as an alias of `error` for pending states ([#10620](https://github.com/diegosouzapw/OmniRoute/issues/10620) — thanks @krishna3554)
- **fix(cli):** Windows support pass — `omniroute update` spawns npm the way Windows needs ([#11374](https://github.com/diegosouzapw/OmniRoute/issues/11374) — thanks @ntdat812); the tray runtime and the DB fallback resolve dynamic imports as `file://` URLs so `--tray` works ([#11332](https://github.com/diegosouzapw/OmniRoute/issues/11332), [#11238](https://github.com/diegosouzapw/OmniRoute/issues/11238) — thanks @pacocartones); the `DEP0190` child-process spawn deprecation is avoided ([#10835](https://github.com/diegosouzapw/OmniRoute/issues/10835) — thanks @adevwithpurpose); and cliproxy platform handling plus the pid probe were completed ([#11263](https://github.com/diegosouzapw/OmniRoute/issues/11263))
- **fix(cli):** OAuth start no longer shows a blank device code and an undefined verification URL ([#11173](https://github.com/diegosouzapw/OmniRoute/issues/11173)); `setup-opencode` understands the OpenCode V2 config format ([#11079](https://github.com/diegosouzapw/OmniRoute/issues/11079)) and defaults `limit.context` to 128k when unknown ([#11054](https://github.com/diegosouzapw/OmniRoute/issues/11054)) — thanks @rqzbeh; provider tests route through the connection API ([#10572](https://github.com/diegosouzapw/OmniRoute/issues/10572) — thanks @hydraxman); packaged machine-token authentication was restored ([#10468](https://github.com/diegosouzapw/OmniRoute/issues/10468) — thanks @xiaoyaner0201); a non-empty `[STARTUP] Fatal` log is guaranteed when the instrumentation hook throws at boot ([#10447](https://github.com/diegosouzapw/OmniRoute/issues/10447)); real OpenRouter key validation and the auth-export argument wiring were fixed ([#11264](https://github.com/diegosouzapw/OmniRoute/issues/11264)); and a tmpfs mount is no longer taken as proof that a config path reaches the host
- **fix(dashboard):** custom mode-pack options are exposed ([#11407](https://github.com/diegosouzapw/OmniRoute/issues/11407)) and explicit auto weights are normalized ([#11402](https://github.com/diegosouzapw/OmniRoute/issues/11402)) — thanks @jacobsparts; unique connection names are computed from the array so one no longer overwrites another ([#11067](https://github.com/diegosouzapw/OmniRoute/issues/11067)) and key validation triggers on Enter in the Add API Key modal ([#11056](https://github.com/diegosouzapw/OmniRoute/issues/11056)) — thanks @rqzbeh; a non-string `apiKey` in the CLI tool cards is guarded ([#10872](https://github.com/diegosouzapw/OmniRoute/issues/10872) — thanks @Rahulsharma0810); the live dashboard sends periodic WebSocket heartbeats to stop reconnect churn ([#10452](https://github.com/diegosouzapw/OmniRoute/issues/10452)); provider-card warning indicators actually expose the interaction they advertise ([#10448](https://github.com/diegosouzapw/OmniRoute/issues/10448)); and media-playground cards stop sending the masked API key as a Bearer token ([#10449](https://github.com/diegosouzapw/OmniRoute/issues/10449))
- **fix(live-ws):** the public socket URL is resolved at runtime instead of baked in at build ([#11377](https://github.com/diegosouzapw/OmniRoute/issues/11377) — thanks @ntdat812), and a `0.0.0.0` dashboard origin is allowed while non-square SVG image warnings are silenced ([#11269](https://github.com/diegosouzapw/OmniRoute/issues/11269) — thanks @Minamaged18)
- **fix(db):** call-log rotation pauses on `SQLITE_CORRUPT` instead of compounding the damage ([#10979](https://github.com/diegosouzapw/OmniRoute/issues/10979) — thanks @RaviTharuma); a Windows native-driver hang no longer stalls requests ([#10709](https://github.com/diegosouzapw/OmniRoute/issues/10709) — thanks @jonlwheat2-gif); native runtime drivers are preserved in standalone bundles ([#10552](https://github.com/diegosouzapw/OmniRoute/issues/10552) — thanks @excessivechaos); the sql.js database is published atomically instead of rewritten in place ([#10278](https://github.com/diegosouzapw/OmniRoute/issues/10278) — thanks @maxmad64bis); pre-migration backups are pruned so `db_backups` stops growing without bound ([#10423](https://github.com/diegosouzapw/OmniRoute/issues/10423)); and test runs are kept off the operator's real `DATA_DIR` ([#10432](https://github.com/diegosouzapw/OmniRoute/issues/10432))
- **fix(build):** the postbuild hook spawns esbuild cross-platform ([#11159](https://github.com/diegosouzapw/OmniRoute/issues/11159) — thanks @aliyosufi); the standalone `package.json` declares its module type for Node 24 worker compatibility ([#10836](https://github.com/diegosouzapw/OmniRoute/issues/10836) — thanks @adevwithpurpose); `assembleStandalone` tolerates a same-realpath symlink and a stale-typed destination ([#10776](https://github.com/diegosouzapw/OmniRoute/issues/10776)); pack-boot sql.js expectations align with dependency-based packaging ([#11266](https://github.com/diegosouzapw/OmniRoute/issues/11266)); and the SQLite driver is kept out of the client bundle ([#10695](https://github.com/diegosouzapw/OmniRoute/issues/10695))
- **fix(docker):** the Next build worker pool is sized for a 16 GB runner ([#11419](https://github.com/diegosouzapw/OmniRoute/issues/11419)); a warning fires when `OMNIROUTE_MEMORY_MB` disagrees with the `NODE_OPTIONS` heap ([#10818](https://github.com/diegosouzapw/OmniRoute/issues/10818) — thanks @RaviTharuma); and cache mount ids are prefixed with the Railway service scope ([#10288](https://github.com/diegosouzapw/OmniRoute/issues/10288) — thanks @anudeepadi)
- **fix(electron):** the embedded server is pinned to loopback so the window shows on Windows ([#10717](https://github.com/diegosouzapw/OmniRoute/issues/10717) — thanks @echoriver89)
- **fix(compression):** `pathToFileURL` is used for the worker URL so bundler resolution stops failing ([#11364](https://github.com/diegosouzapw/OmniRoute/issues/11364) — thanks @TheDemonTuan); CCR no longer strands prompts for callers without the retrieve tool ([#11084](https://github.com/diegosouzapw/OmniRoute/issues/11084) — thanks @HouMinXi); the vendored GCF was bumped with numeric-domain and surplus fixes ([#10807](https://github.com/diegosouzapw/OmniRoute/issues/10807) — thanks @blackwell-systems); the RTK raw-output store is bounded and pointer reads are O(bucket) ([#10660](https://github.com/diegosouzapw/OmniRoute/issues/10660) — thanks @stanleytejakusuma); the Lite and Caveman whitespace/artifact cleaners were accelerated with native V8 RegExp ([#10834](https://github.com/diegosouzapw/OmniRoute/issues/10834) — thanks @adevwithpurpose); i18n was added for `less-code` and `terse-prose` ([#10498](https://github.com/diegosouzapw/OmniRoute/issues/10498) — thanks @abhijeetnardele24-hash); and a stage gate with a metadata-less engine no longer brings the pipeline down ([#10655](https://github.com/diegosouzapw/OmniRoute/issues/10655))
- **fix(translator):** the `functionCall` id survives Gemini→OpenAI request translation ([#11365](https://github.com/diegosouzapw/OmniRoute/issues/11365)) and consecutive same-role contents are merged in the direct `claudeToGeminiRequest` path ([#10658](https://github.com/diegosouzapw/OmniRoute/issues/10658)) — thanks @Siva010; tool-call names are normalized from lowercase to PascalCase when translating upstream responses into the Claude Messages format ([#10392](https://github.com/diegosouzapw/OmniRoute/issues/10392) — thanks @giauphan); and Claude tool-call state is preserved across a translation round-trip
- **fix(gemini):** a missing `items` schema is injected for array-typed MCP tools ([#10605](https://github.com/diegosouzapw/OmniRoute/issues/10605) — thanks @sadSanta-07)
- **fix(codex):** `max_context_window` is preferred over `context_window` as the usable input limit ([#11179](https://github.com/diegosouzapw/OmniRoute/issues/11179) — thanks @excessivechaos); remote compaction V2 completes reliably ([#11041](https://github.com/diegosouzapw/OmniRoute/issues/11041) — thanks @jackjinke); completed Codex tool handoffs survive streaming ([#10608](https://github.com/diegosouzapw/OmniRoute/issues/10608) — thanks @JxnLexn); the header budget, Codex failover and `kv_after_text` handling were corrected together ([#10573](https://github.com/diegosouzapw/OmniRoute/issues/10573) — thanks @HouMinXi); Codex image generation retries on a sibling ChatGPT account ([#10838](https://github.com/diegosouzapw/OmniRoute/issues/10838)); and `apiType="chat"` is respected in `forceResponsesUpstream` ([#10946](https://github.com/diegosouzapw/OmniRoute/issues/10946) — thanks @YunyunZhai)
- **fix(opencode):** Muse Spark 1.2 models route to the OpenAI Responses API ([#10874](https://github.com/diegosouzapw/OmniRoute/issues/10874) — thanks @zoser69); the provider block is guarded when merging an existing config ([#11004](https://github.com/diegosouzapw/OmniRoute/issues/11004) — thanks @ntdat812); Muse Responses streams are closed at completion ([#11385](https://github.com/diegosouzapw/OmniRoute/issues/11385) — thanks @AStupidBear); the `models[0]` default and its guards were restored ([#11133](https://github.com/diegosouzapw/OmniRoute/issues/11133) — thanks @maxmad64bis); bare combo ids stay unprefixed in the OpenCode plugin ([#10821](https://github.com/diegosouzapw/OmniRoute/issues/10821) — thanks @RaviTharuma); and `opencode-go` is classified as a flat-rate subscription in analytics ([#11199](https://github.com/diegosouzapw/OmniRoute/issues/11199) — thanks @pacocartones)
- **fix(executors):** an account rotates on an upstream 400 with an empty body (opencode) ([#11158](https://github.com/diegosouzapw/OmniRoute/issues/11158)) and on a network throw when the account has a dedicated proxy ([#10402](https://github.com/diegosouzapw/OmniRoute/issues/10402)) — thanks @maxmad64bis; probe-origin failures are isolated from every deactivation site ([#10694](https://github.com/diegosouzapw/OmniRoute/issues/10694) — thanks @maxmad64bis); and the direct-path response-start timeout is bounded (thanks @excessivechaos)
- **fix(search):** the `blockedProviders` setting is enforced on the search endpoint ([#10901](https://github.com/diegosouzapw/OmniRoute/issues/10901), [#11125](https://github.com/diegosouzapw/OmniRoute/issues/11125) — thanks @rqzbeh); a `/v1/search` 502 names its provider and cause ([#10756](https://github.com/diegosouzapw/OmniRoute/issues/10756)) and the catalog-default SearXNG `localhost:8888` target is skipped ([#10981](https://github.com/diegosouzapw/OmniRoute/issues/10981)) — thanks @RaviTharuma; with no search provider configured the request falls back to `duckduckgo-free` ([#11097](https://github.com/diegosouzapw/OmniRoute/issues/11097) — thanks @Egorich-print)
- **fix(mcp):** the `mcp:connect` carve-out is honored in the transport route guards ([#11139](https://github.com/diegosouzapw/OmniRoute/issues/11139) — thanks @HouMinXi); provider-bound tool calls get their own fetch budget ([#10860](https://github.com/diegosouzapw/OmniRoute/issues/10860) — thanks @ntdat812); GitHub skill tools are discoverable through `omniroute_tool_search` ([#10575](https://github.com/diegosouzapw/OmniRoute/issues/10575) — thanks @branben); and the web-search provider enum is generated from the registry ([#10209](https://github.com/diegosouzapw/OmniRoute/issues/10209) — thanks @sadSanta-07)
- **fix(memory):** a mid-conversation system injection is no longer sent to Claude when the preceding turn is not a tool result ([#11303](https://github.com/diegosouzapw/OmniRoute/issues/11303)), and TokenRouter is treated as system-must-be-first — confirmed against a live HTTP 400 ([#11114](https://github.com/diegosouzapw/OmniRoute/issues/11114) — thanks @ggdayup)
- **fix(embeddings):** an account is cooled down on hard errors (402/401/5xx) ([#10529](https://github.com/diegosouzapw/OmniRoute/issues/10529) — thanks @HouMinXi), and the configured LM Studio connection URL is honored via the `lm-studio` alias ([#11260](https://github.com/diegosouzapw/OmniRoute/issues/11260))
- **fix(video-bridge):** structural segment sampling is validated, contact sheets render their timestamps, captions route through provider connections, the fetch size broker sizes bodies, and the remote runtime status is clarified; the result-cache identity and bounds were hardened, download flights are keyed with a process HMAC and tenant scope is separated from the download hash
- **fix(images):** bare `dall-e-3` routes to OpenAI ([#10847](https://github.com/diegosouzapw/OmniRoute/issues/10847) — thanks @RaviTharuma); OpenRouter reference-image edits are supported ([#10363](https://github.com/diegosouzapw/OmniRoute/issues/10363) — thanks @tiangao88); and inline images keep high detail ([#10554](https://github.com/diegosouzapw/OmniRoute/issues/10554) — thanks @rinseaid)
- **fix(fusion):** the vision-compatibility filter is applied to both the fusion panel and the judge, so a fan-out with images cannot land on a text-only model ([#10737](https://github.com/diegosouzapw/OmniRoute/issues/10737))
- **fix(proxy):** proxy log batching is non-blocking and asynchronous ([#11182](https://github.com/diegosouzapw/OmniRoute/issues/11182) — thanks @rqzbeh); IPv4-only proxies stop being reported as dead ([#10868](https://github.com/diegosouzapw/OmniRoute/issues/10868) — thanks @ntdat812); a proxy is probed against its assigned provider host instead of a generic target ([#10664](https://github.com/diegosouzapw/OmniRoute/issues/10664) — thanks @maxmad64bis); and local/loopback proxy-subscription fetch URLs are allowed ([#10416](https://github.com/diegosouzapw/OmniRoute/issues/10416))
- **fix(relay):** one private-host guard is shared across the three relay workers ([#10941](https://github.com/diegosouzapw/OmniRoute/issues/10941)) and `x-relay-path` resolves through that same guard in the Cloudflare worker ([#10935](https://github.com/diegosouzapw/OmniRoute/issues/10935)) — thanks @ntdat812; Bifrost errors are normalized, a credential 404 is remapped and analytics were corrected ([#10797](https://github.com/diegosouzapw/OmniRoute/issues/10797))
- **fix(logging):** the component field and printf formats are preserved in the app log ([#10770](https://github.com/diegosouzapw/OmniRoute/issues/10770) — thanks @maxmad64bis); early-keepalive bytes are captured in the call-log artifact ([#10331](https://github.com/diegosouzapw/OmniRoute/issues/10331) — thanks @hartmark); and stream-chunk capture, request-shape logging and other diagnostics are opt-in (thanks @benzntech)
- **fix(logs):** filter predicates are applied to merged in-memory call-log rows ([#11082](https://github.com/diegosouzapw/OmniRoute/issues/11082) — thanks @AndrianBalanescu); auto-routing is queried from the call logs in analytics ([#10685](https://github.com/diegosouzapw/OmniRoute/issues/10685) — thanks @cryptiklemur)
- **fix(webhooks):** three declared-but-never-emitted ghost events were removed ([#11050](https://github.com/diegosouzapw/OmniRoute/issues/11050)) with follow-up dispatcher tests and vi i18n ([#11130](https://github.com/diegosouzapw/OmniRoute/issues/11130)) — thanks @maxmad64bis
- **fix(antigravity):** live chat models are discovered dynamically ([#10422](https://github.com/diegosouzapw/OmniRoute/issues/10422) — thanks @JxnLexn); Gemini and Claude reasoning capabilities are unblocked ([#10376](https://github.com/diegosouzapw/OmniRoute/issues/10376) — thanks @Chewji9875); and a trailing model turn is stripped for native Gemini requests too ([#10436](https://github.com/diegosouzapw/OmniRoute/issues/10436))
- **fix(adobe-firefly):** sessions renew through a durable CDP connection ([#9255](https://github.com/diegosouzapw/OmniRoute/issues/9255) — thanks @artickc), models and media capabilities were synced, and the Topaz catalog models are retained
- **fix(cline):** proxy task ids are no longer generated ([#10279](https://github.com/diegosouzapw/OmniRoute/issues/10279)) and internal health checks are labelled as such ([#10706](https://github.com/diegosouzapw/OmniRoute/issues/10706)) — thanks @arafatkatze; Cline provider models use a valid `modelType`/`model` format ([#11132](https://github.com/diegosouzapw/OmniRoute/issues/11132) — thanks @rqzbeh)
- **fix(agentrouter):** the protocol is inferred from the client endpoint, an alternate protocol is honored through the chat pipeline, and both the Claude and Codex protocols are supported
- **fix(services):** the CLIProxy executable is used on Windows ([#10371](https://github.com/diegosouzapw/OmniRoute/issues/10371) — thanks @tkgo11), and port discovery falls back to `ss` and `netstat` when `lsof` is absent ([#10459](https://github.com/diegosouzapw/OmniRoute/issues/10459) — thanks @aron-intframe)
- **fix(monitoring):** provider aliases are canonicalized in the health matrix ([#10370](https://github.com/diegosouzapw/OmniRoute/issues/10370) — thanks @tkgo11); the Docker `HEALTHCHECK` probes `/healthz` rather than deep monitoring ([#10307](https://github.com/diegosouzapw/OmniRoute/issues/10307) — thanks @RaviTharuma); a slow `/healthz` event-loop lag warns ([#10827](https://github.com/diegosouzapw/OmniRoute/issues/10827) — thanks @RaviTharuma); and the canary install is judged by the SHA on disk instead of npm's exit code ([#10699](https://github.com/diegosouzapw/OmniRoute/issues/10699))
- **fix(github):** GitHub access tokens are verified during health checks instead of being assumed valid ([#11320](https://github.com/diegosouzapw/OmniRoute/issues/11320) — thanks @RaviTharuma)
- **fix(i18n):** locale parity work — the zh-CN/zh-TW CLI locales were completed with a parity guard ([#11339](https://github.com/diegosouzapw/OmniRoute/issues/11339)) and the three missing pt-BR CLI keys added ([#11322](https://github.com/diegosouzapw/OmniRoute/issues/11322)) — thanks @pacocartones; missing routing and compression messages were added ([#10546](https://github.com/diegosouzapw/OmniRoute/issues/10546) — thanks @rizxfrog); HTML entities in UI strings were unescaped and `gatesDescription` restored ([#9721](https://github.com/diegosouzapw/OmniRoute/issues/9721) — thanks @dionjoshualobo); vi parity was completed for the sponsor banner ([#11208](https://github.com/diegosouzapw/OmniRoute/issues/11208)); and the capability-filter messages and web-session guide were translated
- **fix(chat):** the severity-classifier format is detected in the `claudeClassifierCompat` short-circuit ([#11304](https://github.com/diegosouzapw/OmniRoute/issues/11304)); search providers are guarded from the OpenAI fallback ([#10394](https://github.com/diegosouzapw/OmniRoute/issues/10394) — thanks @azzaouiomar19-sketch); and concurrent requests stop colliding on the dedup hash for non-OpenAI formats ([#10438](https://github.com/diegosouzapw/OmniRoute/issues/10438))
- **fix(providers):** small per-provider corrections — Groq strips unsupported message metadata ([#11026](https://github.com/diegosouzapw/OmniRoute/issues/11026) — thanks @sanforex24h); `sensenova` clamps max reasoning effort to `xhigh` ([#10733](https://github.com/diegosouzapw/OmniRoute/issues/10733) — thanks @InkshadeWoods); Zed Hosted Models gained connection-test support ([#10810](https://github.com/diegosouzapw/OmniRoute/issues/10810) — thanks @Hsia97); `kilocode` strips an unsupported `response_format` for DeepSeek V4 Flash ([#10458](https://github.com/diegosouzapw/OmniRoute/issues/10458) — thanks @benzntech); `kimi-web` points at the international `www.kimi.ai` ([#11045](https://github.com/diegosouzapw/OmniRoute/issues/11045) — thanks @MeRezaRezaei); "insufficient credits" is classified as credits-exhausted ([#10116](https://github.com/diegosouzapw/OmniRoute/issues/10116) — thanks @Chewji9875); the ChatGPT Web catalog was refreshed ([#10637](https://github.com/diegosouzapw/OmniRoute/issues/10637) — thanks @backryun); Ollama routes models by advertised capability ([#11088](https://github.com/diegosouzapw/OmniRoute/issues/11088) — thanks @yourspraveen); the Perplexity Web built-in-search hint became opt-in ([#10904](https://github.com/diegosouzapw/OmniRoute/issues/10904)); nested STT models fall back when the prefix provider has no credentials ([#10584](https://github.com/diegosouzapw/OmniRoute/issues/10584) — thanks @RaviTharuma); the mimocode provider was retired ([#10186](https://github.com/diegosouzapw/OmniRoute/issues/10186) — thanks @Tushar49); AWS Polly signing credentials were added ([#11207](https://github.com/diegosouzapw/OmniRoute/issues/11207) — thanks @rafacpti23); and `model_not_found` is returned for an unrecognized prefix model when the provider is inactive ([#10894](https://github.com/diegosouzapw/OmniRoute/issues/10894) — thanks @rqzbeh)
- **fix(radar):** feature availability is separated from opt-in ([#10487](https://github.com/diegosouzapw/OmniRoute/issues/10487)) and stale flag-off responses are bypassed ([#10464](https://github.com/diegosouzapw/OmniRoute/issues/10464)); the aggregate sync body is validated and the remaining client trust boundaries closed — thanks @backryun
- **fix(usage):** quota windows are ordered chronologically on every provider card ([#11241](https://github.com/diegosouzapw/OmniRoute/issues/11241) — thanks @pacocartones)
- **fix(files):** the list `limit` query parameter is validated ([#10673](https://github.com/diegosouzapw/OmniRoute/issues/10673) — thanks @pacocartones), and the gamification leaderboard validates `limit`/`offset` before the SQLite bind ([#11059](https://github.com/diegosouzapw/OmniRoute/issues/11059) — thanks @pacocartones)
- **fix(onboarding):** the setup wizard warns when the password step is skipped ([#10855](https://github.com/diegosouzapw/OmniRoute/issues/10855) — thanks @krishna3554)
- **fix(settings):** `customSystemPrompt` fields were missing from `updateSettingsSchema` and were silently dropped ([#10890](https://github.com/diegosouzapw/OmniRoute/issues/10890) — thanks @rqzbeh); `debugMode` defaults to false and account rotation is skipped on a model-unsupported 400 ([#10525](https://github.com/diegosouzapw/OmniRoute/issues/10525) — thanks @HouMinXi)
- **fix(conversations):** the reconnect walk is bounded and turn hashes are memoized ([#10800](https://github.com/diegosouzapw/OmniRoute/issues/10800) — thanks @adevwithpurpose)
- **fix(reasoning):** compatible response state is preserved across a provider hop ([#10574](https://github.com/diegosouzapw/OmniRoute/issues/10574) — thanks @jackjinke)
- **fix(agent-bridge):** the regenerate-cert endpoint actually mints a new certificate ([#10715](https://github.com/diegosouzapw/OmniRoute/issues/10715) — thanks @ntdatt812)
- **fix(api-manager):** empty combo restrictions are allowed instead of rejected ([#10066](https://github.com/diegosouzapw/OmniRoute/issues/10066) — thanks @xz-dev)
- **fix(db):** the Database settings page no longer answers HTTP 500 when SQLite lacks the optional `dbstat` table ([#10558](https://github.com/diegosouzapw/OmniRoute/issues/10558) — thanks @TechNickAI)
- **fix(models):** health-check-excluded models are hidden from the `/v1/models` catalog ([#10026](https://github.com/diegosouzapw/OmniRoute/issues/10026) — thanks @ritheshcn25)
- **fix(skills):** the CLI skills left stale by the quota subcommands are regenerated ([#10698](https://github.com/diegosouzapw/OmniRoute/issues/10698))
- **fix(mcp):** CLI MCP call protocol issues were resolved ([#10960](https://github.com/diegosouzapw/OmniRoute/issues/10960) — thanks @YunyunZhai)
### 📝 Maintenance
@@ -791,6 +1260,102 @@ _Living section — regenerated 2026-08-12 from all cycle commits (cycle open `e
- **deps:** bump electron from 43.2.0 to 43.3.0 in /electron ([#10042](https://github.com/diegosouzapw/OmniRoute/pull/10042)) — thanks @app/dependabot
- **maint(release):** 45 direct pushes to the release branch with no PR ref — base-red and quality-gate repairs, i18n string completion and stream/type fixes (quality ×6, i18n ×5, deps ×3, agentrouter ×3, providers ×2, release ×2, security ×2, logging ×2)
- **maint(repo):** 29 chore/ci/test/docs commits rolled up — quality baselines, mutation registration, CI re-triggers, doc restructure and repo hygiene (#10187, #10189, #10190, #10193, #10196, #10203, #10204, #10205, #10207, #10210, #10236, #10318)
- **docs(auth):** distinguish dashboard sessions, `oma_live_…` Access Tokens, manage-scoped API keys, and inference keys ([#7786](https://github.com/diegosouzapw/OmniRoute/issues/7786))
- **docs(ops):** document Kubernetes probe recommendations — TCP (or soft HTTP) liveness, HTTP `/healthz` readiness, avoid `/api/monitoring/health` as kubelet liveness ([#10297](https://github.com/diegosouzapw/OmniRoute/pull/10297)) — thanks @RaviTharuma
- **docs(docker):** spell out that `:latest` tracks the highest **published** stable SemVer (not git `main`), and that GitOps should pin `X.Y.Z` ([#10317](https://github.com/diegosouzapw/OmniRoute/issues/10317))
- **docs(backend):** document that memory extraction, skills injection, and token refresh share the request event loop, plus dashboard kill switches ([#10349](https://github.com/diegosouzapw/OmniRoute/issues/10349))
- **docs(docker):** document default SQLite as single-replica / HA-unsupported, including Recreate and HEALTHCHECK session blast radius ([#10350](https://github.com/diegosouzapw/OmniRoute/issues/10350))
- **docs(backend):** document that pre-write SQLite backups (including models.dev pricing) are throttled to once per 60 minutes and can be disabled with `DISABLE_SQLITE_AUTO_BACKUP` ([#10351](https://github.com/diegosouzapw/OmniRoute/issues/10351))
- **fix(tests):** drain three base-reds on the release branch — the Vietnamese locale regained parity with English (6 keys added), the chatCore SSE test now asserts the comment-free default that #10539 introduced instead of the trailer it replaced, and the Antigravity cloudcode test asserts the missing-messages guard it is named for instead of a `/ok/` regex that only ever matched the "ok" inside `: x-omniroute-tokens-in` ([#10704](https://github.com/diegosouzapw/OmniRoute/pull/10704))
- chore(security): remove the unused `enforceSecrets()` duplicate of the boot secret check and pin the live `enforceWebRuntimeEnv()` wiring with a regression test (#10775)
- fix(quality): register GrokBuildToolCard.tsx react-hooks/set-state-in-effect suppression (dropped in #10778's uncommitted fix)
- **docs:** Custom combos are only invoked by their exact name in the `model` field — `auto` remains a separate zero-config router, and `openrouter/auto` is a paid OpenRouter product, not an alias ([#10779](https://github.com/diegosouzapw/OmniRoute/pull/10779)) — thanks @maxmad64bis
- chore(startup): remove `src/server-init.ts` (183 lines, never imported — the boot path is `src/instrumentation-node.ts`) and correct four `"called from server-init.ts"` comments left pointing at the dead entry point (#10780)
- fix(quality): rebaseline file-size for #10859's own modelCapabilities.ts/commandCode.ts growth (missed at merge time)
- **docs(openapi):** document the `GET` and `PUT` operations on `/api/combos/{id}`, and add an operation-level coverage floor so a missing verb can no longer hide behind a path that already counts as covered ([#10875](https://github.com/diegosouzapw/OmniRoute/pull/10875))
- fix(quality): bump EXPECTED_FEATURE_FLAG_COUNT to 52 for #10889's own new flag (missed at merge time)
- **test(db):** replace three empty `test.skip` placeholders in the critical DB-state suite with real assertions — `resetDbInstance` must swap the singleton while the on-disk row survives, the on-disk DB must open in WAL journal mode, and `db_meta` must hold the seeded `schema_version` — so a regression in any of those invariants can no longer pass as silently green ([#10906](https://github.com/diegosouzapw/OmniRoute/pull/10906))
- **docs(docker):** document runtime RAM for coding-agent `/v1/responses` (image default 1GiB heap is dashboard-only; 812GiB heap for agents) ([#10982](https://github.com/diegosouzapw/OmniRoute/issues/10982))
- **docs(database):** align the SQLite cache guide with the 65,536 KiB runtime default, supported 11,000,000 KiB range, and live Settings application behavior ([#11018](https://github.com/diegosouzapw/OmniRoute/issues/11018))
- **docs(docker):** document N independent `DATA_DIR`s as the supported large `/v1/responses` scale-out (one V8 heap ≠ host RAM; do not `replicas>1` on one SQLite file) ([#11024](https://github.com/diegosouzapw/OmniRoute/issues/11024)) — thanks @RaviTharuma
- fix(quality): rebaseline file-size for modelCapabilities.ts (1016->1072) drift from merged tip fixes (#11034 et al)
- fix(quality): register `tests/unit/authz/oauth-autoimport-local-only.test.ts` in stryker `tap.testFiles` (residual of #11053)
- chore(quality): drain two `release/v3.8.50` base-reds — refresh the drifted doc counts (159 migrations, 56 free-forever providers, 40 free-tier pools, incl. the 42 `llm.txt` locale mirrors) and move `uncloseai-noauth.test.ts` to a collected path so the UncloseAI no-auth regression guard actually runs (#11160)
- **chore(lint):** ratchet `@typescript-eslint/no-unused-vars` scoped to `src/` + `open-sse/` + `tests/` (`args: "all"`, `_`-prefix escape hatch) and freeze the 1393 pre-existing violations via bulk suppressions — same pattern as the #7879 `toNumber` ratchet. New unused bindings now fail lint. ([#11247](https://github.com/diegosouzapw/OmniRoute/pull/11247))
- **fix(deps):** prevent pnpm from auto-installing the unused `@lobehub/ui` peer subtree of
`@lobehub/icons`, keeping six unneeded packages with incompatible or unverifiable license
metadata out of production installs ([#11342](https://github.com/diegosouzapw/OmniRoute/pull/11342)).
- **ci(changelog):** replace the broad removal bypass with an exact, hash-bound reconciliation ledger and bind merge-train checks to their requested release base ([#11345](https://github.com/diegosouzapw/OmniRoute/pull/11345)).
- **docs(readme):** reconcile live v3.8.50 provider, free-tier, CLI, routing, test,
community, sponsor, acknowledgment, and SVG metrics with their audited source
denominators, including a deduplicated OmniRoute-in-Action snapshot and distinct
contributor rankings for merged pull requests, GitHub-attributed commits, and Git history
([#11356](https://github.com/diegosouzapw/OmniRoute/pull/11356)).
- **docs(openapi):** document the conditionally management-authenticated, same-origin `POST /api/openapi/try` proxy contract and restore the release branch's operation-coverage ratchet ([#11363](https://github.com/diegosouzapw/OmniRoute/pull/11363))
- **fix(video-bridge):** make opt-in segment-aware sampling use one bounded structural FFmpeg pass (scene, freeze, blur, exposure, and SI/TI), preserve long trailing segments, fail open to uniform sampling, and add real-media structural-oracle, overhead, post-dedup caption-call, and false-positive evidence while holding unconfigured model quality and gain-versus-cost claims ([#11381](https://github.com/diegosouzapw/OmniRoute/pull/11381)).
- **docs:** add an embeddings client runbook with live-verified working/broken model ids and Hindsight 0.9.1 / Memorix 1.6.0 notes — thanks @RaviTharuma
- **chore(ci):** ignore ad-hoc `BOT_TOKEN`/`BOT_URL` in env-doc-sync (scripts/ad-hoc mesh helpers, not runtime config)
- **test(kimi):** the Kimi background health sweep no longer draws its refresh window inside the assertion. `checkKimiWebConnectionIfNeeded` spreads the refresh over `[60, 240)` seconds before expiry so a fleet of connections does not stampede the token endpoint, and the test used a token expiring in 90 seconds and asserted that a refresh happened — which is true only when the draw lands at 90 or above, i.e. 150 of the 180 possible values. Measured: the test fails 1 run in 6 (16.7% by construction; 4 of 20 local runs), and it is what the Node 26 nightly hit and reported as a Node-compat break (#11361). The spread is now `defaultKimiRefreshJitterSec()` and the window is injectable as `jitterSecFn`, so the test decides it instead of rolling for it; production behaviour is unchanged. Cases were added for a token outside the window and for the default spread's range.
- chore(test): regenerate the provider/translate-path golden snapshot to reflect freebuff (#10531), fixing a base-red left by that merge (freebuff/freeinference key ordering only, no value changes).
- **chore(release):** resync the v3.8.50 provider and CLI catalogs, register the existing ChatCore mutation-coverage test, and document the local ZCode handshake identifier so the release quality gates reflect the current tree without changing ratchet baselines.
- **fix(ci):** route `open-sse/handlers/imageGeneration/providers/geminiWeb.ts`'s b64_json
download-failure message through `sanitizeErrorMessage()` instead of embedding a raw
`err.message`, clearing the `check:error-helper` base-red on `release/v3.8.50` (#9985).
- **fix(ci):** drain three more base-reds on `release/v3.8.50` (#9985). ESLint was reporting
219 errors locally (vs. 25 in the last CI run) — all from `react-hooks/set-state-in-effect`,
`react-hooks/preserve-manual-memoization`, `react-hooks/immutability`,
`react-hooks/static-components`, `react-hooks/refs` and `react-hooks/purity`, six React
Compiler lint rules that `eslint-plugin-react-hooks` v7 turns on by default and that were
never frozen in `config/quality/eslint-suppressions.json` after the dependency bump. Froze
the pre-existing violations for those six rules via ESLint's native
`--suppress-rule`/`--suppressions-location` mechanism (the same pattern already used for
`@next/next/no-location-assign-relative-destination`) — no application code changed, no rule
disabled, only genuinely-new violations stay blocking. `check:dead-code` was at 418 against a
415 baseline: removed the unused `src/lib/quota/providerCapabilities.ts` file and the unused
`ProviderQuotaMonitor` interface in `providerQuotaTelemetry.ts` (both dead since PR #10148,
2026-08-18, confirmed via `grep`/knip cross-reference), landing at 416; the residual +1 could
not be attributed to a single recent commit after checking every dead-list entry touched
since the 2026-08-14 baseline measurement, so it is rebaselined with the investigation
recorded in `quality-baseline.json`. `tests/unit/autoCombo/tieredRotation.test.ts`'s
"rotates across all 43 Cerebras connection IDs" case was hitting vitest's 5000ms default
timeout on a 200-iteration synchronous `selectProvider()` loop under shared-devbox
contention (load average 40-60+ observed) — widened its explicit timeout to 20000ms; the
assertion itself is unchanged.
- **fix(tests):** drain two base-reds on the release branch — `auto/glm` now expects the Cloudflare AI Playground backend (its registry advertises `zai-org/glm-5.2` and `zai-org/glm-4.7-flash`, so it belongs in the family pool by the same rule already documented for `auggie`, `devin-cli-agentic` and `zcode`), and the ESLint gate is green again after the GitLab executor test dropped its five `as any` casts for a declared response shape and the CLI OAuth suppression count caught up with the two casts #10491 added.
- **fix(tests):** realign the two `stream-utils` passthrough cases that still asserted the pre-#10017 SSE framing — the event-boundary case declares the OpenAI Responses client format it actually exercises, and the metadata case now pins that surviving lines stay inside one event instead of expecting the `:`/`id:` control lines that #10473 stopped forwarding to every client format.
- **fix(tests):** drain several base-reds on `release/v3.8.50` (#9985) that were all instances
of the same pattern — a legitimate product change landed without updating the test that
asserted the old behavior: `tests/unit/glm-provider-model-import-route.test.ts` (12 tests)
and `tests/unit/model-sync-route.test.ts` (2 tests) predate #10603's "upstream model sync is
opt-in and manual overrides are preserved" change; `tests/unit/antigravity-model-aliases.test.ts`
predated #10537 retiring the collapsed `gemini-3.7-flash` alias in favor of its three tiered
ids. Also fixes a real data drift in `open-sse/config/freeModelCatalog.data.ts` (the `qwen-web`
free-catalog entry still pointed at the retired `qwen3.8-max-preview` id instead of the
current `qwen3.8-max`), corrects the zh-TW `providers.autoFetchModelsTooltip` string to the
glossary-canonical 快取 instead of 緩存, and removes an unused default export from
`src/lib/oauth/providers/zed-hosted.ts` (the named export already covers every consumer) to
shave one symbol off the `check:dead-code` ratchet regression.
- **chore(release):** synchronize migration-count documentation and document the opt-in `PROXY_LOG_INCLUDE_IPS` logging flag so the v3.8.50 quality gates match the release tree.
- fix(i18n): translate the 14 `providers.harImport*` keys into Vietnamese (parity gap left by #11069)
<!-- reconciliation pass 2 (Phase 0a.1): commits that landed in the cycle without a bullet -->
- **fix(release):** release pre-flight and base-red drains for v3.8.50 — the build-breaking `localDb` import, stale provider docs and orphaned suppressions ([#11038](https://github.com/diegosouzapw/OmniRoute/issues/11038)); the docs/golden/GLM cluster with a masked assert restored; the volcengine vision metadata and antigravity BYOP contract pair; the #10534 quota-recovery restoration plus volcengine connect-body validation; the onnxruntime dependency contract test; the post-sweep base regressions; the remaining Adobe and typecheck gates; the base-red tail after the latest root lift ([#10964](https://github.com/diegosouzapw/OmniRoute/issues/10964) — thanks @backryun); the agent-skills catalog tests and base quality reds (thanks @adevwithpurpose); a stray conflict leftover in `managedModelImport` was dropped ([#11259](https://github.com/diegosouzapw/OmniRoute/issues/11259) — thanks @hartmark); and the sweep-stale summary counting matches by their real category ([#11338](https://github.com/diegosouzapw/OmniRoute/issues/11338) — thanks @pacocartones)
- **fix(quality):** quality-gate maintenance — the two Fast Quality Gates base-reds ([#11438](https://github.com/diegosouzapw/OmniRoute/issues/11438)) and the three inventory/coverage base-reds were drained; the gate reports the real failure line and stops double-counting `ci.yml` gates ([#11321](https://github.com/diegosouzapw/OmniRoute/issues/11321) — thanks @pacocartones); file-size baselines were rebaselined for `modelCapabilities.ts`, `imageRegistry.ts`, `AddApiKeyModal` and `combo-routing-engine.test.ts` drift (thanks @hartmark, @adevwithpurpose, @wgordon17); a stale ESLint suppression for `search.ts` was pruned and the `eslintWarnings` baseline tightened to the gate's real measurement (0); the capability gate frozen cap was updated; and the gateway duplication between `chatanywhere` and `regolo` was resolved to unblock typecheck (thanks @backryun)
- **fix(tests):** stale-assertion and isolation repairs — the compression/kiro/memory/i18n base-red mini-cluster ([#11306](https://github.com/diegosouzapw/OmniRoute/issues/11306)); the auto/GLM family pool and ESLint gate pair ([#10726](https://github.com/diegosouzapw/OmniRoute/issues/10726) — thanks @MichaelYcJo); the `translate-path` golden snapshot for `hailuo-web` ([#11161](https://github.com/diegosouzapw/OmniRoute/issues/11161) — thanks @rqzbeh); the provider count realigned to 230 after a merge-train collision (thanks @hartmark); the stale `ALL_ACCOUNTS_INACTIVE``ALL_TARGETS_SKIPPED` assertions and six pre-existing typecheck errors (thanks @wgordon17); an exclusive-connection-lease uniqueness test made self-contained ([#11341](https://github.com/diegosouzapw/OmniRoute/issues/11341)); the DB singleton reset made to survive the full suite, un-skipping three DB-state tests ([#11327](https://github.com/diegosouzapw/OmniRoute/issues/11327)); the CLI suite made to pass on Windows ([#11240](https://github.com/diegosouzapw/OmniRoute/issues/11240)); the Kimi refresh window no longer drawn inside its own assertion ([#11380](https://github.com/diegosouzapw/OmniRoute/issues/11380)); the GLM absolute-ISO-reset regression test given a frozen clock; spawned router-eval CLI children given an explicit `DATA_DIR`; the agent-card route handlers called with a real `NextRequest`; queued call-log writes awaited ([#10683](https://github.com/diegosouzapw/OmniRoute/issues/10683)); the aihorde client browser bundles guarded ([#10682](https://github.com/diegosouzapw/OmniRoute/issues/10682)); the env precondition of the CCR/MCP principal tests declared explicitly ([#10689](https://github.com/diegosouzapw/OmniRoute/issues/10689)); the client-bundle guard widened to every `"use client"` entry point ([#11135](https://github.com/diegosouzapw/OmniRoute/issues/11135), [#10700](https://github.com/diegosouzapw/OmniRoute/issues/10700)); standalone fixture paths resolved from file URLs ([#10451](https://github.com/diegosouzapw/OmniRoute/issues/10451)); and the services adoption tests aligned with the opt-in contract from #11040 ([#11147](https://github.com/diegosouzapw/OmniRoute/issues/11147))
- **fix(ci):** Windows packaged-smoke process trees are cleaned up ([#10453](https://github.com/diegosouzapw/OmniRoute/issues/10453) — thanks @backryun); the coverage-pack and unit gate regressions were cleared; the OpenCode plugin CI tracks the active release branch and its combo-id fixture was aligned ([#11301](https://github.com/diegosouzapw/OmniRoute/issues/11301)); the ad-hoc `BOT_TOKEN`/`BOT_URL` env-doc-sync ignore was added, reverted and then locked by a test ([#10828](https://github.com/diegosouzapw/OmniRoute/issues/10828)); and the Electron release dependency setup was streamlined ([#10325](https://github.com/diegosouzapw/OmniRoute/issues/10325))
- **perf(electron):** desktop packaging and startup pass — the Next standalone is built once and natives hydrated per leg ([#10390](https://github.com/diegosouzapw/OmniRoute/issues/10390)), `better-sqlite3` v13 Node-API prebuilds are verified instead of rebuilt from source ([#10367](https://github.com/diegosouzapw/OmniRoute/issues/10367)), optional ML/browser dependencies ship as installable packs ([#10382](https://github.com/diegosouzapw/OmniRoute/issues/10382)), authoring docs are pruned from the packages ([#10359](https://github.com/diegosouzapw/OmniRoute/issues/10359)), hidden-start renderer creation is deferred ([#10327](https://github.com/diegosouzapw/OmniRoute/issues/10327)) with an option to unload the renderer on close ([#10328](https://github.com/diegosouzapw/OmniRoute/issues/10328)), and lightweight readiness polling is bounded ([#10324](https://github.com/diegosouzapw/OmniRoute/issues/10324)) — thanks @backryun
- **perf(providers):** provider schema validation is lazy and on demand, cutting startup heap ([#11334](https://github.com/diegosouzapw/OmniRoute/issues/11334), [#11220](https://github.com/diegosouzapw/OmniRoute/issues/11220))
- **refactor(sse):** executor lookup routes through a runtime `ExecutorRegistry` (R0.3) ([#10633](https://github.com/diegosouzapw/OmniRoute/issues/10633)); the six TLS client providers were consolidated into a shared factory with wrappers ([#10910](https://github.com/diegosouzapw/OmniRoute/issues/10910) — thanks @oyi77); the Codex virtual quota pools were isolated ([#8367](https://github.com/diegosouzapw/OmniRoute/issues/8367) — thanks @xz-dev); the xAI authentication entry point was unified ([#10201](https://github.com/diegosouzapw/OmniRoute/issues/10201)); duplicate `opencode-zen`/`opencode-go` model entries were deduped ([#11051](https://github.com/diegosouzapw/OmniRoute/issues/11051)); the Radar catalog MCP tool was modularized; and the Video Bridge broker extract route moved to the structured pino logger
- **refactor(dashboard):** custom provider quota keys are formatted into title-cased labels ([#11188](https://github.com/diegosouzapw/OmniRoute/issues/11188)), and the Add API Key modal's Enter handler mirrors the check button's disabled state ([#11156](https://github.com/diegosouzapw/OmniRoute/issues/11156))
- **chore(security):** the self-hosted operator security tier and its blockers were integrated ([#10952](https://github.com/diegosouzapw/OmniRoute/issues/10952) — thanks @arminanton), and test fixtures were sanitized with developer `.env` guidance plus a gitleaks pass ([#10411](https://github.com/diegosouzapw/OmniRoute/issues/10411) — thanks @blarovse)
- **chore(deps):** Dependabot batches landed across the cycle — production and development groups ([#10931](https://github.com/diegosouzapw/OmniRoute/issues/10931), [#10932](https://github.com/diegosouzapw/OmniRoute/issues/10932), [#10625](https://github.com/diegosouzapw/OmniRoute/issues/10625), [#10626](https://github.com/diegosouzapw/OmniRoute/issues/10626), [#10403](https://github.com/diegosouzapw/OmniRoute/issues/10403)), Electron 43.3.0 → 43.4.0 ([#10622](https://github.com/diegosouzapw/OmniRoute/issues/10622)) and `github/codeql-action` 4.37.4 → 4.37.7 ([#10405](https://github.com/diegosouzapw/OmniRoute/issues/10405), [#10406](https://github.com/diegosouzapw/OmniRoute/issues/10406), [#10407](https://github.com/diegosouzapw/OmniRoute/issues/10407), [#10928](https://github.com/diegosouzapw/OmniRoute/issues/10928), [#10929](https://github.com/diegosouzapw/OmniRoute/issues/10929), [#10930](https://github.com/diegosouzapw/OmniRoute/issues/10930)). Security bumps also went out directly: nanoid, DOMPurify, mermaid and js-yaml, plus transitive lifts closing 20 and then 6 more Dependabot CVE alerts
- **fix(deps):** `@atjsh/llmlingua-2` was upgraded to 2.0.5 and `@tensorflow/tfjs` dropped ([#10610](https://github.com/diegosouzapw/OmniRoute/issues/10610) — thanks @jonlwheat2-gif), and `onnxruntime-node` is pinned to the exact version `@huggingface/transformers` requires ([#10543](https://github.com/diegosouzapw/OmniRoute/issues/10543) — thanks @dcox79)
- **docs:** documentation pass across the cycle — a VS Code Copilot Chat guide with the `/v1/models` prefix modes ([#10648](https://github.com/diegosouzapw/OmniRoute/issues/10648)) and its platform-table entry ([#10512](https://github.com/diegosouzapw/OmniRoute/issues/10512)); an embeddings client runbook for Gemini 2 and Jina omni ([#10569](https://github.com/diegosouzapw/OmniRoute/issues/10569)); a free-provider rate-limiting troubleshooting guide ([#10112](https://github.com/diegosouzapw/OmniRoute/issues/10112)); the compression output-style catalog and its extension point ([#10649](https://github.com/diegosouzapw/OmniRoute/issues/10649)); Docker `latest` tag semantics ([#10816](https://github.com/diegosouzapw/OmniRoute/issues/10816)) and SQLite single-replica HA limits ([#10817](https://github.com/diegosouzapw/OmniRoute/issues/10817)); the distinction between access tokens, API keys and management credentials ([#10823](https://github.com/diegosouzapw/OmniRoute/issues/10823)); throttled pre-write SQLite backups ([#10824](https://github.com/diegosouzapw/OmniRoute/issues/10824)); the memory/skills/token-refresh event-loop cost ([#10825](https://github.com/diegosouzapw/OmniRoute/issues/10825)); runtime RAM for the coding-agent `/v1/responses` path ([#10983](https://github.com/diegosouzapw/OmniRoute/issues/10983)); `DEFAULT_RATE_LIMIT_PER_DAY` unset meaning unlimited ([#11031](https://github.com/diegosouzapw/OmniRoute/issues/11031)); the management-auth requirement on the spec endpoint ([#11299](https://github.com/diegosouzapw/OmniRoute/issues/11299)); embedded services explained for beginners ([#11204](https://github.com/diegosouzapw/OmniRoute/issues/11204)); the ChatGPT Web session credential guide with a canonical Cookie Editor install link; the Radar Intel/CLI contract, guided combos, supporter key recovery and end-to-end activation; the Video Bridge fusion telemetry, drill-down byte budget, cache-key dimensions and contact sheets; the OpenCode 128k context fallback JSDoc ([#11157](https://github.com/diegosouzapw/OmniRoute/issues/11157)); and the post-relay CLI documentation audit (run/configure surface, Gemini launcher, smoke harness). Drifted counts were corrected and the free-forever number gated ([#10433](https://github.com/diegosouzapw/OmniRoute/issues/10433)), the accepted doc-drift backlog closed, and the provider count synced to 343
- **docs(i18n):** localization contributions — a complete Persian user guide ([#11254](https://github.com/diegosouzapw/OmniRoute/issues/11254)), improved and completed Turkish documentation ([#11237](https://github.com/diegosouzapw/OmniRoute/issues/11237)), the Italian README restored ([#11246](https://github.com/diegosouzapw/OmniRoute/issues/11246)), a Farsi README ([#10777](https://github.com/diegosouzapw/OmniRoute/issues/10777) — thanks @farshidrezaei), a `SETUP_GUIDE.md` correction ([#10490](https://github.com/diegosouzapw/OmniRoute/issues/10490) — thanks @realize000), a `python_requests.py` example fix ([#10731](https://github.com/diegosouzapw/OmniRoute/issues/10731) — thanks @pandaaaa1990), and a retranslation of the CLI reference and integrations guide across all 42 locales
- **chore(repo):** repository hygiene — the self-referential `_tasks` symlink was untracked and `.gitignore` anchored so a `_tasks` symlink can never be tracked again, `.source/dynamic.ts`, `.source`, `/output/` and the Playwright CLI artifact directory were ignored, an initial `.cbmignore` was added for codebase-memory indexing, unused `.source/dynamic.ts` and `source.config.mjs` files were removed, the stray unresolved conflict marker in `ENVIRONMENT.md` was cleaned up, and the Open Collective sponsorship link was removed from the README
- **chore(release):** localized `llm.txt` mirrors and the v3.8.50 base quality docs were synchronized, and the 363 `changelog.d` fragments were aggregated into this section
### 🙌 Contributors
@@ -799,7 +1364,10 @@ Thanks to everyone whose work landed in v3.8.50:
| Contributor | PRs / Issues |
| --- | --- |
| [@AbdullahFageeh](https://github.com/AbdullahFageeh) | #9087 |
| [@adevwithpurpose](https://github.com/adevwithpurpose) | #9790, #10118, #10222 |
| [@abhijeetnardele24-hash](https://github.com/abhijeetnardele24-hash) | #10498 |
| [@Abhishek4512009](https://github.com/Abhishek4512009) | #10494 |
| [@acc0mplish](https://github.com/acc0mplish) | #10732, #10948 |
| [@adevwithpurpose](https://github.com/adevwithpurpose) | #9790, #10118, #10222, #10800, #10834, #10835, #10836, #10882 |
| [@adrianojiu](https://github.com/adrianojiu) | #8438 |
| [@agisota](https://github.com/agisota) | #9837 |
| [@AgnesRiber](https://github.com/AgnesRiber) | #9718, #9976 |
@@ -807,93 +1375,137 @@ Thanks to everyone whose work landed in v3.8.50:
| [@AIB1TAL0S](https://github.com/AIB1TAL0S) | #9284 |
| [@AlanSyue](https://github.com/AlanSyue) | direct commit / report |
| [@alex-jordan547](https://github.com/alex-jordan547) | #9235, #9245, #9813 |
| [@aliyosufi](https://github.com/aliyosufi) | #11159 |
| [@amartinawi](https://github.com/amartinawi) | #10090, #10091, #10092, #10097, #10101 |
| [@AndrianBalanescu](https://github.com/AndrianBalanescu) | #8888, #8889, #8890, #8891, #8892, #8893, #8894, #8895 |
| [@An0nym0us92](https://github.com/An0nym0us92) | #11394 |
| [@AndrianBalanescu](https://github.com/AndrianBalanescu) | #8888, #8889, #8890, #8891, #8892, #8893, #8894, #8895, #11081, #11082 |
| [@AnhLead](https://github.com/AnhLead) | #9722 |
| [@aniketshukla1](https://github.com/aniketshukla1) | #9148 |
| [@Anjielon](https://github.com/Anjielon) | #8776 |
| [@anudeepadi](https://github.com/anudeepadi) | #10288 |
| [@apoapostolov](https://github.com/apoapostolov) | #8916 |
| [@app](https://github.com/app) | #10042, #10043 |
| [@arafatkatze](https://github.com/arafatkatze) | #10706 |
| [@ARC345](https://github.com/ARC345) | #9628, #10050, #10051 |
| [@artickc](https://github.com/artickc) | #8571, #8578, #8791, #8843, #8870, #8927, #8974, #9097, #9549 |
| [@arminanton](https://github.com/arminanton) | #10933, #10952, #11166 |
| [@aron-intframe](https://github.com/aron-intframe) | #10459 |
| [@artickc](https://github.com/artickc) | #8571, #8578, #8791, #8843, #8870, #8927, #8974, #9097, #9255, #9549 |
| [@Arul-](https://github.com/Arul-) | #9761 |
| [@AStupidBear](https://github.com/AStupidBear) | #10180 |
| [@asorourx](https://github.com/asorourx) | #11036 |
| [@AStupidBear](https://github.com/AStupidBear) | #10180, #11385 |
| [@azzaouiomar19-sketch](https://github.com/azzaouiomar19-sketch) | #10394 |
| [@b1nhm1nh](https://github.com/b1nhm1nh) | direct commit / report |
| [@backryun](https://github.com/backryun) | #8228, #8451, #8627, #8809, #8818, #9084, #9086, #9090, #9091, #9092, #9093, #9114, #9119, #9120, #9122, #9135, #9136, #9137, #9138, #9139, #9141, #9561, #9562, #9563, #9564, #9565, #9566, #9742, #9747, #9748, #9751, #9753, #9755, #9791, #9792, #9793, #9795, #9796, #9797, #9798, #9920, #9972, #9973, #9974, #9975, #9977, #9978, #9979, #9984, #9986, #9987, #9988, #9989, #9990, #9998, #10087, #10088, #10134, #10178, #10175, #10254, #10255, #10256, #10257, #10258, #10339 |
| [@backryun](https://github.com/backryun) | #8228, #8451, #8627, #8809, #8818, #9084, #9086, #9090, #9091, #9092, #9093, #9114, #9119, #9120, #9122, #9135, #9136, #9137, #9138, #9139, #9141, #9561, #9562, #9563, #9564, #9565, #9566, #9742, #9747, #9748, #9751, #9753, #9755, #9791, #9792, #9793, #9795, #9796, #9797, #9798, #9920, #9972, #9973, #9974, #9975, #9977, #9978, #9979, #9984, #9986, #9987, #9988, #9989, #9990, #9998, #10087, #10088, #10134, #10175, #10178, #10195, #10254, #10255, #10256, #10257, #10258, #10324, #10339, #10453, #10464, #10483, #10637, #10964 |
| [@Benson-mk](https://github.com/Benson-mk) | #8369 |
| [@benzntech](https://github.com/benzntech) | #9810, #9812, #9939 |
| [@benzntech](https://github.com/benzntech) | #9810, #9812, #9939, #10124, #10126, #10458 |
| [@Bl0ck154](https://github.com/Bl0ck154) | #9231 |
| [@blackwell-systems](https://github.com/blackwell-systems) | #10807 |
| [@blarovse](https://github.com/blarovse) | #10411 |
| [@bortolidiego](https://github.com/bortolidiego) | #10058 |
| [@branben](https://github.com/branben) | #9940 |
| [@Chewji9875](https://github.com/Chewji9875) | #9257, #9420, #9821, #9994, #10160 |
| [@chirag127](https://github.com/chirag127) | #6674 |
| [@branben](https://github.com/branben) | #9940, #10575 |
| [@Chewji9875](https://github.com/Chewji9875) | #9257, #9420, #9821, #9994, #10116, #10160, #10305, #10376 |
| [@chirag127](https://github.com/chirag127) | #6674, #10071 |
| [@chloeassistant](https://github.com/chloeassistant) | #9675, #9746 |
| [@configurowebmax](https://github.com/configurowebmax) | #8877 |
| [@corefusiion](https://github.com/corefusiion) | #8285 |
| [@costaeder](https://github.com/costaeder) | #8626, #8629, #8630 |
| [@cryptiklemur](https://github.com/cryptiklemur) | #10684, #10685 |
| [@csoftware-arigpt](https://github.com/csoftware-arigpt) | #3440 |
| [@DaDecky](https://github.com/DaDecky) | direct commit / report |
| [@danscMax](https://github.com/danscMax) | #8634 |
| [@DarkEsteves](https://github.com/DarkEsteves) | #10250 |
| [@dcox79](https://github.com/dcox79) | #10543 |
| [@ddarkr](https://github.com/ddarkr) | #9035, #9036, #10177 |
| [@diegosouzapw](https://github.com/diegosouzapw) | maintainer |
| [@Dingding-leo](https://github.com/Dingding-leo) | #7987, #8640, #8678, #8704, #8774, #8790, #8808, #8817 |
| [@DinonowDev](https://github.com/DinonowDev) | #8804 |
| [@dionjoshualobo](https://github.com/dionjoshualobo) | #9721 |
| [@dpozimski](https://github.com/dpozimski) | #10253 |
| [@Dragost](https://github.com/Dragost) | #8339 |
| [@dsitmilis](https://github.com/dsitmilis) | direct commit / report |
| [@Egorich-print](https://github.com/Egorich-print) | #9001, #9020, #9058 |
| [@echoriver89](https://github.com/echoriver89) | #10717 |
| [@Egorich-print](https://github.com/Egorich-print) | #9001, #9020, #9058, #10881, #10887, #11097 |
| [@ekinnee](https://github.com/ekinnee) | #11307 |
| [@electrumguy](https://github.com/electrumguy) | #10774, #11149 |
| [@engmarcosjr](https://github.com/engmarcosjr) | #9993 |
| [@epsilonode](https://github.com/epsilonode) | #8871 |
| [@ervareza](https://github.com/ervareza) | direct commit / report |
| [@excessivechaos](https://github.com/excessivechaos) | #10062, #10138 |
| [@excessivechaos](https://github.com/excessivechaos) | #10062, #10138, #10552, #10883, #10884, #10907, #11179 |
| [@fajarhide](https://github.com/fajarhide) | #9191, #9198 |
| [@fenix007](https://github.com/fenix007) | #9618 |
| [@farshidrezaei](https://github.com/farshidrezaei) | #10777 |
| [@fenix007](https://github.com/fenix007) | #9618, #10016 |
| [@freudantunes](https://github.com/freudantunes) | #10623 |
| [@Gecky2102](https://github.com/Gecky2102) | #9280 |
| [@ggdayup](https://github.com/ggdayup) | #10199 |
| [@geek007git](https://github.com/geek007git) | #10441 |
| [@ggdayup](https://github.com/ggdayup) | #10199, #11113, #11114, #11375 |
| [@Gi99lin](https://github.com/Gi99lin) | #10342 |
| [@giauphan](https://github.com/giauphan) | #10392 |
| [@Gioxaa](https://github.com/Gioxaa) | #9162, #9171 |
| [@HaoNgo232](https://github.com/HaoNgo232) | direct commit / report |
| [@Hariprajwal](https://github.com/Hariprajwal) | #9922 |
| [@hartmark](https://github.com/hartmark) | #9635, #9704, #9711, #9712, #9727, #9734, #9735, #9738, #9741, #9744, #9745, #9822, #10025, #10034, #10037, #10038, #10041, #10121, #10217 |
| [@harkaranbrar7](https://github.com/harkaranbrar7) | #10281 |
| [@hartmark](https://github.com/hartmark) | #9635, #9704, #9711, #9712, #9727, #9734, #9735, #9738, #9741, #9744, #9745, #9822, #10025, #10034, #10037, #10038, #10041, #10121, #10217, #10262, #10263, #10330, #10331, #11259 |
| [@Hdiaktoros](https://github.com/Hdiaktoros) | #8930 |
| [@HectorBernstorff](https://github.com/HectorBernstorff) | direct commit / report |
| [@HellFiveOsborn](https://github.com/HellFiveOsborn) | #9248 |
| [@herjarsa](https://github.com/herjarsa) | #9714, #9816, #9937, #9946, #10128 |
| [@herjarsa](https://github.com/herjarsa) | #9714, #9816, #9937, #9946, #10128, #10456 |
| [@hgaib](https://github.com/hgaib) | #10722 |
| [@horacecar](https://github.com/horacecar) | #7679 |
| [@HouMinXi](https://github.com/HouMinXi) | #8886, #8904, #8905, #8976, #8984, #9079, #9106, #9207, #9242, #9328, #9340, #9342, #9351, #9365, #9380, #9381, #9392, #9449, #9482, #9483, #9509, #9510, #9572, #9631, #9634, #9695, #9929 |
| [@HouMinXi](https://github.com/HouMinXi) | #8886, #8904, #8905, #8976, #8984, #9079, #9106, #9207, #9242, #9328, #9340, #9342, #9351, #9365, #9380, #9381, #9392, #9449, #9482, #9483, #9509, #9510, #9572, #9631, #9634, #9695, #9929, #10457, #10475, #10525, #10529, #10573, #10663, #10846, #11084, #11139, #11140, #11141, #11386, #11418 |
| [@hppsc1215](https://github.com/hppsc1215) | #8970 |
| [@hydraxman](https://github.com/hydraxman) | #10137 |
| [@Hsia97](https://github.com/Hsia97) | #10810 |
| [@hydraxman](https://github.com/hydraxman) | #10137, #10572 |
| [@Iammilansoni](https://github.com/Iammilansoni) | #9353, #9397 |
| [@ignamiranda](https://github.com/ignamiranda) | #11206 |
| [@ikelvingo](https://github.com/ikelvingo) | #8591, #8872, #9053 |
| [@infinit-X](https://github.com/infinit-X) | #9095 |
| [@InkshadeWoods](https://github.com/InkshadeWoods) | #10733 |
| [@isaaclb98](https://github.com/isaaclb98) | #9730 |
| [@jackjinke](https://github.com/jackjinke) | #9556, #9601, #10005, #10045 |
| [@jackjinke](https://github.com/jackjinke) | #9556, #9601, #10005, #10045, #10248, #10533, #10540, #10574, #11041 |
| [@jacobsparts](https://github.com/jacobsparts) | #11399, #11400, #11402 |
| [@jax-novita](https://github.com/jax-novita) | #8913 |
| [@jeff-alves](https://github.com/jeff-alves) | #10221 |
| [@jeyhunfaslanov](https://github.com/jeyhunfaslanov) | #10259 |
| [@jhordanjw123](https://github.com/jhordanjw123) | #8736 |
| [@jktan0504](https://github.com/jktan0504) | #9025 |
| [@joachimBrindeau](https://github.com/joachimBrindeau) | #9200 |
| [@jonlwheat2-gif](https://github.com/jonlwheat2-gif) | #10610, #10709, #11194 |
| [@JoshimOfficial](https://github.com/JoshimOfficial) | #9011 |
| [@jowimila](https://github.com/jowimila) | #9325 |
| [@JxnLexn](https://github.com/JxnLexn) | #8933, #8940, #8944, #8949 |
| [@JxnLexn](https://github.com/JxnLexn) | #8933, #8940, #8944, #8949, #10422, #10608 |
| [@Kaedo17](https://github.com/Kaedo17) | #8922 |
| [@KaspaPulse](https://github.com/KaspaPulse) | #10362 |
| [@khoazero123](https://github.com/khoazero123) | #9272 |
| [@killmonger2317-coder](https://github.com/killmonger2317-coder) | #10445 |
| [@KittisakT](https://github.com/KittisakT) | #9423 |
| [@Kizuno18](https://github.com/Kizuno18) | #10803 |
| [@KooshaPari](https://github.com/KooshaPari) | #7329 |
| [@kriptoburak](https://github.com/kriptoburak) | #10854 |
| [@krishna3554](https://github.com/krishna3554) | #10620, #10855 |
| [@lamchun1110](https://github.com/lamchun1110) | #10372, #10397 |
| [@larin-vas](https://github.com/larin-vas) | #9828 |
| [@lazysaltyfish](https://github.com/lazysaltyfish) | direct commit / report |
| [@LeonG606](https://github.com/LeonG606) | #9457 |
| [@linhdmn](https://github.com/linhdmn) | #10980, #11085, #11214, #11274 |
| [@Llliao1113](https://github.com/Llliao1113) | #8921 |
| [@lucasalx](https://github.com/lucasalx) | #9919 |
| [@lucasmellos](https://github.com/lucasmellos) | #8925 |
| [@lukiod](https://github.com/lukiod) | #8828 |
| [@luoyide](https://github.com/luoyide) | direct commit / report |
| [@maci0](https://github.com/maci0) | #11279 |
| [@mad-gooze](https://github.com/mad-gooze) | #9052 |
| [@maisdesign](https://github.com/maisdesign) | #8858 |
| [@marcelokarval](https://github.com/marcelokarval) | #11397 |
| [@marchlhw](https://github.com/marchlhw) | #9050 |
| [@marcs7](https://github.com/marcs7) | #11180 |
| [@matiasbaglieri](https://github.com/matiasbaglieri) | #9707 |
| [@maxmad64bis](https://github.com/maxmad64bis) | #9150, #9246, #9291, #9414 |
| [@maxmad64bis](https://github.com/maxmad64bis) | #9150, #9246, #9291, #9414, #10278, #10402, #10652, #10662, #10664, #10694, #10769, #10770, #10779, #10876, #10885, #10974, #10978, #11008, #11009, #11047, #11116, #11129, #11130, #11133, #11151, #11162, #11252 |
| [@McLuck](https://github.com/McLuck) | #8914 |
| [@megamen32](https://github.com/megamen32) | #10184 |
| [@MeRezaRezaei](https://github.com/MeRezaRezaei) | #10174, #10614, #10944, #11042, #11045 |
| [@Michael-Rocco-Goldmann](https://github.com/Michael-Rocco-Goldmann) | #9770, #9773, #9777, #9787 |
| [@MichaelYcJo](https://github.com/MichaelYcJo) | #8244 |
| [@MichaelYcJo](https://github.com/MichaelYcJo) | #8244, #10725, #10726 |
| [@Minamaged18](https://github.com/Minamaged18) | #11269 |
| [@minhlongs](https://github.com/minhlongs) | #10805 |
| [@minhnhat166](https://github.com/minhnhat166) | direct commit / report |
| [@MohitRawat017](https://github.com/MohitRawat017) | #8718, #8772, #9605 |
| [@Momen4444](https://github.com/Momen4444) | #9612 |
@@ -901,64 +1513,103 @@ Thanks to everyone whose work landed in v3.8.50:
| [@mtb-ninja](https://github.com/mtb-ninja) | #10114 |
| [@MumuTW](https://github.com/MumuTW) | #8839 |
| [@mvanhorn](https://github.com/mvanhorn) | #9542 |
| [@mymusicmyspace](https://github.com/mymusicmyspace) | #10965 |
| [@Mynacol](https://github.com/Mynacol) | #9733 |
| [@NahuSaruf](https://github.com/NahuSaruf) | #10545 |
| [@Neuron-Mr-White](https://github.com/Neuron-Mr-White) | #10228, #10230, #10957, #11378 |
| [@nguyenha935](https://github.com/nguyenha935) | #9044, #9215 |
| [@nordz0r](https://github.com/nordz0r) | #10170 |
| [@nosolosoft](https://github.com/nosolosoft) | #8900 |
| [@oyi77](https://github.com/oyi77) | #8299, #8752, #9158, #9818 |
| [@pacocartones](https://github.com/pacocartones) | #10216 |
| [@ntdat812](https://github.com/ntdat812) | #10843, #10853, #10857, #10858, #10860, #10862, #10868, #10935, #11004, #11374, #11376, #11377 |
| [@ntdatt812](https://github.com/ntdatt812) | #10715, #11076 |
| [@octo-patch](https://github.com/octo-patch) | #10650 |
| [@ofonseca-pyming](https://github.com/ofonseca-pyming) | #11297 |
| [@oyi77](https://github.com/oyi77) | #8299, #8752, #9158, #9818, #10910, #10942 |
| [@pacocartones](https://github.com/pacocartones) | #10216, #10673, #11059, #11193, #11199, #11238, #11241, #11321, #11322, #11332, #11338 |
| [@pandaaaa1990](https://github.com/pandaaaa1990) | #10731 |
| [@phatchau036](https://github.com/phatchau036) | #10517 |
| [@phuongddx](https://github.com/phuongddx) | #11415 |
| [@PixmaNts](https://github.com/PixmaNts) | #9432 |
| [@pizzav-xyz](https://github.com/pizzav-xyz) | #9077 |
| [@Poid-ZA](https://github.com/Poid-ZA) | #9467 |
| [@Prudhvivuda](https://github.com/Prudhvivuda) | #8807, #9014, #9015, #9016 |
| [@pucedoteth](https://github.com/pucedoteth) | #10607 |
| [@qianze0628](https://github.com/qianze0628) | #9038 |
| [@rafacpti23](https://github.com/rafacpti23) | #11207, #11213 |
| [@raflyazf](https://github.com/raflyazf) | direct commit / report |
| [@Rahulsharma0810](https://github.com/Rahulsharma0810) | #8961 |
| [@rinseaid](https://github.com/rinseaid) | #8945, #9037, #9932, #9933, #9969, #9982 |
| [@Rahulsharma0810](https://github.com/Rahulsharma0810) | #8961, #10872 |
| [@RaviTharuma](https://github.com/RaviTharuma) | #10055, #10297, #10307, #10344, #10352, #10488, #10565, #10566, #10568, #10584, #10771, #10814, #10818, #10820, #10821, #10822, #10827, #10847, #10971, #10979, #10981, #11014, #11015, #11016, #11017, #11020, #11024, #11314, #11318, #11320 |
| [@realize000](https://github.com/realize000) | #10490 |
| [@redzrush101](https://github.com/redzrush101) | #10492 |
| [@rengaryang](https://github.com/rengaryang) | #11333 |
| [@rinseaid](https://github.com/rinseaid) | #8945, #9037, #9932, #9933, #9969, #9982, #10554 |
| [@ritheshcn25](https://github.com/ritheshcn25) | #10026 |
| [@rixzkiye](https://github.com/rixzkiye) | direct commit / report |
| [@rizxfrog](https://github.com/rizxfrog) | #10356, #10546 |
| [@RobertsXML](https://github.com/RobertsXML) | direct commit / report |
| [@royanrosyad85](https://github.com/royanrosyad85) | direct commit / report |
| [@rqzbeh](https://github.com/rqzbeh) | #10415, #10420, #10424, #10430, #10465, #10470, #10890, #10894, #10898, #10899, #10901, #11039, #11054, #11055, #11056, #11078, #11117, #11123, #11125, #11132, #11155, #11161, #11177, #11182, #11189, #11262 |
| [@rushsinging](https://github.com/rushsinging) | #8947 |
| [@ryan-brosas](https://github.com/ryan-brosas) | #9693 |
| [@ryanngit](https://github.com/ryanngit) | direct commit / report |
| [@sadSanta-07](https://github.com/sadSanta-07) | #9938 |
| [@sadSanta-07](https://github.com/sadSanta-07) | #9938, #10209, #10605, #10772 |
| [@SalyyS1](https://github.com/SalyyS1) | direct commit / report |
| [@Sam280903](https://github.com/Sam280903) | #9274, #9278, #9281, #9283, #9448 |
| [@sanforex24h](https://github.com/sanforex24h) | #11026 |
| [@SCys](https://github.com/SCys) | #11107 |
| [@seakleangnhak](https://github.com/seakleangnhak) | direct commit / report |
| [@seanford](https://github.com/seanford) | #8523 |
| [@SemonCat](https://github.com/SemonCat) | direct commit / report |
| [@sha367](https://github.com/sha367) | #10471 |
| [@shixi-li](https://github.com/shixi-li) | #9022, #9513, #10001 |
| [@Siva010](https://github.com/Siva010) | #10658 |
| [@SnCr90](https://github.com/SnCr90) | #10534 |
| [@soulhakr](https://github.com/soulhakr) | #8799 |
| [@stanleytejakusuma](https://github.com/stanleytejakusuma) | #9610 |
| [@sprintberlin](https://github.com/sprintberlin) | #11353, #11355, #11360 |
| [@stanleytejakusuma](https://github.com/stanleytejakusuma) | #9610, #10636, #10660, #10730, #11337 |
| [@Stazyu](https://github.com/Stazyu) | #9007, #9226, #9438 |
| [@SupremeNexas](https://github.com/SupremeNexas) | #9913 |
| [@swingtempo](https://github.com/swingtempo) | #9307 |
| [@swingtempo](https://github.com/swingtempo) | #9307, #10354 |
| [@szzhoujiarui](https://github.com/szzhoujiarui) | #9218 |
| [@tald26](https://github.com/tald26) | #9959 |
| [@taltas](https://github.com/taltas) | direct commit / report |
| [@TechNickAI](https://github.com/TechNickAI) | #9251 |
| [@TechNickAI](https://github.com/TechNickAI) | #9251, #10558 |
| [@TengSivtean](https://github.com/TengSivtean) | #10000, #10002, #10086 |
| [@TheDemonTuan](https://github.com/TheDemonTuan) | #11364 |
| [@TheFrenchGhosty](https://github.com/TheFrenchGhosty) | #9326 |
| [@tiangao88](https://github.com/tiangao88) | #10046 |
| [@tiangao88](https://github.com/tiangao88) | #10046, #10363 |
| [@tientien17](https://github.com/tientien17) | #10798 |
| [@tito13kfm](https://github.com/tito13kfm) | #10227 |
| [@tkgo11](https://github.com/tkgo11) | #10370, #10371 |
| [@tuandinh0801](https://github.com/tuandinh0801) | #10804, #10830, #11230 |
| [@Tushar49](https://github.com/Tushar49) | #10186 |
| [@tuxmonteiro](https://github.com/tuxmonteiro) | #9065 |
| [@vinogradovnet](https://github.com/vinogradovnet) | #9581 |
| [@VXNCXNX](https://github.com/VXNCXNX) | #9111, #9783 |
| [@wgordon17](https://github.com/wgordon17) | #8909, #9233, #9441, #9619 |
| [@Witroch4](https://github.com/Witroch4) | #8713 |
| [@witt3rd](https://github.com/witt3rd) | #9962, #9963 |
| [@wpec](https://github.com/wpec) | #10839 |
| [@XDayonline](https://github.com/XDayonline) | #10053 |
| [@xiaoyaner0201](https://github.com/xiaoyaner0201) | #8757, #8869, #8876, #8883, #8906, #8931, #9021, #9027, #9452, #9042, #9316 |
| [@xz-dev](https://github.com/xz-dev) | #8908, #9199, #9205, #9262, #9290, #9313, #9555, #9569, #9629, #9788, #9983, #10079, #10243 |
| [@yansigit](https://github.com/yansigit) | #9834, #9911, #9917, #9921, #10065 |
| [@xiaoyaner0201](https://github.com/xiaoyaner0201) | #8757, #8869, #8876, #8883, #8906, #8931, #9021, #9027, #9042, #9316, #9452, #10468 |
| [@xyzs996](https://github.com/xyzs996) | #11210 |
| [@xz-dev](https://github.com/xz-dev) | #8367, #8908, #9199, #9205, #9262, #9290, #9313, #9555, #9569, #9629, #9788, #9983, #10066, #10072, #10079, #10162, #10243, #10247, #10437, #10712, #10716, #10723, #10806, #10953 |
| [@yansigit](https://github.com/yansigit) | #9834, #9909, #9911, #9917, #9921, #10065 |
| [@yidecode](https://github.com/yidecode) | direct commit / report |
| [@yourspraveen](https://github.com/yourspraveen) | #11075, #11088, #11165, #11271 |
| [@yulinlina](https://github.com/yulinlina) | #10013 |
| [@YunyunZhai](https://github.com/YunyunZhai) | #10946, #10960 |
| [@yutuknown](https://github.com/yutuknown) | #8999 |
| [@zabrodschiipavel-sketch](https://github.com/zabrodschiipavel-sketch) | #9312 |
| [@Zartharas](https://github.com/Zartharas) | #9161, #9164, #9181, #9182, #9184, #9185, #9186, #9189, #9294, #9825, #9833, #9936, #9965, #9992, #10218 |
| [@zannen7](https://github.com/zannen7) | #10077 |
| [@Zartharas](https://github.com/Zartharas) | #9161, #9164, #9181, #9182, #9184, #9185, #9186, #9189, #9294, #9825, #9833, #9936, #9965, #9992, #10202, #10218, #10272, #10329, #10518, #10519, #10521, #10799, #10873, #10878 |
| [@Zenlyte](https://github.com/Zenlyte) | #9005 |
| [@zhiru](https://github.com/zhiru) | #9099, #9101 |
| [@ziuus](https://github.com/ziuus) | #8912 |
| [@ziuus](https://github.com/ziuus) | #8912, #11372 |
| [@zmf963](https://github.com/zmf963) | direct commit / report |
| [@zoser69](https://github.com/zoser69) | #10874 |
| [@zuckdorsey](https://github.com/zuckdorsey) | #9723 |
| [@diegosouzapw](https://github.com/diegosouzapw) | maintainer |
---

View File

@@ -181,10 +181,23 @@ ENV NODE_OPTIONS="--max-old-space-size=${OMNIROUTE_BUILD_MEMORY_MB}"
# workers for page-data collection (31 on a 32-core builder); on memory-tight
# hosts 31 workers + webpack's multi-GB heap blow past RAM and a worker dies
# with SIGSEGV at teardown ("worker exited with code: null and signal: SIGSEGV"),
# silently leaving no standalone bundle. Next derives the default worker count
# from CIRCLE_NODE_TOTAL (workers = N-1), so N=8 → 7 workers: fast enough while
# fitting comfortably in RAM on any host. (#10060)
ENV CIRCLE_NODE_TOTAL=8
# silently leaving no standalone bundle. Next derives the worker count from
# CIRCLE_NODE_TOTAL (workers = N-1). (#10060)
#
# Lowered 8 → 3 (7 workers → 2). Every page-data worker inherits NODE_OPTIONS
# above, so the ceiling is per PROCESS, not per build: 7 workers on a 16 GB
# GitHub runner (ubuntu-24.04 / ubuntu-24.04-arm, 4 vCPU) exhausted the host and
# buildkit failed the whole step with `ResourceExhausted: ... cannot allocate
# memory`. The compile phase always finished ("✓ Compiled successfully in
# 4.2min"); the kernel killed the build right after "Collecting page data using
# 7 workers". It was intermittent for a while and went 100% on 2026-08-22, which
# is what a threshold being crossed by ordinary codebase growth looks like.
# tests/unit/docker-build-memory-budget.test.ts does the arithmetic and fails if
# either knob is raised past what a 16 GB runner holds. 2 workers also stops
# oversubscribing the runner's 4 vCPU, which 7 did. Override for a big builder:
# `--build-arg OMNIROUTE_BUILD_WORKERS=8`.
ARG OMNIROUTE_BUILD_WORKERS=3
ENV CIRCLE_NODE_TOTAL=${OMNIROUTE_BUILD_WORKERS}
COPY . ./
RUN --mount=type=cache,id=s/92ca8a61-c1ba-421f-a389-d48ac7258c2d-next-cache,target=/app/.build/next/cache \

411
README.md
View File

@@ -7,7 +7,7 @@
# 🚀 OmniRoute — The Free AI Gateway
<img src="./docs/diagrams/readme-hero.svg" width="100%" alt="OmniRoute — Never stop coding. Every AI tool → 350 providers — 90+ free — through one endpoint. Claude Code, Codex, Cursor, Cline, Copilot & Antigravity into FREE Claude / GPT / Gemini with auto-fallback. RTK + Caveman stacked compression saves 1595% tokens (~89% avg) — never hit limits. 350 AI providers · 90+ free tiers · ~1.51B free tokens/mo · 19 routing strategies · $0 to start."/>
<img src="./docs/diagrams/readme-hero.svg" width="100%" alt="OmniRoute — Never stop coding. Every AI tool → 352 providers — 90+ free — through one endpoint. Claude Code, Codex, Cursor, Cline, Copilot & Antigravity into FREE Claude / GPT / Gemini with auto-fallback. RTK + Caveman stacked compression saves 1595% tokens (~89% avg) — never hit limits. 352 AI providers · 90+ free tiers · ~1.51B free tokens/mo · 19 routing strategies · $0 to start."/>
</div>
@@ -17,9 +17,9 @@
</div>
> Stacking free tiers by hand is painful — dozens of SDKs, dozens of rate limits, and no idea how much you actually have. OmniRoute aggregates the **documented** free tiers of **42 provider pools / 495 models** into one honest number and shows it live on the dashboard (`/dashboard/free-tiers`).
> Stacking free tiers by hand is painful — dozens of SDKs, dozens of rate limits, and no idea how much you actually have. OmniRoute catalogs **455 free-tier entries across 40 recurring pool keys** and computes the token headline from the **20 pools with a published positive monthly budget**, deduplicated by shared pool. The result stays visible on the dashboard (`/dashboard/free-tiers`).
<img src="./docs/diagrams/free-tier-budget.svg" width="100%" alt="OmniRoute free-tier budget card: ~1.51B free tokens per month steady, up to ~2.13B in the first month with signup credits, from the documented free tiers of 42 provider pools / 495 models behind one endpoint. Honest pool-deduped math — each shared pool counted once (counting every rate limit 24/7 would read ~10B; not published), 15 providers ToS-flagged so you decide. Budget bar of the countable free pools with per-model grid (Mistral Large 3 1B, GPT-4o mini 150M, Gemini 2.5 Flash 60M … Claude Sonnet 4.5 25K), one-time first-month signup credits (vertex 300M, agentrouter 200M, predibase 25M, together 25M, glm-cn 20M, doubao 15M, ai21 10M, longcat 10M, deepseek 5M, hyperbolic 5M, nscale 5M), plus permanently-free no-token-cap providers (SiliconFlow, Z.AI GLM-Flash, Kilo, OpenCode Zen, baidu …) and a $10 OpenRouter top-up unlocking +24M/mo — surfaced separately so they never inflate the headline. Live used/remaining on /dashboard/free-tiers."/>
<img src="./docs/diagrams/free-tier-budget.svg" width="100%" alt="OmniRoute free-tier budget card: ~1.51B free tokens per month steady, up to ~2.13B in the first month with signup credits, from 40 documented recurring pool keys covering 455 cataloged free-tier entries behind one endpoint. Honest pool-deduped math — each shared pool counted once, including 20 recurring pools with a published positive monthly token budget; 15 providers are marked avoid in the terms-risk catalog so you decide. Budget bar includes Mistral 1B, LLM7 150M, Nara 150M, Gemini 60M and smaller pools, plus first-month signup credits and permanently-free no-token-cap providers surfaced separately so they never inflate the headline. Live used/remaining on /dashboard/free-tiers."/>
> Animated summary of the live `/dashboard/free-tiers` page. Full methodology (pool dedupe, credit tiers, provider terms): **[docs/reference/FREE_TIERS.md](docs/reference/FREE_TIERS.md)**.
>
@@ -61,14 +61,14 @@
<div align="center">
| | v3.8.49 | **v3.8.50** | `v3.8.51+` |
| ------------------------- | :-----: | :---------: | :---------: |
| 🌐 Providers | 290 | **342** | more queued |
| 🧠 Documented models | 1185 | **1202** | — |
| 🖼️ Modality Bridge | — | 🆕 vision | video |
| 📡 Radar free catalog | — | 🆕 opt-in | — |
| ⚖️ Quota-aware scheduling | — | | 🔭 next |
| 📊 Quota telemetry | — | | 🔭 next |
| | v3.8.49 | **v3.8.50** | `v3.8.51+` |
| ------------------------- | :-----: | :-----------------------: | :---------: |
| 🌐 Providers | 290 | **352** | more queued |
| 🧠 Unique chat model IDs | 1185 | **1312** | — |
| 🖼️ Modality Bridge | — | 🆕 vision + audio + video | — |
| 📡 Radar free catalog | — | 🆕 opt-in | — |
| ⚖️ Quota-aware scheduling | — | 🆕 Quota-Share | |
| 📊 Quota telemetry | — | 🆕 live | |
**→ [Roadmap](ROADMAP.md) — riding the rail to `v3.9.0 LTS`**
@@ -101,7 +101,7 @@
<tr>
<td align="right"><b>⚙️ Features</b></td>
<td align="center"><a href="#-combos--the-flagship">🎯 Combos</a></td>
<td align="center"><a href="#-349-ai-providers--90-free">🌐 Providers</a></td>
<td align="center"><a href="#-352-ai-providers--154-catalog-marked-free">🌐 Providers</a></td>
<td align="center"><a href="#-full-cli--a2a--mcp">🔌 CLI &amp; MCP</a></td>
</tr>
<tr>
@@ -126,7 +126,7 @@
<td align="right"><b>📦 Project</b></td>
<td align="center"><a href="#%EF%B8%8F-tech-stack">🛠️ Tech Stack</a></td>
<td align="center"><a href="#-documentation">📖 Docs</a></td>
<td align="center"><a href="#-500-contributors">👥 Contributors</a></td>
<td align="center"><a href="#-600-contributors">👥 Contributors</a></td>
</tr>
</table>
@@ -210,7 +210,7 @@ curl http://localhost:20128/v1/chat/completions \
</div>
<img src="./docs/diagrams/promise-pillars.svg" width="100%" alt="The Promise — One endpoint. 350 providers. Never stop building — OmniRoute picks the cheapest one that works. Six pillars: Never hit limits (auto-fallback across 350 providers in milliseconds, zero downtime) · Save up to 95% tokens (RTK + Caveman stacked compression cuts 1595%, ~89% avg on tool-heavy sessions) · $0 to start (90+ free tiers, 56 free forever — no card needed) · Every tool works (33 coding agents through one config) · One endpoint (OpenAI Claude Gemini Responses API at /v1) · Production-grade (circuit breakers, TLS stealth, MCP 110 tools, A2A, memory, guardrails, evals — 25,000+ tests)."/>
<img src="./docs/diagrams/promise-pillars.svg" width="100%" alt="The Promise — One endpoint and 352 providers. Automatic fallback keeps routing while another healthy target is available. Six pillars: resilient fallback across 352 providers · up to 95% token savings on eligible workloads · $0 to start with 90+ free tiers and 56 recurring/keyless free-forever providers · 35 CLI/agent integrations through one config · OpenAI, Claude, Gemini and Responses API compatibility at /v1 · production controls including circuit breakers, TLS stealth, MCP 110 tools, A2A, memory, guardrails, evals and 39,000+ static test declarations across 5,100+ tracked test files."/>
<br/>
<br/>
@@ -225,7 +225,7 @@ curl http://localhost:20128/v1/chat/completions \
<div align="center">
<img src="./docs/diagrams/tier-cascade.svg" width="100%" alt="OmniRoute request flow: your IDE or CLI (Claude Code, Cursor, Cline…) calls one local endpoint (http://localhost:20128/v1); the OmniRoute Smart Router (RTK + Caveman compression, 19 routing strategies, circuit breakers, TLS stealth, MCP, A2A, guardrails) auto-falls back across 4 provider tiers — Tier 1 Subscription (Claude Code, Codex, Copilot), quota out? Tier 2 API Key (DeepSeek, Groq, xAI), budget hit? Tier 3 Cheap (GLM $0.5, MiniMax $0.2), budget hit? Tier 4 Free (Kiro, Qoder, Pollinations) — always on."/>
<img src="./docs/diagrams/tier-cascade.svg" width="100%" alt="OmniRoute request flow: your IDE or CLI (Claude Code, Cursor, Cline…) calls one local endpoint (http://localhost:20128/v1); the OmniRoute Smart Router (RTK + Caveman compression, 19 routing strategies, circuit breakers, TLS stealth, MCP, A2A, guardrails) can fall back across 4 provider tiers while an eligible healthy target remains — Tier 1 Subscription, Tier 2 API Key, Tier 3 Cheap and Tier 4 Free."/>
</div>
@@ -318,7 +318,7 @@ curl http://localhost:20128/v1/chat/completions \
<img src="./docs/diagrams/strategies-grid.svg" width="100%" alt="All 19 combo routing strategies animated — one tile per strategy: priority, fill-first, weighted, round-robin, p2c, least-used, random, strict-random, cost-optimized, headroom, reset-window, reset-aware, context-relay, context-optimized, cache-optimized, lkgp, auto, fusion, pipeline. See the table above for what each one does."/>
> A **combo** is a chain of models OmniRoute routes across **automatically**. Quota runs out, a provider fails, or costs spike the combo silently slides to the next model. **This is what makes OmniRoute unbreakable.** 🛡️
> A **combo** is a chain of models OmniRoute routes across **automatically**. If quota runs out, a provider fails, or costs spike, the combo can move to the next eligible healthy model. 🛡️
### ⚡ Zero-config — just use `auto`
@@ -429,7 +429,7 @@ All **19** strategies — mix & match per combo step:
<tr>
<td align="center">17</td>
<td nowrap><code>auto</code></td>
<td>14-factor live scoring across every connection 🤖</td>
<td>15-factor live scoring across every connection 🤖</td>
</tr>
<tr>
<td align="center">18</td>
@@ -443,7 +443,7 @@ All **19** strategies — mix & match per combo step:
</tr>
</table>
<sub>The Auto-Combo engine scores every candidate on **14 factors** (health, quota, cost, latency, success rate, freshness…) — see [`docs/routing/AUTO-COMBO.md`](docs/routing/AUTO-COMBO.md).</sub>
<sub>The Auto-Combo engine scores every candidate on **15 factors** (health, quota, cost, latency, task fit, quality, session availability…) — see [`docs/routing/AUTO-COMBO.md`](docs/routing/AUTO-COMBO.md).</sub>
##
@@ -461,7 +461,7 @@ All **19** strategies — mix & match per combo step:
</div>
<img src="./docs/diagrams/comparison-table.svg" width="100%" alt="What sets OmniRoute apart — comparison table vs 9router, OpenRouter, CLIProxyAPI and LiteLLM across 13 capabilities. OmniRoute: 350 providers, 90+ free providers built-in, 19 routing strategies, 12-engine token compression, built-in MCP server with 110 tools, A2A agent protocol, persistent memory, guardrails, cloud agents, TLS fingerprint stealth, Desktop/Termux/PWA, 43 i18n UI locales, 100% MIT self-hosted. OmniRoute is the only one with the full set; competitors show a mix of checks, partials and crosses. Verified from each project&apos;s docs."/>
<img src="./docs/diagrams/comparison-table.svg" width="100%" alt="What sets OmniRoute apart — a dated feature snapshot vs 9router, OpenRouter, CLIProxyAPI and LiteLLM across 13 capabilities. OmniRoute: 352 providers, 90+ free tiers built in, 19 routing strategies, 12-engine token compression, built-in MCP server with 110 tools, A2A agent protocol, persistent memory, guardrails, cloud agents, TLS fingerprint stealth, Desktop/Termux/PWA and 43 i18n UI locales. OmniRoute is MIT-licensed and self-hostable. Competitor capabilities and counts may change; see the linked methodology."/>
<sub>📊 Full methodology &amp; per-feature detail vs 9router, OpenRouter, CLIProxyAPI &amp; LiteLLM → [`docs/comparison/OMNIROUTE_VS_ALTERNATIVES.md`](docs/comparison/OMNIROUTE_VS_ALTERNATIVES.md)</sub>
@@ -517,9 +517,9 @@ Pix copia-e-cola:
## 📡 OmniRoute Radar
The main free-tier headline remains **~1.53B tokens/month** from the documented,
The main free-tier headline remains **~1.51B tokens/month** from the documented,
pool-deduplicated catalog above. Temporary provider signup credits can separately lift the first
month to **~2.15B**. Radar is an optional, signed catalog overlay for people who want fresher
month to **~2.13B**. Radar is an optional, signed catalog overlay for people who want fresher
free-model availability between OmniRoute releases; the community catalog and every existing free
feature remain free.
@@ -548,7 +548,7 @@ the current catalog at **[radar.omniroute.online/planos](https://radar.omniroute
- **🗜️ Compression hardening** — default-on inflation guard, Caveman packs for DE / FR / JA + Chinese (wényán), RTK filters for Gradle & .NET. → [Compression](docs/compression/COMPRESSION_ENGINES.md)
- **💸 Honest flat-rate cost** — subscription / coding-plan providers read **$0** in cost analytics; budget, quota & routing keep estimating. → [API Reference](docs/reference/API_REFERENCE.md)
- **⚖️ Quota-Share routing** — split a shared account's quota fairly across pooled keys, work-conserving so idle slices are lent out. → [Resilience Guide](docs/architecture/RESILIENCE_GUIDE.md)
- **🤖 One-command CLI/agent setup** — `setup-*` configures 12+ coding tools; `omniroute run` launches 7 CLIs (Claude Code, Codex, Aider, Goose, OpenCode, Qwen Code, Gemini CLI) with zero config written; `omniroute configure` is an interactive provider+model picker with per-context favorites. → [CLI Integrations](docs/guides/CLI-INTEGRATIONS.md)
- **🤖 One-command CLI/agent setup** — 12 registered `setup-*` commands; `omniroute run` launches 7 CLIs (Claude Code, Codex, Aider, Goose, OpenCode, Qwen Code, Gemini CLI); `omniroute configure` supports 9 targets with an interactive provider+model picker and per-context favorites. → [CLI Integrations](docs/guides/CLI-INTEGRATIONS.md)
- **🛰️ Remote mode** — drive a remote OmniRoute with scoped tokens (`connect` / `contexts` / `tokens`) + an `antigravity` OAuth helper for VPS installs. → [Remote Mode](docs/guides/REMOTE-MODE.md)
- **🧭 Smarter auto-routing** — `auto/<category>:<tier>` combos, **Fusion** (model panel + judge), task-aware routing, per-request model / mode / USD-budget overrides. → [Auto-Combo](docs/routing/AUTO-COMBO.md)
- **🗜️ Pluggable compression** — 12 composable engines + Compression Studios: LLMLingua-2, two-tier Ultra, omniglyph, per-step fidelity gate, GCF v3.2, drag-reorder editor. → [Compression](docs/compression/COMPRESSION_ENGINES.md)
@@ -559,7 +559,7 @@ the current catalog at **[radar.omniroute.online/planos](https://radar.omniroute
- **🖼️ New endpoints** — `/v1/ocr` (Mistral OCR) and `/v1/audio/translations` (Whisper-style) round out the media surface. → [API Reference](docs/reference/API_REFERENCE.md)
- **🎨 Image / video / audio generation** — one API for media: xAI Grok Imagine & Novita AI video, ComfyUI, Freepik, Adobe Firefly, Microsoft Designer, Segmind, EdgeTTS. → [API Reference](docs/reference/API_REFERENCE.md)
- **🌍 Deployment & ops** — reverse-proxy `basePath`, browser-language auto-detect, per-key device tracking, root-less MITM trust, zh-TW localization. → [Environment](docs/reference/ENVIRONMENT.md)
- **🤝 More providers & agents** — Cursor Cloud Agent, Grok Build (xAI) with browser + OAuth login, Ollama first-class card, Claude Opus 5 & Sonnet 5, Kimi official partnership (Code/Web/Moonshot), Zed, Requesty, SenseNova, Yuanbao, Agnes AI… and a refreshed **350-provider catalog**. → [Providers](docs/reference/PROVIDER_REFERENCE.md)
- **🤝 More providers & agents** — Cursor Cloud Agent, Grok Build (xAI) with browser + OAuth login, Ollama first-class card, Claude Opus 5 & Sonnet 5, Kimi official partnership (Code/Web/Moonshot), Zed, Requesty, SenseNova, Yuanbao, Agnes AI… and a refreshed **352-provider catalog**. → [Providers](docs/reference/PROVIDER_REFERENCE.md)
- **📡 Routing transparency** — every response carries an `X-OmniRoute-Decision` header naming the strategy/provider/latency that served it, a new `cache-optimized` combo strategy + Auto-Combo `cacheAffinity` factor route repeat requests back to the connection holding the cached prefix, and a read-only `/v1/auto-combo/{channel}/candidates` endpoint exposes an `auto/*` channel's live candidate pool. → [Auto-Combo](docs/routing/AUTO-COMBO.md)
- **⚡ Local performance & infra** — one-click local Redis, Cloudflare Workers / Deno Deploy relay deployers, Bifrost & Mux as supervised embedded services. → [Embedded Services](docs/frameworks/EMBEDDED-SERVICES.md)
@@ -642,11 +642,11 @@ of your shell history. → [CLI Integrations](docs/guides/CLI-INTEGRATIONS.md)
<div align="center">
## 🌐 349 AI Providers — 90+ Free
## 🌐 352 AI Providers — 154 Catalog-Marked Free
</div>
> The most complete catalog of any open-source router: **350 providers**, **90+ with a free tier**, **56 free forever**.
> **352 registered providers** across the canonical chat, media, search, local, cloud-agent and system collections, including **154 carrying `hasFree: true` discovery metadata**. The chat model registry covers **268 providers / 2,566 distinct provider-model pairs / 1,312 raw model IDs**; the separate free-budget catalog has **455 per-model rows**, **40 recurring pools** and **56 recurring/keyless free-forever providers**. These are different denominators by design; definitions and pool-deduped calculations live in the [Provider Reference](docs/reference/PROVIDER_REFERENCE.md) and [Free Tiers](docs/reference/FREE_TIERS.md).
<div align="center">
@@ -679,7 +679,7 @@ of your shell history. → [CLI Integrations](docs/guides/CLI-INTEGRATIONS.md)
</tr>
</table>
<sub>…and 220+ more — every icon resolves live from the dashboard's provider catalog. 📖 [Provider Reference](docs/reference/PROVIDER_REFERENCE.md)</sub>
<sub>…and 330+ more — every icon resolves live from the dashboard's provider catalog. 📖 [Provider Reference](docs/reference/PROVIDER_REFERENCE.md)</sub>
<br/>
@@ -769,7 +769,7 @@ From inside the editor: open the **Extensions** view, search **"OmniRoute"**, cl
</div>
<img src="./docs/diagrams/privacy-local.svg" width="100%" alt="Private and local-first — your keys, your machine, your data; OmniRoute is a local proxy that never phones home. Eleven guarantees: runs 100% on your hardware (0 cloud hops), zero telemetry by default, credentials encrypted at rest (AES-256-GCM), no account or sign-up, hardened gateway (API-key scoping, IP filtering, rate limits, prompt-injection guard), loopback-only process routes, upstream header scrubbing, strictly opt-in PII redaction, sanitized errors that never leak internals, a local audit trail in your own SQLite, and MIT-licensed fully open-source code."/>
<img src="./docs/diagrams/privacy-local.svg" width="100%" alt="Private and local-first — OmniRoute's gateway and control plane run on your machine. Prompts are sent to the upstream provider selected for each request; OmniRoute adds no hosted prompt-processing hop and telemetry is disabled by default. Credentials are encrypted at rest with AES-256-GCM; controls include API-key scoping, IP filtering, rate limits, prompt-injection guards, upstream-header scrubbing, opt-in PII redaction, sanitized errors and a local SQLite audit trail. OmniRoute is MIT-licensed and self-hostable."/>
<sub>📖 [Authorization](docs/architecture/AUTHZ_GUIDE.md) · [Guardrails](docs/security/GUARDRAILS.md) · [Compliance](docs/security/COMPLIANCE.md)</sub>
@@ -810,7 +810,7 @@ Tokens are scoped `read` / `write` / `admin`; process-spawning routes stay loopb
<div align="left">
<img src="./docs/diagrams/cli-terminal.svg" width="50%" alt="Animated terminal demoing the OmniRoute CLI — omniroute providers list, omniroute combo list, omniroute health — cycling over the 80+ command surface: providers · oauth · keys · combo · nodes · models · cache · compression · cost · usage · quota · health · resilience · telemetry · logs · audit · mcp · a2a · cloud · memory · skills · eval · tunnel · backup · sync · webhooks · policy · pricing · translator · simulate …"/>
<img src="./docs/diagrams/cli-terminal.svg" width="50%" alt="Animated terminal demoing the OmniRoute CLI — omniroute providers list, omniroute combo list and omniroute health — cycling over the 85-command top-level surface: providers · oauth · keys · combo · nodes · models · cache · compression · cost · usage · quota · health · resilience · telemetry · logs · audit · mcp · a2a · cloud · memory · skills · eval · tunnel · backup · sync · webhooks · policy · pricing · translator · simulate …"/>
</div>
@@ -846,7 +846,7 @@ claude mcp add-server omniroute --type http --url http://localhost:20128/api/mcp
### 📖 How it works — pipeline, architecture & savings math
<img src="./docs/diagrams/compression-pipeline.svg" width="100%" alt="OmniRoute compression pipeline: a client request of 10,000 tokens passes through 12 stacked engines — Session-Dedup, CCR, Lite, RTK, Responses Tool Output, Headroom, Relevance, Caveman, Aggressive, LLMLingua-2, Ultra, OmniGlyph — and reaches the provider at about 1,080 tokens, up to 95% saved. Code, URLs and JSON are always preserved byte-perfect."/>
<img src="./docs/diagrams/compression-pipeline.svg" width="100%" alt="OmniRoute compression pipeline: an illustrative 10,000-token client request passes through 12 composable engines — Session-Dedup, CCR, Lite, RTK, Responses Tool Output, Headroom, Relevance, Caveman, Aggressive, LLMLingua-2, Ultra and OmniGlyph — and can reach the provider at about 1,080 tokens in the documented stacked example. Structured content is protected by preservation guards and per-step fidelity gates; explicit lossy or experimental modes may transform eligible content."/>
Default stacked combo runs `RTK → Caveman`. When both act on the same tool/context payload, savings compound:
@@ -1013,6 +1013,7 @@ Full table: [Docker Guide — runtime RAM](docs/guides/DOCKER_GUIDE.md#runtime-r
**🥟 Bun**
Standard `bun install` and global installation (`bun install -g omniroute`) are supported via Bun runtime detection:
- **Built-in `bun:sqlite`**: OmniRoute uses Bun's built-in `bun:sqlite` driver when running under Bun, falling back to `better-sqlite3` on Node.js or `sql.js`.
- **Automatic Webpack bundler selection**: Development (`bun run dev`) and production builds (`bun run build`) automatically detect Bun and disable Turbopack in favor of Webpack to prevent native V8 binding incompatibilities.
- **Dedicated Bun Dockerfile**: Multi-stage `Dockerfile.bun` for native Bun production deployments (`docker build -f Dockerfile.bun -t omniroute:bun .`).
@@ -1105,7 +1106,7 @@ same process on one port, so there is no separate CLI-only package today.
<div align="center">
<sub>Dados de cobertura social em 2026-08-17 · YT: 741 | TT: 137 | IG: 124 · Frescor (dias): YT 0 · TT 14 · IG 15</sub>
<sub>Snapshot do painel em 2026-08-24 · Catálogo bruto: YT 809 | TT 137 | IG 124 · Frescor (dias): YT 1 | TT 21 | IG 22</sub>
<table>
<tr>
@@ -1114,52 +1115,52 @@ same process on one port, so there is no separate CLI-only package today.
<img src="https://placehold.co/320x180/111827/FFFFFF?text=Instagram+Reel+%7C+nick_saraev&font=montserrat&bold=true" alt="Instagram Reel" width="300"/>
</a><br/>
<b>🎬 #1 — Instagram</b><br/>
<sub>nick_saraev — 1,628,910 views</sub>
<sub>nick_saraev — 3,042,474 views</sub>
</td>
<td align="center" width="320">
<a href="https://www.instagram.com/reel/DaSs65mMrHk/">
<img src="https://placehold.co/320x180/111827/FFFFFF?text=Instagram+Reel+%7C+theopenstack&font=montserrat&bold=true" alt="Instagram Reel — theopenstack" width="300"/>
</a><br/>
<b>🎬 #2 — Instagram</b><br/>
<sub>theopenstack — 692,419 views</sub>
</td>
<td align="center" width="320">
<a href="https://www.tiktok.com/@milesreevesai/video/7667980059189366019">
<img src="https://placehold.co/320x180/111827/FFFFFF?text=TikTok+%7C+milesreevesai&font=montserrat&bold=true" alt="TikTok — milesreevesai" width="300"/>
</a><br/>
<b>🎬 #3 — TikTok</b><br/>
<sub>milesreevesai — 620,400 views</sub>
</td>
<td align="center" width="320">
<a href="https://www.youtube.com/watch?v=QucgvbO5gsM">
<img src="https://img.youtube.com/vi/QucgvbO5gsM/maxresdefault.jpg" alt="YouTube — Vaibhav Sisinty" width="300"/>
</a><br/>
<b>🎬 #2 — YouTube</b><br/>
<sub>Vaibhav Sisinty — 373,084 views</sub>
<b>🎬 #4 — YouTube</b><br/>
<sub>Vaibhav Sisinty — 391,109 views</sub>
</td>
<td align="center" width="320">
<a href="https://www.youtube.com/shorts/fZIBK_4fKq8">
<img src="https://img.youtube.com/vi/fZIBK_4fKq8/maxresdefault.jpg" alt="YouTube Shorts" width="300"/>
<a href="https://www.instagram.com/reel/DbIt9AjK7-U/">
<img src="https://placehold.co/320x180/111827/FFFFFF?text=Instagram+Reel+%7C+buildwithai.club&font=montserrat&bold=true" alt="Instagram Reel — buildwithai.club" width="300"/>
</a><br/>
<b>🎬 #3YouTube Shorts</b><br/>
<sub>Nick Automates — 207,714 views</sub>
</td>
<td align="center" width="320">
<a href="https://www.tiktok.com/@milesreevesai/video/7667980059189366019">
<img src="https://placehold.co/320x180/111827/FFFFFF?text=TikTok+Top+1&font=montserrat&bold=true" alt="TikTok Thumbnail" width="300"/>
</a><br/>
<b>🎬 #4 — TikTok</b><br/>
<sub>milesreevesai — 620,400 views</sub>
</td>
<td align="center" width="320">
<a href="https://www.youtube.com/watch?v=LkP6ocAoQkk">
<img src="https://img.youtube.com/vi/LkP6ocAoQkk/maxresdefault.jpg" alt="Valency Labs" width="300"/>
</a><br/>
<b>🎬 #5 — YouTube</b><br/>
<sub>Valency Labs — 135,974 views</sub>
<b>🎬 #5Instagram</b><br/>
<sub>buildwithai.club — 347,652 views</sub>
</td>
</tr>
</table>
</div>
**Ranking completo (`v > 0`, maior alcance):**
**Ranking completo (URLs canônicas deduplicadas, `v > 0`, maior alcance):**
| #1 | #2 | #3 | #4 | #5 |
| -------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- |
| [nick_saraev — Instagram](https://www.instagram.com/reel/Da8ZthUPK98/) — **1,628,910** | [milesreevesai — TikTok](https://www.tiktok.com/@milesreevesai/video/7667980059189366019) — **620,400** | [Vaibhav Sisinty — YouTube](https://www.youtube.com/watch?v=QucgvbO5gsM) — **373,084** | [Nick Automates — YouTube Shorts](https://www.youtube.com/shorts/fZIBK_4fKq8) — **207,714** | [midudev — TikTok](https://www.tiktok.com/@midudev/video/7664636453544152342) — **177,800** |
| #1 | #2 | #3 | #4 | #5 |
| -------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- |
| [nick_saraev — Instagram](https://www.instagram.com/reel/Da8ZthUPK98/) — **3,042,474** | [theopenstack — Instagram](https://www.instagram.com/reel/DaSs65mMrHk/) — **692,419** | [milesreevesai — TikTok](https://www.tiktok.com/@milesreevesai/video/7667980059189366019) — **620,400** | [Vaibhav Sisinty — YouTube](https://www.youtube.com/watch?v=QucgvbO5gsM) — **391,109** | [buildwithai.club — Instagram](https://www.instagram.com/reel/DbIt9AjK7-U/) — **347,652** |
| #6 | #7 | #8 | #9 | #10 |
| ------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- |
| [theopenstack — Instagram](https://www.instagram.com/reel/DaSs65mMrHk/) — **155,453** | [t.ghoush.ai — TikTok](https://www.tiktok.com/@t.ghoush.ai/video/7669497680527248656) — **152,800** | [Valency Labs — YouTube](https://www.youtube.com/watch?v=LkP6ocAoQkk) — **135,974** | [Asati — YouTube](https://www.youtube.com/watch?v=JjPtJcqwhqg) — **126,130** | [Vaibhav Sisinty — YouTube](https://www.youtube.com/watch?v=NuNDpeZYQ28) — **122,672** |
| #6 | #7 | #8 | #9 | #10 |
| ----------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- |
| [nivedan.ai — Instagram](https://www.instagram.com/reel/DbIrCksJiqq/) — **331,973** | [vaibhavsisinty — Instagram](https://www.instagram.com/reel/Dae05TSAK1l/) — **263,744** | [Nick Automates — YouTube Shorts](https://www.youtube.com/shorts/fZIBK_4fKq8) — **218,174** | [theroshankrishna — Instagram](https://www.instagram.com/reel/Dapjs58z0P0/) — **186,786** | [midudev — TikTok](https://www.tiktok.com/@midudev/video/7664636453544152342) — **177,800** |
Métricas de validação: 1002 vídeos rastreados · 7,069,190 visualizações conhecidas · 595 perfis/canais · 13+ idiomas · 13+ criadores.
Métricas canônicas em 2026-08-24: **1.029 vídeos únicos** · **11.132.922 visualizações conhecidas** (`v > 0`) · **639 canais/perfis por rede**. O painel bruto contém 1.070 linhas; 41 duplicatas do Instagram foram normalizadas pela URL canônica, mantendo a maior contagem por vídeo.
> 🎬 **Made a video about OmniRoute?** Open an [issue](https://github.com/diegosouzapw/OmniRoute/issues/new) or [discussion](https://github.com/diegosouzapw/OmniRoute/discussions) with the link — we'll feature it here.
@@ -1211,7 +1212,7 @@ Métricas de validação: 1002 vídeos rastreados · 7,069,190 visualizações c
<tr><td nowrap><b>Stealth</b></td><td>wreq-js — JA3 / JA4 TLS fingerprint impersonation, 3-level proxy</td></tr>
<tr><td nowrap><b>Resilience</b></td><td>Circuit breaker, exponential backoff, anti-thundering-herd, auto-combo self-healing</td></tr>
<tr><td nowrap><b>Logging</b></td><td>pino — structured JSON logs with request context</td></tr>
<tr><td nowrap><b>Testing</b></td><td>Node.js test runner + Vitest — <b>25,000+ test cases</b> across 3,300+ files (unit, integration, E2E, security, ecosystem)</td></tr>
<tr><td nowrap><b>Testing</b></td><td>Node.js test runner + Vitest — <b>39,000+ static test declarations</b> across 5,100+ tracked test files (unit, integration, E2E, security, ecosystem)</td></tr>
<tr><td nowrap><b>Platforms</b></td><td>Desktop (Electron) · Android (Termux) · PWA (any browser)</td></tr>
<tr><td nowrap><b>CI/CD</b></td><td>GitHub Actions — auto npm publish + Docker Hub on release</td></tr>
<tr><td nowrap><b>Links</b></td><td><a href="https://omniroute.online">Website</a> · <a href="https://www.npmjs.com/package/omniroute">npm</a> · <a href="https://hub.docker.com/r/diegosouzapw/omniroute">Docker Hub</a></td></tr>
@@ -1262,9 +1263,9 @@ Métricas de validação: 1002 vídeos rastreados · 7,069,190 visualizações c
<tr><td nowrap><b><a href="docs/compression/COMPRESSION_RULES_FORMAT.md">Compression Rules Format</a></b></td><td>JSON rule-pack schemas for Caveman and RTK filters</td></tr>
<tr><td nowrap><b><a href="docs/compression/COMPRESSION_LANGUAGE_PACKS.md">Compression Language Packs</a></b></td><td>Language detection and Caveman rule-pack authoring</td></tr>
<tr><td nowrap><b><a href="docs/architecture/RESILIENCE_GUIDE.md">Resilience Guide</a></b></td><td>Circuit breakers, cooldowns, queue, anti-thundering herd, TLS spoofing</td></tr>
<tr><td nowrap><b><a href="docs/routing/AUTO-COMBO.md">Auto-Combo Engine</a></b></td><td>14-factor scoring, mode packs, self-healing</td></tr>
<tr><td nowrap><b><a href="docs/routing/AUTO-COMBO.md">Auto-Combo Engine</a></b></td><td>15-factor scoring, mode packs, self-healing</td></tr>
<tr><td nowrap><b><a href="docs/ops/PROXY_GUIDE.md">Proxy Guide</a></b></td><td>3-level proxy system, 1proxy marketplace, registry CRUD</td></tr>
<tr><td nowrap><b><a href="docs/reference/FREE_TIERS.md">Free Tiers</a></b></td><td>90+ free providers consolidated directory (42 documented token pools / 495 models)</td></tr>
<tr><td nowrap><b><a href="docs/reference/FREE_TIERS.md">Free Tiers</a></b></td><td>Consolidated directory: 40 documented recurring pools / 455 cataloged free-tier entries</td></tr>
<tr><td nowrap><b><a href="docs/guides/FEATURES.md">Features Gallery</a></b></td><td>Visual dashboard tour with screenshots</td></tr>
<tr><td nowrap><b><a href="docs/architecture/CODEBASE_DOCUMENTATION.md">Codebase Documentation</a></b></td><td>Beginner-friendly codebase walkthrough</td></tr>
</table>
@@ -1275,7 +1276,7 @@ Métricas de validação: 1002 vídeos rastreados · 7,069,190 visualizações c
<tr><th align="left">Document</th><th align="left">Description</th></tr>
<tr><td nowrap><b><a href="docs/reference/API_REFERENCE.md">API Reference</a></b></td><td>All endpoints with examples</td></tr>
<tr><td nowrap><b><a href="docs/openapi.yaml">OpenAPI Spec</a></b></td><td>OpenAPI 3.0 specification</td></tr>
<tr><td nowrap><b><a href="open-sse/mcp-server/README.md">MCP Server</a></b></td><td>109 MCP tools, IDE configs, Python/TS/Go clients</td></tr>
<tr><td nowrap><b><a href="open-sse/mcp-server/README.md">MCP Server</a></b></td><td>110 MCP tools, IDE configs, Python/TS/Go clients</td></tr>
<tr><td nowrap><b><a href="docs/frameworks/MCP-SERVER.md">MCP Server Guide</a></b></td><td>MCP installation, transports, and tool reference</td></tr>
<tr><td nowrap><b><a href="src/lib/a2a/README.md">A2A Server</a></b></td><td>JSON-RPC 2.0 protocol, skills, streaming, task mgmt</td></tr>
<tr><td nowrap><b><a href="docs/frameworks/A2A-SERVER.md">A2A Server Guide</a></b></td><td>A2A agent card, tasks, skills, and streaming</td></tr>
@@ -1291,7 +1292,7 @@ Métricas de validação: 1002 vídeos rastreados · 7,069,190 visualizações c
<tr><td nowrap><b><a href="SECURITY.md">Security Policy</a></b></td><td>Vulnerability reporting and security practices</td></tr>
<tr><td nowrap><b><a href="docs/guides/I18N.md">i18n Guide</a></b></td><td>43-language support, translation workflow, RTL</td></tr>
<tr><td nowrap><b><a href="docs/ops/RELEASE_CHECKLIST.md">Release Checklist</a></b></td><td>Pre-release validation steps</td></tr>
<tr><td nowrap><b><a href="docs/ops/COVERAGE_PLAN.md">Coverage Plan</a></b></td><td>Test coverage strategy and 25,000+ test suite</td></tr>
<tr><td nowrap><b><a href="docs/ops/COVERAGE_PLAN.md">Coverage Plan</a></b></td><td>Test coverage strategy for 39,000+ static test declarations across 5,100+ tracked test files</td></tr>
</table>
<br/>
@@ -1302,93 +1303,123 @@ Métricas de validação: 1002 vídeos rastreados · 7,069,190 visualizações c
> OmniRoute is shaped by a passionate open-source community. These individuals have made exceptional contributions that directly impact the quality, stability, and reach of the project. **Thank you.**
### External contributors by merged pull requests
<table>
<tr><th align="center">Rank</th><th align="left">Contributor</th><th align="center">Merged PRs</th><th align="right">~Changed lines</th></tr>
<tr><td align="center">1</td><td align="left"><a href="https://github.com/backryun"><b>backryun</b></a></td><td align="center">190</td><td align="right">227,977</td></tr>
<tr><td align="center">2</td><td align="left"><a href="https://github.com/oyi77"><b>oyi77</b></a></td><td align="center">180</td><td align="right">407,678</td></tr>
<tr><td align="center">3</td><td align="left"><a href="https://github.com/rdself"><b>rdself</b></a></td><td align="center">145</td><td align="right">80,663</td></tr>
<tr><td align="center">4</td><td align="left"><a href="https://github.com/JxnLexn"><b>JxnLexn</b></a></td><td align="center">128</td><td align="right">387,049</td></tr>
<tr><td align="center">5</td><td align="left"><a href="https://github.com/KooshaPari"><b>KooshaPari</b></a></td><td align="center">101</td><td align="right">125,747</td></tr>
<tr><td align="center">6</td><td align="left"><a href="https://github.com/herjarsa"><b>herjarsa</b></a></td><td align="center">88</td><td align="right">230,872</td></tr>
<tr><td align="center">7</td><td align="left"><a href="https://github.com/RaviTharuma"><b>RaviTharuma</b></a></td><td align="center">79</td><td align="right">55,106</td></tr>
<tr><td align="center">8</td><td align="left"><a href="https://github.com/maxmad64bis"><b>maxmad64bis</b></a></td><td align="center">69</td><td align="right">394,715</td></tr>
<tr><td align="center">9</td><td align="left"><a href="https://github.com/artickc"><b>artickc</b></a></td><td align="center">59</td><td align="right">33,260</td></tr>
<tr><td align="center">10</td><td align="left"><a href="https://github.com/HouMinXi"><b>HouMinXi</b></a></td><td align="center">51</td><td align="right">47,334</td></tr>
<tr><td align="center">10</td><td align="left"><a href="https://github.com/chirag127"><b>chirag127</b></a></td><td align="center">51</td><td align="right">5,153</td></tr>
<tr><td align="center">12</td><td align="left"><a href="https://github.com/xz-dev"><b>xz-dev</b></a></td><td align="center">50</td><td align="right">245,976</td></tr>
<tr><td align="center">13</td><td align="left"><a href="https://github.com/hartmark"><b>hartmark</b></a></td><td align="center">47</td><td align="right">52,185</td></tr>
<tr><td align="center">14</td><td align="left"><a href="https://github.com/rqzbeh"><b>rqzbeh</b></a></td><td align="center">39</td><td align="right">143,181</td></tr>
<tr><td align="center">15</td><td align="left"><a href="https://github.com/dhaern"><b>dhaern</b></a></td><td align="center">34</td><td align="right">19,559</td></tr>
<tr><td align="center">16</td><td align="left"><a href="https://github.com/Dingding-leo"><b>Dingding-leo</b></a></td><td align="center">33</td><td align="right">1,986</td></tr>
<tr><td align="center">17</td><td align="left"><a href="https://github.com/NomenAK"><b>NomenAK</b></a></td><td align="center">32</td><td align="right">13,854</td></tr>
<tr><td align="center">18</td><td align="left"><a href="https://github.com/MumuTW"><b>MumuTW</b></a></td><td align="center">30</td><td align="right">16,953</td></tr>
<tr><td align="center">19</td><td align="left"><a href="https://github.com/benzntech"><b>benzntech</b></a></td><td align="center">29</td><td align="right">11,641</td></tr>
<tr><td align="center">20</td><td align="left"><a href="https://github.com/pacocartones"><b>pacocartones</b></a></td><td align="center">24</td><td align="right">9,331</td></tr>
<tr><td align="center">20</td><td align="left"><a href="https://github.com/Prudhvivuda"><b>Prudhvivuda</b></a></td><td align="center">24</td><td align="right">6,312</td></tr>
</table>
<sub>Frozen at live <code>release/v3.8.50</code> tip <code>dafb4ae808</code>, with merges through 2026-08-24 05:26:03 UTC. The paginated GitHub GraphQL census contains 5,911 merged PRs: 2,707 by the repository owner, 179 by Dependabot, and <b>3,025 external PRs from 535 distinct contributors</b>. “Changed lines” is GitHub additions + deletions and includes generated files, lockfiles, catalogs, translations and documentation; it is churn, not authored LOC. Ties at the cutoff are retained.</sub>
### GitHub-attributed commits
<table>
<tr>
<td align="center" width="160">
<a href="https://github.com/oyi77">
<img src="https://github.com/oyi77.png" width="40" style="border-radius:50%" alt="oyi77"/><br/>
<b>oyi77</b>
</a><br/>
<sub>🥇 213 commits • +114K lines</sub><br/>
<sub>Analytics engine, SQL aggregations,<br/>proxy marketplace, test coverage</sub>
</td>
<td align="center" width="160">
<a href="https://github.com/rdself">
<img src="https://github.com/rdself.png" width="40" style="border-radius:50%" alt="R.D. &amp; Randi"/><br/>
<b>R.D. &amp; Randi</b>
</a><br/>
<sub>🥈 108 commits • +38K lines</sub><br/>
<sub>Endpoints page, tunnel integrations,<br/>Docker workflows, A2A status, compression UI</sub>
</td>
<td align="center" width="160">
<a href="https://github.com/christopher-s">
<img src="https://github.com/christopher-s.png" width="40" style="border-radius:50%" alt="Chris Staley"/><br/>
<b>Chris Staley</b>
</a><br/>
<sub>🥉 70 commits • +1.8K lines</sub><br/>
<sub>SSE stream hardening, Responses API,<br/>Gemini pagination, test regression fixes</sub>
</td>
<td align="center" width="160">
<a href="https://github.com/zen0bit">
<img src="https://github.com/zen0bit.png" width="40" style="border-radius:50%" alt="zenobit"/><br/>
<b>zenobit</b>
</a><br/>
<sub>🏅 62 commits • +22K lines</sub><br/>
<sub>CI/CD pipeline, i18n for 33 languages,<br/>Void Linux package, platform fixes</sub>
</td>
<td align="center" width="160">
<a href="https://github.com/JxnLexn">
<img src="https://github.com/JxnLexn.png" width="40" style="border-radius:50%" alt="Jan Leon"/><br/>
<b>Jan Leon</b>
</a><br/>
<sub>🏅 58 commits • +22K lines</sub><br/>
<sub>Reasoning-effort routing, proxy controls,<br/>quota visibility, Live Zone compression</sub>
</td>
</tr>
<tr>
<td align="center" width="160">
<a href="https://github.com/backryun">
<img src="https://github.com/backryun.png" width="40" style="border-radius:50%" alt="backryun"/><br/>
<b>backryun</b>
</a><br/>
<sub>🏅 53 commits • +70K lines</sub><br/>
<sub>Provider catalog curation — Perplexity, Kimi,<br/>Cerebras, Copilot, LMArena refreshes</sub>
<sub>🥇 220 GitHub-attributed commits</sub>
</td>
<td align="center" width="160">
<a href="https://github.com/chirag127">
<img src="https://github.com/chirag127.png" width="40" style="border-radius:50%" alt="Chirag Singhal"/><br/>
<b>Chirag Singhal</b>
<a href="https://github.com/oyi77">
<img src="https://github.com/oyi77.png" width="40" style="border-radius:50%" alt="Paijo"/><br/>
<b>Paijo</b>
</a><br/>
<sub>🏅 46 commits • +4.8K lines</sub><br/>
<sub>Error sanitization, MITM prefill fix,<br/>fusion judge, breaker/429 correctness</sub>
<sub>🥈 219 GitHub-attributed commits</sub>
</td>
<td align="center" width="160">
<a href="https://github.com/kfiramar">
<img src="https://github.com/kfiramar.png" width="40" style="border-radius:50%" alt="kfiramar"/><br/>
<b>kfiramar</b>
<a href="https://github.com/rdself">
<img src="https://github.com/rdself.png" width="40" style="border-radius:50%" alt="Randi"/><br/>
<b>Randi</b>
</a><br/>
<sub>🏅 38 commits • +1.7K lines</sub><br/>
<sub>Codex websocket + passthrough, auth/onboarding,<br/>Electron hardening, DB migrations</sub>
<sub>🥉 108 GitHub-attributed commits</sub>
</td>
<td align="center" width="160">
<a href="https://github.com/benzntech">
<img src="https://github.com/benzntech.png" width="40" style="border-radius:50%" alt="Benson K B"/><br/>
<b>Benson K B</b>
<a href="https://github.com/RaviTharuma">
<img src="https://github.com/RaviTharuma.png" width="40" style="border-radius:50%" alt="Ravi Tharuma"/><br/>
<b>Ravi Tharuma</b>
</a><br/>
<sub>🏅 28 commits • +9.2K lines</sub><br/>
<sub>Electron desktop app, auto-updater,<br/>release build workflows, cross-platform CI</sub>
<sub>🏅 81 GitHub-attributed commits</sub>
</td>
<td align="center" width="160">
<a href="https://github.com/herjarsa">
<img src="https://github.com/herjarsa.png" width="40" style="border-radius:50%" alt="Hernan J. Ardila"/><br/>
<b>Hernan J. Ardila</b>
<a href="https://github.com/christopher-s">
<img src="https://github.com/christopher-s.png" width="40" style="border-radius:50%" alt="Chris"/><br/>
<b>Chris</b>
</a><br/>
<sub>🏅 25 commits • +174K lines</sub><br/>
<sub>Zero-latency combos, vision-bridge auto-routing,<br/>catalog context-length, resilience 429 hints</sub>
<sub>🏅 70 GitHub-attributed commits</sub>
</td>
<td align="center" width="160">
<a href="https://github.com/hartmark">
<img src="https://github.com/hartmark.png" width="40" style="border-radius:50%" alt="Markus Hartung"/><br/>
<b>Markus Hartung</b>
</a><br/>
<sub>🏅 69 GitHub-attributed commits · tied #6</sub>
</td>
</tr>
<tr>
<td align="center" width="160">
<a href="https://github.com/maxmad64bis">
<img src="https://github.com/maxmad64bis.png" width="40" style="border-radius:50%" alt="Dizzle"/><br/>
<b>Dizzle</b>
</a><br/>
<sub>🏅 69 GitHub-attributed commits · tied #6</sub>
</td>
<td align="center" width="160">
<a href="https://github.com/JxnLexn">
<img src="https://github.com/JxnLexn.png" width="40" style="border-radius:50%" alt="Jan Leon"/><br/>
<b>Jan Leon</b>
</a><br/>
<sub>🏅 64 GitHub-attributed commits</sub>
</td>
<td align="center" width="160">
<a href="https://github.com/zen0bit">
<img src="https://github.com/zen0bit.png" width="40" style="border-radius:50%" alt="zenobit"/><br/>
<b>zenobit</b>
</a><br/>
<sub>🏅 62 GitHub-attributed commits</sub>
</td>
<td align="center" width="160">
<a href="https://github.com/HouMinXi">
<img src="https://github.com/HouMinXi.png" width="40" style="border-radius:50%" alt="Bob.Hou"/><br/>
<b>Bob.Hou</b>
</a><br/>
<sub>🏅 51 GitHub-attributed commits · tied #10</sub>
</td>
<td align="center" width="160">
<a href="https://github.com/xz-dev">
<img src="https://github.com/xz-dev.png" width="40" style="border-radius:50%" alt="Xiangzhe"/><br/>
<b>Xiangzhe</b>
</a><br/>
<sub>🏅 51 GitHub-attributed commits · tied #10</sub>
</td>
</tr>
</table>
<sub>Rechecked at 2026-08-24 06:14:31 UTC: GitHub-attributed commits reported by the repository Contributors API for the <code>release/v3.8.50</code> default branch. The API returned 525 identities (415 users, 2 bots, 108 anonymous); this table excludes the maintainer, bots and anonymous identities and retains competition ties. It is distinct from both the merged-PR ranking above and the 639-person Git-metadata census below.</sub>
> 🙏 These contributors' features, bug fixes, and infrastructure improvements are a **core part** of what makes OmniRoute reliable and feature-rich. Every pull request, every test case, and every i18n translation file matters. Open source is built by people like them.
</div>
@@ -1405,25 +1436,48 @@ A heartfelt thank-you to the people who fund OmniRoute out of their own pocket
<table>
<tr>
<td align="center" width="180">
<a href="https://github.com/drewbitt">
<img src="https://github.com/drewbitt.png?size=140" width="72" style="border-radius:50%" alt="Andrew"/><br/>
<b>Andrew</b>
</a><br/>
<sub>💛 Active monthly sponsor</sub>
</td>
<td align="center" width="180">
<a href="https://github.com/psylligent">
<img src="https://github.com/psylligent.png?size=140" width="72" style="border-radius:50%" alt="Vlad I"/><br/>
<b>Vlad I</b>
</a><br/>
<sub>💛 Active monthly sponsor</sub>
</td>
<td align="center" width="180">
<a href="https://github.com/pacocartones">
<img src="https://github.com/pacocartones.png?size=140" width="72" style="border-radius:50%" alt="Paco Cartones"/><br/>
<b>Paco Cartones</b>
</a><br/>
<sub>💛 Active one-time sponsor</sub>
</td>
<td align="center" width="180">
<a href="https://github.com/igormorais123">
<img src="https://github.com/igormorais123.png?size=140" width="72" style="border-radius:50%" alt="Professor Igor Morais Vasconcelos"/><br/>
<b>Prof. Igor Morais</b>
</a><br/>
<sub>💛 Sponsor</sub>
<sub>💛 Past one-time supporter</sub>
</td>
<td align="center" width="180">
<a href="https://github.com/longtao77">
<img src="https://github.com/longtao77.png?size=140" width="72" style="border-radius:50%" alt="longtao"/><br/>
<b>longtao</b>
</a><br/>
<sub>💛 Sponsor</sub>
<sub>💛 Past one-time supporter</sub>
</td>
</tr>
</table>
<sub>… and others who prefer to stay private 💛</sub>
<sub>Public GitHub Sponsors revalidated on 2026-08-24. GitHub's <code>activeOnly</code> status determines the active labels above; previously disclosed public one-time supporters remain thanked, and private sponsors remain anonymous.</sub>
<b><a href="https://github.com/sponsors/diegosouzapw">💖 Become a sponsor →</a></b> — every dollar keeps OmniRoute free and independent.
</div>
@@ -1432,11 +1486,13 @@ A heartfelt thank-you to the people who fund OmniRoute out of their own pocket
<div align="center">
## 👥 320+ Contributors
## 👥 600+ Contributors
</div>
[![Contributors](https://contrib.rocks/image?repo=diegosouzapw/OmniRoute&max=400&columns=20&anon=1)](https://github.com/diegosouzapw/OmniRoute/graphs/contributors)
[![Contributors](https://contrib.rocks/image?repo=diegosouzapw/OmniRoute&max=639&columns=20&anon=1)](https://github.com/diegosouzapw/OmniRoute/graphs/contributors)
<sub>Audited on 2026-08-24 at frozen base <code>ac02c5b42f</code> and rechecked at live <code>release/v3.8.50</code> tip <code>dafb4ae808</code>: <b>639 normalized human Git identities</b> — 407 appear as commit authors (including the maintainer) and 232 only in explicit <code>Co-authored-by</code> trailers. The census normalizes GitHub noreply handles, excludes 26 bot/agent/service/placeholder identities, and does not merge ordinary email addresses merely because their display names match.</sub>
### How to Contribute
@@ -1453,7 +1509,8 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for detailed guidelines.
```bash
# Create a release — npm publish happens automatically
gh release create v3.8.2 --title "v3.8.2" --generate-notes
VERSION=x.y.z
gh release create "v${VERSION}" --title "v${VERSION}" --generate-notes
```
<br/>
@@ -1495,88 +1552,108 @@ gh release create v3.8.2 --title "v3.8.2" --generate-notes
OmniRoute stands on the shoulders of giants. It started as a fork of **[9router](https://github.com/decolua/9router)** and a TypeScript port of the Go project **[CLIProxyAPI](https://github.com/router-for-me/CLIProxyAPI)** — and from there, every subsystem below was inspired by an open-source project that got there first. Each one shaped a concrete piece of OmniRoute. This is our thank-you to all of them. 🙏
> ⭐ star counts as of July 2026 — go give these projects a star.
> ⭐ star counts verified from GitHub's REST API on August 24, 2026 — go give these projects a star. Counts are an exact dated snapshot and will naturally change.
### 🧬 Lineage & gateway
<table>
<tr><th align="left">Project</th><th align="center">⭐</th><th align="left">How it inspired OmniRoute</th></tr>
<tr><td nowrap><b><a href="https://github.com/decolua/9router">9router</a></b></td><td align="center">22.7k</td><td>The original project this fork is built on — extended here with multi-modal APIs and a full TypeScript rewrite.</td></tr>
<tr><td nowrap><b><a href="https://github.com/router-for-me/CLIProxyAPI">CLIProxyAPI</a></b></td><td align="center">43.6k</td><td>The Go implementation that inspired this JavaScript / TypeScript port.</td></tr>
<tr><td nowrap><b><a href="https://github.com/BerriAI/litellm">LiteLLM</a></b></td><td align="center">54.0k</td><td>The AI gateway whose public pricing dataset feeds our cost-tracking sync and whose provider-normalization model informed our routing.</td></tr>
<tr><td nowrap><b><a href="https://github.com/decolua/9router">9router</a></b></td><td align="center">26,161</td><td>The original project this fork is built on — extended here with multi-modal APIs and a full TypeScript rewrite.</td></tr>
<tr><td nowrap><b><a href="https://github.com/router-for-me/CLIProxyAPI">CLIProxyAPI</a></b></td><td align="center">48,497</td><td>The Go implementation that inspired this JavaScript / TypeScript port.</td></tr>
<tr><td nowrap><b><a href="https://github.com/BerriAI/litellm">LiteLLM</a></b></td><td align="center">57,100</td><td>The AI gateway whose public pricing dataset feeds our cost-tracking sync and whose provider-normalization model informed our routing.</td></tr>
<tr><td nowrap><b><a href="https://github.com/miuuyy/codex-chatgpt-web">codex-chatgpt-web</a></b></td><td align="center">1,410</td><td>MIT source adapted into the vendored ChatGPT Web → Codex Responses bridge, including browser-session, response-framing, usage and web-search adapters.</td></tr>
<tr><td nowrap><b><a href="https://github.com/Alishahryar1/free-claude-code">free-claude-code</a></b></td><td align="center">48,112</td><td>Patterns ported into stream recovery, no-thinking aliases, fallback web search, sliding-window limits, log redaction and hardened launcher flows.</td></tr>
<tr><td nowrap><b><a href="https://github.com/standardagents/composer-api">composer-api</a></b></td><td align="center">322</td><td>Cursor Composer tool-choice, output-constraint and tool-commit patterns adapted into the native Cursor executor.</td></tr>
<tr><td nowrap><b><a href="https://github.com/ndycode/codex-multi-auth">codex-multi-auth</a></b></td><td align="center">457</td><td>Fresh-login and refresh-token rotation patterns ported into Codex OAuth reauthentication.</td></tr>
<tr><td nowrap><b><a href="https://github.com/ex-machina-co/opencode-anthropic-auth">opencode-anthropic-auth</a></b></td><td align="center">510</td><td>Claude Code-compatible transform defaults and billing-header behavior generalized into OmniRoute's config-driven bridge.</td></tr>
<tr><td nowrap><b><a href="https://github.com/520mmxx/grok2api-merged">grok2api-merged</a></b></td><td align="center">2</td><td>Its Grok model mappings, fake-TypeError Statsig generator, request and device defaults, and NDJSON response processor were materially adapted into OmniRoute's Grok Web executor.</td></tr>
<tr><td nowrap><b><a href="https://github.com/TQZHR/grok2api">TQZHR/grok2api</a></b></td><td align="center">705</td><td>The principal transitive code source behind grok2api-merged; its model, header, payload, Statsig and processor implementations are preserved in the Grok Web lineage.</td></tr>
<tr><td nowrap><b><a href="https://github.com/chenyme/grok2api">chenyme/grok2api</a></b></td><td align="center">7,520</td><td>The underlying MIT source for Grok payload and device defaults, the Statsig generator, and the <code>result.response</code> processor carried through TQZHR and grok2api-merged.</td></tr>
<tr><td nowrap><b><a href="https://github.com/miuzhaii/grok2api-pro">grok2api-pro</a></b></td><td align="center">27</td><td>A transitive source credited by grok2api-merged for its proxy-pool layer; OmniRoute preserves that lineage notice but does not claim a proxy-pool port in its bounded Grok Web executor.</td></tr>
<tr><td nowrap><b><a href="https://github.com/CNFlyCat/GrokProxy">GrokProxy</a></b></td><td align="center">50</td><td>Its cookie-authenticated Grok proxy and <code>result.response.token</code> streaming pattern informed OmniRoute's Grok Web transport.</td></tr>
<tr><td nowrap><b><a href="https://github.com/lianying1716/GrokBridge">GrokBridge</a></b></td><td align="center">5</td><td>The original Grok Web implementation consulted its HTTP/browser upstream design; its direct HTTP path derives from GrokProxy, so no independent code port is claimed.</td></tr>
<tr><td nowrap><b><a href="https://github.com/imjustprism/grok-web-api">grok-web-api</a></b></td><td align="center">14</td><td>Its Rust <code>ChatOptions</code> and response-envelope schemas informed OmniRoute's TypeScript Grok request and streaming-response types.</td></tr>
</table>
### 🗜️ Context & token compression — engines
<table>
<tr><th align="left">Project</th><th align="center">⭐</th><th align="left">How it inspired OmniRoute</th></tr>
<tr><td nowrap><b><a href="https://github.com/JuliusBrussee/caveman">Caveman</a></b></td><td align="center">90.8k</td><td>The viral "why use many token when few token do trick" project — its caveman-speak philosophy powers our standard compression mode and 30+ filler/condensation rules.</td></tr>
<tr><td nowrap><b><a href="https://github.com/rtk-ai/rtk">RTK Rust Token Killer</a></b></td><td align="center">71.8k</td><td>High-performance command-output compression — inspired our RTK engine, JSON filter DSL, raw-output recovery and the stacked RTK → Caveman pipeline.</td></tr>
<tr><td nowrap><b><a href="https://github.com/headroomlabs-ai/headroom">headroom</a></b></td><td align="center">60.1k</td><td>Reversible context-compression (SmartCrusher) — inspired our <code>headroom</code> engine and the <code>ccr</code> retrieve-marker pattern.</td></tr>
<tr><td nowrap><b><a href="https://github.com/microsoft/LLMLingua">LLMLingua</a></b></td><td align="center">6.5k</td><td>Prompt-compression research (LLMLingua / LLMLingua-2) — inspired our async, code-safe, fail-open <code>llmlingua</code> engine.</td></tr>
<tr><td nowrap><b><a href="https://github.com/atjsh/llmlingua-2-js">llmlingua-2-js</a></b></td><td align="center">30</td><td>The JS/ONNX port (MobileBERT / XLM-RoBERTa) used as the worker-thread backend for our LLMLingua engine.</td></tr>
<tr><td nowrap><b><a href="https://github.com/leninejunior/troglodita">Troglodita</a></b></td><td align="center">26</td><td>PT-BR token compression — powers our pt-BR language pack: pleonasm reduction and filler removal tuned for Brazilian-Portuguese grammar.</td></tr>
<tr><td nowrap><b><a href="https://github.com/DietrichGebert/ponytail">ponytail</a></b></td><td align="center">86.0k</td><td>The viral "lazy senior dev" YAGNI-coder skill — inspired our <b>less-code</b> Output Style: smallest-working-change steering that cuts _generated_ code (the output-axis sibling to Caveman's terse prose).</td></tr>
<tr><td nowrap><b><a href="https://github.com/JuliusBrussee/caveman">Caveman</a></b></td><td align="center">100,538</td><td>The viral "why use many token when few token do trick" project — its caveman-speak philosophy powers our standard compression mode and 30+ filler/condensation rules.</td></tr>
<tr><td nowrap><b><a href="https://github.com/rtk-ai/rtk">RTK Rust Token Killer</a></b></td><td align="center">77,185</td><td>High-performance command-output compression — inspired our RTK engine, JSON filter DSL, raw-output recovery and the stacked RTK → Caveman pipeline.</td></tr>
<tr><td nowrap><b><a href="https://github.com/headroomlabs-ai/headroom">headroom</a></b></td><td align="center">67,310</td><td>Reversible context-compression (SmartCrusher) — inspired our <code>headroom</code> engine and the <code>ccr</code> retrieve-marker pattern.</td></tr>
<tr><td nowrap><b><a href="https://github.com/microsoft/LLMLingua">LLMLingua</a></b></td><td align="center">6,598</td><td>Prompt-compression research (LLMLingua / LLMLingua-2) — inspired our async, code-safe, fail-open <code>llmlingua</code> engine.</td></tr>
<tr><td nowrap><b><a href="https://github.com/atjsh/llmlingua-2-js">llmlingua-2-js</a></b></td><td align="center">31</td><td>The JS/ONNX port (MobileBERT / XLM-RoBERTa) used as the worker-thread backend for our LLMLingua engine.</td></tr>
<tr><td nowrap><b><a href="https://github.com/leninejunior/troglodita">Troglodita</a></b></td><td align="center">40</td><td>PT-BR token compression — powers our pt-BR language pack: pleonasm reduction and filler removal tuned for Brazilian-Portuguese grammar.</td></tr>
<tr><td nowrap><b><a href="https://github.com/DietrichGebert/ponytail">ponytail</a></b></td><td align="center">108,957</td><td>The viral "lazy senior dev" YAGNI-coder skill — inspired our <b>less-code</b> Output Style: smallest-working-change steering that cuts _generated_ code (the output-axis sibling to Caveman's terse prose).</td></tr>
<tr><td nowrap><b><a href="https://github.com/ayghri/i-have-adhd">i-have-adhd</a></b></td><td align="center">23,526</td><td>Its action-first, ADHD-friendly response style was adapted into OmniRoute's concise output style across five languages.</td></tr>
</table>
### 🧩 Compact formats, token research & code-aware tooling
<table>
<tr><th align="left">Project</th><th align="center">⭐</th><th align="left">How it inspired OmniRoute</th></tr>
<tr><td nowrap><b><a href="https://github.com/toon-format/toon">TOON</a></b></td><td align="center">24.9k</td><td>Token-Oriented Object Notation — its columnar, header-plus-rows model shaped our tabular compaction stage.</td></tr>
<tr><td nowrap><b><a href="https://github.com/blackwell-systems/gcf">GCF Graph Compact Format</a></b></td><td align="center">22</td><td>First inspired our tabular compaction stage; now its zero-dependency, lossless generic-profile encoder is <b>vendored directly</b> as the Headroom codec (MIT, SPDX-marked), with later numeric-domain and count-mismatch correctness fixes.</td></tr>
<tr><td nowrap><b><a href="https://github.com/ooples/token-optimizer-mcp">token-optimizer-mcp</a></b></td><td align="center">444</td><td>Brotli/SQLite cache + per-session context-delta — inspired our <code>session-dedup</code> engine.</td></tr>
<tr><td nowrap><b><a href="https://github.com/Mibayy/token-savior">token-savior</a></b></td><td align="center">1.1k</td><td>Bash-output compaction + MCP profiles — inspired our compression bail-out discipline and MCP tool-manifest reduction.</td></tr>
<tr><td nowrap><b><a href="https://github.com/ppgranger/token-saver">token-saver</a></b></td><td align="center">117</td><td>Content-aware, per-file-type output compression with failure-aware bail-out — validated our per-type dispatch and minimum-gain skip.</td></tr>
<tr><td nowrap><b><a href="https://github.com/alexgreensh/token-optimizer">token-optimizer</a></b></td><td align="center">1.7k</td><td>"Find the ghost tokens" — its offload + recoverable-handle pattern informed our CCR offload thinking.</td></tr>
<tr><td nowrap><b><a href="https://github.com/Shweta-Mishra-ai/tokenmizer">TokenMizer</a></b></td><td align="center">16</td><td>A session-graph + cross-turn line-dedup blueprint that informed our session-dedup design.</td></tr>
<tr><td nowrap><b><a href="https://github.com/toon-format/toon">TOON</a></b></td><td align="center">25,233</td><td>Token-Oriented Object Notation — its columnar, header-plus-rows model shaped our tabular compaction stage.</td></tr>
<tr><td nowrap><b><a href="https://github.com/blackwell-systems/gcf">GCF Graph Compact Format</a></b></td><td align="center">41</td><td>Its compact graph format and generic-profile design informed OmniRoute's tabular compaction and Headroom codec format.</td></tr>
<tr><td nowrap><b><a href="https://github.com/blackwell-systems/gcf-typescript">gcf-typescript</a></b></td><td align="center">4</td><td>The MIT TypeScript implementation directly vendored and extended as the Headroom generic-profile codec.</td></tr>
<tr><td nowrap><b><a href="https://github.com/ooples/token-optimizer-mcp">token-optimizer-mcp</a></b></td><td align="center">494</td><td>Brotli/SQLite cache + per-session context-delta — inspired our <code>session-dedup</code> engine.</td></tr>
<tr><td nowrap><b><a href="https://github.com/Mibayy/token-savior">token-savior</a></b></td><td align="center">1,122</td><td>Bash-output compaction + MCP profiles — inspired our compression bail-out discipline and MCP tool-manifest reduction.</td></tr>
<tr><td nowrap><b><a href="https://github.com/ppgranger/token-saver">token-saver</a></b></td><td align="center">138</td><td>Content-aware, per-file-type output compression with failure-aware bail-out — validated our per-type dispatch and minimum-gain skip.</td></tr>
<tr><td nowrap><b><a href="https://github.com/alexgreensh/token-optimizer">token-optimizer</a></b></td><td align="center">1,951</td><td>"Find the ghost tokens" — its offload + recoverable-handle pattern informed our CCR offload thinking.</td></tr>
<tr><td nowrap><b><a href="https://github.com/Shweta-Mishra-ai/tokenmizer">TokenMizer</a></b></td><td align="center">28</td><td>A session-graph + cross-turn line-dedup blueprint that informed our session-dedup design.</td></tr>
<tr><td nowrap><b><a href="https://github.com/jessefreitas/OmniCompress">OmniCompress</a></b></td><td align="center">3</td><td>Rust columnar-JSON + content-addressed retrieve + cross-message dedup — validated our <code>headroom</code>/<code>ccr</code>/<code>session-dedup</code> engine design and the cache-stable "compressed form is position-independent" invariant.</td></tr>
<tr><td nowrap><b><a href="https://github.com/atlassian-labs/mcp-compressor">mcp-compressor</a></b></td><td align="center">98</td><td>MCP tool-schema/description compression — informed our MCP tool-manifest cardinality reduction.</td></tr>
<tr><td nowrap><b><a href="https://github.com/pdavis68/RepoMapper">RepoMapper</a></b></td><td align="center">187</td><td>Aider-style repo-map ranking — informed our repo-map / retrieval-ranking exploration.</td></tr>
<tr><td nowrap><b><a href="https://github.com/atlassian-labs/mcp-compressor">mcp-compressor</a></b></td><td align="center">113</td><td>MCP tool-schema/description compression — informed our MCP tool-manifest cardinality reduction.</td></tr>
<tr><td nowrap><b><a href="https://github.com/pdavis68/RepoMapper">RepoMapper</a></b></td><td align="center">197</td><td>Aider-style repo-map ranking — informed our repo-map / retrieval-ranking exploration.</td></tr>
<tr><td nowrap><b><a href="https://github.com/mrsimpson/quiet-shell-mcp">quiet-shell-mcp</a></b></td><td align="center">4</td><td>Declarative shell-output reduction over MCP — validated our declarative bash-output compaction.</td></tr>
<tr><td nowrap><b><a href="https://github.com/dsherret/ts-morph">ts-morph</a></b></td><td align="center">6.1k</td><td>TypeScript Compiler API toolkit — inspired our parser-based comment removal that preserves string, template and regex literals.</td></tr>
<tr><td nowrap><b><a href="https://github.com/dsherret/ts-morph">ts-morph</a></b></td><td align="center">6,162</td><td>TypeScript Compiler API toolkit — inspired our parser-based comment removal that preserves string, template and regex literals.</td></tr>
</table>
### 🧠 Memory & RAG
<table>
<tr><th align="left">Project</th><th align="center">⭐</th><th align="left">How it inspired OmniRoute</th></tr>
<tr><td nowrap><b><a href="https://github.com/mem0ai/mem0">Mem0</a></b></td><td align="center">61.2k</td><td>Universal memory layer — its proxy-as-write/read-boundary model shaped our memory architecture.</td></tr>
<tr><td nowrap><b><a href="https://github.com/letta-ai/letta">Letta (MemGPT)</a></b></td><td align="center">23.9k</td><td>Stateful agents with tiered memory — inspired our Context Control & Recovery (CCR) tiered model.</td></tr>
<tr><td nowrap><b><a href="https://github.com/onestardao/WFGY">WFGY</a></b></td><td align="center">1.8k</td><td>The ProblemMap taxonomy of 16 recurring RAG/LLM failure modes — the shared vocabulary in our troubleshooting guide.</td></tr>
<tr><td nowrap><b><a href="https://github.com/mem0ai/mem0">Mem0</a></b></td><td align="center">63,902</td><td>Universal memory layer — its proxy-as-write/read-boundary model shaped our memory architecture.</td></tr>
<tr><td nowrap><b><a href="https://github.com/letta-ai/letta">Letta (MemGPT)</a></b></td><td align="center">24,382</td><td>Stateful agents with tiered memory — inspired our Context Control & Recovery (CCR) tiered model.</td></tr>
<tr><td nowrap><b><a href="https://github.com/onestardao/WFGY">WFGY</a></b></td><td align="center">1,781</td><td>The ProblemMap taxonomy of 16 recurring RAG/LLM failure modes — the shared vocabulary in our troubleshooting guide.</td></tr>
</table>
### 🛰️ Traffic inspection, MITM & transparent proxy
<table>
<tr><th align="left">Project</th><th align="center">⭐</th><th align="left">How it inspired OmniRoute</th></tr>
<tr><td nowrap><b><a href="https://github.com/chouzz/llm-interceptor">llm-interceptor</a></b></td><td align="center">49</td><td>MITM interception/analysis of coding-assistant ↔ LLM traffic — our Traffic Inspector ports its SSE merge, conversation normalization, host passthrough and secret masking (MIT).</td></tr>
<tr><td nowrap><b><a href="https://github.com/InterceptSuite/ProxyBridge">ProxyBridge</a></b></td><td align="center">5.5k</td><td>Transparent per-process proxy routing — inspired our crash-safe MITM teardown, socket idle-timeouts, <code>/proc</code> process attribution and TPROXY capture.</td></tr>
<tr><td nowrap><b><a href="https://github.com/chouzz/llm-interceptor">llm-interceptor</a></b></td><td align="center">66</td><td>MITM interception/analysis of coding-assistant ↔ LLM traffic — our Traffic Inspector ports its SSE merge, conversation normalization, host passthrough and secret masking. The upstream's complete license text is still under provenance review.</td></tr>
<tr><td nowrap><b><a href="https://github.com/InterceptSuite/ProxyBridge">ProxyBridge</a></b></td><td align="center">5,995</td><td>Transparent per-process proxy routing — inspired our crash-safe MITM teardown, socket idle-timeouts, <code>/proc</code> process attribution and TPROXY capture.</td></tr>
</table>
### 📚 Model data, observability & UI
<table>
<tr><th align="left">Project</th><th align="center">⭐</th><th align="left">How it inspired OmniRoute</th></tr>
<tr><td nowrap><b><a href="https://github.com/anomalyco/models.dev">models.dev</a></b></td><td align="center">6.0k</td><td>Open database of AI model specs, pricing and capabilities — synced natively into our model catalog.</td></tr>
<tr><td nowrap><b><a href="https://github.com/xyflow/xyflow">React Flow / xyflow</a></b></td><td align="center">37.7k</td><td>The node-based graph library powering our real-time Compression Studio and Combo/Routing Studio.</td></tr>
<tr><td nowrap><b><a href="https://github.com/langchain-ai/langgraph">LangGraph</a></b></td><td align="center">37.6k</td><td>LangGraph Studio's live workflow-graph visualization inspired our Studios' real-time cascade view.</td></tr>
<tr><td nowrap><b><a href="https://github.com/langfuse/langfuse">Langfuse</a></b></td><td align="center">31.4k</td><td>Its trace → span → generation observability model shaped our Compression Studio waterfall.</td></tr>
<tr><td nowrap><b><a href="https://github.com/kiali/kiali">Kiali</a></b></td><td align="center">3.6k</td><td>Istio service-mesh observability — inspired our circuit-breaker badges and error-edge visuals in the Routing/Combo Studio.</td></tr>
<tr><td nowrap><b><a href="https://github.com/lobehub/lobe-icons">lobe-icons</a></b></td><td align="center">2.2k</td><td>AI/LLM brand logos that render the provider icons across our dashboard.</td></tr>
<tr><td nowrap><b><a href="https://github.com/anomalyco/models.dev">models.dev</a></b></td><td align="center">6,555</td><td>Open database of AI model specs, pricing and capabilities — synced natively into our model catalog.</td></tr>
<tr><td nowrap><b><a href="https://github.com/xyflow/xyflow">React Flow / xyflow</a></b></td><td align="center">38,108</td><td>The node-based graph library powering our real-time Compression Studio and Combo/Routing Studio.</td></tr>
<tr><td nowrap><b><a href="https://github.com/langchain-ai/langgraph">LangGraph</a></b></td><td align="center">40,314</td><td>LangGraph Studio's live workflow-graph visualization inspired our Studios' real-time cascade view.</td></tr>
<tr><td nowrap><b><a href="https://github.com/langfuse/langfuse">Langfuse</a></b></td><td align="center">33,592</td><td>Its trace → span → generation observability model shaped our Compression Studio waterfall.</td></tr>
<tr><td nowrap><b><a href="https://github.com/kiali/kiali">Kiali</a></b></td><td align="center">3,631</td><td>Istio service-mesh observability — inspired our circuit-breaker badges and error-edge visuals in the Routing/Combo Studio.</td></tr>
<tr><td nowrap><b><a href="https://github.com/lobehub/lobe-icons">lobe-icons</a></b></td><td align="center">2,428</td><td>AI/LLM brand logos that render the provider icons across our dashboard.</td></tr>
<tr><td nowrap><b><a href="https://github.com/lipis/flag-icons">flag-icons</a></b></td><td align="center">12,354</td><td>Provides the MIT-licensed SVG flags used by the README language selector.</td></tr>
</table>
### 🛡️ Security
<table>
<tr><th align="left">Project</th><th align="center">⭐</th><th align="left">How it inspired OmniRoute</th></tr>
<tr><td nowrap><b><a href="https://github.com/tldrsec/awesome-secure-defaults">awesome-secure-defaults</a></b></td><td align="center">710</td><td>A curated list of secure-by-default libraries that guides our security choices (Helmet.js, DOMPurify, ssrf-req-filter, safe-regex, Google Tink).</td></tr>
<tr><td nowrap><b><a href="https://github.com/tldrsec/awesome-secure-defaults">awesome-secure-defaults</a></b></td><td align="center">721</td><td>A curated list of secure-by-default libraries that guides our security choices (Helmet.js, DOMPurify, ssrf-req-filter, safe-regex, Google Tink).</td></tr>
</table>
### 🧭 Complementary tools
<table>
<tr><th align="left">Project</th><th align="center">⭐</th><th align="left">How it inspired OmniRoute</th></tr>
<tr><td nowrap><b><a href="https://github.com/BlockRunAI/ClawRouter">ClawRouter</a></b></td><td align="center">6,564</td><td>Inspired request deduplication, emergency zero-cost fallback, pluggable Auto-Combo strategies and multilingual intent classification.</td></tr>
<tr><td nowrap><b><a href="https://github.com/lbjlaq/Antigravity-Manager">Antigravity-Manager</a></b></td><td align="center">30,652</td><td>Its account-aware model remapping, executable-path validation and plan-label behavior informed OmniRoute's Antigravity runtime.</td></tr>
<tr><td nowrap><b><a href="https://github.com/jlcodes99/vscode-antigravity-cockpit">vscode-antigravity-cockpit</a></b></td><td align="center">4,817</td><td>Its compact quota-reset countdown format inspired the corresponding provider-limit display in OmniRoute.</td></tr>
<tr><td nowrap><b><a href="https://github.com/iOfficeAI/AionUi">AionUi</a></b></td><td align="center">32,230</td><td>Its ACP integrations inspired OmniRoute's automatic detection of installed CLI agents.</td></tr>
<tr><td nowrap><b><a href="https://github.com/steipete/CodexBar">CodexBar</a></b></td><td align="center">20,507</td><td>Identified the Grok Build quota surface; OmniRoute then verified and corrected the live wire format independently.</td></tr>
</table>
## 📄 License
@@ -1589,7 +1666,7 @@ MIT License - see [LICENSE](LICENSE) for details.
**[⬆ Back to top](#-omniroute)** · Built with ❤️ for the open-source AI community.
<sub>OmniRoute v3.8.49 · Node ≥22.22.2 · MIT License · <a href="https://omniroute.online">omniroute.online</a></sub>
<sub>OmniRoute v3.8.50 · Node ≥22.22.2 · MIT License · <a href="https://omniroute.online">omniroute.online</a></sub>
</div>
<!-- GitHub Discussions enabled for community Q&A -->

View File

@@ -169,7 +169,8 @@ export async function runSetupClaudeCommand(opts = {}) {
let detail = `HTTP ${res.status}`;
try {
const errorBody = await res.json();
const serverMsg = errorBody?.error?.message || errorBody?.error || errorBody?.message || "";
const serverMsg =
errorBody?.error?.message || errorBody?.error || errorBody?.message || "";
if (serverMsg) detail += `${serverMsg}`;
} catch {}
throw new Error(detail);

View File

@@ -5,6 +5,7 @@ import { fileURLToPath } from "node:url";
import { execFile } from "node:child_process";
import { promisify } from "node:util";
import { t } from "../i18n.mjs";
import { npmBin, npmExecOptions } from "../npm-exec.mjs";
const execFileAsync = promisify(execFile);
@@ -31,9 +32,13 @@ export async function getCurrentVersion() {
// they were already on the latest version (#4376). `execFn` is injectable for tests.
export async function getLatestVersion(execFn = execFileAsync) {
try {
const { stdout } = await execFn("npm", ["view", "omniroute", "version", "--prefer-online"], {
timeout: 15000,
});
// argv is all literals, so enabling the shell on win32 cannot splice a
// runtime value into the command line (Hard Rule #13).
const { stdout } = await execFn(
npmBin(),
["view", "omniroute", "version", "--prefer-online"],
npmExecOptions(process.platform, { timeoutMs: 15000 })
);
return stdout.trim();
} catch {
return null;
@@ -114,9 +119,11 @@ export async function runUpdateCommand(opts = {}) {
if (showChangelog) {
try {
const { stdout } = await execFileAsync("npm", ["view", "omniroute", "changelog"], {
timeout: 10000,
});
const { stdout } = await execFileAsync(
npmBin(),
["view", "omniroute", "changelog"],
npmExecOptions(process.platform, { timeoutMs: 15000 })
);
if (stdout.trim()) {
console.log(stdout.trim());
} else {

View File

@@ -26,7 +26,8 @@
"testFailed": "Teste do provedor falhou: {error}",
"loginEnabled": "Login: habilitado (senha atualizada)",
"loginDisabled": "Login: desabilitado",
"providerInfo": "Provedor: {info}"
"providerInfo": "Provedor: {info}",
"opencode": "Instala e configura o plugin @omniroute/opencode-plugin incluído para o OpenCode"
},
"doctor": {
"title": "OmniRoute Doctor",
@@ -254,7 +255,9 @@
"no_recovery": "Desabilitar reinício automático em crash (modo debug)",
"max_restarts": "Máximo de reinícios em 30s antes de desistir (padrão: 2)",
"tray": "Mostrar ícone na bandeja do sistema (apenas desktop, opt-in)",
"no_tray": "Desabilitar ícone na bandeja do sistema"
"no_tray": "Desabilitar ícone na bandeja do sistema",
"tls_cert": "Caminho para um certificado TLS (PEM) para servir HTTPS (também OMNIROUTE_TLS_CERT)",
"tls_key": "Caminho para a chave privada TLS (PEM) para servir HTTPS (também OMNIROUTE_TLS_KEY)"
},
"backup": {
"title": "Backup",

View File

@@ -38,7 +38,8 @@
"testFailed": "提供者测试失败:{error}",
"loginEnabled": "登录:已启用(密码已更新)",
"loginDisabled": "登录:已禁用",
"providerInfo": "提供者:{info}"
"providerInfo": "提供者:{info}",
"opencode": "安装并配置随附的 @omniroute/opencode-plugin 以用于 OpenCode"
},
"doctor": {
"title": "OmniRoute 诊断",
@@ -252,7 +253,9 @@
"no_recovery": "禁用崩溃自动重启(调试模式)",
"max_restarts": "30 秒内的最大崩溃重启次数默认2",
"tray": "显示系统托盘图标(仅桌面,选择加入)",
"no_tray": "禁用系统托盘图标"
"no_tray": "禁用系统托盘图标",
"tls_cert": "用于提供 HTTPS 服务的 TLS 证书PEM路径也可用 OMNIROUTE_TLS_CERT",
"tls_key": "用于提供 HTTPS 服务的 TLS 私钥PEM路径也可用 OMNIROUTE_TLS_KEY"
},
"backup": {
"title": "备份",
@@ -1258,5 +1261,69 @@
"search": "搜索 npm 注册表中的可用插件",
"update": "更新已安装的插件",
"scaffold": "搭建新的插件模板"
},
"authExport": {
"description": "导出已解密的提供者凭据(仅限本地,明文输出)",
"idOpt": "仅导出与此 id/名称/提供者匹配的连接",
"formatOpt": "输出格式json 或 env",
"outOpt": "将输出写入文件而非标准输出(以 0600 权限写入)",
"forceOpt": "确认你了解此操作会打印/写入明文密钥",
"warning": "⚠ 此操作会打印/写入已解密的明文 API 密钥和 OAuth 令牌。请确保你的屏幕、shell 历史记录以及任何输出文件保持私密。",
"confirmHeading": "⚠ 警告:此操作会以明文导出已解密的提供者凭据",
"confirmBody": "此命令会为所选连接解密并打印/写入 apiKey、accessToken、refreshToken 和\nidToken。请将输出视为机密。",
"confirmFooter": "如需确认,请运行:\n omniroute auth export --force",
"missingKey": "导出凭据需要 STORAGE_ENCRYPTION_KEY。",
"notFound": "未找到连接:{id}",
"invalidFormat": "无效格式:{format}。请使用 json 或 env。"
},
"radar": {
"description": "检查并同步本地 Radar 目录订阅源",
"status": "显示本地 Radar 设置和订阅源缓存状态",
"sync": "通过本地服务器同步目录、推荐、优惠和 Intel"
},
"launch": {
"description": "启动指向 OmniRoute 的 Claude Code本地或远程使用 --profile",
"token": "Claude 客户端应发送的令牌ANTHROPIC_AUTH_TOKEN",
"notRunning": "无法在 {port} 访问 OmniRoute。请使用 “omniroute serve” 启动它。",
"notFound": "在 PATH 中未找到 “claude” CLI。"
},
"run": {
"description": "通过 OmniRoute 启动受支持的 CLI 目标"
},
"setupClaude": {
"description": "从 OmniRoute 模型目录生成 ~/.claude/profiles 的 Claude Code 配置文件"
},
"connect": {
"description": "连接到远程 OmniRoute 服务器并进入远程模式"
},
"tokens": {
"description": "管理限定范围的 CLI 访问令牌(远程模式)"
},
"configure": {
"description": "从活动服务器选择提供者+模型并配置受支持的本地 CLI"
},
"launchCodex": {
"description": "启动指向 OmniRoute 的 Codex CLI本地或远程 VPS"
},
"setupCodex": {
"description": "从 OmniRoute 实时模型目录生成 ~/.codex 配置文件"
},
"packs": {
"description": "管理可选的运行时包ML / 浏览器自动化)",
"listDescription": "列出可选包及其安装状态",
"installDescription": "将可选包安装到 DATA_DIR",
"verifyDescription": "根据随附的校验和索引验证已安装的包",
"removeDescription": "移除已安装的可选包",
"sourceOpt": "存放包负载和包索引的目录",
"warnNoIndex": "未找到 optional-packs.index.json —— 此检出无法进行安装/验证(桌面捆绑包会附带它)",
"errUnknown": "未知的包:{name}",
"errNoIndex": "未找到包索引;请通过 --source <dir> 传入存放包负载的目录(桌面捆绑包会将其附带在应用旁)",
"installed": "包 “{name}” 已安装并在 {dir} 验证通过",
"restartHint": "请重启 OmniRoute 服务器(或桌面应用),以便运行时加载该包",
"removed": "包 “{name}” 已移除",
"notInstalled": "包 “{name}” 未安装",
"verifyOk": "所有已安装的包均已验证通过",
"verifyFailed": "{count} 个包验证失败",
"noneInstalled": "未安装可选包"
}
}

View File

@@ -38,7 +38,8 @@
"testFailed": "提供者測試失敗:{error}",
"loginEnabled": "登入:已啟用(密碼已更新)",
"loginDisabled": "登入:已停用",
"providerInfo": "提供者:{info}"
"providerInfo": "提供者:{info}",
"opencode": "安裝並配置隨附的 @omniroute/opencode-plugin 以用於 OpenCode"
},
"doctor": {
"title": "OmniRoute 診斷",
@@ -252,7 +253,9 @@
"no_recovery": "停用崩潰自動重啟(除錯模式)",
"max_restarts": "30 秒內的最大崩潰重啟次數預設2",
"tray": "顯示系統托盤圖示(僅桌面,選擇加入)",
"no_tray": "停用系統托盤圖示"
"no_tray": "停用系統托盤圖示",
"tls_cert": "用於提供 HTTPS 服務的 TLS 憑證PEM路徑也可用 OMNIROUTE_TLS_CERT",
"tls_key": "用於提供 HTTPS 服務的 TLS 私鑰PEM路徑也可用 OMNIROUTE_TLS_KEY"
},
"backup": {
"title": "備份",
@@ -1258,5 +1261,69 @@
"search": "搜尋 npm 登錄檔中的可用外掛",
"update": "更新已安裝的外掛",
"scaffold": "搭建新的外掛模板"
},
"authExport": {
"description": "匯出已解密的提供者憑據(僅限本機,明文輸出)",
"idOpt": "僅匯出與此 id/名稱/提供者相符的連線",
"formatOpt": "輸出格式json 或 env",
"outOpt": "將輸出寫入檔案而非標準輸出(以 0600 權限寫入)",
"forceOpt": "確認你了解此操作會列印/寫入明文密鑰",
"warning": "⚠ 此操作會列印/寫入已解密的明文 API 金鑰和 OAuth 令牌。請確保你的螢幕、shell 歷史記錄以及任何輸出檔案保持私密。",
"confirmHeading": "⚠ 警告:此操作會以明文匯出已解密的提供者憑據",
"confirmBody": "此命令會為所選連線解密並列印/寫入 apiKey、accessToken、refreshToken 和\nidToken。請將輸出視為機密。",
"confirmFooter": "如需確認,請執行:\n omniroute auth export --force",
"missingKey": "匯出憑據需要 STORAGE_ENCRYPTION_KEY。",
"notFound": "找不到連線:{id}",
"invalidFormat": "無效格式:{format}。請使用 json 或 env。"
},
"radar": {
"description": "檢查並同步本機 Radar 目錄訂閱來源",
"status": "顯示本機 Radar 設定和訂閱來源快取狀態",
"sync": "透過本機伺服器同步目錄、推薦、優惠和 Intel"
},
"launch": {
"description": "啟動指向 OmniRoute 的 Claude Code本機或遠端使用 --profile",
"token": "Claude 用戶端應傳送的令牌ANTHROPIC_AUTH_TOKEN",
"notRunning": "無法在 {port} 存取 OmniRoute。請使用「omniroute serve」啟動它。",
"notFound": "在 PATH 中找不到「claude」CLI。"
},
"run": {
"description": "透過 OmniRoute 啟動受支援的 CLI 目標"
},
"setupClaude": {
"description": "從 OmniRoute 模型目錄產生 ~/.claude/profiles 的 Claude Code 配置檔"
},
"connect": {
"description": "連線到遠端 OmniRoute 伺服器並進入遠端模式"
},
"tokens": {
"description": "管理限定範圍的 CLI 存取令牌(遠端模式)"
},
"configure": {
"description": "從使用中的伺服器選擇提供者+模型並配置受支援的本機 CLI"
},
"launchCodex": {
"description": "啟動指向 OmniRoute 的 Codex CLI本機或遠端 VPS"
},
"setupCodex": {
"description": "從 OmniRoute 即時模型目錄產生 ~/.codex 配置檔"
},
"packs": {
"description": "管理可選的執行階段套件ML / 瀏覽器自動化)",
"listDescription": "列出可選套件及其安裝狀態",
"installDescription": "將可選套件安裝到 DATA_DIR",
"verifyDescription": "根據隨附的總和檢查碼索引驗證已安裝的套件",
"removeDescription": "移除已安裝的可選套件",
"sourceOpt": "存放套件負載和套件索引的目錄",
"warnNoIndex": "找不到 optional-packs.index.json —— 此檢出無法進行安裝/驗證(桌面套件會隨附它)",
"errUnknown": "未知的套件:{name}",
"errNoIndex": "找不到套件索引;請透過 --source <dir> 傳入存放套件負載的目錄(桌面套件會將其隨附在應用程式旁)",
"installed": "套件「{name}」已安裝並在 {dir} 驗證通過",
"restartHint": "請重新啟動 OmniRoute 伺服器(或桌面應用程式),以便執行階段載入該套件",
"removed": "套件「{name}」已移除",
"notInstalled": "套件「{name}」未安裝",
"verifyOk": "所有已安裝的套件均已驗證通過",
"verifyFailed": "{count} 個套件驗證失敗",
"noneInstalled": "未安裝可選套件"
}
}

34
bin/cli/npm-exec.mjs Normal file
View File

@@ -0,0 +1,34 @@
// Spawning npm from the CLI, on every platform.
//
// On Windows npm is `npm.cmd`, a batch wrapper. Node ≥ 24 refuses to spawn a
// `.cmd` without a shell (nodejs/node#52554), and a bare `npm` can additionally
// resolve to an extensionless shim that `CreateProcess` cannot execute — so the
// call fails with `EINVAL` or `ENOENT` while npm works fine in the same terminal.
// `src/lib/services/installers/utils.ts` already solves this for the server; this
// is the same rule for the `bin/cli` entry points, which cannot import TypeScript.
//
// SECURITY (Hard Rule #13): enabling the shell means the SHELL splits the command
// line, not `execFile`. Every argv element passed alongside these options must be
// a literal — never a runtime value — or it must be validated first. Callers that
// need to pass a user-supplied name have to guard it themselves.
/** The npm binary to spawn on this platform. */
export function npmBin(platform = process.platform) {
const isBun = Boolean(process.versions.bun);
if (platform === "win32") return isBun ? "bun.exe" : "npm.cmd";
return isBun ? "bun" : "npm";
}
/**
* `execFile` / `spawnSync` options for an npm call.
*
* @param {NodeJS.Platform} platform
* @param {{ timeoutMs?: number, stdio?: string }} [options]
*/
export function npmExecOptions(platform = process.platform, options = {}) {
const base = {};
if (options.timeoutMs !== undefined) base.timeout = options.timeoutMs;
if (options.stdio !== undefined) base.stdio = options.stdio;
if (platform !== "win32") return { ...base, shell: false };
return { ...base, shell: true, windowsHide: true };
}

View File

@@ -2,6 +2,7 @@ import { existsSync, mkdirSync, writeFileSync, chmodSync } from "node:fs";
import { join } from "node:path";
import { homedir } from "node:os";
import { execSync } from "node:child_process";
import { pathToFileURL } from "node:url";
const RUNTIME_DIR = join(homedir(), ".omniroute", "runtime");
// systray2 is a maintained fork with prebuilt binaries — installed lazily at runtime,
@@ -16,6 +17,16 @@ export const SYSTRAY_PACKAGE = "systray2";
export const SYSTRAY_VERSION = "2.1.4";
const SYSTRAY_SPEC = `${SYSTRAY_PACKAGE}@${SYSTRAY_VERSION}`;
// Dynamic `import()` resolves its specifier as a URL, not a filesystem path.
// On Windows the lazily-installed systray2 lives at an absolute path whose
// leading drive letter the ESM loader parses as an unsupported URL scheme
// (e.g. `c:`) and rejects. Build a file:// URL so the tray import works on
// Windows too. Same defect fixed for the CLI db-fallback imports in #11238,
// missed at this call site.
export function systrayModuleSpecifier(runtimeDir: string): string {
return pathToFileURL(join(runtimeDir, "node_modules", SYSTRAY_PACKAGE)).href;
}
export function resolveSystrayBinName(platform: NodeJS.Platform): string | null {
if (platform === "win32") return "tray_windows_release.exe";
if (platform === "darwin") return "tray_darwin_release";
@@ -60,8 +71,7 @@ export async function loadSystray(): Promise<(new (...args: unknown[]) => unknow
// drop the +x bit on extraction (observed on macOS).
chmodSystrayBinAt(RUNTIME_DIR, process.platform);
try {
const modPath = join(RUNTIME_DIR, "node_modules", SYSTRAY_PACKAGE);
const mod = await import(modPath);
const mod = await import(systrayModuleSpecifier(RUNTIME_DIR));
return (mod.default ?? mod.SysTray ?? mod) as (new (...args: unknown[]) => unknown) | null;
} catch (err) {
console.warn(`[omniroute] tray runtime import failed: ${(err as Error).message}`);

View File

@@ -114,10 +114,13 @@ function writeLinuxSystemdUnit(cliPath) {
const unitDir = dirname(linuxSystemdUnitPath());
mkdirSync(unitDir, { recursive: true });
const envFile = join(userHomeDir(), ".omniroute", ".env");
const nodeBinDir = dirname(process.execPath);
const userLocalBin = join(userHomeDir(), ".local", "bin");
const pathEnv = `${nodeBinDir}:${userLocalBin}:/usr/local/sbin:/usr/local/bin:/usr/bin:/bin`;
const lines = [
"[Unit]",
"Description=OmniRoute AI proxy router",
"After=network-online.target",
"After=network-online.target graphical-session.target",
"Wants=network-online.target",
"",
"[Service]",
@@ -134,6 +137,7 @@ function writeLinuxSystemdUnit(cliPath) {
`ExecStart=${buildServeExecLine(cliPath, { tray: false })}`,
"Restart=on-failure",
"RestartSec=5",
`Environment="PATH=${pathEnv}"`,
];
if (existsSync(envFile)) lines.push(`EnvironmentFile=-${envFile}`);
lines.push("", "[Install]", "WantedBy=default.target", "");

View File

@@ -1,2 +0,0 @@
- **feat(admission):** add lane-aware admission probes for combo/fusion/chaos fan-out (fail-open, queueing disabled), an env-wins `OMNIROUTE_CHAT_VIRTUAL_LANES` activation flag applied at boot, and adaptive-lane visibility in the `omniroute_get_health` MCP tool (related to #9654)
- **docs(mcp):** complete the MCP server README tool reference so the `schemas/` catalog is fully covered (agent-skills, oneproxy, web, tool-search, combo/routing, pricing and DB-health tools were previously only discoverable via `omniroute_tool_search`)

View File

@@ -1 +0,0 @@
- **feat(cli):** container-aware auto-config — `setup-*`, `omniroute configure`, `omniroute config set` and the CLI-tool config APIs now refuse to write into a containerised OmniRoute's ephemeral home (CLI exits `2`, API returns `422` with `containerEphemeralTarget`) and point at the host-CLI or bind-mount setup instead; `--allow-container-write` / `OMNIROUTE_ALLOW_CONTAINER_CONFIG_WRITE=true` opt back in. Also fixes `CLI_CONFIG_HOME` so the Compose `host` profile's `/host-home` bind mounts are honoured instead of silently falling back to the container home. (#10057)

View File

@@ -1 +0,0 @@
- feat(dashboard): opt-in `DASHBOARD_ALLOW_EMBED=vscode` relaxes CSP `frame-ancestors` to `'self' vscode-webview:` and drops `X-Frame-Options` for HTML pages only, so the dashboard renders inside the VS Code Simple Browser (OmniCopilot). Default posture unchanged — API routes stay unframable (#10273)

View File

@@ -1 +0,0 @@
- **feat(resilience):** warn when `/healthz` is served under event-loop lag ≥200ms so a slow 200 is visible as sick, not healthy ([#10303](https://github.com/diegosouzapw/OmniRoute/issues/10303))

View File

@@ -1 +0,0 @@
- **feat(docker):** add `GET`/`HEAD` `/livez` as a process-alive probe, distinct from `/healthz` readiness ([#10316](https://github.com/diegosouzapw/OmniRoute/issues/10316))

View File

@@ -1 +0,0 @@
- feat(providers): add **Cloudflare AI Playground** as a No Auth provider (`cloudflare-playground`, alias `cfp`) — free anonymous chat over the reverse-engineered `cf_agent` WebSocket protocol (PartySocket transport, no account/API key/cookies) with GLM 5.2, Kimi K2.7 Code, DeepSeek V4 Pro, gpt-oss-120B, Llama 3.3 70B, Qwen2.5 Coder 32B and 14 more curated models. The executor drives a headless Chromium via Playwright (the WS upgrade is TLS-fingerprint-gated), translates the `cf_agent` frame stream into OpenAI SSE, and surfaces upstream rate limits (3021) as HTTP 429. Fixes #10389

View File

@@ -1,2 +0,0 @@
- **feat(providers):** AI Horde accepts an optional registered API key and advertises only live image models that currently have workers ([#10542](https://github.com/diegosouzapw/OmniRoute/pull/10542))
- **fix(providers):** AI Horde Check validates keys via `/v2/find_user` instead of the unauthenticated OpenAI models list ([#10542](https://github.com/diegosouzapw/OmniRoute/pull/10542))

View File

@@ -1 +0,0 @@
- **feat(providers):** complete Jina AI as one credential pool — dashboard `jina-ai` / `jina-reader` share a token, `JINA_AI_API_KEY` is a real fallback, Test probes `GET https://api.jina.ai/v1/models` (embeddings fallback hits `jina-embeddings-v5-omni-small`), embed/rerank logs keep `connection_id`, catalog adds `jina-reranker-v3.5`, Omni v5 multimodal `{text}`/`{image}`/`{content}` docs pass through intact, and OmniRoute proxies classify / segment / `jina-search` (`s.jina.ai`). Reader stays a separate `r.jina.ai` card with an explicit label. Gemini Embedding 2 (`gemini/gemini-embedding-2`, alias `google/gemini-embedding-2`) uses dashboard `gemini` keys (or `GEMINI_API_KEY` / `GOOGLE_API_KEY` only when none exist), forwards native multimodal parts, and maps N OpenAI `input` items to N `:batchEmbedContents` vectors instead of one aggregated `:embedContent`. ([#10581](https://github.com/diegosouzapw/OmniRoute/pull/10581))

View File

@@ -1 +0,0 @@
- **feat(providers):** accept `response_format=ogg` on `/v1/audio/speech` as an alias for the existing Opus/Ogg encoder ([#10587](https://github.com/diegosouzapw/OmniRoute/issues/10587))

View File

@@ -1 +0,0 @@
- **feat(settings):** add `autoDisableBannedScope` so permanent-ban auto-disable can target subscription/OAuth accounts only, leaving prepaid API keys in the routing pool ([#10617](https://github.com/diegosouzapw/OmniRoute/pull/10617))

View File

@@ -1 +0,0 @@
- feat(server): emit systemd sd_notify READY/WATCHDOG/STOPPING (generated unit becomes Type=notify with WatchdogSec=180) so a frozen server process is killed and restarted by systemd instead of lingering undetected

View File

@@ -1,2 +0,0 @@
- **feat(providers):** add the TabiToken NewAPI gateway (`tabitoken`) and teach the existing HCNSec entry (`hcnsec`) the three further protocols it actually serves. TabiToken leaves the NewAPI pricing endpoint public, so its catalog is read from the host rather than guessed: four Claude models, each reporting the Anthropic and OpenAI protocols. HCNSec shipped OpenAI-only; probing the host showed `/v1/messages`, `/v1/responses` and the Gemini `/v1beta` path all reach its token layer, so each is now declared as an alternate format — with its default format, base URL, auth scheme and regional catalog classification untouched. ([#10668](https://github.com/diegosouzapw/OmniRoute/pull/10668)) — thanks @yawar-aquil
- **feat(sse):** allow an alternate protocol to build its own upstream URL. `AlternateFormat` gained an optional `urlBuilder`, because the Gemini protocol carries the model inside the path (`{base}/{model}:generateContent`) and the existing `chatPath`/`urlSuffix` fields are constants that cannot express it. The route builder is extracted as `buildGeminiGenerateContentUrl` and shared with the native `gemini` provider so the two consumers cannot drift on the `?alt=sse` streaming suffix. ([#10668](https://github.com/diegosouzapw/OmniRoute/pull/10668)) — thanks @yawar-aquil

View File

@@ -1 +0,0 @@
- **feat(call_logs):** persist the per-call error family in `call_logs.error_type` and expose a failure breakdown (`errorBreakdown`) in the usage analytics endpoint, reusing the existing production classifier ([#10670](https://github.com/diegosouzapw/OmniRoute/issues/10670))

View File

@@ -1 +0,0 @@
- **feat(proxy):** the proxy-health sweep and `GET /api/settings/proxies/egress` now report an anonymous summary of egress-IP sharing — how many rotation groups share an egress IP and the largest number of accounts behind one IP — computed from persisted `proxy_logs` over a 24h window. No IPs and no account identities by default; `PROXY_LOG_INCLUDE_IPS=true` restores raw details. ([#10677](https://github.com/diegosouzapw/OmniRoute/issues/10677))

View File

@@ -1 +0,0 @@
- **docs(guides):** OmniRoute now serves VS Code's **native Copilot Chat model picker** through the [OmniCopilot](https://github.com/diegosouzapw/OmniCopilot) extension ([Marketplace](https://marketplace.visualstudio.com/items?itemName=diegosouzapw.omnicopilot) · [Open VSX](https://open-vsx.org/extension/diegosouzapw/omnicopilot) — Cursor, Windsurf, VSCodium, Theia…) — no Copilot subscription needed since VS Code 1.122. New [`docs/guides/VSCODE-COPILOT.md`](docs/guides/VSCODE-COPILOT.md) covers setup, how the picker collapses the `dual`-prefix catalog via `GET /v1/models?prefix=alias`, and the **build-time** `DASHBOARD_ALLOW_EMBED=vscode` flag that renders the dashboard in an editor tab ([#10697](https://github.com/diegosouzapw/OmniRoute/pull/10697))

View File

@@ -1 +0,0 @@
- **feat(docker):** `DASHBOARD_ALLOW_EMBED` is now a Docker build argument — `docker build --build-arg DASHBOARD_ALLOW_EMBED=vscode` produces an image whose dashboard renders inside the VS Code Simple Browser (OmniCopilot's `dashboardOpen: "editor"`). Previously the flag was only reachable from a source build: Docker silently drops a `--build-arg` with no matching `ARG`, so the operator got the default image and no error. Builder-stage only and empty by default — the runtime stages deliberately do not carry it, and the unframable default posture is unchanged ([#10701](https://github.com/diegosouzapw/OmniRoute/pull/10701))

View File

@@ -1 +0,0 @@
- **feat(providers):** new `cursor-api` provider (card "Cursor API", alias `cua`): connect a Cursor user API key (`crsr_…`) and route `cursor-api/<model>` through the existing Cursor agent executor (the key is exchanged for a 1h session token and cached), plus a `/api/cursor-cli/*` passthrough so the Cursor CLI itself runs through OmniRoute (`CURSOR_API_ENDPOINT=http://<omniroute>/api/cursor-cli`, `CURSOR_API_KEY=<OmniRoute key>`) with every RPC attributed and logged. The IDE `cursor` provider is unchanged. (#10729)

View File

@@ -1 +0,0 @@
- **feat(api):** `GET /api/health` now answers `{ status, timestamp }` without a key. Until now the path had no route, so the management-auth boundary answered first with a 401 — indistinguishable from a wrong key or an unknown route, which left Docker HEALTHCHECKs and Kubernetes probes unable to tell "down" from "misconfigured". Kept deliberately minimal: version, uptime and memory stay behind the authenticated `/api/monitoring/health` ([#PRNUM](https://github.com/diegosouzapw/OmniRoute/pull/10771)).

View File

@@ -1 +0,0 @@
- feat(routing): make Task-Aware Smart Routing's detection patterns operator-configurable via `settings.taskRouting.patternOverrides` (`PUT /api/settings/task-routing`) — the built-in patterns are English-only, so a non-English dashboard had no recourse short of turning detection off entirely; an override now replaces the pattern list for one task type without touching the rest (#10783)

View File

@@ -1 +0,0 @@
- feat(api): accept PATCH on /api/combos/[id], the verb the OpenAPI spec already documents (#10869)

View File

@@ -1 +0,0 @@
- **feat(sse):** add GLM-5.3 support (`glm-5.3`, `glm-5.3-high`, `glm-5.3-low`) across the z.ai first-party providers, mapping the upstream `reasoning_effort` request parameter to the existing 5.2 tier UX ([#10896](https://github.com/diegosouzapw/OmniRoute/pull/10896)) — thanks @phuongddx

View File

@@ -1 +0,0 @@
- **feat(home):** add a live **Recent Requests** panel beside the home Provider Topology (polls `GET /api/usage/call-logs?excludeTests=1` every ~3s, gated by the topology appearance toggle + page visibility). `excludeTests` is now an allowlist of real provider inference (`/v1/%` or `/api/v1/%`), applied before `LIMIT`, so connection-test/model-sync/management rows can never leak into the feed ([#10897](https://github.com/diegosouzapw/OmniRoute/pull/10897), extracted from [#8450](https://github.com/diegosouzapw/OmniRoute/pull/8450)) — thanks @nguyenha935

View File

@@ -1 +0,0 @@
- **feat(rankings):** free provider rankings now expose a `reliability` field (raw `testStatus`/`rateLimitedUntil` per connection plus a `healthy`/`degraded`/`down` state, reusing the `ProviderHealthState` vocabulary of the provider health matrix) when the configured/available filters are active — derived from already-loaded data, without touching the ranking order ([#10909](https://github.com/diegosouzapw/OmniRoute/pull/10909))

View File

@@ -1,8 +0,0 @@
- `feat(resilience)`: when an allowlisted provider (opencode family) answers
429 classified `quota_exhausted` or `rate_limit_exceeded` and its free-tier
quota is bucketed by egress IP (#9611), every connection of that family
sharing the IP is cooled down together before the rotation tries them — one
guaranteed-failed upstream call per episode instead of N, on the combo path
as well. For the allowlisted family a 429 now cools the connection instead
of locking a single model. Exclusive allowlist, never terminal, best-effort
when the egress IP is unknown (#10920).

View File

@@ -1 +0,0 @@
- **feat(rankings):** free provider rankings can now report what each provider actually served — `reliability.usage` (requests, successes, success rate over a window) behind the opt-in `withUsage`/`usageRange` query parameters, so a provider that answers every call with an error is no longer described as healthy ([#10926](https://github.com/diegosouzapw/OmniRoute/pull/10926))

View File

@@ -1 +0,0 @@
- **feat(providers):** add Logfare as a free OpenAI-compatible provider — dashboard card with a Free badge and request-logging disclosure (every prompt/completion is logged for research; opt out at logfare.ai/consent), live model discovery from `https://logfare.ai/v1/models` (20 models, 11 chat-capable: kimi-k3, deepseek-v4-pro, glm-5.2, gpt-5.6-luna, minimax-m3…), full chat/streaming through the existing OpenAI-compatible path, the real Logfare logo on the card, and a listing in the free-tiers guide. ([#10987](https://github.com/diegosouzapw/OmniRoute/pull/10987))

View File

@@ -1 +0,0 @@
- **feat(providers):** let operators declare per-provider error rules through `settings.providerErrorRules` instead of patching the catalog — an operator-supplied rule for a provider is consulted before the built-in `providerRuleRegistry`, receives the raw error text, and has its declared scope/cooldown/reason actually honored end to end, for any provider (declaring the rule is the opt-in — no extra allowlist entry needed). Matches are plain case-insensitive substrings (never RegExp) and bounded to 50 rules to keep the hot path safe ([#11104](https://github.com/diegosouzapw/OmniRoute/pull/11104))

View File

@@ -1 +0,0 @@
- **feat(combo):** the shared per-request combo attempt budget is now operator-configurable via `maxGlobalAttempts` (combo config / `comboDefaults` cascade), instead of the hardcoded 30. Lower it to fail fast on a dead target pool, raise it for large combos; clamped to `[1, 200]` so an unbounded budget can never cause runaway background requests ([#11134](https://github.com/diegosouzapw/OmniRoute/issues/11134))

View File

@@ -1 +0,0 @@
- **feat(api):** `/api/usage/om-usage` gains a structured form — `?format=json` returns the key's own usage as `ApiKeyUsageLimitStatus` + `UsageSnapshot` instead of `text/plain`. This is the surface a UI (the OmniCopilot panel) consumes to show a key holder their daily/weekly spend and quota reset. The route is self-service (the caller's own key, gated by `allowUsageCommand`), not the management surface; refusals come back as a discriminated `{ "allowed": false, "error": … }` so a UI can tell "not allowed" apart from "allowed but nothing cached yet". The endpoint was previously undocumented in `API_REFERENCE.md`; it now has a section ([#11190](https://github.com/diegosouzapw/OmniRoute/pull/11190))

View File

@@ -1 +0,0 @@
- **feat(api):** `/api/usage/om-usage?format=json` now returns `providers[]` — every connection's quota snapshot, not just the single selected one — so a panel can render Codex / Claude / OpenCode side by side. The collector already gathered all of them; the single-pick `provider` field (kept) is a terminal presentation choice. Closes the per-connection gap from OmniCopilot #8 ([#11192](https://github.com/diegosouzapw/OmniRoute/pull/11192))

View File

@@ -1 +0,0 @@
- **feat(providers):** allow overriding the rate-limit queue wait timeout (`maxWaitMs`) per connection, alongside the existing `rpm`/`tpm`/`tpd`/`minTime`/`maxConcurrent` overrides — a single slow provider no longer has to lower the global wait budget for every other provider (#11251)

View File

@@ -1 +0,0 @@
- **feat(dashboard):** replace the hard Home → onboarding redirect with a dismissable first-run readiness card so returning users can stay on Home while new users still get a clear 4-step path ([#11282](https://github.com/diegosouzapw/OmniRoute/pull/11282))

View File

@@ -1 +0,0 @@
- **feat(dashboard):** lead Traffic Inspector with a purpose-first header that separates "what happened" from "how it happened", so beginners can read request outcomes without drowning in protocol detail ([#11283](https://github.com/diegosouzapw/OmniRoute/pull/11283))

View File

@@ -1 +0,0 @@
- **feat(dashboard):** add an Essentials sidebar preset that shows only the beginner core path (Home → Endpoints → API Keys → Providers → Health → Settings) while keeping Advanced tools reachable via Command Palette search ([#11286](https://github.com/diegosouzapw/OmniRoute/pull/11286))

View File

@@ -1,2 +0,0 @@
- **feat(credential-health):** pace the credential health sweep per connection via `provider_connections.healthCheckInterval` (minutes, 0 = never), with `CREDENTIAL_HEALTH_CHECK_INTERVAL` as the global default ([#8443](https://github.com/diegosouzapw/OmniRoute/issues/8443))
- **behavior change:** `healthCheckInterval` is a shared column — it paces both the OAuth token refresh and the credential health sweep, and `0` disables both. The connection editor defaults it to 60, so configured OAuth connections are now credential-checked at 60min instead of the previous ~10min (aligned with the probe-volume goal of #8443)

View File

@@ -1 +0,0 @@
- **feat(providers):** publish Poolside's Laguna Preview catalog statically — `poolside/laguna-xs-2.1` and `poolside/laguna-s-2.1` (262144 context, 32768 max completion, tools + reasoning, text-only), so the models are routable and visible before a key is configured instead of only after live discovery. Pins the catalog form of the XS id against the `laguna-xs.2` variant carried by third-party listings. ([#9085](https://github.com/diegosouzapw/OmniRoute/issues/9085))

View File

@@ -1 +0,0 @@
- feat(modality-bridge): bridge Chat and Responses video parts through a strict trusted-loopback, quota-bounded FFmpeg broker; enforce HTTPS redirects/SSRF plus format, protocol, stream, pixel, frame, 50 MiB broker/remote, 36 MiB inline, and 120-second limits; propagate caller aborts; preserve the actual successful fallback model through cache/meta/headers; expose sampled latency and honest success telemetry; and ship the localized Video settings UI (#9760)

View File

@@ -1 +0,0 @@
- **feat(radar):** Persist local model display-name/enabled overrides and hide/restore tombstones, with authenticated catalog controls and feed safety precedence ([#9830](https://github.com/diegosouzapw/OmniRoute/pull/9830))

View File

@@ -1 +0,0 @@
- **feat(radar):** add curated-family combo suggestions, a guided combo page, and the read-only Radar MCP catalog tool ([#9836](https://github.com/diegosouzapw/OmniRoute/pull/9836))

View File

@@ -1 +0,0 @@
- **feat(radar):** add a signed live offers feed and supporter offers dashboard ([#9912](https://github.com/diegosouzapw/OmniRoute/pull/9912))

View File

@@ -1 +0,0 @@
- **feat(radar):** add signed Intel insights, supporter recognition, and local Radar CLI commands ([#9923](https://github.com/diegosouzapw/OmniRoute/pull/9923))

View File

@@ -1 +0,0 @@
- **feat(radar):** add a localized public news feed and dismissible dashboard launch banner, with the Radar announcement staged inactive for a separately authorized launch ([#9926](https://github.com/diegosouzapw/OmniRoute/pull/9926))

View File

@@ -1 +0,0 @@
- feat(command-code): advertise low/medium/high/xhigh/max reasoning-effort suffixes for reasoning-capable models in the catalog and Combo Builder, with request-time resolution to reasoning_effort

View File

@@ -1 +0,0 @@
- feat(crof): advertise reasoning-effort tiers (none/low/medium/high/max) for live-discovered and seed models, so the catalog, Playground, and Combo Builder surface <model>-<tier> aliases and requests resolve max upstream

View File

@@ -1 +0,0 @@
- feat(sse): add Cursor plan image generation via Agent CLI (`IMAGE_PROVIDERS.cursor`, format `cursor-agent-image`), reusing the chat Cursor OAuth connection

View File

@@ -1 +0,0 @@
- feat(routing): add the default-off `DISABLE_CONTEXT_WINDOW_CHECKS` feature flag to let operators bypass OmniRoute's local context-window and max-input-token check for direct single-model requests, leaving upstream limits, prompt compression, and output-token caps intact.

View File

@@ -1 +0,0 @@
- **feat(catalog):** surface runtime-learned `reasoning_effort` tiers in `/v1/models` `capabilities.effort_tiers` (learned set replaces synced metadata when present), map them to OpenCode `ModelV2.variants` in the OmniRoute plugin, and align dispatch `-<tier>` suffix validation to the effective (learned ?? synced) set — so the UI offers exactly the tiers the upstream accepts (e.g. `{low, high, max}` for `oc/x-preview-f-free`) and each advertised variant completes. Excludes codex/glm/kimi, which keep their own dedicated `-{effort}` suffix mechanism and never gain `effort_tiers` from this path (related to #7694, builds on #11232)

View File

@@ -1 +0,0 @@
- **feat(usage):** show Kimi Coding's fixed-order Code 5-hour/7-day quota windows plus Extra Usage status, balance, monthly spend/limit, and the official Additional Credits link on Dashboard → Quota cards.

View File

@@ -1 +0,0 @@
- **feat(providers):** copilot-m365-web now supports OpenAI tool calling — a router planning turn asks the substrate model (as a tool-selection assistant emitting `CALL_TOOL: name({...})` / `NO_TOOL_NEEDED` text, which bypasses its plugin-registry refusal) and validated decisions surface as `tool_calls` with `finish_reason: "tool_calls"` in both stream and non-stream modes; also flattens the full message history (assistant `tool_calls` + compacted tool results) so multi-turn agent loops keep context, replies to SignalR `type:6` keepalives, surfaces `type:3` error frames instead of a silent empty `stop`, and suppresses `writeAtCursor` text from tool-progress frames

View File

@@ -1 +0,0 @@
- **feat(api):** add `GET`/`POST` `/v1/multimodal-embeddings` as an alias of `/v1/embeddings` so Jina-compatible clients do not receive HTTP 404 `unknown_route` — thanks @RaviTharuma

View File

@@ -1 +0,0 @@
- feat(opencode-go): expose Muse Spark 1.2 Contributor reasoning-effort aliases (minimal/low/medium/high/xhigh) in the Combo Builder

View File

@@ -1 +0,0 @@
- **feat(providers):** restore the operator-owned upstream timeout tier per connection via `providerSpecificData.timeoutMs` (preempts the maintainer-only model/provider registry tiers and the global `FETCH_TIMEOUT_MS`), and make the combo per-target timeout ceiling follow the selected connection

View File

@@ -1 +0,0 @@
- **feat(cli):** run `omniroute serve --tray` as a detached desktop process after server and tray readiness, with graphical login auto-start support.

View File

@@ -1 +0,0 @@
- **feat(routing):** add client-, provider-, and model-neutral exclusive managed session connection leases with API-key-bound generation fencing, durable SQLite ownership, explicit allowlist policy, and bounded 429 capacity retry semantics.

View File

@@ -1 +0,0 @@
- **Passthrough streaming:** stop leaking upstream SSE control lines (`id:`/`event:`/`retry:`/`:` comments) to plain OpenAI Chat-Completions-format clients, while preserving `event:` framing for OpenAI Responses API and Claude Messages API passthrough ([#10017](https://github.com/diegosouzapw/OmniRoute/issues/10017)).

View File

@@ -1 +0,0 @@
- fix(cli): stop diagnosing every Next.js instrumentation-hook failure as the Android/Termux cache bug — only the Android "Unsupported platform: android" signal now triggers the Android hint, so a win32/desktop instrumentation error surfaces its real cause instead of a useless `mkdir -p ~/.cache` (#10028)

View File

@@ -1 +0,0 @@
- **fix(build):** stop the native `better-sqlite3` addon from loading during the Next.js production build (#10060). Its `Statement` destructor aborts with `SIGABRT` when a build worker thread exits (assertion in `node::RemoveEnvironmentCleanupHook`, `env == nullptr`), which can leave the build with no standalone bundle. Every DB entry point now keys off a reliable `OMNIROUTE_BUILDING=1` signal (set by `build-next-isolated.mjs` and inherited by every spawned build worker, because Next.js workers sometimes drop `NEXT_PHASE`): `getDbInstance()` returns a no-op SQLite stub during build, `driverFactory` skips the native driver and falls through to `node:sqlite`, and the `codegraph`/`kiro-import` lazy loaders fail closed. A build-time `better-sqlite3` alias to a stub (`next.config.mjs`, turbopack) backs this up without changing runtime behaviour (the real package is still `require()`d natively via `serverExternalPackages`). Also raises the default build heap 4096→6144 MB and caps Next build worker pools (`CIRCLE_NODE_TOTAL=8`) to avoid the many-core page-data-collection SIGSEGV, and adds `.gitattributes` (`*.sh text eol=lf`) so kernel-exec'd shell scripts never ship with CRLF shebangs. Deliberately does NOT downgrade the Node base image: per the maintainer's review on #10060, `release/v3.8.50` moved to `node:26-trixie-slim` through several considered commits, so the `OMNIROUTE_BUILDING` guard is re-derived against the current base rather than reverting the FROM line; the npm pin and binary-hide dance from the original PR are dropped because our build already rebuilds `better-sqlite3` deterministically via `node-gyp` and floats `npm@latest` for the CVE overlay.

View File

@@ -1 +0,0 @@
- **fix(providers):** the five g4f.space sub-providers (Groq, Gemini, Pollinations, Ollama, NVIDIA) no longer advertise a free tier — a keyless `POST /v1/chat/completions` now returns `402 insufficient_credits` behind a proof-of-work "cake" wall (re-verified live 2026-08-22), so `hasFree` is `false` and the notes point at `g4f.dev/members.html`. The gateway still works with a member key, so its registry wiring and `authType: "optional"` are unchanged ([#10071](https://github.com/diegosouzapw/OmniRoute/issues/10071)) — thanks @chirag127

View File

@@ -1 +0,0 @@
- **fix(chatgpt-web):** Preserve native `max` thinking effort through ChatGPT Web routing ([#10077](https://github.com/diegosouzapw/OmniRoute/pull/10077)) — thanks @zannen7

View File

@@ -1,2 +0,0 @@
- Fix: wire AgentRouter's existing console balance fetcher into the Dashboard Quota UI (visibility gate + provider-limits data path + background sync) so its wallet balance renders instead of falling back to "Usage API not implemented" (#10078)
- Fix: AgentRouter's dollar balance now renders as a currency-formatted "$X.XX" credits row in the Dashboard Quota UI instead of a bare percentage, and an exhausted wallet always shows exactly $0.00 (#10078)

View File

@@ -1 +0,0 @@
- fix(sse): bridge generic openai-compatible/anthropic-compatible provider type ids to their concrete uuid node id in credential lookup (#10085)

View File

@@ -1 +0,0 @@
- fix(domain): stop treating an unreported Antigravity quota fraction (`fractionReported:false`) as 0% remaining in `quotaCache.ts`, which was falsely marking every fresh/newly-connected account as exhausted and blocking multi-account rotation (#10095)

View File

@@ -1 +0,0 @@
- fix(dashboard): remap unified Kimi Code card API-key save to the admitted `kimi-coding-apikey` connection id, fixing 400 "Invalid provider" on Save (#10096)

View File

@@ -1 +0,0 @@
- fix(antigravity): strip trailing model turn for native Gemini requests too, not just Claude (#10104)

View File

@@ -1 +0,0 @@
- **fix(admission):** stop the adaptive latency-gradient collapse from permanently locking out ordinary requests — individually valid requests now make solo progress when the system is idle and normal pressure, and the collapsed limit actively recovers on sustained idle windows instead of being stuck; the critical-pressure fuse still wins over solo progress (#10111)

View File

@@ -1 +0,0 @@
- fix(sse): downgrade client-supplied `thinking:{type:"adaptive"}` to `enabled` and gate the `context-1m-2025-08-07` beta on model eligibility when a combo/fallback re-routes a request to a non-adaptive/non-1M model like claude-haiku-4-5 (avoids "adaptive thinking is not supported on this model" and "long context beta is not yet available" 400s, #10119)

View File

@@ -1 +0,0 @@
- **fix(logging):** move call-log artifact serialization and filesystem writes to a bounded singleton worker to keep request handling responsive (#10123)

View File

@@ -1 +0,0 @@
- **perf(logging):** bound each scheduled call-log rotation pass to incremental database and filesystem work (#10125)

View File

@@ -1 +0,0 @@
- **fix(streaming):** start early SSE heartbeats when Responses or Messages requests opt into streaming through the request body (#10127)

View File

@@ -1 +0,0 @@
- **fix(combo):** scope session-stickiness bindings to their owning Combo so identical first messages cannot carry a successful target into another priority chain and bypass its configured order (fixes #10136)

View File

@@ -1 +0,0 @@
- **fix(translator):** resolve the Claude thinking output cap with the routed provider so a provider-scoped-only `max_output_tokens` override is no longer invisible to `fitThinkingToMaxTokens()`, which previously let the synthesized `max_tokens` (caller room + thinking budget) go out unbounded and 400 upstream ([#10139](https://github.com/diegosouzapw/OmniRoute/issues/10139))

View File

@@ -1,3 +0,0 @@
- fix(providers): correct the conol-web registry fallback-models import depth, which pointed at a
non-existent `open-sse/config/services/` and made any suite loading the provider registry fail to
resolve (#10140)

View File

@@ -1 +0,0 @@
- **fix(oauth):** Claude connections created via `claude-auth/import` now send required CLI headers on the bootstrap identity call and persist a `cliUserID` device identity, fixing intermittent "Third-party apps now draw from your extra usage" 400s on otherwise valid imported subscription tokens ([#10144](https://github.com/diegosouzapw/OmniRoute/pull/10144), fixes [#10143](https://github.com/diegosouzapw/OmniRoute/issues/10143))

View File

@@ -1 +0,0 @@
- **fix(sse):** Responses-passthrough `response.completed` snapshots now drop `phase:"commentary"` items the same way live SSE frames already do, so the terminal `response.output` array no longer echoes internal commentary text that was already suppressed from the stream (#10156).

View File

@@ -1 +0,0 @@
- fix(proxy-subscriptions): allow local/loopback proxy-subscription fetch URLs (local-first, cloud-metadata still blocked) (#10158)

View File

@@ -1 +0,0 @@
- **fix(routing):** keep approximate Combo context estimates advisory so requests reach concrete targets instead of returning a pre-dispatch 400 ([#10162](https://github.com/diegosouzapw/OmniRoute/pull/10162)) — thanks @xz-dev

View File

@@ -1 +0,0 @@
- **docs(settings):** document Thinking Budget modes (passthrough vs auto-strip); fix dashboard i18n key collision that showed Auto Combo routing copy on the thinking tab; clarify independence from compression/cache ([#10169](https://github.com/diegosouzapw/OmniRoute/pull/10169))

View File

@@ -1 +0,0 @@
- fix(cli): guarantee a non-empty `[STARTUP] Fatal:` log line for any instrumentation-hook boot throw, not just DB-driver init failures (#10171)

View File

@@ -1 +0,0 @@
- fix(sse): gate structural chat admission shedding on real heap pressure instead of unconditional capacity, with a bounded headroom budget so a healthy heap can no longer bypass admission control indefinitely (#10183, #10268)

View File

@@ -1 +0,0 @@
- **fix(guardrails):** Vision Bridge handles OpenAI Responses `input`/`input_image` requests before combo vision filtering ([#10202](https://github.com/diegosouzapw/OmniRoute/pull/10202)) — thanks @Zartharas

View File

@@ -1 +0,0 @@
- **fix(cursor):** Stop truncating pending tool calls on non-composer models when a KV checkpoint arrives after text but before the `exec_mcp` frame — the KV short-circuit is now gated to the composer family where it was verified ([#10215](https://github.com/diegosouzapw/OmniRoute/issues/10215)).

View File

@@ -1 +0,0 @@
- **fix(responses):** repair corrupted SSE deltas for non-ASCII streams by keeping a single stream-aware `TextDecoder` (`{ stream: true }`) across `transform()` calls instead of recreating it per chunk and decoding without the `stream` flag. When a multi-byte UTF-8 character (CJK/emoji) was split across two TCP chunks — common in Chinese streaming text — the per-chunk decoder truncated it to `U+FFFD`, corrupting every delta while the rebuilt `*.done` snapshot stayed internally identical ([#10223](https://github.com/diegosouzapw/OmniRoute/issues/10223))

View File

@@ -1 +0,0 @@
- **fix(combo):** defer the known-context-overflow hard rejection for compressible requests so compression runs before the final context gate, instead of a raw-body estimate 400'ing generic Responses clients targeting a large model before OmniRoute can shrink it ([#10225](https://github.com/diegosouzapw/OmniRoute/issues/10225))

View File

@@ -1 +0,0 @@
- **fix(api):** deleting a manually-added custom model no longer tombstones a provider-synced model that shares its id. `DELETE /api/provider-models` is addressed by `provider` + `model` alone, so when both a custom row and a synced row existed for one id it removed both and wrote `isDeleted:true`. `replaceSyncedAvailableModelsForConnection` then filtered that id out of every subsequent re-import, so the provider could never resync — model sync kept reporting `added: N` while the catalog stayed empty and `/v1/models` never listed the model again, even though routing to it still worked. The custom row is now removed first and its presence is treated as the operator's intent, leaving the synced sibling importable; a synced-only delete still tombstones as before (#3199, #3782 unaffected) ([#10228](https://github.com/diegosouzapw/OmniRoute/pull/10228)) — thanks @Neuron-Mr-White

View File

@@ -1 +0,0 @@
- **Audio Bridge:** fix production transcription self-loop uploads so real audio reaches the configured STT provider instead of falling back to an unavailable-provider stub ([#10229](https://github.com/diegosouzapw/OmniRoute/pull/10229)).

Some files were not shown because too many files have changed in this diff Show More