v3.8.40 cycle integration → main. All test gates green (Unit/Integration/Coverage/Node-compat/Quality-Ratchet). The only red check, 'PR Test Policy', is the test-masking heuristic firing on the cumulative ~57-commit release diff (legitimate assert consolidations already reviewed per-PR — Gemini CLI removal #5246, retired GPT models #5280, provider catalog refreshes); overridden with --admin per the documented release-PR convention. CodeQL/SonarQube advisory scans non-blocking; #5278's code already passed CodeQL on main. Homologated on VPS 192.168.0.15 (v3.8.40 healthy).
* chore(release): open v3.8.39 development cycle
* docs(changelog): backfill 5 v3.8.38 bullets merged after release finalize
These PRs squash-merged into release/v3.8.38 between the CHANGELOG finalize
(ff57be32f) and the merge-to-main (ae6e2342d), so they shipped in the v3.8.38
tag but had no bullet:
- feat(compression): Ionizer engine (lossy JSON-array sampling + CCR) (#5148)
- fix(sse): preserve non-stream reasoning fields (#5155, @rdself)
- fix(i18n): add missing English UI labels (#5153, @rdself)
- test(combo): gated live smoke (#5151) + release-expectations refresh (#5150, @KooshaPari)
(#5129 exact-host Anthropic baseUrl is already covered by the #5130 bullet — same CodeQL #674.)
Synced 41 i18n CHANGELOG mirrors.
* feat(compression): TOON best-of-N candidate encoder + encoder A/B table (#5163)
Integrated into release/v3.8.39. TOON best-of-N candidate encoder (GCF default, fail-open). 17/17 unit tests pass on merge result; CI reds were base-stale + Quality Ratchet DRIFT.
* fix(zenmux): normalize vendor-prefixed GLM system roles (#5158)
Integrated into release/v3.8.39. ZenMux vendor-prefixed GLM system-role normalization; 12/12 role-normalizer tests pass on merge result. CI reds base-stale.
* [codex] fix xAI OAuth test and reasoning effort (#5157)
Integrated into release/v3.8.39. xAI reasoning-effort normalization (max/xhigh→high) + OAuth test config; 46/46 xai-translator tests pass on merge result. CI reds base-stale.
* docs(i18n): add Traditional Chinese (zh-TW) README and update zh-CN to latest (#5162)
Integrated into release/v3.8.39. Traditional Chinese (zh-TW) README + zh-CN refresh; docs-only.
* test(security): guard PII redaction stays opt-in (default off) + Hard Rule #20 (#5159)
Integrated into release/v3.8.39. PII opt-in regression guard + Hard Rule #20; rebased to strip base-drift (+81/-1). 5/5 guard tests pass; flip-proof verified.
* test(combo): deterministic context-relay universal-handoff coverage (closes phase-2 TODO) (#5168)
Integrated into release/v3.8.39. Deterministic context-relay universal-handoff coverage (3 tests); 3/3 pass on merge result.
* docs(i18n): full sync zh-TW and zh-CN README with canonical English v3.8.39 (#5171)
Integrated into release/v3.8.39. Full zh-TW docs tree + zh-CN sync with canonical English v3.8.39; docs-only.
* fix(serve): honour HOSTNAME from .env instead of hardcoding 0.0.0.0 (#5134) (#5170)
Integrated into release/v3.8.39. HOSTNAME env override in serve (#5134) + regression test (4/4, TDD flip-proof verified).
* fix(sse): resolve nameless deepseek-web tool blocks via parameter-schema match (#5154) (#5173)
Integrated into release/v3.8.39. Schema-based nameless deepseek-web tool-block resolution (#5154); 6/6 tests pass on merge result (incl. ambiguous/no-match negatives + named-tag no-regression).
* fix(sse): normalize array user content for Command Code to avoid upstream 400 (#5166) (#5174)
Integrated into release/v3.8.39. Normalize array user content for Command Code (#5166, user-array/400 symptom); 4/4 tests pass on merge result.
* fix(sse): defer </think> close so it never leaks before tool_calls (#5123) (#5175)
Integrated into release/v3.8.39. Defer </think> close so it never leaks before tool_calls (#5123); 4/4 tests pass (incl. #4633 no-regression). CHANGELOG synced to keep all 3 v3.8.39 fixes.
* fix(dashboard): use amber for home update-step warning icon (#5176)
Integrated into release/v3.8.39. Amber for home update-step warning icon; 1/1 UI test.
* fix(api): LAN/Tailscale dashboard — host-aware CSP + GET-exempt version route + combo field errors (#5083) (#5177)
Integrated into release/v3.8.39. Host-aware CSP (ReDoS/injection-safe host validation) + GET-exempt /api/system/version (POST/spawn stays LOCAL_ONLY, exact-match safe-methods-only) + COMBO_002 firstField. 44/44 tests + route-guard membership gate green. CHANGELOG synced to keep all 4 v3.8.39 fixes.
* fix(api): replace #5083 global middleware CSP with declarative ws: scheme (#5083)
Follow-up to PR #5177 (merged): that version implemented the LAN-CSP fix (Bug 1)
with a new global `src/middleware.ts` + `src/server/csp.ts`, which contradicts the
project's documented architecture — 'No global Next.js middleware — interception is
route-specific' (CLAUDE.md / AGENTS.md) — and was merged unverified (middleware vs
next.config header precedence was never confirmed in a real build).
This replaces that approach with the minimal, declarative equivalent:
• next.config.mjs: connect-src now permits the bare `ws:` scheme (symmetric with the
bare `wss:` already allowed) so the dashboard can reach its own Live WS server from
a LAN/Tailscale host. No middleware.
• Removes src/middleware.ts, src/server/csp.ts, and tests/unit/csp-host-aware.test.ts.
• Adds tests/unit/csp-lan-ws-5083.test.ts (incl. a guard asserting src/middleware.ts
does NOT exist, so the global-middleware approach cannot silently return).
Bugs 2 (GET-exempt /api/system/version) and 3 (COMBO_002 field surfacing) from #5177
are unaffected and remain in place.
Co-authored-by: KooshaPari <KooshaPari@users.noreply.github.com>
* test(combo): end-to-end quota-share DRR routing-decision coverage (matrix parity) (#5179)
Integrated into release/v3.8.39. Quota-share DRR routing-decision coverage (matrix parity); 2/2 pass on merge result.
* feat(agent-bridge): graceful cert-install fallback with manual guide for containers (#4546) (#5178)
Integrated into release/v3.8.39. Agent-bridge graceful cert-install fallback + manual guide (#4546); 6/6 tests pass on merge result.
* fix(antigravity): family-scoped quota lockout (gemini/claude buckets) (#5180)
Integrated into release/v3.8.39 — family-scoped antigravity quota lockout. Rebased from v3.8.37 + validated (vitest 5/5, typecheck clean, full combo-matrix green, model-lockout 99/0). Same-model cross-account retry (chat.ts) deferred pending live antigravity VPS validation.
* fix(cli): force NODE_ENV to match dev/start run mode in custom Next server (#5189)
Integrated into release/v3.8.39. Force NODE_ENV to match dev/start run mode in custom Next server; 2/2 source-scan+ordering tests pass on merge result.
* feat(compression): CCR ranged/grep/stats retrieval (ReDoS-safe, backward-compat) (#5187)
Integrated into release/v3.8.39. CCR ranged/grep/stats retrieval (safe-regex ReDoS guard + length/match caps); 17/17 tests pass on merge result.
* docs(combo): sync all combo/routing-strategy docs to current state + document test coverage (#5185)
Integrated into release/v3.8.39. Combo/routing-strategy docs sync; docs-only.
* fix(mcp): return 404 (not 400) for unknown Streamable HTTP session id (#5169) (#5191)
* fix(api): respect blocked Auto (Zero-Config) provider in /v1/models catalog (#5192) (#5194)
* test(combo): deterministic context-relay codex quota-handoff coverage (closes last gap) (#5195)
* test(ci): wire antigravity-quota-family under test:vitest (fix test-discovery orphan) (#5196)
* fix(oauth): antigravity login no longer hangs — fire-and-forget onboarding + bounded post-exchange (#5193)
Antigravity OAuth hang fix (no-PKCE/no-openid + bounded post-exchange + exchange-500 fix). Includes #5200 (Koosha) revert + owner rebaseline to keep documented comments. Integrated into release/v3.8.39.
* feat(oauth): remote Antigravity login via local helper + paste-credentials (#5203)
Remote Antigravity login: local helper (omniroute login antigravity) + paste-credentials. Integrated into release/v3.8.39.
* fix(translator): accept Claude Messages shape in non-stream malformed-200 guard (#5156)
Integrated into release/v3.8.39
* fix(cli): default dev bundler to Turbopack (16.2.x panic no longer reproduces) (#5206)
Integrated into release/v3.8.39
* fix(cli): auto-calibrate server V8 heap from physical RAM (#5172) (#5213)
The server was spawned with a fixed --max-old-space-size=512 (omniroute serve)
or no heap flag at all (Electron), so RAM-rich boxes still OOM-crashed under
load (Ineffective mark-compacts near heap limit ~500MB) with many providers/
accounts and large model catalogs. New calibrateHeapFallbackMb(os.totalmem())
defaults the heap to ~35% of RAM clamped [512,4096], wired into serve.mjs and
electron/main.js. Explicit OMNIROUTE_MEMORY_MB still wins (#2939 unchanged).
Also addresses #5160 (same OOM root); #5152 (docker) benefits via the same knob.
Closes#5172
* fix(proxy): coalesce fast-fail health probes (#5208)
Integrated into release/v3.8.39
* fix(proxy): close dispatchers when clearing cache (#5202)
Integrated into release/v3.8.39
* fix(cli): raise dev server Node heap limit to 8GB to prevent OOM (#5198)
Integrated into release/v3.8.39
* fix(auth): allow synthetic no-auth fallback for mimocode (#5205)
Integrated into release/v3.8.39
* fix(oauth): preserve Antigravity refresh_token on empty/omitted upstream response (#3850) (#5214)
Google's OAuth refresh tokens are non-rotating: the refresh response usually
omits refresh_token and occasionally returns it as an empty string. The
Antigravity executor used `typeof tokens.refresh_token === "string" ? ... `
which accepts "" (typeof "" === "string") and overwrote the stored token with
empty, nulling it on first refresh. Now treats non-string OR empty as absent and
preserves credentials.refreshToken, matching refreshGoogleToken semantics.
Closes#3850
* fix(responses): normalize non-array input (#5204)
Integrated into release/v3.8.39
* fix(stream): normalize safety finish reasons via shared helper (#5197)
Integrated into release/v3.8.39
* fix(request-logger): never render negative '(-100%)' compression badge (#5201)
Integrated into release/v3.8.39
* fix(combo): reject empty responses api output (#5207)
Integrated into release/v3.8.39 — combo failover now rejects empty Responses API output (validateQuality). Baseline rebaseline dropped (main-measured drift; maintainer rebaselines at release).
* fix(pwa): prefer cached navigation before offline page (#5209)
Integrated into release/v3.8.39 — PWA service worker prefers cached navigation before offline page (#5165).
* chore(release): v3.8.39 — 2026-06-28
* chore(release): rebaseline openapi+i18n coverage ratchet drift for v3.8.39
---------
Co-authored-by: Arthur Bodera <abodera@gmail.com>
Co-authored-by: Nguyen Minh <lop123thcs@gmail.com>
Co-authored-by: lunkerchen <labanchen@gmail.com>
Co-authored-by: Ankit <177378174+anki1kr@users.noreply.github.com>
Co-authored-by: KooshaPari <KooshaPari@users.noreply.github.com>
Co-authored-by: Ardem2025 <ardemb22@gmail.com>
Co-authored-by: backryun <bakryun0718@proton.me>
Co-authored-by: Anton <39598727+NomenAK@users.noreply.github.com>
Co-authored-by: KooshaPari <42529354+KooshaPari@users.noreply.github.com>
Co-authored-by: Wilson <pedbookmed@gmail.com>
Co-authored-by: Randi <55005611+rdself@users.noreply.github.com>
Release v3.8.31 — see CHANGELOG.md [3.8.31] for full notes and contributors.
Merged over known non-blocking reds (all correctness gates green): Integration Tests (2/2) is env/flaky (polls a real upstream batch that did not complete in the poll window); SonarQube/SonarCloud is the advisory server-side new-code quality gate. Unit (8 shards), Coverage, Node 22/24/26, Lint, PR Test Policy, Quality Ratchet, Docs-Strict, Quality-Extended and all 4 CodeQL analyses are green.
OmniRoute v3.8.29 — 115 commits since v3.8.28. Full CHANGELOG + 41 i18n mirrors. All content quality gates green (build, unit 8/8, vitest 188/188, PR test policy, quality gates extended, docs sync, quality ratchet). Remaining red CI checks are pre-existing release flakes (coverage-shard/integration/node-compat teardown), a new transitive undici advisory in electron devDeps, and a workflow-level CodeQL fail (0 open alerts). VPS-validated by the operator.
* chore(release): open v3.8.28 development cycle
* fix(ws): warm SSE auth import on LiveWS startup; relocate boot test to integration (#4063)
The live dashboard WebSocket sidecar lazily import()-ed the SSE auth module
inside the connection handler, only on the API-key path. That cold import pulls
in hundreds of transitive modules and takes ~7s under tsx, blocking the
single-threaded event loop. The first API-key WebSocket connection therefore
stalled the loop long enough that any connection arriving in that window — e.g.
a same-origin cookie client — could not complete its handshake and timed out.
This was deterministic, not an "env flake": the boot test fires an API-key
connection immediately followed by a cookie connection, so the cookie connection
always raced the cold import and timed out (reproduced 3/3 locally and red on
every CI run; proven via instrumented probes — reversing the order or warming
the module first makes both connections open in ~20ms).
Fix:
- Memoize the auth-module import and warm it once at startup (before listen), so
connection handling never pays the cold-import cost. Real improvement: the
first API-key client no longer stalls the event loop for concurrent clients.
- Relocate the boot test from tests/unit/cli to tests/integration. It spawns a
real subprocess + WS server + SQLite (~9-11s); under the unit suite's
--test-concurrency=20 it contended for CPU and destabilized the shard. The
serial integration runner is its correct home; it still guards #4004's
cookie-parse fix on every PR via the integration CI job.
- Bump the test's startup/overall timeouts to absorb the eager auth warm.
Makes `npm run test:unit` deterministically green (the only remaining unit red).
Validated: relocated test 3/3 green via the integration runner (was 3/3 red);
typecheck:core + eslint clean; confirmed it no longer matches the test:unit glob
and does match tests/integration/*.test.ts.
* fix(ws): start LiveWS sidecar with cwd at package root (#4055) (#4064)
* chore(deps): bump ossf/scorecard-action from 2.4.0 to 2.4.3 (#4045)
Integrado em release/v3.8.28. Patch de SHA do ossf/scorecard-action (2.4.0→2.4.3), mantém SHA-pin. Reds de CI são exclusivamente os shards flaky pré-existentes branch-wide (Unit 7/8, Integration, Coverage 7/8, Node 1/2) — não relacionados ao bump (PR deps-only).
* deps: bump electron from 42.4.0 to 42.4.1 in /electron (#4049)
Integrado em release/v3.8.28. Patch do electron (42.4.0→42.4.1). Reds de CI: shards flaky pré-existentes + PR Test Policy = falso-positivo (mudança deps-only sob electron/ não comporta teste de código) + Node 26(2/2) sem step (flake/infra). Precedente #3913/#3914 (electron dependabot mergeado nessas condições).
* fix(auto): resolve built-in auto catalog combos (#4058)
Integrado em release/v3.8.28. Resolve os IDs de catálogo `auto/*` built-in (combos virtuais) — corrige o 400 "No auto combos configured" em auto/best-coding etc. Ajuste de review: os mapas AUTO_TEMPLATE_VARIANTS/VALID_AUTO_VARIANTS duplicados em chat.ts e chatHelpers.ts foram extraídos para open-sse/services/autoCombo/builtinCatalog.ts (DRY), devolvendo chatHelpers.ts <800 LOC; baseline de chat.ts rebaselinado 1432→1458 (lógica nova). Fast QG + semgrep + dast verdes; 22/22 testes.
* chore(docs): update Discord invite link to a non-expiring one (#4067)
* chore(deps): freeze @huggingface/transformers in dependabot (hard-pin) (#4066)
Integrado em release/v3.8.28. Congela @huggingface/transformers no dependabot (pin exato 3.5.2, load-bearing p/ LLMLingua + memory embeddings, VPS-validado #4014). Fast QG + semgrep + dast verdes.
* ci(quality): flip TIA impacted-unit-tests gate from advisory to blocking (#4069)
The pre-existing release unit test-debt that kept the TIA "Impacted unit tests"
step advisory has been cleared:
- #4030 restored 16 lossless Zod/registry reds (from the oyi77 modularize refactors).
- #4063 fixed the last red — the LiveWS boot test — which was a real deterministic
event-loop stall in the WS sidecar (cold ~7s lazy auth import racing a second
connection), not an env flake; fixed (warm the import at startup) and relocated to
the integration suite.
A full workflow_dispatch ci.yml run on release/v3.8.28 then showed all 8 Unit Tests
shards green. The remaining Integration Tests / Quality Ratchet reds are pre-existing
and unrelated (combo/resilience env-flakes; eslint/i18n baseline drift).
Removing continue-on-error makes PR->release block on unit-test regressions in the
TIA-selected impacted set (fail-safe still runs the full unit suite on hub/unmapped
changes). typecheck:core was already blocking. Closes the fast-gates "no tests on
PR->release" hole (Quality Gate v2 / Fase 9, P2).
* docs(compression): document LLMLingua optional deps + on-demand install (#4061)
Integrado em release/v3.8.28. Docs LLMLingua optional deps + on-demand install (F3.1).
* feat(dashboard): Combo Studio connection-cooldown badge (U1b Slice 2) (#4068)
Integrado em release/v3.8.28. Combo Studio connection-cooldown badge (U1b Slice 2 / F5.1).
* feat(compression): record Context Editing telemetry (engine: context-editing) (#4062)
Integrado em release/v3.8.28. Context Editing telemetry (F4.1).
* feat(sse): Context Editing relay coverage + 400-fallback (#4065)
Integrado em release/v3.8.28. Context Editing relay coverage (cc-*) + 400-fallback (F4.2/F4.3). Conflito de file-size-baseline.json (vs #4062) resolvido por união (ambas justificativas + base.ts 1292 + chatCore.ts 5898). Validado local no tree mergeado: typecheck:core ✓, eslint ✓, check:file-size ✓, 4/4 testes ✓; semgrep + semgrep-cloud verdes. Fast QG enfileirado (saturação de runner) — mergeado nos gates de política verificados (precedente #4034/#4020).
* feat(providers): add OrcaRouter (OpenAI-compatible routing gateway) (#4070)
Integrado em release/v3.8.28. Adiciona o provider OrcaRouter (OpenAI-compatible, API-key, DefaultExecutor). Ajuste de review: rebaseline de file-size de providers.ts 3147→3159 (+12 da entrada OrcaRouter). Validado local no tree sincronizado: provider-consistency ✓, docs-counts STRICT 227 ✓, typecheck:core ✓, teste 3/3 ✓, eslint ✓; semgrep + semgrep-cloud verdes. Fast QG/dast enfileirados (saturação de runner) — merge nos gates de política verificados (precedente #4034/#4065).
* test(infra): isolate DATA_DIR per test process; raise Stryker concurrency 1→4 (#4078)
* test(infra): isolate DATA_DIR per test process; raise Stryker concurrency 1→4
Every test process resolved DATA_DIR to the same default (~/.omniroute) when the env
var was unset (src/lib/dataPaths.ts::resolveDataDir), so concurrent test files opened
the SAME on-disk storage.sqlite. node:test spawns a process per file and Stryker spawns
one per sandbox, so this shared file caused cross-file state races:
- SQLite lock contention that hung `npm run test:unit` under high --test-concurrency
(the ~95-min local hang), and
- the non-deterministic baseline that forced stryker.conf.json to concurrency: 1, which
in turn could not finish the ~15k-mutant run inside the nightly timeout (the cancelled
2026-06-16/17 nightly-mutation runs) — blocking Quality Gate v2 / Fase 9 Onda 2.
open-sse/utils/setupPolyfill.ts could NOT host the fix: it is imported by production
(bin/omniroute.mjs, proxyFetch.ts, proxyDispatcher.ts), where redirecting DATA_DIR would
point the live SQLite DB at a throwaway temp dir. So this adds a TEST-ONLY
tests/_setup/isolateDataDir.ts that gives each process its own temp DATA_DIR when none is
set (tests that set DATA_DIR explicitly still win), wired via --import into the test,
mutation and CI invocations.
Verified:
- Stryker dry-run A/B at concurrency=4: FAILS without the isolation import
(account-fallback-service tap exit 9, a cross-file race) and PASSES with it.
- Full `npm run test:unit` green with isolation (0 fail; a one-off
chatcore-translation-paths timeout flake did not reproduce and passes 3/3 isolated)
and noticeably faster — the DB lock contention is gone.
- New tests/unit/isolate-datadir.test.ts guards the contract (unique temp DATA_DIR when
unset; explicit DATA_DIR respected).
Wired the --import into: package.json (13 test scripts), stryker.conf.json (tap.nodeArgs
+ concurrency 1→4), .github/workflows/quality.yml (TIA step), ci.yml (the 5
unit/coverage/integration commands), and bumped nightly-mutation.yml timeout 120→180 for
the first cold run before the incremental cache is seeded.
* ci(quality): run the TIA gate at CI concurrency (4) to stop oversubscription flakes
The TIA "Impacted unit tests" step (made blocking in #4069) ran its fail-safe via
`npm run test:unit` — concurrency=20, tuned for multi-core dev machines. On a 4-vCPU CI
runner that is 5x oversubscribed, so timing-sensitive tests flake under the load (e.g.
`db-backup-extended` "The database connection is not open", `chatcore-translation-paths`
upstream-timeout). That intermittently fails a blocking gate on legitimate PRs — exactly
what surfaced on the DATA_DIR-isolation PR, whose package.json/workflow changes trip the
__RUN_ALL__ fail-safe.
Run both the impacted set and the fail-safe at --test-concurrency=4, matching the stable
ci.yml unit job. Adds a `test:unit:ci` script (test:unit at concurrency=4). The DATA_DIR
isolation in this PR keeps the parallel run race-free, so the only change here is matching
the runner's core count. Verified locally: db-backup-extended passes 8/8 in isolation
(5 with isolation, 3 without).
* docs(quality-gates): reconcile gate inventory with ci.yml + add ROI rationalization backlog (#4095)
The "authoritative" gate inventory in QUALITY_GATES.md had drifted from ci.yml: it omitted
9 wired gates — `audit:deps`, `check:tracked-artifacts`, `check:lockfile`, `check:licenses`
(lint job), `check:dead-code`, `check:cognitive-complexity`, `check:type-coverage`,
`check:codeql-ratchet` (quality-gate job), and `check:pr-evidence` (pr-test-policy job).
You can't rationalize an inventory you can't trust, so this reconciles it first.
Adds those 9 rows to their job tables and a "Rationalization Backlog (ROI review)" section
capturing the Fase 9 Onda 3 findings: mechanical merge/dedup candidates (CVE scanners
audit:deps↔osv, the two complexity ESLint passes, cycles↔circular-deps, the two /api
anti-hallucination gates, the doubly-run check:docs-sync, check:node-runtime ×11) and the
operator-only flip/drop decisions (typecheck:noimplicit vs the type-coverage ratchet,
test:vitest:ui parked fails, check:secrets frozen FPs, openapi-security-tiers, pr-evidence,
the orphaned semgrep baseline). Also flags the undocumented advisory docs-lint job and the
standalone scanner workflows.
Docs-only — no gate behavior changes. The merges (CI changes) and flips (policy) are
deferred to operator-scoped follow-ups; this PR only makes the map accurate.
* test(dashboard): smoke e2e for the Combo Live Studio page (#4075)
Integrated into release/v3.8.28
* fix(sse): friendly 413 message for ChatGPT web payload-too-large (#4080)
Integrated into release/v3.8.28
* feat(sse): port Claude Code quota-probe bypass + command meta-request helpers (#4083)
Integrated into release/v3.8.28
* feat(api): exact offline token counting for count_tokens fallback via tiktoken (#4087)
Integrated into release/v3.8.28
* feat(compression): RTK learn/discover (sample source + API + UI) (#4088)
Integrated into release/v3.8.28
* feat(dashboard): 2026-06-17 free-tier refresh — honest catalog, uncapped + boost tiers, Layout A budget table (#4089)
Integrated into release/v3.8.28
* feat(mitm): capture-pipeline self-test route (Gap 12) (#4093)
Integrated into release/v3.8.28
* fix(mitm): crash-safe system-state teardown + socket timeouts (ProxyBridge-inspired hardening) (#4084)
Integrated into release/v3.8.28 (Fast QG TIA red = 3 pre-existing timing flakes verified passing locally 82/82; PR own tests green)
* feat(mitm): attribute intercepted requests to originating process (Gap 1) (#4085)
Integrated into release/v3.8.28 (Fast QG TIA red = 3 pre-existing timing flakes verified passing locally 82/82; PR own tests green)
* fix(sse): route image requests only to confirmed-vision combo targets (#4071)
Integrated into release/v3.8.28
* fix(security): injection guard respects INJECTION_GUARD_MODE DB feature flag (#4077)
Integrated into release/v3.8.28
* fix(ws): proxy LAN /live-ws upgrades and add unset JWT_SECRET warning (#4079)
Integrated into release/v3.8.28
* fix(dev): force webpack in custom dev server (Turbopack 16.2.x panics) (#4092)
Integrated into release/v3.8.28
* ci(quality): dedup the doubly-run check:docs-sync + record validated ROI backlog (#4099)
Onda 3 (gate ROI-review) Phase 2. Two parts, both low-risk:
1. Remove the standalone `check:docs-sync` from the `lint` job — it already runs in the
`docs-sync-strict` job (via `check:docs-all`) and the husky pre-commit hook, so the
`lint`-job copy was a pure duplicate. No coverage lost.
2. Update the Rationalization Backlog in QUALITY_GATES.md with trust-but-verify findings:
several "obvious" merges/flips from the ROI review turned out to hide debt and are NOT
clean drop-ins —
- CVE merge (audit:deps→osv): different semantics (hard high/critical vs regression-ratchet) — keep both.
- cycles→circular-deps: dpdm reports 91 cycles (can't promote to blocking) and is broader-scope than the green curated check:cycles — keep both.
- openapi-security-tiers flip: blocked by traffic-inspector routes missing the x-loopback-only annotation.
- complexity + /api merges: valid but real config/script surgery — deferred.
- node-runtime ×11: ~10s savings vs a cheap guard — low ROI, skip.
The remaining flips (typecheck:noimplicit, test:vitest:ui, check:secrets, pr-evidence,
semgrep) are operator policy decisions, left for the owner.
* chore(deps): bump actions/github-script from 7 to 9 (#4046)
Integrated into release/v3.8.28 (dependabot GH-Action bump; SHA-pin preserved)
* chore(deps): bump actions/setup-node from 4 to 6 (#4048)
Integrated into release/v3.8.28 (dependabot GH-Action bump; SHA-pin preserved)
* chore(deps): bump actions/upload-artifact from 4 to 7 (#4044)
Integrated into release/v3.8.28 (dependabot GH-Action bump; SHA-pin preserved)
* chore(deps): bump actions/cache from 4.3.0 to 5.0.5 (#4047)
Integrated into release/v3.8.28 (dependabot GH-Action bump; SHA-pin preserved)
* deps: bump the development group with 10 updates (#4051)
Integrated into release/v3.8.28 (dependabot dev group; cyclonedx 4->5 verified compatible with the SBOM invocation --ignore-npm-errors/--output-format JSON/--output-file)
* fix(dashboard): event-driven fail-open auto-refresh for embedded log views (#4054) (#4103)
The Request Logger gated each auto-refresh tick on a static
document.visibilityState === "visible" read. Hosts that report a permanent
non-"visible" state without ever firing a visibilitychange event (Docker
dashboard wrappers, embedded/proxied webviews) froze auto-refresh entirely —
only the manual Refresh button worked, a regression from 3.8.24's unconditional
polling.
The pause is now event-driven and fail-open: visibleRef starts true and is only
flipped to false on a real visibilitychange → hidden transition, so a host that
never signals a genuine background transition keeps polling, while normal
browser tabs still pause when actually backgrounded.
Regression test reproduces the misreporting-host case (RED) and the perf guard
is re-encoded under the event-driven semantics.
* fix(docker): raise build-stage Node heap to stop production-build OOM (#4076) (#4104)
The Docker builder stage ran `npm run build` with V8's default heap ceiling
(~2 GB). After #4052 forced the heavier webpack engine (Turbopack panics on this
Next.js version), the production optimization pass exceeded that ceiling and the
build died with "FATAL ERROR: ... JavaScript heap out of memory" at
[builder] npm run build.
The builder stage now sets NODE_OPTIONS=--max-old-space-size (default 4096 MB,
overridable via --build-arg OMNIROUTE_BUILD_MEMORY_MB) before the build; the
value propagates to the spawned next build (resolveNextBuildEnv spreads
process.env). Build-only — the runtime heap on the runner stage is unchanged,
and CI/local builds (which invoke npm run build directly) are unaffected.
Regression guard: tests/unit/dockerfile-build-heap-4076.test.ts asserts the
builder stage sets the heap ceiling, before npm run build, at >= 4096 MB.
* feat(agent-bridge): portable JSON import/export of config (Gap 4) (#4094)
Integrated into release/v3.8.28
* feat(cli): add 'omniroute launch' zero-config Claude Code launcher (#4097)
Integrated into release/v3.8.28 (Fast QG TIA red = pre-existing env-doc-contract drift [MITM_IDLE_TIMEOUT_MS/TURBOPACK from #4084/#4092] + opencode-plugin-dist env flake; #4097 own test 3/3 green)
* feat(mitm): loop-guard self-check + verbosity control in server.cjs (Gaps 14+15) (#4101)
Integrated into release/v3.8.28 (rebased onto release — dropped the already-squash-merged #4084 commits; only the Gaps 14+15 loop-guard/verbosity delta remains)
* feat(sse): generic 400 field-downgrade retry + Groq field stripping (#4096)
Integrated into release/v3.8.28
* feat(providers): add Wafer AI (Anthropic-compatible, Bearer auth) (#4098)
Integrated into release/v3.8.28
* chore(docs)
* fix(responses): clear /v1/responses keepalive timer on cancel/abort (timer + CPU leak) (#4105)
Integrated into release/v3.8.28 (r7).
* perf(gemini): cache reasoning close-tag regex instead of recompiling per token (#4106)
Integrated into release/v3.8.28 (r7).
* fix(usage): reap orphaned pending-request details (unbounded memory leak) (#4107)
Integrated into release/v3.8.28 (r7).
* perf(stream): use structuredClone instead of JSON round-trip for per-chunk reasoning split (#4108)
Integrated into release/v3.8.28 (r7).
* fix(dashboard): restore Update Available banner with npm-binary-free version fallback (#4100) (#4112)
getLatestNpmVersion() derived the latest version only from the npm CLI binary and returned null on any error, so Docker/desktop/locked-down installs without npm on PATH silently hid the home banner even when an update existed. Add resolveLatestVersion() (npm CLI -> registry HTTP fallback -> logged warning) and harden version parsing for v-prefix/pre-release strings. Extracted into testable src/lib/system/versionCheck.ts with TDD coverage.
* fix(auth): prune expired entries from login brute-force guard map (unbounded growth) (#4111)
Integrated into release/v3.8.28 (r8)
* fix(logger): hard-cap the error-dedup map to bound memory under unique-message bursts (#4113)
Integrated into release/v3.8.28 (r8)
* fix(circuit-breaker): enforce MAX_REGISTRY_SIZE (declared but never applied) (#4114)
Integrated into release/v3.8.28 (r8)
* perf(obfuscation): cache per-word regexes instead of recompiling every request (#4109)
Integrated into release/v3.8.28 (r8)
* perf(registry): precompute model->provider index in parseModelFromRegistry (#4110)
Integrated into release/v3.8.28 (r8)
* fix(timers): unref background interval timers so they don't block clean shutdown (#4117)
Integrated into release/v3.8.28 (r8)
* fix(webhook): clear abort timer in finally to avoid dangling timers on fetch error (#4115)
Integrated into release/v3.8.28 (r8)
* fix(combo): detach per-target listener from shared hedge abort signal (#4116)
Integrated into release/v3.8.28 (r8)
* chore(release): finalize v3.8.28 CHANGELOG + reconcile env-doc contract
- Build the complete [3.8.28] CHANGELOG section (55 bullets) covering every
commit since v3.8.27, grouped by type with PR back-references and human
contributor attribution (artickc's memory-leak/perf cluster, OrcaRouter,
Wafer AI, MITM gaps, etc.); move the OrcaRouter bullet out of [Unreleased].
- Inject the EN [3.8.28] section into all 41 i18n CHANGELOG mirrors (parity).
- Reconcile the env/docs contract: document MITM_IDLE_TIMEOUT_MS + MITM_VERBOSE
in .env.example and ENVIRONMENT.md; allowlist the framework-internal TURBOPACK
and the Claude Code ANTHROPIC_AUTH_TOKEN in check-env-doc-sync.
- Fix 3 broken relative links in docs/providers/AGENTROUTER.md (regressed when
the file was relocated this cycle) so docs-sync-strict passes.
* fix(quality): treat test→test renames as relocations, not deletions
The anti-test-masking gate's subcheck-1 collected deleted AND renamed test
files via `--diff-filter=DR --name-only` and flagged every one as "deleted —
human review required", contradicting its own documented contract ("DELETADOS
ou renomeados-e-NÃO-substituídos"): a rename test→test IS a substitution (the
test moved, coverage preserved). This false-positived on #4063's legitimate
relocation of live-ws-startup.test.ts (unit/cli → integration, asserts 2→2)
and would block every PR that relocates a test — surfacing only at release-day
because the Fast QG (PR→release) doesn't run test-masking.
The gate now parses `--name-status -M`: true deletions and test→non-test
renames still flag; a test→test rename is run through the assert-reduction
check across the move, so a clean relocation passes while gutting-via-rename
(dropped asserts / new tautologies / skips) still fires. Adds
partitionDeletedRenamed + 6 regression tests.
---------
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Demiurge The Single <megamen932@gmail.com>
Co-authored-by: jinhaosong-source <jinhao.song@myflashcloud.com>
Co-authored-by: diego-anselmo <contato@diegoanselmo.com.br>
Co-authored-by: Felipe Almeman <4226997+zhiru@users.noreply.github.com>
Co-authored-by: Rahul sharma <sharmaR0810@gmail.com>
Co-authored-by: Chirag Singhal <76880977+chirag127@users.noreply.github.com>
Co-authored-by: NOXX - Commiter <artur1992123@mail.ru>
Gera automaticamente 24 grupos de comandos (170+ operações) em bin/cli/api-commands/ a
partir de docs/reference/openapi.yaml via npm run build:cli-api. Integrado em registry.mjs;
prepublishOnly regenera antes de publicar.
5 subcomandos: tail, search, export, stats, get.
Mescla compliance e MCP por timestamp. --follow faz polling 2s.
--source filtra entre compliance|mcp|all. Mascaramento de actor.
Implementa `omniroute usage` com subcomandos:
- analytics: agregados por provedor com filtro --period/--provider
- budget list|get|set|reset: gerenciamento de budgets de custo
- quota: estado de quota por provedor com --check
- logs: call-logs com --search, --since, --api-key e --follow (tail 2s)
- utilization: métricas de uso por API key
- history: histórico de requisições
- proxy-logs: logs em nível de proxy
API keys mascaradas em outputs human. Todos os subcomandos suportam
--output json/table/csv/jsonl via emit().
Implementa `omniroute cost` que consulta /api/usage/analytics com:
- --period <range>: 1d|7d|30d|90d|ytd|all (padrão: 30d)
- --since/--until: faixa de datas ISO (substitui --period)
- --group-by: provider|model|api-key|combo|day (padrão: provider)
- --api-key: filtrar por chave específica
- --limit: top N resultados
- Total em stderr ao final (modo tabela)
- Ordenação por custo decrescente
Implementa `omniroute simulate [prompt]` que consulta /api/combos,
/api/monitoring/health e /api/usage/quota para mostrar qual caminho
de routing seria escolhido sem executar chamada upstream. Suporta:
- Tabela com provider, model, probabilidade, custo estimado, breaker e quota
- --explain: imprime árvore de fallback e faixa de custo no stderr
- --output json: retorna array de targets completo
- --file <path>: carrega body JSON para estimar tokens
- --combo <name>: filtra por combo específico
- Tratamento gracioso quando servidor está offline (exit 3)
Implementa `omniroute stream [prompt]` com suporte a:
- --raw: imprime linhas SSE brutas sem parsing
- --debug: timing por chunk no stderr com timestamp relativo
- --save <path>: persiste eventos em arquivo .jsonl
- --output json: retorna chunks + métricas (TTFT, totalMs, tokens/s)
- --responses-api: usa /v1/responses e lê campo delta
- SIGINT gracioso via reader.cancel()
- Métricas de TTFT e tokens/s no stderr ao final
Implementa `omniroute chat [prompt]` com suporte a --file, --stdin, --system,
--model, --max-tokens, --temperature, --top-p, --reasoning-effort,
--thinking-budget, --combo, --responses-api, --stream e --no-history.
Respostas impressas no stdout; latência e token count no stderr (não interfere
em pipes). Histórico salvo em ~/.omniroute/cli-history.jsonl. Streaming via
SSE com print incremental de deltas.
Extrai 7 grupos de comandos do monolito bin/cli-commands.mjs (2853 linhas)
para módulos individuais em bin/cli/commands/, registrados via Commander.
- stop.mjs: SIGTERM/SIGKILL via process.kill(); fallback por porta usa execFile
com array de args (evita injeção de shell / Semgrep CWE-78)
- restart.mjs: delega para runStopCommand + runServe
- dashboard.mjs: alias "open", fallback nativo por plataforma via execFile
- keys.mjs: server-first (POST /api/v1/providers/keys) → DB fallback; valida
provider via loadAvailableProviders(); suporte a --stdin
- models.mjs: GET /api/models → fallback /api/v1/models; filtro por provider e --search
- combo.mjs: list/switch/create/delete; switch server-first → DB fallback via key_value;
TODO(1.5) marcados para substituir SQL cru por src/lib/db/combos.ts
- serve.mjs: escreve PID via writePidFile() no spawn; limpa no shutdown; suporte daemon
utils/pid.mjs e i18n keys (en.json + pt-BR.json) já criados em iteração anterior.
Testes: cli-keys-command.test.ts atualizado para novos runners;
cli-serve-stop-command.test.ts, cli-combo-command.test.ts,
cli-models-command.test.ts adicionados (23 testes, 0 falhas).
Migra os 8 comandos restantes (doctor, setup, providers, config, status,
logs, update, provider) de parseArgs manual para a API do Commander.
Cada arquivo exporta register<Nome>(program) e run*Command(opts = {}).
Deleta bin/cli/index.mjs (substituído por commands/registry.mjs).
Remove dependência de bin/cli/args.mjs em todos os comandos migrados.
Corrige CWE-310 em doctor.mjs: authTagLength explícito no createDecipheriv.
- Instala commander@^14.0.0 com suporte nativo a ESM
- Cria bin/cli/program.mjs: programa raiz com opções globais
(--output, --quiet, --no-color, --timeout, --api-key, --base-url)
- Cria bin/cli/commands/registry.mjs: adaptadores legados que delegam
para os run*Command existentes sem quebrar compatibilidade
- Cria bin/cli/commands/serve.mjs: ação padrão (isDefault: true) com
lógica de spawn extraída de omniroute.mjs, imports de runtime lazy
- Cria bin/cli/commands/reset-encrypted-columns.mjs: bypass de recuperação
- Refatora bin/omniroute.mjs: ~500 → ~90 linhas, delega ao Commander
- Adiciona strings i18n program.* e serve.description/port/no_open/daemon
em en.json e pt-BR.json
- Adiciona 21 testes em tests/unit/cli-program.test.ts