The semantic cache signature (generateSignature) was computed over different
bodies at read-time vs write-time in handleChatCore. The cache read at Phase
9.1 uses the original body, but the writes at Phase 9.1 (non-streaming) and
Phase 9.2 (streaming) used the body after sanitizeChatRequestBody() and
injectMemoryAndSkills() mutated messages. Since the digest includes messages,
every request stored under a key no later request would look up — 0% hit
rate, every request billed.
Fix: snapshot bodyForCacheWrite right after the cache read and use it for
both write paths, so the write-time signature equals the read-time one.
TDD: tests/unit/cache-signature-roundtrip.test.ts proves the mutated body
produces a different signature (bug) and the preserved snapshot produces an
identical one (fix).
Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com>
* fix(quality): green release/v3.8.50 base-reds — env-doc sync + file-size freeze (#9985)
Sweep base-reds from issue #9985 on release/v3.8.50:
- env-doc-sync: add COMMANDCODE_API_URL + ANTIGRAVITY_ALLOW_SIGNATURE_BYPASS to
.env.example and ENVIRONMENT.md (in code, missing from docs); add
OMNIROUTE_STRICT_SYSTEM_PROVIDERS + TLS_FINGERPRINT_PROVIDERS to ENVIRONMENT.md
(in .env.example, missing from doc). Restores the 3-way env contract.
- file-size: freeze open-sse/utils/proxyFetch.ts at 1207 (new proxied-TLS fetch
helper over the 1000 cap). Owner-authorized quick rebaseline; slim for v3.9.0.
Co-authored-by: OmniRoute maintenance <maintainers@omniroute.local>
* fix(quality): green open-sse+dashboard typecheck base-reds (#9985)
Release-equivalent fast-gates surface 5 real TS regressions inherited by the
base from merged Fal/guardrails/cursor work (fast-gates PR->release do not run
these, so they accrued on release/v3.8.50):
- open-sse/handlers/imageGeneration/providers/fal.ts: normalizeProviderImagePayload
missing 4th 'b64_json' arg (TS2554).
- open-sse/handlers/videoGeneration/falHandler.ts: narrow video to Record before .url.
- src/app/api/v1/images/generations/route.ts: type the toJsonErrorPayload read.
- src/lib/guardrails/visionBridgeHelpers.ts: cast through unknown for UA fetch.
- src/lib/providers/mergeProviderModelListing.ts: drop index-signature requirement
that made interface RegistryModel[] unassignable (TS2322, from #9911).
All fixed in source (keeps the gates meaningful); each reproduces on the base tip.
Co-authored-by: OmniRoute maintenance <maintainers@omniroute.local>
* fix(quality): allowlist onnxruntime-node in dependency allowlist (#9985)
check:deps base-red — onnxruntime-node is a real production dep (transformers
embedding path) landed via the LLMLingua/transformers bump (#9962) without an
allowlist entry. Legit package: microsoft onnxruntime, verified in registry.
* fix(quality): rebaseline CodeQL ratchet 1->2 for #9940 fingerprint alerts (#9985)
Base-red: 2nd js/insufficient-password-hash alert on chatBodyAdmission API-key
fingerprints (sha256->16-hex admission-lane key), not password verification.
Reproduces on release/v3.8.50 tip. Owner-authorized rebaseline (revisit v3.9.0).
* fix(quality): green release/v3.8.50 unit base-reds (#9985)
8 unit-test base-reds reproducing on the pristine release tip, fixed in-source
(fast-gates PR->release do not run the unit suite, so these accrued silently):
- ServiceSupervisor: spawn-failure now resolves with error status (was throwing);
health-probe-failure path still rejects. Distinct via spawnFailed flag.
- stream + responseSanitizer: numeric passthrough id preserved as string (was
regenerated chatcmpl-); finish chunk with empty delta no longer swallowed by
the emptyChoices guard.
- proxyFetch: genuine (non-abort) proxy transport failures keep the underlying
reason in the surfaced error.
- auto-combo builtinCatalog: advertised undefined-variant auto/* ids (auto/chat,
auto/best-chat, auto/pro-chat) materialize instead of throwing 'Unknown'.
- getTranslations en.json: add missing providers.iconUrlInvalid.
- optional-transformers-dependency.test: reconcile to #9962's deliberate
move of @huggingface/transformers to a regular dep (napi onnxruntime).
Co-authored-by: OmniRoute maintenance <maintainers@omniroute.local>
---------
Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouzapw@gmail.com>
Co-authored-by: OmniRoute maintenance <maintainers@omniroute.local>
Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com>
* fix(deps): bump nanoid, dompurify for 2 new Dependabot alerts (#189, #190)
Bumps: nanoid ^3.3.17 (was transitive, now overridden), dompurify ^3.4.13
(with monaco-editor scoped override). Closes Dependabot #189, #190.
Remaining #182-#188 (js-yaml + mermaid) already closed by #9651 merge —
awaiting Dependabot re-scan.
npm audit → 0 vulnerabilities.
* fix(repo): harden .gitignore to also ignore a _tasks symlink (/_tasks)
_tasks is a SEPARATE nested git repo (gitignored). The pattern _tasks/ (trailing
slash) ignores only a directory, not a SYMLINK named _tasks. A self-referential
_tasks symlink can slip in via git add -A and, once pulled, checkout materializes
it over the real _tasks repo (destroying plans/specs/hands-off). Anchored /_tasks
ignores the symlink too, preventing re-capture.
* fix(services): stop embedded-service supervisor retry loop when binary cannot spawn
A non-spawnable supervised binary (ENOENT/EACCES, or an ELF on Windows
where spawn() throws EFTYPE synchronously) left the supervisor in
'starting' forever while the HealthChecker polled the dead port every
healthIntervalMs. Each failed probe fired a full ProxyFetch
dispatcher+native fetch pair, burning CPU and eventually collapsing the
server (observed: 24 warns/min against 127.0.0.1:8317 for 2 days).
- handle synchronous spawn() throws and the child 'error' event: stop
the poller and transition to an explicit error state
- transition to error and stop polling when FAILURE_THRESHOLD
consecutive health probes fail, including during startup
- waitForHealthy re-checks the state after its deadline so a
mid-startup error surfaces as a rejection instead of being overwritten
by 'running'
---------
Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com>
Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@outlook.com>
Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com>
Co-authored-by: herjarsa <herjarsa@users.noreply.github.com>
* feat(cursor): prefer live synced catalog for listing and Test All
When an active synced Cursor catalog exists, list only live models plus
injected auto routers (and customs). Keep the static registry as offline
fallback.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(cursor): send live-catalog model ids verbatim on AgentRun
Skip #7289 effort/reasoning splits when the exact id is in the active synced
Cursor catalog so AgentRun does not rewrite flattened live ids into missing
bases that return AI Model Not Found. Also wires auto-cost/balance/intelligence
to default + optimization for the injected routers.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: yansigit <yansigit@users.noreply.github.com>
* fix(deps): bump nanoid, dompurify for 2 new Dependabot alerts (#189, #190)
Bumps: nanoid ^3.3.17 (was transitive, now overridden), dompurify ^3.4.13
(with monaco-editor scoped override). Closes Dependabot #189, #190.
Remaining #182-#188 (js-yaml + mermaid) already closed by #9651 merge —
awaiting Dependabot re-scan.
npm audit → 0 vulnerabilities.
* fix(repo): harden .gitignore to also ignore a _tasks symlink (/_tasks)
_tasks is a SEPARATE nested git repo (gitignored). The pattern _tasks/ (trailing
slash) ignores only a directory, not a SYMLINK named _tasks. A self-referential
_tasks symlink can slip in via git add -A and, once pulled, checkout materializes
it over the real _tasks repo (destroying plans/specs/hands-off). Anchored /_tasks
ignores the symlink too, preventing re-capture.
* fix(antigravity): ban-safety hardening — bounded onboarding retries with jitter, gate the thought-signature bypass sentinel
- onboardAntigravityUser: cap retries 10->3 and jitter the delay (3-7s) so a
stuck loop cannot read as scripted automation to the upstream
- openai-to-gemini: the skip_thought_signature_validator sentinel is an
audit-trail risk; gate it behind ANTIGRAVITY_ALLOW_SIGNATURE_BYPASS (default
enabled for compatibility, set 0 to disable). Real signatures always win.
* test(antigravity): cover the signature-bypass sentinel gate (default on, env-disabled)
Adds tests/unit/translator-antigravity-signature-bypass.test.ts (2 tests, verified
locally with node --import tsx/esm) + CHANGELOG entry for the ban-safety hardening.
---------
Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com>
Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@outlook.com>
Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com>
Co-authored-by: benzntech <benzntech@users.noreply.github.com>
Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
npm ci / next build fail on Node 24/26 because the optional
@huggingface/transformers@3.5.2 pins onnxruntime-node@1.21.0, whose NAN
native code no longer compiles against newer V8 - npm silently skips the
whole optional subtree, and Turbopack fails the build with 'Module not
found: Can't resolve @huggingface/transformers' (lazy import in
src/lib/memory/embedding/transformersLocal.ts).
Fix: move @huggingface/transformers out of optionalDependencies (npm ci
can never skip it), bump to ^4.2.0, add onnxruntime-node ~1.24.3 (napi
prebuilds, no node-gyp). Verified on Node 26.6.0: npm ci + production
build succeed; both packages require() cleanly.
- auto/best-vision and auto/pro-vision now resolve to the vision CATEGORY
(candidate filter by capability) instead of the flat smart variant, so the
vision-bridge describe/reroute target can actually see images
(resolveBuiltinAutoSpec in builtinCatalog).
- vision candidate pool excludes registry entries whose catalog OVERSTATES
vision support (opencode-go/opencode-zen/tokenrouter are forced through the
vision bridge by isVisionBridgeForcedModel) in both the auto-combo candidate
filter (suffixComposition) and the vision router (visionBridgeRouter).
- reroute guard: an auto/* target is a virtual combo; a missing 'auto' provider
row (hasUsableCredentials=false) must never block the reroute.
- claude-wire backends (minimax, zai, ...) reject remote image URLs (MiniMax
403 2013): ensureBase64ImagesForClaudeWire resolves URLs to base64 before
rerouting, and the describe self-loop normalizes to base64 for those targets
(isClaudeWireFormatModel).
- self-loop describe uses a real DB-backed key (resolveSelfLoopApiKey) instead
of the sk_omniroute sentinel rejected by REQUIRE_API_KEY instances, and
bypasses the runtime's hooked global fetch via undici (ProxyFetch with a dead
local proxy would otherwise break every describe); compression is disabled
on the self-loop sub-request so image payloads are never mangled.
Tests: vision-bridge-auto-reroute (2), vision-bridge-selfloop-key (4),
vision-bridge-claude-wire (6), builtin-vision-spec (4),
vision-filter-excludes-forced (4).
Co-authored-by: herjarsa <herjarsa@users.noreply.github.com>
* fix: add per-connection virtual admission lanes (#9654)
Worst-day-ever analysis to harden AdaptiveAdmissionController:
- Guard expireEntry() against null entry (CRITICAL null deref)
- Add deleteLane() to drain+reject on LRU eviction (HIGH orphaned promises)
- Fix Map mutation during evictIdleLanes iteration (MEDIUM safety)
- Add ADMISSION_LANE_EVICTED reject code (MEDIUM clarity)
- Pass sessionId to admitChatRequest in route.ts
- virtualLanes defaults to false in validateConfig
- 7 new controller tests + 14 new byte-level admission tests
- Assertions tightened from >= to === (Matt Pocock methodology)
Debunked 2 false positives: concurrency race (single-threaded JS)
and memory amplification (FairCostQueue bounds per-lane).
Fixes#9654
* fix(admission): restore bounded queue-wait on per-connection lanes (#9654)
The per-connection lane refactor dropped the bounded queue-wait
(acquireHeavyWithin / #waiters / queueMs). #9654's acceptance criteria and
#9608 section C prefer server-side wait/pacing up to defaultMaxWaitMs over
an instant retryable 503.
- ChatAdmissionController: re-add #waiters FIFO + acquireHeavyWithin(timeoutMs);
queueMs: 0 preserves the instant-503 path
- admitChatStructure and admitChatRequest.reserve are async again and take queueMs
- route: pass CHAT_ADMISSION_QUEUE_MAX_MS and await the admission calls
- per-connection lane tests await the async admitChatStructure
Admission suite: 114/114 pass (bun test, 7 files).
* chore: re-trigger CI after dast-smoke infra cancellation (#9654)
* feat(admission): cancel queue-wait on client abort (#9654)
U2 from KC plan 2026-08-09-001. Thread the request AbortSignal through
acquireHeavyWithin so a disconnected client stops parking in the FIFO
for the full queueMs.
- acquireHeavyWithin(timeoutMs, signal?): on abort the waiter is removed
from the FIFO immediately and the promise resolves null early;
pre-aborted signals never park; the deadline timer is cleared when
abort/release wins the race
- admitChatRequest reserve() passes request.signal; admitChatStructure
gains options.signal; the route threads request.signal
- 5 exact-assertion tests (settle-early, pre-aborted, byte-heavy,
structural, FIFO-preservation): 119/119 across the 7-file suite
* fix(admission): bound queued bytes for the queue-wait heap valve (#9654)
U3 from KC plan 2026-08-09-001. The restored queue-wait parks fully-buffered
bodies; without a cap, several large coding-agent bodies (~750 KB) waiting at
once recreates the #4380 heap amplification this module was built to stop.
- acquireHeavyWithin(timeoutMs, signal?, queuedBytes): each parked waiter is
charged its buffered size against CHAT_ADMISSION_MAX_QUEUED_BYTES (default
4 MB); over-budget waits reject immediately with a retryable 503 and never
park. The charge is released on wake, abort, or timeout.
- Real sizes threaded from admitChatRequest (declared length / sniffed bytes);
structural waits charge the conservative 256 KB weight.
- Lower default OMNIROUTE_CHAT_ADMISSION_QUEUE_MS to 2000ms (was 5000ms).
- Env vars documented in .env.example; 6 exact-assertion tests: 125/125 across
the 7-file admission suite (was 119).
* docs: map the two admission-lane systems for operators (#9654)
U5 from KC plan 2026-08-09-001. Verifies lane metrics are exposed by the health
payload (GET /api/monitoring/health -> adaptiveAdmission -> lane* fields) and
records which lane system reports where: byte-level per-connection lanes (always
on, memory scope) vs adaptive virtual lanes (opt-in via OMNIROUTE_CHAT_VIRTUAL_LANES,
dispatch scope) plus the explicit opt-in ops note.
* docs: add required frontmatter to admission-lanes doc (dast-smoke build fix)
* docs: sync env vars with .env.example and ENVIRONMENT.md (docs gate fix)
* fix(admission): complete REJECT_MAP, literal lane env read, split oversized test file
Three CI-gate fixes surfaced by the post-merge check run (head 3de77166e):
1. open-sse-typecheck (TS2741): REJECT_MAP was missing the ADMISSION_LANE_EVICTED
entry that controller.ts:662 emits on lane eviction. Add the 503 mapping so the
Record<AdmissionRejectCode, RejectHttpMapping> is total.
2. Docs Gates fabricated-claim: OMNIROUTE_CHAT_VIRTUAL_LANES was read dynamically
via ENV_KEYS.virtualLanes (env[key]), invisible to the literal env.X scanner.
Read it literally — behavior-identical, doc claim now verifiable.
3. check:file-size: chat-body-admission.test.ts (1307 lines) exceeded the 1000-line
new-file cap. Split the queue-wait/abort/heap-valve section into
chat-body-admission-queue.test.ts (818 + 513 lines, both under cap).
Suite: 125/125 across 8 files. All three checkers pass locally.
* refactor(admission): drop dead ENV_KEYS.virtualLanes entry + lock lane-evicted mapping test
Code-review follow-up on 50c93d266:
1. ENV_KEYS.virtualLanes is now unreferenced since the literal env read landed;
remove it so the config map only lists keys actually read through the map.
2. Add an exact-assertion runtime test for the ADMISSION_LANE_EVICTED mapping:
a queued lane waiter evicted by the 60s idle TTL rejects with 503 /
admission_lane_evicted / Retry-After 1 / sanitized body (no raw tenant key).
Proves the REJECT_MAP entry end-to-end through buildAdmissionRejectResponse.
Suite: 126/126 (17 in runtime file, 125 in the 8-file admission suite).
---------
Co-authored-by: Brandon Bennett <brandonbennett@macbookair.myfiosgateway.com>
Wire Bearer /alpha billing credits and 5h/weekly windows into Provider
Limits and genericQuotaFetcher so dashboard and preflight see live CC quotas.
Co-authored-by: Cursor <cursoragent@cursor.com>
opencode.ai/zen/v1 rejects non-browser clients (urllib) with 403
error_code 1010 while curl on the same key succeeds. The 403 was
treated as an auth-level failure and two of them crystallized a
misleading ALL_ACCOUNTS_INACTIVE on the free pool.
- errorClassifier: new FINGERPRINT_REJECTION type; a 403 carrying
error_code 1010 / browser_signature_banned is the CDN refusing the
client TLS/UA signature, not the account credentials.
- combo/targetExhaustion: fingerprint rejections skip auth-level
exhaustion so remaining targets stay eligible.
- auth: resolveTerminalConnectionStatus no longer treats the
fingerprint rejection as a terminal banned account state.
UA passthrough is deliberately untouched: #5997/#5720 make the
forward-only behavior load-bearing.
Signed-off-by: Minxi Hou <houminxi@gmail.com>