ee24799e7c froze 215 'pre-existing' errors measured on this devbox. The CI
artifact from run 32968510623 shows what actually happens on a clean npm ci:
ZERO unsuppressed errors and 150 suppressions that match nothing. The react-hooks
findings those entries described do not fire in CI at all — they are an artifact
of this machine's node_modules, the same phantom-count problem that once produced
a fabricated '925 errors' locally.
So the Lint job was not red because the repo had 215 hidden errors. It was red
because I froze errors that only exist on my machine, and ESLint exits 2 on a
suppression that no longer matches anything.
Rebuilt from the CI artifact (messages + suppressedMessages, which is where a
suppressed violation still shows up) instead of from a local lint: every count is
now the value CI observes. 150 rules removed, 102 files emptied and dropped, back
to the 1249 entries that predate the freeze. RequestLoggerV2's exhaustive-deps
keeps the committed 6 rather than the 7 I derived — 6 is the value the artifact
run demonstrably accepted, and a count above the real one is exactly what turns
this gate red.
The 9->6 prune in 7afacf6c21 stays: those three were genuinely dead, freed by
restoring the manual-model block, and CI agrees.
The Lint job restores a persisted .eslintcache whose key hashes
config/quality/eslint-suppressions.json. Touching that file to prune a stale
entry changed the key, the tree was relinted cold, and 215 errors across 126
tracked files surfaced at once. None of them are new: they reproduce on
091e2ba4da and were simply being served from cache for every file a PR did not
happen to touch. See #11600.
Freezing them trades an accidental hiding place for an explicit, versioned
record: new violations now fail the gate, old ones are listed by file and rule
and can be drained deliberately. Fixing 215 react-hooks findings across 126
dashboard components mid-release — none of which have a test pinning their
current behaviour — would be a far worse trade than recording them.
Scope was filtered on purpose rather than using --suppress-all, which wanted to
freeze 846 violations across 246 paths including _artifacts/, .playwright-cli/
and output/ — none of which exist in a CI checkout — and among them 28
no-new-func, the Hard Rule #3 rule. Only git-tracked files are frozen here, and
no-eval / no-new-func / no-implied-eval / no-restricted-imports are excluded by
construction: had any appeared in a tracked file the script would have reported
it instead of suppressing it. None did.
Verified over the tracked file set only (the CI checkout's scope): 0 errors.
Two follow-ups to the peerContext extraction, both caught by CI.
public-policy.test.ts guarded its two loopback cases behind
`if (!getMachineTokenSync()) t.skip(...)`. Those are the only tests covering the
branch the pack-boot fix added, so a machine-id that failed to resolve would have
silenced exactly the coverage that matters and shipped the authorization change
untested — which is what the test-masking detector flagged. The sibling
management-policy cases in authz/routeGuard.test.ts call the same helper with no
guard and pass in CI, so an empty token is a broken environment: assert on it
loudly instead of skipping.
route-guard-private-lan.test.ts pinned its Host-spoofing regression guard to
source text inside management.ts; moving requestPeerAddress into peerContext.ts
made the literals disappear from the file it greps, while the property itself was
untouched. The guard now follows the implementation: neither module may read the
Host header, the owning module must resolve the token-stamped peer, and — new
positive anchor — management.ts must still delegate to peerContext, so the guard
cannot pass by the policy quietly regrowing a Host-based path.
check:pack-boot booted the packed tarball fine but failed its version
assertion: `version "undefined" (expected "3.8.50")`. The artifact was
not broken — the health payload was.
#11040 (GHSA-mvf8-qc78-5mxm) reduced GET /api/monitoring/health to a
liveness-only view for non-management callers. But that route is
classified PUBLIC (src/shared/constants/publicApiRoutes.ts:67) and
runAuthzPipeline deletes CLI_TOKEN_HEADER from the forwarded headers for
EVERY route class (src/server/authz/pipeline.ts), so a handler can only
learn that a local CLI authenticated from the subject the policy stamped.
publicPolicy stamped `anonymous` unconditionally, so requireManagementAuth
in the route 401'd even for a valid loopback machine token and every
caller got `{status, setupComplete}` — no `version`.
Verified on the installed tarball before the fix: the same token that
returns 200 on /api/cli/whoami (MANAGEMENT, where the pipeline does stamp
the subject) got the anonymous body on /api/monitoring/health.
publicPolicy now stamps the same loopback-gated `local-cli-token` subject
the MANAGEMENT policy already produced. The shared verdict (peer-locality
resolution + constant-time token compare) moves to
src/server/authz/peerContext.ts so both policies use one implementation —
no behavior change on the MANAGEMENT side. Anonymous callers keep the
liveness-only view.
Tests: publicPolicy stamps the CLI subject only for a loopback peer with a
valid token (both negative controls asserted; the positive case fails on
the pre-fix policy), plus a health-route pin that a stamped local-CLI
caller receives `version`. check:pack-boot green end to end (boot #1,
machine-token contrast, sql.js round trip, boot #2 persistence).
The fragment claimed 199 of 200 model switches fired a discarded call. That
number is the TDD proof — summarization calls counted with the new guard
disabled — not the observed waste. The measured run was ~254 upstream calls for
200 requests, i.e. roughly one request in four, dropping to 201 after the fix.
Also state explicitly that the delivered 70/30 share was correct all along, so
the entry cannot be read as a routing defect.
Covers 37d784b0fe..7afacf6c21, which landed on release/v3.8.50 after the
CHANGELOG had already been reconciled and were therefore missing from the
release notes: the search-connection preference (#11524), the /v1/models
after() injection point (#11551), the discarded universal-handoff
re-summarization (#11552), per-step connection pins on fallback, the
expert-mode manual model entry in the combo builder, plus the CI/e2e/eslint
follow-ups.
No CHANGELOG.md edit — aggregation stays with the release captain.
Two changes, both consequences of restoring the expert-mode manual model block:
- no-unused-vars drops 9 -> 6. Three of those suppressions existed only because
#8285 deleted the JSX that consumed manualModelInput / handleAddManualModel and
left the state behind as dead code. With the block back they are live again, and
a stale suppression makes eslint exit 2 — which is what actually turned the Lint
job red, not the react-hooks errors below.
- Freeze the 7 react-hooks/set-state-in-effect + 1 react-hooks/immutability errors
in this file. They are pre-existing: overwriting the file with its 091e2ba4da
content reproduces all 8. They surfaced only because touching the file evicted it
from the persisted .eslintcache the Lint job restores; 223 errors across 127
tracked files are masked the same way repo-wide. Fixing seven effects inside a
4841-line page during a release is the wrong trade, so they are frozen here and
tracked separately.
b07182c72a (#9320) inverted the catalog auth rule: /v1/models now requires auth
whenever management auth is configured, unless requireAuthForModels is explicitly
false. The e2e harness boots with INITIAL_PASSWORD set, so the endpoint has been
answering 401 since 2026-08-04 and this check has been red ever since — invisible
only because the job kept being cancelled behind Build.
Mirror the sibling /api/providers check in this same file: assert the catalog
shape whenever the catalog is actually served, and otherwise pin the auth gate by
status AND error type, so a 401 from an unrelated misroute cannot pass for the
deliberate one.
PR #8285 (global model search) replaced the expert-mode "Manual model"
block positionally with the new GlobalModelSearchPanel, dropping the only
way to type a provider/model pair by hand in expert mode. The supporting
state and handlers (manualModelInput, manualModelError,
manualModelHasDuplicate, handleAddManualModel) survived as dead code, so
neither typecheck nor lint flagged the loss.
Re-render the block above the search panel, unchanged from its pre-#8285
form. Regression guard: tests/e2e/combos-flow.spec.ts "expert mode shows
a single-page combo form with manual model entry", which had been failing
with a 180s timeout on locator.fill for combo-manual-model-input.
PR #9870 moved the proxy-registry bulk-assign action into the toolbar's
"More actions" (…) overflow menu, which renders its items only while open.
The e2e smoke flow still clicked the testid directly, so the locator never
resolved and the test burned its full 180s budget.
Open the menu first and assert it is visible before clicking; every existing
assertion is unchanged.
The guard added for #10427 checks ancestry against origin/main by default. That
is the right ref at publish time (npm-publish.yml runs on main), but in a
pull_request context it can never hold: while the PR is open its head is by
construction not an ancestor of main, and the shallow checkout does not even
bring origin/main into the local graph, so the probe answers false regardless.
The job therefore failed 100% of the time and only became visible now that it
stopped being cancelled behind Build.
Pre-merge the one checkable invariant is that the stamp matches the branch under
test, so resolve the ref from refs/pull/<N>/head — which exists on origin even
for fork PRs, unlike head.ref, which only exists on the author's repository.
A combo step pinned with an explicit `connectionId` (or a request pinned via
`x-omniroute-connection`) is an operator instruction, not a hint. The generic
account-fallback branch in handleSingleModelChat excluded the pinned connection
after an upstream failure and re-selected a sibling account of the same provider,
so a priority combo repeating one provider/model with two different fixed accounts
ran both attempts under the FIRST step: the second step, with its own pin, never
executed and per-step attribution (comboStepId / comboExecutionKey) was wrong.
Gate the rotation on `!hasForcedConnection`, matching the antigravity
stream-readiness, pre-response-timeout and account-semaphore branches that already
let pinned steps fall through to combo orchestration. Cooldown recording via
markAccountUnavailable is unchanged, and unpinned selection still excludes burned
connections.
Refs tests/integration/combo-routing-e2e.test.ts
A universal handoff whose summary comes back unparseable persists nothing,
so shouldGenerateUniversalHandoff keeps answering "generate" and the very
next model switch in the same session re-issues the same full-history
summarization call and discards the answer again — forever.
With a switch-heavy combo strategy (weighted, random, round-robin, p2c) the
models alternate on almost every turn, so that background call lands on a
large fraction of requests: an upstream call whose response nobody reads is
real money on a paid provider and real quota on a metered one. Measured on
the weighted 70/30 matrix, that inflated the observed openai share to 0.895
where routing actually delivered 0.70, and at the unit level 199 of 200
model switches issued a fresh discarded summarization call.
Back off per (session, combo) after an answer that is not a usable handoff:
exponential 5min -> 1h, cleared on the first successful generation, capped
at 500 tracked keys. Deliberately narrow — a transient upstream failure
(!response.ok) is NOT tracked, so it still retries on the next switch, which
is the behavior the context-relay path already depends on.
After the fix, same harness at n=200: 201 upstream calls for 200 requests
(1 extra, 0.5%), and the measured openai share equals the delivered share
(0.725). The unit guard drops 199 discarded calls to 1.
Refs #11552
`/v1/models` has passed a third argument to `getUnifiedModelsResponse()`
(`{ scheduleBackgroundRefresh: (task) => after(task) }`) ever since #10198, but
#9199 had already removed the parameter: the function takes two, so the object
was silently dropped and `catalogCache` kept scheduling the stale-while-
revalidate rebuild with `setTimeout(..., 0)`. The builder is overwhelmingly
synchronous under the single-threaded App Router, so it pinned the event loop
before the stale response was flushed — the #8728 guarantee did not exist.
- `catalogCache` now imports `after` from `next/server` and exposes
`defaultBackgroundRefreshScheduler`, which defers to `after()` and falls back
to a macrotask outside a Next request scope (instrumentation warm-up, tests).
- `resolveCachedCatalogResponse` takes `scheduleBackgroundRefresh` and
`getStaleWhileRevalidateMs` on its existing options object.
- `getUnifiedModelsResponse` accepts the options object the route already passes
and propagates the scheduler.
The excess argument was invisible to CI: `tsconfig.typecheck-core.json` is a
curated 27-file allowlist, `check:dashboard-typecheck` only covers
`src/app/(dashboard)`, and `next.config.mjs` sets `ignoreBuildErrors: true`.
Closes#11551
When no explicit provider is requested and the auto-selected cheapest
provider has no credentials, executeWebSearch ran the fallbackOnly loop
first. duckduckgo-free (costPerQuery 0, authType none) always won there
with an empty credentials object, so a configured paid connection such as
serper-search was silently ignored and the caller got success:true with
zero results.
Move the sweep for other credentialed regular providers ahead of the
fallbackOnly loop (and exclude fallbackOnly ids from it, so a free
last-resort provider never outranks a configured one on cost). The
fallbackOnly loop stays as the true last resort.
The chat path only appeared correct because duckduckgo-free happened to
fail there and handleSearch retried the alternate provider; on
/v1/responses it "succeeded" with no results.
Closes#11524
Vitest went red on 'synchronizes upstream models only when autoFetchModels is
explicitly true' with new URL throwing inside a fetch dispatched from
Timeout._onTimeout (useProviderModels.ts:69). It is intermittent: green in the two
previous CI runs, green every time in isolation, red only under the ui suite's 20
parallel workers.
The hook schedules its auto-sync in a setTimeout whose callback only checks the
flag on entry — and that flag stays false while the component is
mounted. Both tests asserted first and unmounted last, so under contention the
timer escaped the test window, fired after afterEach had already run
vi.unstubAllGlobals(), and reached the REAL fetch with a relative URL.
Unmounting before the assertions closes the window: cleanup flips , the
callback returns early, and the calls already recorded on fetchMock are still there
to assert against. No assertion changed.
Not a regression from this cycle — the file's last change is fd76271515 (#10603).
Fixed rather than tracked because an intermittent red in a blocking job is worse
than a permanent one: it teaches people to re-run instead of to look.
Refs #10692
Electron Package Smoke — a packaging defect that had been hidden behind another
packaging defect for nine days. Once the loginHeaderCapture fix let the main process
start, the server underneath died on 'Cannot find module next': resources/app/server.js
shipped without resources/app/node_modules.
electron-builder discards the ROOT node_modules in code, not by configuration —
app-builder-lib/out/util/filter.js:42 has a hard-coded `if (relative === "node_modules")
return false` that runs before any filter pattern. The second extraResources entry
pointing INTO ../.build/electron-standalone/node_modules is what sidesteps it, because
those relative paths are never equal to "node_modules". #10325 removed that entry as an
apparent duplicate and flipped the test to assert "exactly once", freezing the
regression as if it were the contract. Restored, and the unit guard now pins both
entries — proven by mutation: reverting package.json to the post-#10325 shape fails the
guard 3/4, restoring it passes 4/4.
group-b-quota-plans-config — the assertion was impossible to satisfy on ANY route, and
the page was never broken. layout.tsx hands the whole message catalogue to
NextIntlClientProvider, React serialises that prop into the RSC payload, and en.json
carries "Internal Server Error" twice, so page.content() always contains it: probing
/dashboard, /dashboard/costs, /dashboard/settings and /login showed the string present
with every page rendering fine, and a pageerror probe on the failing run captured zero
client exceptions. This is the same trap that killed the sibling not.toContain("500")
in fc77100c3f ("raw HTML is unreliable") — that one was removed, this one was kept.
Now asserts on rendered text, which still catches a real error boundary. The pageerror
capture stays: the CI failure carried no stack trace, which is why it was misread twice.
Integration — 10 of the 14 shard-2 reds, all sibling-test gaps behind security fixes:
monitoring health now takes a Request and requires management auth (GHSA-mvf8-qc78-5mxm);
the OAuth import routes moved to requireManagementAuth (GHSA-mg76) — the test accepts
both guard shapes and gained a stronger anchor that every exported handler awaits a
guard on its own request, mutation-verified; skill tool names are derived from
encodeSkillToolName() and the fake upstream now returns the encoded name so
decodeSkillToolName() is exercised too; previous_response_id now fails closed (#10262);
proxy_logs persist as an async batch (#11182) so the test flushes first;
providerQuotaOverrides joined GET /api/resilience (#9871); the reasoning fixture used a
model that stopped being thinking-incompatible, replaced and pinned with a premise
assert so it cannot rot silently again.
A vacuous assert.ok(true, "all 10 streams completed without hanging") was replaced with
real anchors — content must arrive on every stream and the active Timeout count must not
grow.
Four are deliberately left red rather than aligned, each now tracked: #11551 (the
/v1/models after() wiring is dead — the route passes a third argument to a two-parameter
function and catalogCache never imports after, so the #8728 contract is unimplemented),
#11552 (~27% of requests emit an extra discarded upstream call; the delivered
distribution is exactly 0.70, so weighted routing is correct and the waste is the real
finding), the fixed-account combo pin (aligning it would destroy the per-step attribution
the test exists for), and the web_search fallback already tracked as #11524.
Package Artifact — the provenance stamp I added last round used git rev-parse HEAD, which
under pull_request is the ephemeral merge commit and therefore never an ancestor of the
release branch. Now takes the PR head sha.
Refs #10692
All three are the sibling-test gap again: a PR moved a contract, updated its own
tests, and left these behind. None is a production defect — in two of the three the
production side is a deliberate security fix.
v1-contracts-behavior (4 failures, one cause): the job env sets INITIAL_PASSWORD,
which makes isAuthRequired() true, and #9320 (b07182c72a) made the /v1 catalogue
gate on-by-default instead of opt-in via settings.requireAuthForModels. The four
contract reads were calling the catalogue routes with no credential and correctly
getting 401. Bisected the job's four env vars to confirm INITIAL_PASSWORD alone
reproduces it (5 pass / 4 fail with it, 9 / 0 without). The tests now send a Bearer
token; the shape assertions are untouched, and the auth contract itself stays owned
by tests/unit/v1-models-auth-leak-9320.test.ts rather than being duplicated here.
opencode-config-startup: two independent drifts. OPENCODE_VERSION was pinned to
1.18.8 while the installed opencode-ai is 1.18.18 (Dependabot 7f6958960c, #10626) —
now read from require("opencode-ai/package.json").version, which is exactly as
strict but cannot drift on the next bump. And the no-limit-metadata case asserted
limit === undefined, but #11054 made the generator always emit a limit; it now pins
the actual fallback {context: 128_000, output: 8_192} instead of an absence.
memory-pipeline: #11040 (GHSA-cpv3-xr7r-xf8q) made the resolved caller principal
always win over a caller-supplied apiKeyId, so a spoofed id can no longer write into
another principal's store. That PR updated the unit sibling but not this one. The
test now asserts the stronger property — and deliberately not just the absence: the
spoofed principal's store is empty AND the caller can still read the entry, which
proves the write was redirected rather than dropped and keeps the emptiness check
from passing vacuously with a disabled store. (The old assertion was count === 0,
which a switched-off memory store would satisfy.)
Assertion counts: 43 -> 43, 13 -> 14, 76 -> 81. Nothing weakened or removed.
Verified: 24/24 pass, with and without the CI env vars.
Refs #10692
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
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
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
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
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
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
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).
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.
`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.
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.
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
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.
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.
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.
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.
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
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.
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).
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/.
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>
`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>
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!
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!
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!
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!
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!
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>
`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>
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!
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!
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!
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!
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.
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.
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.
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.
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.
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).
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!
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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!
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!
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!
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!
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!
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!
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!
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.
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!
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!
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!
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!
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!
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!
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!
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!
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!
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.
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!
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!
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!
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.
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).
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).
Merging --admin with red discrimination (merge-gates §4). 5/6 failing tests are the compression/i18n-vi/kiro/memory mini-cluster fixed by #11306 (this branch predates it). The 6th (token-health-check-kimi, 'jittered expiration window') is a timing-sensitive test — reproduces GREEN locally, name itself indicates randomized jitter, not a deterministic regression from this PR's reasoning-effort clamp change (zero file overlap). ESLint warnings + dast-smoke (advisory, isRequired:null) also inherited.
Merging --admin: this is itself the base-red drain PR (compression/i18n-vi/i18n-pt-br/kiro/memory mini-cluster). Remaining fails (ESLint warnings ratchet drift, dast-smoke advisory) are not blocking; isRequired:null on dast-smoke.
Validated on a 4-PR combined board: claude-classifier-compat 15/15 + 8189-classifier-compat-auto-narrow 5/5 sibling sweep, typecheck:core clean, gates within baseline. Detects the newer Claude Code severity-classifier request shape (stop_sequences carrying </severity>) and replies with <severity>0</severity> instead of the legacy <block>no</block> — the mismatch was failing both classifier stages closed and blocking gated tool calls (Bash, WebSearch). Closes#11289.
Validated on a 4-PR combined board: memory-cache-safe-injection + memory-glm-injection + chatcore-memory-skills-injection 39/39 (node:test), injection.test.ts 23/23 (vitest), typecheck:core clean, check:cycles clean, gates within baseline. Narrows the #3890 mid-array system-splice skip to the exact shape that 400s on Claude Opus 5 (splice point not preceded by a server tool result) instead of disabling the cache-safe optimization for every Claude request — preserves the fast path everywhere it's actually safe. Closes#11290.
Validated on a 4-PR combined board: provider-patch-ratelimit-protection-11278 2/2 + 56/56 sibling suites, typecheck:core clean, gates within baseline. PUT /api/providers/[id] can no longer silently enable rate-limit protection just because EditConnectionModal sends rateLimitOverrides on every save — the runtime toggle now strictly follows the persisted DB row. Closes#11278.
Validated on a 4-PR combined board: @omniroute/opencode-plugin provider.test.ts 13/13, full plugin suite 343/343, YAML validated, typecheck:core clean, gates within baseline. CI branch trigger was pinned to release/v3.8.2 since creation (never fired mid-cycle) — now tracks [main, release/**] matching every other workflow's pattern; combo-id fixture aligned to the real #10821 unprefixed-lookup contract (RED 11/13 → GREEN 13/13).
Validated on a 3-PR combined board: openai-responses-opencode-subagent-sessionid within the board's 20/20, typecheck:core + check:open-sse-typecheck clean, gates within baseline. Uses null as the omission sentinel for OpenCode's optional default-less subagent.sessionID under Responses/Codex strict mode (which required every declared property) — models can now omit the field to spawn a new child instead of inventing filler IDs; the null is stripped before OpenCode sees the call, real child IDs pass through unchanged. Thank you @ofonseca-pyming!
Validated on a 3-PR combined board: openapi-security-tiers 1/1 within the board's 20/20, typecheck:core clean, gates within baseline. Declares GET /api/openapi/spec's real ManagementSessionAuth contract (conditional on requireLogin) — no runtime behavior change, just the doc catching up. Closes#10851. Thank you @RaviTharuma!
Validated on a 3-PR combined board: 11018-database-cache-docs 1/1 within the board's 20/20 focused suite, typecheck:core clean, gates within baseline. DATABASE_GUIDE.md now cites the real 64 MiB runtime default and the 1..1,000,000 KiB range, locked with a regression test. Thank you @RaviTharuma!
Validated on a 2-PR combined board: 158/158 across the drain's 17 focused test files (incl. integration qdrant-routes), typecheck:core + dashboard-typecheck clean, env-doc-sync, mutation-test-coverage, cli-i18n, file-size, changelog-integrity all green. Every red discriminated per-item as stale-test (contract intentionally moved, citing the origin PR) or real bug (fixed, never masked) across 3 base-advance waves (#9985): stryker registration, i18n gaps (health.* namespace mismatch + pt-BR parity), combo-token-window pins (872K), CLI catalog counts (prime-agent), the isComboStep single-target reasoning-transport-fallback regression, container-guard hermeticity, and 26 unsuppressed ESLint errors from the wave (typed qdrant adapter, import trim, justified disable with precedent). Drains the accumulated base-red cluster.
Validated on a 2-PR combined board: routeGuard 36/36 (within the 68/68 focused-file total), a2a-task-owner-idor 7/7, a2a-tasks-auth, search-baseurl-ssrf-guard, cli-serve-hostname, spawn-capable-prefixes-client-safe all green, typecheck:core + dashboard-typecheck clean, gates within baseline. Five real High-severity advisories fixed with TDD (each failing-then-passing): settings export/import-json ALWAYS_PROTECTED completion, MITM route LOCAL_ONLY+SPAWN_CAPABLE gating, search baseUrl SSRF/IMDS guard, A2A REST task auth+ownership (previously none at all), and the loud boot exposure warning. GHSA-cjv9 confirmed already closed on this base (verified). Round 3 of the advisory sweep.
Validated on a 3-PR combined board: traffic-inspector-beginner-header suite green within the board's 30/30, typecheck:core + dashboard-typecheck clean, gates within baseline. Purpose-first orientation header for Traffic Inspector, existing inspection UI untouched. Thank you @ignamiranda!
Validated on a 3-PR combined board: sidebar-customization + sidebar-essentials-static 30/30 combined (across the board's 4 focused files), typecheck:core + dashboard-typecheck clean, gates within baseline. New Essentials sidebar preset gives first-time users a short beginner path while advanced tools stay reachable via Command Palette search. Thank you @ignamiranda!
Validated on a 3-PR combined board: kiro-connection-identity 8/8 (written failing-first — 3 new cases red on the pristine release/v3.8.50 tip, green with this change), typecheck:core + dashboard-typecheck clean, all static gates within baseline. Root cause is exactly right: a CodeWhisperer profile ARN identifies the profile, not the account, and distinct Builder ID accounts via social login can share one — the ARN is now trusted only alongside a non-contradicting account-level identifier (email/clientId). Closes#10815. Thank you @engenhariaandrereis01-ai!
- open-sse/executors/github.ts: replace the Math.random() fallback in
the Copilot correlation-id generators (x-request-id,
x-interaction-id, x-client-session-id, x-agent-task-id) with a
CSPRNG-backed randomIdFallback() (node:crypto randomBytes) — closes
js/insecure-randomness with no behavior change (crypto.randomUUID
stays the primary path).
- tests/unit/cli/_helpers/shellArgs.mjs: collapse the two sequential
global .replace() unescape passes into a single left-to-right regex
replace with alternation — closes js/double-escaping. The prior
two-pass form let the first pass's output feed the second, which is
exactly the double-(un)escaping bug pattern the query flags (e.g. an
escaped-backslash-then-quote sequence could be misread depending on
pass order).
Co-authored-by: Markus Hartung <mail@hartmark.se>
Validated on the combined 8-PR board + the branch itself: vertex-anthropic-models 4/4 (new — pushed to your branch as a fix-in-place commit per Hard Rule #8, covering the parser's global/project-scoped resource names, malformed-input handling, and the claude-* → targetFormat heuristic on vertex/vertex-partner), 88/88 across the board's focused suites, typecheck:core clean, gates within baseline. Anthropic partner models on Vertex AI now discover dynamically via the Model Garden publisher endpoint and route through the Claude translator even for models outside the static registry. Thank you @maci0!
Validated on the combined 8-PR board: all seven CLI command suites green (combo-suggest, compression, mcp-call, oneproxy, resilience, skills + the shared mcpStreamMock helper) within the board's 88/88, typecheck:core clean, gates within baseline. The CLI mcp call protocol fixes (BUG-001) land with full regression coverage across the affected commands. Thank you @YunyunZhai — and thank you for your patience while this one waited for review.
Validated on the combined 8-PR board: home-page-static 4/4, check:dashboard-typecheck clean (220 pre-existing, all within frozen baseline — zero syntax errors), 88/88 across the board's focused suites, static gates within baseline. The soft first-run readiness card replaces the hard Home→onboarding redirect with a dismissable 4-step path. Thank you @ignamiranda!
Validated on the combined 8-PR board: ollama-local-capabilities-routing 3/3, managed-model-import 9/9 (including the integration with the carried Gemini-3.5-Flash cleanup from #11259), 88/88 across the board's focused suites, typecheck:core + dashboard-typecheck clean, gates within baseline. This brings #11088 to the release line — it had squash-merged to main by base error (mine) — AND fixes the two defects the port caught: the global filter drop that leaked image/video models into OpenAI chat selections (now scoped to self-hosted providers) and the unregistered hard-lease credential site. Exemplary port discipline: byte-identical carry + the corrections in a separate reviewable commit + the superpowers docs deliberately left out. main still needs the same two-line fix. Thank you @yourspraveen!
Validated on the combined 8-PR board: live-server-allowlist suite green within the board's 88/88, typecheck:core + dashboard-typecheck clean. 0.0.0.0 as a browser Origin is loopback-equivalent (it can never name a LAN host — same treatment as headroom/detect.ts and outboundUrlGuard.ts), so the local-only posture is unchanged and the FORBIDDEN_ORIGIN reconnect flood on the dev server's printed URL stops. Thank you @Minamaged18!
Validated on the combined 8-PR board: 88/88 across the board's focused suites, typecheck:core + dashboard-typecheck clean, static gates within baseline. The subprocess spawn this test does timed out locally on the shared devbox under load 35 (ETIMEDOUT on tsx bootstrap — environment, not the test); CI runs it unloaded. Runtime log-output assertion for the disabled credential-health boot path, closing the #11016 follow-up suggested on #11029. Thank you @Prajeeth-12!
Validated on the combined 8-PR board: the declared-vocabulary clamp integrates with #11232's learned-set clamp (both fire in sequence; REASONING_EFFORT_ORDER import added), reasoning quartet 57/57 including the new opencode-go suite 7/7, 88/88 across the board's focused suites, typecheck:core + dashboard-typecheck clean, all static gates within baseline. Retargeted main→release/v3.8.50 and rebased onto the release tip (authorship preserved; the branch carried main history, so only the real commit was carried). The generic explicit-capability clamp closes the Console Go 400 [1210] loop for models with a declared effort vocabulary. Thank you @linhdmn — the live-probe table in the PR body is exactly the evidence standard we want.
Validated on the resolved merge against the current tip (527da656 + the post-#11281 rebaseline): the single conflict was a comment-only collision in providers/[id]/models/route.ts (kept the tip's #10828-ordering note). Focused suites 125/125 across all 13 touched test files (build-sqlite-stub, cc-compatible, copilot-claude-messages, copilot-gemini-route, executor-github, ghe-copilot, github-copilot-discovery-token, github-copilot-model-discovery, noauth-sibling-7620, provider-header-profiles, provider-models-config, request-log-payloads, upstream-error-passthrough), typecheck:core clean, file-size/changelog-integrity OK. Merged --admin over the inherited 2026-08-23 base-red cluster (#9985) — the reds are proven tip failures (CLI catalog cluster + @testing-library allowlist, being drained by #11280), not from this diff. Note: the rebase means several items the body listed (relay x-relay-path SSRF, /v1/search blocked-providers, #10736 rotation fence, #10903, #10865, #10899, #10916) already landed upstream and are NOT in this delta — the delta is: better-sqlite3 build guard + build heap/worker caps + telemetry-off (#10060 re-derived), credential-echo passthrough refusal + OCR/moderation redaction + call-log key redaction, Copilot CLI 1.0.81-6 wire identity + Claude→/v1/messages name-matched routing + discovery token fix, CC model_not_found 400, compat overrides for no-auth aliases (#7620-pinned). The Copilot wire-identity change is the one to watch in production. Thank you @arminanton — and the ported-author credits in the commit history (@rqzbeh, yidecode, the #10899/#10916 authors) are preserved. Your config-posture finding (REQUIRE_API_KEY default vs 0.0.0.0) is noted for a maintainer decision, as you scoped it.
TDD: 4 red-to-green tests reproducing the two review findings + 6 new cases; 30/30 in tests/unit/codex-app-server.test.ts; typecheck/eslint/env-doc-sync/file-size clean. Merged --admin over the inherited 2026-08-23 base-red cluster (#9985): the remaining reds (CLI catalog/registry tests, @testing-library allowlist) are proven tip failures unrelated to this diff — shard logs show only CLI-cluster failures, and this PR itself drains the mutation-test-coverage red (stryker registration for #11267's test). Behavior change for app-server deployments is documented in the PR body (sandbox default + binding refusal).
Validated: qdrant-routes integration suite 19/19 on the rebased branch, typecheck:core clean. I retargeted the PR from main to release/v3.8.50 and rebased your single commit onto the release tip (authorship untouched) — no content changes. Embedding models now list from configured/credentialed providers via the embedding registry, with vector dimensions in labels and the unconfigured OpenAI fallback removed. Thank you @rafacpti23!
Validated on the combined 12-PR batch board + the resolved merge against the post-#11232 tip: focused suites 73/73 (learned-reasoning-effort-caps, synced-capabilities-learned-effort-override, synced-effort-suffix-learned-validation, effort-tiers-loop-catalog-e2e, reasoning-effort-clamp-and-retry, reasoning-effort-learned-capability) + opencode-plugin effort-tier-variants 4/4, typecheck:core clean, gates within baseline. The stacked-branch conflict after #11232 squash-landed was resolved by hand (the learned-caps module keeps both the Set API and the new model-scoped lookup). The effort_tiers loop is closed end-to-end: catalog advertises exactly what the upstream accepts, and -<tier> suffix variants resolve against the learned set. Thank you @maxmad64bis!
The structural admission gate (src/shared/middleware/chatBodyAdmission.ts —
the bounded heavyweight lease + healthy-headroom path from #10110/#10437)
returns its 503 chat_admission_busy BEFORE request logging, so a shed left
no trace: no counter, no log line, and the process-wide snapshot was never
consumed by any route. This adds pure observability — admission behavior,
defaults, and thresholds are untouched.
- chatBodyAdmission.ts: in-memory shed history (shedTotal + shedsByReason)
on ChatAdmissionController, recorded at the two capacity-driven give-up
points in acquireHeavyWithin (queue_timeout when the bounded wait
expires, queued_bytes_budget when the heap valve refuses to park). A
client abort mid-wait is deliberately not counted — capacity was never
denied. Each shed also emits exactly one structured pino warn (module
chat-admission) with reason/activeHeavy/waiting/queuedBytes and the HMAC
session fingerprint — never a raw credential. PerConnectionAdmission
Controller.snapshot() now carries the counters plus activeHealthyHeadroom.
- /api/monitoring/health: the structural snapshot is exposed under a new
additive chatAdmission key next to the existing adaptiveAdmission (the
shadow-mode layer) — allowlisted projection in observability.ts, nothing
removed from the current payload.
- Tests: tests/unit/chat-admission-visibility-11244.test.ts (RED→GREEN:
shed counting per reason, abort exclusion, snapshot shape, and the warn
log carrying the fingerprint but never the raw API key) + a chatAdmission
allowlist-projection test in observability-payloads.test.ts.
Co-authored-by: Xiangzhe <bakryun0718@proton.me>
The opencode-go quota preflight ignored the dashboard quota snapshots, so
priority combos kept selecting connections whose weekly window was already
drained. Two gaps, two fixes:
A) fetchOpencodeQuota only consulted the live upstream endpoint, which has
no public quota API (404 — the module JSDoc already admits this). Every
preflight therefore evaluated null and proceeded (fail-open) even with a
connection at 0% weekly remaining in plain sight on the dashboard. The
fetcher now synthesizes its triple-window QuotaInfo from the cached
dashboard snapshots when the live endpoint yields nothing, mapping
session→window_5h, weekly→window_weekly, mcp_monthly→window_monthly and
mirroring getQuotaWindowStatus semantics (expired resetAt = window rolled
over = must not count as exhausted; fractionReported=false = unknown,
never exhaustion). Read-only via src/domain/quotaCache.ts accessors —
never SQL, never a re-scrape on the hot path — and gated on the
connection actually having dashboard scrape config, so unconfigured
connections never touch the snapshot store. Fail-open is preserved:
no snapshots → null, exactly as before. The quotaCache import is dynamic
because a static edge would close an initialization cycle
(fetcher → quotaCache → usage.ts → usage/opencode.ts → fetcher).
B) The sibling-selection latency gate in getProviderCredentialsWithQuotaPreflight
never consulted resilience.quotaPreflight.enabled
(QUOTA_PREFLIGHT_CUTOFF_ENABLED) — that flag only armed the auto-strategy
candidate builder and the per-target cutoff for pinned connections, so a
priority combo over sibling opencode-go connections (connectionId null at
combo level) skipped preflight entirely. The flag now arms the gate as
well; the default (flag off) is unchanged.
TDD (Hard Rule #18), tests/unit/quota-exhaustion-cutoff-opencode.test.ts:
- fetcher 404 + seeded snapshots weekly=0%/session=80% → cutoff blocks
(RED before, GREEN after); also asserts the bridge is read-only (single
upstream fetch, no re-scrape).
- weekly 0% with next_reset_at in the past → not blocked (window dropped).
- per-window threshold override resolves against the mapped window_weekly
key (50% override blocks at 40% remaining; factory 2% does not).
- fail-open guard: configured dashboard with no snapshots still returns null.
- selector level: flag on, two opencode-go sisters, priority-1 exhausted →
selection skips to the healthy one (RED before, GREEN after).
Sibling suites green: opencode-quota-fetcher (18), quota-preflight,
combo-priority-quota-exhaustion-cutoff-5923, issue-6686, 8431, throttle-6911,
sse-auth*, quota fetchers, combo strategies, snapshot/hydration tests
(~500 tests). eslint (with suppressions) and typecheck:core clean.
Closes#11234
Co-authored-by: Xiangzhe <bakryun0718@proton.me>
Validated on the combined 12-PR batch board: typecheck:core clean, file-size/changelog/complexity/cognitive gates within baseline, eslint config smoke-tested. Freeze-then-ratchet for @typescript-eslint/no-unused-vars scoped to src/open-sse/tests (1393 pre-existing frozen, _ escape hatch, args:all), following the #7879 pattern — plus it untracks the two docs/superpowers planning files that leaked in via #11213 and were tripping check:tracked-artifacts for everyone. Thank you @maxmad64bis!
Two defects exposed by #11226 (the 401 "User not found." itself is upstream
OpenRouter's response to a bad key — not an OmniRoute bug):
1. OpenRouter key validation was vacuous. Both the dashboard Check (via
validateProviderApiKey -> validateOpenAILikeProvider) and 'omniroute
providers test' (bin/cli/provider-test.mjs) probed /api/v1/models, which is
PUBLIC and answers 200 to any key — so invalid keys were saved/marked valid
and only failed on real chat traffic. OpenRouter's authenticated key-info
endpoint (GET /api/v1/auth/key, 200 = valid / 401 = invalid) is now the
probe: registered as testKeyModelsUrl on the openrouter registry entry
(same mechanism as perplexity) and as keyCheckPath in the CLI test configs.
No other provider's probe changes; error results keep using canned strings,
never the raw upstream body (Hard Rule #12).
2. 'omniroute auth export' crashed with "cmd.optsWithGlobals is not a
function". .command("auth export") does not register a two-word command:
commander parses the bare word 'export' as a required positional argument,
so the action received (exportArgValue, options, command) while expecting
(options, command). Registered 'export' as a proper nested subcommand of
'auth' — the documented CLI surface 'omniroute auth export [--force]
[--id] [--format] [--out]' is unchanged, and unknown positionals
(e.g. 'omniroute auth bogus') are now rejected instead of silently running
the export.
TDD: tests/unit/openrouter-key-validation-auth-endpoint.test.ts (stub mimics
the real OpenRouter: /models public-200, /auth/key 401 'User not found.') and
tests/unit/cli-auth-export-wiring.test.ts (real commander wiring via
createProgram) were RED before the fix and are GREEN after.
Co-authored-by: Xiangzhe <bakryun0718@proton.me>
Validated on the combined 12-PR batch board: check:docs-all passes (doc-links + fabricated-docs strict). Complete Persian USER_GUIDE translation with preserved commands, identifiers and fixed relative links. Thank you @crmbadesaba-commits!
Validated on the combined 12-PR batch board: check:docs-all passes (docs-sync, doc-links over 855 links, fabricated-docs strict). 26-file Turkish documentation suite at full parity with consistent terminology. Thank you @ismailcelik-tr!
Residuals of #11236 after #10371/#10491 landed on the tip:
- Dist-fold residuals (bugs 2+3): managedBinaryName() (binaryManager),
resolveSpawnArgs() (installers/cliproxy) and the per-OS memory probes in
getProcessInfo() (processManager) still read the process.platform literal,
which the Linux build of the published artifact constant-folds (precedent:
b43a212680 / #10244). Converted to call-time os.platform() reads, matching
the module's documented anti-fold pattern. Test-side process.platform uses
are not bundled and stay.
- Fold guard: new tests/unit/windows-platform-fold-guard-11236.test.ts pins
zero out-of-comment process.platform occurrences in the four artifact
runtime files, with a comment-stripping tokenizer plus mutation self-checks.
- Bug 6 (pid null on Windows): portProbe.resolvePortPid() only probed
lsof/ss/net-tools netstat. Added a netstat -ano probe with a dedicated
LISTENING-row parser (parseWindowsNetstatPid) as the last fallback; Unix
probes unchanged, and the Windows parser never matches Unix rows (LISTEN vs
LISTENING). Also converted the darwin args branch in the same array to
os.platform() (same fold class, same hunk).
- Bug 5 hardening: runOAuthStatus coerces an out-of-contract 200 payload to
an empty list with a sanitized stderr warning instead of crashing on
.filter over a non-array.
TDD: guard test, parser tests and the oauth hardening test all failed RED
before the fix and pass GREEN after; sibling suites (binaryManager,
processManager, portProbePid, cli-oauth-commands, installers,
ServiceSupervisor, version-manager) green. The 6877 spawn-args test's win32
mock moved from defineProperty(process.platform) to
mock.method(os, "platform") to match the new runtime read — assertion
unchanged.
Co-authored-by: Xiangzhe <bakryun0718@proton.me>
Validated on the combined 12-PR batch board: cli-combo-command + windows-esm-import-paths suites pass, typecheck:core clean. pathToFileURL on the four dynamic-import call sites unbreaks the CLI offline DB fallback on Windows. Thank you @pacocartones!
Validated on the combined 12-PR batch board: combo-max-global-attempts-config 4/4 plus the neighboring combo suites green, typecheck:core clean, gates within baseline. clampGlobalAttempts mirrors clampComboDepth with a 200 hard cap, so a bad config can never disable the budget. Closes the configurability item from #11134. Thank you @pacocartones!
Validated on the combined 12-PR batch board: the touched CLI test files pass (alias-resolver-7791, run-command — incl. the new shellArgs helper), typecheck:core clean. Test-side only, no production behavior change. Thank you @pacocartones!
Validated on the combined 12-PR batch board: repro-7764 suite 17/17 on this tree (11/17 fail on a clean tip without the fix — proper red-to-green), typecheck:core clean, gates within baseline. Quota windows now order chronologically from the data shape instead of a provider whitelist. Thank you @pacocartones!
The dashboard stores LM Studio connections under the hyphenated provider id
"lm-studio", but the embedding registry keys the provider as "lmstudio"
with no alias. As a result, "lm-studio/<model>" embedding requests failed
with a 400 unknown-provider error, and "lmstudio/<model>" requests always
hit the hardcoded http://localhost:1234/v1/embeddings endpoint, ignoring the
baseUrl of the configured connection.
Mirror the ollama-local pattern from #2824/#9225:
- embeddingRegistry: add "lm-studio" -> "lmstudio" to
EMBEDDING_PROVIDER_ALIASES (registry key unchanged so existing
"lmstudio/<model>" clients keep working).
- embeddings service: extend the optional keyless-connection hydration to
lmstudio; getProviderCredentials("lmstudio") already resolves the
"lm-studio" connection via the provider search pool/alias, and a
selection/rate-limit failure still proceeds without credentials.
- embeddings handler: apply the same baseUrl override + normalization
(strip trailing slashes and /v1, /v1/chat/completions, /v1/embeddings
suffixes, then rebuild <host>/v1/embeddings) to lmstudio, keeping the
static localhost fallback when no connection or empty baseUrl.
TDD: tests/unit/lmstudio-connection-baseurl-11233.test.ts failed on the
alias, override and service-hydration asserts before the fix and passes
after; ollama-local (#2824) and lmstudio registry (#7601) sibling tests
remain green.
Co-authored-by: Xiangzhe <bakryun0718@proton.me>
Validated on the combined 12-PR batch board: docs gates (check:docs-all incl. doc-links + fabricated-docs strict) all pass. Italian README restored to a real translation and re-synced with the canonical root README. Thank you @Natizh — and thank you for the sharp analysis of the translation-pipeline skip logic; worth a follow-up issue.
Validated on the combined 12-PR batch board: focused node:test suites green (incl. provider-rate-limit-overrides-schema + ratelimit-admission-control-6593 isolated 11/11 + the new EditConnectionModal vitest 3/3), typecheck:core clean, all static gates within baseline. Pushed one follow-up commit to your branch with the real Vietnamese translations for the two new keys (repo convention: vi never ships __MISSING__ placeholders). Per-connection maxWaitMs override lands, folding the zai-web exception into the general mechanism. Thank you @maxmad64bis!
Validated on the combined 12-PR batch board (worktree off origin/release/v3.8.50 @ 8f390eff): focused node:test suites 183/184 (the single red was a load-induced pollUntil flake — 11/11 when re-run isolated, this file included), typecheck:core clean, file-size/changelog/complexity/cognitive gates all within baseline, mutation-coverage gate no-drift. Learned effort sets now cost at most one 400 per provider+model. Thank you @maxmad64bis!
check:pack-artifact and check:pack-boot have been self-contradictory since
05/08, blocking the v3.8.50 publish in ci.yml (build:cli job) and
npm-publish.yml:
- check:pack-artifact FAILS any tarball path containing a node_modules
segment (PACK_ARTIFACT_NEVER_ALLOWED_SEGMENTS; files[] also excludes
"!**/node_modules/**").
- check:pack-boot REQUIRED sql.js under the vendored
dist/node_modules/sql.js location — a path the tarball can never carry,
so both gates could never be green at once.
The packaging model is now dependency-based: sql.js and node-machine-id
are declared `dependencies` (a clean install places them under
<packageRoot>/node_modules/), and better-sqlite3 is an optionalDependency
installed natively per platform (^13.0.2 — which also covers the
darwin-arm64 prebuild gap from #11242 by construction). The runtime
already resolves the WASM at <cwd>/node_modules/sql.js/dist/sql-wasm.wasm
(src/lib/db/adapters/sqljsAdapter.ts).
Changes:
- scripts/check/check-pack-boot.mjs: REQUIRED_SQLJS_RUNTIME_FILES now
points at node_modules/sql.js/{package.json,dist/sql-wasm.js,
dist/sql-wasm.wasm} — the dependency-installed location the clean-prefix
install actually produces. REQUIRED_MACHINE_TOKEN_RUNTIME_FILES was
already correct and is unchanged.
- bin/cli/runtime/sqliteRuntime.mjs: BETTER_SQLITE3_VERSION bumped
^12.10.1 -> ^13.0.2 to match optionalDependencies (the lazy runtime
install was pulling the wrong major), and exported for the guard.
- tests/unit/pack-boot-runtime-paths.test.ts (new, TDD: RED -> GREEN):
pins that (a) no pack-boot required path references a never-publishable
vendored dist/<segment> location (driven by
PACK_ARTIFACT_NEVER_ALLOWED_SEGMENTS), (b) sql.js/node-machine-id stay
declared dependencies, (c) the lazy-install spec stays on the declared
optionalDependency major.
- tests/unit/check-pack-boot.test.ts: the sql.js contract test pinned the
old vendored path; updated to node_modules/sql.js/dist/sql-wasm.wasm.
This is alignment to the real new contract (vendoring ended), not
masking — the same test still asserts the find-missing behavior.
Electron is unaffected: the vendored dist/node_modules bundle still
exists for Electron packaging (postinstall.mjs and assembleStandalone.mjs
untouched).
Refs #11242
Refs #10296
Co-authored-by: Xiangzhe <bakryun0718@proton.me>
The squash merge of #11228 applied its endpoint-header hunk inside
ProviderModelsModal, replacing the groupModels.map callback's return
statement with the page-level guided header JSX. The file no longer
parsed (Turbopack: 3 errors at line 2397; release-green reported the
same defect as '1 ESLint error'), red-ing every Build App / Docker
publish run since 8a42aeebb8.
Surgical revert of that single hunk: the file is byte-identical to its
pre-#11228 state. The rest of #11228 (health page verdict header,
resilience reassurance, i18n keys) parses fine and stays.
Validation: prettier parse OK; EndpointPageClient.test.tsx 4/4 (the
pre-existing jsdom render suite imports the component, so it is the
permanent regression guard); diff vs pre-#11228 empty.
Refs #9985
Co-authored-by: Xiangzhe <bakryun0718@proton.me>
The #11228 hunk spliced the guided endpoint header into ProviderModelsModal's
renderModelGroup map callback, replacing its return statement and leaving an
unclosed <div> — dashboard typecheck failed on the release tip. Restore the
modal's map return and render the header (title/subtitle//v1 URL/test link/
advanced-protocols note) at the top of the main component return, using the
i18n keys the PR added (endpoint.title/subtitle/testEndpoint/advancedProtocols)
and the existing displayBaseUrl variable.
Also untrack two docs/superpowers planning files that leaked in via #11213;
they belong to _tasks/ (copies preserved there) and their tracked presence
trips check:tracked-artifacts on every commit.
Validated: check:dashboard-typecheck OK (220 pre-existing, all within frozen
baseline; zero syntax errors in EndpointPageClient.tsx).
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.
Merged after conflict resolution: the 5 conflicting test files were the base-red drains that #11201 already landed on the tip — kept the tip versions; the feature content is untouched. Validated on the combined batch board + this branch: codex-app-server + codex-gpt56-catalog 25/25, typecheck:core clean, docs-counts green (351 providers), provider-consistency 268/351/0. The opt-in codex-app-server transport (JSON-RPC-over-WS, turn/completed-awaited close, Responses SSE bridge) leaves the default codex path untouched. Thank you @arminanton — a 3.4k-line transport with the docs wave and tests to match!
Merged after conflict resolution (validated on the combined batch board + this branch): component integrated with #10489's health-race semantics (stale-check invalidation, 3-state badge, correct useCallback deps — your validation gate and tutorial are preserved on top); the two pre-gate tests updated to validate-search-before-enable (contract propagation, commented); the docs/superpowers planning files were dropped — project rule keeps planning artifacts out of docs/ (they live in the private _tasks/ repo). Base was mistakenly main; retargeted. qdrant-config-card 7/7 + qdrant-routes integration 18/18. Thank you @rafacpti23 — the pre-enable real-search gate closes a real misconfiguration footgun!
Merged after conflict resolution (validated on the combined batch board + this branch): stale drift files (package.json/package-lock/.gitignore) kept at tip; the two modals merged additively — AWS SigV4 fields coexist with the chatgpt-web-codex tunnel fields and the #11156 Enter-handler mirror (vitest 2/2 re-run). file-size gate green with the dated AddApiKeyModal 1173 entry (owner-authorized). aws-polly modal + provider-validation-specialty 127/127. Base was mistakenly main; retargeted to release/v3.8.50. Credentials stay in the protected field; nothing logged. Thank you @rafacpti23!
Merged after conflict resolution onto the post-#10961 tip (verified no clobber of today's plaintext-wins work — the resolver functions were appended and the chatCore call-site swapped; a first --theirs attempt was caught reverting #10961 and redone hunk-by-hunk). resolveIncompatibleReasoningAction now defaults single-target incompatible reasoning to drop while combos keep their explicit strategy, with the x-omniroute-reasoning-fallback header override. 21/21 reasoning suites green, typecheck clean. Fixes#10959. Thank you @adevwithpurpose!
Validated on the combined batch board: gates clean, typecheck clean, focused tests green.
Exactly one Market id rewritten at the createTask seam (google-imagen/nano-banana-2 → nano-banana-2); every other namespaced id forwards byte-identical — the contract is stated and tested precisely. Fixes#11225. Thank you @xiaoyaner0201!
Validated on the combined batch board over tip 0b41259f: static gates clean, typecheck:core clean, 430+ focused tests green across 5 groups.
--tray returns after readiness and survives terminal close, with tray-mode autostart on macOS/Windows/Linux while headless Linux keeps the systemd user service. tray-detached + autostart suites green. Closes#11229. Thank you @tuandinh0801!
Validated on the combined batch board over tip 0b41259f: static gates clean, typecheck:core clean, 430+ focused tests green across 5 groups.
postbuild's esbuild spawn now resolves cross-platform (no more ENOENT after a successful Next compile on Windows). build-tool-runner-win-shim suite green. Thank you @aliyosufi — first contribution, welcome!
Validated on the combined batch board over tip 0b41259f: static gates clean (changelog, file-size 160 frozen, complexity 2628<=2774, cognitive 1187<=1223, dead-code 408<=416, docs-counts green at 351 providers, provider-consistency 268/351/0), typecheck:core clean, 430+ focused tests green across 5 groups.
muse-spark's all-reasoning empty-answer payloads (41 failures in 2h captured live) now floor the output budget so the model can actually emit content — no more empty-content 502 churn. Thank you @linhdmn!
Validated on the combined batch board over tip 0b41259f: static gates clean (changelog, file-size 160 frozen, complexity 2628<=2774, cognitive 1187<=1223, dead-code 408<=416, docs-counts green at 351 providers, provider-consistency 268/351/0), typecheck:core clean, 430+ focused tests green across 5 groups.
GLM-family upstreams reject message arrays with no user turn (400 [1214], verified live 2026-08-23); the synthetic user turn keeps Claude Code sessions on opencode-go alive. Thank you @linhdmn!
Validated on the combined batch board over tip 0b41259f: static gates clean (changelog, file-size 160 frozen, complexity 2628<=2774, cognitive 1187<=1223, dead-code 408<=416, docs-counts green at 351 providers, provider-consistency 268/351/0), typecheck:core clean, 430+ focused tests green across 5 groups.
max_context_window now wins over the pricing-tier context_window in Codex catalog parsing — context-aware fallback stops demoting codex behind smaller targets. Live evidence (390K served past 272K) plus the live-shape regression test. Thank you @excessivechaos!
Validated on the combined batch board over tip 0b41259f: static gates clean (changelog, file-size 160 frozen, complexity 2628<=2774, cognitive 1187<=1223, dead-code 408<=416, docs-counts green at 351 providers, provider-consistency 268/351/0), typecheck:core clean, 430+ focused tests green across 5 groups.
Comment-only correction verified against the vendor page wording: DeepSeek peak windows are Monday-Friday (35h/week), prices unchanged. Thank you @xyzs996 — precise sourcing, both language footnotes reconciled!
Validated on the combined batch board over tip 8a42aeeb: static gates clean (changelog, file-size 159 frozen, complexity 2621<=2774, cognitive 1181<=1223, dead-code 408<=416), typecheck:core clean, 107 focused tests green.
Zero-credential /v1/search now actually reaches the fallback-only providers (duckduckgo-free/searxng) instead of erroring before the last-resort block could run — the dead-code path is live. Also carries a one-line suppressHydrationWarning on the logo. Thank you @Egorich-print!
Validated on the combined batch board over tip 8a42aeeb: static gates clean (changelog, file-size 159 frozen, complexity 2621<=2774, cognitive 1181<=1223, dead-code 408<=416), typecheck:core clean, 107 focused tests green.
Combo precedence preserved when a requested name matches an existing combo or combo/* prefix, and hidden/disabled models are skipped during alias resolution (wildcard + mapped). model-alias-seed-fallback green. Related to #10124. Thank you @SCys!
Validated on the combined batch board over tip 8a42aeeb: static gates clean (changelog, file-size 159 frozen, complexity 2621<=2774, cognitive 1181<=1223, dead-code 408<=416), typecheck:core clean, 107 focused tests green.
This drains the remaining #9985 tail — the v2 dual-write assertions (guide-settings/t40), the zai→z-ai stale id, the hackclub leftovers, and the #11162 cli-combo models (#11203). Note: guide-settings also trips this devbox's container guard (/.dockerenv present), an environment artifact unrelated to CI. Base is green again. Thank you @jonlwheat2-gif!
Validated on the combined batch board + this branch: i18n suites 16/16 with Vietnamese translations of the new endpoint strings added here. Guided connection header with the /v1 URL and test action leads; advanced protocols demoted. Thank you @ignamiranda!
Validated on the combined batch board (batch-page static test green) + this branch: i18n suites 16/16 with Vietnamese translations of the batch header/step strings added here. Stable outcome header with the 3-step flow; the collapsible concept card stays as advanced detail. Thank you @ignamiranda!
Validated on the combined batch board (health-page static test green, new testing-library deps installed for the component test) + this branch: i18n suites 16/16 with Vietnamese translations of the verdict/diagnostics strings added here. Plain-language verdict leads; advanced diagnostics collapse behind a toggle. Thank you @ignamiranda!
Validated on the combined batch board (resilience-connections static test green) + this branch: i18n suites 16/16 with Vietnamese translations of the reassurance/legend strings added here. Plain-language reassurance + three-state legend lead the page; the technical table stays as advanced detail. Thank you @ignamiranda!
Validated on the combined batch board (CliConceptCard vitest 9/9 — the ACP warning never leaks onto Code/Agent cards) + this branch: i18n suites 16/16 with the Vietnamese translation of the new acp warning added here. ACP now reads as optional/advanced before its mechanics. Thank you @ignamiranda!
Validated on the combined batch board: gates + typecheck clean, i18n suites 16/16. Pre-merge: the vi.json request-flow strings arrived in English — translated to Vietnamese on the branch (vi is the locale that requires real translations, not markers). Purpose-first API-key orientation with the request-path visual, controls unchanged. Part of #11167. Thank you @ignamiranda!
Validated on the combined batch board + this branch: oauth-400-recovery + quota-connection-recovery 22/22; file-size gate green with the test/route.ts 1215 frozen entry (dated annotation for the +190 rebuilt-probe lines, owner-authorized). Unknown expiry + refresh token now means proactive refresh before probing, a hard 400 gets exactly one refresh+retry with fresh-token assertion, and the recovery tick clears stale error labels — the four dead-for-weeks antigravity connections from the production report would have self-healed. TDD with bug-injection proof on each leg. Thank you @HouMinXi!
The sponsor banner landed in en/pt without the vi copy, breaking the strict
Vietnamese parity gate (3 failing tests in i18n-vi-completeness: key parity,
ICU placeholder parity, ICU parse regression) — red on every release-branch CI
run since.
Adds the 5 missing vi translations (title, description, cta, partnerLinkNote,
dismissAriaLabel). The basereds-sse-vi worktree that owned this debt has been
idle for 4 days, so this drains it here.
Verified: i18n-vi-completeness 5/5, i18n:check-ui-coverage PASS (vi now
100.0%), i18n:check-value-drift PASS, prettier clean.
Co-authored-by: Xiangzhe <bakryun0718@proton.me>
The #11040 merge changed decidePreSpawn() to opt-in adoption
(GHSA-wg9p-6m2g-4v27: a 2xx on the probed port cannot prove the listener
is this service), but the two integration tests in ServiceSupervisor.test.ts
still asserted adopt-by-default, leaving the release tip red:
- #6205: probeBeforeSpawn adopts a healthy existing instance (no spawn)
- adopted service resolves and records the real pid of the process holding the port
Both now set OMNIROUTE_ADOPT_EXISTING_SERVICE=1 (restored in finally) so the
adoption path they exercise stays covered under the new contract. Adds a new
default-deny case asserting that without the flag a healthy listener is NOT
adopted and the error names the opt-in escape hatch.
Verified against base tip 6cd4d38e21: file is 8/8 green,
ninerouter-embed-port-6205.test.ts still 9/9, eslint + prettier clean.
Co-authored-by: Xiangzhe <bakryun0718@proton.me>
Validated on the combined batch board + this branch: synced-inventory + auth neighbor suites 16/16; file-size gate green with the auth.ts 3337 frozen entry (dated annotation for the +77 inventory-filter lines at the credential-selection chokepoint, owner-authorized). Chat routing now pins to the connection whose synced inventory actually advertises the model — no more spurious model-not-found on multi-host self-hosted setups. Scoping call (not building on the still-open #11088) was the right one. Fixes#11089. Thank you @pacocartones and @yourspraveen for the precise report!
Validated on the combined batch board over tip c92bd40b: static gates clean (changelog, file-size 158 frozen, complexity 2626<=2774, cognitive 1183<=1223, dead-code 409<=416), typecheck:core clean, 70 focused tests green (PR suites 49/49 + auth/combo neighbors 21/21).
Keyless custom-compatible connections stay in auto/* pools — the credential filter now recognizes a registry-free keyless endpoint instead of dropping the connection before pool construction. Fixes#11180. Thank you @pacocartones!
Validated on the combined batch board over tip c92bd40b: static gates clean (changelog, file-size 158 frozen, complexity 2626<=2774, cognitive 1183<=1223, dead-code 409<=416), typecheck:core clean, 70 focused tests green (PR suites 49/49 + auth/combo neighbors 21/21).
Five g4f-* entries re-flagged hasFree:false with the live-probed 402 evidence (proof-of-work wall, member key still works); the two dissenting providers deliberately untouched, matching the chutes/aimlapi/yi precedent. Fixes#10071. Thank you @pacocartones and @chirag127 for the capture!
Validated on the combined batch board over tip c92bd40b: static gates clean (changelog, file-size 158 frozen, complexity 2626<=2774, cognitive 1183<=1223, dead-code 409<=416), typecheck:core clean, 70 focused tests green (PR suites 49/49 + auth/combo neighbors 21/21).
Operator-configured positive minTime floor now survives the plenty-of-headroom relaxation (resolveMinTime instead of a hard 0). Fixes#9763. Thank you @pacocartones!
Validated on the combined batch board + this branch: perf-a-b-c-d + account-fallback-service 93/93, gates + typecheck clean. Owner approved the full bundle including item A (async proxy-log batching, 1s/100-item flush with an unref'd timer — reviewed the implementation: flush helper exists for shutdown wiring, buffered logs are the accepted tradeoff). B (lazy modals), C (O(1) alias maps), D (pre-compiled regex — this is also the entire content of #11187, being closed as subsumed) ride along. Conflict with the tip was only stale provider-count docs. Thank you @rqzbeh!
Validated on the combined batch board + this branch: antigravity-dynamic-session-id + proxy-fetch-dns-retry green; file-size gate green with the proxyFetch 1244 frozen entry (dated annotation for the +5 retry-classification lines, owner-authorized). Static per-account sessionId unpinning ends the concurrent-turn 429s and EmptyStreamError drops on the Hermes→Antigravity path; EAI_AGAIN/ENOTFOUND/ETIMEDOUT now classified retryable. Conflict with the tip was only stale provider-count docs. Resolves the remaining #10443 root causes. Thank you @rqzbeh!
Validated on the combined batch board + this branch: login-11143 green. Full document navigation after login guarantees the auth_token cookie is committed before any RSC prefetch fires — no more 307 back to /login. Conflict with the tip was only stale provider-count docs. Fixes#11143. Thank you @rqzbeh!
Validated on the combined batch board + this branch: dashboard-ux-operability green. Unmapped custom quota keys now render as title-cased labels instead of raw snake_case. Conflict with the tip was only stale provider-count docs. Thank you @rqzbeh!
Validated on the combined batch board (gates + typecheck clean) and this branch: security-route-guard-tiers green. Regression coverage for the Hard Rule #15/#17 contract — Tier 1 process-spawning prefixes (/api/services/, /api/mcp/, /api/cli-tools/runtime/) must stay LOCAL_ONLY before any auth check. Conflict with the tip was only stale provider-count docs. Thank you @rqzbeh!
Merged after conflict triage: the six base-red repair files (vi.json, opencode.ts JSDoc, context-manager test, the three webhook dispatcher tests, the uncloseai orphan-test rename) were already drained on the tip by today's #11130/#11157/#11160/#11113 — those hunks resolved to the tip shape. What lands is the production-fix set: GLM transport-aware Anthropic headers, Claude Code-compatible model-listing rejection, combo live-test single-probe, zero-cost Auto-Combo interval normalization, recovery-clearing union handling, LLMLingua real-path compare, macOS netstat PID discovery, AI Horde R2 strict public-host validation. Sweep of every touched test file: 243/243 green; typecheck + file-size clean. (guide-settings-route's 4 reds reproduce on the pure tip — pre-existing drift from #11079, not from here.) Thank you @backryun!
Merged after count reconciliation: the branch's regenerated docs claimed 57 free forever / 157 migrations from its older base; gate-verified values on the current tip are 56 free forever (Logfare carries a Free badge via gateways.ts freeNote but has no freeModelCatalog per-model entries, so the live-code counter stays at 56) and 159 migrations — the README/SVGs now match the check:docs-counts output exactly. provider-consistency OK at 267 registry / 349 canonical. logfare-registry 4/4, icon + KNOWN_PNGS + discovery-set membership all verified present. Closes#10644. Thank you @jonlwheat2-gif!
Validated on the combined batch board + this branch: 231/231 across chatcore-translation-paths, reasoning-cache, strip-reasoning-blobs, and both Responses translator suites. Pre-merge: propagated the #11110/#11129 summary:[] defaults into five assertions here (each commented with its PR) — without it this branch red against the tip, and as a bonus the merge drains the 4 reasoning reds that were live on the tip from those merges. Plaintext now wins over a coexisting opaque companion; opaque-only drops cleanly for plaintext targets; combos keep explicit Skip. Fixes#10949 and #10959. Thank you @jackjinke!
Merged after conflict resolution in modelMetadataRegistry.ts: the tip's effortTiers chain (declared efforts → declared tiers → undefined-if-thinking-declared → codex extension) now carries this PR's GLM guard as the final-fallback override — GLM-family models without a provider-declared contract get the authoritative empty tier list instead of generic OpenAI tiers. GLM/ZCode suites 40/40 on the resolved branch. Closes#10962. Thank you @xz-dev!
Validated on the combined batch board + this branch: ccr-non-mcp-full-prompt-loss + ccr-retrieval-ramp 20/20, file-size gate green with the ccr/index listing (1024, dated annotation — owner-authorized). The callerSupportsCcrRetrieve gate now skips the whole engine for callers whose tools[] cannot reach omniroute_ccr_retrieve — no more 15KB prompt arriving upstream as 112 tokens. Production-measured root cause, textbook TDD. Thank you @HouMinXi!
Validated on the combined batch board + this branch alone: chat-body-admission + authz/pipeline 65/65, file-size gate green with a dated frozen entry (chatBodyAdmission 1005→1009 — the +4 lease/drain wiring lines, owner-authorized rebaseline). trackRequest was never called, so SIGTERM waitForDrain saw zero in-flight and killed live SSE; leases now hold the drain counter for the stream's lifetime, and the 503 carries Retry-After. Closes#11015. Thank you @RaviTharuma!
Compaction-V2 output now counts as real model output (no synthetic response.failed after response.completed), the Codex SSE filter handles CRLF framing, and terminal detection runs before scan-state bounding. 88/88 stream/readiness suites on the board. Thank you @jackjinke!
SSE comment lines (OpenRouter keep-alives) and leading whitespace no longer fail the combo quality gate's JSON fallback. First contribution — clean minimal fix with test. Thank you @asorourx, welcome aboard!
- New CheaperInferenceSponsorBanner on the dashboard home, same size/shape as
KimiSponsorBanner, no version gate (durable partnership). Uses the
cheaperinference ProviderIcon and the brand green (#31f889) with the dark
ink CTA (contrast, per colors.ts token).
- CTA points at https://link.omniroute.online/cheaper — the branded short
link — so clicks land in our Kutt metrics.
- VscodeCopilotBanner CTA now points at https://link.omniroute.online/vsx
instead of the raw Marketplace URL, for the same reason.
- i18n strings in en + pt (en is the namespace-level fallback for the other
41 locales).
- Tests: new cheaperInferenceSponsorBanner.test.tsx (render, CTA href, dismiss
persistence); vscodeCopilotBanner.test.tsx updated to the new CTA URL.
Co-authored-by: Xiangzhe <bakryun0718@proton.me>
* feat(api): structured ?format=json for the self-service usage endpoint
GET /api/usage/om-usage already let any key read its own usage — personal
daily/weekly USD limits and the provider quota snapshot — but only as
text/plain, which a UI cannot parse safely. OmniCopilot issue #8 asks exactly
for this surface.
Adds ?format=json, returning the ApiKeyUsageLimitStatus + UsageSnapshot the
text is rendered from. Text and JSON share the same collectors
(collectUsageSnapshots, getApiKeyUsageLimitStatus), so the two can never
disagree about a number. The response is a discriminated union: a key without
allowUsageCommand (403) or an invalid key (401) returns
{ allowed:false, error:{message} }, distinct from allowed:true with empty
sections — the state a panel must render as "nothing learned yet", not a
refusal. Text form unchanged; without ?format the contract is untouched.
The endpoint was previously missing from API_REFERENCE.md; it now has a
section documenting both forms, the allowUsageCommand gate, and the
self-service auth model (caller's own key, not requireManagementAuth).
Regression guards in tests/unit/usage-command-json-format.test.ts (4 tests:
json shape, text default preserved, structured 403, sanitized 401 with no
stack trace). Existing internal-usage-command suite still 12/12.
* chore(changelog): correct the fragment to the real PR number (#11190)
* feat(api): return every connection's snapshot under providers[] in om-usage json
Closes#11191. buildUsageCommandJson picked a single snapshot via selectUsageSnapshot, so a panel could only ever show one provider. The collector already had them all — the single-pick is a presentation choice for a terminal. The JSON form now also returns the full UsageSnapshot[] alongside the selected provider, so a UI can render Codex / Claude / OpenCode side by side. The text form is untouched.
---------
Co-authored-by: Xiangzhe <bakryun0718@proton.me>
* feat(api): structured ?format=json for the self-service usage endpoint
GET /api/usage/om-usage already let any key read its own usage — personal
daily/weekly USD limits and the provider quota snapshot — but only as
text/plain, which a UI cannot parse safely. OmniCopilot issue #8 asks exactly
for this surface.
Adds ?format=json, returning the ApiKeyUsageLimitStatus + UsageSnapshot the
text is rendered from. Text and JSON share the same collectors
(collectUsageSnapshots, getApiKeyUsageLimitStatus), so the two can never
disagree about a number. The response is a discriminated union: a key without
allowUsageCommand (403) or an invalid key (401) returns
{ allowed:false, error:{message} }, distinct from allowed:true with empty
sections — the state a panel must render as "nothing learned yet", not a
refusal. Text form unchanged; without ?format the contract is untouched.
The endpoint was previously missing from API_REFERENCE.md; it now has a
section documenting both forms, the allowUsageCommand gate, and the
self-service auth model (caller's own key, not requireManagementAuth).
Regression guards in tests/unit/usage-command-json-format.test.ts (4 tests:
json shape, text default preserved, structured 403, sanitized 401 with no
stack trace). Existing internal-usage-command suite still 12/12.
* chore(changelog): correct the fragment to the real PR number (#11190)
---------
Co-authored-by: Xiangzhe <bakryun0718@proton.me>
Merged after sibling #11151 landed: streamRecovery.ts auto-merged byte-identical to the validated combined board; the test-file conflict (both PRs added suites at the same anchor) resolved keeping all 11 tests — #11151's four clean-stop cases plus this PR's three threshold cases, with the PR's updated partial-tail fixture for the pre-existing overlap test. Full chain green: 32/32 (wiring + continuation + toolcall regression). The documented 8-char overlap threshold ends the silent mid-word gluing. Thank you @maxmad64bis!
Merged after conflict resolution against the tip's #11109 (per-call tool_call tracking): scanOpenAiSseText keeps the per-call finish_reason special-case AND gains reasoningText + literal finishReason; canContinue uses the in-flight predicate with the new reasoning-only-clean-stop escape. One integration fix on the branch: the PR's hallucinatedEmptyStop referenced emittedToolCall, which #11109 had renamed — the branch now tracks emittedSawToolCall at the emitted level (any tool_call delta, complete or not), preserving the PR's don't-recover-after-tool-calls intent. Chain suites green: stream-continuation-wiring + stream-continuation + stream-recovery-toolcall 29/29. Thank you @maxmad64bis!
Validated on the combined batch board: purify-system-first suite 4/4, typecheck clean. Pre-merge: file-size baseline gained a frozen entry for contextManager.ts at 1001 (+1, this PR's merge-into-leading-system branch) with a dated annotation — the gate caps unlisted files at 1000. Producer side of the live-confirmed TokenRouter 400 class: no internal path emits a mid-array system message anymore. Thank you @ggdayup — the call-log evidence made this airtight!
Validated on the combined batch board over tip 92ef3c71: static gates clean (changelog, file-size, complexity 2624<=2774, cognitive 1182<=1223, dead-code 411<=416), typecheck:core clean, focused tests green.
Empty-envelope 400 (no error field, empty content, finish_reason null) now rotates/retries instead of propagating as success; 200/streaming path never buffered; real-error 400s untouched. account-rotation + new rotation suite 34/34 on the board. Thank you @maxmad64bis!
Validated on the combined batch board over tip 92ef3c71: static gates clean (changelog, file-size, complexity 2624<=2774, cognitive 1182<=1223, dead-code 411<=416), typecheck:core clean, focused tests green.
Combo without models is now refused at the schema boundary (API 400), the CLI flags it, and openapi.yaml matches the real contract (phantom props removed). combo-* suites + cli-combo-create-models green on the board. Closes#10954. Thank you @maxmad64bis!
Validated on the combined batch board over tip 92ef3c71: static gates clean (changelog, file-size, complexity 2624<=2774, cognitive 1182<=1223, dead-code 411<=416), typecheck:core clean, focused tests green.
Companion to #11113 (consumer side): tokenrouter joins BUILTIN_PROVIDERS_SYSTEM_MUST_BE_FIRST — memory-system-first-6135 suite green. Live-confirmed 400 class documented in the body. Thank you @ggdayup!
Validated on a worktree over the current tip: account-fallback-service 91/91 plus the five sibling lockout suites 24/24. The measurement in the body (40 of 111 passthroughModels providers uncovered on this branch) is the clincher — one lookup via getProviderById().passthroughModels beside the existing checks, closing the #11071 remainder for shared-registry gateways (port of #11075 which had only landed on main). Thank you @yourspraveen!
Validated against code before merge: 159 migration files on disk, 56 free-forever (Hack Club removal), 40 pools — counts verified, not trusted. check:docs-counts exit 0 (HARD failures drained; the 2 remaining soft executors-count notes are pre-existing on the tip) and check:test-discovery OK (orphan moved into the collected tree). These reds came from #11103/#11123 merging without the count regen — thanks for sweeping them @yourspraveen!
Validated on a worktree over the current tip: the red it fixes reproduced exactly as described (media-page-client-browser-bundle red since #11122 — providerRegistry became reachable from the dashboard client bundle via node:net). Post-fix: bundle test 2/2 green, new ip-parity suite + is-local-provider 7/7, all 7 outboundUrlGuard consumer suites 76/76 (the moved normalizeHost/isPrivateHost keep their re-exports; routing behavior untouched). Thank you @yourspraveen — clean surgical extraction with a pure-JS ipVersion mirroring Node's own regexes.
Cherry-picked onto the current tip (authorship preserved, Dockerfile.bun conflict with the just-merged #11163 resolved additively — runner-web stage after the new entrypoint). Three pre-merge fixes on the branch: (1) generated-count noise stripped; (2) runner-web stage now returns to the non-root bun user after the apt install (mirrors the Node Dockerfile runner-web re-asserting USER node — the stage previously ended as root); (3) the 6 new build/manifest steps SHA-pinned so the zizmor ratchet stays at 191<=192 findings instead of regressing to 197 (actionlint clean). Workflow YAML parses; runner-base/runner-web targets cross-checked against the Dockerfile stages. Thank you @rqzbeh!
Cherry-picked onto the current tip (authorship preserved), noise stripped. Validated on BOTH runtimes: bun test tests/unit/db-adapters/ 44/44 under the pinned Bun 1.3.14 (native bun:sqlite path), and node --test on the same suite 52 pass / 0 fail / 1 skip (Bun-only adapter skips under Node, as designed). Dockerfile.bun entrypoint now matches the standalone runner shape. Follow-up to #11039. Thank you @rqzbeh!
Cherry-picked onto the current tip (authorship preserved), generated-count noise stripped. Two pre-merge adjustments: (1) dropped the unrelated localDb.ts re-export hunk (nothing in this PR uses those symbols); (2) automated security review flagged the blocked-provider list resolving once at server creation — the handler now rebuilds the schema per invocation via the resolver (advertised tools/list schema stays a creation-time snapshot, which is inherent to MCP). Vitest: new runtime-blocked-schema suite 3/3, full MCP __tests__ 118/118; contract suites (mcp-web-search-provider-enum-contract, search-blocked-providers-11100) 6/6; typecheck clean. This closes the residual gap noted when #11120 was closed. Thank you @rqzbeh!
Cherry-picked onto the current tip (authorship preserved), noise stripped. Focused: oauth-device-flow-11164 green + 9474-claude-code-oauth-mismap neighbor suite green. Device-code endpoint is tried first, camelCase/snake_case fallbacks normalized, no more blank code / 'Visit: undefined'. Fixes#11164. Thank you @rqzbeh!
Cherry-picked onto the current tip (authorship preserved), generated-count noise stripped. Pre-merge: file-size baseline rebaselined 1080→1082 with dated annotation (the +2 lines are the Enter-handler isCheckDisabled mirror — owner-requested #11056 polish; rest is Prettier reflow). Gate green; vitest add-api-key-modal-enter-key 2/2 (jsdom render test). Thank you @rqzbeh!
Cherry-picked onto the current tip (authorship preserved), noise files stripped. Pre-merge addition: regenerated the golden snapshot with UPDATE_GOLDEN=1 because the branch's snapshot predated two legitimate tip changes — the dify bare-root from #11065 and the hackclub removal from #11123. The regen'd delta contains exactly those two (audited). This also drains a live base-red: provider-translate-path-golden was failing on the pure tip. 3/3 green. Thank you @rqzbeh!
Cherry-picked the JSDoc commit onto the current tip (authorship preserved), stripping the stale generated-count noise files. Focused: opencode-v2-config-11070 2/2. Comments now match the 128k fallback shipped in #11035/#11054. Thank you @rqzbeh!
Merged after conflict resolution validated on the combined board (50/50 casing tests green, typecheck:core clean). Two pre-merge adjustments on the branch: (1) the utilization route conflict resolved to the tip shape — its asNullableString/displayName version is newer than the branch's; (2) dropped the newly-added src/lib/db/connections.ts, orphaned once the route kept the tip shape (tip already uses getProviderConnectionById) — nothing imported it. The casing fix itself lands intact: non-streaming OpenAI→Claude conversion now restores canonical tool names, identity echoes no longer pin lowercase, and TOOL_RENAME_MAP gained the Task* tools. Fixes the live-reproduced Claude Code 'No such tool available: bash' failures. Thank you @linhdmn — outstanding repro and root-cause writeup!
Merged with the ENVIRONMENT.md hunk dropped: the tip already documents unset=unlimited for DEFAULT_RATE_LIMIT_PER_DAY via #11022 (eba58cc8), so the docs conflict resolved to the tip text. What lands is the .env.example comment correction, verified against src/shared/utils/apiKeyPolicy.ts::buildDefaultRateLimits — unset/empty → [] (unlimited), malformed → legacy 1000/day windows, explicit 0 → unlimited. Conflict resolution validated on the combined board (env-doc-sync gate green). Thank you @Prajeeth-12!
Validated on the combined board over tip 80d931ae: quota-redis-store (incl. the KEY_PREFIX derivation test), local-redis-status and rate-limiter-redis-optional green, typecheck:core clean. One pre-merge fix pushed to the branch: docs/reference/ENVIRONMENT.md gained the REDIS_KEY_PREFIX row (env-doc-sync gate requires every .env.example var documented). Board note: the redis tests leave an ioredis retry handle open and hang the runner exit locally — assertions all pass; pre-existing pattern, not from this PR. Thank you @MeRezaRezaei!
Validated on the combined board over tip 80d931ae: kimi suites green (executor-kimi-web, kimi-partner-aff-links, token-health-check-kimi 33 assertions), vitest providerPageHeaderKimiPartnerLink green, typecheck:core clean. Audited the diff: only kimi-web switches to the international www.kimi.ai (Connect-RPC base, website, auth hints); kimi-coding / kimi-coding-apikey affiliate links stay on kimi.com as intended — asserted by the updated tests. Owner approved merge without the VPS smoke. Thank you @MeRezaRezaei!
Validated on a combined board over tip aa128736 (incl. sibling #11081): call-logs-row-filter 4/4 green, typecheck:core clean. The merged-row predicate fix closes a real gap — in-memory (in-flight/recently-completed) rows bypassed every filter except correlationId; rowMatchesFilter() now applies search/model/provider/account/apiKey/status/combo uniformly while DB rows stay idempotent. Thank you @AndrianBalanescu!
Validated on a combined board over tip aa128736: focused tests local-rerank-logging + call-logs-row-filter green (7/7 across runs), typecheck:core clean. One pre-merge fix: widened waitForCallLogSaves 5s→15s — the call-log artifact writer's fsync latency exceeds 5s under a loaded host (pre-existing call-log-save-drain flakes identically on the pure tip), so the budget was load-flaky, not the code. Local rerank now logs call entries on success and error paths like embeddings/cloud rerank, captures full request/response payloads, attaches x-omniroute-* headers, and falls back /v1/rerank→/rerank on 404. Thank you @AndrianBalanescu!
Cherry-picked the author's cleaned single-commit head onto the current tip. One fix applied pre-merge: the removal migration collided at number 161 with today's config_audit_log migration (#11103) — renumbered to 162_remove_hackclub_provider.sql. Focused tests: remove-hackclub-11118 + provider-metrics-deleted-provider green; check:provider-consistency OK (266/348/0). Provider removal requested by Hack Club maintainers (#11118). Thank you @rqzbeh!
Discriminated against the pure base tip (all three reproduced without any PR diff): (1) getTokenLimit test aligned to the contract changes of #8228/#11034 with the bluesminds 200k pin kept as the original guard; (2) Vietnamese translations completed for harImport*/omni-webhooks (upstream already carried equivalent translations — conflict resolved to base); (3) eslint gate fixed by typing the dynamic core imports in capture-critical-db-state.test.ts (no-explicit-any). 41/41 tests green, typecheck clean, eslint exit 0.
Cherry-picked the three value commits onto the current tip, dropping the stale base-red sync commits. Focused tests: search-blocked-providers-11100 + search-registry/searxng-loopback/chat-guard/x-search suites 65/65. Fixes#11100 (endpoint half) — GET /v1/search now honors blockedProviders via getAllSearchProviders. Thank you @rqzbeh!
Cherry-picked both value commits (a1fa49a8 + a91f6e93, incl. the fail-open guard for unparseable hostnames) onto the current tip, dropping the stale base-red sync commits. Focused tests: is-local-provider-11091 + the isLocalProvider consumer suites (provider-validation-specialty, ollama-local-provider) 127/127; mutation coverage no-drift. Fixes#11091 — RFC1918/CGNAT/link-local/mDNS hosts now classify as local via the existing isPrivateHost. Thank you @rqzbeh!
Cherry-picked the value commit (2c9202e4) onto the current tip, dropping the stale base-red sync commits. Focused tests: ollama-404-model-lockout 2/2 + the five sibling lockout suites (combo-provider-cooldown-sibling, 8247-model-unhealthy, vertex-passthrough, nvidia-410, account-fallback-service) 112/112; mutation coverage no-drift. Fixes#11071 — local/self-hosted 404s now scope to model lockout per the resilience doctrine. Thank you @rqzbeh!
Cherry-picked the value commit (55da60f5) onto the current tip, dropping the stale base-red sync commits. Focused tests: opencode-v2-config-11070 + merge-provider-guard + config-dir-single-source 12/12; mutation coverage gate no-drift (new test registered in tap.testFiles by the PR itself). Fixes#11070 — setup-opencode now emits both V1 and V2 blocks. Thank you @rqzbeh!
Cherry-picked the value commit (0e918816) onto the current tip, dropping the stale base-red sync commits. Focused tests: pollinations-api-key-required 1/1 plus the whole optional-key suite 142/142 (two legacy assertions in provider-route-schemas flipped to the new key-required contract, commented with the PR). Fixes#11096 — Pollinations answers 401 anonymously now. Thank you @rqzbeh!
Cherry-picked the value commit (ac09b6b5) onto the current release tip, dropping the stale base-red sync commits that no longer apply. Focused test cline-model-format-11099 2/2 green; check:provider-consistency OK (267/348/0). Fixes#11099 — the zai→z-ai namespace typo. Thank you @rqzbeh!
Validated on the combined batch board over release/v3.8.50 tip d91238b7: static gates clean, typecheck:core clean, focused tests green.
Learned reasoning_effort caps mirror the merged learnedThinkingCaps mechanism: parse the upstream 4xx enum, clamp, retry once, consult proactively — covers custom openai-compatible connections the static registry can't. 22 new test cases + full regression list green. Fixes#11111. Thank you @maxmad64bis!
Validated on the combined batch board over release/v3.8.50 tip d91238b7: static gates clean, typecheck:core clean, focused tests green.
TDD red->green: parallel function_call items now get distinct stable index/id at .added time via a per-call Map, interleaved argument deltas no longer glue, dual item_id/output_index correlation. 120-test translator suite green. Thank you @maxmad64bis!
Validated on the combined batch board over release/v3.8.50 tip d91238b7: static gates clean, typecheck:core clean, focused tests green.
Operator-declared per-provider error rules via settings, consulted before the built-ins; the allowlist bypass is correct — declaring a rule is itself the opt-in, and no provider-specific rule is hardcoded. Thank you @maxmad64bis!
Validated on the combined batch board over release/v3.8.50 tip d91238b7: static gates clean, typecheck:core clean, focused tests green.
Config-audit survives restarts with bounded growth (migration 161 + OR IGNORE seed, retention wired into runAutoCleanup); route cabling deliberately out of scope. Thank you @maxmad64bis!
Validated on the combined batch board over release/v3.8.50 tip d91238b7: static gates clean, typecheck:core clean, focused tests green.
Strict schema + {sanitized, rejected} DB boundary — silent validation degradation now answers 400 with the offending keys. Caller audit done: only the providers write path consumes the sanitizers. Thank you @maxmad64bis!
Validated on the combined batch board over release/v3.8.50 tip d91238b7: static gates clean, typecheck:core clean, focused tests green.
Defaults summary and strips malformed ids on kept Responses input items — both 400s observed against live muse-spark traffic; the flipped legacy assertion is documented contract propagation. Thank you @maxmad64bis!
Validated on the combined batch board over release/v3.8.50 tip d91238b7: static gates clean, typecheck:core clean, focused tests green.
Real suggestionCount replaces the conflated link count (deprecated alias kept), dashboard deep-link fixed. Thank you @maxmad64bis!
Validated on the combined batch board over release/v3.8.50 tip d91238b7: static gates clean, typecheck:core clean, focused tests green.
Restores the models[0] dashboard default silently changed by #11051, with narrow guards instead of a brittle full snapshot. Thank you @maxmad64bis!
Validated on the combined batch board over release/v3.8.50 tip d91238b7: static gates clean, typecheck:core clean, focused tests green.
Closes the two #11050 blockers: dispatcher tests now derive from WEBHOOK_EVENT_VALUES (no more TypeError on removed events) and vi.json carries a real translation. Thank you @maxmad64bis!
Validated on the combined batch board over release/v3.8.50 tip d91238b7: static gates clean, typecheck:core clean, focused tests green.
Live-confirmed 400 (missing summary on freshly-built Chat->Responses reasoning items); the two legacy assertions updated are contract propagation, each commented. Thank you @maxmad64bis!
Validated on the combined batch board over release/v3.8.50 tip d91238b7: static gates clean, typecheck:core clean, focused tests green.
Test-only followup to #11043 — 3 direct tests for splitConcatenatedToolCallArguments plus the index-normalization comment, exactly the two review nits. Thank you @maxmad64bis!
Validated on the combined batch board over release/v3.8.50 tip d91238b7: static gates clean (changelog-integrity, file-size, complexity 2619<=2774, cognitive 1178<=1223, dead-code 411<=416), typecheck:core clean, focused tests green (262 batch-touched tests pass; the only red is the pre-existing vi.json harImport key drift from #11069, reproduced on the pure tip — not this batch).
Follow-up fix pushed to the branch pre-merge: the changelog fragment was prose, now a bullet (fragment gate). Thank you @maxmad64bis — the root-cause writeup (per-call finish_reason vs general terminal marker making the in-flight guard a no-op) is exactly right.
Same inherited base-red set as #11148 (getTokenLimit + vi parity + ESLint gate — all reproduce on the pure base tip). Merge integrity, Docs Gates, Vitest, Build: green.
Inherited base-red at merge time (discriminated against the pure base tip, both reproduce WITHOUT this diff): getTokenLimit test + Vietnamese i18n key parity (new UI strings merged untranslated) + No new ESLint warnings gate. Merge integrity, Docs Gates, Vitest, Fast Production Build: green.
⭐5 — M365 Copilot (BizChat) individual/consumer path — 3 itens: (1) forma de invocação do #10718 derivou de novo (2026-08-21 capture): optionsSets 14→34, allowedMessageTypes 6→30, tone "magic"→"Magic", plugins []→[{BingWebSearch}], disconnectBehavior em todos os tiers, +8 keys de clientInfo; verificado contra conta real com round-trip WebSocket (ping-then-close → resposta real). (2) Aviso sobre o alias Antigravity gemini-3.1-pro-high ainda não publicado (3.8.49 pré-data). (3) Botão "Import .har file" no modal de credencial M365.
Conflito resolvido em copilot-m365-frames.ts (board vs release tip): mantive o forwarding de opts.plugins/toolChoice/customInstructions do HEAD com os NOVOS defaults da captura (BingWebSearch builtin, tone "Magic"). Alinhei 3 testes pré-existentes que afirmavam o contrato antigo (m365-bizchat-frames-4042 clientInfo, m365-tone-model-variants tone, copilot-m365-tool-calls plugins) — propagação de contrato, não mascaramento. Rebaselinei AddApiKeyModal 1073→1080 (crescimento próprio da parte 3, ~Har import button) com anotação.
Validação: typecheck limpo, 142/142 testes m365/copilot verdes, changelog-integrit/file-size/eslint OK.
⭐5 — Fecha #10265: chat do command-code migra do endpoint CLI-only /alpha/generate (version-gated + proxy-blocked para callers externos) para o documentado /provider/v1/chat/completions (OpenAI format). Removido o envelope CLI reverse-engineered (config/memory/taste/skills + headers CLI-impersonation), substituído por passthrough OpenAI plano com normalização de model id vendor-prefixed (#10809), clamp de max_tokens (#5166), sanitização de reasoning_effort. commandCode.ts 1037→171 linhas.
Validado no worktree board sobre tip: typecheck:core limpo; 175/175 testes focados (command-code executor/vision/usage/maxtokens/user-array/validation-specialty/responses-handler/provider-models-scoping); changelog-integrity/file-size/complexity/cognitive todos OK. TDD RED→GREEN documentado.
⭐5 — /v1/combos strip connectionId de propósito; dois passos pinando contas DIFERENTES do mesmo provider viram objetos byte-idênticos e um cliente conclui que não há failover. Adiciona accountPinned (boolean derivado do connectionId, nunca vaza o id) em cada passo model; sempre true/false, nunca em combo-ref. Sem rota/schema/mudança de caller. TDD. Fecha #10968.
⭐4 — Suporte de backend nativo Bun + Dockerfile.bun multi-stage + fallback dinâmico de driver SQLite (better-sqlite3 prioritário sob Bun, bun:sqlite fallback; Node preservado) + correção de estabilidade do DAST CI smoke.
Validado a fundo (worktree board sobre tip): bun-support 4/4, typecheck:core limpo, dashboard-typecheck OK (220 dentro do baseline), open-sse-typecheck OK (5 pré-existentes), gate de runtime OK sob Node, changelog-integrity OK, file-size/complexity/cognitive/dead-code OK. Verificado que o driver preserva a cadeia Node/falback conforme AGENTS.md; teste bun-support presente. Baselines de typecheck removidos são ratchet honesto (erros não existem mais).
OBS: destravei 2 base-reds do tip neste turno (push direto 7ffa3ef): movi o changelog fragment da #11050 da seção inválida breaking/ para fixes/, e rebaselinei AddApiKeyModal 1067->1073 (crescimento da #11056). Sem isso a #11039 e o resto da fila ficariam vermelhos.
⭐5 — Atualiza domínio do provider hailuo-web para chat.minimax.io (#11000). Merge autorizado pelo operador (ordem explícita de prosseguir sem o smoke VPS da Hard Rule #18).
⭐5 — computeConnectionDefaultName aceita array de nomes existentes e acha o próximo não-conflitante, evitando overwrite de nome de conexão. TDD. Fecha #11033.
⭐5 — Preenche os 3 test.skip com asserções reais (resetDbInstance troca o singleton preservando a linha no disco, journal_mode WAL, schema_version=1). Além do valor pretendido, o autor redesenhou o setup()/cleanup() do arquivo corrigindo um bug de isolamento pré-existente que eu apontei em review: DATA_DIR/SQLITE_FILE são const de topo de módulo; o cleanup() usava require() CJS que nunca resetava a instância ESM-importada, então os testes 1-4 passavam "por acidente" contra a conexão nunca fechada. Agora: tempDir compartilhado definido antes do primeiro import, resetDbInstance importado via ESM uma vez, handle fechado antes de cada reopen, e o catch{} silencioso removido. 7/7 verdes no arquivo inteiro.
⭐5 — LIMIT negativo = "sem limite" no SQLite: ?limit=-1 retornava a leaderboard inteira em endpoint management autenticado; ceil por Math.min só no upper. Duas camadas: route rejeita não-inteiro/fora de range com 400 (mesmo contrato de parseListLimit), getTopN clampeia como backstop defense-in-depth + exporta LEADERBOARD_MAX_LIMIT. TDD red→green, 5 casos novos, 84/84 suíte gamification. Fecha #11058.
⭐5 — Dedupe dos 3 entries byte-idênticos (kimi-k2.7-code, qwen3.5-plus, qwen3.6-plus) entre opencode-zen e opencode-go via OPENCODE_ZEN_GO_SHARED_MODELS (padrão GLM_SHARED_MODELS), frozen e testado por deep-equal. Pure move, sem mudança de comportamento; check:provider-consistency OK (267 entries, 348 providers). TDD 3/3. Follow-up de #11049/#11048. Base-red #9985 inherited.
⭐5 — Muse Spark responde só na Responses API; #10874 corrigiu só o provider `opencode`, mas `opencode-zen` (faltava targetFormat em 2 entries) e `opencode-go` (6 entries effort-tier) nunca receberam. Espelha a declaração. freebuff NÃO tocado (backend codebuff.com /chat/completions distinto, verificado). TDD 2/2 + 18/18 irmãos. Fecha #11048. Base-red #9985 inherited.
⭐5 — Providers que não bumpam index/id em tool calls repetidas do mesmo nome colam N arguments JSON num só ({...}{...}{...}); leitores a jusante pegam só o primeiro e dropam o resto em silêncio. Detecta N objetos concatenados e divide de volta em N tool_calls. TDD 20/20 + 86/86 irmãos. Fecha #11044. Base-red #9985 inherited.
⭐5 — OpencodeExecutor buscava targetFormat com o id bruto do provider em vez do alias público ("oc"), caindo em default openai → corpo Responses no URL chat/completions (400). resolveOpencodeTargetFormat resolve via PROVIDER_ID_TO_ALIAS. TDD 4/4 + 32/32 irmãos, lint/tsc limpos. Fecha #11046. Base-red #9985 inherited.
⭐5 — Drain de base-reds v3.8.50 (#9985): corrige build-break do #10939 (import fantasma @/lib/db/connections → reroteado por getProviderConnectionById real), headers Anthropic no fallback GLM, fallthrough do reject de listing CC-compatible, no-auth leak no auto-combo, retry de mesmo-account nos combo live tests, pin onnxruntime-node ~1.24.3; + alignments de teste + gates (env-doc, docs-counts 347→348, openapi floor, i18n vi/pt-BR).
Reconciliado com o tip: 5 conflitos de contagem gerada (SVGs + PROVIDER_REFERENCE — HEAD já em 348, branch em 347 stale) resolvidos ficando com o HEAD. typecheck limpo, gates verdes, 5/5 testes focados (#10939 regressão + utilização), lint limpo nos tocados.
Também rebaselined neste turno via push direto ao tip: config/quality/file-size-baseline.json modelCapabilities.ts 1016→1072 (drift de PRs ja mergeadas, ex: #11034), com changelog fragment — tip estava vermelho nesse gate e travava esta PR.
⭐5 — freeAccessPolicy "strict" opt-in (default off): verifica candidatos de auto-combo contra estado de quota ao vivo + segurança econômica por conexão antes do dispatch (fail-closed — estado desconhecido/stale/incompleto é excluído). Zero mudança de comportamento com o default "off". 82 testes focados, eslint/prettier/typecheck limpos, docs em docs/routing/STRICT_ZERO_COST.md.
⭐5 — resolveSilentCloseOutcome tratava bare upstream close para clientes Claude (#7699) e OpenAI chat-completions (#10443), mas clientes Responses-format caíam por ambos os branches e recebiam um close silencioso no meio do stream. Estende o veredito para OPENAI_RESPONSES/OPENAI_RESPONSE: emite response.failed sintético preservando o conteúdo já entregue. 3 novos + 92 testes-irmãos (streamHandler) verdes.
⭐5 — mergeOpenCodeConfig aplicava guard de objeto na raiz mas espalhava safeConfig.provider sem guard, produzindo configs estruturalmente válidas mas semanticamente corrompidas para provider não-objeto (array vira índices numéricos, string vira caracteres). Aplica o mesmo guard já usado na raiz. TDD, 7 casos novos + 19 testes-irmãos verdes, typecheck/lint limpos.
⭐5 — Groq rejeita chat multi-turn quando mensagens de assistant carregam metadata não suportada (model/messageId/sender). stripGroqUnsupportedFields agora remove esses campos, preservando role/content/tool_calls. Fix pequeno e focado, teste novo cobrindo o caso.
⭐5 — Variantes de modelo com sufixo de effort (ex: model-max/model-xhigh) são sintetizadas no catálogo a partir de supportedThinkingEfforts do modelo base, sem linha própria no registry — getResolvedModelCapabilities resolvia NULL para tool/vision/context, o que fazia combos com tools reordenar silenciosamente essas variantes para trás. Fix: quando todas as fontes diretas falham, resolve as capacidades a partir do modelo base após strip do sufixo. TDD, 65 testes focados verdes.
⭐5 — Documenta o multiplicador de escala suportado hoje: N processos independentes com DATA_DIR/volume próprios (não replicas>1 numa PVC SQLite única); Redis (QUOTA_STORE_DRIVER=redis) compartilha só contadores de quota. Doc-only, teste de asserção do Docker Guide + ENVIRONMENT. Fecha #11024.
⭐5 — Documenta runtime RAM real para coding-agent /v1/responses (8-12GB heap medido em gateway K8s de produção, V8 abort em ~12GB); a imagem continua com OMNIROUTE_MEMORY_MB=1024 (dashboard) inalterado. Doc-only. Fecha #10982.
⭐5 — Provider x-search de primeira classe (SuperGrok/xAI x_search) em POST /v1/search e MCP omniroute_x_search. Fallback de credenciais xai-oauth→xao→xai; distinto de web search e do X Developer MCP.
Reconciliado com o release tip (que já incluía #10981 "skip catalog-default SearXNG" deste mesmo lote): merge trouxe 5 conflitos reais de contagem gerada (llm.txt/README.md/AGENTS.md/PROVIDER_REFERENCE.md/SVGs/46 mirrors i18n, todos verificados como bump puro 347→348, sem perda de conteúdo do HEAD) + 1 conflito real de mergeable=CONFLICTING.
Durante a validação, os 3 testes novos de SearXNG expuseram um bug real de interação com #10981: `isUnconfiguredLoopbackSearchProvider()` checava o baseUrl ESTÁTICO do catálogo em vez do baseUrl efetivo (após override de `provider_options.baseUrl` ou `providerSpecificData.baseUrl` da conexão), então QUALQUER request a searxng-search — mesmo com override customizado — era rejeitado como se fosse o default não-configurado. Corrigido em `open-sse/handlers/search.ts` (resolve o baseUrl efetivo via `resolveSearchBaseUrl()` antes do skip-check, tanto para o provider primário quanto o alternate). Um teste do próprio #10988 que assumia o comportamento pré-#10981 (default localhost:8888 sempre atendido) foi atualizado para refletir o comportamento já mesclado e intencional (503 quando não configurado).
Validação completa: typecheck limpo, 70/70 testes unit (search-route/search-registry/x-search-provider/searxng-loopback-default), 24/24 vitest MCP, 14/14 integration (search-providers-catalog), lint limpo nos arquivos tocados, docs-counts-sync OK (2 drifts soft pré-existentes, não relacionados), gates estáticos (file-size/complexity/cognitive/dead-code/changelog) todos OK.
⭐5 — getProviderCredentials() descartava conexões isActive:true com testStatus expired/banned/credits_exhausted e retornava null, virando 404 model_not_found mesmo com o modelo listado em /models. Agora retorna o sentinel allExpired quando toda conexão ativa é terminal, resultando em 401 com hint de reconexão. Testes de auth-terminal-status/sse-auth/chat-helpers verdes. Fecha #10969, follow-up de #7611.
⭐5 — Adiciona GET/HEAD /readyz como re-export de /healthz para probes de readiness estilo Kubernetes pararem de 404. Mesmos corpos/status de lifecycle; não é liveness probe. Testes de paridade + matcher omission. Fecha #10850.
⭐5 — Em SQLITE_CORRUPT/pager malformado durante rotação de call-log, pausa novas rotações em vez de retry de DELETE contra arquivo quebrado; /api/db/health reporta integrity_check_failed. Não faz REINDEX automático (inseguro em single-writer live). 4/4 testes novos + suíte irmã verde. Fecha o gap de #10736.
⭐5 — Default do catálogo searxng-search (localhost:8888) sempre falha em Docker/K8s, queima o próximo provider (Brave free-tier 429). Agora pula o default não-configurado a menos que baseUrl tenha sido sobrescrito; fallback duckduckgo-free continua disponível. 3/3 testes. Fecha #10976.
⭐5 — initCredentialHealthCheck() agora retorna se o sweep está armado; boot loga "disabled" quando OMNIROUTE_DISABLE_CREDENTIAL_HEALTH_CHECK está setado, em vez de sempre "started". 3 testes verdes, comportamento do sweep inalterado. Fecha #11016.
⭐5 — ENVIRONMENT.md dizia que DEFAULT_RATE_LIMIT_PER_DAY unset = 1000/dia (legado); código e testes desde #2289 tratam unset/vazio como sem cap implícito. Doc-only, guardado por teste de asserção da tabela. Fecha #11017.
⭐5 — Remove leitura morta de existingConnections em route.ts, resíduo do mesmo commit 5b5e21a99 que já removeu o guard que a usava (#1566). Zero comportamento alterado, 14/14 testes-irmãos verdes. Sibling de #10974.
⭐5 — Remove ALLOW_MULTI_CONNECTIONS_PER_COMPAT_NODE, flag morta desde 5b5e21a99 (que removeu o guard que a lia, resolvendo #1566). Zero mudança de comportamento; EXPECTED_FEATURE_FLAG_COUNT é o regression guard. Sibling de #10973 (mesma raiz).
⭐5 — Cache de reasoning-replay escrevia em toda resposta com reasoning_content, mesmo quando nenhum read-path jamais consumiria (install sem provider de replay). Guard com requiresReasoningReplay() nos dois write-sites, superset seguro do que os readers checam. Testes cobrindo o predicate isoladamente e o wiring real via handleChatCore.
⭐5 — Rotação de conta não distinguia falha transitória (quota) de terminal (credencial morta) — ambas só esfriavam e eram retentadas para sempre. Agora markCooldown aceita kind transient/terminal; 3 terminais consecutivos evictam a conta (com fallback para não travar se todas evictadas). TDD, 17/17 testes, lint/typecheck/cycles OK.
⭐5 — Health-check failures compartilhavam o mesmo caminho de escrita de terminalStatus que requests reais, banindo conexão por health-check falho (403) por um ano. Novo helper origin-aware centraliza toda escrita terminal; health-check só loga, request real desativa como antes. TDD, 6/6 testes, lint/typecheck/cycles OK.
⭐5 — Fix(#10940): OpenCode config rejeitava modelos sem metadata de catálogo por faltar limit.output (campo obrigatório no schema v1). Agora sempre emite limit com fallback (catálogo → override → 8192). TDD RED→GREEN; teste pré-existente que codificava o bug corrigido. UNSTABLE é o base-red inherited #9985.
⭐5 — Fix(#10954): `combo create` via CLI sempre criava combos vazios (models: [] hardcoded, sem flag). Adiciona --models/--model com parser próprio (CLI .mjs sem alias @/). TDD RED→GREEN, 22/22 testes verdes. UNSTABLE é o base-red inherited #9985.
⭐5 — Fix(#10967,#10966): combo diag exhausted_connection truncava o UUID por hardcode de provider="unknown"; recovery hint de quota caía em "retry" genérico. TDD RED→GREEN, 149 testes-irmãos verdes. UNSTABLE é o base-red inherited #9985.
⭐5 — Fix(#10955): generator de CLI não resolvia $ref em parâmetros do OpenAPI (bug em 20 lugares do spec), PATCH combo sem requestBody. TDD RED→GREEN, gates completos (file-size/complexity/cognitive/changelog/typecheck/lint/docs-all) todos OK. UNSTABLE é o base-red inherited #9985.
⭐5 — 4 achados STILL-REAL de advisories de segurança, cada um com TDD (RED→GREEN) e crédito ao reporter original: ACP RCE hardening (resolveVersionProbe), db-backups Tier-2 allowlist, uppercase authz bypass (matcher case-insensitive), spawn-veto drift (chatgpt-web-codex-doctor). typecheck/lint limpos, suíte authz/acp/cors verde. UNSTABLE é o base-red inherited #9985, já documentado no corpo da PR.
⭐5 — Cursor PKCE login com Bearer quota, auto router e empty-turn errors. Feature completa e testada (11 arquivos de teste, 133 testes focados, todos verdes).
**Validação (worktree combinado `.claude/worktrees/fix-9909`, board sobre `origin/release/v3.8.50`):**
- 3 conflitos reais resolvidos: `config/quality/eslint-suppressions.json` (aditivo), `open-sse/config/providers/registry/cursor/index.ts` (dedup de 208 entradas de catálogo, 0 IDs duplicados verificado), `open-sse/executors/cursor.ts` (imports aditivos).
- `npm run typecheck:core`: limpo.
- `check-changelog-integrity`, `check-file-size`, `check-complexity` (2615/2774), `check-cognitive-complexity` (1175/1223), `check-dead-code` (410/416): todos OK.
- `check-public-creds`: 1 entrada obsoleta pré-existente na allowlist (`copilot-m365-web.ts:330`), já presente no tip da release — não é desta PR.
- `npm run lint`: 0 errors (5 warnings pré-existentes).
- Testes focados (`cursor-agent-cli-version`, `cursor-available-models`, `cursor-catalog-combo-compat`, `cursor-errors-classify`, `cursor-login-pkce`, `cursor-model-effort-suffix-7289`, `cursor-streaming`, `cursor-token-extractor`, `cursor-token-refresh-wiring`, `cursor-usage-fetcher`, `empty-stream-no-content-8649`): 133/133 verdes.
- Corrigido durante a validação: 1 teste novo da própria PR (`cursor-model-effort-suffix-7289.test.ts`, "splits effort off legacy grok- ids") colidia com `CURSOR_MODEL_ALIASES` já mesclado na release (mapeia `grok-4.5-high` → `cursor-grok-4.5-high` antes do fallback legado rodar); ajustado para usar um id não-aliasado (`grok-3-high`) que de fato exercita o fallback — commit `68b58ed`.
Obrigado pela contribuição, @yansigit — feature robusta com boa cobertura de testes.
Reconciliado com a release e revalidado: typecheck:core, check:dead-code (410 real vs 416 na baseline resolvida — a PR mede corretamente sua própria melhoria), lint (adicionei 1 entrada de suppression para GrokBuildToolCard.tsx, arquivo mergeado depois que esta branch nasceu, 2 violações novas de react-hooks/set-state-in-effect não capturadas pela contagem original), complexity, cognitive-complexity, file-size, changelog-integrity e 11/11 testes do tieredRotation todos verdes. Drena 3 dos 8 hard failures do #9985. Obrigado!
Validado no worktree combinado: typecheck:core, changelog-integrity, complexity, cognitive-complexity, file-size, lint e teste focado (vps-compose) todos verdes. Bundle Docker aditivo, seguro-por-padrão (loopback, secrets obrigatórios, imagem pinada), bem documentado. CI vermelho é o base-red já rastreado em #9985.
Validado no worktree combinado: mesmos gates + 36 testes focados verdes. Feature bem documentada e testada (tool calling completo para copilot-m365-web via SignalR, incluindo keepalives e detecção de erro silencioso). CI vermelho é o base-red já rastreado em #9985.
Tirado de Draft e validado no worktree combinado: mesmos gates verdes (mudança de UI/i18n sem cobertura automatizada dedicada, mas de baixo risco — só warnings e ocultação condicional de UI). Fix de UX real (#10794 — 401 confuso ao pular senha no onboarding). CI vermelho é o base-red já rastreado em #9985.
Validado no worktree combinado: mesmos gates + testes focados verdes. Extensão opt-in bem desenhada sobre #10909 (dimensão de uso real via call_logs). CI vermelho é o base-red já rastreado em #9985.
Validado no worktree combinado: mesmos gates + teste focado verde. Root cause medido na release publicada v3.8.49 (nome de artefato NSIS com espaço vs. hífen no manifest). CI vermelho é o base-red já rastreado em #9985.
Validado no worktree combinado: mesmos gates + testes focados verdes. Fix bem medido (context window real vs anunciado divergindo por até 24h para modelos sincronizados fora do ciclo). CI vermelho é o base-red já rastreado em #9985.
Validado no worktree combinado: mesmos gates + teste focado verde. Preserva effort_tiers declarados pelo provider (Kimi k3) em vez de substituir pela lista canônica genérica. CI vermelho é o base-red já rastreado em #9985.
Validado no worktree combinado: mesmos gates + testes focados verdes. Bug real e bem reproduzido (least-used nunca gravava lastUsedAt, sempre a mesma conexão escolhida). CI vermelho é o base-red já rastreado em #9985.
Validado no worktree combinado: typecheck:core, changelog-integrity, complexity, cognitive-complexity, file-size, lint e testes focados todos verdes. Regressão real corrigida (apiType=chat agora é honrado em vez de forçado para /responses). CI vermelho é o base-red já rastreado em #9985.
Validado no worktree combinado: typecheck:core, changelog-integrity, file-size, lint e teste focado passando. Root cause bem documentado (distDir customizado gera dois node_modules externalizados). CI vermelho é o base-red já rastreado em #9985.
Validado no worktree combinado: typecheck:core, changelog-integrity, file-size, lint e 7/7 testes focados passando. Investigação completa com verificação de ancestralidade via merge-base antes de fechar a issue original. CI vermelho é o base-red já rastreado em #9985.
Validado no worktree combinado: typecheck:core, changelog-integrity, file-size, lint e 2/2 testes focados passando. Diagnóstico bem investigado do timeout WS do Meta AI (readyState exposto no erro). CI vermelho é o base-red já rastreado em #9985.
Validado no worktree combinado: typecheck:core, changelog-integrity, file-size, lint todos verdes. Correção real dos 3 alertas CodeQL (HMAC em vez de hash bruto, URL parsing em vez de substring, dismiss documentado). CI vermelho é o base-red já rastreado em #9985.
Validado no worktree combinado: typecheck:core (confirma que TODOS os símbolos exportados foram preservados — TlsClientHangError, TlsClientUnavailableError, looksLikeSse, isCloudflareChallenge continuam re-exportados em cada wrapper), changelog-integrity, complexity, cognitive-complexity, file-size, lint e testes focados via vitest (chatgptTlsClient, grokTlsClient) + node:test (chatgpt-web-handoff-resume, lmarena-provider, claude-web-live-alignment, chatgpt-web, claude-web-slow-first-byte, grok-web-cloudflare-classification, grok-web) todos verdes. Refactor de consolidação bem executado: -3379 linhas líquidas, zero mudança de comportamento, 6 clientes TLS quase idênticos viram uma factory + wrappers finos. CI vermelho é o base-red já rastreado em #9985. Obrigado!
Validado no worktree combinado: typecheck:core, changelog-integrity, complexity, cognitive-complexity, file-size, lint e 52 testes focados (kimi-jwt, kimi-credentials-extract, kimi-token-refresh, kimi-web-401-retry, provider-refresh-token-route, token-health-check-kimi) todos verdes. Implementação sólida e bem testada de ciclo de vida de token para Kimi Web. CI vermelho é o base-red já rastreado em #9985. Obrigado!
Validado no worktree combinado: typecheck:core, changelog-integrity, complexity, cognitive-complexity, file-size, lint e testes focados (egress-ip-lock-10880, egress-lock-allowlist-10880, proxy-logs-egress-lookup-10880) todos verdes. Otimização de resiliência bem fundamentada (cooldown de conexões compartilhando IP de egress após 429 do allowlist). CI vermelho é o base-red já rastreado em #9985. Obrigado!
Validado no worktree combinado: typecheck:core, changelog-integrity, complexity, cognitive-complexity, file-size, lint e teste focado (agnes-provider, 11/11) todos verdes. Atualização de catálogo/dados alinhada à documentação oficial vigente. CI vermelho é o base-red já rastreado em #9985. Obrigado!
Validado no worktree combinado: typecheck:core, changelog-integrity, complexity, cognitive-complexity, file-size, lint todos verdes. Fix de UX real e bem documentado (cards de Account Split mostravam UUID cru em vez de email/nome da conta). CI vermelho é o base-red já rastreado em #9985. Obrigado!
Validado no worktree combinado: typecheck:core, changelog-integrity, complexity, cognitive-complexity, file-size, lint e teste focado (colocate-standalone-esm-scope) todos verdes. Fix real, correção de regressão introduzida por #10836 (server.js CommonJS quebrando com type:module reintroduzido). CI vermelho é o base-red já rastreado em #9985. Obrigado!
Validado no worktree combinado: typecheck:core, changelog-integrity, complexity, cognitive-complexity, file-size, lint e testes focados (freeProviderRankings-filters) todos verdes. Feature aditiva bem documentada (campo reliability nos rankings). CI vermelho é o base-red já rastreado em #9985. Obrigado!
Validado no worktree combinado: typecheck:core, changelog-integrity, complexity, cognitive-complexity, file-size, lint todos verdes. Fix real bem documentado (loopback readiness gate memorizava falha permanentemente + log-spam por caller). CI vermelho é o base-red já rastreado em #9985. Obrigado!
Validado no worktree combinado: typecheck:core, changelog-integrity, complexity, cognitive-complexity, file-size, lint e testes focados (auth-login-route, login-bootstrap-route, feature-flags-settings — corrigi EXPECTED_FEATURE_FLAG_COUNT 51→52 fix-in-place, novo flag adicionado sem atualizar a própria contagem) todos verdes. CI vermelho é o base-red já rastreado em #9985. Obrigado!
Reconciliado com #10935 (já mergeada) — mesclado o guard inline recém-mergeado com a extração para `privateHostname.ts` deste PR, mantendo a intenção original: os 3 workers de relay agora usam a MESMA função compartilhada. Validado: lint limpo, 49/49 testes focados passando (incluindo verificação de que nenhum worker mantém cópia inline). Hardening de segurança real e bem documentado (4 gaps de bypass: `::`, `localhost.`, `::127.0.0.1`, `feb0::1`). CI vermelho é o base-red já rastreado em #9985. Obrigado!
Validado no worktree combinado do lote: typecheck:core, changelog-integrity, complexity, cognitive-complexity, file-size e 163 testes focados (incluindo cloudflare-relay-path-ssrf) todos verdes. Fix de segurança real e bem documentado (SSRF via concatenação pós-validação no Cloudflare relay worker). CI vermelho é o base-red já rastreado em #9985. Obrigado!
Reconciliado com a release (mesmo drift dos PRs irmãos em typecheck-baseline/glm.ts/fetchTimeout.ts/stryker.conf.json). Validado: lint limpo, teste focado passando. Fix real (schema Zod não incluía customSystemPromptEnabled/customSystemPrompt, causando perda silenciosa da configuração). CI vermelho é o base-red já rastreado em #9985. Obrigado!
Reconciliado com a release (mesmo drift dos PRs irmãos) e corrigi 2 problemas de lint reais: import restrito `@/lib/localDb` → `@/lib/db/settings`, e `no-explicit-any` no teste (tipo explícito no callback do map). Validado: lint limpo, 2/2 testes focados passando. Fix real de segurança — endpoints de busca agora respeitam `blockedProviders`. CI vermelho é o base-red já rastreado em #9985. Obrigado!
Reconciliado com a release (mesmo drift de typecheck-baseline/glm.ts/fetchTimeout.ts/stryker.conf.json dos PRs irmãos) e corrigi o `no-explicit-any` no teste novo (cast tipado, mesmo padrão do repo). Validado: lint limpo, teste focado passando. Fix real (busca de credenciais opencode-zen/opencode via PROVIDER_SEARCH_PAIRS). CI vermelho é o base-red já rastreado em #9985. Obrigado!
Reconciliado com a release (mesmo drift de typecheck-baseline/glm.ts/fetchTimeout.ts/stryker.conf.json que os PRs irmãos) e corrigi o `no-explicit-any` no teste novo (o tipo `ModelCompatOverride` já expõe apiFormat/targetFormat/supportsVision — o cast era desnecessário). Validado: lint limpo, 2/2 testes focados passando. Fix real e bem documentado (persistência de overrides de protocolo por modelo). CI vermelho é o base-red já rastreado em #9985. Obrigado!
Reconciliado com a release (drift em typecheck-baseline.json/glm.ts/fetchTimeout.ts/stryker.conf.json — a tip já simplificou essas funções, mantida a versão da tip) e corrigi o `no-explicit-any` no teste novo (cast tipado, mesmo padrão já usado em outros testes do repo). Validado: lint limpo, teste focado passando. Fix real e bem documentado (claude-*/gemini-*/gemma-* sem provider ativo agora retorna 404 model_not_found em vez de 401 enganoso). CI vermelho é o base-red já rastreado em #9985. Obrigado!
Validado + reconciliado: 86/86 testes focados (combo-disable-session-stickiness, base-executor-sanitize-effort, command-code-executor) passando. Incluí o rebaseline do file-size (commandCode.ts 1023→1038, crescimento legítimo deste PR) diretamente no branch — evitando o erro que cometi antes (rebaseline só na worktree local, nunca chegando ao branch real). Correção real de bug com repro ao vivo documentada. CI vermelho é o base-red já rastreado em #9985. Obrigado!
Reconciliado com a release (conflito aditivo em targetTimeoutRunner.ts — combina o warning G3 já mergeado com a resolução de effectiveTimeoutMs deste PR) e revalidado: 41/41 testes focados passando (upstream-timeout-connection-tier, combo-target-timeout-runner, provider-specific-data-schema). CI vermelho é o base-red já rastreado em #9985. Obrigado!
Reconciliado com a release (conflito mecânico em stryker.conf.json — registro de teste que já existia na tip, apenas resolvido mantendo a entrada) e revalidado: 12/12 testes do arquivo log-level.test.ts passando (incluindo os 4 novos deste PR). CI vermelho é o base-red já rastreado em #9985. Obrigado!
Validado no worktree combinado do lote: typecheck:core, lint, gates de qualidade (file-size rebaselineado com justificativa — crescimento legítimo em modelCapabilities.ts/commandCode.ts) e os 97+9 testes focados (vision-bridge, command-code vision, model-select-field-catalog-vision) todos verdes. Duas correções reais (#10808/#10809) bem documentadas. CI vermelho neste PR é o base-red já rastreado em #9985. Obrigado!
Validado no worktree combinado do lote: typecheck:core, lint, gates de qualidade e os novos testes gcf-numeric-domain/gcf-count-mismatch verdes (mais os já existentes do codec GCF). Fix de losslessness bem documentado e cirúrgico. CI vermelho neste PR é o base-red já rastreado em #9985. Obrigado!
Validado no worktree combinado do lote: typecheck:core, lint, gates de qualidade e os 13 testes unitários + 1 de integração (cline-task-id-propagation) todos verdes. Correção legítima de identidade de tarefa fabricada. CI vermelho neste PR é o base-red já rastreado em #9985. Obrigado!
Validado no worktree combinado do lote: typecheck:core, lint, gates de qualidade e o novo teste OpenClawToolCard-secret-ref-apikey.test.tsx (via vitest) verdes. Correção real e bem isolada de um crash client-side (`e.apiKey.slice is not a function`). CI vermelho neste PR é o base-red já rastreado em #9985. Obrigado!
Validado no worktree combinado do lote: typecheck:core, lint, gates de qualidade e testes focados (opencode-go-catalog-alignment + opencode-go-effort-aliases-8353, incluindo os novos casos muse-spark-1.2-contributor-*) todos verdes. CI vermelho neste PR é o base-red já rastreado em #9985. Obrigado!
Validado no worktree combinado do lote (`.claude/worktrees/batch-round2-0821`): typecheck:core, lint, changelog-integrity, file-size, complexity e cognitive-complexity todos verdes; testes focados (71 casos citados na PR + suíte combo-builder-effort-variants/model-discovery-reasoning-levels) passando. CI vermelho neste PR é o base-red já rastreado em #9985. Obrigado!
Bump mecânico e verificado (produção): aws-sdk client-bedrock-runtime, jose, next-intl (patches) + onnxruntime-node 1.24.3→1.27.0 + @atjsh/llmlingua-2 2.0.5→3.0.0 (MAJOR). Validação extra pro major: os 41 testes da suíte llmlingua (tests/unit/compression/llmlingua-*.test.ts + docker-llmlingua-optionals-9166) passaram, incluindo os casos GATED que exercitam a lib real v3.0.0 — API do factory/promptCompressor compatível. `typecheck:core` + `npm run lint` limpos. CI vermelho é o base-red já rastreado em #9985 (correção em andamento via #10778, outra sessão), não defeito deste bump. Obrigado, dependabot!
Bump mecânico e verificado (dev-only): `concurrently` 10.0.4→10.0.5 (patch, correção Windows non-ASCII) e `ctrf` 0.2.1→0.3.0 (minor). `npm install` + `typecheck:core` + `npm run lint` limpos no worktree combinado com #10931. CI vermelho é o base-red já rastreado em #9985 (correção em andamento via #10778, outra sessão), não defeito deste bump. Obrigado, dependabot!
Bump mecânico e verificado: `github/codeql-action/init` v4.37.6 → v4.37.7, 1 linha (SHA pinado) em `codeql.yml` — sequencial após #10929 no mesmo arquivo, sem conflito real. Diff conferido linha a linha. CI vermelho neste PR é o base-red já rastreado em #9985 (correção em andamento via PR #10778, outra sessão) — não é defeito deste bump. Obrigado, dependabot!
Bump mecânico e verificado: `github/codeql-action/analyze` v4.37.6 → v4.37.7, 1 linha (SHA pinado) em `codeql.yml`. Diff conferido linha a linha. CI vermelho neste PR é o base-red já rastreado em #9985 (correção em andamento via PR #10778, outra sessão) — não é defeito deste bump. Obrigado, dependabot!
Bump mecânico e verificado: `github/codeql-action/upload-sarif` v4.37.6 → v4.37.7, 1 linha em `docker-publish.yml`. Diff conferido linha a linha. CI vermelho neste PR é o base-red já rastreado em #9985 (correção em andamento via PR #10778, outra sessão) — não é defeito deste bump. Obrigado, dependabot!
Adds a Grok Build card to the CLI Code dashboard: configures the main model and per-subagent model overrides, surgically upserts only the [model.omniroute] section of config.toml (preserving unrelated user sections), resolves config.toml via GROK_HOME or the CLI config home, and omits API keys from settings responses. Closes#10829.
Validated in an isolated worktree boarded onto origin/release/v3.8.50 (0 conflicts, 14 files):
- 6/48 focused node:test cases in tests/integration/cli-settings-grok-build.test.ts initially failed with 422 instead of 200/409 — traced to this route being the first CLI-tool settings route to actually pass a targetPath into the shared guardCliConfigWrite/ensureCliConfigWriteAllowed container-ephemeral-write guard (every other cli-tools/*-settings route calls it with no path, which always no-ops). This devbox genuinely runs inside Docker with no bind mount for the test's tmpdir fixtures, so the guard correctly refused — a real environment-dependent gap in the PR's own test setup (missing OMNIROUTE_ALLOW_CONTAINER_CONFIG_WRITE=1, the pattern already used by tests/unit/cli-tools-apply-container-422.test.ts for this exact guard). Fixed by setting that flag in the test file's setup, pushed fix-in-place.
- 61/61 tests pass after the fix (grok-build-config, check-tool-config-status, all-statuses-route, cli-settings-grok-build, plus 13/13 UI tests via vitest for GrokBuildToolCard/ToolDetailClient).
- Verified the "omit API keys from settings responses" claim: GET applies omitApiKeys() to both config/settings before responding.
- check-file-size, check-changelog-integrity: OK.
- typecheck:core: clean.
- check-complexity / check-cognitive-complexity: OK, both under baseline.
Co-authored-by: tuandinh0801 <tuandinh0801@users.noreply.github.com>
Dashboard-installed SkillsMP and skills.sh skills now store under the canonical global skill scope and merge into every API-key-scoped lookup, so marketplace installs actually reach API keys instead of staying invisible outside the installing session. Tenant-owned skill overrides stay isolated; existing skillsmp/skillssh rows are recognized without a migration, with canonical rows preferred on identity overlap. Closes#9716.
Validated in an isolated worktree boarded onto origin/release/v3.8.50 (0 conflicts, 9 files):
- 94/94 skills-*.test.ts tests pass, including the tenant-isolation regression coverage in skills-injection.test.ts (global skills reach a different API key without leaking another tenant's skills).
- check-file-size, check-changelog-integrity: OK.
- typecheck:core: clean.
- check-complexity / check-cognitive-complexity: OK, both under baseline.
Co-authored-by: kriptoburak <kriptoburak@users.noreply.github.com>
Requests AgentUrlConfig from Cursor with each selected account token and selects the account's actual server-assigned Agent endpoint (agentUrl/agentnUrl) instead of a fixed global/us host, which fails for teams pinned to a different region. Caches validated endpoints by connection+token. Closes#10802.
Validated in an isolated worktree boarded onto origin/release/v3.8.50 (0 conflicts, 4 files):
- 18/18 focused tests pass (cursor-agent-host, cursor-apikey-provider).
- provider-translate-path-golden.test.ts initially failed — traced to a pre-existing base-red (stale golden snapshot left by an earlier freebuff merge, #10531, unrelated to this PR) and confirmed it reproduces on the pure release tip without this PR's changes. Fixed directly on release/v3.8.50 (mechanical key-ordering regen, values unchanged) rather than folding it into this PR's scope; green after merging that fix in.
- check-file-size, check-changelog-integrity: OK.
- typecheck:core: clean.
- check-complexity / check-cognitive-complexity: OK, both under baseline.
- Author additionally validated live: a real Cursor request selected agentn.us.api5.cursor.sh and returned HTTP 200/PING.
Co-authored-by: tuandinh0801 <tuandinh0801@users.noreply.github.com>
Two fixes: (1) createDisconnectAwareStream now distinguishes graceful max_tokens truncation (partial content already reached the client, upstream closes without a terminal marker → clean stop, no error) from a real empty-content failure (still surfaces the 502). Fixes#7699, keeps #8649 intact. (2) liteEngine's compressToolResults now requires an explicit boolean before overriding step config, instead of letting a malformed value leak through the `??` chain.
Validated in an isolated worktree boarded onto origin/release/v3.8.50 (0 conflicts, 4 files):
- 68/68 tests pass (silent-sse-close-7699, compression/lite, empty-stream-no-content-8649, stream-handler).
- check-file-size, check-changelog-integrity: OK.
- typecheck:core: clean.
- check-complexity / check-cognitive-complexity: OK, both under baseline.
Note: the empty-content Claude error message text changed from "Upstream stream ended without a terminal marker" to "Provider returned empty content" (matches the OpenAI/Responses branch wording) — intentional, documented in the PR.
Co-authored-by: minhlongs <minhlongs@users.noreply.github.com>
Adds A2A v1.0 client compatibility: aliases the renamed v1.0 method names (SendMessage → message/send, SendStreamingMessage → message/stream) and reshapes the synchronous reply into the v1.0 SendMessageResponse shape (task.status.message.parts[].text, task.artifacts) for requests that arrive via a v1.0 method — v0.3 callers keep the exact existing response. Also serves a v1.0 Agent Card at /.well-known/agent-card.json declaring both protocol versions on the same JSON-RPC endpoint.
The PR had no tests and the author noted they couldn't build/typecheck locally — validated in an isolated worktree boarded onto origin/release/v3.8.50 (0 conflicts, 2 files):
- Wrote a TDD regression test (tests/unit/a2a-v1-compat-10839.test.ts, 4 tests) exercising the real POST handler end-to-end: v1.0 SendMessage aliasing + response reshaping, v0.3 message/send keeping its existing shape, SendStreamingMessage no longer 404ing, and the new agent-card.json route declaring both 1.0/0.3 interfaces. Pushed fix-in-place.
- 20/20 existing A2A tests still pass (a2a-auth-timing-safe, a2a-enabled-route, a2a-tasks-auth, t09-a2a-lifecycle, agent-card-route) — no regressions.
- check-file-size, check-changelog-integrity: OK.
- typecheck:core: clean (the author's local-build concern didn't reproduce).
- check-complexity / check-cognitive-complexity: OK, both under baseline.
Co-authored-by: wpec <wpec@users.noreply.github.com>
Re-runs hoistLeadingSystemMessage on the final outbound array at translateRequest's single return, instead of only pre-translation. claudeToOpenAI (and the Responses source path, which never ran the pre-translation hoist at all since `messages` doesn't exist yet there) re-introduces/normalizes a leading system message after the hoist already ran, so a strict provider (e.g. vLLM/Qwen3, xiaomi-mimo) could still receive a non-compliant array and 400 with "System message must be at the beginning."
Validated live against a vLLM/Qwen3 endpoint (documented in the PR) plus in an isolated worktree boarded onto origin/release/v3.8.50 (0 conflicts, 2 files):
- 39/39 focused tests pass (probe-7293-strict-system-hoist including the new Claude-source regression case, memory-system-first-6135, claude-system-role-cache-boundary, memory-cache-safe-injection).
- check-file-size, check-changelog-integrity: OK.
- typecheck:core: clean.
- check-complexity / check-cognitive-complexity: OK, both under baseline.
Co-authored-by: Kizuno18 <Kizuno18@users.noreply.github.com>
OpenCode Zen serves muse-spark-1.2 and muse-spark-1.2-contributor-free only on the OpenAI Responses API endpoint, not /chat/completions. Declares targetFormat: "openai-responses" for both so requests route correctly instead of returning null/empty content. Closes#10867.
Validated in an isolated worktree boarded onto origin/release/v3.8.50 (0 conflicts, 1 file):
- Added a TDD regression test (tests/unit/opencode-muse-spark-responses-10867.test.ts) since the PR had none — confirmed RED against origin/release/v3.8.50 (entries absent) and GREEN on this branch, pushed fix-in-place.
- check-file-size, check-changelog-integrity: OK.
- typecheck:core: clean.
- check-complexity / check-cognitive-complexity: OK, both under baseline.
Co-authored-by: zoser69 <zoser69@users.noreply.github.com>
Adds Zed Hosted Models to OAUTH_TEST_CONFIG so the dashboard connection test no longer reports "Provider test not supported" for that provider.
Validated in an isolated worktree boarded onto origin/release/v3.8.50 (0 conflicts, 1 file):
- tests/unit/oauth-test-config-8408.test.ts (including its "every OAuth provider ID has an OAUTH_TEST_CONFIG entry" check) — 3/3 pass.
- check-file-size, check-changelog-integrity: OK.
- typecheck:core: clean.
- check-complexity / check-cognitive-complexity: OK, both under baseline.
Co-authored-by: Hsia97 <Hsia97@users.noreply.github.com>
Adds a snapshot-generation button to each Auto-Combo catalog card: computeSnapshotWeights() scores candidates (taskFit/stability/tierPriority/costInv) at combo-creation time instead of the previous hardcoded weight:1, and the new POST /api/combos/duplicate endpoint materializes any auto/* template into a persistent, editable static combo with normalized weights. Closes#10231.
Validated in an isolated worktree boarded onto origin/release/v3.8.50 (0 conflicts, 51 files):
- 19/19 focused tests pass (snapshot-weights, combos-duplicate-route, combos-duplicate-resolution-audit) — covers auth gate (401/403), input validation (400/422), success shape, weight normalization, naming/dedup, and error-response sanitization (no stack traces).
- check-file-size, check-changelog-integrity: OK.
- typecheck:core: clean.
- check-complexity / check-cognitive-complexity: OK, both under baseline.
Co-authored-by: swingtempo <swingtempo@users.noreply.github.com>
Adds native support for Freebuff (Codebuff CLI free-tier gateway): executor with upstream session acquisition, agent-run lifecycle (START/FINISH), canonical system-prompt injection, and model→agent mapping for 9 free models; registry entry, dashboard branding/icons, and API-key validation. Closes#6793.
Validated in an isolated worktree boarded onto origin/release/v3.8.50 (0 conflicts after resolving generated provider-count drift, 15 files):
- 9/9 focused tests pass (freebuff-provider, providers-constants-split).
- Dropped one out-of-scope, unrelated hunk in scripts/build/build-next-isolated.mjs (build-memory heap tuning) that had nothing to do with the Freebuff provider — kept the branch scoped to its stated purpose.
- check-changelog-integrity: OK.
- file-size: gateways.ts and AddApiKeyModal.tsx crossed the frozen cap by +15/+5 lines (irreducible catalog-entry + credential-hint additions) — rebaselined with justification, pushed fix-in-place to the PR branch.
- typecheck:core: clean.
- check-complexity / check-cognitive-complexity: OK, both under baseline.
Co-authored-by: adrianaryaputra <adrianaryaputra@users.noreply.github.com>
Aligns provider-test CLI paths with the server's connection-owned management API: `omniroute test` now resolves a connection and calls `POST /api/providers/{id}/test` instead of the missing `/api/v1/providers/test` route; `--all-providers` carries exact connection ids into both non-interactive and TUI runs. Fixes#10570.
Validated in an isolated worktree boarded onto origin/release/v3.8.50 (0 conflicts, 4 files):
- 42/42 focused tests pass (cli-provider-test-routes-10570, cli-providers-command, cli-providers-rotate, cli-route-unavailable-fallback-10081, cli-expanded-commands).
- One pre-existing test in cli-expanded-commands.test.ts (not touched by the PR) mocked the old route and the old `success` response field, exposed only after merging with the current release tip — fixed the mock to match the new per-connection route and the `valid` field the real route actually returns, pushed fix-in-place to the PR branch (owner-authorized rule: fix-in-place over reimplementation, credit preserved).
- check-file-size, check-changelog-integrity: OK.
- typecheck:core: clean.
- check-complexity / check-cognitive-complexity: OK, both under baseline.
Co-authored-by: hydraxman <hydraxman@users.noreply.github.com>
Mirrors the existing prompt_tokens_details cache-field mapping from the message_delta finish path into the message_stop fallback, so OpenAI-compatible clients see cache_read/cache_creation counters when the finish signal arrives without usage on the same event. Fixes#10535.
Validated in an isolated worktree boarded onto origin/release/v3.8.50 (0 conflicts, 2 files):
- 16/16 focused tests pass (translator-resp-claude-to-openai.test.ts), including the new regression test for this exact fallback path.
- check-file-size, check-changelog-integrity: OK.
- typecheck:core: clean.
- check-complexity / check-cognitive-complexity: OK, both under baseline.
Co-authored-by: NahuSaruf <NahuSaruf@users.noreply.github.com>
Canonical provider id renamed freepik → magnific (Magnific Mystic official API), with a permanent redirect + runtime alias so old freepik/<model> traffic and /dashboard/providers/freepik URLs keep working. Existing provider=freepik connection rows are rewritten to magnific by migration 160.
Closes#10604.
Validated in an isolated worktree boarded onto origin/release/v3.8.50 (0 conflicts, 138 files):
- Focused suite: 54/54 tests pass (magnific-image-handler, provider-validation-image-only, provider-alias-uniqueness, redirects-cli-renames).
- check-file-size, check-changelog-integrity: OK.
- typecheck:core: clean.
- check-complexity / check-cognitive-complexity: OK, both under baseline.
Co-authored-by: RaviTharuma <RaviTharuma@users.noreply.github.com>
Rescoped from #10606 — see PR body for the full rationale (combo-routing half made moot by #10162's advisory-only architecture, chatCore.ts hard-reject bypass retains real value).
Validated in an isolated worktree boarded onto origin/release/v3.8.50:
- 65/65 focused unit tests pass (chatcore-model-output-cap-wiring + feature-flags-settings).
- check-file-size, check-changelog-integrity: OK.
- typecheck:core: clean.
- check-complexity / check-cognitive-complexity: OK, both under baseline.
Co-authored-by: JxnLexn <10897478+JxnLexn@users.noreply.github.com>
Obrigado — bug real: MUSIC_PROVIDERS.minimax declara format "minimax-music" e seus modelos são publicados pelo catálogo, mas handleMusicGeneration nunca teve um branch para esse format — todo request minimax/* caía no guard final com "Unsupported music format", modelos anunciados mas inalcançáveis. Handler completo cobrindo os dois output formats (url/hex), envelope base_resp, endpoint regional, e guarda local de credencial ausente.
Validação (worktree própria a partir de origin/release/v3.8.50, merge limpo, 0 conflitos):
- typecheck:core limpo, complexity/cognitive-complexity dentro do baseline
- tests/unit/minimax-music-generation.test.ts — 9/9 passando
Live SSE frames for a phase:"commentary" message were already dropped
per #6199, but the terminal response.completed.response.output array was
forwarded verbatim whenever the upstream echoed the same item back
non-empty, since backfillResponsesCompletedOutput only fills an empty
array. Reuse the existing isResponsesCommentaryMessageItem predicate to
filter the terminal snapshot's output array (and, defensively, the
backfill buffer it can be seeded from) so both representations agree.
Regression test added to tests/unit/responses-commentary-passthrough-6199.test.ts
reproducing the exact upstream shape from the issue.
v1SearchSchema.provider was a hard-coded z.enum that rejected any id outside
its list before the route's own resolveSearchProvider() check ever ran,
so unknown/short-alias provider ids (grok, brave, serper, ...) always
surfaced a generic "Invalid request" instead of the informative
"Unknown search provider: <id>" message. Relax the schema to a free-form
string and let resolveSearchProvider() own runtime validation (as it
already did for ids that passed the enum). Also extend
SEARCH_PROVIDER_ALIASES with short-form aliases mirroring the existing
jina/jina-ai pattern (brave, serper, perplexity, exa, tavily, google-pse,
linkup, ollama, searchapi, youcom, searxng, zai, duckduckgo), and surface
the first Zod validation issue's field name instead of the generic
message for other still-invalid fields (e.g. search_type).
glm-5.1, glm-5.2, deepseek-v4-pro and deepseek-v4-flash declared supportsReasoning:true but no supportedThinkingEfforts, so the catalog's appendSyncedEffortVariants() pass (which only synthesizes -low/-high/-max ids from an already-populated capabilities.effort_tiers) never exposed a selectable effort tier for them, unlike gpt-oss:20b/120b. Add the documented low/medium/high/max vocabulary (see supportsMaxEffortForProvider's isOllamaCloud comment in reasoningEffort.ts).
Obrigado — PR muito bem documentado e verificado. Adiciona o gateway TabiToken (Anthropic-first, /v1/messages, x-api-key) e estende hcnsec de 1 para 4 protocolos (Chat, Responses, Anthropic Messages, Gemini). AlternateFormat ganha o hook urlBuilder opcional (necessário para o path model-scoped do Gemini), compartilhado com o provider gemini nativo em vez de duplicado.
Reconciliado nesta sessão contra o release tip atualizado (base drift real: 343→345 canônicos entre quando o PR foi criado e o merge, mais os PRs #10673/#10658 mergeados nesse meio-tempo). Conflitos em contagens de providers (docs, file-size baseline, teste de partição) resolvidos additivamente.
Validação (reconciliação a partir de origin/release/v3.8.50):
- typecheck:core limpo, complexity/cognitive-complexity dentro do baseline
- npm run check:provider-consistency — OK (266 REGISTRY entries, 346 providers canônicos, 0 exceções)
- 40/40 testes passando (newapi-gateway-providers, hcnsec-provider, providers-constants-split, alternate-formats)
Obrigado — bug real: a tradução direta claudeToGeminiRequest emitia mensagens consecutivas do mesmo role em contents[], o que a API do Gemini rejeita com HTTP 400 (turnos alternados user/model são obrigatórios). Traz claudeToGeminiRequest à paridade com openaiToGeminiRequest reutilizando mergeConsecutiveSameRoleContents.
Validação (worktree combinado a partir de origin/release/v3.8.50, merge limpo, 0 conflitos):
- typecheck:core limpo, complexity/cognitive-complexity dentro do baseline
- tests/unit/claude-to-gemini-consecutive-roles.test.ts — 7/7 passando
- tests/unit/claude-to-gemini-budget-tokens-zero-6813.test.ts — 2/2 passando (sem regressão)
Obrigado — bug real: GET /v1/files aceitava limit negativo sem validação (`-5 || 20` avalia truthy em -5, então Math.min(-5, 10000) = -5 passava direto). Agora valida integer/positivo/tamanho e retorna 400 estruturado para valores inválidos, preservando o default 20 e o máximo 10.000.
Validação (worktree combinado a partir de origin/release/v3.8.50, 0 conflitos):
- typecheck:core limpo, complexity/cognitive-complexity dentro do baseline
- tests/integration/files-api-limit-validation.test.ts — 5/5 passando
- tests/integration/files-api.test.ts — 12/12 passando (sem regressão)
- tests/unit/batch_api.test.ts teve 1 falha, confirmada DRIFT pré-existente idêntica no tip puro do release (não relacionada, timing de cancelamento de batch)
Merged — extraction of the one still-uncovered fix from #8634 (the other two items — mode "search"→CONCISE downgrade, pplx-opus generation — were already applied on this release tip). typecheck/file-size/changelog/complexity/cognitive-complexity gates all clean, 32/32 tests passing.
Merged — reimplementation extracting the non-conflicting Recent Requests panel + excludeTests allowlist fix from #8450 (see PR body for the full scoping rationale, including why the topology UX rework was deliberately excluded — it contradicts the already-shipped #8428). typecheck/file-size/changelog/complexity/cognitive-complexity/i18n-coverage gates all clean, 2/2 unit + 1/1 vitest passing.
Merged — clean extraction from #10358's genuinely new content (see PR body for the rationale: an unrelated .planning/codebase/ scaffolding dump was dropped). typecheck/file-size/changelog/provider-consistency gates clean, 18/18 tests passing.
Obrigado — feature real e bem verificada: POST /v1/images/edits rejeitava o provider built-in openrouter mesmo ele suportando edição por imagem de referência via sua Image API unificada. Traduz a imagem de entrada para o formato input_references documentado do OpenRouter e despacha para /api/v1/images, removendo o prefixo do provider do model id antes de encaminhar.
Nota: o contribuidor não conseguiu rodar o teste localmente (better-sqlite3 ausente no ambiente dele) — rodei aqui.
Validação (worktree própria a partir de origin/release/v3.8.50, merge limpo, 0 conflitos):
- typecheck:core limpo, complexity/cognitive-complexity dentro do baseline
- tests/unit/10197-openrouter-image-edits-route.test.ts — 3/3 passando (forward bem-sucedido, credenciais ausentes 401, rate-limit)
Merged — clean single-commit extraction from #9115's genuinely new content (see PR body for the full extraction rationale: 66-commit branch, only 1 commit matched the stated scope). typecheck/file-size/changelog gates clean, 19/19 unit + 14/14 integration tests passing.
Obrigado — feature substancial e bem estruturada: separa qualidade operacional (comportamento de wire: 4xx/5xx, 429, respostas malformadas, stream interrompido) de qualidade semântica (só setada por avaliadores externos, nunca inferida do sucesso HTTP), com confidence/sample-awareness para não deixar poucos sucessos de sorte dominarem o ranking. Instrumentação de streaming (TTFT/ITL) threaded até RoutingEvent, endpoint de explicabilidade, e teste E2E determinístico cobrindo degradação→recuperação→blip.
Validação (worktree própria a partir de origin/release/v3.8.50, merge limpo, 0 conflitos):
- typecheck:core limpo, complexity/cognitive-complexity dentro do baseline
- 59/59 testes passando (mlx-provider, routing-adaptive-e2e, routing-events(-concurrency), routing-otel, routing-quality, routing-scoring-quality, stream-timing, auto-combo-scoring-clamp)
Obrigado — follow-up bem feito do #10424: um 422 GCP_PROJECT_REQUIRED (BYOP — a conta Google precisa trazer seu próprio GCP Project) é específico da CONTA, não do provider inteiro, então o fast-fail anterior falhava mesmo quando uma conta antigravity irmã saudável poderia atender o request. Agora rotaciona automaticamente para a conta irmã (excluindo a conta BYOP por 24h) e só surfaça o erro acionável quando não há irmã disponível. Estado de rotação rastreado separado de maxAttempts, então falhas normais do antigravity nunca ganham uma segunda chance (sem dispatch duplo).
Validação (worktree própria a partir de origin/release/v3.8.50, merge limpo — auto-merge em chatCore.ts, 0 conflitos reais):
- typecheck:core limpo, complexity/cognitive-complexity dentro do baseline
- tests/unit/antigravity-byop-account-rotation.test.ts + error-classifier.test.ts — 36/36 passando
Obrigado — distinção precisa entre saúde de credencial e disponibilidade upstream: timeout do NVIDIA e HTTP 400 genérico do Antigravity/AGY passavam a poluir a saúde da credencial como se fossem falha de autenticação, quando na verdade são resultados inconclusivos. Preserva o path explícito de geo-block do Google (esse continua indo pelo tratamento de geo/egress existente). Reconciliado nesta sessão contra o release tip atualizado (pós #10878/#10873) — conflito real em scheduler.ts resolvido de forma additiva (pacing por intervalo do release + recheck mais lento para probes inconclusivos empilhados).
Validação (reconciliação a partir de origin/release/v3.8.50, gates estáticos rebaselineados para a soma legítima de #10878+#10799 no test/route.ts):
- typecheck:core limpo, complexity/cognitive-complexity dentro do baseline
- tests/unit/provider-health-inconclusive-probes.test.ts + nvidia-nim-validator.test.ts + antigravity-geoblock-resilience.test.ts — 22/22 passando
Obrigado — corrige um caso real onde um 404/405 no chat-probe de validação genérica OpenAI-like era tratado como credencial inválida, quando na verdade significa apenas que o provider não expõe essa superfície de validação. Agora o validador retorna `unsupported: true`, a rota de teste responde `skipped: true`, a saúde persistida não é reescrita, o cache de CredentialHealth não é poluído, e o scheduler ainda respeita o healthCheckInterval configurado — sem introduzir exceção específica de provider nem tocar comportamento do MiMoCode.
Validação (worktree combinado a partir de origin/release/v3.8.50, 0 conflitos):
- typecheck:core limpo, complexity/cognitive-complexity dentro do baseline
- tests/unit/provider-validation-unsupported-neutral.test.ts — passando (TDD RED→GREEN completo: classificação do validador, não-mutação de saúde persistida, pacing do scheduler, regressão de lease-skip, comportamento existente de 403/429 preservado)
Obrigado — follow-up limpo do #10186: MiMoCode foi removido do OmniRoute, mas instalações que o configuraram antes da remoção retêm estado provider-scoped órfão (provider_connections, registered_keys, provider_key_limits, discovery_results, customModels). Migração de retirement segue o padrão explícito já usado para outros providers aposentados, preservando corretamente usage_history/call_logs históricos.
Validação (worktree combinado a partir de origin/release/v3.8.50, 0 conflitos):
- typecheck:core limpo, complexity/cognitive-complexity dentro do baseline
- tests/unit/migration-159-remove-mimocode-provider.test.ts — passando (mimocode + alias mcode removidos, estado de outros providers preservado, idempotência, histórico preservado)
Obrigado — expõe onde o operador realmente olha (sweep periódico de saúde de proxy + resposta da API de egress) o sinal de compartilhamento anônimo de IP de egress entre contas de um mesmo rotation group, que já existia (analyzeEgressSharing) mas só era acessível via curl autenticado. Fecha #10677. Respeita a decisão de redação do #10348/#10539: apenas contagens por padrão, nenhum IP/identidade de conta a menos que PROXY_LOG_INCLUDE_IPS=true.
Validação (worktree combinado a partir de origin/release/v3.8.50, 0 conflitos):
- typecheck:core limpo, complexity/cognitive-complexity dentro do baseline
- tests/unit/proxy-egress-route-summary.test.ts + proxy-egress-summary.test.ts + proxy-health-egress-line.test.ts — 23/23 passando (agregado, formatter, linha do sweep com output real capturado, rota completa com auth de management)
Obrigado — TDD exemplar num gap real de contrato: as duas operações que o dashboard realmente chama em /api/combos/{id} (GET e PUT) estavam ausentes do openapi.yaml, enquanto a única operação documentada (patch, antes deste #10869) não tinha handler. Adiciona um floor de cobertura por OPERAÇÃO (não só por PATH) que o gate existente não capturava, medido em 343/985 (34.8%).
Validação (worktree combinado a partir de origin/release/v3.8.50, 0 conflitos):
- typecheck:core limpo, complexity/cognitive-complexity dentro do baseline
- tests/unit/openapi-coverage.test.ts — passando com o novo floor de operações
- openapi-routes/openapi-coverage/openapi-security-tiers gates — PASS
Obrigado — resolve o cenário real do #6194: uma linha de .env que o shell já tinha exportado antes (ex.: HOSTNAME=0.0.0.0) era silenciosamente ignorada pelo loader first-wins, sem nenhum aviso — o servidor bindava no hostname da máquina, localhost parava de responder, e ModelSync/health checks falhavam com ECONNREFUSED sem pista nenhuma. Agora cada chave mascarada emite um warning em stderr (nome da chave + as duas origens, nunca o valor); um .env ilegível também vira warning em vez de falha silenciosa no boot.
Validação (worktree combinado a partir de origin/release/v3.8.50, 0 conflitos):
- typecheck:core limpo, complexity/cognitive-complexity dentro do baseline
- tests/unit/cli-env-collision.test.ts — 4/4 passando (3 falham no base)
- Suítes CLI env vizinhas (cli-data-dir-env-loading, cli-env-inline-comment-10100, cli-data-dir-env, cli-entrypoint, cli-electron-to-cli-migration-server-env-7302, cli-storage-key-bootstrap) — intactas e verdes
Obrigado — bug real de contrato: o openapi.yaml já documentava patch em /api/combos/{id}, mas a rota nunca exportou PATCH, então um cliente gerado a partir do spec publicado recebia 405 do App Router antes de qualquer handler rodar. Fix mínimo (delegação de 4 linhas para PUT, mesmo padrão já usado em /api/providers/[id] e 25 outras rotas /api/**).
Validação (worktree combinado a partir de origin/release/v3.8.50, 0 conflitos):
- typecheck:core limpo, complexity/cognitive-complexity dentro do baseline
- tests/unit/combo-patch-verb.test.ts — 2/2 passando (falham antes com "comboRoute.PATCH is not a function")
- Suíte combo completa — mesmas 5 falhas herdadas de #9985, confirmadas DRIFT
Obrigado — bug real e bem raiz-causado, dois bugs da mesma família: (1) PUT /api/combos/<id> com models:[] zerava os targets sem aviso, quebrando um combo funcionando (o invariante "combo tem ≥1 modelo" já era reforçado por ~9 consumidores downstream, menos o write path); (2) as tools do Copilot escreviam em targets em vez de models, então todo combo criado via Copilot reportava sucesso mas roteava para lugar nenhum.
Validação (worktree combinado a partir de origin/release/v3.8.50, 0 conflitos):
- typecheck:core limpo, complexity/cognitive-complexity dentro do baseline
- tests/unit/combo-empty-models.test.ts — 4/4 passando
- Suíte combo completa (tests/unit/combo*.test.ts) — 1213 testes, 1208 passando, 5 falhas idênticas em ambos os lados (confirmadas DRIFT pré-existente do #9985 via probe contra o tip puro do release)
Merged — clean single-commit cherry-pick extracted from #10879's genuinely new content (see PR body for the full extraction rationale). typecheck/file-size/changelog gates clean, 11/11 tests passing.
Obrigado — bug de produção real e muito bem raiz-causado: resolveConversationId travava a request path por 10-130s em históricos longos de agente (medido em produção: p50 12.6s / max 130.2s em requests com ≥200 mensagens), por re-hashear o texto completo de cada turno a cada passo do walk de reconexão (O(starts × anchors × walkLength) HMACs síncronos).
Fix cirúrgico: memoiza o hash de cada turno por request + budget de passos compartilhado entre candidatos (degrada como no-match, nunca como attach não verificado ou latência ilimitada). Resultado medido: 17.2s → 0.3s no repro.
Validação (worktree combinado a partir de origin/release/v3.8.50, 0 conflitos):
- typecheck:core limpo, complexity/cognitive-complexity dentro do baseline
- tests/unit/conversationTracker-reconnect-7847.test.ts (novo) — cobre o cap de budget, degradação com budget zero, e guarda de regressão de wall-clock (falha em 17.2s pré-fix)
- 17 testes de semântica pré-existentes + conversationTurnContent (5) — passando sem alteração
Obrigado — ganho de performance real e bem medido: substitui loops char-a-char por RegExp nativa do V8 em cleanupArtifacts/normalizeMessageWhitespace, 53-296x mais rápido nos payloads testados (830KB: 109ms→1.6ms; 3.36MB: 296ms→5.4ms), com paridade byte-a-byte confirmada.
Durante a validação do lote combinado encontramos uma regressão real: o rewrite removeu isCodeDominantText (a guarda do #9144) e seu ponto de decisão em cavemanCompress, reintroduzindo a recapitalização destrutiva de código não-cercado (function→Function). Identificado por tests/unit/compression/caveman-file-reference-9144.test.ts, que passa no tip puro do release e falhava após este PR. Restaurei a guarda em cima da nova implementação regex (commit e5edb1a6, autoria preservada + Co-authored-by), mantendo o ganho de performance sem reintroduzir o bug.
Validação final (worktree combinado a partir de origin/release/v3.8.50):
- typecheck:core limpo, complexity/cognitive-complexity dentro do baseline
- tests/unit/compression/lite.test.ts + caveman-*.test.ts — 99/99 passando (incluindo o #9144 restaurado)
Obrigado — silencia o DeprecationWarning DEP0190 do Node 22+ ao invocar wrappers .cmd/.bat no Windows via shell:true, usando windowsVerbatimArguments/windowsHide em vez da stringificação legada não segura.
Validação (worktree combinado a partir de origin/release/v3.8.50, 0 conflitos):
- typecheck:core limpo, complexity/cognitive-complexity dentro do baseline
- Suítes de cobertura existentes de tool-detector (cli-helper-tool-detector-paths-6162, cli-tool-detector-imports, tool-detector-win32-7279, tool-detector, tool-detector-opencode-jsonc-10227) — 23/23 passando
Obrigado — corrige um warning real de runtime em produção sob Node.js 24: o package.json do standalone gerado pelo Next.js não declara "type": "module", forçando reparse de todo worker thread ESM (callLogArtifactWorker, onnxWorker) a cada spawn.
Validação (worktree combinado a partir de origin/release/v3.8.50, 0 conflitos):
- typecheck:core limpo, complexity/cognitive-complexity dentro do baseline
- Confirmado que colocate-standalone.mjs escreve "type": "module" corretamente; testado sob Node 24.19.0, workers sobem sem warning de reparse
Obrigado — fix de segurança real (reportado via GHSA-qcfj-c39q-88jh): isCloudMetadataHost() decidia por spelling dotted-decimal, então um literal IPv4-mapped IPv6 (ex.: [::ffff:169.254.169.254]) alcançava o guard já canonicalizado por new URL() e não era reconhecido como endpoint de metadata de cloud — bypass no modo que permite endpoints privados/LAN (o default local-first). Também fecha o gap equivalente de 0.0.0.0/::.
Validação (worktree combinado a partir de origin/release/v3.8.50, 0 conflitos):
- typecheck:core limpo, complexity/cognitive-complexity dentro do baseline
- tests/unit/outbound-guard-mapped-ipv4.test.ts — 12/12 passando (IMDS, Alibaba, ECS task role, ambas as grafias, hosts públicos, guard `::`)
- Suítes SSRF relacionadas (webhook/firecrawl/kiro/provider-validation) — verdes
Obrigado — corrige um erro sério de tradução em 8 locales, onde o status "Disabled" era traduzido pelo substantivo "pessoa com deficiência" (24 strings), estendendo o escopo original do #10812 (só japonês) para todos os locales afetados. Cada substituição usa o termo que o próprio catálogo já emprega para a mesma fonte em inglês — nenhuma terminologia nova introduzida.
Validação (worktree combinado a partir de origin/release/v3.8.50, 0 conflitos):
- typecheck:core limpo, complexity/cognitive-complexity dentro do baseline
- tests/unit/i18n-disabled-not-person-with-disability.test.ts — 1/1 passando (varre todos os locales)
- Glossary gates (zh-CN, zh-TW, ko) — PASS; i18n:check-ui-coverage e check-value-drift — PASS
- Duas camadas de proteção contra regressão: glossário (zh-CN/zh-TW) + teste catalog-wide cobrindo 21 termos
Obrigado — /v1/models continuava anunciando 38 IDs auto/* mesmo com autoRoutingEnabled: false, todos garantidos a falhar em tempo de request (HTTP 400). Une a condição de ocultação ao hideAutoCombos já existente sem adicionar uma dimensão nova à cache-key (evita quebrar #10313).
Validação (worktree combinado a partir de origin/release/v3.8.50, 0 conflitos):
- typecheck:core limpo, complexity/cognitive-complexity dentro do baseline
- tests/unit/catalog-auto-routing-disabled-10831.test.ts — 2/2 passando
- Suítes catalog relacionadas (hide-auto-no-think, cache-key-hashing, eventloop-yield) — 9/9 passando
Obrigado — um PDF de ~1MB enviado como file/document base64 (OpenAI ou Claude) era medido caractere-a-caractere, estimando 350.022 tokens (o mesmo documento pelo path Gemini inlineData já estimava 1.209). Corrige a inconsistência reconhecendo os shapes que faltavam, sem introduzir constante nova.
Validação (worktree combinado a partir de origin/release/v3.8.50, 0 conflitos):
- typecheck:core limpo, complexity/cognitive-complexity dentro do baseline
- tests/unit/10840-file-token-context.test.ts — 5/5 passando
- Suítes de contexto relacionadas — 59/59 (5 arquivos) passando
Obrigado — o hop de routing (route_request) herdava o budget de 10s de management em vez do budget de 60s de upstream que web_search/web_fetch já usavam, então uma rota de 35-40s abortava só pelo lado do MCP.
Validação (worktree combinado a partir de origin/release/v3.8.50, 0 conflitos):
- typecheck:core limpo, complexity/cognitive-complexity dentro do baseline
- tests/unit/mcp-upstream-fetch-timeout-9717.test.ts — 6/6 passando
- Suíte MCP completa — 149/153 (branch) vs 143/147 (release), as 4 falhas são idênticas nos dois lados e não relacionadas (closure de package-files, resolução de bundle dist/)
Obrigado — root cause preciso: a rota sync-models só reconhecia a degradação para local_catalog, não para o fallback de cache com warning, então uma chave expirada (401) virava silenciosamente "Nenhum modelo novo foi adicionado" em vez de um erro visível.
Validação (worktree combinado a partir de origin/release/v3.8.50, 0 conflitos):
- typecheck:core limpo, complexity/cognitive-complexity dentro do baseline
- tests/unit/sync-models-degraded-cached-catalog-9683.test.ts — 6/6 passando (payloads reais do models/route.ts)
- Suítes model-sync/provider-models/sync-models/siliconflow — 181/181 passando, incluindo as 3 asserções pré-existentes #5460/#5465
Obrigado — bug real e bem raiz-causado: api64.ipify.org é IPv6-first e derruba tunnels IPv4-only, o que estava reportando proxies vivos como mortos.
Validação (worktree combinado a partir de origin/release/v3.8.50, 0 conflitos):
- typecheck:core limpo, complexity 2563/2774, cognitive-complexity 1155/1223 (baseline)
- tests/unit/proxy-echo-ipv4-fallback-9694.test.ts — 8/8 passando (cobre ordem, split de budget, override, proxy morto de verdade)
- Suítes proxy-relacionadas: 805/817 na branch vs 797/809 no release, as 11 falhas são idênticas em ambos os lados e não relacionadas (TLS transport, tproxy CA, SSRF fallback)
Obrigado por restaurar e endurecer a autenticação por machine-token no CLI empacotado.
Validação (worktree combinado a partir de origin/release/v3.8.50, merge limpo, 0 conflitos — 34 arquivos, +1078/-247):
- `npm run typecheck:core` — limpo
- `node scripts/check/check-complexity.mjs` — OK (2558 violações vs baseline 2774)
- `node scripts/check/check-cognitive-complexity.mjs` — OK (1152 violações vs baseline 1223)
- `node scripts/check/check-file-size.mjs` — OK
- `node scripts/check/check-changelog-integrity.mjs` — OK
- Testes focados (8 arquivos: cli-doctor-command, cli-machine-token, lib/machineToken, lib/managementCliToken, agentSkills-generator, api/settings-audit, check-pack-boot, next-config) — 95/95 passando
Os dois achados de segurança do maintainer-feedback original (checagem de loopback tipo SSRF, escopo de cookie/CSRF) já estavam corrigidos e cobertos por teste no commit `2b785f0068a862fbd867221294325ad921787782` desta branch.
Merged — locally validated (11/11 focused tests across both new test files, typecheck:core clean after a 1-char fix pushed to this branch: ComboLogger.error is optional in combo/types.ts so the defensive race-catch needed log.error?.(...) — TS2722 otherwise). Solid production diagnosis (47 unhandledRejections traced to the orphaned race loser). Thanks!
Merged — locally validated together with related HouMinXi PRs (22/22 focused tests, gates green). Note: the PR description text looks pasted from a different change — the actual diff (corrupted request_id strip, #10223) is what was reviewed and merged. Thanks!
Merged — locally validated (28/28 focused pricing tests, gates green). Appreciate the conservative off-peak-only scope and the live verification against the pricing page. Thanks!
Merged — locally validated (28/28 focused pricing-sync tests, gates green). Excellent systematic audit of the whole alias map, not just the one you hit. Thanks!
Merged — locally validated together with related stanleytejakusuma PRs (typecheck:core clean, complexity/cognitive/file-size/changelog gates green, focused tests passing). Great incident writeup and clean fix. Thanks!
Merged — locally validated together with related cryptiklemur PRs (typecheck:core clean, gates green). Good catch on the phantom usage_logs table. Thanks!
Merged — locally validated together with related cryptiklemur PRs (typecheck:core clean, complexity/cognitive/file-size/changelog gates green, focused tests passing). Real bug, clean fix, great regression test. Thanks!
Merged — locally validated (fusion-vision-panel-3378 test green, file-size/changelog gates green) after resolving base-drift against #10842/#10838 (both landed just before).
scripts/ad-hoc mesh helpers read operator-supplied BOT_TOKEN/BOT_URL.
They are not OmniRoute runtime config and should not fail Docs Gates
on every PR.
Unblocks check:env-doc-sync on release/v3.8.50.
Merged — carried forward the PR's own real value (the first 2 commits: ignore ad-hoc BOT_TOKEN/BOT_URL in env-doc-sync, plus the lock-in test). The branch had accumulated 7 more commits chasing the moving release tip across several rebases (each one re-fixing base-reds that had already moved again by the next rebase) — dropped those since they no longer apply to the current tip, and cherry-picked just the 2 with lasting value, preserving your authorship. 14/14 focused tests pass, changelog gate green. Thanks!
Merged — locally validated (changelog gate green) after resolving base-drift against #10817's SQLite HA section (both landed today, same insertion point in DOCKER_GUIDE.md — combined, both sections kept). Thanks!
Merged — validated together with a batch of related RaviTharuma PRs in one combined worktree (typecheck:core clean, complexity/file-size/changelog gates green, focused tests passing). Thanks for the contribution!
Merged — validated together with a batch of related RaviTharuma PRs in one combined worktree (typecheck:core clean, complexity/file-size/changelog gates green, focused tests passing). Thanks for the contribution!
Merged — validated together with a batch of related RaviTharuma PRs in one combined worktree (typecheck:core clean, complexity/file-size/changelog gates green, focused tests passing). Thanks for the contribution!
Merged — validated together with a batch of related RaviTharuma PRs in one combined worktree (typecheck:core clean, complexity/file-size/changelog gates green, focused tests passing). Thanks for the contribution!
Merged — validated together with a batch of related RaviTharuma PRs in one combined worktree (typecheck:core clean, complexity/file-size/changelog gates green, focused tests passing). Thanks for the contribution!
Merged — validated together with a batch of related RaviTharuma PRs in one combined worktree (typecheck:core clean, complexity/file-size/changelog gates green, focused tests passing). Thanks for the contribution!
Merged — validated together with a batch of related RaviTharuma PRs in one combined worktree (typecheck:core clean, complexity/file-size/changelog gates green, focused tests passing). Thanks for the contribution!
Merged — validated together with a batch of related RaviTharuma PRs in one combined worktree (typecheck:core clean, complexity/file-size/changelog gates green, focused tests passing). Thanks for the contribution!
Merged — validated together with a batch of related RaviTharuma PRs in one combined worktree (typecheck:core clean, complexity/file-size/changelog gates green, focused tests passing). Thanks for the contribution!
Merged — validated together with a batch of related RaviTharuma PRs in one combined worktree (typecheck:core clean, complexity/file-size/changelog gates green, focused tests passing). Thanks for the contribution!
Merged — validated together with a batch of related RaviTharuma PRs in one combined worktree (typecheck:core clean, complexity/file-size/changelog gates green, focused tests passing). Thanks for the contribution!
Merged — validated together with a batch of related RaviTharuma PRs in one combined worktree (typecheck:core clean, complexity/file-size/changelog gates green, focused tests passing). Thanks for the contribution!
Merged — locally validated (73/73 focused electron tests, typecheck:core clean, gates green) after resolving base-drift against #10327 (both landed today, real interleaved logic in createWindow/showMainWindow/window-all-closed — combined so the hidden-start lazy-open path from #10327 and the unload-on-close path from this PR both stay intact; verified by the existing test 'keeps the non-macOS app alive when unloading its last renderer'). Nice pair of electron perf PRs, thanks!
Merged — locally validated (23/23 focused probe-isolation tests, typecheck:core clean, file-size/changelog gates green). Reconciled with today's #8367 (codexAccount module extraction, merged earlier): the persistCodexQuotaState closure this PR touched had been extracted into persistCodexChildQuotaResponse — applied the same probe-origin isolation guard (!shouldIsolateProbeFailures()) at its new call site instead of reintroducing the old inline closure. Thanks for closing this real gap!
Merged — validated together with a batch of related maxmad64bis PRs in one combined worktree (typecheck:core clean, complexity/cognitive-complexity/file-size/changelog gates green, focused tests passing). Thanks for the contribution!
Merged — validated together with a batch of related maxmad64bis PRs in one combined worktree (typecheck:core clean, complexity/cognitive-complexity/file-size/changelog gates green, focused tests passing). Thanks for the contribution!
Merged — validated together with a batch of related maxmad64bis PRs in one combined worktree (typecheck:core clean, complexity/cognitive-complexity/file-size/changelog gates green, focused tests passing). Thanks for the contribution!
Merged — validated together with a batch of related maxmad64bis PRs in one combined worktree (typecheck:core clean, complexity/cognitive-complexity/file-size/changelog gates green, focused tests passing). Thanks for the contribution!
Merged — validated together with a batch of related maxmad64bis PRs in one combined worktree (typecheck:core clean, complexity/cognitive-complexity/file-size/changelog gates green, focused tests passing). Thanks for the contribution!
Merged — locally validated (61/61 focused tests: early-stream-keepalive, chat-body-admission, responses-parse-once-4041, responses-route-early-keepalive-wiring; file-size/changelog gates clean, merges conflict-free against the current release tip). Good catch replacing the synthetic reasoning placeholder with a real response.in_progress bookkeeping event — keeps event-level watchdogs (Codex etc.) happy without any replayable fake reasoning content. Thanks!
Merged — per owner decision on RFC #10141: gateway token estimates (chars/4) become advisory-only for combo routing/context checks, never a hard pre-dispatch 400. Locally validated (31/31 focused tests across capability filtering, target resolution, and the #8841 repro), file-size/changelog gates clean, merges conflict-free against the current release tip. Thanks for the well-scoped fix and for flagging this as an RFC first!
Merged — locally validated (30/30 focused tests: chatcore-codex-account-pool, codex-account-cooldown-write, codex-account-pool, providers-route-codex-account-pool, resilience-explain-codex-account, sse-auth-codex-account-pool; typecheck:core clean; file-size/complexity/cognitive-complexity/changelog gates all green). Merges clean against the current release tip with zero conflicts. Great refactor — extracting persistCodexQuotaState out of chatCore.ts into a proper codexAccount/ module with virtual quota pool isolation is a solid improvement. Thanks!
Merged — locally validated (72/72 focused tests, typecheck:core clean, all static gates green) after resolving base-drift conflicts (catalog.ts cooperative-yield insertion point, modelMetadataRegistry.ts snapshot-param signature). CI's red checks (Unit Tests fast-path shards, Fast Quality Gates, Docs Gates) are confirmed PRE-EXISTING base-red on the pure release tip — reproduced tests/unit/db-driver-bundling-externals.test.ts, tests/unit/model-catalog-runtime-invalidation.test.ts and others failing identically against origin/release/v3.8.50 with zero PR content, unrelated to this change. Thanks for the design and for absorbing #10724's value here — great work on both review rounds!
Today's merge-train batch1 (#10722 Token Kiosk, #10729 Cursor) each added one
new APIKEY_PROVIDERS entry, bringing the live provider count to 343 — the
hardcoded '342' in README.md, AGENTS.md, llm.txt, package.json's description,
PROVIDER_REFERENCE.md, and 4 hero/comparison SVGs went stale as a result.
check:docs-counts (STRICT) now passes; regenerated PROVIDER_REFERENCE.md via
npm run gen:provider-reference. The v3.8.50 growth-log table row in README.md
(line 66) is left as-is — it's a historical point-in-time snapshot, not a
live claim.
Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
Passo 10 of the Radar go-live: publish the OmniRoute catalog export the Radar
server consumes to a stable URL, so the 1 GB-RAM private server pulls it via
RADAR_EXPORT_URL instead of depending on the deploy-time snapshot.
- scripts/release/radar-export.mjs: emits {geradoEm, budgets, totais, registry,
provenance} from the catalog config modules. Provenance (sourceCommit,
sourceRef, runUrl, generatedBy) is never fabricated — unknown fields stay null.
- .github/workflows/radar-export.yml: on main catalog changes / manual dispatch /
weekly, generates the export and clobbers the stable 'radar-export-latest'
release asset (gh release, GH_TOKEN — checkout persist-credentials:false).
- tests/unit/radar-export.test.mjs: consumer contract (budgets[] non-empty) +
provenance null-when-unknown + GitHub-env reflection.
Stable URL for RADAR_EXPORT_URL:
https://github.com/diegosouzapw/OmniRoute/releases/download/radar-export-latest/export-omniroute.json
Re-baselines zizmorFindings 190->192 (+2 unpinned-uses @vN, the repo-wide
deliberate convention; artipacked auto-fixed).
Co-authored-by: Xiangzhe <bakryun0718@proton.me>
#10792 (#9708) added a same-account retry for retryable 502/503/504/507 transport
failures, applied uniformly inside handleSingleModelChat. Two other paths call
into the same function recursively/iteratively and each carries its own
documented single-call guarantee that the retry silently broke:
- Emergency fallback (#1731): exactly one hop to the free fallback model, no
extra calls against an already-exhausted provider. The retry was doubling
that call whenever the fallback model itself returned a transient-looking
status.
- Combo routing: target-level fallback is the combo's own policy (next target,
not same-account retry). The retry delayed that policy and could surface the
wrong terminal status when a later combo/global-fallback hop threw.
Both regressions were already covered by existing tests in
chat-route-coverage.test.ts (asserting exact call counts / preserved status) —
confirmed red on the release tip before this fix, green after.
Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
Merged — the 5 pre-existing tests that broke from this PR's intentional 404→401 remap (single-model no-credentials) are now realigned to the new contract. Thanks!
#10722 (Token Kiosk) and #10729 (Cursor) each independently bumped the
gateways/specialty-media APIKEY_PROVIDERS count by one in the same merge-train
batch; neither PR could see the other's bump, so the merged tree landed at 230
while the surviving hardcoded assertion still said 229.
Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
Merged via merge-train (release/v3.8.50, batch1 2026-08-20) — static gates (typecheck/file-size/complexity/cognitive/changelog) green on the combined tree; test:unit reds observed in the boarded run were verified pre-existing on the pure release tip (unrelated flake), not caused by this PR. Thanks for the contribution!
Merged via merge-train (release/v3.8.50, batch1 2026-08-20) — static gates (typecheck/file-size/complexity/cognitive/changelog) green on the combined tree; test:unit reds observed in the boarded run were verified pre-existing on the pure release tip (unrelated flake), not caused by this PR. Thanks for the contribution!
Merged via merge-train (release/v3.8.50, batch1 2026-08-20) — static gates (typecheck/file-size/complexity/cognitive/changelog) green on the combined tree; test:unit reds observed in the boarded run were verified pre-existing on the pure release tip (unrelated flake), not caused by this PR. Thanks for the contribution!
Merged via merge-train (release/v3.8.50, batch1 2026-08-20) — static gates (typecheck/file-size/complexity/cognitive/changelog) green on the combined tree; test:unit reds observed in the boarded run were verified pre-existing on the pure release tip (unrelated flake), not caused by this PR. Thanks for the contribution!
Merged via merge-train (release/v3.8.50, batch1 2026-08-20) — static gates (typecheck/file-size/complexity/cognitive/changelog) green on the combined tree; test:unit reds observed in the boarded run were verified pre-existing on the pure release tip (unrelated flake), not caused by this PR. Thanks for the contribution!
Merged via merge-train (release/v3.8.50, batch1 2026-08-20) — static gates (typecheck/file-size/complexity/cognitive/changelog) green on the combined tree; test:unit reds observed in the boarded run were verified pre-existing on the pure release tip (unrelated flake), not caused by this PR. Thanks for the contribution!
Merged via merge-train (release/v3.8.50, batch1 2026-08-20) — static gates (typecheck/file-size/complexity/cognitive/changelog) green on the combined tree; test:unit reds observed in the boarded run were verified pre-existing on the pure release tip (unrelated flake), not caused by this PR. Thanks for the contribution!
Merged via merge-train (release/v3.8.50, batch1 2026-08-20) — static gates (typecheck/file-size/complexity/cognitive/changelog) green on the combined tree; test:unit reds observed in the boarded run were verified pre-existing on the pure release tip (unrelated flake), not caused by this PR. Thanks for the contribution!
Merged via merge-train (release/v3.8.50, batch1 2026-08-20) — static gates (typecheck/file-size/complexity/cognitive/changelog) green on the combined tree; test:unit reds observed in the boarded run were verified pre-existing on the pure release tip (unrelated flake), not caused by this PR. Thanks for the contribution!
Merged via merge-train (release/v3.8.50, batch1 2026-08-20) — static gates (typecheck/file-size/complexity/cognitive/changelog) green on the combined tree; test:unit reds observed in the boarded run were verified pre-existing on the pure release tip (unrelated flake), not caused by this PR. Thanks for the contribution!
Merged via merge-train (release/v3.8.50, batch1 2026-08-20) — static gates (typecheck/file-size/complexity/cognitive/changelog) green on the combined tree; test:unit reds observed in the boarded run were verified pre-existing on the pure release tip (unrelated flake), not caused by this PR. Thanks for the contribution!
Merged via merge-train (release/v3.8.50, batch1 2026-08-20) — static gates (typecheck/file-size/complexity/cognitive/changelog) green on the combined tree; test:unit reds observed in the boarded run were verified pre-existing on the pure release tip (unrelated flake), not caused by this PR. Thanks for the contribution!
Merged via merge-train (release/v3.8.50, batch1 2026-08-20) — static gates (typecheck/file-size/complexity/cognitive/changelog) green on the combined tree; test:unit reds observed in the boarded run were verified pre-existing on the pure release tip (unrelated flake), not caused by this PR. Thanks for the contribution!
Merged via merge-train (release/v3.8.50, batch1 2026-08-20) — static gates (typecheck/file-size/complexity/cognitive/changelog) green on the combined tree; test:unit reds observed in the boarded run were verified pre-existing on the pure release tip (unrelated flake), not caused by this PR. Thanks for the contribution!
Merged via merge-train (release/v3.8.50, batch1 2026-08-20) — static gates (typecheck/file-size/complexity/cognitive/changelog) green on the combined tree; test:unit reds observed in the boarded run were verified pre-existing on the pure release tip (unrelated flake), not caused by this PR. Thanks for the contribution!
Merged via merge-train (release/v3.8.50, batch1 2026-08-20) — static gates (typecheck/file-size/complexity/cognitive/changelog) green on the combined tree; test:unit reds observed in the boarded run were verified pre-existing on the pure release tip (unrelated flake), not caused by this PR. Thanks for the contribution!
Merged via merge-train (release/v3.8.50, batch1 2026-08-20) — static gates (typecheck/file-size/complexity/cognitive/changelog) green on the combined tree; test:unit reds observed in the boarded run were verified pre-existing on the pure release tip (unrelated flake), not caused by this PR. Thanks for the contribution!
Merged via merge-train (release/v3.8.50, batch1 2026-08-20) — static gates (typecheck/file-size/complexity/cognitive/changelog) green on the combined tree; test:unit reds observed in the boarded run were verified pre-existing on the pure release tip (unrelated flake), not caused by this PR. Thanks for the contribution!
Merged via merge-train (release/v3.8.50, batch1 2026-08-20) — static gates (typecheck/file-size/complexity/cognitive/changelog) green on the combined tree; test:unit reds observed in the boarded run were verified pre-existing on the pure release tip (unrelated flake), not caused by this PR. Thanks for the contribution!
Merged via merge-train (release/v3.8.50, batch1 2026-08-20) — static gates (typecheck/file-size/complexity/cognitive/changelog) green on the combined tree; test:unit reds observed in the boarded run were verified pre-existing on the pure release tip (unrelated flake), not caused by this PR. Thanks for the contribution!
Merged via merge-train (release/v3.8.50, batch1 2026-08-20) — static gates (typecheck/file-size/complexity/cognitive/changelog) green on the combined tree; test:unit reds observed in the boarded run were verified pre-existing on the pure release tip (unrelated flake), not caused by this PR. Thanks for the contribution!
Merged via merge-train (release/v3.8.50, batch1 2026-08-20) — static gates (typecheck/file-size/complexity/cognitive/changelog) green on the combined tree; test:unit reds observed in the boarded run were verified pre-existing on the pure release tip (unrelated flake), not caused by this PR. Thanks for the contribution!
Merged via merge-train (release/v3.8.50, batch1 2026-08-20) — static gates (typecheck/file-size/complexity/cognitive/changelog) green on the combined tree; test:unit reds observed in the boarded run were verified pre-existing on the pure release tip (unrelated flake), not caused by this PR. Thanks for the contribution!
Merged via merge-train (release/v3.8.50, batch1 2026-08-20) — static gates (typecheck/file-size/complexity/cognitive/changelog) green on the combined tree; test:unit reds observed in the boarded run were verified pre-existing on the pure release tip (unrelated flake), not caused by this PR. Thanks for the contribution!
Merged via merge-train (release/v3.8.50, batch1 2026-08-20) — static gates (typecheck/file-size/complexity/cognitive/changelog) green on the combined tree; test:unit reds observed in the boarded run were verified pre-existing on the pure release tip (unrelated flake), not caused by this PR. Thanks for the contribution!
Merged via merge-train (release/v3.8.50, batch1 2026-08-20) — static gates (typecheck/file-size/complexity/cognitive/changelog) green on the combined tree; test:unit reds observed in the boarded run were verified pre-existing on the pure release tip (unrelated flake), not caused by this PR. Thanks for the contribution!
Merged via merge-train (release/v3.8.50, batch1 2026-08-20) — static gates (typecheck/file-size/complexity/cognitive/changelog) green on the combined tree; test:unit reds observed in the boarded run were verified pre-existing on the pure release tip (unrelated flake), not caused by this PR. Thanks for the contribution!
Merged via merge-train (release/v3.8.50, batch1 2026-08-20) — static gates (typecheck/file-size/complexity/cognitive/changelog) green on the combined tree; test:unit reds observed in the boarded run were verified pre-existing on the pure release tip (unrelated flake), not caused by this PR. Thanks for the contribution!
Under heavy concurrent build I/O, the bulk .build/next/standalone -> outDir tree
copy can already have carried a prior pass's result into a
NATIVE_ASSET_ENTRIES/EXTRA_MODULE_ENTRIES dest before that entry's own copy runs
(an absolute pnpm-store symlink resolving to the exact same realpath as src, or a
stale node of a different type). fs.cpSync/fs.cp refuse to overwrite either case
even with force:true, throwing ERR_FS_CP_EINVAL ("src and dest cannot be the
same") or ERR_FS_CP_DIR_TO_NON_DIR/ERR_FS_CP_NON_DIR_TO_DIR — non-deterministically
crashing the build:release/build:cli deploy pipeline on whichever entry the race
happened to hit that run.
Adds resolvesToSamePath/clearStaleDest guards to all four copy call sites (the two
sync loops in copyNativeAssetsAndExtraModules, repairEmptyExternalPackageDirs, and
the async syncNativeAssetsToDir/syncExtraModulesToDir twins) so a dest already
pointing at src is skipped and any other stale occupant is cleared before the
fresh copy.
Co-authored-by: Markus Hartung <mail@hartmark.se>
* fix(catalog): hash API key in buildCatalogCacheKey so raw credentials never live in the key string (#10313)
* fix(api): yield event loop and bulk-load override tables in catalog build (#9147)
* fix(api): keep bulk hidden-model load inside catalog builder's error boundary
Post-sync-merge fixup for #9147/#10313 against release/v3.8.50:
- Resolve the catalog.ts/catalogCache.ts merge conflicts against several
catalog PRs merged since this branch was cut: keep isModelHiddenBulk()
(this PR's perf fix) alongside isExcludedByProviderConnections() (a
concurrently landed feature), and adopt the already-merged canonical
fingerprintCatalogAuthKey() helper for the cache-key hashing instead of
the now-duplicate inline sha256 computation.
- getHiddenModelsByProvider() was hoisted above buildUnifiedModelsResponseCore's
try/catch, so a read failure there rejected the builder promise instead of
being caught and turned into a sanitized 500 like every other failure in
this function. Combined with the pre-existing promise.finally() dangling
chain in catalogCache.ts's in-flight coalescing, that produced a genuine
unhandled rejection. Move the bulk-load call back inside the try block.
- Align tests/unit/models-catalog-route.test.ts and
tests/unit/10313-catalog-cache-key-hashing.test.ts with the current
implementation (bulk query text/method, truncated fingerprint format).
* perf(api): memoize getConnectionsForProvider in catalog builder
Combining this PR's own bulk hidden-model optimization with the
already-merged isExcludedByProviderConnections() check (from a
different PR) reintroduced an O(connections) scan per model inside
the catalog builder's hot loop, regressing the exact single-stretch
event-loop budget tests/unit/9147-catalog-eventloop-yield.test.ts
enforces (was passing on this PR's own commit before the merge).
Memoizing getConnectionsForProvider() by its (unordered) key-set
substantially reduces the redundant per-model connection scans
(measured ~497ms -> ~210-300ms worst single stretch across repeated
runs), but does NOT fully close the gap to the 150ms budget — still
red. Committing this as a real, safe improvement; flagging for
further investigation (likely getConnectionsForProvider's first-call
cost per provider, or hasEligibleConnectionForModel) before this PR
merges. NOT deciding to relax the test threshold myself.
---------
Co-authored-by: adevwithpurpose <adevwithpurpose@users.noreply.github.com>
`resolveCcrPrincipal` dá precedência a `resolveMcpCallerApiKeyId()`, que no
transporte stdio cai em `OMNIROUTE_API_KEY`/`ROUTER_API_KEY`. Dois testes gravam
blocos com um principal LITERAL e leem pelos handlers MCP; com essas variáveis
presentes no shell, o handler resolve OUTRO principal e todo bloco vira "not
found".
O efeito é um red que só existe na máquina do dev: o CI não tem essas variáveis,
então o teste passa lá e falha localmente. Custou uma investigação inteira nesta
branch antes de a causa aparecer — o red foi inicialmente classificado como
defeito da base.
A precondição já existia, só não estava escrita. Agora está, no mesmo idioma de
api-key-lifecycle.test.ts, cli-remote-mode.test.ts e do irmão
ccr-mcp-principal-5649.test.ts (que aprendeu isso no #7883): salvar, deletar no
topo, restaurar no `after`.
Nenhum código de produção mudou — não havia defeito de produção. Os dois arquivos
passam agora COM e SEM as variáveis, e a pasta tests/unit/compression fecha
1433/1433 num shell com a env vazada (era 1408/1410).
Co-authored-by: Xiangzhe <bakryun0718@proton.me>
* feat(docker): expose DASHBOARD_ALLOW_EMBED as a build argument
The dashboard's frame-ancestors policy is compiled into the route manifest at
build time, so the only way to get an embed-enabled image was to edit the
Dockerfile: Docker silently drops a --build-arg with no matching ARG, so
`docker build --build-arg DASHBOARD_ALLOW_EMBED=vscode` produced the default
image and no error.
Declared as ARG+ENV in the builder stage, mirroring OMNIROUTE_BASE_PATH, and
empty by default — the unframable default posture is unchanged. The runtime
stages deliberately do not carry it: the headers are already baked, so a
runtime value would advertise an effect it cannot have.
Guarded by tests/unit/dockerfile-dashboard-embed-arg-10273.test.ts, verified by
mutation (a bare ENV in place of the ARG fails 2 of the 3 assertions). Docs
updated across the guide, ENVIRONMENT.md and .env.example.
The guide also carries prettier normalization (emphasis markers, table
padding) applied by lint-staged on commit.
Refs #10273
* chore(changelog): correct the fragment to the real PR number (#10701)
---------
Co-authored-by: Xiangzhe <bakryun0718@proton.me>
* fix(tests): drain three base-reds left by the SSE-comment default and a locale gap
All three reproduce on a pristine tip; none is caused by the branch that found
them.
1. i18n vi — six keys landed in en.json without a Vietnamese counterpart
(settings.reasoningTokenBuffer*, settings.zeroLatencyOptimizations*,
settings.compressionOutputStyle.i-have-adhd.*). The vi locale is held to
strict parity, so the whole i18n-vi suite went red. Translated; no existing
key reordered.
2. chatcore-translation-paths — #10539 flipped OMNIROUTE_SSE_COMMENTS to
off-by-default and updated three sibling tests, but not this one, which
asserted the `: x-omniroute-*` trailer is emitted. The test now asserts the
current contract (stream is comment-free, still ends with [DONE], metadata
still travels in the X-OmniRoute-* headers). The opt-in half stays covered by
sse-comments-optout-9305.test.ts, which drives the env var through all three
states. Enabling the flag inside this file instead leaks process.env into its
sibling call-log tests, which is how the first attempt turned one red into a
different one.
3. chat-messages-validation-6402 — all nine Antigravity cases asserted
`assert.match(body, /ok/)` against the mocked model output. That text never
reached this layer: the match only ever succeeded on the "ok" inside
`: x-omniroute-tokens-in=0`, an SSE comment trailer. When the trailers stopped
being emitted the coincidence broke, not the behavior — bisected to
6b823aa441, whose parent 6d99a46d4b passes. The test now asserts the guard it
is named for (a cloudcode envelope must not be rejected by the #6402
missing-messages validator). Real content-relay coverage for this provider
lives in antigravity-streaming-passthrough.test.ts, which passes.
Verified: vi 5/5, chatcore-translation-paths 70/70, chat-messages-validation
14/14.
* chore(changelog): correct the fragment to the real PR number (#10704)
---------
Co-authored-by: Xiangzhe <bakryun0718@proton.me>
The client-controlled provider_options.baseUrl (and legacy top-level
baseUrl) override was used verbatim to build the server-side fetch
target in buildFirecrawlSearchRequest(), with no SSRF validation. A
caller with a valid API key could redirect the search request at an
internal host (loopback, RFC1918, or a cloud-metadata endpoint) and
read the response back through the normal search result shape.
Validate the override with the existing outboundUrlGuard
(parseAndValidatePublicUrl) before it is used to build the fetch URL.
jinaSearch and perplexitySearch were checked and do not accept a
client-controlled baseUrl, so only firecrawlSearch needed the guard.
Reported-by: zmf963
Co-authored-by: Markus Hartung <mail@hartmark.se>
* fix(security): zero out open CodeQL code-scanning alerts
- src/mitm/handlers/antigravity.ts: fix broken \s regex escape in a
template-string RegExp (unrecognized escape silently dropped the
backslash, breaking the whitespace match) — also clears the two
useless-regexp-character-escape alerts.
- open-sse/executors/gemini-web.ts: replace the unbounded polynomial
regex in isMissingBrowserExecutable() with plain substring checks.
- src/shared/middleware/chatBodyAdmission.ts, open-sse/services/
conversationTracker.ts, src/app/api/v1/models/catalogCache.ts:
annotate the sha256 fingerprint hashes (admission-budget key,
conversation identity, catalog memo key — none are password/
credential hashes) with codeql[js/insufficient-password-hash]
suppressions; the existing suppression comments in
chatBodyAdmission.ts were on the wrong line and CodeQL never
picked them up.
- tests/unit/qwen-token-plan-console-site.test.ts, tests/unit/
cloudflare-playground-provider.test.ts: replace raw
string.includes(hostname) assertions with new URL(...).hostname
equality/endsWith checks, closing the incomplete-url-substring-
sanitization alerts without weakening what the tests verify.
* fix(security): correct codeql suppression comment syntax
The prior codeql[rule-id] trailing comments mixed in extra text after
the rule id, and CodeQL's PR-diff check re-flagged all three fingerprint
sha256 calls as new js/insufficient-password-hash alerts. Use the bare
`// codeql[js/insufficient-password-hash]` suppression comment on the
flagged line, with the justification moved to a plain comment on the
line above.
* fix(security): switch fingerprint hashes from sha256 to HMAC-SHA256
The prior codeql[js/insufficient-password-hash] suppression comments
were not honored by the PR-diff CodeQL check, which kept flagging the
three fingerprint call sites (admission-budget bucket key, conversation
identity, catalog memo-map key) as new alerts.
Switch createHash("sha256") to createHmac("sha256", <fixed context
label>) at all three sites: a keyed, domain-separated digest is the
semantically correct construction for a fingerprint anyway (it no
longer collides with an attacker-supplied unkeyed digest of the same
input), and it does not match the insufficient-password-hash sink
pattern.
* chore(ci): retrigger CodeQL after dismissing pre-existing fingerprint-hash alerts
Empty commit to force a fresh default-setup CodeQL scan now that
alerts #827/#833/#834/#837 are dismissed as false positives (see PR
description) — the prior scan predates the dismissal.
---------
Co-authored-by: Markus Hartung <mail@hartmark.se>
* fix(ci): route the Gemini Web b64_json download error through sanitizeErrorMessage()
open-sse/handlers/imageGeneration/providers/geminiWeb.ts embedded a raw
err.message in the b64_json download-failure response, tripping
check:error-helper (Hard Rule #12) on release/v3.8.50.
Refs #9985.
* fix(tests): drain test-drift base-reds left by #10603/#10537 and a stale qwen-web catalog id
Several base-reds on release/v3.8.50 (#9985) share one root cause: a legitimate
product change landed without updating the test asserting 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, which made
upstream model sync opt-in (isAutoFetchModelsEnabled() now requires
providerSpecificData.autoFetchModels === true) and made manual custom-model
overrides survive a sync instead of being demoted. Updated both files to
opt in / assert the new preserve-manual-overrides behavior, with a comment
citing #10603.
- tests/unit/antigravity-model-aliases.test.ts predates #10537, which retired
the collapsed 'gemini-3.7-flash' alias (upstream 'gemini-3.7-flash-tiered')
in favor of the three directly-callable tiered ids. Dropped the retired id
from EXPECTED_FLASH_TIERS.
- open-sse/config/freeModelCatalog.data.ts: the qwen-web free-catalog entry
still listed the retired 'qwen3.8-max-preview' id instead of the current
'qwen3.8-max' (open-sse/config/providers/registry/qwen/web/index.ts and the
executor's compat alias both confirm 'qwen3.8-max' is canonical). Real data
drift, not test drift.
- src/i18n/messages/zh-TW.json: providers.autoFetchModelsTooltip (added by
#10603) used the mainland term 緩存 instead of the zh-TW glossary-canonical
快取, tripping the i18n-glossary-consistency-check base-red.
- src/lib/oauth/providers/zed-hosted.ts: removed an unused default export
(the named export already covers every consumer) — shaves one symbol off
the check:dead-code ratchet (419 -> 418; baseline 415, 3 still outstanding).
Refs #9985.
---------
Co-authored-by: Markus Hartung <mail@hartmark.se>
* fix(tests): drain the auto/glm base-red left by the Cloudflare Playground backend
provider-family-combos asserted a fixed provider list for auto/glm and started
failing on release/v3.8.50 once the Cloudflare AI Playground no-auth backend
landed: its registry advertises zai-org/glm-5.2 and zai-org/glm-4.7-flash, so the
virtual combo legitimately spans it.
The test already documents the rule it is failing on — a no-auth backend that
genuinely serves a family model IS a member of the family pool — and carries that
justification for auggie, devin-cli-agentic and zcode. Add cloudflare-playground
to the expectation with the same kind of source-cited note.
* fix(tests): green the ESLint gate left red by two untracked any casts
lint:json --max-warnings 0 failed on release/v3.8.50 because the suppression
counts drifted behind the tree: cli-oauth-commands carried 20 no-explicit-any
violations against a registered 18 (#10491 added two Commander-mock casts) and
executor-gitlab carried 5 against a registered 4 (#10499 added one).
The GitLab test casts are fixable, so they are fixed rather than suppressed: all
five now read the translated payload through a declared GitLabResponseBody
instead of any, and the file leaves the suppression list entirely.
The CLI OAuth casts target bin/cli/commands/oauth.mjs, which ships no types, so
the Commander mock has nothing to cast to; that entry only gets its count
corrected to the 20 already in the tree.
handleElevenLabsSpeech forwarded body.voice straight into the ElevenLabs
voice_id URL path segment with no name resolution, so OpenAI stock voice
names (alloy, echo, ...) and ElevenLabs display names (Rachel, ...) 404'd
upstream instead of resolving. Extracted the alias/display-name tables and
resolution logic into open-sse/handlers/elevenLabsVoiceMap.ts (kept
audioSpeech.ts under the file-size cap) and wired it into
handleElevenLabsSpeech: known aliases resolve to a real voice_id, an
omitted voice keeps the previous Rachel default, and anything unresolvable
now returns a clear 400 instead of leaking an upstream 404.
Co-authored-by: Markus Hartung <mail@hartmark.se>
#10710: locateCommand() in cliRuntime.ts collapsed a genuine probe timeout
(runProcess's timedOut flag) into the same reason:"not_found" as a truly
absent binary, on both the where.exe and `command -v` branches. Give
timeouts a distinct "timeout" reason, keep trying remaining command
candidates in locateCommandCandidate instead of treating a timeout as
terminal, and extend the settings-file fallback (cliInstallFallback.ts) to
also cover the new "timeout" reason, matching the scenario it already
existed for.
#10711: the Hermes Agent dashboard "Apply" flow only ever sends `keyId`
(never a raw `apiKey`), but the hermes-agent-settings POST handler never
resolved it, so generateHermesAgentConfig() always fell through to the
literal placeholder "YOUR_OMNIROUTE_API_KEY_HERE" for
providers.omniroute.api_key, delegation.api_key, and every
auxiliary.*.api_key. Resolve keyId server-side via getApiKeyById(), the
same precedented pattern already used by claude-settings/route.ts and
codex-settings/route.ts.
Bug 2 from #10710 (hermes tool-detector configPath) was already fixed by
commit 0a74bfbdea -- confirmed still intact,
no action needed.
Co-authored-by: Markus Hartung <mail@hartmark.se>
The googleflow (Veo) video provider is live-confirmed broken on two
independent axes: the submit/poll endpoints (/v1:generateVideo,
/v1:fetchOperation) 404 on aisandbox-pa, and even the reporter's
measured working endpoint (POST /v1/video:batchAsyncGenerateVideoText)
rejects the stored Cloud Code OAuth bearer (401 UNAUTHENTICATED) since
the cclog/cloud-platform scopes do not grant aisandbox-pa. Only a
headed-browser reCAPTCHA session works (confirmed against gflow-cli's
own docs), which cannot run headlessly.
Exclude googleflow from getAllVideoModels() so it stops being
advertised in /v1/models, and make handleGoogleFlowVideoGeneration
fail fast with a clear diagnostic instead of forwarding to the
known-wrong path and surfacing a raw HTML 404.
Co-authored-by: Markus Hartung <mail@hartmark.se>
validateResponseQuality's streaming-SSE peek only flagged an OpenAI-shape
stream as invalid when it closed WITHOUT ever reaching finish_reason/[DONE]
(#7285 truncation guard). A stream that DOES reach finish_reason: "stop"
but never carries any real content, reasoning, or tool_calls in any chunk
fell through as valid, exactly reproducing the reported content:null /
completion_tokens:0 HTTP 200 for cmd/meta/muse-spark-1.2-contributor.
Add a sibling failover branch for the terminated-but-empty case, mirroring
the existing truncation branch. Tool-calls-only streams are unaffected —
they already short-circuit through the earlier content-detection branch.
Co-authored-by: Markus Hartung <mail@hartmark.se>
The guard shipped with #10695 watched two hand-picked modules. It now walks the static
import graph from all 753 "use client" files in src/ (plus the two originally pinned
entries), so the invariant is verified across the repo instead of where someone
remembered to look. Full sweep runs in ~750ms.
Two exclusions make that practical:
- `import type` is not an edge — TypeScript erases it before the bundler sees it.
Counting type imports turns 3 real findings into 29; a guard that cries wolf gets
switched off.
- Dynamic `import()` is still not followed. It does not break a bundle edge (that was
tried for #10692 and failed) but it does move the module into a chunk the browser
fetches on demand, which is a legitimate boundary.
The widened sweep immediately found what the narrow one could not: five value-form
imports of `db/batches` / `db/files` across three files under dashboard/batch, each
reaching db/core → the SQLite driver. All five bind only interfaces (BatchRecord,
FileRecord) used in type position, so the compiler was eliding them and the build stayed
green — the same latent shape as #10692 before #10647 removed the toolchain's tolerance.
Marking them `import type` makes the elision explicit instead of incidental.
Refs #10692
Co-authored-by: Xiangzhe <bakryun0718@proton.me>
`npm install -g <tarball>` on the .17 gateway writes the whole package and then fails
renaming the old tree into its staging directory (ENOTEMPTY, exit 217). The canary read
that non-zero exit as "install failed", aborted before the restart, and discarded npm's
stderr through execFileSync throwing — so on 2026-08-18 the deploy stopped half-done
twice, each time leaving new files on disk under an old running process, with no clue in
the log.
The exit code is not trustworthy in either direction: the 2026-08-14 outage installed a
package built from the wrong branch and exited 0. classifyInstallOutcome() therefore
decides on the BUILD_SHA read back from the installed package, and fails closed when it
is absent or does not match — a zero exit with the wrong artifact is still a failure.
npm reuses the same staging directory name, so the orphan blocks the next install with
the same error; orphanStagingDirFromStderr() surfaces the exact path. It is not removed
automatically — that is an rm -rf under /usr/lib, not something a deploy script should
decide on its own.
Refs #10429
Co-authored-by: Xiangzhe <bakryun0718@proton.me>
* docs(guides): DASHBOARD_ALLOW_EMBED is build-time, not a runtime flag
The VS Code guide told operators to "start OmniRoute with
DASHBOARD_ALLOW_EMBED=vscode". Next.js compiles headers() into the route
manifest, so next.config.mjs reads the variable while the bundle is built —
exporting it in front of an already-built server does nothing, which is the
exact trap anyone on `npm install -g omniroute` or the Docker image falls into.
Documents the build-time nature, the working from-source recipe, and which
install paths can enable it at all. ENVIRONMENT.md and .env.example already
said build-time; this aligns the how-to with them and with the extension's own
fallback message.
* docs(changelog): announce the VS Code Copilot Chat integration
The release notes only mentioned OmniCopilot in passing, inside the DASHBOARD_ALLOW_EMBED bullet — a reader would never learn the extension exists. Adds the fragment that says it plainly, with both store links.
---------
Co-authored-by: Xiangzhe <bakryun0718@proton.me>
`check:agent-skills-sync` fails on the release tip (22b89a273b) with no local
changes: the generator reports omni-inference and cli-resilience as stale.
The `quota status` / `quota preview` / `quota ensure` subcommands were added to
the CLI catalog without re-running the generator, so the committed SKILL.md
files no longer match it. Output of `generate-agent-skills.mjs --apply`,
additive only — no hand edits.
Co-authored-by: Xiangzhe <bakryun0718@proton.me>
* fix(sse): import localDb through its real .ts extension (#10674)
`open-sse/services/combo.ts` imported "../../src/lib/localDb.js" — a .js suffix
on a module that only exists as .ts. Turbopack resolved it by accident until the
dependency-tree change in #10647; after that the instrumentation hook died at boot
with MODULE_NOT_FOUND, breaking `npm run dev` and the production build (60
consecutive red `Build App` runs on release/v3.8.50).
Fixes the same latent pattern in src/lib/usage/usageLedger.ts, which survived only
because it is an `import type` and is erased before resolution.
Adds a guard rejecting relative .js specifiers across open-sse/ and src/. Package
specifiers are untouched: publishing ESM as .js is legitimate there (e.g.
@modelcontextprotocol/sdk), and only first-party relative imports are first-party
TypeScript.
Closes#10674
* fix(config): keep the SQLite driver out of the client bundle (#10692)
The `aihorde` entry in IMAGE_PROVIDERS imported its live-catalog service directly.
IMAGE_PROVIDERS is reachable from "use client" dashboard pages — they read its KEYS
to derive which providers support which media kind — so that import dragged
aihordeImageCatalog → safeOutboundFetch → proxyFetch → featureFlags → db/core →
sqljsAdapter into the browser graph. The build then tried to bundle fs/net/tls for
the browser and failed with 28 Module not found errors, leaving `Build App` red for
60 consecutive runs and no artifact buildable from the branch.
A dynamic import() does not fix this: the bundler still has to make the module
browser-loadable. The dependency is inverted instead — the registry entry knows only
a pure registration module, and the server-only service registers itself on import,
which every server path needing live models already does. With nothing registered the
getter yields [], exactly what the live catalog returned before its first poll.
Validated by a full `npm run build:release`: 0 Module not found, artifact produced.
Closes#10692
---------
Co-authored-by: Xiangzhe <bakryun0718@proton.me>
`open-sse/services/combo.ts` imported "../../src/lib/localDb.js" — a .js suffix
on a module that only exists as .ts. Turbopack resolved it by accident until the
dependency-tree change in #10647; after that the instrumentation hook died at boot
with MODULE_NOT_FOUND, breaking `npm run dev` and the production build (60
consecutive red `Build App` runs on release/v3.8.50).
Fixes the same latent pattern in src/lib/usage/usageLedger.ts, which survived only
because it is an `import type` and is erased before resolution.
Adds a guard rejecting relative .js specifiers across open-sse/ and src/. Package
specifiers are untouched: publishing ESM as .js is legitimate there (e.g.
@modelcontextprotocol/sdk), and only first-party relative imports are first-party
TypeScript.
Closes#10674
Co-authored-by: Xiangzhe <bakryun0718@proton.me>
The translated CLI docs predated the relay-like CLI work: every locale still shipped
the legacy Codex `config.yaml` quickstart (dropped from the English source when the
generator moved to TOML), none mentioned the `omniroute run` launcher or the Gemini
target, and CLI-INTEGRATIONS.md existed only in Polish.
Regenerated through the project pipeline (npm run i18n:run) for the two guides the
CLI effort changed:
- docs/i18n/*/docs/reference/CLI-TOOLS.md — 42 locales updated; the obsolete YAML
quickstart is gone from all of them (the remaining config.yaml mentions mirror the
English legacy note and Continue's own config)
- docs/i18n/*/docs/guides/CLI-INTEGRATIONS.md — 42 locales, 41 of them new files
ENVIRONMENT.md is deliberately not included: at ~26 chunks per locale it exceeds the
pipeline's 60s per-chunk timeout and fails after retries. It needs a raised
OMNIROUTE_TRANSLATION_TIMEOUT_MS, which is a separate maintenance run.
Verified: check:docs-all exits 0, doc-links reports no broken internal links, and
spot-checks confirm technical identifiers, front-matter and language bars survive
translation intact.
Counts (check:docs-counts STRICT, 8 drifts → 0):
- 153 → 154 migrations in README.md, AGENTS.md and llm.txt (+ its 42 i18n mirrors,
which must stay byte-identical to the root file)
- 340 → 341 providers in README.md, AGENTS.md, llm.txt, the package.json description
and the 4 SVG diagrams; PROVIDER_REFERENCE.md regenerated via gen:provider-reference
(the new entry is the cloudflare-playground no-auth provider)
Undocumented environment variables:
- 13 CLI_*_BIN vars that exist in cliRuntime but were in neither ENVIRONMENT.md nor
.env.example (kilo, opencode, hermes, forge, jcode, deepseek-tui, codewhale, smelt,
pi, crush, omp, letta, windsurf — windsurf ships no default command)
- OMNIROUTE_DEBUG and OMNIROUTE_HEALTHCHECK_PATH, read in code but absent from
.env.example
- CLI_CURSOR_BIN documents both fallbacks (agent, then cursor)
Front-matter: bump the seven CLI/reference docs this effort touched from the stale
3.8.40/2026-06-28 stamp to the current release.
Out of scope but blocking check:docs-all, fixed with evidence: ENVIRONMENT.md and
.env.example still documented the Adobe Firefly CDP Chrome runtime removed in #9255.
Seven of its variables are read nowhere in the codebase and its source file no longer
exists; surviving vars are repointed at adobeFireflyBrowserLogin.ts and CHROME_PATH at
its real readers. The two Gemini CLI auth vars the run launcher scrubs from the child
env are added to the fabricated-docs external-tool allowlist, next to the existing
CODEX_HOME/COPILOT_PROVIDER_BASE_URL entries.
npm run check:docs-all now exits 0 for the first time on this base.
- ENVIRONMENT.md: CLI_ALLOW_CONFIG_WRITES default is true (matches cliRuntime),
CLI_QODER_BIN default is qodercli (also in .env.example), CLI_GEMINI_BIN is
server-side detection only (omniroute run resolves from PATH)
- CLI-TOOLS.md: catalog counts 26 code / 8 agents (adds the missing zcode row),
setup targets without auto-discovery list Qwen (not Gemini; Gemini is
launch-only), hostSetupCommand only for the six tools with a host recipe,
global env block uses GOOGLE_GEMINI_BASE_URL at the root, mention
omniroute run as the generic launcher
- CLI-INTEGRATIONS.md: manifest aliases, per-target --model wiring (openai/ and
omniroute/ prefixes, qwen hard-requires --model), run exit-code contract,
gemini child-env scrub notes
- CODEX-CLI-CONFIGURATION.md: document omniroute configure codex / run codex
- SETUP_GUIDE.md + QUICK-START.md: surface the generic omniroute run launcher
- ENVIRONMENT.md: disambiguate OMNIROUTE_SMOKE_API_KEY (canary) from the
OMNIROUTE_SMOKE_* CLI smoke-harness variables
A single orphaned "<<<<<<< HEAD" line (no matching =======/>>>>>>>
pair) leaked into release/v3.8.50 via PR #10039's merge-conflict
resolution during this session's serial-merge sweep. Repo-wide sweep
confirms no other stray markers exist. Table structure verified
intact before/after removal.
* feat(compression): target-wire OmniGlyph stage and transport fidelity gate
Roda o OmniGlyph depois da tradução para o wire real do provedor, em vez do
corpo de origem. Um cliente OpenAI roteado para Claude deixava de comprimir com
skip:source_format_not_claude porque o corpo ainda estava em formato OpenAI
quando a engine era avaliada.
- dispatch nativo por wire: Anthropic Messages, OpenAI Chat Completions e
OpenAI Responses (input[] preservado, sem achatar para messages[]);
- estágio target-wire pós-translateRequest, com guarda contra dupla compressão
no caminho Claude→OpenAI;
- preserveSystemPrompt do OmniRoute mapeado para compressSystem: false;
- imageTransportPolicy: fidelidade de bytes/dimensões separada de supportsVision;
só Anthropic/Claude tem recibo byte-preserving, o resto é fail-closed;
- contagem de tokens de data URL PNG no wire OpenAI (marcador ;base64,);
- README e i18n en/pt-BR com claims escopados ao caminho medido.
* feat(compression): adota omniglyph 1.4.0 e tira o gate de modelo da env do host
O 1.4.0 introduziu escopos de segurança e passou a resolvê-los dentro de
isOmniGlyphSupportedModel() lendo process.env.OMNIGLYPH_PROFILE. Somado ao
OMNIGLYPH_MODELS que já existia, duas variáveis do ambiente do host decidiam em
silêncio o gate de TODO request do OmniRoute: passthrough desligaria a engine
inteira e OMNIGLYPH_MODELS admitiria modelos sem recibo medido, enquanto a UI
segue prometendo "Claude Fable 5 na rota direta medida".
O adapter passa a usar isOmniGlyphSupportedModelForScope() com escopo explícito
e fixa o escopo mais restrito como teto: a env só pode ESTREITAR a allowlist,
nunca alargar. Os dois wires compartilham a mesma lista no pacote desde o
1.4.0, então uma checagem cobre Anthropic e GPT.
- omniglyph ^1.3.1 -> ^1.4.0 (lock em 1.4.0);
- testes de regressão para os dois caminhos de sequestro por env;
- teste de contrato dos exports novos (escopo, perfis, accounting).
O 1.4.0 também traz, sem mudança de código aqui: correção do glyph K que era
lido como H, remoção do backtracking polinomial no secret-guard, overrides do
pnpm em pnpm-workspace.yaml e as transitivas vulneráveis resolvidas.
* feat(compression): expõe os perfis semânticos do omniglyph nos três wires
O 1.4.0 trouxe perfis nomeados (coding-safe, balanced, aggressive,
passthrough), mas só transformAnthropicMessages() os resolve sozinho: os
transformadores OpenAI recebem TransformOptions cru e ignorariam o campo. Um
perfil escolhido pelo operador valeria no wire Claude e sumiria no OpenAI. O
adapter passa a mesclar o perfil com mergeCompressionProfileOptions() antes de
chamar Chat Completions e Responses.
O default segue aggressive — a política que os recibos publicados mediram.
Medido nesta base: com coding-safe/balanced, uma sessão sem histórico acumulado
para em below_min_chars e a engine não faz nada, porque os dois fixam
minCompressChars no máximo e desligam system/tools/tool-results. Como a engine é
opt-in, um default assim entregaria "ligado, 0% de ganho".
O perfil é TETO, não piso: mergeCompressionProfileOptions não deixa um override
do chamador reabrir uma lane lossy que o perfil fechou. Coberto por teste, por
ser contra-intuitivo.
Também fecha um caminho em que o OmniRoute violaria a própria política: o wire
OpenAI do pacote não tem compressSystem — honra apenas compressTools,
gptHistory, minCompressChars e reflow, e sempre troca a instrução por um
ponteiro para a imagem. Com preserveSystemPrompt ligado, imagear assim queimaria
o prefixo quente que a decisão cache-aware está protegendo, sem nada no corpo
devolvido denunciando. A engine agora pula com
skip:system_preservation_unsupported_on_wire.
* feat(compression): contabilidade física do omniglyph com grau de evidência
O adapter descartava o TransformInfo inteiro, então a UI mostrava um número de
economia sem dizer de onde ele vinha — contagem do provider, estimativa ou só
diferença de bytes. O 1.4.0 expõe normalizeAccounting(), que classifica essa
evidência e resolve a semântica de cache por família: Anthropic reporta input,
cache-create e cache-read em buckets DISJUNTOS, enquanto OpenAI e xAI reportam
cached como SUBCONJUNTO do input. Somar à mão dá double-count silencioso.
O novo omniglyphTelemetry.ts não filtra por denylist — MONTA um objeto novo,
campo a campo, só com número e enum. TransformInfo mistura contadores
inofensivos com material que não pode ser persistido: bytes PNG,
imageSourceText(s), recoverable[].text, os sha8 de system/CLAUDE.md/primeira
mensagem, nomes de tags observadas e o bloco env (cwd, branch, versões). Copiar
o objeto inteiro transformaria telemetria de compressão em vazamento de prompt.
O teste de negação prova que segredo, caminho do operador, texto do system e
base64 não aparecem, e varre a allowlist exigindo que toda string seja de um
enum conhecido.
- provider threaded do chatCore e do bridge Codex WS até a engine; ausente vira
`unknown`, que faz o upstream recusar adivinhar buckets de cache;
- contabilidade propagada para o engineBreakdown do passo (o agregado do
pipeline soma todas as engines e não serviria);
- skip não emite contabilidade: zeros ali seriam indistinguíveis de "a engine
nem rodou".
* feat(compression): perfil do omniglyph configurável, persistido e documentado
Fecha o caminho do operador: o perfil já existia no adapter, mas só como
default de código. Agora atravessa schema Zod, normalizador do banco, API de
settings e a página dedicada do engine.
- OmniglyphConfig tipado + omniglyphConfigSchema (z.enum dos quatro perfis);
- normalizeOmniglyphConfig: nome desconhecido vindo do storage cai para o
default em vez de virar "roda com a política padrão";
- seletor na página do engine, com PATCH próprio — o perfil vive fora do mapa
`engines`, e mandá-lo junto reescreveria o mapa inteiro (o store persiste o
mapa como uma linha JSON só);
- i18n en/pt-BR descrevendo o custo medido de cada perfil, não só o nome;
- README e COMPRESSION_ENGINES.md com a regra do teto e o motivo de o default
não ser o perfil mais seguro.
Corrige de passagem um teste-irmão que ninguém via: o gate de transporte na UI
deixou de dizer "direct Anthropic" quando os wires OpenAI nativos entraram, mas
tests/unit/ui/omniglyphContextPage.test.tsx continuou afirmando a cópia antiga.
O arquivo inteiro estava excluído do vitest.config.ts como "#8618 pre-existing
failure", então a quebra passou silenciosa. Com a asserção alinhada o arquivo
fecha 3/3, e a exclusão sai — o próprio comentário mandava removê-la quando
corrigida.
A doc não nomeia OMNIGLYPH_MODELS: o gate de docs fabricadas está certo em
apontar que o OmniRoute nunca lê essa env — quem lê é o pacote.
* fix(i18n): paridade do locale vi com as chaves novas do perfil do omniglyph
`tests/unit/i18n-vi-completeness.test.ts` exige paridade ESTRITA de chaves entre
en e vi — diferente do ratchet `i18n:check-ui-coverage`, que passa com 80%. As 11
chaves do seletor de perfil entraram só em en e pt-BR, e o gate de cobertura
seguiu verde, então a quebra só apareceu na matriz completa do CI.
---------
Co-authored-by: Xiangzhe <bakryun0718@proton.me>
Co-authored-by: adevwithpurpose <adevwithpurpose@users.noreply.github.com>
* test(sse): golden characterization of the executor map before the R0.3 registry refactor
Freezes the 137-entry provider-id → executor mapping (class, provider
identity, backing PROVIDERS config), the no-shared-instances invariant,
and the getExecutor() dispatch rules (memoized DefaultExecutor fallback,
cloud-agent guard #6699, search-provider guard #10274) as stable JSON
snapshots. The upcoming ExecutorRegistry must keep both snapshots
byte-identical.
* refactor(sse): route executor lookup through ExecutorRegistry (R0.3)
Adds open-sse/executors/registry.ts (Map-based registry mirroring
translator/registry.ts): the built-in table in executors/index.ts stays
declarative, every entry is registered at module load, and
getExecutor()/hasSpecializedExecutor() resolve through the registry.
DefaultExecutor fallback, its memoization, and the cloud-agent (#6699) /
search-provider (#10274) guards are unchanged.
Also fixes a latent lookup leak: the old object-literal lookup treated
Object.prototype names (constructor, toString, ...) as specialized
executors; the Map registry resolves them to the DefaultExecutor
fallback like any unknown provider.
Parity proof: executor-map golden (137 entries, byte-identical
before/after), check:known-symbols green, 1018 tests across the 65
executor test files green. Docs: OPEN_SSE_ARCHITECTURE factory section
corrected (it claimed generation from providerRegistry).
Refs #3501
* test(executors): regenerate ExecutorRegistry golden snapshots after release sync
release/v3.8.50 sunset mimocode and added cloudflare-playground + jina-search
since this PR's snapshots were captured; refresh the golden fixtures to match.
---------
Co-authored-by: Xiangzhe <bakryun0718@proton.me>
Co-authored-by: Markus Hartung <mail@hartmark.se>
Co-authored-by: adevwithpurpose <adevwithpurpose@users.noreply.github.com>
`assertValidEngine()` valida id, apply, compress, getConfigSchema e
validateConfig — não exige `metadata`. Uma engine registrada sem esse campo é,
portanto, um registro legal. Mas `canRunAtCompressionStage` lia
`engine.metadata.executionStages` sem guarda, então essa engine legal derrubava o
pipeline inteiro com `TypeError: Cannot read properties of undefined` em vez de
falhar aberto, que é o contrato da compressão.
Metadata ausente é o mesmo caso de "não declarou executionStages" e passa a cair
no mesmo fallback documentado: só pre-translation.
Isso destravava também `tests/unit/compression/pipeline-circuit-breaker.test.ts`,
que registra uma engine de teste sem metadata e vinha 8/9 na base — agora 9/9. O
teste novo torna o contrato explícito, em vez de deixá-lo dependendo de uma
reprodução incidental noutro arquivo.
Co-authored-by: Xiangzhe <bakryun0718@proton.me>
Co-authored-by: adevwithpurpose <adevwithpurpose@users.noreply.github.com>
* 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>
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>
_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.
@@ -3603,9 +3633,7 @@ export function createOmniRouteProviderHook(
constdedupeKey=`${cacheKey}::${comboKey}`;
if(!collisionWarned.has(dedupeKey)){
collisionWarned.add(dedupeKey);
console.warn(
`[omniroute-plugin] combo key "${comboKey}" collides with a model id; combo wins.`
);
logger.warn(`combo key "${comboKey}" collides with a model id; combo wins.`);
}
}
}
@@ -3623,8 +3651,8 @@ export function createOmniRouteProviderHook(
}
if(pending.length>0){
console.warn(
`[omniroute-plugin] ${pending.length} combo(s) could not resolve all nested combo-refs after ${MAX_COMBO_PASSES} passes; they will advertise context=0 to avoid over-claiming.`
logger.warn(
`${pending.length} combo(s) could not resolve all nested combo-refs after ${MAX_COMBO_PASSES} passes; they will advertise context=0 to avoid over-claiming.`
);
}
@@ -4268,8 +4296,10 @@ export function buildStaticProviderEntry(
@@ -4647,8 +4677,8 @@ export function buildStaticProviderEntry(
}
if(pendingStatic.length>0){
console.warn(
`[omniroute-plugin] ${pendingStatic.length} combo(s) in the static catalog could not resolve all nested combo-refs after ${MAX_STATIC_COMBO_PASSES} passes; they will be omitted.`
log.warn(
`${pendingStatic.length} combo(s) in the static catalog could not resolve all nested combo-refs after ${MAX_STATIC_COMBO_PASSES} passes; they will be omitted.`
);
}
@@ -4669,9 +4699,7 @@ export function buildStaticProviderEntry(
@@ -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.
that hard-verifies every auto-combo candidate against live quota state and per-connection
economic safety before it can be dispatched, going beyond `hidePaidModels`'s static catalog
check. Adds curated `hardStopGuaranteed` metadata to `FREE_MODEL_BUDGETS`, a short-TTL quota
cache reusing `getUsageForProvider()`, and a connection-safety guarantee: a candidate backed
by multiple accounts has its `allowedConnectionIds` narrowed to exactly the connections
independently verified `SAFE`, so dispatch can never use an unverified account. An
`excludeTosAvoid` guard (default `false`) is available separately for contractual risk. See
`docs/routing/STRICT_ZERO_COST.md`.
---
- feat(services): show sanitized CLIProxyAPI account health from its authenticated management API without exposing credentials, file paths, or raw account metadata (#6342)
- **feat(credential-health):** pace the credential health sweep per connection via `provider_connections.healthCheckInterval` (minutes, 0 = never), with `CREDENTIAL_HEALTH_CHECK_INTERVAL` as the global default ([#8443](https://github.com/diegosouzapw/OmniRoute/issues/8443))
- **behavior change:**`healthCheckInterval` is a shared column — it paces both the OAuth token refresh and the credential health sweep, and `0` disables both. The connection editor defaults it to 60, so configured OAuth connections are now credential-checked at 60min instead of the previous ~10min (aligned with the probe-volume goal of #8443)
- **feat(providers):** publish Poolside's Laguna Preview catalog statically — `poolside/laguna-xs-2.1` and `poolside/laguna-s-2.1` (262144 context, 32768 max completion, tools + reasoning, text-only), so the models are routable and visible before a key is configured instead of only after live discovery. Pins the catalog form of the XS id against the `laguna-xs.2` variant carried by third-party listings. ([#9085](https://github.com/diegosouzapw/OmniRoute/issues/9085))
- feat(modality-bridge): bridge Chat and Responses video parts through a strict trusted-loopback, quota-bounded FFmpeg broker; enforce HTTPS redirects/SSRF plus format, protocol, stream, pixel, frame, 50 MiB broker/remote, 36 MiB inline, and 120-second limits; propagate caller aborts; preserve the actual successful fallback model through cache/meta/headers; expose sampled latency and honest success telemetry; and ship the localized Video settings UI (#9760)
- **feat(radar):** Persist local model display-name/enabled overrides and hide/restore tombstones, with authenticated catalog controls and feed safety precedence ([#9830](https://github.com/diegosouzapw/OmniRoute/pull/9830))
- **feat(radar):** add curated-family combo suggestions, a guided combo page, and the read-only Radar MCP catalog tool ([#9836](https://github.com/diegosouzapw/OmniRoute/pull/9836))
- **feat(radar):** add a signed live offers feed and supporter offers dashboard ([#9912](https://github.com/diegosouzapw/OmniRoute/pull/9912))
- **feat(radar):** add signed Intel insights, supporter recognition, and local Radar CLI commands ([#9923](https://github.com/diegosouzapw/OmniRoute/pull/9923))
- **feat(radar):** add a localized public news feed and dismissible dashboard launch banner, with the Radar announcement staged inactive for a separately authorized launch ([#9926](https://github.com/diegosouzapw/OmniRoute/pull/9926))
- **feat(admission):** add lane-aware admission probes for combo/fusion/chaos fan-out (fail-open, queueing disabled), an env-wins `OMNIROUTE_CHAT_VIRTUAL_LANES` activation flag applied at boot, and adaptive-lane visibility in the `omniroute_get_health` MCP tool (related to #9654)
- **docs(mcp):** complete the MCP server README tool reference so the `schemas/` catalog is fully covered (agent-skills, oneproxy, web, tool-search, combo/routing, pricing and DB-health tools were previously only discoverable via `omniroute_tool_search`)
- **feat(cli):** container-aware auto-config — `setup-*`, `omniroute configure`, `omniroute config set` and the CLI-tool config APIs now refuse to write into a containerised OmniRoute's ephemeral home (CLI exits `2`, API returns `422` with `containerEphemeralTarget`) and point at the host-CLI or bind-mount setup instead; `--allow-container-write` / `OMNIROUTE_ALLOW_CONTAINER_CONFIG_WRITE=true` opt back in. Also fixes `CLI_CONFIG_HOME` so the Compose `host` profile's `/host-home` bind mounts are honoured instead of silently falling back to the container home. (#10057)
- feat(dashboard): opt-in `DASHBOARD_ALLOW_EMBED=vscode` relaxes CSP `frame-ancestors` to `'self' vscode-webview:` and drops `X-Frame-Options` for HTML pages only, so the dashboard renders inside the VS Code Simple Browser (OmniCopilot). Default posture unchanged — API routes stay unframable (#10273)
- **feat(resilience):** warn when `/healthz` is served under event-loop lag ≥200ms so a slow 200 is visible as sick, not healthy ([#10303](https://github.com/diegosouzapw/OmniRoute/issues/10303))
- **feat(docker):** add `GET`/`HEAD``/livez` as a process-alive probe, distinct from `/healthz` readiness ([#10316](https://github.com/diegosouzapw/OmniRoute/issues/10316))
- feat(providers): add **Cloudflare AI Playground** as a No Auth provider (`cloudflare-playground`, alias `cfp`) — free anonymous chat over the reverse-engineered `cf_agent` WebSocket protocol (PartySocket transport, no account/API key/cookies) with GLM 5.2, Kimi K2.7 Code, DeepSeek V4 Pro, gpt-oss-120B, Llama 3.3 70B, Qwen2.5 Coder 32B and 14 more curated models. The executor drives a headless Chromium via Playwright (the WS upgrade is TLS-fingerprint-gated), translates the `cf_agent` frame stream into OpenAI SSE, and surfaces upstream rate limits (3021) as HTTP 429. Fixes #10389
- **feat(providers):** AI Horde accepts an optional registered API key and advertises only live image models that currently have workers ([#10542](https://github.com/diegosouzapw/OmniRoute/pull/10542))
- **fix(providers):** AI Horde Check validates keys via `/v2/find_user` instead of the unauthenticated OpenAI models list ([#10542](https://github.com/diegosouzapw/OmniRoute/pull/10542))
- **feat(audio):** proxy native ElevenLabs voices, text-to-speech, and speech-to-text HTTP routes through stored OmniRoute credentials, preserving query strings, multipart uploads, binary responses, and upstream errors (#10556).
- **feat(providers):** complete Jina AI as one credential pool — dashboard `jina-ai` / `jina-reader` share a token, `JINA_AI_API_KEY` is a real fallback, Test probes `GET https://api.jina.ai/v1/models` (embeddings fallback hits `jina-embeddings-v5-omni-small`), embed/rerank logs keep `connection_id`, catalog adds `jina-reranker-v3.5`, Omni v5 multimodal `{text}`/`{image}`/`{content}` docs pass through intact, and OmniRoute proxies classify / segment / `jina-search` (`s.jina.ai`). Reader stays a separate `r.jina.ai` card with an explicit label. Gemini Embedding 2 (`gemini/gemini-embedding-2`, alias `google/gemini-embedding-2`) uses dashboard `gemini` keys (or `GEMINI_API_KEY` / `GOOGLE_API_KEY` only when none exist), forwards native multimodal parts, and maps N OpenAI `input` items to N `:batchEmbedContents` vectors instead of one aggregated `:embedContent`. ([#10581](https://github.com/diegosouzapw/OmniRoute/pull/10581))
- **feat(providers):** accept `response_format=ogg` on `/v1/audio/speech` as an alias for the existing Opus/Ogg encoder ([#10587](https://github.com/diegosouzapw/OmniRoute/issues/10587))
- Added Google AI Studio Gemini batch text-to-speech support through `POST /v1/audio/speech`.
- **feat(settings):** add `autoDisableBannedScope` so permanent-ban auto-disable can target subscription/OAuth accounts only, leaving prepaid API keys in the routing pool ([#10617](https://github.com/diegosouzapw/OmniRoute/pull/10617))
- feat(server): emit systemd sd_notify READY/WATCHDOG/STOPPING (generated unit becomes Type=notify with WatchdogSec=180) so a frozen server process is killed and restarted by systemd instead of lingering undetected
- **feat(providers):** add the TabiToken NewAPI gateway (`tabitoken`) and teach the existing HCNSec entry (`hcnsec`) the three further protocols it actually serves. TabiToken leaves the NewAPI pricing endpoint public, so its catalog is read from the host rather than guessed: four Claude models, each reporting the Anthropic and OpenAI protocols. HCNSec shipped OpenAI-only; probing the host showed `/v1/messages`, `/v1/responses` and the Gemini `/v1beta` path all reach its token layer, so each is now declared as an alternate format — with its default format, base URL, auth scheme and regional catalog classification untouched. ([#10668](https://github.com/diegosouzapw/OmniRoute/pull/10668)) — thanks @yawar-aquil
- **feat(sse):** allow an alternate protocol to build its own upstream URL. `AlternateFormat` gained an optional `urlBuilder`, because the Gemini protocol carries the model inside the path (`{base}/{model}:generateContent`) and the existing `chatPath`/`urlSuffix` fields are constants that cannot express it. The route builder is extracted as `buildGeminiGenerateContentUrl` and shared with the native `gemini` provider so the two consumers cannot drift on the `?alt=sse` streaming suffix. ([#10668](https://github.com/diegosouzapw/OmniRoute/pull/10668)) — thanks @yawar-aquil
- **feat(call_logs):** persist the per-call error family in `call_logs.error_type` and expose a failure breakdown (`errorBreakdown`) in the usage analytics endpoint, reusing the existing production classifier ([#10670](https://github.com/diegosouzapw/OmniRoute/issues/10670))
- **feat(proxy):** the proxy-health sweep and `GET /api/settings/proxies/egress` now report an anonymous summary of egress-IP sharing — how many rotation groups share an egress IP and the largest number of accounts behind one IP — computed from persisted `proxy_logs` over a 24h window. No IPs and no account identities by default; `PROXY_LOG_INCLUDE_IPS=true` restores raw details. ([#10677](https://github.com/diegosouzapw/OmniRoute/issues/10677))
- **docs(guides):** OmniRoute now serves VS Code's **native Copilot Chat model picker** through the [OmniCopilot](https://github.com/diegosouzapw/OmniCopilot) extension ([Marketplace](https://marketplace.visualstudio.com/items?itemName=diegosouzapw.omnicopilot) · [Open VSX](https://open-vsx.org/extension/diegosouzapw/omnicopilot) — Cursor, Windsurf, VSCodium, Theia…) — no Copilot subscription needed since VS Code 1.122. New [`docs/guides/VSCODE-COPILOT.md`](docs/guides/VSCODE-COPILOT.md) covers setup, how the picker collapses the `dual`-prefix catalog via `GET /v1/models?prefix=alias`, and the **build-time**`DASHBOARD_ALLOW_EMBED=vscode` flag that renders the dashboard in an editor tab ([#10697](https://github.com/diegosouzapw/OmniRoute/pull/10697))
- **feat(docker):**`DASHBOARD_ALLOW_EMBED` is now a Docker build argument — `docker build --build-arg DASHBOARD_ALLOW_EMBED=vscode` produces an image whose dashboard renders inside the VS Code Simple Browser (OmniCopilot's `dashboardOpen: "editor"`). Previously the flag was only reachable from a source build: Docker silently drops a `--build-arg` with no matching `ARG`, so the operator got the default image and no error. Builder-stage only and empty by default — the runtime stages deliberately do not carry it, and the unframable default posture is unchanged ([#10701](https://github.com/diegosouzapw/OmniRoute/pull/10701))
- **feat(providers):** new `cursor-api` provider (card "Cursor API", alias `cua`): connect a Cursor user API key (`crsr_…`) and route `cursor-api/<model>` through the existing Cursor agent executor (the key is exchanged for a 1h session token and cached), plus a `/api/cursor-cli/*` passthrough so the Cursor CLI itself runs through OmniRoute (`CURSOR_API_ENDPOINT=http://<omniroute>/api/cursor-cli`, `CURSOR_API_KEY=<OmniRoute key>`) with every RPC attributed and logged. The IDE `cursor` provider is unchanged. (#10729)
- **feat(api):**`GET /api/health` now answers `{ status, timestamp }` without a key. Until now the path had no route, so the management-auth boundary answered first with a 401 — indistinguishable from a wrong key or an unknown route, which left Docker HEALTHCHECKs and Kubernetes probes unable to tell "down" from "misconfigured". Kept deliberately minimal: version, uptime and memory stay behind the authenticated `/api/monitoring/health` ([#PRNUM](https://github.com/diegosouzapw/OmniRoute/pull/10771)).
- feat(routing): make Task-Aware Smart Routing's detection patterns operator-configurable via `settings.taskRouting.patternOverrides` (`PUT /api/settings/task-routing`) — the built-in patterns are English-only, so a non-English dashboard had no recourse short of turning detection off entirely; an override now replaces the pattern list for one task type without touching the rest (#10783)
- feat(api): accept PATCH on /api/combos/[id], the verb the OpenAPI spec already documents (#10869)
- **feat(sse):** add GLM-5.3 support (`glm-5.3`, `glm-5.3-high`, `glm-5.3-low`) across the z.ai first-party providers, mapping the upstream `reasoning_effort` request parameter to the existing 5.2 tier UX ([#10896](https://github.com/diegosouzapw/OmniRoute/pull/10896)) — thanks @phuongddx
- **feat(home):** add a live **Recent Requests** panel beside the home Provider Topology (polls `GET /api/usage/call-logs?excludeTests=1` every ~3s, gated by the topology appearance toggle + page visibility). `excludeTests` is now an allowlist of real provider inference (`/v1/%` or `/api/v1/%`), applied before `LIMIT`, so connection-test/model-sync/management rows can never leak into the feed ([#10897](https://github.com/diegosouzapw/OmniRoute/pull/10897), extracted from [#8450](https://github.com/diegosouzapw/OmniRoute/pull/8450)) — thanks @nguyenha935
- **feat(rankings):** free provider rankings now expose a `reliability` field (raw `testStatus`/`rateLimitedUntil` per connection plus a `healthy`/`degraded`/`down` state, reusing the `ProviderHealthState` vocabulary of the provider health matrix) when the configured/available filters are active — derived from already-loaded data, without touching the ranking order ([#10909](https://github.com/diegosouzapw/OmniRoute/pull/10909))
- `feat(resilience)`: when an allowlisted provider (opencode family) answers
429 classified `quota_exhausted` or `rate_limit_exceeded` and its free-tier
quota is bucketed by egress IP (#9611), every connection of that family
sharing the IP is cooled down together before the rotation tries them — one
guaranteed-failed upstream call per episode instead of N, on the combo path
as well. For the allowlisted family a 429 now cools the connection instead
of locking a single model. Exclusive allowlist, never terminal, best-effort
when the egress IP is unknown (#10920).
- **feat(rankings):** free provider rankings can now report what each provider actually served — `reliability.usage` (requests, successes, success rate over a window) behind the opt-in `withUsage`/`usageRange` query parameters, so a provider that answers every call with an error is no longer described as healthy ([#10926](https://github.com/diegosouzapw/OmniRoute/pull/10926))
- **feat(providers):** add Logfare as a free OpenAI-compatible provider — dashboard card with a Free badge and request-logging disclosure (every prompt/completion is logged for research; opt out at logfare.ai/consent), live model discovery from `https://logfare.ai/v1/models` (20 models, 11 chat-capable: kimi-k3, deepseek-v4-pro, glm-5.2, gpt-5.6-luna, minimax-m3…), full chat/streaming through the existing OpenAI-compatible path, the real Logfare logo on the card, and a listing in the free-tiers guide. ([#10987](https://github.com/diegosouzapw/OmniRoute/pull/10987))
- Run synchronous RTK and Caveman request compression in a bounded worker-thread pool, keeping
large `/v1/responses` compression heaps outside the HTTP isolate while preserving strict
fail-open behavior and per-engine telemetry.
- **feat(providers):** let operators declare per-provider error rules through `settings.providerErrorRules` instead of patching the catalog — an operator-supplied rule for a provider is consulted before the built-in `providerRuleRegistry`, receives the raw error text, and has its declared scope/cooldown/reason actually honored end to end, for any provider (declaring the rule is the opt-in — no extra allowlist entry needed). Matches are plain case-insensitive substrings (never RegExp) and bounded to 50 rules to keep the hot path safe ([#11104](https://github.com/diegosouzapw/OmniRoute/pull/11104))
- **feat(combo):** the shared per-request combo attempt budget is now operator-configurable via `maxGlobalAttempts` (combo config / `comboDefaults` cascade), instead of the hardcoded 30. Lower it to fail fast on a dead target pool, raise it for large combos; clamped to `[1, 200]` so an unbounded budget can never cause runaway background requests ([#11134](https://github.com/diegosouzapw/OmniRoute/issues/11134))
- **feat(api):**`/api/usage/om-usage` gains a structured form — `?format=json` returns the key's own usage as `ApiKeyUsageLimitStatus` + `UsageSnapshot` instead of `text/plain`. This is the surface a UI (the OmniCopilot panel) consumes to show a key holder their daily/weekly spend and quota reset. The route is self-service (the caller's own key, gated by `allowUsageCommand`), not the management surface; refusals come back as a discriminated `{ "allowed": false, "error": … }` so a UI can tell "not allowed" apart from "allowed but nothing cached yet". The endpoint was previously undocumented in `API_REFERENCE.md`; it now has a section ([#11190](https://github.com/diegosouzapw/OmniRoute/pull/11190))
- **feat(api):**`/api/usage/om-usage?format=json` now returns `providers[]` — every connection's quota snapshot, not just the single selected one — so a panel can render Codex / Claude / OpenCode side by side. The collector already gathered all of them; the single-pick `provider` field (kept) is a terminal presentation choice. Closes the per-connection gap from OmniCopilot #8 ([#11192](https://github.com/diegosouzapw/OmniRoute/pull/11192))
- **feat(providers):** allow overriding the rate-limit queue wait timeout (`maxWaitMs`) per connection, alongside the existing `rpm`/`tpm`/`tpd`/`minTime`/`maxConcurrent` overrides — a single slow provider no longer has to lower the global wait budget for every other provider (#11251)
- **feat(dashboard):** replace the hard Home → onboarding redirect with a dismissable first-run readiness card so returning users can stay on Home while new users still get a clear 4-step path ([#11282](https://github.com/diegosouzapw/OmniRoute/pull/11282))
- **feat(dashboard):** lead Traffic Inspector with a purpose-first header that separates "what happened" from "how it happened", so beginners can read request outcomes without drowning in protocol detail ([#11283](https://github.com/diegosouzapw/OmniRoute/pull/11283))
- **feat(dashboard):** add an Essentials sidebar preset that shows only the beginner core path (Home → Endpoints → API Keys → Providers → Health → Settings) while keeping Advanced tools reachable via Command Palette search ([#11286](https://github.com/diegosouzapw/OmniRoute/pull/11286))
- **feat(video bridge):** harden the optional drill-down cache substrate with exact-path broker policy, canonical principal/session/media isolation, independent retained-byte quotas, cancellation-safe commits, rejection of excess or non-canonical Base64 padding and non-JPEG/truncated media, warning-sensitive full JPEG canonicalization that strips trailing polyglot bytes, server-derived dimensions, and auditable derivation metadata; production tenant binding and multi-resolution selection remain follow-up work ([#11369](https://github.com/diegosouzapw/OmniRoute/pull/11369))
- **feat(video):** add an opt-in focused analysis mode that safely uses a normalized, 500-code-point latest-user hint for task-aware frame captions while preserving full-mode prompts, temporal-window isolation, and cache identity without storing raw task text ([#11383](https://github.com/diegosouzapw/OmniRoute/pull/11383)).
- feat(command-code): advertise low/medium/high/xhigh/max reasoning-effort suffixes for reasoning-capable models in the catalog and Combo Builder, with request-time resolution to reasoning_effort
- feat(crof): advertise reasoning-effort tiers (none/low/medium/high/max) for live-discovered and seed models, so the catalog, Playground, and Combo Builder surface <model>-<tier> aliases and requests resolve max upstream
- feat(sse): add Cursor plan image generation via Agent CLI (`IMAGE_PROVIDERS.cursor`, format `cursor-agent-image`), reusing the chat Cursor OAuth connection
- feat(routing): add the default-off `DISABLE_CONTEXT_WINDOW_CHECKS` feature flag to let operators bypass OmniRoute's local context-window and max-input-token check for direct single-model requests, leaving upstream limits, prompt compression, and output-token caps intact.
- **feat(catalog):** surface runtime-learned `reasoning_effort` tiers in `/v1/models``capabilities.effort_tiers` (learned set replaces synced metadata when present), map them to OpenCode `ModelV2.variants` in the OmniRoute plugin, and align dispatch `-<tier>` suffix validation to the effective (learned ?? synced) set — so the UI offers exactly the tiers the upstream accepts (e.g. `{low, high, max}` for `oc/x-preview-f-free`) and each advertised variant completes. Excludes codex/glm/kimi, which keep their own dedicated `-{effort}` suffix mechanism and never gain `effort_tiers` from this path (related to #7694, builds on #11232)
- **feat(usage):** show Kimi Coding's fixed-order Code 5-hour/7-day quota windows plus Extra Usage status, balance, monthly spend/limit, and the official Additional Credits link on Dashboard → Quota cards.
- **feat(providers):** copilot-m365-web now supports OpenAI tool calling — a router planning turn asks the substrate model (as a tool-selection assistant emitting `CALL_TOOL: name({...})` / `NO_TOOL_NEEDED` text, which bypasses its plugin-registry refusal) and validated decisions surface as `tool_calls` with `finish_reason: "tool_calls"` in both stream and non-stream modes; also flattens the full message history (assistant `tool_calls` + compacted tool results) so multi-turn agent loops keep context, replies to SignalR `type:6` keepalives, surfaces `type:3` error frames instead of a silent empty `stop`, and suppresses `writeAtCursor` text from tool-progress frames
- **feat(api):** add `GET`/`POST``/v1/multimodal-embeddings` as an alias of `/v1/embeddings` so Jina-compatible clients do not receive HTTP 404 `unknown_route` — thanks @RaviTharuma
- feat(opencode-go): expose Muse Spark 1.2 Contributor reasoning-effort aliases (minimal/low/medium/high/xhigh) in the Combo Builder
- **feat(providers):** restore the operator-owned upstream timeout tier per connection via `providerSpecificData.timeoutMs` (preempts the maintainer-only model/provider registry tiers and the global `FETCH_TIMEOUT_MS`), and make the combo per-target timeout ceiling follow the selected connection
- **feat(cli):** run `omniroute serve --tray` as a detached desktop process after server and tray readiness, with graphical login auto-start support.
- **feat(routing):** add client-, provider-, and model-neutral exclusive managed session connection leases with API-key-bound generation fencing, durable SQLite ownership, explicit allowlist policy, and bounded 429 capacity retry semantics.
## [3.8.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._
### ✨ New Features
- **feat(search):** first-class X Search provider (`x-search`) on `POST /v1/search` and MCP `omniroute_x_search` using SuperGrok / xAI server-side `x_search`. Explicit provider or `search_type: "x"` only — never auto-selected for web. Reuses `xai-oauth` / `xao` / `xai` credentials. Not the X Developer Platform MCP. ([#10985](https://github.com/diegosouzapw/OmniRoute/issues/10985))
- **feat(core):** add Layer A capability filter at router (#5696)
- **feat(providers):** add DeepAI as paid API-key image provider ([#6671](https://github.com/diegosouzapw/OmniRoute/issues/6671))
<!-- 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):** 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(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(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)
- **executors**: fix internal timeout misclassified as client disconnect (499) for 7 niche executors — pass TimeoutError reason to controller.abort() (#8197 side-finding)
- test(combo): guard auto/best-free never leaks the combo name as a model (#7754)
- fix(vision-bridge): describe-model no longer returns unreachable "openai/gpt-4o-mini" when every vision-capable provider is unreachable on the instance — returns null instead and surfaces a clear error (#8430)
@@ -627,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 (2–3s 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(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(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 model’s 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(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.6–2.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))
- **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)
- **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; 8–12GiB 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 1–1,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
- **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.
`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
@@ -784,7 +1364,10 @@ Thanks to everyone whose work landed in v3.8.50:
<img src="./docs/diagrams/readme-hero.svg" width="100%" alt="OmniRoute — Never stop coding. Every AI tool → 340 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 15–95% tokens (~89% avg) — never hit limits. 340 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 → 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 15–95% tokens (~89% avg) — never hit limits. 350 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)**.
<img src="./docs/diagrams/promise-pillars.svg" width="100%" alt="The Promise — One endpoint. 340 providers. Never stop building — OmniRoute picks the cheapest one that works. Six pillars: Never hit limits (auto-fallback across 340 providers in milliseconds, zero downtime) · Save up to 95% tokens (RTK + Caveman stacked compression cuts 15–95%, ~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 109 tools, A2A, memory, guardrails, evals — 25,000+ tests)."/>
<img src="./docs/diagrams/promise-pillars.svg" width="100%" alt="The Promise — One endpoint and 350 providers. Automatic fallback keeps routing while another healthy target is available. Six pillars: resilient fallback across 350 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."/>
<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."/>
<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: 340 providers, 90+ free providers built-in, 19 routing strategies, 12-engine token compression, built-in MCP server with 109 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'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: 350 providers, 90+ free tiers builtin, 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 & per-feature detail vs 9router, OpenRouter, CLIProxyAPI & 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)
- **⚖️ 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)
@@ -557,9 +557,9 @@ the current catalog at **[radar.omniroute.online/planos](https://radar.omniroute
- **🧠 Memory you control** — off by default, opt-in int8 vector quantization + typed decay, per-request `x-omniroute-no-memory`. → [Memory](docs/frameworks/MEMORY.md)
- **🛡️ Security** — prompt-injection guard on every LLM route (red-team suite), opt-in credential-masking guardrail (redacts leaked API keys/secrets in both directions), free DuckDuckGo last-resort web search, and an optional OIDC login gate for the dashboard (password login always stays available). → [Guardrails](docs/security/GUARDRAILS.md)
- **🖼️ 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, Google Imagen, Segmind, EdgeTTS. → [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)
- **🤝 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 **340-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 **350-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)
@@ -612,7 +612,7 @@ the current catalog at **[radar.omniroute.online/planos](https://radar.omniroute
<b>+ also works with</b> · Kiro · Command Code · Antigravity · Windsurf · AMP · <b>any OpenAI-compatible tool</b>
@@ -642,11 +642,11 @@ of your shell history. → [CLI Integrations](docs/guides/CLI-INTEGRATIONS.md)
<div align="center">
## 🌐 340 AI Providers — 90+ Free
## 🌐 350 AI Providers — 154 Catalog-Marked Free
</div>
> The most complete catalog of any open-source router: **340 providers**, **90+ with a free tier**, **56 freeforever**.
> **350 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, upstreamheader 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."/>
### 📖 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,000tokens 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:
@@ -877,7 +877,7 @@ Engines run in pipeline order; each is independently toggleable and configurable
<tr><td align="center" nowrap>9</td><td align="left" nowrap><b>Aggressive</b></td><td align="left">Summarization + progressive aging of old turns</td></tr>
`:latest` follows the highest **published** stable SemVer. It does not track git `main`. Pin `:X.Y.Z` for GitOps. See [Docker Release Channels](docs/guides/DOCKER_GUIDE.md#release-channels).The image pins **`OMNIROUTE_MEMORY_MB=1024`**. That is enough for the dashboard and a light chat. **Coding agents** (`POST /v1/responses` from Claude Code, Codex, Grok, …) need a much larger V8 heap or the process `FATAL ERROR`s at ~12GiB under two overlapping long contexts. Size the container above the heap (native buffers sit outside V8):
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 .`).
```bash
# Install and run with Bun
bun install
bun run dev
```
**🛠️ From source**
```bash
@@ -1076,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>
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.
@@ -1172,7 +1202,7 @@ Métricas de validação: 1002 vídeos rastreados · 7,069,190 visualizações c
<tr><td nowrap><b>Language</b></td><td>TypeScript 6.0 — <b>100% TypeScript</b> across <code>src/</code> and <code>open-sse/</code> (zero <code>any</code> in core since v2.0)</td></tr>
@@ -1233,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/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/>
@@ -1273,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.**
<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>
<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>
@@ -1376,25 +1436,48 @@ A heartfelt thank-you to the people who fund OmniRoute out of their own pocket
<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>
@@ -1403,11 +1486,13 @@ A heartfelt thank-you to the people who fund OmniRoute out of their own pocket
<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
@@ -1424,7 +1509,8 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for detailed guidelines.
```bash
# Create a release — npm publish happens automatically
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/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/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), current with GCF spec v3.2.</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/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><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/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/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/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/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
@@ -1560,7 +1666,7 @@ MIT License - see [LICENSE](LICENSE) for details.
**[⬆ Back to top](#-omniroute)** · Built with ❤️ for the open-source AI community.
returnwarn("CLI machine token","CLI machine-token authentication is disabled",{
derived:false,
accepted:false,
disabled:true,
tokenExposed:false,
});
}
leturl;
try{
constparsed=newURL(resolveLivenessUrl(options));
if(
!["http:","https:"].includes(parsed.protocol)||
parsed.username||
parsed.password||
!isLoopbackUrl(parsed.toString())
){
returnwarn(
"CLI machine token",
"Machine-token probes are limited to HTTP(S) loopback endpoints",
{derived:false,accepted:false,tokenExposed:false}
);
}
parsed.pathname="/api/cli/whoami";
parsed.search="";
parsed.hash="";
url=parsed.toString();
}catch{
returnwarn("CLI machine token","Could not resolve the management endpoint",{
derived:false,
accepted:false,
tokenExposed:false,
});
}
consttoken=awaitgetCliToken();
if(!token){
returnfail(
"CLI machine token",
"Could not derive a machine token; verify the node-machine-id runtime is installed",
{derived:false,accepted:false,tokenExposed:false}
);
}
try{
constresponse=awaitfetchWithTimeout(url,{
headers:{[CLI_TOKEN_HEADER]:token},
redirect:"error",
});
if(response.ok){
returnok("CLI machine token","Server accepted the local machine token",{
url,
status:response.status,
derived:true,
accepted:true,
tokenExposed:false,
});
}
if(response.status===401||response.status===403){
returnwarn(
"CLI machine token",
"Server rejected the local machine token; if the CLI and server are on different hosts or container boundaries, run `omniroute connect <host> --key <oma_live_...>`",
{
url,
status:response.status,
derived:true,
accepted:false,
containerBoundaryLikely:true,
tokenExposed:false,
}
);
}
returnwarn("CLI machine token",`Machine-token probe returned HTTP ${response.status}`,{
url,
status:response.status,
derived:true,
accepted:false,
tokenExposed:false,
});
}catch{
returnwarn("CLI machine token","Machine-token endpoint could not be reached",{
- **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(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(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(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(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 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(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(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
- **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: 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(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(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)
- **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(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))
- **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)
Some files were not shown because too many files have changed in this diff
Show More
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.