Compare commits

..

15 Commits

Author SHA1 Message Date
diegosouzapw
1c351cc6b9 fix(security): embed the Raycast signature secret via resolvePublicCred (HR#11)
The secret-scan ratchet only ran once the earlier Fast Quality Gates steps
stopped failing, and it lands at 1 finding vs baseline 0.

The finding is open-sse/services/raycast.ts:19 —
RAYCAST_DEFAULT_SIG_SECRET, a 64-hex request-signature secret that #8895
committed as a bare string literal. It is genuinely public (community-extracted
from the Raycast macOS client; the SAME value ships to every install, it is not
a per-user credential), which is exactly the category Hard Rule #11 governs:
public upstream credentials MUST go through resolvePublicCred()
(open-sse/utils/publicCreds.ts), never a literal — see
docs/security/PUBLIC_CREDS.md.

So the fix is the mandated pattern, not a .gitleaks.toml allowlist entry: added
`raycast_sig_secret` to EMBEDDED_DEFAULTS as the XOR-masked byte sequence and
resolved it with the existing RAYCAST_SIG_SECRET env override. The
providerSpecificData.sigSecret override is untouched. Verified the decoded value
is byte-identical to the literal it replaces.

check:secrets secretFindings 1 -> 0. check:public-creds exit 0.
publicCreds 12/12, raycast-auth 6/6, raycast-local-extract 1/1,
trae-publiccred 3/3. typecheck:core exit 0.

Refs #9298
2026-08-07 08:12:27 -03:00
diegosouzapw
9d353217f1 fix(db): drop the never-wired getSessionModelUsageCounts (knip regression)
The dead-code ratchet only ran once the earlier Fast Quality Gates steps stopped
failing, and it lands at 228 vs baseline 227.

The extra symbol is src/lib/db/contextHandoffs.ts::getSessionModelUsageCounts,
added by #8894 "for least-used strategy" and never wired: the least-used branch
in applyStrategyOrdering.ts uses the pre-existing sortTargetsByUsage(), and the
helper has no caller in src/, open-sse/ or tests/. It is the same incomplete-PR
shape as that PR's import of a module which does not exist in the repo (fixed
earlier in this branch).

Removed rather than baselined — bumping the ratchet would loosen the gate, and
removal is exactly the remedy the gate prescribes. Same treatment the Dario
installer's never-wired uninstall() got in #9600. The implementation is
recoverable from a598fbb090 whenever someone actually wires a session-aware
least-used strategy.

check:dead-code 228 -> 227 (baseline untouched). check:db-rules exit 0.
context-handoff 13/13, db-context-handoffs 7/7, service-context-handoff 11/11.

Refs #9298
2026-08-07 08:01:24 -03:00
diegosouzapw
16904141f7 chore(stryker): register the two covering suites missing from tap.testFiles
check:mutation-test-coverage flags any unit test that covers a mutated module but
is absent from stryker.conf.json tap.testFiles — without the entry its mutant
kills do not count toward the module's score.

- tests/unit/antigravity-prefer-stored-project.test.ts covers
  open-sse/services/combo/quotaStrategies.ts (added earlier in this PR).
- tests/unit/executor-devin-cli-agentic-acp.test.ts covers
  src/sse/services/auth.ts — pre-existing drift, same gate, same fix.

Inserted in alphabetical position only; the rest of the file is byte-identical
(it is not prettier-formatted upstream and reformatting it is out of scope here).

Refs #9298
2026-08-07 07:39:40 -03:00
diegosouzapw
18717aa6b8 fix(db): restore node-backed synced catalogs and realign the #8944 context hints
**Production regression from #9294 (d69f521491)**

lookupModelMeta moved from getSyncedAvailableModels(providerId) to
getActiveSyncedCatalog(providerId). The new reader unions models only from rows
in `provider_connections` with isActive = 1 — but a provider NODE lives in
`provider_nodes` and NEVER has a connections row, so filtering by active
connection ids silently dropped every node's synced catalog.

The consequence was not just a missing list: lookupModelMeta reads that catalog
for RUNTIME METADATA, so for openai-compatible nodes it took out
- `supportedThinkingEfforts`, which is what splitSyncedEffortSuffix needs — so
  `<prefix>/<model>-high` stopped resolving to the base id and the effort was
  never derived (#7694), and
- `contextWindow` / `maxInputTokens`, used by the combo context-window filter.

getActiveSyncedCatalog now falls back to the provider-wide key_value set — the
exact pre-#9294 source — when no active connection carries a catalog, and marks
that fallback explicitly NON-authoritative. #9294's live-catalog gating is about
what an active connection actually serves, so a node-backed catalog informs
metadata while never being able to reject a model as unavailable. `available`
therefore stays fail-open for nodes, as it was before.

sync-reasoning-supported-efforts-7694 23/23 (was 21/2).
live-model-catalog-reconciliation-8926 11/11 and combo-provider-wildcard 23/23
confirm #9294's own coverage is untouched.

**#8944 sibling-test drift**

714a315a1a ("Treat context metadata as a routing hint") deliberately turned the
context-window check from a HARD filter into an ordering hint: a catalog-too-small
target is demoted, not removed, because a stale catalog entry must never delete
the only target that could accept the request at runtime. The PR updated one case
in this suite and left three asserting the old drop behaviour. Realigned to the
new contract — the too-small target must lose the ordering to the fitting one
while remaining present — and renamed them from "still rejects"/"still dropped"
to "is demoted"/"ordered last" so the names stop describing the removed
behaviour. 14/14.

**file-size**

tests/unit/translator-openai-to-gemini.test.ts testFrozen 1616 -> 1619: the
frozen value sat exactly at the base size, so the 3 lines the previous commit's
_toolNameMap alignment needs could not fit. Justified in the baseline.

typecheck:core exit 0.

Refs #9298
2026-08-07 07:02:12 -03:00
diegosouzapw
744ab48f1c test(base): run the orphaned #8890 suite and realign three mechanism pins
**check:test-discovery — a suite that had NEVER executed**

#8890 landed open-sse/services/__tests__/fail-fast-concurrency-gate.test.ts into
a directory no runner collects (only one explicit file from that folder is in
vitest.mcp.config.ts), so it ran zero times since it merged. Wired it into the
runner AND into check-test-discovery.mjs's mirrored collector list, which the
gate keeps in sync deliberately. It passes 4/4 now that it actually runs —
test:vitest goes 36 -> 37 files, 340 -> 344 tests.

**check-db-rules-classification** — 37 -> 38 audited modules, adding probeUtils
alongside the INTENTIONALLY_INTERNAL entry from the previous commit.

**ratelimit-reservoir-refresh** — #9604 (rolling RPM leases) DELETED Bottleneck's
fixed-window reservoir, so currentReservoir() is null and the poll for
`reservoir === 2` could never settle. It updated several sibling suites but not
this one. The pin on the removed mechanism is gone; what remains is the
invariant the original Bottleneck heartbeat bug actually broke and that #9529
opened this test for — after a header-learned updateSettings() the limiter must
keep admitting work, proven by racing a post-exhaustion request against a 5s
timer. 1/1.

**translator-openai-to-gemini** — #9568 (c9a3361e5a) made
buildChangedToolNameMap emit IDENTITY entries too, because Gemini lowercases
tool names in functionCall responses and the response translator needs a key to
map them back. Any request carrying tools therefore carries `_toolNameMap` in
the Antigravity envelope now. Expected key list updated and the map's contents
asserted explicitly rather than left implicit. 45/45.

Refs #9298
2026-08-07 06:12:43 -03:00
diegosouzapw
db7c066f87 fix(combo,usage,oauth): drain the base-reds the shard fix exposed
With the migration collision and the broken import out of the way the four unit
shards actually run, and a further layer of base-reds became visible on the pure
tip 9995bc4893. Three are production defects.

**Production defects**

- open-sse/services/combo/runtimeUnitCapacity.ts:58 called resolveComboTargets()
  WITHOUT the hidden-model snapshot, so it fell back to the default
  getHiddenModelsByProvider() — a fresh full key_value read PER nested combo-ref
  unit, on every request. #8878 threaded the snapshot through the other call
  sites and missed this one. Threaded it from executeRuntimeUnitCombo (and from
  the dispatchPrelude call site), restoring the one-snapshot-per-request
  invariant combo-hidden-leaf-routing.test.ts pins. 9/9.
- open-sse/services/usage/firecrawl.ts silently ignored its own `apiKey`
  parameter: 91bb6aa619 moved the fetch to
  fetchFirecrawlQuota(connectionId, connection), which reads the key off the
  connection record, so any caller passing the key directly got "Firecrawl API
  key not available". The explicit key is now merged into the connection passed
  down. firecrawl-usage 8/8.
- src/lib/oauth/constants/oauth.ts was missing a RAYCAST entry in PROVIDERS
  while src/lib/oauth/providers/index.ts registers `raycast` (#8895), so every
  consumer reading PROVIDERS did not know Raycast Pro exists. Also added its
  OAUTH_TEST_CONFIG entry (checkExpiry only — it is an `import_token` provider
  with refreshToken always null), which #8408's guard explicitly requires rather
  than grandfathering. oauth-providers-config 25/25, oauth-test-config-8408 2/2.

**Count / contract drift from the same batch**

- feature flags 45 -> 46, APIKEY_PROVIDERS 197 -> 198 (Raycast Pro #8895),
  unique MCP tools 107 -> 108. Each re-derived from the source of truth.
- vi + pt-BR locales: translated the 8 keys #9415 added
  (providers.newApiAggregator* and providers.modelTestQuotaTooltip) instead of
  relaxing the parity guard. i18n-vi 5/5, i18n-pt-br 3/3.
- login-bootstrap-route: #9491 added `authenticated` to the require-login
  payload so /login can redirect an active session; the three deepEqual bodies
  now carry it. 10/10.

**Flaky-by-construction, made deterministic**

tests/unit/chat-combo-live-test.test.ts asserted the early-keepalive frame with
a 100ms mocked upstream while resolveKeepaliveThreshold() is 2000ms for
openai/*. It only ever passed while unrelated handler latency happened to push
the total past the threshold — incidental, not deterministic, and it stopped
holding once the handler got faster. The mock now sleeps 2400ms so the slow path
is guaranteed and the assertion means what it says. 5/5.

typecheck:core exit 0. check:file-size (base-relative) OK.

Refs #9298
2026-08-07 05:31:07 -03:00
diegosouzapw
eba6fb42d4 docs(mcp): bump the tool count to 105 and realign two vitest count pins
Three more base-reds from the same 08-06 batch, all count/contract drift that
the merged PRs left in sibling files.

**Docs Gates (fast-path) — 3 STRICT drifts**

check:docs-counts measures the MCP tool set from live code: it is 105 now
(#8925 added omniroute_create_combo), while README.md, AGENTS.md and
docs/frameworks/MCP-SERVER.md still claimed 104. Updated all five occurrences
(two of them inside SVG alt text). check:docs-all exits 0.

**Vitest (fast-path) — 2 failures**

- open-sse/mcp-server/__tests__/essentialTools.test.ts pinned 11 phase-1 tools;
  #8925 shipped omniroute_create_combo as phase 1, making it 12. Verified by
  enumerating MCP_ESSENTIAL_TOOLS directly.
- tests/unit/autoCombo/provider-family-combos.test.ts pinned the auto/glm
  provider set to [auggie, glm, zai]. #8914 (Devin ACP bridge) added
  devin-cli-agentic, whose catalog (registry/devin/catalog.ts:90-93) advertises
  the glm-5-2* line — so it belongs in the family pool for exactly the reason
  the test's own comment gives for auggie: a no-auth backend that genuinely
  serves a family model is a legitimate member. Expected set updated, invariant
  unchanged.

npm run test:vitest 36/36 files, 340/340 tests.

Refs #9298
2026-08-07 05:02:11 -03:00
diegosouzapw
43f78b712d test(base): allowlist probeUtils and realign the #7849 suite to the replacement bound
Two more base-reds, both visible only after the migration collision stopped
killing the shards.

**check-db-rules — src/lib/db/probeUtils.ts not classified**

#9541 added probeUtils.ts (transient-error retry for the SQLite corruption
probe). It is imported ONLY by src/lib/db/core.ts, exactly like its siblings
schemaColumns / optimizationSettings / providerNodeSelect, so re-exporting it
through localDb.ts would push callers toward the barrel-import anti-pattern the
gate exists to prevent. Added to INTENTIONALLY_INTERNAL with that rationale.
check-db-rules 22/22, check:db-rules exit 0.

**session-dedup-memory-7849 — pinned a mechanism that was replaced**

7f36b192f0 (#7855 follow-up) swapped the shared "suffix work budget" for the
MAX_SUFFIX_STARTS / MAX_TOTAL_BLOCK_BYTES guards and deleted both the budget and
its SUFFIX_WORK_BUDGET_WARNING string. It updated session-dedup.test.ts but not
this sibling, so 3 of its 4 cases asserted a warning that can no longer be
emitted.

Realigned to the contract that actually survives — which is the invariant #7849
was opened for, not the mechanism:
- the pathological pair must stay BOUNDED (completes in <4s, body intact) —
  measured at ~280ms on the current guards;
- it must FAIL OPEN — original body returned by identity, compressed false,
  stats null (the explanatory zero-savings stats belonged to the removed
  budget path, which skipped before producing any);
- the 512 MiB child fixture must still exit 0 with the full engine chain
  (session-dedup, lite, rtk, headroom, caveman) — that IS the OOM guard — and
  session-dedup must still report its skip, now pinned by prefix since the
  reason string moved with the mechanism.

No threshold was loosened and no case was deleted: 4/4 here, 8/8 on the sibling
session-dedup.test.ts.

Refs #9298
2026-08-07 04:56:36 -03:00
diegosouzapw
54922fa1a6 docs(proxy): use an RFC 5737 documentation IP in the proxy examples
The #9298 verdict headlines its docs failure with
`L810 [stale-version] 1.2.3: const removed = await failOneproxyProxy("1.2.3.4", 8080)`.
That is a false positive: check-deprecated-versions.mjs matches
`/\bv?[12]\.\d+\.\d+\b/`, and the example IP literal 1.2.3.4 contains "1.2.3".

Swapped both occurrences in PROXY_GUIDE.md (and its pl mirror) for 203.0.113.7,
from the RFC 5737 documentation range that exists precisely for examples — it
cannot collide with a version pattern and is the correct thing to print in docs
regardless. Drift count 64 -> 62; no gate threshold was touched.

The gate that actually FAILED under "Docs sync + fabricated-docs (strict)" was
check:fabricated-docs (the invented pool env vars), fixed in the previous
commit; this one removes the misleading line the verdict quotes.
2026-08-07 04:40:04 -03:00
diegosouzapw
5d25d34700 fix(tests): type the #3440 vertex helpers instead of any (the 3 base ESLint errors)
The "ESLint errors: 3 error(s)" HARD failure in the #9298 verdict is
tests/unit/vertex-functioncall-id-3440.test.ts lines 32/41/50: the three
find*(result: any) walkers. `@typescript-eslint/no-explicit-any` is an ERROR in
tests/ (and open-sse/) since #6218, and this file landed on 2026-08-04 without a
suppressions entry, so every run of `lint:json --max-warnings 0` failed. That
step prints nothing on failure, which is why the gate looked like a silent
crash across the open PRs.

Replaced with a GeminiRequestLike interface describing exactly what the three
walkers traverse (contents[].parts[]), so the assertions keep their meaning and
nothing is cast away.

eslint on the file: clean. Suite: 6/6.

Refs #9298
2026-08-07 04:38:35 -03:00
diegosouzapw
db2c3a4753 fix(types,docs): clear the 5 typecheck errors and the fabricated env vars on the base
Third pass over the base-reds, from the 2026-08-06T22:51Z verdict on #9298 —
it reported "Typecheck (core)" with only the FIRST error; there are five, all on
the pure tip 9995bc4893. Two are real production defects.

**Real bugs**

- open-sse/services/compression/engines/ccr/index.ts:295 called
  enforceGlobalBudget(entry.bytes) against an (owner, bytes) signature. The
  `bytes` argument arrived undefined, so `ccrTotalBytes + undefined` is NaN,
  `NaN > MAX` is false (the eviction loop exits immediately) and `NaN <= MAX` is
  false (the re-admit is refused). The #9061 durable tier therefore NEVER
  repopulated its in-memory map: every retrieve after a restart or an eviction
  re-read from SQLite forever, and evictions could not prefer the owning
  principal. Fixed and pinned by a new case in
  tests/unit/ccr-durable-store-9061.test.ts (11/11) — verified failing against
  the buggy call and passing against the fix.
- open-sse/services/combo/fusionPanel.ts:54 read `step.model` after #8894
  widened ComboStep with ComboProviderWildcardStep (which carries modelPattern,
  not model), so a wildcard step in a fusion panel pushed `undefined` onto the
  panel. Now resolved through getComboModelString(), which already handles every
  step shape and returns null for the ones without a concrete model id.

**Type-only**

- accountSemaphore.ts:203 — isBypassed() returns a plain boolean and cannot
  narrow `number | null` (an `x is null | undefined` predicate would be unsound:
  0 bypasses too). Added resolveActiveCap(), the narrowing companion isBypassed
  is now defined in terms of; the acquire path uses the narrowed value.
- comboStructure.ts:140 — same #8894 widening: `prompt` only exists on a model
  step, so it is now read under a kind check.
- firecrawlQuotaFetcher.ts:136 — the function returns full FirecrawlQuota
  objects but was annotated Promise<QuotaInfo | null>, which made the
  custom-base literal an excess-property error. Widened to the accurate type
  (FirecrawlQuota extends QuotaInfo, so callers are unaffected).

**Fabricated docs (the "Docs sync + fabricated-docs (strict)" HARD failure)**

docs/ops/VM_DEPLOYMENT_GUIDE.md recommended OMNIROUTE_MAX_POOL_SIZE and
OMNIROUTE_DB_POOL_SIZE (#9471). Neither is read anywhere in the codebase.
Replaced with the two knobs that do exist and are already documented in
ENVIRONMENT.md: OMNIROUTE_MEMORY_MB and OMNIROUTE_CHAT_MAX_HEAVY_IN_FLIGHT.

typecheck:core 5 errors -> 0. check:fabricated-docs + check:env-doc-sync OK.
accountSemaphore 6/6, ccr-durable-store 11/11, ccr-protocol 9/9,
combo-fusion-strategy 10/10, combo-fusion-comboref 5/5, combo-fusion-warn 4/4,
firecrawl-executor 7/7, executor-firecrawl-fetch 4/4.

Refs #9298
2026-08-07 04:35:29 -03:00
diegosouzapw
b80987e4d4 fix(changelog): convert the #9415 fragment to the required bullet shape
Another base-red from the 08-06 batch: bd4407cb64 landed
changelog.d/features/9415-newapi-sub2api-aggregator-balance.md as YAML
frontmatter + a prose paragraph. Every other fragment in changelog.d/ is a
single markdown bullet, and both consumers enforce that —
scripts/check/check-changelog-integrity.mjs:97 and the release aggregator
(scripts/release/aggregate-changelog.mjs:57) reject anything that does not
start with '- ', so 'Merge integrity (changelog + generated skills)' was red
for every PR targeting the release branch.

Rewritten as a bullet with the standard issue link, preserving the feature
description (aggregator gateway toggle, /api/user/self balance read, dashboard
badge, quota-preflight skip, NEWAPI_AGGREGATOR_BALANCE flag default off,
quotaPerUnit override). Swept the rest of changelog.d/ — this was the only
malformed fragment.

check:changelog-integrity OK.

Refs #9298
2026-08-07 04:13:20 -03:00
diegosouzapw
e8936f53f4 fix(db,combo): clear the NEW base-reds the 08-06 merge batch introduced
The tip moved while the first sweep PR (#9600) was in review, and three fresh
base-reds landed with it — same classes as before, all reproduced on the pure
tip 9995bc4893:

1. ANOTHER migration collision: #9061 shipped 134_ccr_blocks.sql onto the slot
   134_proxy_logs_egress_ip.sql (#9291) has held since 08-04. getMigrationFiles()
   throws on collision, so every DB-touching test died at bootstrap again.
   Renumbered to 139 (next free slot). No retroactive guard needed this time:
   both statements are IF NOT EXISTS, and no DB can have applied it as 134 —
   the runner refused to run at all while the collision existed.
2. BROKEN IMPORT killing the combo module graph: #8894 imported
   preferAntigravityConnectionsWithStoredProject from
   ../antigravityProjectPersistence.ts — a module that exists NOWHERE in the
   repo (it came from an unmerged sibling branch). Anything importing
   quotaStrategies.ts died with ERR_MODULE_NOT_FOUND. Implemented the helper in
   the real persistence module (antigravityProjectPersist.ts, #8491) with the
   semantics the call site needs — prefer connections that already carry a
   stored projectId, never emptying the pool — and pointed the import there.
   New regression suite tests/unit/antigravity-prefer-stored-project.test.ts
   (5/5), including an import-graph probe that reproduces the break shape.
3. Sibling-test drift from #9106 (gemini-3.1-pro-high now user-callable): its
   own suites were updated but provider-models-route.test.ts was not. Expected
   discovery list realigned; testFrozen 1784->1787 justified in the baseline
   (irreducible +2 after comment compression; gate counts split-newlines).

Also regenerated tests/snapshots/provider/translate-path.json — addition-only:
devin-cli-agentic, raycast, regolo (today's provider merges), zero removals.

image-generation-route 20/20 (was import-dead), provider-models-route 59/59,
antigravity-prefer-stored-project 5/5, provider-translate-path-golden 3/3.

Refs #9298
2026-08-07 04:08:35 -03:00
diegosouzapw
e4f55c7d92 fix(guardrails): forward the router deps seam through callVisionModel
tests/unit/guardrails/vision-bridge-sse-and-reasoning.test.ts was 7/7 red on any
clean box (CI shard 3/4): callVisionModel() called getBestVisionModel()/
getFallbackModels() WITHOUT the routers' existing VisionBridgeRouterDeps seam,
so the credential check always hit the live connections DB — no vision-capable
connection meant 'No vision-capable provider connected' before the mocked fetch
was ever reached, and on a dev box auto-selection could swap the fixed model
under the assertions.

The routers already accepted deps; only the forwarding was missing. Added the
optional 5th param (backward compatible — the sole production caller,
visionBridge.ts, injects its own callVisionModel and is unaffected) and the
suite now pins selection with hasUsableCredentials: async () => null
(indeterminate → the fixed model is honored, DB untouched). 7/7.

Sibling suites re-run green: vision-bridge-callmodel 2/2, visionBridge 25/25,
visionBridgeHelpers.callVisionModel 8/8, visionBridgeRouter 10/10,
vision-bridge-cc-no-reroute 8/8.

Refs #9298
2026-08-07 03:35:29 -03:00
diegosouzapw
5dfc96ae54 test(base): realign six suites with contracts that #9100/#8990/#9009 deliberately changed
Continuing the base-red drain — every one of these reproduces on the pure tip.

- tests/snapshots/provider/translate-path.json: regenerated via UPDATE_GOLDEN=1.
  The diff is ADDITION-ONLY — the unorouter block from #9009; no existing
  provider entry changed. 3/3.
- tests/unit/provider-models-route.test.ts: ff012ff420 added onboardUser as a
  bootstrap fallback next to loadCodeAssist; the mock now excludes it from the
  discovery-URL ledger like it already excluded loadCodeAssist, otherwise it
  consumed the injected 503 and the retry assertion misfired. 59/59.
- tests/unit/responses-commentary-passthrough-6199.test.ts: #8990 (c996dc93c2)
  deliberately preserves `tools` on the TERMINAL response.completed snapshot
  (Codex CLI rebuilds its tool list from it); the assertion now pins the echoed
  tools instead of their absence. Still stripped on created/in_progress. 7/7.
- tests/unit/vision-compression-authoritative-capability-7237.test.ts:
  68cb678780 added the 'gpt-5' fragment, so the heuristic-vs-spec DRIFT this
  suite documented no longer exists; the cases now guard the agreement, keep a
  conservative-for-unknown-ids probe, and reproduce the strip-bug shape with an
  explicit false instead of deriving it. 4/4.
- tests/unit/provider-limits-proxy-fail-closed.test.ts +
  tests/unit/image-generation-route.test.ts: #9100 made the proxy reachability
  probe NON-BLOCKING (optimistic dispatch; the probe aborts only in-flight
  requests — its own t14 sibling was updated to this exact pattern). Instant
  mocks therefore won the race and the PROXY_UNREACHABLE 503 became unobservable
  (a success or a generic 502). The mocks now stay in flight (never-resolving,
  so the aborted continuation cannot reach the restored real fetch), and the
  fail-closed proof is the settled rejection itself plus zero egress AFTER the
  fast-fail. Production fail-closed semantics are unchanged — the proxy dispatch
  path still throws; only the mock timing was stale. 3/3 and 20/20.

Refs #9298
2026-08-07 03:35:29 -03:00
368 changed files with 1353 additions and 17360 deletions

View File

@@ -345,11 +345,8 @@ ALLOW_API_KEY_REVEAL=false
# OMNIROUTE_CHAT_HEAVY_TOOL_COUNT=64
# Conservative string-size token estimate that classifies a request as heavyweight. Default 32000.
# OMNIROUTE_CHAT_HEAVY_ESTIMATED_TOKENS=32000
# Optional opt-in hard message-count cap; excess receives compact-required 413 before
# compression can run. Unset/0 (the default) means no history cap: heap growth is bounded
# by OMNIROUTE_CHAT_MAX_HEAVY_IN_FLIGHT and the heap-pressure shed instead. Set a positive
# value only on memory-constrained deployments that need a hard ceiling.
# OMNIROUTE_CHAT_HARD_MAX_MESSAGES=0
# Hard message-count cap; excess receives compact-required 413. Default 800.
# OMNIROUTE_CHAT_HARD_MAX_MESSAGES=800
# Hard cap (bytes) for a non-streaming upstream response buffered fully into memory
# (#5152). Past this the upstream reader is cancelled and the request fails fast
@@ -797,16 +794,6 @@ PROVIDER_LIMITS_SYNC_SPACING_MS=1500
# Disable the proactive recovery scheduler entirely (default: false).
# OMNIROUTE_DISABLE_CONNECTION_RECOVERY=false
# Proactive Claude warmup scheduler (#8848): fires a trivial request to opted-in
# OAuth connections on a cron schedule (America/Los_Angeles) so accounts do not
# hit the 5-hour sliding window cold. Off by default — set ENABLED=1 and flip
# per-connection flags in settings.claudeWarmup.connections to activate.
# Used by: src/lib/warmupScheduler.ts.
# OMNIROUTE_WARMUP_ENABLED=false
# OMNIROUTE_WARMUP_CRON="0 7 * * *"
# OMNIROUTE_WARMUP_CONCURRENCY=3
# OMNIROUTE_WARMUP_MODEL=
# Background job interval for budget reset checks (ms). Default: 600000 (10m).
# Used by: src/lib/jobs/budgetResetJob.ts. Floor: 10000.
#OMNIROUTE_BUDGET_RESET_JOB_INTERVAL_MS=600000
@@ -1540,15 +1527,6 @@ APP_LOG_TO_FILE=true
# ═══════════════════════════════════════════════════════════════════════════════
# 19. MODEL SYNC (Dev)
# ═══════════════════════════════════════════════════════════════════════════════
# Enable the models.dev capability sync. Default: false (opt-in only).
# Also settable from Dashboard > Settings > AI. This variable wins over that
# setting whenever it is set to anything non-empty, in either direction, so a
# deployment can pin the sync on or off without depending on database state
# surviving a rebuild. Leave it unset to let the dashboard toggle decide.
# On: 1, true, yes or on (any casing). Any other value is off.
# Used by: src/lib/modelsDevSync.ts
# MODELS_DEV_SYNC_ENABLED=false
# Development-time model catalog sync interval in seconds.
# Used by: src/lib/modelsDevSync.ts
# Default: 86400 (24 hours)
@@ -2492,10 +2470,10 @@ QUOTA_STORE_DRIVER=sqlite # sqlite | redis
# ═══════════════════════════════════════════════════════════════════════════════
# Optional add-on (feature flag RADAR_ENABLED, default off — see feature flag
# settings, not an env var) that overlays a signed, freshly-curated free-model
# catalog on top of the release baseline. All four variables below are optional
# and only needed to point the client at a self-hosted/forked feed or
# supporter-key flow instead of the default OmniRoute Radar service. Used by:
# src/lib/radar/sync.ts, src/lib/radar/pinnedKeys.ts, src/lib/radar/links.ts.
# catalog on top of the release baseline. Both variables below are optional and
# only needed to point the client at a self-hosted/forked feed instead of the
# default OmniRoute Radar feed. Used by: src/lib/radar/sync.ts,
# src/lib/radar/pinnedKeys.ts.
# Base URL of the Radar feed service. Overrides the built-in default so forks
# and self-hosters can point at their own signed feed.
@@ -2505,12 +2483,3 @@ QUOTA_STORE_DRIVER=sqlite # sqlite | redis
# signature, replacing the pinned default key. Required when self-hosting a
# feed signed with a different key pair.
# RADAR_FEED_PUBKEY=
# URL the dashboard's "I'm a contributor" button opens (GitHub OAuth
# supporter-key claim flow). No pricing/value lives in this repo — only the
# link.
# RADAR_CONTRIBUTOR_CLAIM_URL=https://radar.omniroute.online/auth/github
# URL the dashboard's "Support the project" button opens (payment/plans
# page). No pricing/value lives in this repo — only the link.
# RADAR_SUPPORTER_PLANS_URL=https://radar.omniroute.online/planos

View File

@@ -22,10 +22,10 @@ jobs:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: github/codeql-action/init@f205ea1c3313d32999d8d6a48b4f6530d4437b38 # v4.37.4
- uses: github/codeql-action/init@e4fba868fa4b1b91e1fdab776edc8cfbe6e9fb81 # v4.37.3
with:
languages: javascript-typescript
queries: security-extended
- uses: github/codeql-action/analyze@f205ea1c3313d32999d8d6a48b4f6530d4437b38 # v4.37.4
- uses: github/codeql-action/analyze@e4fba868fa4b1b91e1fdab776edc8cfbe6e9fb81 # v4.37.3
with:
category: "/language:javascript-typescript"

View File

@@ -137,13 +137,13 @@ jobs:
uses: docker/setup-buildx-action@v4
- name: Login to Docker Hub
uses: docker/login-action@v4.6.0
uses: docker/login-action@v4.5.2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GitHub Container Registry
uses: docker/login-action@v4.6.0
uses: docker/login-action@v4.5.2
with:
registry: ghcr.io
username: ${{ github.actor }}
@@ -237,13 +237,13 @@ jobs:
uses: docker/setup-buildx-action@v4
- name: Login to Docker Hub
uses: docker/login-action@v4.6.0
uses: docker/login-action@v4.5.2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GitHub Container Registry
uses: docker/login-action@v4.6.0
uses: docker/login-action@v4.5.2
with:
registry: ghcr.io
username: ${{ github.actor }}
@@ -372,7 +372,7 @@ jobs:
- name: Upload Trivy SARIF to Security tab
if: needs.prepare.outputs.version != 'main'
continue-on-error: true
uses: github/codeql-action/upload-sarif@v4.37.4
uses: github/codeql-action/upload-sarif@v4.37.3
with:
sarif_file: trivy-results.sarif
category: trivy-image

View File

@@ -271,10 +271,6 @@ jobs:
# covered by typecheck:core's curated allowlist. See check-dashboard-typecheck.mjs.
- name: Typecheck (dashboard)
run: npm run check:dashboard-typecheck
# #8781: open-sse workspace typecheck gate — the workspace imports @/ which
# escapes to src/ via undeclared path aliases. See check-open-sse-typecheck.mjs.
- name: Typecheck (open-sse)
run: npm run check:open-sse-typecheck
# WS4.2 (v3.8.49 plan): TypeScript 7 native-compiler SHADOW — advisory only.
# TS7 went GA 2026-07-08 with 8-12x type-check speedups; its Compiler API only
# arrives in 7.1, so typescript-eslint / type-coverage / Stryker stay on 6.x

View File

@@ -30,7 +30,7 @@ omniroute setup opencode --auth
# 3. Restart OpenCode — /models lists the full live catalog
```
The `--auth` flag runs `opencode auth login --provider opencode-omniroute` automatically.
The `--auth` flag runs `opencode auth login --provider omniroute` automatically.
Use `--base-url` to point at a non-default OmniRoute address:
```sh
@@ -84,7 +84,7 @@ Peer dep: `@opencode-ai/plugin` (managed by your OpenCode install).
```
```sh
opencode auth login --provider opencode-omniroute
opencode auth login --provider omniroute
# prompts for the OmniRoute API key, writes to ~/.local/share/opencode/auth.json
```
@@ -164,8 +164,8 @@ Then in `~/.config/opencode/opencode.json` reference each directory by absolute
Paths are relative to `~/.config/opencode/`. Each entry now resolves to a distinct module file, so OC loads them as two separate plugin instances. Authenticate each:
```sh
opencode auth login --provider opencode-omniroute
opencode auth login --provider opencode-omniroute-preprod
opencode auth login --provider omniroute
opencode auth login --provider omniroute-preprod
```
Each entry gets its own provider id, its own model picker entry, its own slot in `auth.json`, and its own TTL cache. Closures are isolated per plugin instance — no cross-talk.

1
_tasks
View File

@@ -1 +0,0 @@
/home/diegosouzapw/dev/proxys/OmniRoute/_tasks

View File

@@ -218,26 +218,6 @@ function registerPluginInOpenCodeConfig({
* a clear "could not run opencode" message instead of a hard import
* failure.
*/
/**
* Resolve the provider id used for `opencode auth login --provider <id>`.
*
* The bundled @omniroute/opencode-plugin registers its provider under
* `opencode-<id>` (the `opencode-` prefix is required by OpenCode >=1.17.8's
* native-adapter gate). The auth login command must use the prefixed form
* because OpenCode resolves `--provider <id>` against the provider id the
* plugin actually registered.
*
* Idempotent: if the id already starts with `opencode-`, it passes through
* unchanged. This protects users who manually worked around the bug with
* `--provider opencode-omniroute`.
*
* @param {string} providerId
* @returns {string}
*/
export function resolveOpenCodeAuthProviderId(providerId) {
return providerId.startsWith("opencode-") ? providerId : `opencode-${providerId}`;
}
/**
* Pure resolver for the `opencode auth login` spawn descriptor. Extracted so the
* platform-branching logic is unit-testable without mocking child_process or
@@ -251,23 +231,21 @@ export function resolveOpenCodeAuthProviderId(providerId) {
*/
export function resolveOpenCodeAuthSpawn(providerId, platform = process.platform) {
const isWin = platform === "win32";
const authProviderId = resolveOpenCodeAuthProviderId(providerId);
return {
command: isWin ? "opencode.cmd" : "opencode",
args: ["auth", "login", "--provider", authProviderId],
args: ["auth", "login", "--provider", providerId],
options: { stdio: "inherit", shell: isWin },
};
}
export function runOpenCodeAuth(providerId) {
const authProviderId = resolveOpenCodeAuthProviderId(providerId);
const { command, args, options } = resolveOpenCodeAuthSpawn(providerId);
const res = spawnSync(command, args, options);
if (res.error) {
// ENOENT = opencode is not on PATH
if (res.error.code === "ENOENT") {
printInfo(
`opencode CLI not found on PATH. Run \`opencode auth login --provider ${authProviderId}\` manually after installing OpenCode.`
`opencode CLI not found on PATH. Run \`opencode auth login --provider ${providerId}\` manually after installing OpenCode.`
);
return 1;
}
@@ -365,8 +343,7 @@ export async function runSetupOpenCodeCommand(opts = {}) {
if (wantsAuth) {
if (nonInteractive) {
printInfo(`Skipping \`opencode auth login\` (non-interactive mode).`);
const authProviderId = resolveOpenCodeAuthProviderId(providerId);
printInfo(`Run manually: opencode auth login --provider ${authProviderId}`);
printInfo(`Run manually: opencode auth login --provider ${providerId}`);
} else {
printHeading("Authenticating with OpenCode");
const authExit = runOpenCodeAuth(providerId);
@@ -375,9 +352,8 @@ export async function runSetupOpenCodeCommand(opts = {}) {
}
}
} else {
const authProviderId = resolveOpenCodeAuthProviderId(providerId);
printInfo(
`Next step: opencode auth login --provider ${authProviderId} (pass --auth to do this automatically)`
`Next step: opencode auth login --provider ${providerId} (pass --auth to do this automatically)`
);
}

View File

@@ -17,7 +17,7 @@ export const SYSTRAY_VERSION = "2.1.4";
const SYSTRAY_SPEC = `${SYSTRAY_PACKAGE}@${SYSTRAY_VERSION}`;
export function resolveSystrayBinName(platform: NodeJS.Platform): string | null {
if (platform === "win32") return "tray_windows_release.exe";
if (platform === "win32") return null;
if (platform === "darwin") return "tray_darwin_release";
return "tray_linux_release";
}
@@ -45,6 +45,7 @@ export function chmodSystrayBinAt(runtimeRoot: string, platform: NodeJS.Platform
}
export async function loadSystray(): Promise<(new (...args: unknown[]) => unknown) | null> {
if (process.platform === "win32") return null; // Windows uses tray.ps1 instead
ensureRuntimeDir();
if (!isInstalled()) {
try {

View File

@@ -130,7 +130,7 @@ async function openSqliteDatabase(dbPath, options = {}) {
try {
return new loaded.Database(dbPath, options);
} catch (error) {
return openWithSyncDriverFallback(dbPath, options, error);
throw createSqliteNativeError(error);
}
}

View File

@@ -167,10 +167,6 @@ export function getAutostartStatus() {
linger: tryReadLingerEnabled(),
};
}
if (process.platform === "win32") {
const winMechanism = isAutostartEnabled() ? "vbs-startup" : null;
return { enabled: isAutostartEnabled(), mechanism: winMechanism };
}
return { enabled: isAutostartEnabled(), mechanism: null };
}

View File

@@ -1,4 +1,5 @@
import { isTraySupported, initSystrayUnix, killSystrayUnix } from "./traySystray.mjs";
import { initWinTray, killWinTray } from "./trayWindows.mjs";
let active = null;
@@ -9,17 +10,15 @@ export async function initTray({ port, onQuit, onOpenDashboard, onShowLogs }) {
const ctx = { port, onQuit, onOpenDashboard, onShowLogs };
// initSystrayUnix is async: it lazily installs/loads systray2 from the runtime
// dir (trayRuntime.ts) rather than from node_modules. (#4605)
// Use systray2 on all platforms including Windows — the tarball ships
// tray_windows_release.exe, avoiding the Norton/AVG IDP.HELU.PSE85 heuristic
// that fires on temp-dir PowerShell scripts. (#8609)
active = await initSystrayUnix(ctx);
active = process.platform === "win32" ? initWinTray(ctx) : await initSystrayUnix(ctx);
return active;
}
export function killTray() {
if (!active) return;
try {
killSystrayUnix(active);
if (process.platform === "win32") killWinTray(active);
else killSystrayUnix(active);
} catch {}
active = null;
}

View File

@@ -3,7 +3,7 @@
import { spawn } from "node:child_process";
import { existsSync } from "node:fs";
import { dirname, join } from "node:path";
import { fileURLToPath, pathToFileURL } from "node:url";
import { fileURLToPath } from "node:url";
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
@@ -43,15 +43,7 @@ export async function startMcpCli(rootDir = ROOT) {
}
// `tsx` loader is only required for local `.ts` fallback; JS entry works without it.
const tsxLoaderArgs = mcpEntry.endsWith(".ts") ? ["--import", "tsx"] : [];
// Preload the stdout/stderr console guard before mcpEntry's own module graph evaluates —
// DB init (a side effect of createMcpServer()'s tool registration) logs via plain
// console.log, and by the time any code inside mcpEntry itself could redirect it, that
// module's own (hoisted) imports have already run. Loading the guard first, in a separate
// module, is the only point early enough to guarantee it never leaks into the JSON-RPC
// stream on stdout.
const consoleGuard = pathToFileURL(join(__dirname, "mcpStdioConsoleGuard.mjs")).href;
const loaderArgs = ["--import", consoleGuard, ...tsxLoaderArgs];
const loaderArgs = mcpEntry.endsWith(".ts") ? ["--import", "tsx"] : [];
await new Promise((resolve, reject) => {
const child = spawn(process.execPath, [...loaderArgs, mcpEntry], {

View File

@@ -1,16 +0,0 @@
// Preloaded (via `node --import`) before open-sse/mcp-server/server.ts and its entire
// import graph evaluate. The stdio MCP transport uses stdout exclusively for JSON-RPC
// messages, but DB init (getDbInstance(), triggered as a side effect of evaluating the
// server's module graph — e.g. tool registration reading compression settings) logs via
// plain console.log. A redirect placed *inside* server.ts (even at the top of its first
// executed function) is too late: static imports are hoisted and fully evaluated before
// any of that function's own code runs, so earlier console.log calls during import-time
// side effects already escaped to the real stdout by then. Redirecting here, in a module
// that loads before server.ts is even requested, is the only point early enough to
// guarantee no startup output leaks into the JSON-RPC stream and corrupts it client-side
// (e.g. Claude Desktop: "Unexpected token 'D', \"[DB] Changi\"... is not valid JSON").
import { Console } from "node:console";
const stderrConsole = new Console({ stdout: process.stderr, stderr: process.stderr });
console.log = stderrConsole.log.bind(stderrConsole);
console.warn = stderrConsole.warn.bind(stderrConsole);

View File

@@ -43,19 +43,6 @@ if (isVersionFastPath(process.argv)) {
process.exit(0);
}
// MCP stdio transport uses stdout exclusively for JSON-RPC messages. Redirect
// console.log/warn to stderr before anything else runs — including the tsx/esm and
// polyfill imports below, since those (and their transitive module graphs, e.g. DB
// init) can themselves log during evaluation. Redirecting after those imports let
// early output leak straight into the JSON-RPC stream and corrupt it client-side
// (e.g. Claude Desktop: "Unexpected token 'D', \"[DB] Changi\"... is not valid JSON").
if (process.argv.includes("--mcp")) {
const { Console } = await import("node:console");
const stderrConsole = new Console({ stdout: process.stderr, stderr: process.stderr });
console.log = stderrConsole.log.bind(stderrConsole);
console.warn = stderrConsole.warn.bind(stderrConsole);
}
// Register tsx so dynamic imports of .ts source files (referenced as .js per
// TypeScript conventions) resolve correctly. The build never emits .js for
// src/lib/cli-helper/, so tsx handles the .ts → .js resolution at runtime.
@@ -71,6 +58,16 @@ await import("../open-sse/utils/setupPolyfill.ts");
const { registerAliasResolver } = await import("./aliasResolver.mjs");
await registerAliasResolver(ROOT);
// MCP stdio transport uses stdout exclusively for JSON-RPC messages.
// Redirect console.log/warn to stderr early (before loadEnvFile and DB init)
// so no startup output corrupts the protocol.
if (process.argv.includes("--mcp")) {
const { Console } = await import("node:console");
const stderrConsole = new Console({ stdout: process.stderr, stderr: process.stderr });
console.log = stderrConsole.log.bind(stderrConsole);
console.warn = stderrConsole.warn.bind(stderrConsole);
}
// Electron persists secrets (JWT_SECRET, API_KEY_SECRET, STORAGE_ENCRYPTION_KEY) to
// `<DATA_DIR>/server.env` (electron/main.js), never `.env`. Migrating an existing
// install (storage.sqlite + server.env) to the CLI left those secrets undiscoverable —

View File

@@ -39,8 +39,7 @@ snap="$(ops_find_snapshot "$ID")"
# Policy definition tables present in BOTH the snapshot and the live DB. GLOB
# keeps `_` literal; we drop usage counters / logs so accounting isn't rewound.
tables=()
while IFS= read -r t; do tables+=("$t"); done < <(
readarray -t tables < <(
sqlite3 "$snap/storage.sqlite" \
"SELECT name FROM sqlite_master WHERE type='table' AND name GLOB 'api_key*' \
AND name NOT GLOB '*counter*' AND name NOT GLOB '*_log*' ORDER BY name;"

View File

@@ -1 +0,0 @@
- **feat(api):** add response content encoding verification — confirms Next.js compress:true and documents stripStaleForwardingHeaders behavior ([#6736](https://github.com/diegosouzapw/OmniRoute/issues/6736))

View File

@@ -1 +0,0 @@
- **feat(api):** add plugins marketplace install endpoint with checksum verification ([#6752](https://github.com/diegosouzapw/OmniRoute/issues/6752))

View File

@@ -1 +0,0 @@
- **feat(chatgpt-web):** harden prompt-emulated tool contract for thinking models (#7679 — thanks @horacecar)

View File

@@ -1 +0,0 @@
- **feat(providers):** make video_url passthrough configurable per provider/model via compat override ([#9248](https://github.com/diegosouzapw/OmniRoute/issues/9248)) — thanks @HellFiveOsborn

View File

@@ -1 +0,0 @@
- **feat(dashboard):** render a conditional "Get API key" link on the provider detail page, surfaced from the existing `notice.apiKeyUrl` / `notice.signupUrl` catalog metadata (e.g. `pioneer`, `jina`, `together`). The link opens in a new tab and is hidden when neither URL is present, so existing providers are unaffected. Tracks the notice field in `ProviderCatalogMetadata` ([#9270](https://github.com/diegosouzapw/OmniRoute/pull/9270))

View File

@@ -1 +0,0 @@
- **feat(providers):** accept JSON cookie objects in normalizeSessionCookieHeader (#9284 — thanks @AIB1TAL0S)

View File

@@ -1 +0,0 @@
- **feat(providers):** support max reasoning effort for opencode-zen DeepSeek models (#9318)

View File

@@ -1 +0,0 @@
- feat(plugins): expose client request headers in plugin onRequest/onResponse context (#9570)

View File

@@ -1 +0,0 @@
- **feat(audio):** Soniox STT + TTS provider (`sx`) — async speech-to-text (`stt-async-v5`, `stt-async-v4`) and real-time text-to-speech (`tts-rt-v1`) ([#9579](https://github.com/diegosouzapw/OmniRoute/pull/9579))

View File

@@ -1,2 +0,0 @@
- fix(tests): make machineId tests macOS-compatible by stubbing ioreg in test helper (#8577)
- fix(scripts): replace bash 4+ readarray with compatible while-read loop in restore-policies.sh (#8577)

View File

@@ -1 +0,0 @@
- fix(cli): enable systray2 on Windows for Norton-friendly tray (#8609)

View File

@@ -1 +0,0 @@
- fix(providers): gate premium opencode-zen/opencode-go models behind an API key (#8681)

View File

@@ -1 +0,0 @@
- fix(yuanbao-web): accept `content` field in SSE text events (upstream format change) (#8739)

View File

@@ -1 +0,0 @@
- fix(build): remove misleading open-sse/package.json facade and add workspace typecheck gate (#8781)

View File

@@ -1 +0,0 @@
- fix(errorClassifier): classify ChatGPT Web SENTINEL_BLOCKED 403 as terminal FORBIDDEN, enabling proper combo fallback (#8813)

View File

@@ -1 +0,0 @@
- fix(cli): fall back to node:sqlite when better-sqlite3 constructor throws at runtime (#8826)

View File

@@ -1 +0,0 @@
- fix(opencode): prefix provider id with "opencode-" for auth login command (#8830)

View File

@@ -1 +0,0 @@
- fix(opencode-zen): add current free-tier models to registry to enable combo context pre-filtering (#8841)

View File

@@ -1 +0,0 @@
- **fix(opencode):** generate schema-complete model limits so OpenCode accepts catalog entries without an explicit output cap ([#8869](https://github.com/diegosouzapw/OmniRoute/pull/8869)) — thanks @xiaoyaner0201

View File

@@ -1 +0,0 @@
- **fix(cli):** default omitted Codex CLI wire API settings to Responses and clear stale Chat state after reset ([#8876](https://github.com/diegosouzapw/OmniRoute/pull/8876)) — thanks @xiaoyaner0201

View File

@@ -1 +0,0 @@
- **fix(proxy):** isolate new proxy credential fields from browser and password-manager autofill after form reset ([#8883](https://github.com/diegosouzapw/OmniRoute/pull/8883)) — thanks @xiaoyaner0201

View File

@@ -1 +0,0 @@
- **fix(providers):** expose both OAuth Connect and manual API-key actions for dual-auth providers such as CodeBuddy CN ([#8921](https://github.com/diegosouzapw/OmniRoute/pull/8921)) — thanks @Llliao1113

View File

@@ -1 +0,0 @@
- fix(opencode): propagate vision capability from live catalog into opencode.json (#8960)

View File

@@ -1 +0,0 @@
- fix(providers): switch Antigravity quota RPCs to iterate ANTIGRAVITY_RUNTIME_BASE_URLS (#8965)

View File

@@ -1 +0,0 @@
- fix(vertex): route Claude models to native rawPredict endpoint and respect custom targetFormat overrides (#8994)

View File

@@ -1 +0,0 @@
- fix(proxies): resolveProxyForConnection now returns the proxy name, so the dashboard badge shows the name instead of the hostname (#8995)

View File

@@ -1 +0,0 @@
- fix(cursor): preserve tool context across multi-turn conversations when client lacks conversation_id (#9029)

View File

@@ -1 +0,0 @@
- fix(sse): move Antigravity client system content to first user message to avoid upstream 429 RESOURCE_EXHAUSTED on oversized systemInstruction (#9030)

View File

@@ -1 +0,0 @@
- fix(db): stream DB backup export instead of buffering entire file into memory (#9045)

View File

@@ -1 +0,0 @@
- fix(ui): normalize Free Pool API response payload to read from data.proxies (#9046)

View File

@@ -1 +0,0 @@
- fix(api/analytics): stop charging :free models at arbitrary fallback price (#9054)

View File

@@ -1 +0,0 @@
- fix(providers): modal.com validation returns clear error when Base URL is missing, instead of leaking "Invalid outbound URL" (#9102)

View File

@@ -1,2 +0,0 @@
- fix(catalog): repair dead guard and synced-first ordering for custom model Vision capable override (#9195)
- fix(routing): consult customModels supportsVision flag in Combo vision filter (#9195)

View File

@@ -1 +0,0 @@
- fix(web-search): bind each search provider attempt to its connection proxy (#9201)

View File

@@ -1 +0,0 @@
- fix(auth): make antigravity and agy equivalent in credential selection (#9204)

View File

@@ -1 +0,0 @@
- fix(lmarena): emit Uint8Array SSE chunks instead of strings to satisfy shared pipeline contract (#9237)

View File

@@ -1 +0,0 @@
- fix(qoder): include actionable CLI_QODER_BIN hint in connection test when qodercli is not found (#9277)

View File

@@ -1 +0,0 @@
- **fix(providers):** the web search fallback detector in `webSearchFallback.ts` used an exact `Set` (`web_search`, `web_search_preview`) that missed Anthropic's date-suffixed server-tool variant `web_search_20250305` (sent by Claude Code 2.1.220+). Changed to prefix regex `/^web_search/`, matching the two other detectors in the codebase, so the fallback intercepts versioned web search tools for OpenAI-compatible upstreams ([#9279](https://github.com/diegosouzapw/OmniRoute/pull/9279))

View File

@@ -1 +0,0 @@
- fix(credential-health): scheduler never retries failed connections due to static interval comparison (#9289)

View File

@@ -1 +0,0 @@
- fix(api): specialty model catalog ignores hidden OpenRouter model flags (#9293)

View File

@@ -1 +0,0 @@
- fix(catalog): cache getModelsDevPricing() to prevent OOM at startup (#9300)

View File

@@ -1 +0,0 @@
- fix(providers): bump qwen-web SPA version header from 0.2.66 to 0.2.81 (#9304)

View File

@@ -1 +0,0 @@
- fix(backend): use accumulated responseBody for provider payload in dashboard log viewer to avoid stale data from truncated SSE events (#9315)

View File

@@ -1 +0,0 @@
- fix(qoder): surface qodercli stderr in error message instead of generic 502 (#9319)

View File

@@ -1 +0,0 @@
- **fix(providers):** Codex GPT-5.6 model metadata reports the 1M context window and 922K input limit ([#9431](https://github.com/diegosouzapw/OmniRoute/issues/9431)).

View File

@@ -1 +0,0 @@
- fix(api): make the 800-message chat history cap opt-in so long conversations reach compression instead of a terminal 413 (#9494)

View File

@@ -1 +0,0 @@
- **fix(standalone):** multipart uploads (`POST /v1/audio/transcriptions`) no longer hang — the WebDAV wrapper hands non-WebDAV requests to Next synchronously instead of losing the start of a streaming body ([#9580](https://github.com/diegosouzapw/OmniRoute/pull/9580))

View File

@@ -1 +0,0 @@
- fix(combo): distinguish pre-dispatch skips from genuine failures to prevent false 503 ALL_ACCOUNTS_INACTIVE (#9630)

View File

@@ -1 +0,0 @@
- **fix(providers):** new per-provider `noAuthFallbackDisabledProviders` setting lets operators disable the synthetic anonymous (no-auth) credential fallback for API-key providers whose static definition declares `anonymousFallback: true` (e.g. `opencode-go`, `opencode-zen`) — upstream endpoints now reject anonymous requests with `401 Missing API key`, so the fallback added latency and caused UI health/reconnect churn. Real keyed connections keep working and recover automatically once quota state clears; true no-auth providers (`opencode`, `mimocode`, …) are unaffected, with `blockedProviders` remaining their disable mechanism. Default behavior is unchanged ([#9675](https://github.com/diegosouzapw/OmniRoute/pull/9675))

View File

@@ -1356,11 +1356,24 @@
"count": 1
}
},
"src/sse/handlers/chat.ts": {
"no-restricted-imports": {
"count": 1
}
},
"src/sse/handlers/chatHelpers.ts": {
"no-restricted-imports": {
"count": 1
}
},
"src/sse/services/auth.ts": {
"no-restricted-imports": {
"count": 1
},
"no-restricted-syntax": {
"count": 1
}
},
"src/sse/services/model.ts": {
"no-restricted-imports": {
"count": 2

View File

@@ -161,130 +161,185 @@
"_rebaseline_2026_07_18_6979_codex_test": "PR #6979 own growth: executor-codex.test.ts 1340->1347 (+7 = generalized ensureThinkingBudget assertion added to the existing codex thinking-budget cases). antigravity-test bump 942->977 REVERTED here: #7408's test split dropped that file to 888, so this PR's +35 fits under the original 942 frozen cap.",
"cap": 1000,
"frozen": {
"_rebaseline_2026_07_02_5816_qoder": "PR #5816 (@AgentKiller45, qoder PAT via qodercli): qoderCli.ts 666->989, new-above-cap frozen (owner-approved baseline freeze). The growth is the legitimate PAT job-token exchange + quota parsing CLI transport (the pure-JS Cosy path 500'd on every PAT request); extracting the spawn/parse helpers now would just add indirection to a contributor PR mid-merge. Test frozen also raised for this PR's coverage growth: providers-page-utils.test.ts 1052->1092. Additionally clears an inherited base-red from the already-merged #5933 (codex json_schema->text.format): translator-openai-responses-req.test.ts 1097->1172 (+75 regression tests, no offending branch left). All remain frozen (cannot grow further); release captain's rebaseline-at-release supersedes.",
"open-sse/services/qoderCli.ts": 989,
"_rebaseline_pr1043_minimax_tts": "Upstream port decolua/9router#1043 (toanalien) own growth: audioSpeech.ts 965->1061 (+96). Adds MiniMax T2A v2 TTS dispatch (handleMinimaxSpeech + hexToBytes helper) — provider entry was already in audioRegistry (format: minimax-tts) but no handler existed, falling through to the OpenAI-compatible default that fails (T2A has custom shape + hex-encoded audio + base_resp envelope). New branch sits next to the other inline provider branches (xiaomi-mimo, coqui, tortoise, aws-polly) — extracting would just create indirection. Covered by tests/unit/minimax-tts-1043.test.ts (3 tests, GREEN: success, base_resp error, invalid-hex).",
"_rebaseline_2026_07_23_8266_alibaba_media": "#8266 (@backryun) own growth: imageRegistry.ts 821->979 (+158) — Alibaba-family media models (Qwen image/video, Bailian, Wan) added to the image/video registry. Registry model data, not extractable logic; frozen at new size.",
"open-sse/config/imageRegistry.ts": 979,
"open-sse/config/providerRegistry.ts": 4731,
"open-sse/executors/antigravity.ts": 1813,
"open-sse/executors/base.ts": 1540,
"open-sse/executors/chatgpt-web.ts": 3206,
"open-sse/executors/claude-web.ts": 1057,
"open-sse/executors/codex.ts": 1541,
"open-sse/executors/cursor.ts": 1577,
"open-sse/executors/deepseek-web.ts": 1148,
"_rebaseline_2026_06_22_4644_deepseek_web_tools": "PR #4644 (BugsBag/robust deepseek-web tool-call parsing): open-sse/executors/deepseek-web.ts 1117->1125 (+8). The new agentic tool-call path emits surrounding text + reasoning before tool_calls and swaps to the dedicated deepseekWebTools.ts parser; the +8 lines are cohesive wiring at the existing transformSSE chokepoint (the parser itself lives in the new deepseekWebTools.ts file, already under cap). The PR's own fast-gate (PR->release) does not run check:file-size, so this surfaced only at release reconcile. Covered by tests/unit/deepseek-web-tools-variants.test.ts + deepseek-web-tools-execute.test.ts.",
"_rebaseline_2026_06_23_4712_deepseek_web_tool_results": "PR for #4712 (deepseek-web drops role:tool): open-sse/executors/deepseek-web.ts 1125->1148 (+23). messagesToPrompt() now folds role:\"tool\" results into the single-prompt transcript (recovering the tool name from the preceding assistant tool_calls by tool_call_id) instead of silently dropping them; the lines are cohesive wiring inside the existing function. Covered by tests/unit/deepseek-web-tool-result-prompt-4712.test.ts.",
"_rebaseline_2026_06_28_5237_impersonation_ua_refresh": "PR #5237 (refresh impersonation UAs): grok-web.ts 1871->1873 (+2), muse-spark-web.ts 1284->1302 (+18), perplexity-web.ts 1013->1032 (+19). Net semantic change in each file is a single User-Agent constant (Chrome 147->149 for grok/muse; perplexity kept at Firefox 148 to stay matched with the firefox_148 TLS profile — the contributor's 152 bump was reverted to avoid a UA-vs-JA3 mismatch, #2459). The growth is Prettier reflow that lint-staged unavoidably applies to these grandfathered long-line files the moment they are touched; not extractable. src/sse/services/auth.ts 2336->2401 in the same reconcile is #5222's antigravity-LRU-retry growth that merged via --admin without a baseline bump.",
"open-sse/executors/duckduckgo-web.ts": 925,
"open-sse/executors/grok-web.ts": 1873,
"open-sse/executors/hyperagent.ts": 937,
"open-sse/executors/muse-spark-web.ts": 1396,
"open-sse/executors/perplexity-web.ts": 1032,
"open-sse/handlers/audioSpeech.ts": 1061,
"open-sse/handlers/chatCore.ts": 5125,
"open-sse/handlers/imageGeneration.ts": 3777,
"open-sse/handlers/responseSanitizer.ts": 1139,
"open-sse/handlers/search.ts": 1546,
"open-sse/handlers/sseParser.ts": 830,
"open-sse/handlers/videoGeneration.ts": 1275,
"_rebaseline_2026_07_22_8010_codex_responses_engine": "PR #8010 (@JxnLexn) own growth: open-sse/mcp-server/schemas/tools.ts 1497->1505 (+8 = threading the new \"codex-responses\" literal into the compressionConfigureInput strategy/autoTriggerMode Zod enums and setCompressionEngineInput engine enum, mirroring the existing rtk/omniglyph enum entries; no new tool). open-sse/services/compression/strategySelector.ts 1043->1054 (+11 = one new `if (mode === \"codex-responses\")` dispatch branch in runCompression that delegates 100% to the new codexResponsesEngine.apply, mirroring the existing rtk single-mode dispatch, plus threading config.codexResponsesConfig.preserveToolNames into the shared adaptBodyForCompression call at the 3 existing call sites). src/lib/db/compression.ts (untracked, new-file cap 800) 794->845 (+51 = normalizeCodexResponsesConfig, mirroring the existing normalizeRtkConfig normalizer, plus registering \"codex-responses\" in the COMPRESSION_MODES/STACKED_PIPELINE_ENGINE_IDS/SINGLE_MODE_ENGINE sets and the getCompressionSettings load/save switch) — added to the baseline at its current size. All three are cohesive dispatch/normalizer wiring at existing chokepoints (mirroring the prior compression-mode rebaselines #6534/#6556), not extractable without hiding the mode-dispatch boundary. Covered by tests/unit/compression/codex-responses.test.ts (6) + omniglyph-registries.test.ts/types.test.ts (22, updated for the new mode).",
"_rebaseline_2026_07_22_8034_compression_exclusions_persistence": "#8034 (compression exclusions) own growth: src/lib/db/compression.ts 845->850 (+5 = threading the new compressionExclusions field through the existing getCompressionSettings/saveCompressionSettings load/save switch over the shared key_value compression namespace — no new table, no raw SQL). Mirrors the prior compression-field rebaselines (#8010 codex-responses normalizer at the same chokepoint); the load/save switch is a single dispatch boundary, not extractable without hiding it. Covered by the PR's 8 node:test + 3 vitest cases.",
"src/lib/db/compression.ts": 866,
"open-sse/mcp-server/schemas/tools.ts": 1505,
"open-sse/mcp-server/server.ts": 1555,
"open-sse/mcp-server/tools/advancedTools.ts": 1120,
"_rebaseline_2026_06_27_5193_antigravity_basered": "Base-red (pre-existing release drift, fast-gate PR->release skips check:file-size): accountFallback.ts 1773->1777 and src/app/api/providers/[id]/test/route.ts 924->940 were already over their frozen caps on release/v3.8.39 independent of any antigravity change. Owner chose to rebaseline (keep the documented issue-reference comments #1846/#1449/#347 etc.) rather than accept the contributor comment-stripping in #5200/#5198. Reverted #5200 to restore the comments; bumped these two frozen caps to the actual base sizes. No logic change.",
"_rebaseline_2026_07_22_8213_gemini_tpm_quota_cooldown_wait": "PR #8213 (hartmark, fix/gemini-tpm-quota-cooldown-wait) own growth: open-sse/services/accountFallback.ts 1857->1932 on the merged tip (release 1892 incl #8050 +35, plus this PR own growth +40); measured against the PR own merge-base was 1857->1898 (+41 — the release tip separately carries an unrelated +34 from #8050's antigravity 404 model-not-found lockout scoping, which this PR's branch does not include and this entry does not cover). Own growth is the Gemini TPM-ceiling classification + cooldown-wait wiring feeding into the combo cooldown-wait state machine (rate-limit wedge recovery) introduced by this PR's commit series. Irreducible additions at the existing account-fallback/model-lockout chokepoint. Covered by the PR's own gemini-rate-limit-tracker and TPM-ceiling benchmark test additions.",
"_rebaseline_2026_07_23_8252_combo_400_advance": "#8252 (@RaviTharuma) own growth: accountFallback.ts 1932->1940 (+8) + combo.ts 3604->3630 (+26) — advance combo on model-scoped 400s wrapped as invalid/Bad-Request. Irreducible wiring at existing account-fallback + combo dispatch chokepoints. Covered by combo-model-scoped-400-advance.test.ts.",
"open-sse/services/accountFallback.ts": 1940,
"open-sse/services/adobeFireflyClient.ts": 1958,
"open-sse/services/batchProcessor.ts": 915,
"open-sse/services/browserBackedChat.ts": 850,
"open-sse/services/claudeCodeCompatible.ts": 1202,
"_rebaseline_pr4592_exclude_exhausted_auto": "Reconcile #4592 already-merged growth: combo.ts 2991->3036 (+45, terminal-status quota-cutoff exclusion in buildAutoCandidates + opt-in gate). Fast-gate PR->release does not run check:file-size.",
"_rebaseline_2026_06_24_headroom_strategy": "Headroom-aware connection selection (dario technique): combo.ts 3168->3180 (+12 = a new `else if (strategy === \"headroom\")` dispatch branch in handleComboChat that delegates to orderTargetsByHeadroom + its log line, plus the import). The actual logic lives OUT of the god-file: the pure ranker rankByHeadroom/computeHeadroom is the new leaf open-sse/services/combo/headroomRanking.ts (91 LOC, <cap) and the async orderer orderTargetsByHeadroom is appended to the existing open-sse/services/combo/quotaStrategies.ts (<cap) next to its sibling reset-aware/reset-window orderers (reuses their connection-expansion machinery). headroom = 1 - max(util_5h, util_7d) from getSaturation (src/lib/quota/saturationSignals.ts), prefers the connection with the most free capacity. Only the dispatch wiring is irreducible at the existing combo strategy chokepoint (mirrors the reset-aware/reset-window/context-optimized branches); not extractable without hiding the call site. fill-first stays default; all existing strategies untouched. Covered by tests/unit/combo-headroom-ranking.test.ts (pure helper) + tests/unit/combo-headroom-strategy.test.ts (orderer, saturation injected). Structural shrink of combo.ts tracked in #3501.",
"_rebaseline_2026_06_24_quota_share_strategy": "Dedicated quota-share strategy (Phase 3 #9): combo.ts 3180->3190 (+10 = one new `else if (strategy === \"quota-share\")` dispatch branch in handleComboChat that delegates 100% to selectQuotaShareTarget + its log line, plus the import). All the new logic lives OUT of the god-file in two new leaves under open-sse/services/combo/: quotaShareInflight.ts (in-flight counter with TTL/lease, ~150 LOC <cap) and quotaShareStrategy.ts (per-model bucket gating via isBucketSaturated + DRR proportional to weight + P2C over in-flight, ~240 LOC <cap). Only the dispatch wiring is irreducible at the existing combo strategy chokepoint (mirrors the headroom/reset-aware/reset-window/context-optimized branches); not extractable without hiding the call site. ZERO existing strategy cases were modified — only this branch was added, and the qtSd/ combos switched from fill-first to quota-share in src/lib/quota/quotaCombos.ts. Covered by tests/unit/quota-share-strategy.test.ts (gating, DRR fairness, P2C in-flight, fail-open, activation). Structural shrink of combo.ts tracked in #3501.",
"_rebaseline_2026_06_24_task_aware_routing": "Task-aware routing strategy (port PR #2045, OmniRoute #4945): combo.ts 3190->3225 (+35) = one new `else if (strategy === \"task-aware\")` dispatch branch delegating 100% to selectTaskAwareTarget + its imports/log lines. All scoring/classification logic lives OUT of the god-file in the new leaf open-sse/services/taskAwareRouting.ts (553 LOC <cap). Only the dispatch wiring is irreducible at the existing combo strategy chokepoint (mirrors quota-share/headroom/reset-aware branches). ZERO existing strategy cases modified. Covered by tests/unit/combo-task-aware.test.ts (35 tests). Structural shrink of combo.ts tracked in #3501.",
"_rebaseline_2026_07_22_8213_combo_cooldown_wait_recording": "PR #8213 (hartmark, fix/gemini-tpm-quota-cooldown-wait) own growth: open-sse/services/combo.ts 3548->3604 (+56, entirely this PR's diff — no other commit touched this file between the PR's merge-base and the release tip). Fixes combo cooldown-wait state recording so a bogus 503 is no longer crystallized when the cooldown-wait vars reset every setTry, adds an OpenAI-format SSE error frame path for combo-exhausted rejections (capturing request body + attempted models), and gives an abandoned per-target dispatch its own timeout instead of leaking a permanent 'pending' dashboard entry. Irreducible additions at the existing handleComboChat dispatch/retry chokepoint (mirrors the prior quota-share/headroom/task-aware strategy-branch precedents already frozen in this file). Covered by the PR's own combo-config + Gemini TPM-ceiling benchmark test additions.",
"open-sse/services/combo.ts": 3630,
"_rebaseline_2026_06_26_fidelity_gate_extraction": "Milestone-B fidelity-gate wiring residual: bodyToText+gateAdvance extracted to fidelityGateStep.ts (889->854, -35), but the StackOptions.fidelityGate field, the `const fidelityGate` reads at the two stacked-loop dispatch chokepoints, and the import of FidelityGateConfig are irreducible wiring that cannot leave strategySelector without an architectural refactor of the pre-existing stacked pipeline. Net: 889->854 (+6 vs the pre-Milestone-B frozen 848). Covered by tests/unit/compression/*.test.ts (940 pass).",
"_rebaseline_2026_06_27_5193_5203_antigravity_oauthmodal": "Antigravity remote-login own growth: OAuthModal.tsx 960->969 (gate units). #5193 (+~4: remote paste instruction shown for all remote incl. Google + its rationale comment) and #5203 (+~5: handleManualSubmit credential-blob branch + button guard; submit logic extracted to oauthBlobSubmit.ts to minimize). Frozen set to the SUM so either merge order passes. Cohesive at the existing manual-submit chokepoint.",
"_rebaseline_2026_06_27_5193_antigravity_basered": "Base-red (pre-existing release drift, fast-gate PR->release skips check:file-size): accountFallback.ts 1773->1777 and src/app/api/providers/[id]/test/route.ts 924->940 were already over their frozen caps on release/v3.8.39 independent of any antigravity change. Owner chose to rebaseline (keep the documented issue-reference comments #1846/#1449/#347 etc.) rather than accept the contributor comment-stripping in #5200/#5198. Reverted #5200 to restore the comments; bumped these two frozen caps to the actual base sizes. No logic change.",
"_rebaseline_2026_06_28_5237_impersonation_ua_refresh": "PR #5237 (refresh impersonation UAs): grok-web.ts 1871->1873 (+2), muse-spark-web.ts 1284->1302 (+18), perplexity-web.ts 1013->1032 (+19). Net semantic change in each file is a single User-Agent constant (Chrome 147->149 for grok/muse; perplexity kept at Firefox 148 to stay matched with the firefox_148 TLS profile — the contributor's 152 bump was reverted to avoid a UA-vs-JA3 mismatch, #2459). The growth is Prettier reflow that lint-staged unavoidably applies to these grandfathered long-line files the moment they are touched; not extractable. src/sse/services/auth.ts 2336->2401 in the same reconcile is #5222's antigravity-LRU-retry growth that merged via --admin without a baseline bump.",
"_rebaseline_2026_06_28_5243_risk_gate_prepass": "PR #5243 (compression risk-gate pre-pass) own growth: open-sse/services/compression/strategySelector.ts 854->899 (+45). The three exported entry points (applyCompression/applyStackedCompression/applyStackedCompressionAsync) become thin wrappers over pure-extracted private bodies (runCompression/runStackedCompression/runStackedCompressionAsync) so the risk-gate mask->run->restore wrapper sits strictly OUTSIDE the per-step loop — a single universal integration point. The wrapper logic itself (resolveRiskGate/withRiskGate) lives in the new riskGate/strategyWrap.ts (<cap); the residual growth is the duplicated thin-wrapper signatures + the extracted bodies' dispatch boundary, guarded by a byte-identical parity test (riskGateIntegration). Default off (DEFAULT_COMPRESSION_CONFIG unchanged). Not extractable without hiding the dispatch boundary, mirroring prior compression rebaselines. Structural shrink tracked in #3501.",
"_rebaseline_2026_06_28_5275_correlation_id_extract": "Extraction of the safe CorrelationId subset of #5275 (hartmark) — request correlation id stored in call_logs (migration 109) and returned via the X-Correlation-Id response header, WITHOUT the combo/resilience or build/lazy-loading changes (those stay in #5275). Own growth: callLogs.ts 975->985 (correlation_id column on CallLogSummaryRow + read/map), usageHistory.ts 983->988 (correlationId metadata normalize), chat.ts 1575->1632 (withCorrelationId response wiring + combo-failure log carrying correlationId), chatHelpers.ts new 811 (withCorrelationId helper + reqId threading; was 791<cap pre-feature). Cohesive request/logging chokepoint wiring; structural shrink of chat.ts tracked in #3501.",
"_rebaseline_2026_06_29_4038_cas_guard": "PR (#4038) own growth: tokenRefresh.ts 2103->2181 (+78 = the compare-and-swap guard on the refresh persist — runWithCasGuard/getActiveCasGuard AsyncLocalStorage pair mirroring runWithOnPersist, casGuardShouldSkipPersist that rereads the row right before persisting and skips the write when a concurrent writer already rotated the refresh_token past the one presented, plus getCasGuardStats counters). Fixes the sibling-rotation-revert → token-family-revocation storm. Gated behind an active guard (opt-in; no guard => byte-identical). Wiring lives at the two persist chokepoints inside getAccessToken; the comparison reuses wasRefreshTokenRotated from refreshSerializer. Not extractable without splitting the refresh hot path.",
"_rebaseline_2026_06_29_5286_memoization": "PR #5286 own growth: strategySelector.ts 899->960 (+61 = the opt-in result-memoization branches in applyCompression/applyCompressionAsync — principal+determinism gate, makeMemoKey lookup/store with model+supportsVision folded into the key, recompute-with-memo-off). Default off (memoizeCompressionResults), so zero behavior change. The memo helpers live in the leaf resultMemo.ts (<cap); the chokepoint wiring here is not extractable. Structural shrink of this hot-path file tracked in #3501.",
"_rebaseline_2026_07_01_v3843_release_5609": "Rebaseline v3.8.43 (PR #5609 release reconciliation). DRIFT dos 109 commits do ciclo: 8 god-files existentes cresceram (ApiManagerPageClient 2983->3017, combos/page 4594->4608, AddApiKeyModal 868->869, providerPageHelpers 974->996, chat.ts 1635->1647, auth.ts 2401->2403, batchProcessor 828->915, combo.ts 3368->3387) + 2 novos acima do cap (huggingchat.ts 813, tests web-cookie-providers-new 827) + 4 test files cresceram. Modularizacao deferida (blast-radius mid-release); congelado no estado atual p/ o proximo ciclo ratchetar daqui.",
"_rebaseline_2026_07_02_5816_qoder": "PR #5816 (@AgentKiller45, qoder PAT via qodercli): qoderCli.ts 666->989, new-above-cap frozen (owner-approved baseline freeze). The growth is the legitimate PAT job-token exchange + quota parsing CLI transport (the pure-JS Cosy path 500'd on every PAT request); extracting the spawn/parse helpers now would just add indirection to a contributor PR mid-merge. Test frozen also raised for this PR's coverage growth: providers-page-utils.test.ts 1052->1092. Additionally clears an inherited base-red from the already-merged #5933 (codex json_schema->text.format): translator-openai-responses-req.test.ts 1097->1172 (+75 regression tests, no offending branch left). All remain frozen (cannot grow further); release captain's rebaseline-at-release supersedes.",
"open-sse/services/compression/strategySelector.ts": 1060,
"_rebaseline_2026_07_10_gcf_v3_2_decode": "PR #6838 own growth: new vendored file open-sse/services/compression/engines/headroom/gcf/decode_generic.ts frozen at 880 (> 800 cap). It is the vendored GCF generic-profile decoder (spec v3.2 nested flattening plus the prototype-pollution / hasOwnProperty hardening added in this PR's Gemini review). Kept as one file faithful to upstream gcf-typescript so re-vendoring stays a clean copy rather than a re-split each cycle (sibling generic.ts/scalar.ts stay < cap; extraction would also fragment the file's frozen eslint no-explicit-any suppressions). Round-trip + prototype-pollution regression coverage in tests/unit/compression/headroom-smartcrusher.test.ts. Frozen: only shrinks from here.",
"open-sse/services/compression/engines/headroom/gcf/decode_generic.ts": 880,
"open-sse/services/rateLimitManager.ts": 1035,
"_rebaseline_2026_06_29_4038_cas_guard": "PR (#4038) own growth: tokenRefresh.ts 2103->2181 (+78 = the compare-and-swap guard on the refresh persist — runWithCasGuard/getActiveCasGuard AsyncLocalStorage pair mirroring runWithOnPersist, casGuardShouldSkipPersist that rereads the row right before persisting and skips the write when a concurrent writer already rotated the refresh_token past the one presented, plus getCasGuardStats counters). Fixes the sibling-rotation-revert → token-family-revocation storm. Gated behind an active guard (opt-in; no guard => byte-identical). Wiring lives at the two persist chokepoints inside getAccessToken; the comparison reuses wasRefreshTokenRotated from refreshSerializer. Not extractable without splitting the refresh hot path.",
"_rebaseline_2026_07_09_6126_clinepass_dual_auth": "PR #6126 (@hajilok, dual-auth ClinePass) own growth: tokenRefresh.ts 2181->2182 (+1 = a single `case \"clinepass\":` fallthrough label added to the existing `case \"cline\":` in _getAccessTokenInternal's provider switch, so clinepass token refresh dispatches to the already-shared refreshClineToken() instead of silently falling through to the generic OAuth refresh). Irreducible 1-line switch-case wiring at the existing chokepoint; the header-building logic for the same feature was extracted to a new leaf src/shared/utils/clineAuth.ts::buildClinepassHeaders() (well under cap) to avoid growing open-sse/executors/default.ts. Covered by tests/unit/clinepass-provider.test.ts.",
"_rebaseline_2026_07_09_6363_kiro_external_idp": "PR #6363 (@artickc, Kiro external IdP) own growth: tokenRefresh.ts 2182->2249 (+67 = the external_idp refresh branch inside refreshKiroToken — standard public-client OAuth2 refresh_token grant against the org IdP tokenEndpoint via buildExternalIdpRefreshParams/isExternalIdpAuthMethod from the new leaf open-sse/services/kiroExternalIdp.ts, with invalid_grant/invalid_client -> unrecoverable_refresh_error mapping). Cohesive addition at the existing refreshKiroToken chokepoint. Covered by tests/unit/kiro-external-idp.test.ts.",
"_rebaseline_2026_07_09_6587_kiro_api_key_auth": "PR #6587 (@strangersp) own growth for Kiro long-lived API-key auth, merged onto v3.8.47 tip: openai-to-kiro.ts 890->912 (+22, auth-header selection for API-key-vs-OAuth-token connections), providerLimits.ts 998->1000 (+2, API-key auth-type branch), translator-openai-to-kiro.test.ts 1234->1257 (+23), providers-page-utils.test.ts 1109->1107 (net -2 after merging with parallel release drift; connectionMatchesProviderCard api_key coverage added), provider-validation-specialty.test.ts 2856->2980 (+124 net after merge with parallel release drift; this PR also removed the file's `@typescript-eslint/no-explicit-any` eslint-suppression entry by fixing all `any` usages, adding typed replacements). Cohesive additive feature growth, well tested; not extractable without splitting the existing chokepoints mid-merge.",
"_rebaseline_2026_07_09_6678_routing_strategy_9router": "#6678 (SeaXen) — 9router-parity Routing Strategy settings card + per-provider/combo sticky-round-robin override. Own growth: ProviderDetailPageClient.tsx 784->786 (single ProviderAccountRoutingCard mount + import), auth.ts 2448->2458 (providerStrategies override resolution: fallbackStrategy/stickyRoundRobinLimit per-provider cascade in getProviderCredentials). Both additive, zero unrelated refactor; new UI/logic lives in new files (ProviderAccountRoutingCard.tsx, RoutingStrategyCard.tsx, rrState.ts::resolveComboStickyRoundRobinLimit). chat.ts value below reflects the current release tip (grown by other concurrent PRs, e.g. #6640), not this PR own change.",
"_rebaseline_2026_07_10_6318_omp_letta": "PR #6318 (@hamsa0x7, omp+letta CLI integrations) own growth: cliTools.ts (+53 = 2 registry entries incl. omp docsUrl) and cliRuntime.ts (+18 = runtime-detection wiring for the 2 new tools). Cohesive registry/wiring growth at the existing chokepoints; scope reduced from the original 5 tools (pi/codewhale/jcode shipped separately).",
"_rebaseline_2026_07_10_gcf_v3_2_decode": "PR #6838 own growth: new vendored file open-sse/services/compression/engines/headroom/gcf/decode_generic.ts frozen at 880 (> 800 cap). It is the vendored GCF generic-profile decoder (spec v3.2 nested flattening plus the prototype-pollution / hasOwnProperty hardening added in this PR's Gemini review). Kept as one file faithful to upstream gcf-typescript so re-vendoring stays a clean copy rather than a re-split each cycle (sibling generic.ts/scalar.ts stay < cap; extraction would also fragment the file's frozen eslint no-explicit-any suppressions). Round-trip + prototype-pollution regression coverage in tests/unit/compression/headroom-smartcrusher.test.ts. Frozen: only shrinks from here.",
"_rebaseline_2026_07_12_v3847_mergeprs_tail": "v3.8.47 /merge-prs tail (owner-approved): src/lib/localDb.ts NEW>800 (799->805, +6 re-exports countFreeProxies + recordFreeProxySyncErrors/clearFreeProxySyncErrors/getFreeProxySyncErrors + FreeProxySyncErrors type for #6909 free-pool relay-repair; re-export-only per Hard Rule #2, not extractable).",
"open-sse/services/tokenRefresh.ts": 2249,
"open-sse/services/usage.ts": 3454,
"open-sse/translator/request/openai-to-gemini.ts": 906,
"open-sse/translator/request/openai-to-kiro.ts": 912,
"_rebaseline_2026_07_22_8211_gemini_malformed_tool_choice": "PR #8211 (hartmark, fix/gemini-malformed-function-call-tool-choice) own growth: open-sse/translator/response/gemini-to-openai.ts 771->821 (+50, entirely this PR's diff — no other commit touched this file between the PR's merge-base and the release tip). Adds MALFORMED_FUNCTION_CALL/UNEXPECTED_TOOL_CALL handling inside geminiToOpenAIResponse(): synthesizes a `malformed_tool_call` tool_calls entry so finish_reason normalizes to the standard \"tool_calls\" instead of an unrecognized raw enum value that OpenAI-compatible clients (e.g. OpenClaw) silently ignore, and always synthesizes (rather than skipping when a real tool call already exists) so a malformed attempt alongside a real one in the same turn is not silently discarded. Irreducible cohesive addition at the existing candidate/finishReason translation chokepoint (mirrors the 9router#2462 raw-finish-reason precedent immediately below it in the same function). Covered by the PR's own tests/unit test additions for both the malformed-only and malformed-plus-real-call cases.",
"open-sse/translator/response/gemini-to-openai.ts": 821,
"_rebaseline_2026_07_22_7936_namespace_roundtrip": "#7936 (@RCrushMe, Responses-Chat namespace round-trip identity seam) own growth: open-sse/translator/response/openai-responses.ts 1092->1125 (+33) and open-sse/utils/stream.ts 2814->2869 (+55) — threading the namespace-identity seam through the Responses↔Chat translation + stream paths so tool-call namespaces survive the round-trip. Cohesive translation/stream wiring at existing chokepoints, frozen at new size.",
"_rebaseline_2026_07_22_8210_openrouter_midstream_error": "PR #8210 (hartmark, fix/openrouter-midstream-error-surfacing) own growth: open-sse/translator/response/openai-responses.ts 1137->1163 (+26) measured on the merged tip (release 1137 + this PR own growth). Adds a single new branch inside openaiToOpenAIResponsesResponse() that detects an OpenRouter-style mid-stream aggregator error (HTTP 200 SSE chunk with empty choices + a top-level error object) and surfaces it as state.upstreamError instead of silently falling through to the no-op/awaitingTrailingUsage path, which previously masked the failure as a false empty-success completion and skipped combo fallback. Irreducible call-site addition at the existing chunk-dispatch chokepoint (mirrors the Gemini-to-OpenAI translator's #4177 precedent for the same class of upstream error surfacing). Note: this baseline entry does NOT cover the separate pre-existing +11 drift already on the release tip from #8081/#8162 (1125->1136, unrelated reasoning-placeholder-stripping fix merged after this PR branched) — that drift belongs to the maintainer's rebaseline, not this PR.",
"open-sse/translator/response/openai-responses.ts": 1163,
"open-sse/utils/cursorAgentProtobuf.ts": 1521,
"_rebaseline_2026_07_23_8143_empty_catch_logging": "#8143 (@chirag127) own growth: open-sse/utils/stream.ts 2869->2887 (+18) — replacing empty catch blocks in the SSE stream subsystem with console.debug logging (Rule #6 silent-swallow fix, issues #8138-#8142). Cohesive logging additions at the existing catch chokepoints, not extractable; frozen at new size. Covered by tests/unit/stream-handler-catch-logging-8143.test.ts.",
"open-sse/utils/stream.ts": 2887,
"src/app/(dashboard)/dashboard/HomePageClient.tsx": 1385,
"src/app/(dashboard)/dashboard/analytics/ComboHealthTab.tsx": 1031,
"src/app/(dashboard)/dashboard/api-manager/ApiManagerPageClient.tsx": 3120,
"src/app/(dashboard)/dashboard/cache/media/MediaPageClient.tsx": 1105,
"src/app/(dashboard)/dashboard/cache/page.tsx": 845,
"src/app/(dashboard)/dashboard/cli-code/components/CodexToolCard.tsx": 900,
"src/app/(dashboard)/dashboard/cloud-agents/page.tsx": 931,
"_rebaseline_2026_07_15_7070_combos_memo": "PR #7070 (perf/p1-memo) own growth: src/app/(dashboard)/dashboard/combos/page.tsx 4655->4656 (+1 = React.memo wrapping of ComboCard). Covered by tests/unit/ui/combos-page-smoke.test.tsx.",
"src/app/(dashboard)/dashboard/combos/page.tsx": 4656,
"src/app/(dashboard)/dashboard/costs/CostOverviewTab.tsx": 1495,
"src/app/(dashboard)/dashboard/costs/quota-share/components/PoolWizard.tsx": 1022,
"src/app/(dashboard)/dashboard/endpoint/EndpointPageClient.tsx": 2615,
"_rebaseline_2026_07_22_8213_health_unblock_model_cooldowns": "PR #8213 (hartmark, fix/gemini-tpm-quota-cooldown-wait) own growth: src/app/(dashboard)/dashboard/health/page.tsx 1094->1165 (+71, entirely this PR's diff — no other commit touched this file between the PR's merge-base and the release tip). Adds handleUnblockAll/handleUnblockOne dashboard actions (DELETE /api/resilience/model-cooldowns) so an operator can manually clear a Gemini TPM-wedge model lockout surfaced by this PR's cooldown-wait fixes, instead of waiting out the ceiling. Irreducible UI wiring at the existing health-page action chokepoint. Covered by the PR's own dashboard/resilience test additions.",
"src/app/(dashboard)/dashboard/health/page.tsx": 1165,
"src/app/(dashboard)/dashboard/playground/components/tabs/ApiTab.tsx": 847,
"src/app/(dashboard)/dashboard/providers/[id]/ProviderDetailPageClient.tsx": 804,
"src/app/(dashboard)/dashboard/providers/[id]/components/ConnectionRow.tsx": 958,
"src/app/(dashboard)/dashboard/providers/[id]/components/modals/AddApiKeyModal.tsx": 967,
"src/app/(dashboard)/dashboard/providers/[id]/components/modals/EditConnectionModal.tsx": 1288,
"src/app/(dashboard)/dashboard/providers/[id]/hooks/useProviderConnections.ts": 986,
"src/app/(dashboard)/dashboard/providers/[id]/hooks/useProviderModels.ts": 155,
"src/app/(dashboard)/dashboard/providers/[id]/hooks/useProviderSettings.ts": 264,
"src/app/(dashboard)/dashboard/providers/[id]/providerPageHelpers.ts": 1054,
"src/app/(dashboard)/dashboard/providers/components/onboarding/ProviderOnboardingWizard.tsx": 948,
"src/app/(dashboard)/dashboard/providers/page.tsx": 1927,
"src/app/(dashboard)/dashboard/runtime/RuntimePageClient.tsx": 1201,
"src/app/(dashboard)/dashboard/settings/components/AppearanceTab.tsx": 819,
"src/app/(dashboard)/dashboard/settings/components/ComboDefaultsTab.tsx": 903,
"src/app/(dashboard)/dashboard/settings/components/CompressionSettingsTab.tsx": 974,
"src/app/(dashboard)/dashboard/settings/components/MemorySkillsTab.tsx": 898,
"src/app/(dashboard)/dashboard/settings/components/PricingTab.tsx": 1019,
"src/app/(dashboard)/dashboard/settings/components/ProxyRegistryManager.tsx": 1464,
"src/app/(dashboard)/dashboard/settings/components/ResilienceTab.tsx": 1183,
"src/app/(dashboard)/dashboard/settings/components/RoutingTab.tsx": 1629,
"src/app/(dashboard)/dashboard/settings/components/SystemStorageTab.tsx": 1924,
"src/app/(dashboard)/dashboard/usage/components/BudgetTab.tsx": 1028,
"src/app/(dashboard)/dashboard/usage/components/EvalsTab.tsx": 2148,
"src/app/(dashboard)/dashboard/usage/components/ProviderLimits/index.tsx": 1127,
"src/app/api/oauth/[provider]/[action]/route.ts": 970,
"src/app/api/providers/[id]/models/route.ts": 2593,
"src/app/api/providers/[id]/test/route.ts": 940,
"src/app/api/usage/analytics/route.ts": 948,
"src/app/api/v1/models/catalog.ts": 1615,
"src/lib/cloudflaredTunnel.ts": 935,
"src/lib/db/apiKeys.ts": 1662,
"src/lib/db/core.ts": 1825,
"src/lib/db/migrationRunner.ts": 1125,
"src/lib/db/models.ts": 1259,
"src/lib/db/providers.ts": 1107,
"src/lib/db/proxies.ts": 1177,
"src/lib/db/settings.ts": 1155,
"src/lib/db/usageAnalytics.ts": 925,
"src/lib/evals/evalRunner.ts": 961,
"src/lib/memory/retrieval.ts": 1171,
"src/lib/modelsDevSync.ts": 934,
"src/lib/providers/validation.ts": 4523,
"src/lib/resilience/settings.ts": 841,
"src/lib/tailscaleTunnel.ts": 1202,
"src/lib/usage/callLogs.ts": 997,
"src/lib/usage/providerLimits.ts": 1006,
"src/lib/usage/usageHistory.ts": 988,
"_rebaseline_2026_06_27_5193_5203_antigravity_oauthmodal": "Antigravity remote-login own growth: OAuthModal.tsx 960->969 (gate units). #5193 (+~4: remote paste instruction shown for all remote incl. Google + its rationale comment) and #5203 (+~5: handleManualSubmit credential-blob branch + button guard; submit logic extracted to oauthBlobSubmit.ts to minimize). Frozen set to the SUM so either merge order passes. Cohesive at the existing manual-submit chokepoint.",
"_rebaseline_2026_07_18_7399_xai_oauth_modal": "PR #7399 (xAI OAuth PKCE) own growth: OAuthModal.tsx 993->998 (+5 = provider entry + PKCE flow branch wiring at the existing provider-switch chokepoint; the provider logic itself lives in src/lib/oauth/providers/xai-oauth.ts, new leaf). Third irreducible wiring bump on this modal (969->989->993->998); structural shrink tracked in #3501.",
"_rebaseline_2026_07_19_6636_codex_session_json": "#6636 own growth: OAuthModal.tsx 998->1030 (gate units, split(\"\\n\").length incl. trailing newline; +32 = session-JSON paste branch for handleManualSubmit plus a shared submitCodexAccessToken() helper extracted from the pre-existing bare-JWT branch, mirroring the #5203 oauthBlobSubmit.ts extraction precedent; the normalizer logic itself lives in the new src/lib/oauth/utils/codexSessionImport.ts leaf module, not here). Fourth irreducible wiring bump on this modal (969->989->993->998->1030); structural shrink tracked in #3501.",
"_rebaseline_2026_07_19_7546_ghe_copilot_modal": "PR #7546 (GHE Copilot OAuth provider) own growth: OAuthModal.tsx 1030->1056 (gate units). Adds a gheUrl input state, routes ghe-copilot through the existing device-code branch, and threads gheUrl into the device-code request/poll extraData at the existing provider-switch chokepoints (+~24 lines, cohesive with the same pattern as #7399/#6636). The standalone GHE enterprise-URL config step JSX (originally +31 lines inline) was extracted to the new src/shared/components/oauthModal/GheConfigStep.tsx leaf component to minimize the bump; what remains is the irreducible provider-branch wiring. Fifth bump on this modal (969->989->993->998->1030->1056); structural shrink tracked in #3501.",
"_rebaseline_2026_07_19_7787_ic2_localdb_reexports": "PR #7787 (IC2 raw connections cache + lazy-decrypt) own growth: localDb.ts 805->807 (gate units, +2). localDb.ts is the re-export-only layer (hard rule #2 — no logic); the PR adds 4 new db/readCache re-exports (touchConnectionLastUsed, getCachedRawProviderConnections, getCachedProviderConnectionById, getCachedProviderNodes) required by existing barrel importers. Irreducible for a re-export list; frozen so it can only shrink.",
"_rebaseline_2026_07_20_7779_routingcombo_thread": "PR #7779 own growth: chatHelpers.ts 876->877 (+1, thread routingComboId into executeChatWithBreaker for compression-combo assignment). Frozen so it can only shrink.",
"_rebaseline_2026_07_20_7819_autocandidateoverrides_reexport": "PR for #7819 (Level 1+2: read-only auto/* candidate transparency + per-API-key exclusions) own growth: localDb.ts 807->808 (+1). Adds a single `export * from \"./db/autoCandidateOverrides\"` barrel re-export (hard rule #2 — no logic) for the new DB module backing per-apiKey candidate exclusions. Irreducible for a re-export list; frozen so it can only shrink.",
"_rebaseline_2026_07_21_8027_grok_cli_auth_json_paste": "PR #8027 (RaviTharuma, fix(grok-cli) #7610) own growth: OAuthModal.tsx 1080->1100 (gate units). Requires the full ~/.grok/auth.json (with refresh_token) on the paste-import path instead of a bare JWT, at the existing paste-token chokepoint (renamed tab label, updated instructions/placeholder, textarea for the auth.json blob, inline error surface). The validation logic itself (parseGrokCliPasteToken, previously an inline ~75-line function) was extracted to the new src/lib/oauth/utils/grokCliAuthJson.ts leaf module — mirroring the #6636/#7546 extraction precedent — so only the irreducible UI wiring remains here. Sixth bump on this modal (969->989->993->998->1030->1056->1100); structural shrink tracked in #3501.",
"src/shared/components/OAuthModal.tsx": 1100,
"_rebaseline_2026_07_22_8213_requestloggerdetail_unblock_ui": "PR #8213 (hartmark, fix/gemini-tpm-quota-cooldown-wait) own growth: src/shared/components/RequestLoggerDetail.tsx 799->941 (+142, entirely this PR's diff — no other commit touched this file between the PR's merge-base and the release tip; crosses the general 800-line new-file cap so is frozen here for the first time). Adds a collapsible section header (open/expand-less toggle) plus per-log-entry unblock (`unblocking`/`cleared` state, isCombo503 detection) so the request-logger detail panel surfaces the same Gemini TPM cooldown-wait / model-lockout unblock action introduced by this PR at the individual-request level (mirrors the health-page bulk unblock action added in the same PR). Covered by the PR's own dashboard/resilience test additions.",
"src/shared/components/RequestLoggerDetail.tsx": 941,
"src/shared/components/RequestLoggerV2.tsx": 1629,
"src/shared/components/analytics/charts.tsx": 1558,
"_rebaseline_2026_07_10_6318_omp_letta": "PR #6318 (@hamsa0x7, omp+letta CLI integrations) own growth: cliTools.ts (+53 = 2 registry entries incl. omp docsUrl) and cliRuntime.ts (+18 = runtime-detection wiring for the 2 new tools). Cohesive registry/wiring growth at the existing chokepoints; scope reduced from the original 5 tools (pi/codewhale/jcode shipped separately).",
"src/shared/constants/cliTools.ts": 916,
"src/shared/constants/pricing.ts": 1662,
"src/shared/constants/providers.ts": 3276,
"src/shared/constants/sidebarVisibility.ts": 1198,
"src/shared/services/cliRuntime.ts": 1128,
"src/shared/validation/schemas.ts": 2523,
"_rebaseline_2026_06_28_5275_correlation_id_extract": "Extraction of the safe CorrelationId subset of #5275 (hartmark) — request correlation id stored in call_logs (migration 109) and returned via the X-Correlation-Id response header, WITHOUT the combo/resilience or build/lazy-loading changes (those stay in #5275). Own growth: callLogs.ts 975->985 (correlation_id column on CallLogSummaryRow + read/map), usageHistory.ts 983->988 (correlationId metadata normalize), chat.ts 1575->1632 (withCorrelationId response wiring + combo-failure log carrying correlationId), chatHelpers.ts new 811 (withCorrelationId helper + reqId threading; was 791<cap pre-feature). Cohesive request/logging chokepoint wiring; structural shrink of chat.ts tracked in #3501.",
"_rebaseline_2026_07_09_6678_routing_strategy_9router": "#6678 (SeaXen) — 9router-parity Routing Strategy settings card + per-provider/combo sticky-round-robin override. Own growth: ProviderDetailPageClient.tsx 784->786 (single ProviderAccountRoutingCard mount + import), auth.ts 2448->2458 (providerStrategies override resolution: fallbackStrategy/stickyRoundRobinLimit per-provider cascade in getProviderCredentials). Both additive, zero unrelated refactor; new UI/logic lives in new files (ProviderAccountRoutingCard.tsx, RoutingStrategyCard.tsx, rrState.ts::resolveComboStickyRoundRobinLimit). chat.ts value below reflects the current release tip (grown by other concurrent PRs, e.g. #6640), not this PR own change.",
"_rebaseline_2026_07_22_8213_chat_abandoned_target_abort": "PR #8213 (hartmark, fix/gemini-tpm-quota-cooldown-wait) own growth: src/sse/handlers/chat.ts 1794->1860 (+66, measured against the PR's own merge-base — the release tip separately carries an unrelated -5 net shrink from #8013's antigravity callable-catalog alignment, which this PR's branch does not include and this entry does not cover). Adds resolveDispatchClientRawRequest(): merges a per-target modelAbortSignal into clientRawRequest.signal (via mergeAbortSignals) so a combo target abandoned by comboTargetTimeoutMs actually observes its own abort and reaches its cleanup path, instead of hanging forever inside withRateLimit/acquireAccountSemaphore and leaking a permanent 'pending' dashboard entry (live incident, log id 1784418258231-14961a). Also wires combo-exhausted rejection logging to capture request body + attempted models via the new rejectedRequestUsage helper. Irreducible additions at the existing chat dispatch chokepoint. Covered by the PR's own combo-config + integration test additions.",
"_rebaseline_2026_07_23_8127_grok_weekly_quota": "#8127 (@apoapostolov) own growth: src/sse/handlers/chat.ts 1861->1865 (+4) — weekly quota tracking for grok-web wires a quota-fetch hook at the existing dispatch chokepoint. Thin wiring mirroring adjacent provider-quota branches; not extractable. Covered by tests/unit/grok-quota-fetcher.test.ts.",
"src/sse/handlers/chat.ts": 1865,
"_rebaseline_2026_07_20_7779_routingcombo_thread": "PR #7779 own growth: chatHelpers.ts 876->877 (+1, thread routingComboId into executeChatWithBreaker for compression-combo assignment). Frozen so it can only shrink.",
"src/sse/handlers/chatHelpers.ts": 878,
"src/sse/services/auth.ts": 2475,
"open-sse/executors/default.ts": 890,
"open-sse/translator/request/openai-responses.ts": 902,
"open-sse/executors/kiro.ts": 944,
"open-sse/translator/request/openai-to-claude.ts": 823,
"tests/unit/account-fallback-service.test.ts": 1572,
"tests/unit/provider-validation-specialty.test.ts": 2980,
"open-sse/executors/huggingchat.ts": 813,
"_rebaseline_2026_07_01_v3843_release_5609": "Rebaseline v3.8.43 (PR #5609 release reconciliation). DRIFT dos 109 commits do ciclo: 8 god-files existentes cresceram (ApiManagerPageClient 2983->3017, combos/page 4594->4608, AddApiKeyModal 868->869, providerPageHelpers 974->996, chat.ts 1635->1647, auth.ts 2401->2403, batchProcessor 828->915, combo.ts 3368->3387) + 2 novos acima do cap (huggingchat.ts 813, tests web-cookie-providers-new 827) + 4 test files cresceram. Modularizacao deferida (blast-radius mid-release); congelado no estado atual p/ o proximo ciclo ratchetar daqui.",
"src/lib/providers/validation/webProvidersA.ts": 809,
"src/lib/tokenHealthCheck.ts": 832,
"_rebaseline_2026_07_09_6587_kiro_api_key_auth": "PR #6587 (@strangersp) own growth for Kiro long-lived API-key auth, merged onto v3.8.47 tip: openai-to-kiro.ts 890->912 (+22, auth-header selection for API-key-vs-OAuth-token connections), providerLimits.ts 998->1000 (+2, API-key auth-type branch), translator-openai-to-kiro.test.ts 1234->1257 (+23), providers-page-utils.test.ts 1109->1107 (net -2 after merging with parallel release drift; connectionMatchesProviderCard api_key coverage added), provider-validation-specialty.test.ts 2856->2980 (+124 net after merge with parallel release drift; this PR also removed the file's `@typescript-eslint/no-explicit-any` eslint-suppression entry by fixing all `any` usages, adding typed replacements). Cohesive additive feature growth, well tested; not extractable without splitting the existing chokepoints mid-merge.",
"_rebaseline_2026_07_19_7787_ic2_localdb_reexports": "PR #7787 (IC2 raw connections cache + lazy-decrypt) own growth: localDb.ts 805->807 (gate units, +2). localDb.ts is the re-export-only layer (hard rule #2 — no logic); the PR adds 4 new db/readCache re-exports (touchConnectionLastUsed, getCachedRawProviderConnections, getCachedProviderConnectionById, getCachedProviderNodes) required by existing barrel importers. Irreducible for a re-export list; frozen so it can only shrink.",
"_rebaseline_2026_07_20_7819_autocandidateoverrides_reexport": "PR for #7819 (Level 1+2: read-only auto/* candidate transparency + per-API-key exclusions) own growth: localDb.ts 807->808 (+1). Adds a single `export * from \"./db/autoCandidateOverrides\"` barrel re-export (hard rule #2 — no logic) for the new DB module backing per-apiKey candidate exclusions. Irreducible for a re-export list; frozen so it can only shrink.",
"src/lib/localDb.ts": 808,
"_rebaseline_2026_07_12_v3847_mergeprs_tail": "v3.8.47 /merge-prs tail (owner-approved): src/lib/localDb.ts NEW>800 (799->805, +6 re-exports countFreeProxies + recordFreeProxySyncErrors/clearFreeProxySyncErrors/getFreeProxySyncErrors + FreeProxySyncErrors type for #6909 free-pool relay-repair; re-export-only per Hard Rule #2, not extractable).",
"_rebaseline_2026_07_21_8034_compression_exclusions_sidebar": "#8034 (compression exclusions dashboard tab) own growth: sections.ts 796->806 (+10, one new COMPRESSION_CONTEXT_GROUP sidebar item linking /dashboard/compression/exclusions). The file was already 796/800 before this PR (organic growth from prior sidebar entries), so a single new nav item pushed it 6 lines over cap. Freezing at 806 (cannot grow further); the sidebar item array is data, not extractable logic.",
"_rebaseline_2026_07_22_7936_namespace_roundtrip": "#7936 (@RCrushMe, Responses-Chat namespace round-trip identity seam) own growth: open-sse/translator/response/openai-responses.ts 1092->1125 (+33) and open-sse/utils/stream.ts 2814->2869 (+55) — threading the namespace-identity seam through the Responses↔Chat translation + stream paths so tool-call namespaces survive the round-trip. Cohesive translation/stream wiring at existing chokepoints, frozen at new size.",
"_rebaseline_2026_07_22_8010_codex_responses_engine": "PR #8010 (@JxnLexn) own growth: open-sse/mcp-server/schemas/tools.ts 1497->1505 (+8 = threading the new \"codex-responses\" literal into the compressionConfigureInput strategy/autoTriggerMode Zod enums and setCompressionEngineInput engine enum, mirroring the existing rtk/omniglyph enum entries; no new tool). open-sse/services/compression/strategySelector.ts 1043->1054 (+11 = one new `if (mode === \"codex-responses\")` dispatch branch in runCompression that delegates 100% to the new codexResponsesEngine.apply, mirroring the existing rtk single-mode dispatch, plus threading config.codexResponsesConfig.preserveToolNames into the shared adaptBodyForCompression call at the 3 existing call sites). src/lib/db/compression.ts (untracked, new-file cap 800) 794->845 (+51 = normalizeCodexResponsesConfig, mirroring the existing normalizeRtkConfig normalizer, plus registering \"codex-responses\" in the COMPRESSION_MODES/STACKED_PIPELINE_ENGINE_IDS/SINGLE_MODE_ENGINE sets and the getCompressionSettings load/save switch) — added to the baseline at its current size. All three are cohesive dispatch/normalizer wiring at existing chokepoints (mirroring the prior compression-mode rebaselines #6534/#6556), not extractable without hiding the mode-dispatch boundary. Covered by tests/unit/compression/codex-responses.test.ts (6) + omniglyph-registries.test.ts/types.test.ts (22, updated for the new mode).",
"_rebaseline_2026_07_22_8034_compression_exclusions_persistence": "#8034 (compression exclusions) own growth: src/lib/db/compression.ts 845->850 (+5 = threading the new compressionExclusions field through the existing getCompressionSettings/saveCompressionSettings load/save switch over the shared key_value compression namespace — no new table, no raw SQL). Mirrors the prior compression-field rebaselines (#8010 codex-responses normalizer at the same chokepoint); the load/save switch is a single dispatch boundary, not extractable without hiding it. Covered by the PR's 8 node:test + 3 vitest cases.",
"_rebaseline_2026_07_22_8050_model_lockout_exact_family": "#8050 (@AndrianBalanescu) own growth: accountFallback.ts 1864->1892 (+28) — exact-vs-family model-lockout scoping (getModelLockKey/isModelLocked/clearModelLock/getModelLockoutInfo) so an Antigravity 404 for one bare model no longer hijacks the whole family cooldown. Cohesive lockout logic; frozen at new size.",
"_rebaseline_2026_07_23_8219_cache_ttl_settings_sidebar": "#8219 (@oyi77) own growth: sections.ts 806->813 (+7) — configurable model-catalog cache-TTL settings adds a new sidebar nav entry + its visibility wiring. Sidebar item array is data, not extractable logic; frozen at new size.",
"src/shared/constants/sidebarVisibility/sections.ts": 813,
"_rebaseline_2026_07_22_8056_headroom_minrows": "#8056 (@RaviTharuma, persist Headroom minRows) own growth: src/lib/db/compression.ts 850->866 (+16 HeadroomConfig+DEFAULT_HEADROOM_CONFIG+normalize/store in get/updateCompressionSettings) and open-sse/services/compression/strategySelector.ts 1054->1060 (+6 merge settings.headroom into stacked stepConfig). Cohesive settings-persistence + stacked-merge wiring at existing chokepoints, frozen at new size.",
"_rebaseline_2026_07_22_8081_reasoning_placeholder_guard": "#8081 (@Dingding-leo) own growth: openai-responses.ts 1125->1137 (+12) restructuring the reasoning-placeholder guard so it skips only the empty content block and still emits finish_reason/tool_calls in the same chunk. Cohesive translator wiring; frozen at new size.",
"_rebaseline_2026_07_22_8210_openrouter_midstream_error": "PR #8210 (hartmark, fix/openrouter-midstream-error-surfacing) own growth: open-sse/translator/response/openai-responses.ts 1137->1163 (+26) measured on the merged tip (release 1137 + this PR own growth). Adds a single new branch inside openaiToOpenAIResponsesResponse() that detects an OpenRouter-style mid-stream aggregator error (HTTP 200 SSE chunk with empty choices + a top-level error object) and surfaces it as state.upstreamError instead of silently falling through to the no-op/awaitingTrailingUsage path, which previously masked the failure as a false empty-success completion and skipped combo fallback. Irreducible call-site addition at the existing chunk-dispatch chokepoint (mirrors the Gemini-to-OpenAI translator's #4177 precedent for the same class of upstream error surfacing). Note: this baseline entry does NOT cover the separate pre-existing +11 drift already on the release tip from #8081/#8162 (1125->1136, unrelated reasoning-placeholder-stripping fix merged after this PR branched) — that drift belongs to the maintainer's rebaseline, not this PR.",
"_rebaseline_2026_07_22_8211_gemini_malformed_tool_choice": "PR #8211 (hartmark, fix/gemini-malformed-function-call-tool-choice) own growth: open-sse/translator/response/gemini-to-openai.ts 771->821 (+50, entirely this PR's diff — no other commit touched this file between the PR's merge-base and the release tip). Adds MALFORMED_FUNCTION_CALL/UNEXPECTED_TOOL_CALL handling inside geminiToOpenAIResponse(): synthesizes a `malformed_tool_call` tool_calls entry so finish_reason normalizes to the standard \"tool_calls\" instead of an unrecognized raw enum value that OpenAI-compatible clients (e.g. OpenClaw) silently ignore, and always synthesizes (rather than skipping when a real tool call already exists) so a malformed attempt alongside a real one in the same turn is not silently discarded. Irreducible cohesive addition at the existing candidate/finishReason translation chokepoint (mirrors the 9router#2462 raw-finish-reason precedent immediately below it in the same function). Covered by the PR's own tests/unit test additions for both the malformed-only and malformed-plus-real-call cases.",
"_rebaseline_2026_07_22_8213_chat_abandoned_target_abort": "PR #8213 (hartmark, fix/gemini-tpm-quota-cooldown-wait) own growth: src/sse/handlers/chat.ts 1794->1860 (+66, measured against the PR's own merge-base — the release tip separately carries an unrelated -5 net shrink from #8013's antigravity callable-catalog alignment, which this PR's branch does not include and this entry does not cover). Adds resolveDispatchClientRawRequest(): merges a per-target modelAbortSignal into clientRawRequest.signal (via mergeAbortSignals) so a combo target abandoned by comboTargetTimeoutMs actually observes its own abort and reaches its cleanup path, instead of hanging forever inside withRateLimit/acquireAccountSemaphore and leaking a permanent 'pending' dashboard entry (live incident, log id 1784418258231-14961a). Also wires combo-exhausted rejection logging to capture request body + attempted models via the new rejectedRequestUsage helper. Irreducible additions at the existing chat dispatch chokepoint. Covered by the PR's own combo-config + integration test additions.",
"_rebaseline_2026_07_22_8213_combo_cooldown_wait_recording": "PR #8213 (hartmark, fix/gemini-tpm-quota-cooldown-wait) own growth: open-sse/services/combo.ts 3548->3604 (+56, entirely this PR's diff — no other commit touched this file between the PR's merge-base and the release tip). Fixes combo cooldown-wait state recording so a bogus 503 is no longer crystallized when the cooldown-wait vars reset every setTry, adds an OpenAI-format SSE error frame path for combo-exhausted rejections (capturing request body + attempted models), and gives an abandoned per-target dispatch its own timeout instead of leaking a permanent 'pending' dashboard entry. Irreducible additions at the existing handleComboChat dispatch/retry chokepoint (mirrors the prior quota-share/headroom/task-aware strategy-branch precedents already frozen in this file). Covered by the PR's own combo-config + Gemini TPM-ceiling benchmark test additions.",
"_rebaseline_2026_07_22_8213_gemini_tpm_quota_cooldown_wait": "PR #8213 (hartmark, fix/gemini-tpm-quota-cooldown-wait) own growth: open-sse/services/accountFallback.ts 1857->1932 on the merged tip (release 1892 incl #8050 +35, plus this PR own growth +40); measured against the PR own merge-base was 1857->1898 (+41 — the release tip separately carries an unrelated +34 from #8050's antigravity 404 model-not-found lockout scoping, which this PR's branch does not include and this entry does not cover). Own growth is the Gemini TPM-ceiling classification + cooldown-wait wiring feeding into the combo cooldown-wait state machine (rate-limit wedge recovery) introduced by this PR's commit series. Irreducible additions at the existing account-fallback/model-lockout chokepoint. Covered by the PR's own gemini-rate-limit-tracker and TPM-ceiling benchmark test additions.",
"_rebaseline_2026_07_22_8213_health_unblock_model_cooldowns": "PR #8213 (hartmark, fix/gemini-tpm-quota-cooldown-wait) own growth: src/app/(dashboard)/dashboard/health/page.tsx 1094->1165 (+71, entirely this PR's diff — no other commit touched this file between the PR's merge-base and the release tip). Adds handleUnblockAll/handleUnblockOne dashboard actions (DELETE /api/resilience/model-cooldowns) so an operator can manually clear a Gemini TPM-wedge model lockout surfaced by this PR's cooldown-wait fixes, instead of waiting out the ceiling. Irreducible UI wiring at the existing health-page action chokepoint. Covered by the PR's own dashboard/resilience test additions.",
"_rebaseline_2026_07_22_8213_requestloggerdetail_unblock_ui": "PR #8213 (hartmark, fix/gemini-tpm-quota-cooldown-wait) own growth: src/shared/components/RequestLoggerDetail.tsx 799->941 (+142, entirely this PR's diff — no other commit touched this file between the PR's merge-base and the release tip; crosses the general 800-line new-file cap so is frozen here for the first time). Adds a collapsible section header (open/expand-less toggle) plus per-log-entry unblock (`unblocking`/`cleared` state, isCombo503 detection) so the request-logger detail panel surfaces the same Gemini TPM cooldown-wait / model-lockout unblock action introduced by this PR at the individual-request level (mirrors the health-page bulk unblock action added in the same PR). Covered by the PR's own dashboard/resilience test additions.",
"open-sse/services/usage/antigravity.ts": 802,
"_rebaseline_2026_07_22_fusion_8013_8098_antigravity": "Fusion of #8013 (backryun, catalog/IDE-CLI-split rewrite) + #8098 (nguyenha935, protocol-fidelity/fail-closed/credits/tool-cloaking): open-sse/services/usage/antigravity.ts NEW 802 (>cap 800, +2 — #8098 credits/tier usage service on #8013's profile-aware headers). Test growth (models-catalog-route 1605->1608, provider-models-route 1752->1757 from #8013 Gemini 3.6 catalog) tracked in testFrozen.",
"_rebaseline_2026_07_23_8127_grok_weekly_quota": "#8127 (@apoapostolov) own growth: src/sse/handlers/chat.ts 1861->1865 (+4) — weekly quota tracking for grok-web wires a quota-fetch hook at the existing dispatch chokepoint. Thin wiring mirroring adjacent provider-quota branches; not extractable. Covered by tests/unit/grok-quota-fetcher.test.ts.",
"_rebaseline_2026_07_23_8143_empty_catch_logging": "#8143 (@chirag127) own growth: open-sse/utils/stream.ts 2869->2887 (+18) — replacing empty catch blocks in the SSE stream subsystem with console.debug logging (Rule #6 silent-swallow fix, issues #8138-#8142). Cohesive logging additions at the existing catch chokepoints, not extractable; frozen at new size. Covered by tests/unit/stream-handler-catch-logging-8143.test.ts.",
"_rebaseline_2026_07_23_8219_cache_ttl_settings_sidebar": "#8219 (@oyi77) own growth: sections.ts 806->813 (+7) — configurable model-catalog cache-TTL settings adds a new sidebar nav entry + its visibility wiring. Sidebar item array is data, not extractable logic; frozen at new size.",
"_rebaseline_2026_07_23_8247_8248_model_unhealthy": "#8247+#8248 own growth: accountFallback.ts 1940->1941 (+1, irreducible import statement only — the substantive #8248 DEGRADED-pattern classifier was extracted into open-sse/config/errorConfig.ts, which has ample headroom, instead of growing this frozen file; #8247's fix is a single existing-line condition change, net zero lines). Scoping the credits-exhausted 403/429 branch to isCompatibleProvider() (per-model-quota openai/anthropic-compatible-* nicknames) so it stays model-scoped instead of terminalling the whole connection, and classifying NVIDIA NIM 'Function ... DEGRADED' 400 bodies as model-access-denied instead of a raw passthrough 400. Covered by tests/unit/8247-accountfallback-model-unhealthy.test.ts and tests/unit/8248-accountfallback-nvidia-degraded.test.ts.",
"_rebaseline_2026_07_23_8252_combo_400_advance": "#8252 (@RaviTharuma) own growth: accountFallback.ts 1932->1940 (+8) + combo.ts 3604->3630 (+26) — advance combo on model-scoped 400s wrapped as invalid/Bad-Request. Irreducible wiring at existing account-fallback + combo dispatch chokepoints. Covered by combo-model-scoped-400-advance.test.ts.",
"_rebaseline_2026_07_23_8266_alibaba_media": "#8266 (@backryun) own growth: imageRegistry.ts 821->979 (+158) — Alibaba-family media models (Qwen image/video, Bailian, Wan) added to the image/video registry. Registry model data, not extractable logic; frozen at new size.",
"_rebaseline_2026_07_24_8388_compression_detail_persist": "#8388 (compression engine DETAIL settings — Headroom/session-dedup/CCR — dropped on save) own growth: src/lib/db/compression.ts 866->872 (+6 = irreducible call-site wiring at the existing getCompressionSettings/updateCompressionSettings chokepoint: one import line, one `...buildDetailConfigDefaults()` spread in the seed config, and one `case \"sessionDedup\": case \"ccr\": applyDetailConfigUpdate(config, key, parsed); break;` load-switch case, mirroring the existing headroom/#8056 case immediately above it). The actual normalizer logic (normalizeSessionDedupConfig/normalizeCcrConfig, matching SESSION_DEDUP_SCHEMA/CCR_SCHEMA bounds) was EXTRACTED into a new leaf src/lib/db/compressionDetailNormalizers.ts (well under cap) so this frozen file only carries the minimal dispatch wiring. Covered by tests/unit/8388-compression-detail-persist.test.ts (schema-accept + full DB save->reload round-trip for both new sub-objects, plus a no-regression assertion on the existing headroom round-trip).",
"_rebaseline_2026_07_24_responses_toolcalls_log_summary": "hartmark, fix/responses-tool-calls-log-summary own growth: open-sse/translator/response/openai-responses.ts 1163->1174 (+11). closeToolCall() now also writes the completed tool call into the shared state.toolCalls Map (already populated by the openai-to-claude / claude-to-openai / gemini-to-openai response translators) so stream.ts's completion-log summary builder (which reads state.toolCalls, not this translator's own funcCallIds/funcNames/funcArgsBuf bookkeeping) reports finish_reason \"tool_calls\" and message.tool_calls for openai->openai-responses translated streams instead of always logging \"stop\" with no tool_calls — the actual client-facing SSE events were already correct; only the persisted call-log summary was wrong. Irreducible call-site addition at the existing tool-call-close chokepoint. Covered by the new regression test in tests/unit/translator-resp-openai-responses.test.ts.",
"_rebaseline_2026_07_25_8476_combo_input_bound_homogeneous_scope": "PR #8476 (herjarsa, fix/8375-8459-combo-image-fixes, #8375) own growth: open-sse/services/combo.ts 3642->3679 (+37 net: +29 the PR's own isInputBoundFailure short-circuit for deterministic context_length_exceeded/context_window_exceeded failures, +8 a /green-prs pre-merge fix scoping that short-circuit to homogeneous remainders only — the shipped code fired unconditionally on ANY target, regressing the intentional heterogeneous-combo fallback #6637/isContextOverflow400 protects, exactly as flagged by this PR's own review evidence but never actually implemented in the branch). The fix compares orderedTargets[i+1..] modelStr against the failing target's modelStr at the existing executeTarget dispatch chokepoint (mirrors the sameProviderNext precedent a few lines below) — irreducible call-site wiring, not extractable without hiding the dispatch boundary. Covered by tests/unit/combo-input-bound-failure-8375.test.ts (homogeneous pool still short-circuits) and the new tests/unit/combo-input-bound-heterogeneous-8375.test.ts (heterogeneous combo now correctly falls through to the larger-context target).",
"_rebaseline_2026_07_25_adobe_firefly_reference_images": "Follow-up to #8006: storage upload + referenceBlobs for image/video and /v1/images/edits dispatch. adobeFireflyClient.ts 1958->2317 (+upload helpers, extract sources, resolve blob ids). Note: 2317 not 2316 — check-file-size.mjs counts LOC via split(\"\\n\").length (counts the trailing-newline empty element), which is 1 higher than `wc -l` on a file ending in \\n; the PR's original entry (2316) was measured with wc -l and undercounted by 1 against the actual gate.",
"_rebaseline_pr1043_minimax_tts": "Upstream port decolua/9router#1043 (toanalien) own growth: audioSpeech.ts 965->1061 (+96). Adds MiniMax T2A v2 TTS dispatch (handleMinimaxSpeech + hexToBytes helper) — provider entry was already in audioRegistry (format: minimax-tts) but no handler existed, falling through to the OpenAI-compatible default that fails (T2A has custom shape + hex-encoded audio + base_resp envelope). New branch sits next to the other inline provider branches (xiaomi-mimo, coqui, tortoise, aws-polly) — extracting would just create indirection. Covered by tests/unit/minimax-tts-1043.test.ts (3 tests, GREEN: success, base_resp error, invalid-hex).",
"_rebaseline_pr4592_exclude_exhausted_auto": "Reconcile #4592 already-merged growth: combo.ts 2991->3036 (+45, terminal-status quota-cutoff exclusion in buildAutoCandidates + opt-in gate). Fast-gate PR->release does not run check:file-size.",
"open-sse/executors/antigravity.ts": 1528,
"open-sse/executors/base.ts": 1640,
"open-sse/executors/chatgpt-web.ts": 3241,
"open-sse/executors/codex.ts": 1562,
"open-sse/executors/cursor.ts": 1563,
"open-sse/executors/deepseek-web.ts": 1148,
"open-sse/executors/grok-web.ts": 1044,
"open-sse/executors/muse-spark-web.ts": 1405,
"open-sse/handlers/chatCore.ts": 5034,
"open-sse/handlers/imageGeneration.ts": 3101,
"open-sse/handlers/responseSanitizer.ts": 1128,
"open-sse/handlers/search.ts": 1536,
"open-sse/handlers/videoGeneration.ts": 1063,
"open-sse/mcp-server/schemas/tools.ts": 1505,
"open-sse/mcp-server/server.ts": 1411,
"open-sse/mcp-server/tools/advancedTools.ts": 1120,
"open-sse/services/accountFallback.ts": 1972,
"open-sse/services/adobeFireflyClient.ts": 2385,
"open-sse/services/claudeCodeCompatible.ts": 1202,
"open-sse/services/combo.ts": 3648,
"open-sse/services/compression/strategySelector.ts": 1060,
"open-sse/services/rateLimitManager.ts": 1167,
"open-sse/translator/response/openai-responses.ts": 1204,
"open-sse/utils/cursorAgentProtobuf.ts": 1505,
"open-sse/utils/stream.ts": 2889,
"src/app/(dashboard)/dashboard/HomePageClient.tsx": 1388,
"src/app/(dashboard)/dashboard/analytics/ComboHealthTab.tsx": 1031,
"src/app/(dashboard)/dashboard/api-manager/ApiManagerPageClient.tsx": 3117,
"src/app/(dashboard)/dashboard/cache/media/MediaPageClient.tsx": 1067,
"src/app/(dashboard)/dashboard/combos/page.tsx": 4647,
"src/app/(dashboard)/dashboard/costs/CostOverviewTab.tsx": 1283,
"src/app/(dashboard)/dashboard/costs/quota-share/components/PoolWizard.tsx": 1022,
"src/app/(dashboard)/dashboard/endpoint/EndpointPageClient.tsx": 2615,
"src/app/(dashboard)/dashboard/health/page.tsx": 1165,
"src/app/(dashboard)/dashboard/providers/[id]/components/modals/EditConnectionModal.tsx": 1316,
"src/app/(dashboard)/dashboard/providers/page.tsx": 1944,
"src/app/(dashboard)/dashboard/runtime/RuntimePageClient.tsx": 1201,
"src/app/(dashboard)/dashboard/settings/components/PricingTab.tsx": 1019,
"src/app/(dashboard)/dashboard/settings/components/ProxyRegistryManager.tsx": 1464,
"src/app/(dashboard)/dashboard/settings/components/ResilienceTab.tsx": 1123,
"src/app/(dashboard)/dashboard/settings/components/RoutingTab.tsx": 1629,
"src/app/(dashboard)/dashboard/settings/components/SystemStorageTab.tsx": 1573,
"src/app/(dashboard)/dashboard/usage/components/BudgetTab.tsx": 1028,
"src/app/(dashboard)/dashboard/usage/components/EvalsTab.tsx": 2148,
"src/app/(dashboard)/dashboard/usage/components/ProviderLimits/index.tsx": 1119,
"src/app/api/providers/[id]/models/route.ts": 2250,
"src/app/api/v1/models/catalog.ts": 1549,
"src/lib/tokenHealthCheck.ts": 1021,
"src/lib/db/apiKeys.ts": 1529,
"src/lib/db/core.ts": 1637,
"src/lib/db/migrationRunner.ts": 1084,
"src/lib/db/models.ts": 1097,
"src/lib/db/providers.ts": 1034,
"src/lib/memory/retrieval.ts": 1073,
"src/lib/tailscaleTunnel.ts": 1202,
"src/lib/usage/providerLimits.ts": 1013,
"src/shared/components/OAuthModal.tsx": 1134,
"src/shared/components/RequestLoggerV2.tsx": 1629,
"src/shared/components/analytics/charts.tsx": 1035,
"src/shared/services/cliRuntime.ts": 1122,
"src/sse/handlers/chat.ts": 1877,
"src/sse/services/auth.ts": 2508,
"tests/unit/account-fallback-service.test.ts": 1572,
"tests/unit/provider-validation-specialty.test.ts": 2985,
"open-sse/executors/hyperagent.ts": 1026,
"open-sse/executors/default.ts": 1042,
"open-sse/executors/kiro.ts": 1069
"_rebaseline_2026_07_22_8050_model_lockout_exact_family": "#8050 (@AndrianBalanescu) own growth: accountFallback.ts 1864->1892 (+28) — exact-vs-family model-lockout scoping (getModelLockKey/isModelLocked/clearModelLock/getModelLockoutInfo) so an Antigravity 404 for one bare model no longer hijacks the whole family cooldown. Cohesive lockout logic; frozen at new size."
},
"testCap": 1000,
"testFrozen": {
@@ -326,7 +381,7 @@
"tests/unit/stream-utils.test.ts": 2445,
"tests/unit/token-refresh-service.test.ts": 1378,
"tests/unit/translator-openai-responses-req.test.ts": 1194,
"tests/unit/translator-openai-to-gemini.test.ts": 1622,
"tests/unit/translator-openai-to-gemini.test.ts": 1619,
"tests/unit/translator-openai-to-kiro.test.ts": 1275,
"tests/unit/translator-resp-gemini-to-openai.test.ts": 1234,
"tests/unit/usage-service-hardening.test.ts": 1483,
@@ -411,6 +466,132 @@
"_rebaseline_2026_07_27_3850_relax_filesize_cap": "OWNER-APPROVED TEMPORARY relax for v3.8.50-3.8.54 PREPARE phase (docs/ROADMAP.md). cap 800->900 (+100), testCap 800->900 (+100). Targets: decompose-existing-frozen unchanged (frozen still only-shrink); this only relaxes the cap for NEW files in the decompose/extract-while-PREPARE phase (.51='executor registry in-place' and .52='combo.ts decomposition' create new leaf modules above 800). RE-TIGHTENING MANDATORY in v3.8.51: cap target 850 = 850 once decomposition wave stabilizes. SUPERSEDED by _rebaseline_2026_07_27_3850_relax_filesize_cap_v2_20pct (v1 +20% buffer) — retained for audit. Tracked via same roadmap issue.",
"_rebaseline_2026_07_27_v3849_train1h": "Merge-train 1H (31 PRs) — owner-approved 2026-07-27. Two distinct causes, kept separate on purpose: (1) GENUINE irreducible growth at existing chokepoints — providerLimits/auth (#8632 Kimi quota-reset recovery), rateLimitManager (#8616 idle wedged limiters), models-catalog-route.test (#8610 OpenCode Go effort aliases); (2) COLLISION with #8585, which banked shrinks measured on the pre-train release tip while 30 sibling PRs in the SAME train grew those files again — chat/accountFallback (#8628), chatCore (#8613), videoGeneration (#8581), imageGeneration. The zero-headroom frozen entries cannot absorb either. Ceilings re-pinned to the post-merge tip; #8612 (also in this train) automates shrink-banking so this self-inflicted drift stops recurring. Detail: src/lib/usage/providerLimits.ts 1006->1013 (#8632); src/sse/services/auth.ts 2492->2508 (#8632); open-sse/services/rateLimitManager.ts 1014->1060 (#8616); src/sse/handlers/chat.ts 1842->1845 (#8628); open-sse/handlers/chatCore.ts 4939->4955 (#8613); open-sse/handlers/imageGeneration.ts 3100->3101 ((sem PR — teto do #8585)); open-sse/handlers/videoGeneration.ts 1038->1063 (#8581); open-sse/services/accountFallback.ts 1965->1966 (#8628); tests/unit/models-catalog-route.test.ts 1608->1636 (#8610)",
"_rebaseline_2026_08_02_9242_token_health_transient": "PR #9242 (fix/refresh-circuit-transient): src/lib/tokenHealthCheck.ts 1021 (new file, above cap 1000). The file consolidates token-refresh health checking logic that was previously scattered across auth.ts and tokenRefresh.ts. Cohesive single-responsibility module for refresh circuit state management; not extractable without splitting the refresh state machine. Covered by tests/unit/tokenHealthCheck-transient.test.ts.",
"frozen": {
"_rebaseline_2026_06_22_4644_deepseek_web_tools": "PR #4644 (BugsBag/robust deepseek-web tool-call parsing): open-sse/executors/deepseek-web.ts 1117->1125 (+8). The new agentic tool-call path emits surrounding text + reasoning before tool_calls and swaps to the dedicated deepseekWebTools.ts parser; the +8 lines are cohesive wiring at the existing transformSSE chokepoint (the parser itself lives in the new deepseekWebTools.ts file, already under cap). The PR's own fast-gate (PR->release) does not run check:file-size, so this surfaced only at release reconcile. Covered by tests/unit/deepseek-web-tools-variants.test.ts + deepseek-web-tools-execute.test.ts.",
"_rebaseline_2026_06_23_4712_deepseek_web_tool_results": "PR for #4712 (deepseek-web drops role:tool): open-sse/executors/deepseek-web.ts 1125->1148 (+23). messagesToPrompt() now folds role:\"tool\" results into the single-prompt transcript (recovering the tool name from the preceding assistant tool_calls by tool_call_id) instead of silently dropping them; the lines are cohesive wiring inside the existing function. Covered by tests/unit/deepseek-web-tool-result-prompt-4712.test.ts.",
"_rebaseline_2026_06_24_headroom_strategy": "Headroom-aware connection selection (dario technique): combo.ts 3168->3180 (+12 = a new `else if (strategy === \"headroom\")` dispatch branch in handleComboChat that delegates to orderTargetsByHeadroom + its log line, plus the import). The actual logic lives OUT of the god-file: the pure ranker rankByHeadroom/computeHeadroom is the new leaf open-sse/services/combo/headroomRanking.ts (91 LOC, <cap) and the async orderer orderTargetsByHeadroom is appended to the existing open-sse/services/combo/quotaStrategies.ts (<cap) next to its sibling reset-aware/reset-window orderers (reuses their connection-expansion machinery). headroom = 1 - max(util_5h, util_7d) from getSaturation (src/lib/quota/saturationSignals.ts), prefers the connection with the most free capacity. Only the dispatch wiring is irreducible at the existing combo strategy chokepoint (mirrors the reset-aware/reset-window/context-optimized branches); not extractable without hiding the call site. fill-first stays default; all existing strategies untouched. Covered by tests/unit/combo-headroom-ranking.test.ts (pure helper) + tests/unit/combo-headroom-strategy.test.ts (orderer, saturation injected). Structural shrink of combo.ts tracked in #3501.",
"_rebaseline_2026_06_24_quota_share_strategy": "Dedicated quota-share strategy (Phase 3 #9): combo.ts 3180->3190 (+10 = one new `else if (strategy === \"quota-share\")` dispatch branch in handleComboChat that delegates 100% to selectQuotaShareTarget + its log line, plus the import). All the new logic lives OUT of the god-file in two new leaves under open-sse/services/combo/: quotaShareInflight.ts (in-flight counter with TTL/lease, ~150 LOC <cap) and quotaShareStrategy.ts (per-model bucket gating via isBucketSaturated + DRR proportional to weight + P2C over in-flight, ~240 LOC <cap). Only the dispatch wiring is irreducible at the existing combo strategy chokepoint (mirrors the headroom/reset-aware/reset-window/context-optimized branches); not extractable without hiding the call site. ZERO existing strategy cases were modified — only this branch was added, and the qtSd/ combos switched from fill-first to quota-share in src/lib/quota/quotaCombos.ts. Covered by tests/unit/quota-share-strategy.test.ts (gating, DRR fairness, P2C in-flight, fail-open, activation). Structural shrink of combo.ts tracked in #3501.",
"_rebaseline_2026_06_24_task_aware_routing": "Task-aware routing strategy (port PR #2045, OmniRoute #4945): combo.ts 3190->3225 (+35) = one new `else if (strategy === \"task-aware\")` dispatch branch delegating 100% to selectTaskAwareTarget + its imports/log lines. All scoring/classification logic lives OUT of the god-file in the new leaf open-sse/services/taskAwareRouting.ts (553 LOC <cap). Only the dispatch wiring is irreducible at the existing combo strategy chokepoint (mirrors quota-share/headroom/reset-aware branches). ZERO existing strategy cases modified. Covered by tests/unit/combo-task-aware.test.ts (35 tests). Structural shrink of combo.ts tracked in #3501.",
"_rebaseline_2026_06_26_fidelity_gate_extraction": "Milestone-B fidelity-gate wiring residual: bodyToText+gateAdvance extracted to fidelityGateStep.ts (889->854, -35), but the StackOptions.fidelityGate field, the `const fidelityGate` reads at the two stacked-loop dispatch chokepoints, and the import of FidelityGateConfig are irreducible wiring that cannot leave strategySelector without an architectural refactor of the pre-existing stacked pipeline. Net: 889->854 (+6 vs the pre-Milestone-B frozen 848). Covered by tests/unit/compression/*.test.ts (940 pass).",
"_rebaseline_2026_06_27_5193_5203_antigravity_oauthmodal": "Antigravity remote-login own growth: OAuthModal.tsx 960->969 (gate units). #5193 (+~4: remote paste instruction shown for all remote incl. Google + its rationale comment) and #5203 (+~5: handleManualSubmit credential-blob branch + button guard; submit logic extracted to oauthBlobSubmit.ts to minimize). Frozen set to the SUM so either merge order passes. Cohesive at the existing manual-submit chokepoint.",
"_rebaseline_2026_06_27_5193_antigravity_basered": "Base-red (pre-existing release drift, fast-gate PR->release skips check:file-size): accountFallback.ts 1773->1777 and src/app/api/providers/[id]/test/route.ts 924->940 were already over their frozen caps on release/v3.8.39 independent of any antigravity change. Owner chose to rebaseline (keep the documented issue-reference comments #1846/#1449/#347 etc.) rather than accept the contributor comment-stripping in #5200/#5198. Reverted #5200 to restore the comments; bumped these two frozen caps to the actual base sizes. No logic change.",
"_rebaseline_2026_06_28_5237_impersonation_ua_refresh": "PR #5237 (refresh impersonation UAs): grok-web.ts 1871->1873 (+2), muse-spark-web.ts 1284->1302 (+18), perplexity-web.ts 1013->1032 (+19). Net semantic change in each file is a single User-Agent constant (Chrome 147->149 for grok/muse; perplexity kept at Firefox 148 to stay matched with the firefox_148 TLS profile — the contributor's 152 bump was reverted to avoid a UA-vs-JA3 mismatch, #2459). The growth is Prettier reflow that lint-staged unavoidably applies to these grandfathered long-line files the moment they are touched; not extractable. src/sse/services/auth.ts 2336->2401 in the same reconcile is #5222's antigravity-LRU-retry growth that merged via --admin without a baseline bump.",
"_rebaseline_2026_06_28_5243_risk_gate_prepass": "PR #5243 (compression risk-gate pre-pass) own growth: open-sse/services/compression/strategySelector.ts 854->899 (+45). The three exported entry points (applyCompression/applyStackedCompression/applyStackedCompressionAsync) become thin wrappers over pure-extracted private bodies (runCompression/runStackedCompression/runStackedCompressionAsync) so the risk-gate mask->run->restore wrapper sits strictly OUTSIDE the per-step loop — a single universal integration point. The wrapper logic itself (resolveRiskGate/withRiskGate) lives in the new riskGate/strategyWrap.ts (<cap); the residual growth is the duplicated thin-wrapper signatures + the extracted bodies' dispatch boundary, guarded by a byte-identical parity test (riskGateIntegration). Default off (DEFAULT_COMPRESSION_CONFIG unchanged). Not extractable without hiding the dispatch boundary, mirroring prior compression rebaselines. Structural shrink tracked in #3501.",
"_rebaseline_2026_06_28_5275_correlation_id_extract": "Extraction of the safe CorrelationId subset of #5275 (hartmark) — request correlation id stored in call_logs (migration 109) and returned via the X-Correlation-Id response header, WITHOUT the combo/resilience or build/lazy-loading changes (those stay in #5275). Own growth: callLogs.ts 975->985 (correlation_id column on CallLogSummaryRow + read/map), usageHistory.ts 983->988 (correlationId metadata normalize), chat.ts 1575->1632 (withCorrelationId response wiring + combo-failure log carrying correlationId), chatHelpers.ts new 811 (withCorrelationId helper + reqId threading; was 791<cap pre-feature). Cohesive request/logging chokepoint wiring; structural shrink of chat.ts tracked in #3501.",
"_rebaseline_2026_06_29_4038_cas_guard": "PR (#4038) own growth: tokenRefresh.ts 2103->2181 (+78 = the compare-and-swap guard on the refresh persist — runWithCasGuard/getActiveCasGuard AsyncLocalStorage pair mirroring runWithOnPersist, casGuardShouldSkipPersist that rereads the row right before persisting and skips the write when a concurrent writer already rotated the refresh_token past the one presented, plus getCasGuardStats counters). Fixes the sibling-rotation-revert → token-family-revocation storm. Gated behind an active guard (opt-in; no guard => byte-identical). Wiring lives at the two persist chokepoints inside getAccessToken; the comparison reuses wasRefreshTokenRotated from refreshSerializer. Not extractable without splitting the refresh hot path.",
"_rebaseline_2026_06_29_5286_memoization": "PR #5286 own growth: strategySelector.ts 899->960 (+61 = the opt-in result-memoization branches in applyCompression/applyCompressionAsync — principal+determinism gate, makeMemoKey lookup/store with model+supportsVision folded into the key, recompute-with-memo-off). Default off (memoizeCompressionResults), so zero behavior change. The memo helpers live in the leaf resultMemo.ts (<cap); the chokepoint wiring here is not extractable. Structural shrink of this hot-path file tracked in #3501.",
"_rebaseline_2026_07_01_v3843_release_5609": "Rebaseline v3.8.43 (PR #5609 release reconciliation). DRIFT dos 109 commits do ciclo: 8 god-files existentes cresceram (ApiManagerPageClient 2983->3017, combos/page 4594->4608, AddApiKeyModal 868->869, providerPageHelpers 974->996, chat.ts 1635->1647, auth.ts 2401->2403, batchProcessor 828->915, combo.ts 3368->3387) + 2 novos acima do cap (huggingchat.ts 813, tests web-cookie-providers-new 827) + 4 test files cresceram. Modularizacao deferida (blast-radius mid-release); congelado no estado atual p/ o proximo ciclo ratchetar daqui.",
"_rebaseline_2026_07_02_5816_qoder": "PR #5816 (@AgentKiller45, qoder PAT via qodercli): qoderCli.ts 666->989, new-above-cap frozen (owner-approved baseline freeze). The growth is the legitimate PAT job-token exchange + quota parsing CLI transport (the pure-JS Cosy path 500'd on every PAT request); extracting the spawn/parse helpers now would just add indirection to a contributor PR mid-merge. Test frozen also raised for this PR's coverage growth: providers-page-utils.test.ts 1052->1092. Additionally clears an inherited base-red from the already-merged #5933 (codex json_schema->text.format): translator-openai-responses-req.test.ts 1097->1172 (+75 regression tests, no offending branch left). All remain frozen (cannot grow further); release captain's rebaseline-at-release supersedes.",
"_rebaseline_2026_07_09_6126_clinepass_dual_auth": "PR #6126 (@hajilok, dual-auth ClinePass) own growth: tokenRefresh.ts 2181->2182 (+1 = a single `case \"clinepass\":` fallthrough label added to the existing `case \"cline\":` in _getAccessTokenInternal's provider switch, so clinepass token refresh dispatches to the already-shared refreshClineToken() instead of silently falling through to the generic OAuth refresh). Irreducible 1-line switch-case wiring at the existing chokepoint; the header-building logic for the same feature was extracted to a new leaf src/shared/utils/clineAuth.ts::buildClinepassHeaders() (well under cap) to avoid growing open-sse/executors/default.ts. Covered by tests/unit/clinepass-provider.test.ts.",
"_rebaseline_2026_07_09_6363_kiro_external_idp": "PR #6363 (@artickc, Kiro external IdP) own growth: tokenRefresh.ts 2182->2249 (+67 = the external_idp refresh branch inside refreshKiroToken — standard public-client OAuth2 refresh_token grant against the org IdP tokenEndpoint via buildExternalIdpRefreshParams/isExternalIdpAuthMethod from the new leaf open-sse/services/kiroExternalIdp.ts, with invalid_grant/invalid_client -> unrecoverable_refresh_error mapping). Cohesive addition at the existing refreshKiroToken chokepoint. Covered by tests/unit/kiro-external-idp.test.ts.",
"_rebaseline_2026_07_09_6587_kiro_api_key_auth": "PR #6587 (@strangersp) own growth for Kiro long-lived API-key auth, merged onto v3.8.47 tip: openai-to-kiro.ts 890->912 (+22, auth-header selection for API-key-vs-OAuth-token connections), providerLimits.ts 998->1000 (+2, API-key auth-type branch), translator-openai-to-kiro.test.ts 1234->1257 (+23), providers-page-utils.test.ts 1109->1107 (net -2 after merging with parallel release drift; connectionMatchesProviderCard api_key coverage added), provider-validation-specialty.test.ts 2856->2980 (+124 net after merge with parallel release drift; this PR also removed the file's `@typescript-eslint/no-explicit-any` eslint-suppression entry by fixing all `any` usages, adding typed replacements). Cohesive additive feature growth, well tested; not extractable without splitting the existing chokepoints mid-merge.",
"_rebaseline_2026_07_09_6678_routing_strategy_9router": "#6678 (SeaXen) — 9router-parity Routing Strategy settings card + per-provider/combo sticky-round-robin override. Own growth: ProviderDetailPageClient.tsx 784->786 (single ProviderAccountRoutingCard mount + import), auth.ts 2448->2458 (providerStrategies override resolution: fallbackStrategy/stickyRoundRobinLimit per-provider cascade in getProviderCredentials). Both additive, zero unrelated refactor; new UI/logic lives in new files (ProviderAccountRoutingCard.tsx, RoutingStrategyCard.tsx, rrState.ts::resolveComboStickyRoundRobinLimit). chat.ts value below reflects the current release tip (grown by other concurrent PRs, e.g. #6640), not this PR own change.",
"_rebaseline_2026_07_10_6318_omp_letta": "PR #6318 (@hamsa0x7, omp+letta CLI integrations) own growth: cliTools.ts (+53 = 2 registry entries incl. omp docsUrl) and cliRuntime.ts (+18 = runtime-detection wiring for the 2 new tools). Cohesive registry/wiring growth at the existing chokepoints; scope reduced from the original 5 tools (pi/codewhale/jcode shipped separately).",
"_rebaseline_2026_07_10_gcf_v3_2_decode": "PR #6838 own growth: new vendored file open-sse/services/compression/engines/headroom/gcf/decode_generic.ts frozen at 880 (> 800 cap). It is the vendored GCF generic-profile decoder (spec v3.2 nested flattening plus the prototype-pollution / hasOwnProperty hardening added in this PR's Gemini review). Kept as one file faithful to upstream gcf-typescript so re-vendoring stays a clean copy rather than a re-split each cycle (sibling generic.ts/scalar.ts stay < cap; extraction would also fragment the file's frozen eslint no-explicit-any suppressions). Round-trip + prototype-pollution regression coverage in tests/unit/compression/headroom-smartcrusher.test.ts. Frozen: only shrinks from here.",
"_rebaseline_2026_07_12_v3847_mergeprs_tail": "v3.8.47 /merge-prs tail (owner-approved): src/lib/localDb.ts NEW>800 (799->805, +6 re-exports countFreeProxies + recordFreeProxySyncErrors/clearFreeProxySyncErrors/getFreeProxySyncErrors + FreeProxySyncErrors type for #6909 free-pool relay-repair; re-export-only per Hard Rule #2, not extractable).",
"_rebaseline_2026_07_15_7070_combos_memo": "PR #7070 (perf/p1-memo) own growth: src/app/(dashboard)/dashboard/combos/page.tsx 4655->4656 (+1 = React.memo wrapping of ComboCard). Covered by tests/unit/ui/combos-page-smoke.test.tsx.",
"_rebaseline_2026_07_18_7399_xai_oauth_modal": "PR #7399 (xAI OAuth PKCE) own growth: OAuthModal.tsx 993->998 (+5 = provider entry + PKCE flow branch wiring at the existing provider-switch chokepoint; the provider logic itself lives in src/lib/oauth/providers/xai-oauth.ts, new leaf). Third irreducible wiring bump on this modal (969->989->993->998); structural shrink tracked in #3501.",
"_rebaseline_2026_07_19_6636_codex_session_json": "#6636 own growth: OAuthModal.tsx 998->1030 (gate units, split(\"\\n\").length incl. trailing newline; +32 = session-JSON paste branch for handleManualSubmit plus a shared submitCodexAccessToken() helper extracted from the pre-existing bare-JWT branch, mirroring the #5203 oauthBlobSubmit.ts extraction precedent; the normalizer logic itself lives in the new src/lib/oauth/utils/codexSessionImport.ts leaf module, not here). Fourth irreducible wiring bump on this modal (969->989->993->998->1030); structural shrink tracked in #3501.",
"_rebaseline_2026_07_19_7546_ghe_copilot_modal": "PR #7546 (GHE Copilot OAuth provider) own growth: OAuthModal.tsx 1030->1056 (gate units). Adds a gheUrl input state, routes ghe-copilot through the existing device-code branch, and threads gheUrl into the device-code request/poll extraData at the existing provider-switch chokepoints (+~24 lines, cohesive with the same pattern as #7399/#6636). The standalone GHE enterprise-URL config step JSX (originally +31 lines inline) was extracted to the new src/shared/components/oauthModal/GheConfigStep.tsx leaf component to minimize the bump; what remains is the irreducible provider-branch wiring. Fifth bump on this modal (969->989->993->998->1030->1056); structural shrink tracked in #3501.",
"_rebaseline_2026_07_19_7787_ic2_localdb_reexports": "PR #7787 (IC2 raw connections cache + lazy-decrypt) own growth: localDb.ts 805->807 (gate units, +2). localDb.ts is the re-export-only layer (hard rule #2 — no logic); the PR adds 4 new db/readCache re-exports (touchConnectionLastUsed, getCachedRawProviderConnections, getCachedProviderConnectionById, getCachedProviderNodes) required by existing barrel importers. Irreducible for a re-export list; frozen so it can only shrink.",
"_rebaseline_2026_07_20_7779_routingcombo_thread": "PR #7779 own growth: chatHelpers.ts 876->877 (+1, thread routingComboId into executeChatWithBreaker for compression-combo assignment). Frozen so it can only shrink.",
"_rebaseline_2026_07_20_7819_autocandidateoverrides_reexport": "PR for #7819 (Level 1+2: read-only auto/* candidate transparency + per-API-key exclusions) own growth: localDb.ts 807->808 (+1). Adds a single `export * from \"./db/autoCandidateOverrides\"` barrel re-export (hard rule #2 — no logic) for the new DB module backing per-apiKey candidate exclusions. Irreducible for a re-export list; frozen so it can only shrink.",
"_rebaseline_2026_07_21_8027_grok_cli_auth_json_paste": "PR #8027 (RaviTharuma, fix(grok-cli) #7610) own growth: OAuthModal.tsx 1080->1100 (gate units). Requires the full ~/.grok/auth.json (with refresh_token) on the paste-import path instead of a bare JWT, at the existing paste-token chokepoint (renamed tab label, updated instructions/placeholder, textarea for the auth.json blob, inline error surface). The validation logic itself (parseGrokCliPasteToken, previously an inline ~75-line function) was extracted to the new src/lib/oauth/utils/grokCliAuthJson.ts leaf module — mirroring the #6636/#7546 extraction precedent — so only the irreducible UI wiring remains here. Sixth bump on this modal (969->989->993->998->1030->1056->1100); structural shrink tracked in #3501.",
"_rebaseline_2026_07_21_8034_compression_exclusions_sidebar": "#8034 (compression exclusions dashboard tab) own growth: sections.ts 796->806 (+10, one new COMPRESSION_CONTEXT_GROUP sidebar item linking /dashboard/compression/exclusions). The file was already 796/800 before this PR (organic growth from prior sidebar entries), so a single new nav item pushed it 6 lines over cap. Freezing at 806 (cannot grow further); the sidebar item array is data, not extractable logic.",
"_rebaseline_2026_07_22_7936_namespace_roundtrip": "#7936 (@RCrushMe, Responses-Chat namespace round-trip identity seam) own growth: open-sse/translator/response/openai-responses.ts 1092->1125 (+33) and open-sse/utils/stream.ts 2814->2869 (+55) — threading the namespace-identity seam through the Responses↔Chat translation + stream paths so tool-call namespaces survive the round-trip. Cohesive translation/stream wiring at existing chokepoints, frozen at new size.",
"_rebaseline_2026_07_22_8010_codex_responses_engine": "PR #8010 (@JxnLexn) own growth: open-sse/mcp-server/schemas/tools.ts 1497->1505 (+8 = threading the new \"codex-responses\" literal into the compressionConfigureInput strategy/autoTriggerMode Zod enums and setCompressionEngineInput engine enum, mirroring the existing rtk/omniglyph enum entries; no new tool). open-sse/services/compression/strategySelector.ts 1043->1054 (+11 = one new `if (mode === \"codex-responses\")` dispatch branch in runCompression that delegates 100% to the new codexResponsesEngine.apply, mirroring the existing rtk single-mode dispatch, plus threading config.codexResponsesConfig.preserveToolNames into the shared adaptBodyForCompression call at the 3 existing call sites). src/lib/db/compression.ts (untracked, new-file cap 800) 794->845 (+51 = normalizeCodexResponsesConfig, mirroring the existing normalizeRtkConfig normalizer, plus registering \"codex-responses\" in the COMPRESSION_MODES/STACKED_PIPELINE_ENGINE_IDS/SINGLE_MODE_ENGINE sets and the getCompressionSettings load/save switch) — added to the baseline at its current size. All three are cohesive dispatch/normalizer wiring at existing chokepoints (mirroring the prior compression-mode rebaselines #6534/#6556), not extractable without hiding the mode-dispatch boundary. Covered by tests/unit/compression/codex-responses.test.ts (6) + omniglyph-registries.test.ts/types.test.ts (22, updated for the new mode).",
"_rebaseline_2026_07_22_8034_compression_exclusions_persistence": "#8034 (compression exclusions) own growth: src/lib/db/compression.ts 845->850 (+5 = threading the new compressionExclusions field through the existing getCompressionSettings/saveCompressionSettings load/save switch over the shared key_value compression namespace — no new table, no raw SQL). Mirrors the prior compression-field rebaselines (#8010 codex-responses normalizer at the same chokepoint); the load/save switch is a single dispatch boundary, not extractable without hiding it. Covered by the PR's 8 node:test + 3 vitest cases.",
"_rebaseline_2026_07_22_8050_model_lockout_exact_family": "#8050 (@AndrianBalanescu) own growth: accountFallback.ts 1864->1892 (+28) — exact-vs-family model-lockout scoping (getModelLockKey/isModelLocked/clearModelLock/getModelLockoutInfo) so an Antigravity 404 for one bare model no longer hijacks the whole family cooldown. Cohesive lockout logic; frozen at new size.",
"_rebaseline_2026_07_22_8056_headroom_minrows": "#8056 (@RaviTharuma, persist Headroom minRows) own growth: src/lib/db/compression.ts 850->866 (+16 HeadroomConfig+DEFAULT_HEADROOM_CONFIG+normalize/store in get/updateCompressionSettings) and open-sse/services/compression/strategySelector.ts 1054->1060 (+6 merge settings.headroom into stacked stepConfig). Cohesive settings-persistence + stacked-merge wiring at existing chokepoints, frozen at new size.",
"_rebaseline_2026_07_22_8081_reasoning_placeholder_guard": "#8081 (@Dingding-leo) own growth: openai-responses.ts 1125->1137 (+12) restructuring the reasoning-placeholder guard so it skips only the empty content block and still emits finish_reason/tool_calls in the same chunk. Cohesive translator wiring; frozen at new size.",
"_rebaseline_2026_07_22_8210_openrouter_midstream_error": "PR #8210 (hartmark, fix/openrouter-midstream-error-surfacing) own growth: open-sse/translator/response/openai-responses.ts 1137->1163 (+26) measured on the merged tip (release 1137 + this PR own growth). Adds a single new branch inside openaiToOpenAIResponsesResponse() that detects an OpenRouter-style mid-stream aggregator error (HTTP 200 SSE chunk with empty choices + a top-level error object) and surfaces it as state.upstreamError instead of silently falling through to the no-op/awaitingTrailingUsage path, which previously masked the failure as a false empty-success completion and skipped combo fallback. Irreducible call-site addition at the existing chunk-dispatch chokepoint (mirrors the Gemini-to-OpenAI translator's #4177 precedent for the same class of upstream error surfacing). Note: this baseline entry does NOT cover the separate pre-existing +11 drift already on the release tip from #8081/#8162 (1125->1136, unrelated reasoning-placeholder-stripping fix merged after this PR branched) — that drift belongs to the maintainer's rebaseline, not this PR.",
"_rebaseline_2026_07_22_8211_gemini_malformed_tool_choice": "PR #8211 (hartmark, fix/gemini-malformed-function-call-tool-choice) own growth: open-sse/translator/response/gemini-to-openai.ts 771->821 (+50, entirely this PR's diff — no other commit touched this file between the PR's merge-base and the release tip). Adds MALFORMED_FUNCTION_CALL/UNEXPECTED_TOOL_CALL handling inside geminiToOpenAIResponse(): synthesizes a `malformed_tool_call` tool_calls entry so finish_reason normalizes to the standard \"tool_calls\" instead of an unrecognized raw enum value that OpenAI-compatible clients (e.g. OpenClaw) silently ignore, and always synthesizes (rather than skipping when a real tool call already exists) so a malformed attempt alongside a real one in the same turn is not silently discarded. Irreducible cohesive addition at the existing candidate/finishReason translation chokepoint (mirrors the 9router#2462 raw-finish-reason precedent immediately below it in the same function). Covered by the PR's own tests/unit test additions for both the malformed-only and malformed-plus-real-call cases.",
"_rebaseline_2026_07_22_8213_chat_abandoned_target_abort": "PR #8213 (hartmark, fix/gemini-tpm-quota-cooldown-wait) own growth: src/sse/handlers/chat.ts 1794->1860 (+66, measured against the PR's own merge-base — the release tip separately carries an unrelated -5 net shrink from #8013's antigravity callable-catalog alignment, which this PR's branch does not include and this entry does not cover). Adds resolveDispatchClientRawRequest(): merges a per-target modelAbortSignal into clientRawRequest.signal (via mergeAbortSignals) so a combo target abandoned by comboTargetTimeoutMs actually observes its own abort and reaches its cleanup path, instead of hanging forever inside withRateLimit/acquireAccountSemaphore and leaking a permanent 'pending' dashboard entry (live incident, log id 1784418258231-14961a). Also wires combo-exhausted rejection logging to capture request body + attempted models via the new rejectedRequestUsage helper. Irreducible additions at the existing chat dispatch chokepoint. Covered by the PR's own combo-config + integration test additions.",
"_rebaseline_2026_07_22_8213_combo_cooldown_wait_recording": "PR #8213 (hartmark, fix/gemini-tpm-quota-cooldown-wait) own growth: open-sse/services/combo.ts 3548->3604 (+56, entirely this PR's diff — no other commit touched this file between the PR's merge-base and the release tip). Fixes combo cooldown-wait state recording so a bogus 503 is no longer crystallized when the cooldown-wait vars reset every setTry, adds an OpenAI-format SSE error frame path for combo-exhausted rejections (capturing request body + attempted models), and gives an abandoned per-target dispatch its own timeout instead of leaking a permanent 'pending' dashboard entry. Irreducible additions at the existing handleComboChat dispatch/retry chokepoint (mirrors the prior quota-share/headroom/task-aware strategy-branch precedents already frozen in this file). Covered by the PR's own combo-config + Gemini TPM-ceiling benchmark test additions.",
"_rebaseline_2026_07_22_8213_gemini_tpm_quota_cooldown_wait": "PR #8213 (hartmark, fix/gemini-tpm-quota-cooldown-wait) own growth: open-sse/services/accountFallback.ts 1857->1932 on the merged tip (release 1892 incl #8050 +35, plus this PR own growth +40); measured against the PR own merge-base was 1857->1898 (+41 — the release tip separately carries an unrelated +34 from #8050's antigravity 404 model-not-found lockout scoping, which this PR's branch does not include and this entry does not cover). Own growth is the Gemini TPM-ceiling classification + cooldown-wait wiring feeding into the combo cooldown-wait state machine (rate-limit wedge recovery) introduced by this PR's commit series. Irreducible additions at the existing account-fallback/model-lockout chokepoint. Covered by the PR's own gemini-rate-limit-tracker and TPM-ceiling benchmark test additions.",
"_rebaseline_2026_07_22_8213_health_unblock_model_cooldowns": "PR #8213 (hartmark, fix/gemini-tpm-quota-cooldown-wait) own growth: src/app/(dashboard)/dashboard/health/page.tsx 1094->1165 (+71, entirely this PR's diff — no other commit touched this file between the PR's merge-base and the release tip). Adds handleUnblockAll/handleUnblockOne dashboard actions (DELETE /api/resilience/model-cooldowns) so an operator can manually clear a Gemini TPM-wedge model lockout surfaced by this PR's cooldown-wait fixes, instead of waiting out the ceiling. Irreducible UI wiring at the existing health-page action chokepoint. Covered by the PR's own dashboard/resilience test additions.",
"_rebaseline_2026_07_22_8213_requestloggerdetail_unblock_ui": "PR #8213 (hartmark, fix/gemini-tpm-quota-cooldown-wait) own growth: src/shared/components/RequestLoggerDetail.tsx 799->941 (+142, entirely this PR's diff — no other commit touched this file between the PR's merge-base and the release tip; crosses the general 800-line new-file cap so is frozen here for the first time). Adds a collapsible section header (open/expand-less toggle) plus per-log-entry unblock (`unblocking`/`cleared` state, isCombo503 detection) so the request-logger detail panel surfaces the same Gemini TPM cooldown-wait / model-lockout unblock action introduced by this PR at the individual-request level (mirrors the health-page bulk unblock action added in the same PR). Covered by the PR's own dashboard/resilience test additions.",
"_rebaseline_2026_07_22_fusion_8013_8098_antigravity": "Fusion of #8013 (backryun, catalog/IDE-CLI-split rewrite) + #8098 (nguyenha935, protocol-fidelity/fail-closed/credits/tool-cloaking): open-sse/services/usage/antigravity.ts NEW 802 (>cap 800, +2 — #8098 credits/tier usage service on #8013's profile-aware headers). Test growth (models-catalog-route 1605->1608, provider-models-route 1752->1757 from #8013 Gemini 3.6 catalog) tracked in testFrozen.",
"_rebaseline_2026_07_23_8127_grok_weekly_quota": "#8127 (@apoapostolov) own growth: src/sse/handlers/chat.ts 1861->1865 (+4) — weekly quota tracking for grok-web wires a quota-fetch hook at the existing dispatch chokepoint. Thin wiring mirroring adjacent provider-quota branches; not extractable. Covered by tests/unit/grok-quota-fetcher.test.ts.",
"_rebaseline_2026_07_23_8143_empty_catch_logging": "#8143 (@chirag127) own growth: open-sse/utils/stream.ts 2869->2887 (+18) — replacing empty catch blocks in the SSE stream subsystem with console.debug logging (Rule #6 silent-swallow fix, issues #8138-#8142). Cohesive logging additions at the existing catch chokepoints, not extractable; frozen at new size. Covered by tests/unit/stream-handler-catch-logging-8143.test.ts.",
"_rebaseline_2026_07_23_8219_cache_ttl_settings_sidebar": "#8219 (@oyi77) own growth: sections.ts 806->813 (+7) — configurable model-catalog cache-TTL settings adds a new sidebar nav entry + its visibility wiring. Sidebar item array is data, not extractable logic; frozen at new size.",
"_rebaseline_2026_07_23_8247_8248_model_unhealthy": "#8247+#8248 own growth: accountFallback.ts 1940->1941 (+1, irreducible import statement only — the substantive #8248 DEGRADED-pattern classifier was extracted into open-sse/config/errorConfig.ts, which has ample headroom, instead of growing this frozen file; #8247's fix is a single existing-line condition change, net zero lines). Scoping the credits-exhausted 403/429 branch to isCompatibleProvider() (per-model-quota openai/anthropic-compatible-* nicknames) so it stays model-scoped instead of terminalling the whole connection, and classifying NVIDIA NIM 'Function ... DEGRADED' 400 bodies as model-access-denied instead of a raw passthrough 400. Covered by tests/unit/8247-accountfallback-model-unhealthy.test.ts and tests/unit/8248-accountfallback-nvidia-degraded.test.ts.",
"_rebaseline_2026_07_23_8252_combo_400_advance": "#8252 (@RaviTharuma) own growth: accountFallback.ts 1932->1940 (+8) + combo.ts 3604->3630 (+26) — advance combo on model-scoped 400s wrapped as invalid/Bad-Request. Irreducible wiring at existing account-fallback + combo dispatch chokepoints. Covered by combo-model-scoped-400-advance.test.ts.",
"_rebaseline_2026_07_23_8266_alibaba_media": "#8266 (@backryun) own growth: imageRegistry.ts 821->979 (+158) — Alibaba-family media models (Qwen image/video, Bailian, Wan) added to the image/video registry. Registry model data, not extractable logic; frozen at new size.",
"_rebaseline_2026_07_24_8388_compression_detail_persist": "#8388 (compression engine DETAIL settings — Headroom/session-dedup/CCR — dropped on save) own growth: src/lib/db/compression.ts 866->872 (+6 = irreducible call-site wiring at the existing getCompressionSettings/updateCompressionSettings chokepoint: one import line, one `...buildDetailConfigDefaults()` spread in the seed config, and one `case \"sessionDedup\": case \"ccr\": applyDetailConfigUpdate(config, key, parsed); break;` load-switch case, mirroring the existing headroom/#8056 case immediately above it). The actual normalizer logic (normalizeSessionDedupConfig/normalizeCcrConfig, matching SESSION_DEDUP_SCHEMA/CCR_SCHEMA bounds) was EXTRACTED into a new leaf src/lib/db/compressionDetailNormalizers.ts (well under cap) so this frozen file only carries the minimal dispatch wiring. Covered by tests/unit/8388-compression-detail-persist.test.ts (schema-accept + full DB save->reload round-trip for both new sub-objects, plus a no-regression assertion on the existing headroom round-trip).",
"_rebaseline_2026_07_24_responses_toolcalls_log_summary": "hartmark, fix/responses-tool-calls-log-summary own growth: open-sse/translator/response/openai-responses.ts 1163->1174 (+11). closeToolCall() now also writes the completed tool call into the shared state.toolCalls Map (already populated by the openai-to-claude / claude-to-openai / gemini-to-openai response translators) so stream.ts's completion-log summary builder (which reads state.toolCalls, not this translator's own funcCallIds/funcNames/funcArgsBuf bookkeeping) reports finish_reason \"tool_calls\" and message.tool_calls for openai->openai-responses translated streams instead of always logging \"stop\" with no tool_calls — the actual client-facing SSE events were already correct; only the persisted call-log summary was wrong. Irreducible call-site addition at the existing tool-call-close chokepoint. Covered by the new regression test in tests/unit/translator-resp-openai-responses.test.ts.",
"_rebaseline_2026_07_25_8476_combo_input_bound_homogeneous_scope": "PR #8476 (herjarsa, fix/8375-8459-combo-image-fixes, #8375) own growth: open-sse/services/combo.ts 3642->3679 (+37 net: +29 the PR's own isInputBoundFailure short-circuit for deterministic context_length_exceeded/context_window_exceeded failures, +8 a /green-prs pre-merge fix scoping that short-circuit to homogeneous remainders only — the shipped code fired unconditionally on ANY target, regressing the intentional heterogeneous-combo fallback #6637/isContextOverflow400 protects, exactly as flagged by this PR's own review evidence but never actually implemented in the branch). The fix compares orderedTargets[i+1..] modelStr against the failing target's modelStr at the existing executeTarget dispatch chokepoint (mirrors the sameProviderNext precedent a few lines below) — irreducible call-site wiring, not extractable without hiding the dispatch boundary. Covered by tests/unit/combo-input-bound-failure-8375.test.ts (homogeneous pool still short-circuits) and the new tests/unit/combo-input-bound-heterogeneous-8375.test.ts (heterogeneous combo now correctly falls through to the larger-context target).",
"_rebaseline_2026_07_25_adobe_firefly_reference_images": "Follow-up to #8006: storage upload + referenceBlobs for image/video and /v1/images/edits dispatch. adobeFireflyClient.ts 1958->2317 (+upload helpers, extract sources, resolve blob ids). Note: 2317 not 2316 — check-file-size.mjs counts LOC via split(\"\\n\").length (counts the trailing-newline empty element), which is 1 higher than `wc -l` on a file ending in \\n; the PR's original entry (2316) was measured with wc -l and undercounted by 1 against the actual gate.",
"_rebaseline_pr1043_minimax_tts": "Upstream port decolua/9router#1043 (toanalien) own growth: audioSpeech.ts 965->1061 (+96). Adds MiniMax T2A v2 TTS dispatch (handleMinimaxSpeech + hexToBytes helper) — provider entry was already in audioRegistry (format: minimax-tts) but no handler existed, falling through to the OpenAI-compatible default that fails (T2A has custom shape + hex-encoded audio + base_resp envelope). New branch sits next to the other inline provider branches (xiaomi-mimo, coqui, tortoise, aws-polly) — extracting would just create indirection. Covered by tests/unit/minimax-tts-1043.test.ts (3 tests, GREEN: success, base_resp error, invalid-hex).",
"_rebaseline_pr4592_exclude_exhausted_auto": "Reconcile #4592 already-merged growth: combo.ts 2991->3036 (+45, terminal-status quota-cutoff exclusion in buildAutoCandidates + opt-in gate). Fast-gate PR->release does not run check:file-size.",
"open-sse/executors/antigravity.ts": 1528,
"open-sse/executors/base.ts": 1640,
"open-sse/executors/chatgpt-web.ts": 3241,
"open-sse/executors/codex.ts": 1562,
"open-sse/executors/cursor.ts": 1560,
"open-sse/executors/deepseek-web.ts": 1148,
"open-sse/executors/grok-web.ts": 1044,
"open-sse/executors/muse-spark-web.ts": 1405,
"open-sse/handlers/chatCore.ts": 5034,
"open-sse/handlers/imageGeneration.ts": 3101,
"open-sse/handlers/responseSanitizer.ts": 1128,
"open-sse/handlers/search.ts": 1536,
"open-sse/handlers/videoGeneration.ts": 1063,
"open-sse/mcp-server/schemas/tools.ts": 1505,
"open-sse/mcp-server/server.ts": 1411,
"open-sse/mcp-server/tools/advancedTools.ts": 1120,
"open-sse/services/accountFallback.ts": 1972,
"open-sse/services/adobeFireflyClient.ts": 2385,
"open-sse/services/claudeCodeCompatible.ts": 1202,
"open-sse/services/combo.ts": 3648,
"open-sse/services/compression/strategySelector.ts": 1060,
"open-sse/services/rateLimitManager.ts": 1167,
"open-sse/translator/response/openai-responses.ts": 1204,
"open-sse/utils/cursorAgentProtobuf.ts": 1505,
"open-sse/utils/stream.ts": 2889,
"src/app/(dashboard)/dashboard/HomePageClient.tsx": 1388,
"src/app/(dashboard)/dashboard/analytics/ComboHealthTab.tsx": 1031,
"src/app/(dashboard)/dashboard/api-manager/ApiManagerPageClient.tsx": 3117,
"src/app/(dashboard)/dashboard/cache/media/MediaPageClient.tsx": 1067,
"src/app/(dashboard)/dashboard/combos/page.tsx": 4647,
"src/app/(dashboard)/dashboard/costs/CostOverviewTab.tsx": 1283,
"src/app/(dashboard)/dashboard/costs/quota-share/components/PoolWizard.tsx": 1022,
"src/app/(dashboard)/dashboard/endpoint/EndpointPageClient.tsx": 2615,
"src/app/(dashboard)/dashboard/health/page.tsx": 1165,
"src/app/(dashboard)/dashboard/providers/[id]/components/modals/EditConnectionModal.tsx": 1316,
"src/app/(dashboard)/dashboard/providers/page.tsx": 1944,
"src/app/(dashboard)/dashboard/runtime/RuntimePageClient.tsx": 1201,
"src/app/(dashboard)/dashboard/settings/components/PricingTab.tsx": 1019,
"src/app/(dashboard)/dashboard/settings/components/ProxyRegistryManager.tsx": 1464,
"src/app/(dashboard)/dashboard/settings/components/ResilienceTab.tsx": 1123,
"src/app/(dashboard)/dashboard/settings/components/RoutingTab.tsx": 1629,
"src/app/(dashboard)/dashboard/settings/components/SystemStorageTab.tsx": 1573,
"src/app/(dashboard)/dashboard/usage/components/BudgetTab.tsx": 1028,
"src/app/(dashboard)/dashboard/usage/components/EvalsTab.tsx": 2148,
"src/app/(dashboard)/dashboard/usage/components/ProviderLimits/index.tsx": 1119,
"src/app/api/providers/[id]/models/route.ts": 2250,
"src/app/api/v1/models/catalog.ts": 1549,
"src/lib/tokenHealthCheck.ts": 1021,
"src/lib/db/apiKeys.ts": 1529,
"src/lib/db/core.ts": 1637,
"src/lib/db/migrationRunner.ts": 1084,
"src/lib/db/models.ts": 1097,
"src/lib/db/providers.ts": 1034,
"src/lib/memory/retrieval.ts": 1073,
"src/lib/tailscaleTunnel.ts": 1202,
"src/lib/usage/providerLimits.ts": 1013,
"src/shared/components/OAuthModal.tsx": 1134,
"src/shared/components/RequestLoggerV2.tsx": 1629,
"src/shared/components/analytics/charts.tsx": 1035,
"src/shared/services/cliRuntime.ts": 1122,
"src/sse/handlers/chat.ts": 1877,
"src/sse/services/auth.ts": 2508,
"tests/unit/account-fallback-service.test.ts": 1572,
"tests/unit/provider-validation-specialty.test.ts": 2985,
"open-sse/executors/hyperagent.ts": 1026,
"open-sse/executors/default.ts": 1042,
"open-sse/executors/kiro.ts": 1069
},
"_rebaseline_2026_07_27_v3849_train2": "Merge-train 2 (7 PRs) — owner-approved 2026-07-27. Single entry: chatCore.ts 4955->5006 (#8595, Responses multi-turn image compaction before the context hard-reject). Genuine irreducible growth at the existing compaction chokepoint in handleChatCore — the PR adds a last-resort retry against the concrete budget plus the estimateFinalInputTokens helper, both wired at the pre-existing call site rather than a new branch. Covered by tests/unit/8560-responses-image-compaction.test.ts (4 tests).",
"_rebaseline_2026_07_27_v3849_train3": "Merge-train 3 (13 PRs) — owner-approved 2026-07-27. Both entries are genuine irreducible growth at existing chokepoints, not new branches: src/lib/db/apiKeys.ts 1518->1529 (#8805 cx/* ≡ codex/* API-key model permissions); open-sse/handlers/chatCore.ts 5006->5020 (#8806 real response payload into plugin onResponse hooks). Covered by tests/unit/db-apiKeys-crud.test.ts (4 new cases) and the two plugin-hook test files updated in #8806 respectively.",
"_rebaseline_2026_07_28_8842_antigravity_projectid_refresh": "PR #8842 (fix/antigravity-projectid-refresh) own growth: open-sse/executors/antigravity.ts 1493->1528 (+35 = projectId discovery in refreshCredentials: import ensureAntigravityProjectAssigned + trim projectId + call ensureAntigravityProjectAssigned with 8s timeout + persistDiscoveredAntigravityProjectId + log success/failure). Irreducible wiring at the existing credential-refresh chokepoint. Covered by tests/unit/executor-antigravity.test.ts (4 new test cases).",

View File

@@ -1,176 +0,0 @@
{
"open-sse/executors/azure-openai.ts": {
"TS2345": 1
},
"open-sse/executors/chatgpt-web.ts": {
"TS2339": 1
},
"open-sse/executors/claude-web/stream.ts": {
"TS2322": 1,
"TS2345": 1
},
"open-sse/executors/copilot-web.ts": {
"TS2353": 1
},
"open-sse/executors/deepseek-web.ts": {
"TS2352": 1
},
"open-sse/executors/default.ts": {
"TS2352": 1
},
"open-sse/executors/duckduckgo-web.ts": {
"TS2345": 2
},
"open-sse/executors/duckduckgo-web/challenge.ts": {
"TS2304": 1
},
"open-sse/executors/edgeTts.ts": {
"TS2345": 1
},
"open-sse/executors/gemini-business.ts": {
"TS2339": 1
},
"open-sse/executors/ghe-copilot.ts": {
"TS2554": 1
},
"open-sse/executors/inner-ai.ts": {
"TS2352": 2
},
"open-sse/executors/theoldllm.ts": {
"TS2322": 1
},
"open-sse/executors/veoaifree-web.ts": {
"TS2322": 1
},
"open-sse/executors/windsurf.ts": {
"TS2322": 1
},
"open-sse/handlers/chatCore.ts": {
"TS2339": 30,
"TS2322": 1,
"TS2345": 11
},
"open-sse/handlers/chatCore/claudeUpstreamMessages.ts": {
"TS2345": 1
},
"open-sse/handlers/chatCore/clientUsageBuffer.ts": {
"TS2345": 1
},
"open-sse/handlers/chatCore/clineResponseEnvelope.ts": {
"TS2698": 1
},
"open-sse/handlers/chatCore/compressionAnalyticsWrite.ts": {
"TS2724": 1
},
"open-sse/handlers/chatCore/nonStreamingResponseHeaders.ts": {
"TS2322": 2
},
"open-sse/handlers/chatCore/sanitization.ts": {
"TS2339": 1,
"TS2537": 1
},
"open-sse/handlers/chatCore/semanticCacheStore.ts": {
"TS2345": 1
},
"open-sse/handlers/chatCore/streamingPipeline.ts": {
"TS2345": 2
},
"open-sse/handlers/chatCore/streamingSemanticCacheStore.ts": {
"TS2345": 1
},
"open-sse/handlers/chatCore/thinkingSignatureRecovery.ts": {
"TS2339": 2
},
"open-sse/handlers/imageGeneration.ts": {
"TS2554": 2
},
"open-sse/handlers/responsesHandler.ts": {
"TS2339": 1,
"TS2345": 1
},
"open-sse/handlers/sseParser.ts": {
"TS2322": 2
},
"open-sse/handlers/videoGeneration.ts": {
"TS2339": 2
},
"open-sse/mcp-server/tools/compressionTools.ts": {
"TS2339": 2
},
"open-sse/services/__tests__/specificityDetector.test.ts": {
"TS2353": 2
},
"open-sse/services/browserBackedChat.ts": {
"TS2322": 1,
"TS2794": 1
},
"open-sse/services/claudeAdaptiveThinking.ts": {
"TS2352": 2
},
"open-sse/services/comboManifestMetrics.ts": {
"TS2307": 1
},
"open-sse/services/compression/engines/ccr/index.ts": {
"TS2339": 1
},
"open-sse/services/payloadRules.ts": {
"TS2677": 1
},
"open-sse/services/tokenLimitCounter.ts": {
"TS2551": 1
},
"open-sse/transformer/responsesTransformer.ts": {
"TS2339": 1
},
"open-sse/utils/stream.ts": {
"TS2339": 7,
"TS2345": 1,
"TS2556": 1
},
"src/app/api/v1/_shared/mediaGenerationRoute.ts": {
"TS2339": 2
},
"src/app/api/v1/models/catalog.ts": {
"TS2345": 1
},
"src/app/api/v1/models/catalogVision.ts": {
"TS2322": 1
},
"src/app/api/v1/videos/generations/route.ts": {
"TS2322": 1,
"TS2345": 1
},
"src/lib/guardrails/visionBridge.ts": {
"TS2345": 1
},
"src/lib/providers/codexFastTier.ts": {
"TS2367": 1
},
"src/lib/skills/builtins.ts": {
"TS2322": 1
},
"src/lib/skills/injection.ts": {
"TS2339": 1
},
"src/lib/skills/webFetchExecution.ts": {
"TS2322": 1
},
"src/lib/streamingPiiTransform.ts": {
"TS2345": 1
},
"src/shared/providers/webSessionCredentials.ts": {
"TS2353": 1,
"TS2322": 1
},
"src/shared/validation/helpers.ts": {
"TS2339": 1
},
"src/sse/handlers/chat.ts": {
"TS2352": 1,
"TS2322": 2,
"TS2339": 1
},
"src/sse/services/model.ts": {
"TS2339": 4
}
}

View File

@@ -110,8 +110,9 @@
"_rebaseline_2026_06_26_v3837_release": "343->345. v3.8.37 cycle drift surfaced by the release-green pre-flight (the Quality Ratchet does NOT run on PR->release fast-gates, so warnings/complexity accrued unmeasured across this cycle's 76 commits — provider adds DGrid/Pioneer/xAI, headroom proxy lifecycle #4649, ~50 SSE/translator fixes, Engine Combos #5062). Trust-but-verify: this release-finalize working tree touches ONLY CHANGELOG.md, docs/i18n/*/CHANGELOG.md mirrors, and these baselines — 0 production-code change, so all drift is inherited cycle drift (`any` warn-allowed in open-sse/ + tests/). Tighten via --require-tighten next cycle."
},
"cognitiveComplexity": {
"value": 957,
"_rebaseline_2026_07_25_dario_upstream_proxy_selector": "951->957 (+6). Same cycle-drift + own-growth split as the complexity-baseline.json note dated 2026-07-25 (PR #8523, Dario embedded service): cognitive-complexity does not run on PR->release fast-gates, so drift accrues unratcheted. Base upstream/release/v3.8.49 tip measures 956 locally with this PR\u0027s commits removed; this branch measures 957 both locally and on the CI runner. This PR\u0027s own genuine contribution is +1: the new mode-selector conditional rendering (Native/CLIProxyAPI/Dario/Fallback branches plus the fallback-backend picker) in ConnectionRow.tsx. Structural shrink stays tracked in #3501. Tighten via --update next cycle.",
"value": 1223,
"_rebaseline_2026_07_25_dario_upstream_proxy_selector": "951->957 (+6). Same cycle-drift + own-growth split as the complexity-baseline.json note dated 2026-07-25 (PR #8523, Dario embedded service): cognitive-complexity does not run on PR->release fast-gates, so drift accrues unratcheted. Base upstream/release/v3.8.49 tip measures 956 locally with this PR's commits removed; this branch measures 957 both locally and on the CI runner. This PR's own genuine contribution is +1: the new mode-selector conditional rendering (Native/CLIProxyAPI/Dario/Fallback branches plus the fallback-backend picker) in ConnectionRow.tsx. Structural shrink stays tracked in #3501. Tighten via --update next cycle.",
"_rebaseline_2026_07_25_8470_hyperagent_sticky_thread": "951->957 (+6). PR #8470 (artickc, fix/hyperagent-tool-loop-thread-sticky) pre-green validation. Trust-but-verify: origin/release/v3.8.49 tip alone (pristine, no PR changes) already measures 956 with node scripts/check/check-cognitive-complexity.mjs — i.e. +5 is inherited cycle drift unrelated to this PR (cognitive-complexity does not run on PR->release fast-gates). This PR's OWN growth adds exactly +1: per-file eslint scoped scan (eslint --config eslint.complexity-ratchets.config.mjs open-sse/executors/hyperagent.ts) on base vs PR shows extractMessageText() crossing the threshold for the first time (new sonarjs/cognitive-complexity violation, 26 > 15) from the new Anthropic tool_use/tool_result flattening branches; resolveHyperAgentThreadBinding's existing pre-#8470 violation (16) grows to 21 (still counted once) from the new root-key lookup tier; createHyperAgentThread and execute() are unchanged pre-existing violations. Net repo-wide total = 956 (inherited drift) + 1 (this PR's own new violation) = 957. Full-repo re-measurement of the merged branch was attempted but not completed live due to heavy concurrent devbox load (many other /green-prs sessions running the identical full-repo eslint scan in parallel); the value here is derived from two independently-clean measurements (base-tip full scan + per-file base-vs-PR delta) rather than a third full-repo run. Covered by tests/unit/executor-hyperagent.test.ts (19/19). Tighten via --update next cycle.",
"_rebaseline_2026_07_25b_v3849_mergetrain_owngrowth": "Owner-approved (chat, 2026-07-25): 956->968 (+12). v3.8.49 /merge-prs 41-PR merge-train aggregate own-growth: measured 968 on the combined boarded tree (tip ac15014ca7) vs 956 on the pristine release tip. The batch's new over-threshold functions come from the pre-screen-flagged complexity-growth set (#8378/#8432/#8476/#8526 etc); each PR is under-ceiling alone, the combined batch adds +12. Same merge-burst class as the notes below; owner chose ceiling-absorb over per-PR extraction. Structural shrink tracked in #3501; tighten via --update next cycle.",
"_rebaseline_2026_07_25_v3849_mergequeue_drain": "Owner-approved (chat, 2026-07-25): 951->956 (+5). v3.8.49 /merge-prs queue-drain: inherited cognitive-complexity drift from the cycle's merge burst (base-red slices + owner PRs + parallel-session merges #8500-8508); check:cognitive-complexity does not run on PR->release fast-gates, so it accrued unmeasured. Measured 956 on the pristine release tip 4053e2314a alone (BEFORE any queue PR boards) — the entire +5 is base drift already on the tip, reddening Fast Quality Gates for every merge-ready PR. Owner approved raising the ceiling to the measured tip value so the ~34-PR merge-train lands without per-PR extraction churn. Structural shrink tracked in #3501; tighten via --update next cycle.",
@@ -147,10 +148,9 @@
"dedicatedGate": true
},
"codeqlAlerts": {
"value": 1,
"value": 0,
"direction": "down",
"dedicatedGate": true,
"_rebaseline_2026_08_06_base_grew": "Base branch file-size drift: translator-openai-to-gemini.test.ts grew 1619->1622 (test assertions for Gemini translator compatibility). CodeQL alert (js/insufficient-password-hash in raycast.ts) is pre-existing base-red; incremented baseline to match."
"dedicatedGate": true
},
"secretFindings": {
"_note": "Zeroed 2026-07-13 (WS6/D3): the 3 frozen generic-api-key FPs are allowlisted with justification in .gitleaks.toml — any NEW finding regresses the ratchet.",

View File

@@ -183,11 +183,6 @@ Per environment:
ships slim by design.
- **VPS (PM2)** — install into the app's `node_modules`, then restart the process so the
worker re-probes the gate.
- **Raw Next standalone (`npm run build``.build/next/standalone/server.js`)** — the
standalone trace ships NEITHER the worker nor the optional deps, so the engine silently
fail-opens. `scripts/build/colocate-standalone.mjs` re-applies both (worker esbuild +
optional-dep closure into the standalone tree); it runs automatically via the
`postbuild` npm hook after every build. Idempotent, fail-soft when deps are absent.
**Verify it is active:** with LLMLingua selected, real prose actually shrinks (the engine
stops fail-opening), and the first request triggers the model download into

View File

@@ -146,26 +146,6 @@ That `78-95%` number applies when both RTK and Caveman can reduce the same input
Caveman response output mode is separate: when enabled, use Caveman's own output savings (`65%`
average, `~75%` headline, `22-87%` range). Total billing savings depend on your prompt/output mix.
### What "eligible" actually means
The 15-95% headline range is real, but it only applies to **redundant or verbose** content — repeated
error lines, a build log that spams the same warning, an oversized `grep`/file-read dump. It does
**not** mean every request saves that much.
Verified empirically (`tests/unit/compression/stacked-compression-tool-result-savings.test.ts`): a
`stacked` (RTK + Caveman) run against an Anthropic-shape `tool_result` block containing 300 identical
error lines produced **95.93% token savings / 96.26% character savings** — squarely in the advertised
range. But the same pipeline run against normal, non-redundant tool output (a clean `grep` match list,
a short file read, ordinary conversational text) correctly produces **near-zero savings**, because
there is nothing repetitive to remove and `validateCompression()` (`validation.ts`) refuses to ship a
rewrite that would drop or alter code blocks, URLs, headings, versions, or `CONST_CASE` identifiers.
This is expected, safe behavior, not a bug: a coding session that mostly reads/greps clean files will
see modest total savings even with compression fully enabled, while a session that hits a failing
loop or a chatty linter will see the full 78-95% range on that traffic. Don't use a single session's
low aggregate savings percentage as evidence compression is misconfigured — check whether the
underlying tool output was actually redundant first.
---
## Token Savings Visualization

View File

@@ -1,17 +1,17 @@
---
title: "Radar Free-Model Catalog"
version: 3.8.50
lastUpdated: 2026-08-07
lastUpdated: 2026-08-05
---
# Radar Free-Model Catalog
> **Source of truth:** `src/lib/radar/`, `src/lib/db/radar.ts`, `src/app/api/radar/`
> **Last updated:** 2026-08-07 — v3.8.50
> **Last updated:** 2026-08-05 — v3.8.50
Radar is an **optional add-on** that overlays a signed, freshly-curated free-model
catalog on top of the release baseline (`FREE_MODEL_BUDGETS` in
`open-sse/config/freeModelCatalog.data.ts`). It exists because the free-tier landscape moves
`open-sse/config/freeModelCatalog.ts`). It exists because the free-tier landscape moves
faster than release cadence — providers add, shrink, or discontinue free quotas between
releases, and the baseline catalog can only be refreshed when a new version ships.
@@ -82,43 +82,6 @@ that lets the feed service decide which tier to serve (see
---
## Getting a supporter key
The activation screen (`/dashboard/radar`) links out to two flows for **obtaining** a
supporter key. The OSS repo itself never issues one, never runs payment code, and
**never states a price** — pricing is decided and displayed entirely on the
destination pages, not in this repo (spec decision D14).
- **"I'm a contributor"** — opens `RADAR_CONTRIBUTOR_CLAIM_URL` (default
`https://radar.omniroute.online/auth/github`), a GitHub OAuth claim flow hosted on
the private radar server. It verifies the visitor's GitHub account and grants a
supporter key to anyone with 5+ merged pull requests or a top-100 contributor spot
on the repo.
- **"Support the project"** — opens `RADAR_SUPPORTER_PLANS_URL` (default
`https://radar.omniroute.online/planos`), the payment/plans page.
Both URLs are resolved server-side (`src/lib/radar/links.ts`, same env-override
pattern as `RADAR_FEED_URL`) and relayed to the dashboard through the existing
`GET /api/radar/settings` response (`contributorClaimUrl`, `supporterPlansUrl`) — the
client component never reads `process.env` itself.
| Var | Purpose |
| -------------------------------- | ---------------------------------------------------------------------------------------------- |
| `RADAR_CONTRIBUTOR_CLAIM_URL` | Overrides the contributor-claim URL (default `https://radar.omniroute.online/auth/github`). |
| `RADAR_SUPPORTER_PLANS_URL` | Overrides the supporter-plans URL (default `https://radar.omniroute.online/planos`). |
Once a visitor has a key (`omr_` + 40 hex chars), it is set with `POST
/api/radar/settings` (`{ supporterKey }`) — the same endpoint documented under
[Data sync](#data-sync-is-a-separate-opt-in--the-privacy-promise) above.
**Known gap:** the dashboard activation screen does not yet have a dedicated
key-paste input — pasting a key today requires calling `POST /api/radar/settings`
directly (curl, a script, or a future UI). This release only adds the two claim/plans
buttons; the API already accepts and masks the key, but no `<input>` for it exists in
`src/app/(dashboard)/dashboard/radar/page.tsx` yet.
---
## Security model
### Ed25519 signature over exact bytes
@@ -164,24 +127,6 @@ untouched. The cached payload is defensively re-validated again on every read
(`getRadarCatalog()`) — a corrupted or hand-edited cache row falls back to the
baseline rather than being served.
### Response size cap (10 MB)
`syncRadar()` enforces a **10 MB hard cap** on the feed response body — the signed
feed is a KB-scale JSON document, so anything past this points at a misconfigured or
hostile `RADAR_FEED_URL` (or an upstream serving garbage), not a legitimate catalog.
Enforcement is two-layered:
1. A `Content-Length` preflight check skips reading the body entirely when the
header already declares a value over the cap.
2. A running-total check while reading the body enforces the cap even when
`Content-Length` is absent or understates the real size — the header is never
trusted on its own. Concatenating the accumulated chunks preserves the exact
bytes needed for the Ed25519 signature check afterward.
Exceeding the cap returns `{ status: "too_large" }` and leaves the cache untouched,
following the same non-destructive pattern as every other sync failure
(`invalid_signature`, `invalid_schema`, `stale`).
---
## Tiers: `community` and `live`
@@ -201,28 +146,6 @@ recoverable, all non-fatal to the cached state) from a successful `{ status:
"updated", version, tier }`. There is no tier-specific error path a client needs to
handle.
### The served tier comes from a response header, not the signed body
The signed feed **body**'s `tier` field is always `"live"` — the feed service ships
**one signed artifact per version**, so the body cannot carry a per-request tier
without invalidating the Ed25519 signature (re-signing per request would defeat the
point of a pinned, cacheable, verifiable artifact). The tier actually served for a
given request is instead carried in the **`x-omniroute-feed-tier` response header**,
decided server-side from the request's `Authorization` key.
`syncRadar()` (`src/lib/radar/sync.ts::parseServedTierHeader()`) is the single place
that resolves the tier a client should trust:
1. Parse `x-omniroute-feed-tier` with `RadarTierSchema` (Zod) — an absent header, or
a value that isn't exactly `"community"` or `"live"`, is treated as **not
present** (never trusted into the cache/UI as-is; this also covers older feed
servers that predate the header).
2. Fall back to the signed body's `tier` field (always `"live"`) only when step 1
yields nothing.
3. The resolved tier is what gets cached and returned as `{ status: "updated",
version, tier }` — this is the value the dashboard shows, never the raw body
field.
---
## Read-time overlay merge rules
@@ -260,15 +183,13 @@ Every merged entry carries an `origin` field the UI renders as a badge:
## Local surfaces — never a feed proxy
Five local routes back the UI, all under `src/app/api/radar/`:
Three local routes back the UI, all under `src/app/api/radar/`:
| Route | Method | Purpose |
| ----------------------- | ------ | -------------------------------------------------------------------------------------------------- |
| `/api/radar/catalog` | GET | Returns the merged catalog (`getRadarCatalog()`) from the local cache. |
| `/api/radar/sync` | POST | Triggers `syncRadar()` server-side; returns the resulting status. |
| `/api/radar/settings` | GET | Returns `{ optIn, hasSupporterKey, supporterKeyMasked }` — never the raw key. |
| `/api/radar/settings` | POST | Sets opt-in and/or the (encrypted) supporter key. |
| `/api/radar/referrals` | GET | Returns `{ fixed, campaigns, tier }` from the local cache — see [Referral links](#referral-links-free-credits) below. |
| Route | Method | Purpose |
| --------------------- | ------ | ---------------------------------------------------------------------- |
| `/api/radar/catalog` | GET | Returns the merged catalog (`getRadarCatalog()`) from the local cache. |
| `/api/radar/sync` | POST | Triggers `syncRadar()` server-side; returns the resulting status. |
| `/api/radar/settings` | POST | Sets opt-in and/or the (encrypted) supporter key. |
**Hard rule: these routes never proxy the feed service.** The browser only ever talks
to the local OmniRoute server; `syncRadar()` is the single module in the whole client
@@ -276,118 +197,11 @@ that touches the network for Radar (`src/lib/radar/sync.ts`), and it always runs
server-side, never client-side. This keeps the feed URL and any supporter key
out of client-facing network traffic entirely.
All five routes return `404` when `RADAR_ENABLED` is off (see
All three routes return `404` when `RADAR_ENABLED` is off (see
[Flag](#flag-radar_enabled-default-off) above), and route error responses through
`buildErrorBody()`/`sanitizeErrorMessage()` per the repo-wide error-sanitization rule
(`docs/security/ERROR_SANITIZATION.md`).
### Authentication
All five routes require authentication via `isAuthenticated()`
(`src/shared/utils/apiAuth.ts`) — a dashboard session cookie or a management-scoped
API key, the same gate that protects the rest of `/api/settings/*`. The flag-off
`404` check always runs **before** the auth check, so an install with `RADAR_ENABLED`
off stays byte-identical (no auth prompt just to learn the surface doesn't exist);
once the flag is on, an unauthenticated request gets `401` before any DB read or
write. `GET /api/radar/settings` never returns the raw supporter key regardless of
auth state — only the masked form and a `hasSupporterKey` boolean.
---
## Referral links (free credits)
The server-published feed carries a `referrals` section (server-side D28 work, already
in production — this section documents the **client** consumption only):
```ts
referrals: {
fixed: RadarReferral[], // present in EVERY tier, including community
campaigns: RadarReferral[], // only populated on the live (supporter) tier;
// the community artifact always publishes []
}
// RadarReferral = { provider, url, kind: "fixo" | "campanha", validUntil,
// requiredAction, isDefault }
```
The client never decides which tier it received or which referrals belong in which
tier — the server already publishes two artifacts (`live`/`community`) with
`campaigns` gated server-side, same principle as the [tiers](#tiers-community-and-live)
section above. `RadarFeedSchema` (`src/lib/radar/feedSchema.ts`) validates `referrals`
as a whole-object `.default({fixed:[],campaigns:[]})`, and `campaigns` defaults
independently inside it — so a feed cached before this section existed on the server
still parses cleanly, and `campaigns` alone can also be absent without failing
validation. Every `RadarReferral.url` must be `https://` — a `http://` url fails
schema validation.
### Accessor
`src/lib/radar/index.ts` exports two read-only accessors, both never throwing (same
defensive contract as `getRadarCatalog()` — flag off, no cache, or a corrupt/old cached
payload all resolve to the empty shape instead of an error):
- `getRadarReferrals()` → `{ fixed: RadarReferral[], campaigns: RadarReferral[] }`.
- `getDefaultReferralFor(provider)` → the `fixed` referral with `isDefault: true` for
that provider, or `null`. Only looks at `fixed` — a campaign is never used as a
provider's "default" link.
The actual "which referral is the default for a provider" rule lives in
`findDefaultReferral()` (`src/lib/radar/referrals.ts`), a small pure function with **no
DB import** — it is safe to import into a `"use client"` component. `getRadarReferrals`/
`getDefaultReferralFor` (in `index.ts`) pull in `@/lib/db/radar` and therefore stay
server-only; the providers dashboard imports `referrals.ts` directly instead of
`index.ts` (see below) to avoid bundling `better-sqlite3` into the browser.
### `GET /api/radar/referrals`
Follows the exact same gate order as every other Radar route: `RADAR_ENABLED` off →
`404` (checked first, byte-identical inertia); unauthenticated → `401`; otherwise `200`
with `{ fixed, campaigns, tier }` — `tier` comes straight from the cache row and is
purely informative (drives the UI's soft upsell copy below), the route does no
gating of its own. Never proxies the feed server — same local-cache-only contract as
`/api/radar/catalog`.
### Dashboard UI — "Free credits" tab on `/dashboard/radar`
Reuses the existing Radar page (`src/app/(dashboard)/dashboard/radar/page.tsx`) as a
second tab instead of a new route — less routing/i18n surface for a feature that is a
variation on data the page already fetches. Once opted in, the tab bar offers
**Catalog** (existing table) and **Free credits**:
- Fixed links are grouped by provider, each showing `requiredAction` (when present)
and a `target="_blank" rel="noopener noreferrer"` button to the referral URL.
- Campaigns show the same, plus `validUntil` when present.
- When `campaigns` is empty **and** the served tier is `community`, the UI shows a
short upsell note ("limited-time campaigns are a supporter extra") — this **never**
hides or gates the fixed links list, which stays fully populated for every tier. The
upsell is soft messaging only, never a block.
### Referral link on the provider name (providers dashboard)
`ProviderPageHeader` (`src/app/(dashboard)/dashboard/providers/[id]/components/`)
already linked the provider name to `providerInfo.website` when present, with one
precedent for a monetized link: the Kimi (Moonshot AI) partner-link note
(`providers.kimiPartnerLinkNote` i18n key). D28 reuses that exact same discreet-note
pattern for Radar default referrals instead of introducing a new key.
Loose coupling, by design:
- `resolveProviderHeaderLink()` (`src/app/(dashboard)/dashboard/providers/providerPageUtils.ts`)
is a **pure** function — `(staticWebsite, referralUrl) => { website, isReferralLink }`
— with no dependency on `@/lib/radar` or `@/lib/db/*`. `providerPageUtils.ts` as a
whole stays free of those imports (asserted by
`tests/unit/provider-header-referral-link.test.ts`).
- `ProviderDetailPageClient.tsx` (a `"use client"` component) is the one place allowed
to fetch Radar data — via `fetch("/api/radar/referrals")`, the same local-route
pattern the Radar dashboard page itself uses — and it computes the default referral
client-side with `findDefaultReferral()` from the DB-free `src/lib/radar/referrals.ts`.
- With `RADAR_ENABLED` off, the fetch 404s, `referralUrl` stays `null`, and
`resolveProviderHeaderLink()` returns the static catalog `website` unchanged — the
provider page is byte-identical to before this feature existed. Same outcome when
there is no cache yet or no default referral for that specific provider.
- When a default referral does apply, `ProviderPageHeader` receives `isReferralLink`
and shows the same discreet note/tooltip as the Kimi partner link (reusing the
`providers.kimiPartnerLinkNote` key) — never a new, separate visual treatment.
---
## How to self-host a feed
@@ -417,6 +231,6 @@ feed.
## Related docs
- [`docs/security/ERROR_SANITIZATION.md`](../security/ERROR_SANITIZATION.md) — the
error-response pattern the five `/api/radar/*` routes follow.
error-response pattern the three `/api/radar/*` routes follow.
- [`docs/reference/ENVIRONMENT.md`](../reference/ENVIRONMENT.md#27-radar-feed-self-hosting)
— `RADAR_FEED_URL` / `RADAR_FEED_PUBKEY` reference.

View File

@@ -489,59 +489,6 @@ Provider profiles support these settings:
When many concurrent requests hit a rate-limited provider, OmniRoute uses mutex + auto rate-limiting to serialize requests and prevent cascading failures. This is automatic for API key providers.
### Chat requests fail with 503 / chat_admission_busy
**Symptoms:**
- The chat completions endpoint returns a retryable `503` response whose error code is
`chat_admission_busy`.
- The response includes `Retry-After`; the byte-based path uses 2 seconds, while the
structure-based path uses 1 second and includes `reason: "structure_limit"`.
- This can happen while another heavyweight chat or long-running streaming response is still
in flight.
The byte-based response body is:
```json
{
"error": {
"message": "Chat admission capacity is temporarily unavailable. Retry shortly.",
"type": "server_error",
"code": "chat_admission_busy"
}
}
```
The structure-based response uses the same type and code, with the message
`Structurally heavy chat request capacity is busy; retry shortly.` and
`reason: "structure_limit"`.
At the default thresholds, a request is structurally heavy when it has at least `200` messages,
at least `64` tools, or at least `32,000` estimated tokens, or when bounded structure estimation
exhausts its bounds of `10,000` visited nodes or depth `12`.
**Cause:** This is deliberate load shedding inside OmniRoute, not an upstream-provider failure.
Each process uses a process-local guard to reserve limited heavyweight capacity before retaining
and parsing a large request body. A heavyweight lease remains held for the lifetime of an SSE
response.
Current heavyweight lease occupancy is not surfaced in the dashboard.
Settings → Resilience → Request Queue → Concurrent Requests does not control this; that setting
governs a separate provider request-queue mechanism.
**Fix:**
1. Retry first. Clients should honor `Retry-After` and use backoff rather than immediately
repeating the request.
2. If normal deployment traffic repeatedly exhausts the guard, you can cautiously raise
`OMNIROUTE_CHAT_MAX_HEAVY_IN_FLIGHT` from its default of `1`. Increase it one step at a time,
restart OmniRoute after each change, and observe memory headroom under representative load.
Every additional heavyweight request can increase concurrent V8 heap use and container or
host OOM risk. No value is safe for every deployment; validate the setting against your own
traffic and memory limits rather than assuming that `2` is universally safe.
See the [environment-variable reference](../reference/ENVIRONMENT.md#4-security--authentication)
for the authoritative admission settings. Loosening the heavyweight classification thresholds
can let expensive requests bypass this guard and is riskier than a cautious in-flight increase.
---
## Optional RAG / LLM failure taxonomy (16 problems)

View File

@@ -194,7 +194,7 @@ OmniRoute uses **SQLite** (via `better-sqlite3`) for all persistence. These vari
| `OMNIROUTE_CHAT_HEAVY_MESSAGE_COUNT` | `200` | `src/shared/middleware/chatBodyAdmission.ts` | Message count that classifies a chat request as heavyweight even when its body is below the byte threshold. |
| `OMNIROUTE_CHAT_HEAVY_TOOL_COUNT` | `64` | `src/shared/middleware/chatBodyAdmission.ts` | Tool count that classifies a chat request as heavyweight even when its body is below the byte threshold. |
| `OMNIROUTE_CHAT_HEAVY_ESTIMATED_TOKENS` | `32000` | `src/shared/middleware/chatBodyAdmission.ts` | Conservative string-size token estimate that classifies a request as heavyweight; this is an admission-cost proxy, not provider billing tokenization. |
| `OMNIROUTE_CHAT_HARD_MAX_MESSAGES` | `0` (disabled) | `src/shared/middleware/chatBodyAdmission.ts` | Optional opt-in chat history cap. Disabled by default: a message count is deployment policy, not a universal property of a request, and capping here rejects conversations with a terminal `413` before the compression pipeline can make them servable. Heap growth is bounded by `OMNIROUTE_CHAT_MAX_HEAVY_IN_FLIGHT` and the heap-pressure shed. Set a positive value on memory-constrained deployments that need a hard ceiling; excess then receives structured compact-required `413`. |
| `OMNIROUTE_CHAT_HARD_MAX_MESSAGES` | `800` | `src/shared/middleware/chatBodyAdmission.ts` | Hard chat history cap. Requests above it receive structured compact-required `413` before compression, translation, or provider dispatch. |
| `OMNIROUTE_MAX_NONSTREAMING_RESPONSE_BYTES` | `67108864` (64 MB) | `open-sse/handlers/chatCore/nonStreamingResponseBody.ts` | Hard cap for a non-streaming upstream response buffered fully into memory. Past this the upstream reader is cancelled and the request fails fast instead of growing an unbounded string until the heap is exhausted. |
| `OMNIROUTE_FORWARDING_HEADER_BUDGET_BYTES` | `768` | `open-sse/handlers/chatCore/responseHeaders.ts` | Max wire bytes forwarded from upstream response headers. When the budget is exceeded, lower-priority headers (e.g., custom `x-codex-*`, `x-oai-request-id`) are dropped to stay within common reverse-proxy header limits. Set higher to forward more upstream metadata at the cost of larger response header size. |
| `CORS_ORIGIN` | _(unset)_ | `src/server/cors/origins.ts` | Legacy single-origin CORS allowlist. Prefer `CORS_ALLOWED_ORIGINS` for new deployments. CORS is only for cross-origin browser API clients; authenticated dashboard writes use same-origin requests plus session-bound CSRF protection instead. |
@@ -853,7 +853,6 @@ Reverse-engineered session bridge for hyperagent.com (`src/shared/constants/prov
| Variable | Default | Source File | Description |
| ----------------------------------- | ------------- | ---------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `MODELS_DEV_SYNC_ENABLED` | `false` | `src/lib/modelsDevSync.ts` | Opt-in switch for the models.dev capability sync. Set to anything non-empty it wins over the `modelsDevSyncEnabled` setting (Dashboard > Settings > AI) in either direction, so a deployment can pin the sync on or off without depending on database state surviving a rebuild; unset, it defers to that setting. On for `1`, `true`, `yes` or `on` in any casing; any other value is off. |
| `MODELS_DEV_SYNC_INTERVAL` | `86400` (24h) | `src/lib/modelsDevSync.ts` | Development-time model catalog sync interval in seconds. |
| `CONTEXT_WINDOW_RECONCILE_INTERVAL` | `86400` (24h) | `src/lib/contextWindowResolver.ts` | Interval (seconds) for the self-correcting context-window reconciler (5004): pins provider-declared windows from `/models` discovery as `auto:discovery` overrides when they diverge from the catalog. Set to `0` to disable. Reuses already-synced data (no new fetch); never overwrites `manual` overrides. |
@@ -1207,17 +1206,6 @@ Provider quota endpoints, network tunnels (Tailscale, Ngrok, MITM debug proxy),
| `OMNIROUTE_ROTATE_400_THRESHOLD` | `1` | `open-sse/services/rotationConfig.ts` | Number of `400` errors within `OMNIROUTE_ROTATE_400_WINDOW_SECONDS` required before the account is rotated (only consulted when `OMNIROUTE_ROTATE_ON_400=true`). |
| `OMNIROUTE_ROTATE_400_WINDOW_SECONDS` | `120` | `open-sse/services/rotationConfig.ts` | Sliding window (seconds) over which `400` errors are counted toward `OMNIROUTE_ROTATE_400_THRESHOLD`. |
### Claude Warmup Scheduler
Cron-driven warmup for opted-in Anthropic OAuth connections, so the 5-hour rate-limit window is opened by a trivial scheduled request instead of by the first real one (#8848). The scheduler is off unless `OMNIROUTE_WARMUP_ENABLED` is truthy **and** the connection is flagged in `settings.claudeWarmup.connections`; an empty connection list means nothing is warmed even with the env var on.
| Variable | Default | Source File | Description |
| ----------------------------- | -------------------------------------- | ---------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `OMNIROUTE_WARMUP_ENABLED` | _(unset → off)_ | `src/lib/warmupScheduler.ts` | Master switch for the warmup scheduler. Accepts `1`/`true`/`yes`/`on` (case-insensitive, trimmed). Any other value, or unset, leaves the scheduler off. |
| `OMNIROUTE_WARMUP_CRON` | `0 7 * * *` | `src/lib/warmupScheduler.ts` | Five-field cron expression for the warmup tick, evaluated in `America/Los_Angeles` (Anthropic's reset timezone) regardless of the host clock. |
| `OMNIROUTE_WARMUP_CONCURRENCY` | `3` | `src/lib/warmupScheduler.ts` | How many connections are warmed in parallel per tick. Clamped to `1`-`10`; a non-numeric value falls back to `3`. |
| `OMNIROUTE_WARMUP_MODEL` | `claude-3-5-haiku-20241022` | `src/lib/warmupScheduler.ts` | Model used for the warmup request. Override only if the default is unavailable on your plan; pick the cheapest model that still opens the window. |
### Browser-Login VNC Sessions & Data-Dir Alias
Containerized Chromium+VNC used for interactive browser-login credential capture (`/api/vnc-session`), plus a legacy `DATA_DIR` alias. All optional — the VNC defaults target the bundled `omniroute-vnc-chromium:local` image and are only overridden for a custom container image, ports, or lifecycle tuning.
@@ -1287,17 +1275,14 @@ that should be able to run the docs translator.
Optional add-on gated by the RADAR_ENABLED feature flag (default off — a feature
flag toggled via Settings/DB, not an env var; see
[docs/frameworks/RADAR.md](../frameworks/RADAR.md#flag-radar_enabled-default-off)).
The four variables below are optional overrides used only to point the client at a
self-hosted or forked feed / supporter-key flow instead of the default OmniRoute
Radar service. See [docs/frameworks/RADAR.md](../frameworks/RADAR.md) for the full
module doc.
Both variables below are optional overrides used only to point the client at a
self-hosted or forked feed instead of the default OmniRoute Radar feed. See
[docs/frameworks/RADAR.md](../frameworks/RADAR.md) for the full module doc.
| Variable | Default | Source File | Description |
| -------------------------------- | --------------------------------------------------- | ------------------------------ | ------------------------------------------------------------------------------------------------ |
| `RADAR_FEED_URL` | `https://radar.omniroute.online` | `src/lib/radar/sync.ts` | Base URL of the Radar feed service. Override to point at a self-hosted or forked feed. |
| `RADAR_FEED_PUBKEY` | _(pinned default key)_ | `src/lib/radar/pinnedKeys.ts` | Ed25519 public key (base64-DER SPKI or PEM) used to verify feed signatures from a custom feed. |
| `RADAR_CONTRIBUTOR_CLAIM_URL` | `https://radar.omniroute.online/auth/github` | `src/lib/radar/links.ts` | URL the "I'm a contributor" dashboard button opens (GitHub OAuth supporter-key claim flow). |
| `RADAR_SUPPORTER_PLANS_URL` | `https://radar.omniroute.online/planos` | `src/lib/radar/links.ts` | URL the "Support the project" dashboard button opens (payment/plans page). |
| Variable | Default | Source File | Description |
| -------------------- | ------------------------------- | ------------------------------ | ------------------------------------------------------------------------------------------------ |
| `RADAR_FEED_URL` | `https://radar.omniroute.dev` | `src/lib/radar/sync.ts` | Base URL of the Radar feed service. Override to point at a self-hosted or forked feed. |
| `RADAR_FEED_PUBKEY` | _(pinned default key)_ | `src/lib/radar/pinnedKeys.ts` | Ed25519 public key (base64-DER SPKI or PEM) used to verify feed signatures from a custom feed. |
---

View File

@@ -1,12 +1,12 @@
{
"name": "omniroute-desktop",
"version": "3.8.50",
"version": "3.8.49",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "omniroute-desktop",
"version": "3.8.50",
"version": "3.8.49",
"license": "MIT",
"dependencies": {
"electron-updater": "^6.8.9"
@@ -297,6 +297,45 @@
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/@electron/windows-sign": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/@electron/windows-sign/-/windows-sign-1.2.2.tgz",
"integrity": "sha512-dfZeox66AvdPtb2lD8OsIIQh12Tp0GNCRUDfBHIKGpbmopZto2/A8nSpYYLoedPIHpqkeblZ/k8OV0Gy7PYuyQ==",
"dev": true,
"license": "BSD-2-Clause",
"optional": true,
"peer": true,
"dependencies": {
"cross-dirname": "^0.1.0",
"debug": "^4.3.4",
"fs-extra": "^11.1.1",
"minimist": "^1.2.8",
"postject": "^1.0.0-alpha.6"
},
"bin": {
"electron-windows-sign": "bin/electron-windows-sign.js"
},
"engines": {
"node": ">=14.14"
}
},
"node_modules/@electron/windows-sign/node_modules/fs-extra": {
"version": "11.3.6",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.6.tgz",
"integrity": "sha512-w8ZNZr2mKIc7qeNaQ9AVPT1+iFaI+Avd4xudVOvdDJ8VytREi1Ft5Ih7hd9jjehod8vAM5GMsfQ/TpPf4EyoEA==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"graceful-fs": "^4.2.0",
"jsonfile": "^6.0.1",
"universalify": "^2.0.0"
},
"engines": {
"node": ">=14.14"
}
},
"node_modules/@isaacs/fs-minipass": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz",
@@ -1091,6 +1130,15 @@
"dev": true,
"license": "MIT"
},
"node_modules/cross-dirname": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/cross-dirname/-/cross-dirname-0.1.0.tgz",
"integrity": "sha512-+R08/oI0nl3vfPcqftZRpytksBXDzOUveBq/NBVx0sUp1axwzPQrKinNx5yd5sxPu8j1wIy8AfnVQ+5eFdha6Q==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true
},
"node_modules/cross-spawn": {
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
@@ -1411,6 +1459,19 @@
"node": ">=14.0.0"
}
},
"node_modules/electron-builder-squirrel-windows": {
"version": "26.15.3",
"resolved": "https://registry.npmjs.org/electron-builder-squirrel-windows/-/electron-builder-squirrel-windows-26.15.3.tgz",
"integrity": "sha512-Jc19XPV9y9+2bAdZPkXuVNGNIEFBq9poHC61l8Kv6FdK7DRG3+Ic0rerC0DXOaeHNz8yW0fg/JnF8GQROOF5MA==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"app-builder-lib": "26.15.3",
"builder-util": "26.15.3",
"electron-winstaller": "5.4.0"
}
},
"node_modules/electron-publish": {
"version": "26.15.3",
"resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-26.15.3.tgz",
@@ -1445,6 +1506,66 @@
"tiny-typed-emitter": "^2.1.0"
}
},
"node_modules/electron-winstaller": {
"version": "5.4.0",
"resolved": "https://registry.npmjs.org/electron-winstaller/-/electron-winstaller-5.4.0.tgz",
"integrity": "sha512-bO3y10YikuUwUuDUQRM4KfwNkKhnpVO7IPdbsrejwN9/AABJzzTQ4GeHwyzNSrVO+tEH3/Np255a3sVZpZDjvg==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@electron/asar": "^3.2.1",
"debug": "^4.1.1",
"fs-extra": "^7.0.1",
"lodash": "^4.17.21",
"temp": "^0.9.0"
},
"engines": {
"node": ">=8.0.0"
},
"optionalDependencies": {
"@electron/windows-sign": "^1.1.2"
}
},
"node_modules/electron-winstaller/node_modules/fs-extra": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz",
"integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"graceful-fs": "^4.1.2",
"jsonfile": "^4.0.0",
"universalify": "^0.1.0"
},
"engines": {
"node": ">=6 <7 || >=8"
}
},
"node_modules/electron-winstaller/node_modules/jsonfile": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
"integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==",
"dev": true,
"license": "MIT",
"peer": true,
"optionalDependencies": {
"graceful-fs": "^4.1.6"
}
},
"node_modules/electron-winstaller/node_modules/universalify": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
"dev": true,
"license": "MIT",
"peer": true,
"engines": {
"node": ">= 4.0.0"
}
},
"node_modules/emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
@@ -1575,9 +1696,9 @@
"license": "MIT"
},
"node_modules/fast-uri": {
"version": "3.1.5",
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.5.tgz",
"integrity": "sha512-gHwA1O9LDIcKunMKhObS/HimwtehO1nPUECKAu5TpKgaO19fcWEl4bliWe1jWxVFvIXztJjjQ4L8XQ1EU9f7Jw==",
"version": "3.1.4",
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.4.tgz",
"integrity": "sha512-8JnbkQ4juDyvYs4mgFGQqg4yCYtFDtUtmp2QIQq11ZZe5CFQ5wcqm1rqDgAh/QdMySuBnPzMUiJUNZG5N/AiQw==",
"dev": true,
"funding": [
{
@@ -2359,6 +2480,20 @@
"node": ">= 18"
}
},
"node_modules/mkdirp": {
"version": "0.5.6",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"minimist": "^1.2.6"
},
"bin": {
"mkdirp": "bin/cmd.js"
}
},
"node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
@@ -2622,6 +2757,36 @@
"node": ">=18"
}
},
"node_modules/postject": {
"version": "1.0.0-alpha.6",
"resolved": "https://registry.npmjs.org/postject/-/postject-1.0.0-alpha.6.tgz",
"integrity": "sha512-b9Eb8h2eVqNE8edvKdwqkrY6O7kAwmI8kcnBv1NScolYJbo59XUF0noFq+lxbC1yN20bmC0WBEbDC5H/7ASb0A==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"commander": "^9.4.0"
},
"bin": {
"postject": "dist/cli.js"
},
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/postject/node_modules/commander": {
"version": "9.5.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz",
"integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"engines": {
"node": "^12.20.0 || >=14"
}
},
"node_modules/proc-log": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz",
@@ -2816,6 +2981,21 @@
"node": ">= 4"
}
},
"node_modules/rimraf": {
"version": "2.6.3",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz",
"integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==",
"deprecated": "Rimraf versions prior to v4 are no longer supported",
"dev": true,
"license": "ISC",
"peer": true,
"dependencies": {
"glob": "^7.1.3"
},
"bin": {
"rimraf": "bin.js"
}
},
"node_modules/roarr": {
"version": "2.15.4",
"resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz",
@@ -3071,6 +3251,21 @@
"node": ">=18"
}
},
"node_modules/temp": {
"version": "0.9.4",
"resolved": "https://registry.npmjs.org/temp/-/temp-0.9.4.tgz",
"integrity": "sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"mkdirp": "^0.5.1",
"rimraf": "~2.6.2"
},
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/temp-file": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/temp-file/-/temp-file-3.4.0.tgz",
@@ -3177,9 +3372,9 @@
}
},
"node_modules/undici": {
"version": "7.29.0",
"resolved": "https://registry.npmjs.org/undici/-/undici-7.29.0.tgz",
"integrity": "sha512-IDxfleLmmbSskfWSUATiN1nfn2rDuvnMOqb5CWR92iIfojA0Ud+ulOAAEQ57LPr9rWmsreUyf5lwyao+7GNNVw==",
"version": "7.28.0",
"resolved": "https://registry.npmjs.org/undici/-/undici-7.28.0.tgz",
"integrity": "sha512-cRZYrTDwWznlnRiPjggAGxZXanty6M8RV1ff8Wm4LWXBp7/IG8v5DnOm74DtUBp9OONpK75YlPnIjQqX0dBDtA==",
"dev": true,
"license": "MIT",
"engines": {

View File

@@ -37,7 +37,7 @@
"plist": "^4.0.0",
"form-data": "^4.0.6",
"js-yaml": "^4.2.0",
"undici": "^7.29.0"
"undici": "^7.28.0"
},
"build": {
"appId": "online.omniroute.desktop",

View File

@@ -145,19 +145,6 @@ export const AUDIO_TRANSCRIPTION_PROVIDERS: Record<string, AudioProvider> = {
],
},
soniox: {
id: "soniox",
baseUrl: "https://api.soniox.com/v1/transcriptions",
authType: "apikey",
authHeader: "bearer",
async: true,
format: "soniox",
models: [
{ id: "stt-async-v5", name: "Soniox STT Async v5" },
{ id: "stt-async-v4", name: "Soniox STT Async v4" },
],
},
nvidia: {
id: "nvidia",
baseUrl: "https://integrate.api.nvidia.com/v1/audio/transcriptions",
@@ -333,15 +320,6 @@ export const AUDIO_SPEECH_PROVIDERS: Record<string, AudioProvider> = {
],
},
soniox: {
id: "soniox",
baseUrl: "https://tts-rt.soniox.com/tts",
authType: "apikey",
authHeader: "bearer",
format: "soniox-tts",
models: [{ id: "tts-rt-v1", name: "Soniox TTS RT v1" }],
},
elevenlabs: {
id: "elevenlabs",
baseUrl: "https://api.elevenlabs.io/v1/text-to-speech",

View File

@@ -85,13 +85,14 @@ export const opencode_zenProvider: RegistryEntry = {
{ id: "qwen3.6-plus", name: "Qwen3.6 Plus", targetFormat: "claude", supportsVision: false },
// ── Free Tier ──────────────────────────────────────────────
// #6998 (2026-07-14): upstream free tier rotated — minimax-m2.5-free,
// nemotron-3-super-free and qwen3.6-plus-free were delisted (401). Replaced
// by the 4 entries below with upstream-verified limits.
{ id: "deepseek-v4-flash-free", name: "DeepSeek V4 Flash Free", supportsReasoning: true },
{ id: "mimo-v2.5-free", name: "MiMo V2.5 Free", contextLength: 200000 },
{ id: "hy3-free", name: "HY3 Free", contextLength: 200000 },
{ id: "nemotron-3-ultra-free", name: "Nemotron 3 Ultra Free", contextLength: 1000000 },
{ id: "north-mini-code-free", name: "North Mini Code Free", contextLength: 200000 },
{ id: "minimax-m2.5-free", name: "MiniMax M2.5 Free", contextLength: 204800 },
{ id: "nemotron-3-super-free", name: "Nemotron 3 Super Free", contextLength: 1000000 },
{
id: "qwen3.6-plus-free",
name: "Qwen3.6 Plus Free",
targetFormat: "claude",
contextLength: 200000,
},
],
};

View File

@@ -13,12 +13,5 @@ export const openrouterProvider: RegistryEntry = {
"HTTP-Referer": "https://endpoint-proxy.local",
"X-Title": "Endpoint Proxy",
},
// OpenRouter multiplexes hundreds of independent upstream models behind one
// connection/API key — without this flag, hasPerModelQuota() (accountFallback.ts)
// falls through to connection-wide cooldown on any model-specific failure (e.g. a
// 404 "No endpoints found" for one dead/renamed model), poisoning every OTHER
// OpenRouter model on the same connection for the cooldown window and surfacing
// that first model's stale error message on their unrelated requests.
passthroughModels: true,
models: [{ id: "auto", name: "Auto (Best Available)" }],
};

View File

@@ -271,14 +271,16 @@ export const GPT_5_6_API_CAPABILITIES = {
maxOutputTokens: 128000,
} as const;
// Codex's live catalog reports a 272K input context window for GPT-5.6.
// Keep the input and output limits explicit for catalog consumers that expose them separately.
export const GPT_5_6_CODEX_CAPABILITIES = {
targetFormat: "openai-responses",
toolCalling: true,
supportsReasoning: true,
supportsVision: true,
supportsXHighEffort: true,
contextLength: 1050000,
maxInputTokens: 922000,
contextLength: 272000,
maxInputTokens: 272000,
maxOutputTokens: 128000,
} as const;

View File

@@ -23,11 +23,6 @@ import {
import { persistCreditBalance, getAllPersistedCreditBalances } from "@/lib/db/creditBalance";
import { setConnectionRateLimitUntil } from "@/lib/db/providers";
import { getMitmAlias } from "@/lib/db/models";
import {
MAX_ANTIGRAVITY_OUTPUT_TOKENS,
resolveAntigravityOutputCap,
} from "./antigravityOutputCap.ts";
export { MAX_ANTIGRAVITY_OUTPUT_TOKENS } from "./antigravityOutputCap.ts";
import { ensureAntigravityProjectAssigned } from "../services/antigravityProjectBootstrap.ts";
import { persistDiscoveredAntigravityProjectId } from "../services/antigravityProjectPersist.ts";
import {
@@ -284,10 +279,18 @@ async function cleanModelName(model: string, modelIdOverride?: string): Promise<
return clean;
}
function applyAntigravityGenerationDefaults(
request: Record<string, unknown>,
modelId?: string | null
): void {
/**
* Hard ceiling on `generationConfig.maxOutputTokens` for Antigravity Cloud Code.
*
* Ports decolua/9router#779 (lukmanfauzie): VS Code GitHub Copilot Chat in
* Agent mode regularly requests 32K65K output tokens, which the Antigravity
* backend rejects with HTTP 400 "Invalid Argument". 16384 matches the
* upstream-accepted ceiling confirmed via successful 200 OK runs with
* claude-sonnet-4-6 and gemini-pro-agent across both Ask and Agent modes.
*/
export const MAX_ANTIGRAVITY_OUTPUT_TOKENS = 16384;
function applyAntigravityGenerationDefaults(request: Record<string, unknown>): void {
const generationConfig =
request.generationConfig && typeof request.generationConfig === "object"
? (request.generationConfig as Record<string, unknown>)
@@ -319,10 +322,9 @@ function applyAntigravityGenerationDefaults(
// (32K65K) that trigger upstream 400 "Invalid Argument". Clamp silently
// — the cap is provider-driven, not client-driven, and only matters when
// the request would otherwise be rejected outright.
const cap = resolveAntigravityOutputCap(modelId);
const finalMax = Number(generationConfig.maxOutputTokens);
if (Number.isFinite(finalMax) && finalMax > cap) {
generationConfig.maxOutputTokens = cap;
if (Number.isFinite(finalMax) && finalMax > MAX_ANTIGRAVITY_OUTPUT_TOKENS) {
generationConfig.maxOutputTokens = MAX_ANTIGRAVITY_OUTPUT_TOKENS;
}
request.generationConfig = generationConfig;
@@ -664,7 +666,7 @@ export class AntigravityExecutor extends BaseExecutor {
)
: rawTransformedRequest;
applyAntigravityGenerationDefaults(transformedRequest, upstreamModel);
applyAntigravityGenerationDefaults(transformedRequest);
const {
project: _project,

View File

@@ -1,52 +0,0 @@
import { getExplicitModelOutputCap } from "@/lib/modelCapabilities";
/**
* Fallback ceiling on `generationConfig.maxOutputTokens` for Antigravity
* Cloud Code, used when the model is unknown to the catalogue.
*
* Ports decolua/9router#779 (lukmanfauzie): VS Code GitHub Copilot Chat in
* Agent mode regularly requests 32K65K output tokens, which the Antigravity
* backend rejects with HTTP 400 "Invalid Argument". 16384 was the ceiling
* confirmed safe at the time, via successful 200 OK runs with
* claude-sonnet-4-6 and gemini-pro-agent across both Ask and Agent modes.
*
* Both of those models are catalogue-known today, so neither one reaches this
* constant anymore: they get their own declared limit via
* `resolveAntigravityOutputCap` (65536 and 65535, respectively). The higher
* limit holds against the live upstream. A gemini-3.6-flash-high request came
* back with completion_tokens 16754 and finish_reason "stop", which exceeds
* 16384 on its own and so cannot be an artifact of thinking-token accounting.
*
* Note also that #779 was reported against Copilot Chat in Agent mode, a path
* that does not reach this executor, so 16384 arrived with that port rather
* than from a limit measured here. Beware of re-deriving it from a running
* instance: the clamp below rewrites maxOutputTokens before the request
* leaves, so a build still carrying a low constant measures its own clamp and
* reports it as an upstream ceiling.
*/
export const MAX_ANTIGRAVITY_OUTPUT_TOKENS = 16384;
/**
* The output ceiling this specific model accepts, or the conservative
* fallback above when the id is not in the catalogue.
*
* The declared limits are not uniform: most Antigravity models publish
* 65535 or 65536, but gpt-oss-120b-medium publishes 32768. A single global
* ceiling either starves the first group or lets an oversized request
* through to the second, so the number has to come from the model.
*/
export function resolveAntigravityOutputCap(modelId: string | null | undefined): number {
const id = typeof modelId === "string" ? modelId.trim() : "";
if (!id) return MAX_ANTIGRAVITY_OUTPUT_TOKENS;
try {
const declared = getExplicitModelOutputCap({ provider: "antigravity", model: id });
return typeof declared === "number" && Number.isFinite(declared) && declared > 0
? declared
: MAX_ANTIGRAVITY_OUTPUT_TOKENS;
} catch {
// DB not available (build phase, transient error) -- fall through to the
// conservative fallback, the same guard cleanModelName uses above for
// its own MITM alias lookup.
return MAX_ANTIGRAVITY_OUTPUT_TOKENS;
}
}

View File

@@ -28,11 +28,7 @@ export class AzureOpenAIExecutor extends DefaultExecutor {
void urlIndex;
const providerSpecificData = credentials?.providerSpecificData || {};
const baseUrl = normalizeAzureBaseUrl(
typeof providerSpecificData.baseUrl === "string"
? providerSpecificData.baseUrl
: this.config.baseUrl
);
const baseUrl = normalizeAzureBaseUrl(providerSpecificData.baseUrl || this.config.baseUrl);
const apiVersion =
typeof providerSpecificData.apiVersion === "string" && providerSpecificData.apiVersion.trim()
? providerSpecificData.apiVersion.trim()

View File

@@ -234,11 +234,71 @@ export function mergeAbortSignals(primary: AbortSignal, secondary: AbortSignal):
return controller.signal;
}
import {
hasActiveClaudeThinking,
readNestedThinkingBudget,
clampNestedThinkingBudget,
} from "../utils/thinkingBudget.ts";
function hasActiveClaudeThinking(body: Record<string, unknown>): boolean {
const thinking = body.thinking as Record<string, unknown> | undefined;
return thinking?.type === "enabled" || thinking?.type === "adaptive";
}
/**
* Collect every `thinkingConfig` object in a transformed request body that holds
* a thinking budget, wherever the provider's envelope nests it:
* - body.generationConfig.thinkingConfig (native Gemini / openai→gemini)
* - body.request.generationConfig.thinkingConfig (Antigravity Cloud Code envelope)
* Returns only objects that actually carry a `thinkingBudget`/`thinking_budget`
* field — a request without thinking config is never mutated.
*/
function collectThinkingConfigs(body: unknown): Array<Record<string, unknown>> {
if (!body || typeof body !== "object") return [];
const root = body as Record<string, unknown>;
const configs: Array<Record<string, unknown>> = [];
const envelopes: unknown[] = [
root.generationConfig,
(root.request as Record<string, unknown> | undefined)?.generationConfig,
];
for (const env of envelopes) {
if (!env || typeof env !== "object") continue;
const tc = (env as Record<string, unknown>).thinkingConfig;
if (tc && typeof tc === "object") {
const tcr = tc as Record<string, unknown>;
if ("thinkingBudget" in tcr || "thinking_budget" in tcr) configs.push(tcr);
}
}
return configs;
}
/**
* Read the first thinking budget found in the body (any supported nest / naming).
* Returns null when the body carries no readable numeric budget.
*/
function readNestedThinkingBudget(body: unknown): number | null {
for (const tc of collectThinkingConfigs(body)) {
const raw = tc.thinkingBudget ?? tc.thinking_budget;
const n = Number(raw);
if (Number.isFinite(n)) return n;
}
return null;
}
/**
* Clamp every thinking budget in the body down to `max` (only lowers; never
* raises a budget already below max). Mutates in place. Returns true when at
* least one budget was actually lowered (i.e. a retry would send a different
* body) — false means the 400 was not caused by an over-max budget we hold, so
* retrying would resend an identical body and loop.
*/
function clampNestedThinkingBudget(body: unknown, max: number): boolean {
let changed = false;
for (const tc of collectThinkingConfigs(body)) {
for (const key of ["thinkingBudget", "thinking_budget"] as const) {
const n = Number(tc[key]);
if (Number.isFinite(n) && n > max) {
tc[key] = max;
changed = true;
}
}
}
return changed;
}
/**
* Strip the OmniRoute provider prefix from tool model fields (e.g.

View File

@@ -151,8 +151,7 @@ export function supportsMaxEffortForProvider(provider: string, model: string): b
// Ollama Cloud also accepts literal max (for example GLM 5.2 supports
// low|medium|high|max|none) and rejects xhigh.
const isOpencodeGoDeepSeek =
(provider === "opencode-go" || provider === "opencode-zen") &&
model.toLowerCase().includes("deepseek");
provider === "opencode-go" && model.toLowerCase().includes("deepseek");
const isOllamaCloud = provider === "ollama-cloud";
const isMoonshotK3 =
(provider === "moonshot" || provider === "kimi") && /^kimi-k3(?:$|-)/i.test(model);

View File

@@ -2816,14 +2816,11 @@ export class ChatGptWebExecutor extends BaseExecutor {
};
}
// Tool-call emulation (#5240, #7679): inject a `<tool>` contract when tools
// are present; parsed back on the response side. Hardened for thinking models.
const resolvedModel = resolveChatGptModel(model, body, credentials.providerSpecificData);
const modelSlug = resolvedModel.slug;
// Tool-call emulation (#5240): inject a `<tool>` contract when `tools` are
// present; parsed back on the response side. Mirrors qwen-web/perplexity-web.
const { hasTools, requestedTools, effectiveMessages } = prepareToolMessages(
(body || {}) as Record<string, unknown>,
messages as Array<{ role: string; content: unknown }>,
{ hardened: isThinkingCapableModel(model, modelSlug) }
messages as Array<{ role: string; content: unknown }>
);
if (!credentials.apiKey) {
@@ -2921,9 +2918,12 @@ export class ChatGptWebExecutor extends BaseExecutor {
log
);
// 2a''. Apply thinking-effort preference for thinking models.
// Dedicated thinking models mirror the browser's user-config PATCH;
// GPT-5.5 Pro effort is sent with the conversation body.
// 2a''. Resolve model + effort and apply thinking-effort preference for
// thinking-capable models. Dedicated thinking models mirror the browser's
// user-config PATCH; GPT-5.5 Pro sends the effort with the conversation
// body because the Pro standard/extended budget is part of that turn.
const resolvedModel = resolveChatGptModel(model, body, credentials.providerSpecificData);
const modelSlug = resolvedModel.slug;
const requestedEffort = resolvedModel.effort;
if (requestedEffort && isThinkingCapableModel(model, modelSlug)) {
await setUserThinkingEffort(

View File

@@ -461,31 +461,14 @@ function applyEventToAggregateOrThrow(event: JsonRecord, state: AggregateState):
function usageFromCommandCode(usage: JsonRecord | null) {
if (!usage) return undefined;
const details = isRecord(usage.inputTokenDetails) ? usage.inputTokenDetails : {};
const cacheRead = numberValue(details.cacheReadTokens) || 0;
const noCache = numberValue(details.noCacheTokens) || 0;
// Command Code's totalUsage.inputTokens is the FULL prompt total and already
// includes the cached portion (noCacheTokens + cacheReadTokens = inputTokens),
// so we must NOT add cacheRead back — that would double-count. There is no
// cache-write field in the upstream payload, so cache creation stays unset.
const inputTokens = numberValue(usage.inputTokens) || 0;
const prompt = inputTokens;
const prompt =
(numberValue(usage.inputTokens) || 0) + (numberValue(details.cacheReadTokens) || 0);
const completion = numberValue(usage.outputTokens) || 0;
const result: JsonRecord = {
return {
prompt_tokens: prompt,
completion_tokens: completion,
total_tokens: prompt + completion,
};
// Surface the cache breakdown as informational fields so logUsage prints
// `| cache_read=X | no_cache=Y` and appendRequestLog persists them. These are
// NOT added to prompt_tokens (already included) — metering stays accurate.
if (cacheRead > 0) result.cache_read_input_tokens = cacheRead;
if (noCache > 0) result.no_cache_tokens = noCache;
// Keep reasoning_token_details (reasoningTokens) when present so stream.ts's
// extractUsage can surface it as reasoning_tokens.
const reasoningDetails = isRecord(usage.reasoningTokenDetails) ? usage.reasoningTokenDetails : {};
const reasoning = numberValue(reasoningDetails.reasoningTokens);
if (reasoning !== undefined && reasoning > 0) result.reasoning_tokens = reasoning;
return result;
}
function createStreamResponse(
@@ -566,22 +549,6 @@ function createStreamResponse(
state.finishReason = mapFinishReason(event.finishReason);
state.usage = isRecord(event.totalUsage) ? event.totalUsage : null;
controller.enqueue(sse(chatCompletionChunk(id, model, {}, state.finishReason)));
// Emit a standards-compliant usage-only chunk (choices: []) before
// [DONE] when upstream reported usage. stream.ts's extractUsage
// recognizes this shape (see stream.ts:1661) and logs the ACTUAL
// token counts (in/out/cache_read/no_cache) instead of estimates.
const usagePayload = usageFromCommandCode(state.usage);
if (usagePayload) {
controller.enqueue(
sse({
id,
object: "chat.completion.chunk",
model,
usage: usagePayload,
choices: [],
})
);
}
controller.enqueue(encoder.encode("data: [DONE]\n\n"));
closed = true;
controller.close();

View File

@@ -1219,9 +1219,6 @@ export class CursorExecutor extends BaseExecutor {
if (isToolFollowUp) {
session = cursorSessionManager.acquire(conversationId);
// #9029: content-based session match when client lacks conversation_id.
if (!session && !body.conversation_id) session = cursorSessionManager.findByToolCallIds(
messages.filter(m => m.role === "tool" && m.tool_call_id).map(m => m.tool_call_id!));
}
if (session) {

View File

@@ -165,7 +165,7 @@ function baseChunk(model: string) {
}
function enqueueSse(controller: ReadableStreamDefaultController, chunk: Record<string, unknown>) {
controller.enqueue(new TextEncoder().encode(`data: ${JSON.stringify(chunk)}\n\n`));
controller.enqueue(`data: ${JSON.stringify(chunk)}\n\n`);
}
function emitStopAndDone(controller: ReadableStreamDefaultController, model: string) {
@@ -173,7 +173,7 @@ function emitStopAndDone(controller: ReadableStreamDefaultController, model: str
...baseChunk(model),
choices: [{ index: 0, delta: {}, finish_reason: "stop" }],
});
controller.enqueue(new TextEncoder().encode("data: [DONE]\n\n"));
controller.enqueue("data: [DONE]\n\n");
controller.close();
}
@@ -213,7 +213,7 @@ export function createOpenAIArenaStream(opts: {
model: string;
signal?: AbortSignal;
log?: { error?: (scope: string, msg: string) => void };
}): ReadableStream<Uint8Array> {
}): ReadableStream<Uint8Array | string> {
const { reader, model, signal, log } = opts;
const decoder = new TextDecoder();
let buffer = "";

View File

@@ -40,31 +40,6 @@ const OPENCODE_COOLDOWN_MAX_MS = 60_000;
const EFFORT_LEVELS = ["low", "medium", "high", "max"] as const;
/**
* Models that work WITHOUT any API key on the free/noauth opencode tier.
*
* The upstream free tier rotates frequently — when a `-free` suffix model is
* delisted upstream, the upstream returns "Model X is not supported" (a separate
* issue from this gate). The set is defined by two data sources:
*
* 1. **Known free models** — models explicitly listed in the noauth
* `opencode` provider registry (`open-sse/config/providers/registry/opencode/index.ts`).
* These are the canonical free models. `deepseek-v4-flash-free` appears in both
* the noauth AND the zen registry (it is free on both tiers).
* 2. **`-free` suffix** — any model whose id ends in `-free`. This automatically
* covers upstream free-tier additions without a code deploy.
*
* For `opencode-go`, there is no free tier — ALL models require an API key.
*/
const OPENCODE_FREE_MODELS = new Set([
"big-pickle",
"deepseek-v4-flash-free",
"mimo-v2.5-free",
"hy3-free",
"nemotron-3-ultra-free",
"north-mini-code-free",
]);
/**
* Models on opencode-go that support effort-tier aliases. Each entry maps the
* canonical base id to the set of effort suffixes the upstream supports.
@@ -111,31 +86,7 @@ export function parseEffortLevel(model: string): { baseModel: string; effort: st
return null;
}
/**
* Determine whether a model requires an API key on the given opencode provider.
*
* - `opencode-go`: ALL models require a key (no free tier).
* - `opencode` / `opencode-zen`: premium = any model NOT in the free set (known
* free models OR ending in `-free`).
* - Unknown models are assumed premium (fail-safe).
*/
export function isPremiumOpencodeModel(model: string, provider: string): boolean {
// opencode-go has no free tier — every model requires a key.
if (provider === "opencode-go") return true;
// Models ending in `-free` are always free on the noauth/zen tier.
if (model.endsWith("-free")) return false;
// Check the known free model catalog.
return !OPENCODE_FREE_MODELS.has(model);
}
export class OpencodeExecutor extends BaseExecutor {
/** Delegates to `isPremiumOpencodeModel`. Exported for testability. */
static isPremiumModel(model: string, provider: string): boolean {
return isPremiumOpencodeModel(model, provider);
}
_requestFormat: string | null = null;
/**
@@ -230,34 +181,6 @@ export class OpencodeExecutor extends BaseExecutor {
async execute(input: ExecuteInput) {
this._requestFormat = getModelTargetFormat(this.provider, input.model) || "openai";
// #8681: Gate premium opencode models behind a usable API key.
// When the connection is keyless (no apiKey, no accessToken) and the model
// is a premium model (not on the free tier), return a clear 402 error
// instead of proxying the raw upstream 401 "Missing API key" response.
const creds = input.credentials;
const isKeyless =
!creds?.apiKey && !creds?.accessToken && !creds?.providerSpecificData?.extraApiKeys;
if (isKeyless && isPremiumOpencodeModel(input.model, this.provider)) {
const bodyJson = JSON.stringify({
error: {
message:
"This model requires an opencode API key — add one in Settings → Providers.",
type: "invalid_request_error",
code: "premium_model_requires_key",
},
});
return {
response: new Response(bodyJson, {
status: 402,
headers: { "Content-Type": "application/json" },
}),
url: "",
headers: {} as Record<string, string>,
transformedBody: null,
};
}
try {
this.syncAccountsFromCredentials(input.credentials);

View File

@@ -372,16 +372,8 @@ export class QoderExecutor extends BaseExecutor {
const { text, isError, errorMessage } = parseQoderCliResult(run.stdout);
if (isError) {
// When qodercli exits 0 but returns is_error=true with an empty result,
// the real upstream error is almost always on stderr. Surface it instead
// of the generic "qodercli returned an error" fallback (#9319).
let effectiveError = errorMessage;
if (errorMessage === "qodercli returned an error" && run.stderr.trim()) {
const stderrTrimmed = run.stderr.trim().slice(0, 300);
effectiveError = `qodercli returned an error: ${stderrTrimmed}`;
}
return {
response: createQoderErrorResponse(parseQoderCliFailure(effectiveError)),
response: createQoderErrorResponse(parseQoderCliFailure(errorMessage)),
url,
headers: {},
transformedBody: body,

View File

@@ -47,8 +47,8 @@ const BX_UMIDTOKEN_FALLBACK = "T2gA0000000000000000000000000000000000000000";
// header the upstream returns HTTP 200 with `{"success":false,"data":{"code":"Bad_Request"}}`
// for every completion request, even with a valid session. The version string is
// the SPA build identifier shipped in the React client's `version` request header.
// Pinned from a live capture (2026-08); bump if Qwen ships a breaking change.
const QWEN_SPA_VERSION = "0.2.81";
// Pinned from a live capture (2026-07); bump if Qwen ships a breaking change.
const QWEN_SPA_VERSION = "0.2.66";
const MODEL_ALIASES: Record<string, string> = {
// Legacy OmniRoute ids → current upstream catalog (GET /api/models).

View File

@@ -138,149 +138,13 @@ function isPartnerModel(model: string) {
return [...PARTNER_MODELS].some((prefix) => normalizedModel.startsWith(prefix));
}
// Anthropic models need their own branch: they use Vertex's native Anthropic Messages API
// (publishers/anthropic/.../rawPredict), not the generic OpenAI-compatible partner endpoint the
// other PARTNER_MODELS entries (DeepSeek, Qwen, Llama, Mistral, GLM) go through — the OpenAI-shaped
// endpoint 404s/"malformed argument"s for Claude models on at least some projects.
function isClaudeModel(model: string) {
return model.toLowerCase().startsWith("claude-");
}
// Defensive normalizer: target-format resolution for manually-added custom Claude models under
// "vertex"/"vertex-partner" was observed sending a Gemini-shaped body (contents/parts) to the
// Anthropic rawPredict endpoint instead of the configured "claude" format, causing a hard
// "messages: Field required" error upstream regardless of the stored per-model targetFormat. This
// converts a Gemini-shaped body to Anthropic Messages shape so the executor works either way,
// independent of that unresolved upstream resolution gap.
function toAnthropicBody(body: Record<string, unknown>): Record<string, unknown> {
const contents = body.contents as Array<{ role?: string; parts?: Array<{ text?: string }> }> | undefined;
if (!Array.isArray(contents)) return body;
const messages = contents.map((c) => ({
role: c.role === "model" ? "assistant" : "user",
content: (c.parts || []).map((p) => p.text || "").join(""),
}));
const generationConfig = body.generationConfig as { maxOutputTokens?: number } | undefined;
const systemInstruction = body.systemInstruction as { parts?: Array<{ text?: string }> } | undefined;
const converted: Record<string, unknown> = {
messages,
max_tokens: generationConfig?.maxOutputTokens || 4096,
};
if (systemInstruction?.parts?.length) {
converted.system = systemInstruction.parts.map((p) => p.text || "").join("");
}
return converted;
}
// rawPredict always returns a single complete JSON body, never real SSE framing (see buildUrl).
// When the caller actually requested a stream, synthesize a genuine Anthropic-native event
// sequence from that JSON so the existing claude-to-openai.ts (and sibling) response translators
// — which already parse real message_start/content_block_*/message_delta/message_stop events —
// can consume it correctly, instead of relying on the OpenAI-`choices`-only JSON→SSE fallback
// (open-sse/utils/jsonToSse.ts) which cannot represent Anthropic's native response shape at all.
function synthesizeClaudeSse(response: Record<string, unknown>): string {
const messageId = typeof response.id === "string" ? response.id : `msg_${Date.now()}`;
const model = typeof response.model === "string" ? response.model : "";
const usage = (response.usage as Record<string, unknown>) || {};
const stopReason = typeof response.stop_reason === "string" ? response.stop_reason : "end_turn";
const stopSequence = (response.stop_sequence as string | null | undefined) ?? null;
const content = Array.isArray(response.content) ? response.content : [];
const events: Array<{ event: string; data: Record<string, unknown> }> = [];
events.push({
event: "message_start",
data: {
type: "message_start",
message: {
id: messageId,
type: "message",
role: "assistant",
content: [],
model,
stop_reason: null,
stop_sequence: null,
usage: { input_tokens: usage.input_tokens || 0, output_tokens: 0 },
},
},
});
content.forEach((block: Record<string, unknown>, index: number) => {
if (block.type === "text") {
events.push({
event: "content_block_start",
data: { type: "content_block_start", index, content_block: { type: "text", text: "" } },
});
if (block.text) {
events.push({
event: "content_block_delta",
data: {
type: "content_block_delta",
index,
delta: { type: "text_delta", text: block.text },
},
});
}
events.push({ event: "content_block_stop", data: { type: "content_block_stop", index } });
} else if (block.type === "tool_use") {
events.push({
event: "content_block_start",
data: {
type: "content_block_start",
index,
content_block: { type: "tool_use", id: block.id, name: block.name, input: {} },
},
});
events.push({
event: "content_block_delta",
data: {
type: "content_block_delta",
index,
delta: { type: "input_json_delta", partial_json: JSON.stringify(block.input ?? {}) },
},
});
events.push({ event: "content_block_stop", data: { type: "content_block_stop", index } });
} else if (block.type === "thinking") {
events.push({
event: "content_block_start",
data: { type: "content_block_start", index, content_block: { type: "thinking", thinking: "" } },
});
if (block.thinking) {
events.push({
event: "content_block_delta",
data: {
type: "content_block_delta",
index,
delta: { type: "thinking_delta", thinking: block.thinking },
},
});
}
events.push({ event: "content_block_stop", data: { type: "content_block_stop", index } });
}
});
events.push({
event: "message_delta",
data: {
type: "message_delta",
delta: { stop_reason: stopReason, stop_sequence: stopSequence },
usage: { output_tokens: usage.output_tokens || 0 },
},
});
events.push({ event: "message_stop", data: { type: "message_stop" } });
return events.map((e) => `event: ${e.event}\ndata: ${JSON.stringify(e.data)}\n\n`).join("");
}
export class VertexExecutor extends BaseExecutor {
constructor() {
super("vertex", PROVIDERS.vertex);
}
async execute(input: ExecuteInput) {
const { credentials, log, model, stream } = input;
const { credentials, log } = input;
// Defensive: trim stray surrounding whitespace from a pasted credential.
if (typeof credentials.apiKey === "string") {
credentials.apiKey = credentials.apiKey.trim();
@@ -296,53 +160,7 @@ export class VertexExecutor extends BaseExecutor {
throw err;
}
}
if (isClaudeModel(model) && input.body && typeof input.body === "object") {
let body = input.body as Record<string, unknown>;
if (!Array.isArray(body.messages)) {
body = toAnthropicBody(body);
input.body = body;
}
// The rawPredict endpoint requires "anthropic_version" in the body (Vertex's substitute
// for the "anthropic-version" header used by Anthropic's direct API).
body.anthropic_version ??= "vertex-2023-10-16";
// Unlike Anthropic's direct API (which reads the model from the body), Vertex's
// rawPredict endpoint already encodes project/region/model in the URL and 400s with
// "model: Extra inputs are not permitted" if the translated request body still carries
// one (the openai→claude request translator copies the client's model field over).
delete body.model;
}
const result = await super.execute(input);
if (isClaudeModel(model) && stream) {
const response = result instanceof Response ? result : result?.response;
if (response?.ok) {
const contentType = response.headers.get("content-type") || "";
if (contentType.includes("application/json") && !contentType.includes("text/event-stream")) {
const jsonText = await response.text();
let newBody = jsonText;
let newContentType = contentType;
try {
newBody = synthesizeClaudeSse(JSON.parse(jsonText));
newContentType = "text/event-stream";
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
log?.warn?.("VERTEX", `Failed to synthesize Claude SSE stream: ${message}`);
}
const newHeaders = new Headers(response.headers);
newHeaders.set("content-type", newContentType);
newHeaders.delete("content-length");
const newResponse = new Response(newBody, {
status: response.status,
statusText: response.statusText,
headers: newHeaders,
});
return result instanceof Response ? newResponse : { ...result, response: newResponse };
}
}
}
return result;
return super.execute(input);
}
buildUrl(model: string, stream: boolean, urlIndex = 0, credentials: any = null) {
@@ -371,13 +189,6 @@ export class VertexExecutor extends BaseExecutor {
}
}
if (isClaudeModel(model)) {
// streamRawPredict?alt=sse was verified to return a single plain JSON body (not real SSE
// framing) rather than actual chunked events, which breaks the SSE parser upstream
// ("stream ended before producing a non-ping SSE event"). rawPredict is confirmed reliable
// for both streaming and non-streaming requests; always use it here.
return `https://aiplatform.googleapis.com/v1/projects/${project}/locations/${region}/publishers/anthropic/models/${model}:rawPredict`;
}
if (isPartnerModel(model)) {
return `https://aiplatform.googleapis.com/v1/projects/${project}/locations/global/endpoints/openapi/chat/completions`;
}

View File

@@ -457,12 +457,9 @@ function transformYuanbaoStream(
if (event.type === "think" && event.content) {
ensureRole();
emit({ reasoning_content: event.content });
} else if (event.type === "text") {
const text = event.msg ?? event.content;
if (text) {
ensureRole();
emit({ content: text });
}
} else if (event.type === "text" && typeof event.msg === "string" && event.msg) {
ensureRole();
emit({ content: event.msg });
}
}
}
@@ -501,10 +498,7 @@ async function collectYuanbaoResponse(
const event = parseYuanbaoDataLine(line);
if (!event) continue;
if (event.type === "think" && event.content) reasoning += event.content;
else if (event.type === "text") {
const text = event.msg ?? event.content;
if (text) content += text;
}
else if (event.type === "text" && typeof event.msg === "string") content += event.msg;
}
}
} finally {

View File

@@ -228,35 +228,6 @@ async function handleDeepgramSpeech(providerConfig, body, modelId, token) {
return audioStreamResponse(res);
}
/**
* Handle Soniox TTS (OpenAI speech shape → Soniox /tts, returns raw audio bytes)
*/
async function handleSonioxSpeech(providerConfig, body, modelId, token) {
const fmt = typeof body.response_format === "string" ? body.response_format : "mp3";
const audioFormat = fmt === "pcm" ? "pcm_s16le" : fmt;
const res = await fetch(providerConfig.baseUrl, {
method: "POST",
headers: {
"Content-Type": "application/json",
...buildAuthHeaders(providerConfig, token),
},
body: JSON.stringify({
text: body.input,
model: modelId,
...(body.voice ? { voice: body.voice } : {}),
audio_format: audioFormat,
}),
});
if (!res.ok) {
return upstreamErrorResponse(res, await res.text());
}
const contentType = fmt === "wav" ? "audio/wav" : fmt === "opus" ? "audio/opus" : "audio/mpeg";
return audioStreamResponse(res, contentType);
}
/**
* Handle ElevenLabs TTS
* POST {baseUrl}/{voice_id} with { text, model_id }
@@ -875,10 +846,6 @@ export async function handleAudioSpeech({
return handleDeepgramSpeech(providerConfig, body, modelId, token);
}
if (providerConfig.format === "soniox-tts") {
return handleSonioxSpeech(providerConfig, body, modelId, token);
}
if (providerConfig.format === "elevenlabs") {
return handleElevenLabsSpeech(providerConfig, body, modelId, token);
}

View File

@@ -336,78 +336,6 @@ async function handleGladiaTranscription(providerConfig, file, modelId, token) {
return errorResponse(504, "Gladia transcription timed out after 120s");
}
/**
* Handle Soniox transcription (async: upload file → create job → poll → get transcript)
*/
async function handleSonioxTranscription(providerConfig, file, modelId, token) {
const authHeaders = buildAuthHeaders(providerConfig, token);
const { body: uploadBody, contentType: uploadContentType } = await buildMultipartBody(file, {});
const uploadRes = await fetch("https://api.soniox.com/v1/files", {
method: "POST",
headers: { ...authHeaders, "Content-Type": uploadContentType },
body: uploadBody,
});
if (!uploadRes.ok) {
return upstreamErrorResponse(uploadRes, await uploadRes.text());
}
const fileId = (await uploadRes.json()).id;
const createRes = await fetch(providerConfig.baseUrl, {
method: "POST",
headers: { ...authHeaders, "Content-Type": "application/json" },
body: JSON.stringify({
model: modelId,
file_id: fileId,
enable_language_identification: true,
}),
});
if (!createRes.ok) {
return upstreamErrorResponse(createRes, await createRes.text());
}
const { id: transcriptionId } = await createRes.json();
const statusUrl = `${providerConfig.baseUrl}/${transcriptionId}`;
const maxWait = 120_000;
const start = Date.now();
let completed = false;
while (Date.now() - start < maxWait) {
await new Promise((r) => setTimeout(r, 2000));
const pollRes = await fetch(statusUrl, { headers: authHeaders });
if (!pollRes.ok) {
continue;
}
const result = await pollRes.json();
if (result.status === "completed") {
completed = true;
break;
}
if (result.status === "error") {
return errorResponse(
500,
result.error_message || result.error || "Soniox transcription failed"
);
}
}
if (!completed) {
return errorResponse(504, "Soniox transcription timed out after 120s");
}
const transcriptRes = await fetch(`${statusUrl}/transcript`, { headers: authHeaders });
if (!transcriptRes.ok) {
return upstreamErrorResponse(transcriptRes, await transcriptRes.text());
}
const transcript = await transcriptRes.json();
const text =
typeof transcript.text === "string" && transcript.text.length > 0
? transcript.text
: Array.isArray(transcript.tokens)
? transcript.tokens.map((t: { text?: string }) => t.text ?? "").join("")
: "";
return Response.json({ text }, { headers: { ...CORS_HEADERS } });
}
/**
* Handle Nvidia NIM transcription
* Multipart POST, transform response to { text }
@@ -807,10 +735,6 @@ export async function handleAudioTranscription({
return handleGladiaTranscription(providerConfig, file, modelId, token);
}
if (providerConfig.format === "soniox") {
return handleSonioxTranscription(providerConfig, file, modelId, token);
}
if (providerConfig.format === "nvidia-asr") {
return handleNvidiaTranscription(providerConfig, file, modelId, token);
}

View File

@@ -490,10 +490,9 @@ export async function handleChatCore({
model,
provider,
apiKeyInfo,
headers: clientRawRequest?.headers,
log,
});
if (pluginGate.blocked === true) {
if (pluginGate.blocked) {
return {
success: false,
status: 403,
@@ -1884,7 +1883,7 @@ export async function handleChatCore({
modelOutputCap,
toPositiveInteger(resolveInputTokenCapForGate({ provider, model: effectiveModel }, { isCombo }))
);
if (outputBudget.ok === false) {
if (!outputBudget.ok) {
const exceededInputCap = outputBudget.maxInputTokens !== undefined;
const message =
`Input exceeds ${exceededInputCap ? "maximum input tokens" : "context window"} for ${provider}/${effectiveModel}: ` +
@@ -4623,7 +4622,6 @@ export async function handleChatCore({
model,
provider,
apiKeyInfo,
headers: clientRawRequest?.headers,
response: { status: 200, data: translatedResponse },
});
@@ -5014,7 +5012,6 @@ export async function handleChatCore({
model,
provider,
apiKeyInfo,
headers: clientRawRequest?.headers,
response: { status: 200, streamed: true },
});

View File

@@ -10,10 +10,13 @@
*/
type LoggerLike =
{ info?: (...args: unknown[]) => void; debug?: (...args: unknown[]) => void } | null | undefined;
| { info?: (...args: unknown[]) => void; debug?: (...args: unknown[]) => void }
| null
| undefined;
export type PluginOnRequestGate =
{ blocked: true; response: Response } | { blocked: false; body?: unknown };
| { blocked: true; response: Response }
| { blocked: false; body?: unknown };
const JSON_HEADERS = { status: 403, headers: { "Content-Type": "application/json" } } as const;
@@ -23,7 +26,6 @@ export async function runPluginOnRequestHook(args: {
model: string | null | undefined;
provider: string | null | undefined;
apiKeyInfo: unknown;
headers?: Record<string, string | string[] | undefined>;
log?: LoggerLike;
}): Promise<PluginOnRequestGate> {
try {
@@ -34,7 +36,6 @@ export async function runPluginOnRequestHook(args: {
model: args.model,
provider: args.provider,
apiKeyInfo: args.apiKeyInfo,
headers: args.headers,
metadata: {},
};
const pluginResult = await runOnRequest(pluginCtx);

View File

@@ -24,7 +24,6 @@ export async function runPluginOnResponseHook(args: {
model: string | null | undefined;
provider: string | null | undefined;
apiKeyInfo: unknown;
headers?: Record<string, string | string[] | undefined>;
response: PluginOnResponsePayload;
}): Promise<void> {
try {
@@ -36,7 +35,6 @@ export async function runPluginOnResponseHook(args: {
model: args.model,
provider: args.provider,
apiKeyInfo: args.apiKeyInfo,
headers: args.headers,
metadata: {},
},
args.response

View File

@@ -46,7 +46,7 @@ export function sanitizeChatRequestBody(
}
if (Array.isArray(body.tools)) {
const tools = body.tools.filter((tool: Record<string, unknown>) => {
body.tools = body.tools.filter((tool: Record<string, unknown>) => {
const toolType = typeof tool.type === "string" ? tool.type : "";
if (toolType && toolType !== "function" && !tool.function && tool.name === undefined) {
return true;
@@ -56,7 +56,7 @@ export function sanitizeChatRequestBody(
return name && String(name).trim().length > 0;
});
body.tools = tools.map((tool) => sanitizeOpenAITool(tool));
body.tools = body.tools.map((tool) => sanitizeOpenAITool(tool) as (typeof body.tools)[number]);
}
return body;

View File

@@ -46,15 +46,13 @@ export function resolveChatCoreTargetFormat(opts: {
sourceFormat === FORMATS.CLAUDE)
? sourceFormat
: undefined;
// #8994: model-level targetFormat overrides (from registry or custom-model DB override)
// take precedence over apiFormat="responses" — otherwise Vertex Claude models with
// targetFormat="claude" get wrongly routed to OpenAI Responses format.
let targetFormat =
modelTargetFormat ||
customModelTargetFormat ||
(apiFormat === "responses"
apiFormat === "responses"
? FORMATS.OPENAI_RESPONSES
: inferredAgentRouterTargetFormat || getTargetFormat(provider, providerSpecificData));
: modelTargetFormat ||
customModelTargetFormat ||
inferredAgentRouterTargetFormat ||
getTargetFormat(provider, providerSpecificData);
if (nativeXaiResponsesPassthrough) targetFormat = FORMATS.OPENAI_RESPONSES;
return { alias, targetFormat };
}

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