mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-05 06:42:12 +03:00
bbc9d1e1c5772883eb59d6db77615568d2d3fb75
4543 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
bbc9d1e1c5 |
feat(quality): seed per-module mutationScore floors + blocking aggregation ratchet (T3) (#4305)
First full mutation measurement landed (run 27823984918, the split nightly from #4272): 31 modules now have a COVERED mutation score. T3 turns that into an enforced gate. Seed: 31 `mutationScore.<path>` floors in quality-baseline.json at ~2pt below the measured score (absorbs run-to-run variance), direction:up, dedicatedGate:true. dedicatedGate means the generic check-quality-ratchet SKIPS them (check-quality-ratchet.mjs:62) — they are enforced only by check-mutation-ratchet.mjs. Range: memorySkillsInjection 13.49 (weakest) to headers 94.29 (strongest); the security/critical floors: auth 52.57, accountFallback 68.38, routeGuard 76.08, circuitBreaker 56.94, error 43.83, publicCreds 59.76. Gate: a new `mutation-ratchet` job in nightly-mutation.yml runs AFTER all batches (needs: stryker, if: always()), downloads every mutation report, and ratchets the MERGED per-module scores with `check-mutation-ratchet --ratchet` (blocking). It must aggregate because the split batches each emit a PARTIAL view of a file (auth.ts in a1+a2, accountFallback in b1+b2) — a per-batch ratchet would compare half a file against the whole-file floor. check-mutation-ratchet unions same-file mutants across reports (#4272). A module dropping below its floor fails the run; missing reports (upload flake) are skipped. Verified: ratchet exits 0 on the seeded measurements, exits 1 on a synthetic regression (auth 33.33 < 52.57), exits 0 advisory without --ratchet. Baseline change is additive (31 floors + one comment; existing keys untouched). check-mutation-ratchet tests 8/8. |
||
|
|
23455fdb0a |
feat(cli): setup-gemini — point the Gemini CLI at OmniRoute's native /v1beta endpoint (#4303)
The Gemini CLI is not OpenAI-compatible — it speaks the native Gemini API. OmniRoute exposes a Gemini-native surface at /v1beta, so setup-gemini emits the @google/genai env recipe (GOOGLE_GEMINI_BASE_URL root + GEMINI_API_KEY) and optionally writes ~/.gemini/settings.json (model). Remote-aware (resolves baseUrl + key from the active context, --remote or --api-key). Documents the cached-Google-login caveat that can override the base URL. Completes the per-CLI setup series (Codex, Claude, OpenCode, Cline, Kilo, Continue, Cursor, Roo, Crush, Goose, Qwen, Aider, Gemini). |
||
|
|
0ab1876008 |
feat(mitm): translate Antigravity cloudcode end-to-end (Gap B) (#4299)
The Antigravity IDE speaks cloudcode (the Gemini payload wrapped under
`request`) and expects a cloudcode reply ({response:{candidates}}). The
AgentBridge proxy forwarded that envelope verbatim to /v1/chat/completions
(OpenAI), which 400s on the missing `messages` field — so the IDE could be
decrypted/intercepted but never actually routed to a provider.
Wire the inbound cloudcode path, reusing the already-registered bidirectional
translators (no new translators needed):
- provider.ts: detectFormatFromEndpoint classifies the /antigravity path as
sourceFormat "antigravity" (mirrors /messages -> claude), so the pipeline
translates request antigravity->openai and response openai->antigravity.
- /v1/antigravity route (new): cloudcode-compatible endpoint — just calls
handleChat (mirrors /v1/messages).
- server.cjs: routes cloudcode envelopes to /v1/antigravity (translates both
ways) and plain OpenAI bodies to /v1/chat/completions, via a testable shim.
Tests: forward-target shim (cloudcode vs openai routing) + endpoint format
detection. The antigravity<->openai translators are already covered by
translator-antigravity-to-openai / translator-resp-openai-to-antigravity.
Stacked on #4285 (Gap A). Full Antigravity-IDE e2e validates on the next
standalone deploy (provider.ts + the route compile into .next).
|
||
|
|
915991c762 |
fix(codex): isolate Spark quota scope (#4293)
* fix(codex): isolate Spark quota scope * fix(codex): address Spark quota review feedback * fix(ci): update Electron undici override * fix(ci): update root undici overrides * test(integration): sync stale expectations * test(tproxy): tolerate available native addon * test(tproxy): avoid environment-specific skips * test(tproxy): keep assertion count stable * fix(ci): stabilize quality and tproxy checks * chore(ci): rebaseline auth file size * fix(ci): extend node compatibility budget * chore(quality): reconcile complexity + file-size baselines after release/v3.8.30 merge (#4293) Measured on the actual merged tree (not the PR's main-based estimate): complexity 1885->1887 (+2); file-size auth.ts 2219->2279, chatCore.ts 5116->5125, accountFallback.ts 1727->1731, + the 4 Codex test files. Drift test-file conflicts (search-providers-catalog, tproxy-transparent-socket, integration-wiring) resolved to the already-merged release versions (#4276). Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com> --------- Co-authored-by: ci <ci@local> Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com> |
||
|
|
165d9cdae9 |
feat(cli): setup-aider — configure Aider for OmniRoute (.aider.conf.yml + env recipe) (#4302)
CLI #12 of the series. `omniroute setup-aider` writes Aider's ~/.aider.conf.yml (openai-api-base = ROOT url, NO /v1 — LiteLLM appends /v1/chat/completions — model: openai/<id>), merges to preserve existing config, and prints the env recipe (OPENAI_API_BASE + OPENAI_API_KEY in the env, never the file) plus the headless command (aider --message ... --yes). Remote-aware; model via --model or interactive pick. Researched against aider.chat: OpenAI-compatible via OPENAI_API_BASE (base, no /v1) + --model openai/<id>. Aider's wire (/v1/chat/completions) already validated → "OK". Tests: resolveAiderTarget (/v1 strip, key), buildAiderConfig (openai-api-base + openai/<model> + preserve), buildAiderRecipe (env-ref key + headless). 4 unit tests; cli-i18n green. |
||
|
|
b01b72052f |
fix(dashboard): improve API try it functionality (#4296)
* fix(dashboard): improve api try it functionality and allow manual key entry * test(api): cover generateExampleFromSchema for the Try It panel (#4296) Export generateExampleFromSchema from the /api/openapi/spec route and add a unit test covering type handling, property-name heuristics, $ref/oneOf/anyOf/ allOf resolution, the 'required + first 3 optional' object policy, and the depth-3 recursion guard — the example bodies the dashboard Try It panel pre-fills. Rule #18 regression guard for the new helper. Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com> --------- Co-authored-by: ci <ci@local> Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com> |
||
|
|
b56b7b1914 |
fix: polyfill crypto.randomUUID for non-secure contexts (#4287)
* fix: polyfill crypto.randomUUID for non-secure contexts crypto.randomUUID() requires a secure context (HTTPS or localhost). When accessing the dashboard over HTTP on a LAN IP, the function is undefined, causing 'Failed to add account' errors on providers that generate account IDs client-side (e.g. mimocode). Adds a lightweight polyfill that falls back to a Math.random()-based UUID v4 generator when the native API is unavailable. * fix: address review comments on crypto.randomUUID polyfill - Use crypto.getRandomValues() for cryptographic security instead of Math.random() - Add typeof window !== 'undefined' guard to avoid ReferenceError in non-browser envs - Use window.crypto for safe access instead of bare crypto reference - Replace var with const and == with === for modern JS syntax - Add fallback to Math.random() when getRandomValues is unavailable - Add unit tests verifying valid UUID v4 format, version/variant nibbles, uniqueness, and preference for getRandomValues over Math.random * test(dashboard): regression guard for crypto.randomUUID polyfill (#4287) Reads src/app/layout.tsx and asserts the blocking inline script installs a guarded window.crypto.randomUUID polyfill (RFC4122 v4 shape, getRandomValues preferred with a Math.random fallback). Fails on the pre-fix tree (no polyfill), passes with the fix — Rule #18 regression guard for the non-secure-context (HTTP/LAN-IP) dashboard breakage. Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com> --------- Co-authored-by: pizzav-xyz <pizzav-xyz@users.noreply.github.com> Co-authored-by: ci <ci@local> Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com> |
||
|
|
fcdf29f8c5 |
chore(deps): bump actions/checkout from 4 to 7 (#4297)
Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 7. - [Release notes](https://github.com/actions/checkout/releases) - [Commits](https://github.com/actions/checkout/compare/v4...v7) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '7' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
|
|
133432b523 | fix(proxy): allow concurrent proxy dispatcher streams (#4288) | ||
|
|
0acb8d0aeb |
fix(build): co-locate llmlingua SLM optionals into dist/node_modules (postinstall) (#4286)
The compression "ultra" SLM tier (#4257) runs @atjsh/llmlingua-2 + transformers + tfjs + js-tiktoken in a worker thread shipped under dist/. These are optionalDependencies installed into the ROOT node_modules on --include=optional, but the Next.js standalone trace bundles ONLY @huggingface/transformers (3.5.2, pinned) into dist/node_modules — not the dynamically-imported optionals. Result: the worker resolves transformers from dist/node_modules (3.5.2) for its env config but resolves @atjsh/llmlingua-2 from the ROOT, whose own transformers import hits a DIFFERENT instance. The cacheDir config never reaches the instance llmlingua-2 uses, so the local model never loads and the SLM tier silently fails-open (and on a root transformers 4.x, llmlingua-2 throws on the tokenizer API change). Fix: postinstall co-locates the SLM optional closure from the root node_modules into dist/node_modules (no-clobber, so the pinned dist transformers/onnxruntime stay), so the worker resolves a single 3.5.2 instance and the local model loads. VPS-validated (Rule #18): the co-located layout produced real 54.8% compression (11520->5203 chars) via real ONNX inference on the production host, both the default and the #4257 modelPath code paths. - scripts/build/colocateOptionals.mjs: closure walk (deps+optionalDeps, skips the transformers peer) + no-clobber co-location; idempotent + fail-soft - wired into scripts/build/postinstall.mjs next to ensureSwcHelpers - registered in package.json files + pack-artifact allow/required lists - tests/unit/colocate-optionals.test.ts: closure, no-clobber, idempotence, gates - docs/ops/RELEASE_CHECKLIST.md: note the auto co-location |
||
|
|
efbe0a6af1 |
fix(mitm): surface AgentBridge traffic in the Traffic Inspector (D4 ingest) (#4285)
The standalone server.cjs proxy intercepts AgentBridge requests inline (no MitmHandlerBase / agentBridgeHook), so intercepted traffic never reached the TS hook that pushes into globalTrafficBuffer — the Traffic Inspector stayed empty for AgentBridge even on successful intercepts. Three gaps closed: - _internal/ingest.cjs (new): pure payload builder + fire-and-forget poster (never throws — capture must not break proxy traffic). - server.cjs: intercept() accumulates response (bounded) + status/headers and posts the captured entry to the local-only /internal/ingest endpoint in a finally block; also captures error/4xx intercepts. - manager.ts: resolves the ingest token via getIngestTokenForBootstrap() and passes it to the spawned proxy so the endpoint accepts the post. - authz management policy: exempt the loopback /internal/ingest endpoint from management auth — it has its own shared-secret token gate, and server.cjs has no dashboard cookie. Stays strictly loopback (LOCAL_ONLY gate unchanged). - ingest route: masks secrets / strips hop-by-hop headers before buffering (server.cjs sends raw over the token-gated loopback) — Hard Rule #12. Tests: ingest shim (build/post/no-token/error) + route sanitization + 403 + management-policy carve-out (loopback allow / remote LOCAL_ONLY). |
||
|
|
0ec476b755 |
feat(cli): setup-qwen — configure Qwen Code for OmniRoute (settings.json modelProvider) (#4301)
CLI #11 of the series. `omniroute setup-qwen` writes Qwen Code's file-based ~/.qwen/settings.json: an openai `modelProvider` (id omniroute, authType openai, baseUrl WITH /v1, envKey OMNIROUTE_API_KEY — secret stays in the env), selects it, sets the model. Merges (de-dupes the omniroute provider, preserves the rest). Remote-aware; model via --model or interactive pick; headless test `qwen -p`. Researched against QwenLM/qwen-code: modelProviders authType openai, baseUrl /v1, envKey reference. Qwen's wire (/v1/chat/completions) already validated → "OK". Tests: resolveQwenTarget (/v1, key), buildQwenSettings (openai provider + /v1 + envKey + model, de-dupe + preserve). 4 unit tests; cli-i18n green. |
||
|
|
25f9dac9e9 |
feat(cli): setup-goose — configure Goose for OmniRoute (config.yaml + env recipe) (#4300)
CLI #10 of the series. `omniroute setup-goose` writes Goose's file-based ~/.config/goose/config.yaml (GOOSE_PROVIDER=openai, GOOSE_MODEL=<model>, OPENAI_HOST=<root, NO /v1 — Goose appends the path itself>), merges to preserve existing keys, and prints the guaranteed env-var recipe (the key lives in the env / OS keyring, never the config). Remote-aware (--remote/--api-key → context → localhost); model via --model or interactive pick. Researched against block/goose: provider openai + OPENAI_HOST base (no /v1). Goose's wire (/v1/chat/completions) already validated → "OK". Tests: resolveGooseTarget (/v1 strip, key), buildGooseConfig (provider/model/host + preserve), buildGooseEnvRecipe (env-ref key). 4 unit tests; cli-i18n green. |
||
|
|
26dd5d775c |
feat(cli): setup-crush — OmniRoute openai-compat provider in crush.json (#4298)
CLI #9 of the series. `omniroute setup-crush` writes Crush's file-based ~/.config/crush/crush.json with an `openai-compat` provider for OmniRoute: base_url WITH /v1, api_key referenced as $OMNIROUTE_API_KEY (secret off disk), curated catalog models with context_window. Merges (preserves existing config). Remote-aware (--remote/--api-key → active context → localhost); --only filter. Researched against charmbracelet/crush: openai-compat provider type, base_url needs /v1, $VAR api_key references. Crush's wire (/v1/chat/completions) already validated → "OK". Tests: resolveCrushTarget (/v1, key), buildCrushProvider (openai-compat + env-ref + curated models + skip-unknown), mergeCrushConfig (preserve). 4 unit tests; cli-i18n green. |
||
|
|
bf5b615969 |
feat(memory): x-omniroute-no-memory opt-out + memory off-by-default + token-cost alert (PRD-2026-06-19) (#4290)
* feat(memory): x-omniroute-no-memory opt-out + memory off-by-default + token-cost UI alert PRD-2026-06-19-no-memory-header. The gateway injects up to memorySettings.maxTokens (~2k) of memory (and skills) context into every chat call for memory-enabled keys, inflating tokens+cost ~137x for clients that manage their own context (e.g. Omniflow). Three changes: - A) x-omniroute-no-memory request header (mirrors x-omniroute-no-cache): when truthy (true/1/yes), skip memory+skills injection for that request. New pure helper isNoMemoryRequested() in chatCore/headers.ts; chatCore passes memoryOwnerId=null on opt-out (a null owner disables both injection branches). - B) Memory OFF by default: DEFAULT_MEMORY_SETTINGS.enabled true->false. Enabling injects billed context per request, so it's now an explicit opt-in. Installs that already enabled it keep it; unset installs default off (no migration seeds memoryEnabled). - C) Settings -> Memory shows a token-cost warning callout when memory is enabled (new settings.memoryTokenCostWarning i18n key, interpolating the configured maxTokens). Tests: no-memory-header.test.ts (5, helper truthiness/case/Headers); memory-settings-default and chatcore-memory-skills-injection aligned to the new off-by-default. 65/65 memory+chatcore tests green; typecheck/lint/file-size/i18n(@65) clean. * test(memory): enable memory in memory-tools test (memory now off by default) The full CI unit suite flagged memory-tools.test.ts 'memory search ...' failing after DEFAULT_MEMORY_SETTINGS.enabled flipped to false: omniroute_memory_search routes through retrieveMemories, which returns [] while memory is disabled (enabled:false → maxTokens 0). The memory MCP tools operate within the memory subsystem, so the test now enables memory explicitly (updateSettings + cache invalidation) — the realistic precondition for a client using the tools. Aligns the test to the intentional off-by-default change; assertions unchanged. |
||
|
|
5b40069b71 |
feat(cli): setup-roo — configure Roo Code for OmniRoute (import JSON + autoImport + UI) (#4292)
CLI #8 of the series. Roo Code (RooVeterinaryInc.roo-cline, a Cline fork) keeps live settings in opaque VS Code globalStorage, but supports Settings Import and an `roo-cline.autoImportSettingsPath` (VS Code settings.json) that loads a JSON at startup. `omniroute setup-roo`: - writes ~/.omniroute/roo-settings.json — a Roo provider profile (providerProfiles.apiConfigs.OmniRoute: apiProvider=openai, openAiBaseUrl WITH /v1 — Roo appends /chat/completions — openAiApiKey, openAiModelId). - sets roo-cline.autoImportSettingsPath in VS Code settings.json when present (preserves other settings). - prints the guaranteed UI path (Settings → Providers → OpenAI Compatible) + the "Import Settings" fallback. - remote-aware; model via --model or interactive pick. Researched against current Roo docs: OpenAI-compatible needs baseUrl WITH /v1 and native tool-calling (OmniRoute supports it). Roo's wire (/v1/chat/completions) already validated → "OK". Tests: resolveRooTarget (/v1, key), buildRooImport (provider profile + /v1 + key fallback), buildRooVscodeAutoImport (pointer + preserve). 5 unit tests; cli-i18n green. |
||
|
|
9616b65b53 |
feat(cli): setup-cursor — print Cursor setup steps for OmniRoute (#4291)
CLI #7 of the series. Cursor stores its OpenAI key + "Override OpenAI Base URL" in an opaque SQLite DB (state.vscdb) with no stable schema — not safe to file-write. So `omniroute setup-cursor` prints the exact in-app steps and lists real model names from /v1/models. - Resolves apiBase WITH /v1 (Cursor appends /chat/completions) + key from --remote/--api-key → active context → localhost. - Prints Settings → Models → Override OpenAI Base URL + key + model-name steps, with a clear caveat that the custom base URL powers Cursor's CHAT panel only (Composer / inline-edit / autocomplete stay on Cursor's backend). Researched against current Cursor behavior. Tests: resolveCursorTarget (/v1, key), buildCursorInstructions (base URL + /v1 note + model samples + caveat). 4 unit tests; check:cli-i18n green. |
||
|
|
138ea6628d |
feat(cli): setup-continue — generate ~/.continue/config.yaml for OmniRoute (#4289)
CLI #6 of the series. `omniroute setup-continue` writes Continue's file-based, mergeable ~/.continue/config.yaml (shared by the VS Code/JetBrains extensions AND the `cn` CLI) from the live model catalog. - Each curated model → a Continue model entry: provider: openai, model: <id>, apiBase WITH /v1 (Continue appends /chat/completions), apiKey: ${{ secrets.OMNIROUTE_API_KEY }} (secret referenced, never written), roles [chat, edit, apply] (+ autocomplete for the fast tier). - Merges into existing config.yaml (js-yaml load/dump): drops prior models on the same apiBase, preserves the user's other models + top-level keys. - Remote-aware (--remote/--api-key → active context → localhost); --only filter. - Prints how to provide the key (shell env for cn; ~/.continue/.env for IDE). Researched against current Continue docs: provider: openai + custom apiBase (with /v1), the ${{ secrets.X }} syntax, roles, and that the `cn` CLI shares the same config. Continue's wire (/v1/chat/completions) already validated → "OK". Tests: buildContinueModels (provider/apiBase/secret/roles, fast→autocomplete, skip uncategorised), mergeContinueConfig (replace-ours/keep-others/defaults), resolveContinueTarget (/v1). 6 unit tests; check:cli-i18n green. |
||
|
|
70bd6fbcc9 |
feat(cli): setup-kilo — configure Kilo Code for OmniRoute (CLI auth + VS Code settings) (#4284)
CLI #5 of the series. `omniroute setup-kilo` configures Kilo Code (kilocode.kilo-code, a Cline/Roo descendant) to use OmniRoute. Two surfaces (both written, matching the dashboard cli-tools/kilo-settings): - ~/.local/share/kilo/auth.json — CLI mode: auth["openai-compatible"] = { apiKey, baseUrl (WITH /v1 — Kilo appends /chat/completions), model }. - VS Code settings.json — extension: kilocode.customProvider (name/baseURL/apiKey) + kilocode.defaultModel. Only touched when the file already exists. Remote-aware (--remote/--api-key → active context → localhost). Model via --model or an interactive pick from /v1/models (Kilo's extension has no auto-discovery). Prints the exact UI settings to paste. Merges both files (preserves existing). Researched against current Kilo docs: confirmed openAiBaseUrl needs /v1 (unlike Cline's root url), the openai-compatible keys, and the export/import + CLI surfaces. Kilo's wire (/v1/chat/completions) already validated → "OK". Tests: buildKiloAuth (provider + /v1 + merge + key fallback), buildKiloVscodeSettings (kilocode.* keys + preserve), resolveKiloTarget (/v1 ensure, key win). 6 unit tests; check:cli-i18n green. |
||
|
|
6f16faa039 |
fix(models): keep vision capability for imported (synced) models (#4264) (#4283)
After importing a provider key, vision-capable models (OpenRouter models whose
architecture declares image input, and other synced providers) were shown as
text-only in /v1/models and the dashboard, even though image requests worked.
Root cause: SyncedAvailableModel never captured a vision flag, and the catalog's
OpenRouter live-enrichment block (which derives vision from architecture.input_modalities)
is skipped once a provider has synced models. So the synced path emitted no vision.
Fix (mirrors the existing supportsThinking capture):
- modelDiscovery.normalizeDiscoveredModels derives supportsVision via the new
detectVisionInput() from architecture.input_modalities, the string
architecture.modality ("text+image->text"), or a top-level input_modalities.
- SyncedAvailableModel gains supportsVision; the read-normalize path preserves it.
- catalog.ts emits capabilities.vision for synced models and merges (not clobbers)
capabilities when the model already exists.
TDD: tests/unit/openrouter-vision-sync-4264.test.ts — capture unit test + an
end-to-end /v1/models assertion (RED before, GREEN after).
Closes #4264
|
||
|
|
158a2246dd |
feat(cli): setup-cline — configure Cline for OmniRoute (CLI files + VS Code hints) (#4280)
CLI #4 of the series. Cline's VS Code extension keeps config in opaque VS Code globalStorage (not file-writable); its CLI/standalone mode reads ~/.cline/data/. `omniroute setup-cline`: - writes ~/.cline/data/globalState.json (act/planModeApiProvider=openai, openAiBaseUrl = ROOT url WITHOUT /v1 — Cline appends /v1/chat/completions — openAiModelId + planModeOpenAiModelId) and ~/.cline/data/secrets.json (openAiApiKey), both merged to preserve existing state. Matches the dashboard cli-tools/cline-settings schema. - remote-aware (--remote/--api-key → active context → localhost). - model resolved via --model or an interactive pick from /v1/models (Cline has no model auto-discovery). - prints the exact VS Code extension settings (Base URL/key/model) to paste, since the extension's storage can't be written directly. Researched against the current Cline docs (saoudrizwan.claude-dev): confirmed the openai-compatible keys, the Plan/Act split, and that openAiBaseUrl must be the ROOT (no /v1). Cline's wire (/v1/chat/completions) already validated → "OK". Tests: buildClineGlobalState (provider+root+model, merge-preserve), buildClineSecrets (key + placeholder), resolveClineTarget (/v1 strip, key win). 6 unit tests; check:cli-i18n green. |
||
|
|
550440f65f |
fix(providers): Cloudflare Workers AI discovery uses model names, not UUIDs (#4259) (#4282)
Cloudflare's /ai/models/search returns { id: "<uuid>", name: "@cf/..." } where
name is the callable slug and id is an internal UUID. The cloudflare-ai discovery
config passed the raw objects through (parseResponse: data.result), so buildResponse
used id (the UUID) as the model id — the dashboard/import listed UUIDs instead of
@cf/... model names. Map each result's name -> id (mirrors the gemini/huggingface/
clarifai parseResponse normalizers in the same map); falls through to the local
catalog on error so import never breaks.
TDD: tests/unit/cloudflare-models-uuid-4259.test.ts (RED on UUID ids -> GREEN on slugs).
Closes #4259
|
||
|
|
98b0d5e51e |
fix(sse): surface undici err.cause on dispatcher failure (#4281)
Surface err.cause + propagate diagnosable error fast on undici dispatcher failure. TDD. #4252. |
||
|
|
5c4b0e327d |
fix(cli): harden launch/launch-codex with free-claude-code patterns (#4278)
Applies proven patterns from the free-claude-code reference adapters: launch (Claude Code): - always set ANTHROPIC_AUTH_TOKEN — a no-auth sentinel when none is resolved — so newer Claude Code doesn't stop at its local login gate before contacting OmniRoute (an open backend ignores the value; ANTHROPIC_API_KEY stays stripped). launch-codex: - remote-aware: resolves the root base URL + auth from --remote/--api-key, the active context, then localhost (was localhost/--remote only). - inject the `omniroute` provider via `-c` flags (model_provider + base_url + env_key + wire_api=responses + requires_openai_auth=false) so it works WITHOUT a pre-existing ~/.codex/config.toml. - strip OPENAI_*/CODEX_* from the child env (defense-in-depth) and set OMNIROUTE_API_KEY to the token or a sentinel. (Honest note: this does NOT silence codex's refresh_token log noise — that comes from ~/.codex/auth.json, is cosmetic, and does not block requests.) - replace a hard-coded Tailscale IP in --remote help with a placeholder. Tests: buildCodexEnv (strip + sentinel + no-mutate), buildCodexProviderArgs (inline provider def), resolveCodexTarget; updated buildClaudeEnv sentinel test. Validated remotely vs VPS v3.8.30: `launch-codex --remote ... exec` → "OK". 19 unit tests pass; check:cli-i18n green. |
||
|
|
871d109066 |
feat(quality): cap test-file size (anti-reinflation Layer 1) — freeze god-tests, cap new at 800 (#4273)
Layer 1 anti-reinflation: cap test-file size (freeze god-tests, cap new at 800). Gate validated green against the full combined tree. |
||
|
|
1696944dce |
feat(cli): OpenCode setup commands (openai-compatible provider + remote-aware plugin) (#4277)
setup-opencode: remote-aware openai-compatible provider generator; API key referenced by env var (never on disk). 6 tests. |
||
|
|
b7414c21c5 |
ci(mutation): split over-budget batches by range/pair so every batch fits the job cap (#4272)
Split over-budget mutation batches by range/pair + union same-file mutants across sibling batches. CI-only + TDD. |
||
|
|
ec4d94f4c1 |
test(ci): reconcile release/v3.8.30 baseline + test drift (#4276)
Reconcile baseline + test drift on release/v3.8.30 (complexity, opaque surface, search count, tproxy addon). Round-robin left as a canary for the undici-dispatcher issue. |
||
|
|
3e6be47012 |
feat(cli): Claude Code launcher + setup commands (remote mode + profiles) (#4274)
Brings Claude Code to parity with the Codex CLI integration. - `omniroute launch` is now remote-aware: --remote <url>, --profile <name>, --api-key. Resolves base URL + auth from the active context (so `omniroute connect <vps>` then `omniroute launch` just works), accepts a bare port OR a full base URL, health-checks the (possibly remote) server, and sets CLAUDE_CONFIG_DIR for the chosen profile. - `omniroute setup-claude` (new): fetches the live /v1/models catalog and writes ~/.claude/profiles/<name>/settings.json per model. Claude Code has no native profile files, so CLAUDE_CONFIG_DIR is the idiomatic mechanism. Reuses the SAME profile names as setup-codex (glm52, kimi-k27, …) via the shared categoriseModel (now exported). The auth token is NEVER written to disk — launch injects it. - Docs: docs/guides/CLAUDE-CODE-CONFIGURATION.md + README index. i18n (en + pt-BR). - Tests: setup-claude profile generation (incl. "no token on disk"), buildClaudeEnv (port + URL + CLAUDE_CONFIG_DIR), resolveLaunchTarget. check:cli-i18n + check-docs-sync green; 13 unit tests pass. |
||
|
|
3517d935e4 |
docs(design): add OmniRoute design system and visual identity specification
Introduce a design system and visual identity plan to unify the OmniRoute dashboard and marketing site. The document covers color alignment, missing design tokens, and the implementation strategy for a consistent brand experience. |
||
|
|
8aa9d6330b |
chore(ci): align electron audit gate to root advisory policy (#4275)
Align electron audit gate to root policy (critical blocks, high warns). Un-blocks the Lint job on release/v3.8.30. |
||
|
|
c34d37a11e |
feat(cli): Codex CLI launcher + setup commands (#4270)
* feat(cli): Codex CLI launcher + setup commands Two new CLI subcommands mirroring the `launch`/`configure` pattern, for driving the OpenAI Codex CLI against OmniRoute: - `omniroute launch-codex` — boots OmniRoute (if needed) and launches Codex CLI pointed at it (local or remote VPS), with no manual env/config editing. - `omniroute setup-codex` — generates ~/.codex profile files from OmniRoute's live model catalog. Registered in the command registry; en.json + pt-BR.json locale sections added (keeps the cli-i18n-catalog top-level parity gate green). CODEX-CLI-CONFIGURATION.md refreshed. Consolidated from work-in-progress that was uncommitted on the shared checkout; reconstructed on release/v3.8.30. * chore(vscode): reduce git repo-detection overhead for nested worktrees Disable VS Code's git auto-repository-detection / submodule scan / autofetch so the Source Control view stops indexing the ~44 nested repos (worktrees + _references/* + _mono_repo/*), which caused constant "validating" churn. Only the root repo is tracked. Editor-only settings; no runtime impact. |
||
|
|
a5c0576c3b | chore(release): open v3.8.30 development cycle | ||
|
|
3c9883bb73 |
Release v3.8.29 (#4126)
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.v3.8.29 |
||
|
|
dd5a3db55e |
fix(docs): move DOCUMENTATION_OVERHAUL_PLAN out of the fumadocs guides collection (#4123)
A cycle-internal docs housekeeping commit (
v3.8.28
|
||
|
|
f165efcd0b |
Release v3.8.28 (#4053)
* 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> |
||
|
|
8842414d8a |
fix(docker): build release image with webpack (Turbopack internal panic) (#4052)
The v3.8.27 release Docker build failed on BOTH linux/amd64 and linux/arm64 with a non-recoverable Turbopack panic — `TurbopackInternalError: internal error: entered unreachable code: there must be a path to a root` in `ImportTracer::get_traces` (during issue reporting), at Dockerfile `RUN npm run build`. Deterministic (not transient), so a re-run does not help. The webpack build is the proven engine — `build:release` (deployed to the VPS), the CI `Build` job, and `npm run build:cli` all use it and are green. Switch the Docker build to webpack (OMNIROUTE_USE_TURBOPACK=0); re-enable once the upstream Turbopack tracer bug is fixed. Documented in QUALITY_GATE_PLAYBOOK Parte 6.v3.8.27 |
||
|
|
fa367dd99e |
Release v3.8.27 (#3968)
* chore(release): open v3.8.27 development cycle * fix(security): polynomial ReDoS in comboAgentMiddleware regex (#3982) * fix(security): eliminate polynomial ReDoS in comboAgentMiddleware <omniModel> regex (CodeQL js/polynomial-redos) CACHE_TAG_PATTERN wrapped the tag in an unbounded `(?:\\n|\n|\r)*` prefix/suffix. On an unanchored `.test()`/`.exec()` that is O(n²) on inputs with many newlines (CodeQL js/polynomial-redos, alerts #612/#613). The surrounding runs are irrelevant to detecting/capturing the tag, so the detection pattern now matches only the core `<omniModel>([^<]+)</omniModel>`; the global strip pattern still consumes the wrapping newlines (combo.ts streaming, #531) but BOUNDED ({0,16}) so it stays linear. Behavior preserved: detection, model extraction, multi-tag stripping (#454) and blank-line cleanup all unchanged (107 related tests green). Adds ReDoS-safety regression tests (50k-newline inputs complete in <1ms). * docs(changelog): add #3982 ReDoS fix to [3.8.27] * ci(security): harden workflows — artipacked persist-credentials + cache-poisoning + SC2086 (#3965) * Refine provider quota card display (#3969) Integrated into release/v3.8.27 * feat: add sidebar group separator toggles (#3971) Integrated into release/v3.8.27 * Gate control-plane proxy direct fallback (#3963) Integrated into release/v3.8.27 * Capture actual upstream provider requests (#3941) Integrated into release/v3.8.27 * ci(quality): flip require-tighten + osv + Trivy to blocking (v3.8.27 cycle-end) (#3984) * fix(resilience): respect connection cooldown stored as numeric epoch (#3954) (#3995) rate_limited_until is a TEXT column, but setConnectionRateLimitUntil (Antigravity full-quota path) persists a raw epoch number that SQLite coerces to a numeric string ("1781696905131.0"). The selection predicate isAccountUnavailable then did new Date("1781696905131.0") -> NaN, so the cooling connection was never skipped and the router kept dispatching to rate-limited accounts. Normalize numeric-epoch strings (and number/Date/ISO) via a shared cooldownUntilMs() helper in isAccountUnavailable / getEarliestRateLimitedUntil / filterAvailableAccounts / parseFutureDateMs. ISO behavior preserved. * fix(providers): fetch live /models for LLM7 and BytePlus (#3976) (#3996) llm7 and byteplus carry a real modelsUrl but were not classified by any live-fetch branch of the model-import route, so their hardcoded 4-entry registry catalog was served (source local_catalog) instead of the upstream catalog. Add both to NAMED_OPENAI_STYLE_PROVIDERS so the route probes <baseUrl>/models and serves the live list, falling back to the local catalog only on fetch failure. * fix(dashboard): logs auto-refresh reads live visibility, not a stale mount ref (#3972) (#3997) The auto-refresh interval gated each tick on visibleRef, seeded once at mount and updated only by a visibilitychange event. A tab mounted while document.visibilityState is 'hidden' (background load, bfcache, embedded/proxied webviews) with no later visibilitychange left the ref false forever, so the interval ticked but never fetched — only the manual button worked. Read the live document.visibilityState in the tick instead. * feat(compression): add Indonesian caveman rules and language pack (#3975) Integrated into release/v3.8.27 (cherry picked from commit |
||
|
|
c9b5b1a892 |
feat(compression): add Indonesian caveman rules and language pack (#3975)
Integrated into release/v3.8.27 |
||
|
|
7509a32e9d |
fix(security): polynomial ReDoS in comboAgentMiddleware regex → main (#3983)
Brings the release/v3.8.27 fix (#3982) to main so CodeQL alerts #612/#613 close on the next scan. Code + regression test only; the [3.8.27] CHANGELOG bullet lives on release/v3.8.27 and reaches main when v3.8.27 ships (identical file → no merge conflict). Detection pattern drops the unbounded surrounding newline run; global strip pattern bounds it ({0,16}). Behavior unchanged (107 related tests green). |
||
|
|
ca1e17f740 |
test(opencode-plugin): ESM default-export test (#3967)
The plugin became ESM-only when the CJS bundle was dropped to fix the OpenCode loader (#3883), so tests/scaffold.test.ts's 'CJS default export resolves via require()' test fails at publish time with 'Cannot find module ../dist/index.cjs' (it only runs in the npm-publish opencode-plugin job, so the cycle never caught it). Replaced with an ESM import of the built dist/index.js asserting the same v1 { id, server } shape; dropped the now-unused createRequire import. omniroute@3.8.26 itself already published fine. |
||
|
|
d59cd14391 |
fix(ci): electron-release publish-npm contents:write (#3966)
The v3.8.25→v3.8.26 #3874 fix bumped npm-publish.yml's publish job to contents:write (gh release upload for the SBOM). electron-release.yml calls that workflow as a reusable job (publish-npm) but only granted contents:read — a reusable job cannot request more than the caller grants, so GitHub rejected the v3.8.26 electron run at startup (startup_failure). Aligns the caller permission to contents:write. |
||
|
|
4d21044ba5 |
fix(release): post-merge quality gates to main for v3.8.26 (#3964)
Cherry-picks #3961 + #3962 from release/v3.8.26 to main (parity before tagging).v3.8.26 |
||
|
|
81a37b67ed |
Release v3.8.26 (#3875)
OmniRoute v3.8.26 — see CHANGELOG.md [3.8.26] for the full notes. Highlights: Vertex AI media generation (#3929), GLM-5.2 effort-tier routing (#3885), sticky round-robin combos (#3846), OpenRouter connection presets (#3878), compression prompt-cache fix (#3936/#3890), and a security pass (form-data/vite + workflow hardening, #3949). Co-authored-by: artickc <artickc@users.noreply.github.com> Co-authored-by: rdself <rdself@users.noreply.github.com> Co-authored-by: herjarsa <herjarsa@users.noreply.github.com> Co-authored-by: Jack Smith <16862258+YunyunZhai@users.noreply.github.com> Co-authored-by: dhaern <dhaern@users.noreply.github.com> Co-authored-by: adivekar-utexas <adivekar-utexas@users.noreply.github.com> Co-authored-by: megamen32 <megamen32@users.noreply.github.com> Co-authored-by: zhiru <zhiru@users.noreply.github.com> Co-authored-by: insoln <insoln@users.noreply.github.com> Co-authored-by: diego-anselmo <diego-anselmo@users.noreply.github.com> |
||
|
|
1f87a9589c |
deps: bump electron from 42.3.3 to 42.4.0 in /electron (#3914)
electron 42.3.3->42.4.0; rebased onto main after #3913 to resolve the electron/package-lock.json conflict. /electron-only, Build green. |
||
|
|
f5706a6528 |
deps: bump electron-builder from 26.15.2 to 26.15.3 in /electron (#3913)
electron-only dependency bump; Build green, reds are pre-existing main-wide (mid-cycle). Verified the PR touches only electron/package*.json. |
||
|
|
4066a2ca31 |
fix(ci): grant contents:write to npm publish job for SBOM attach (#3874)
Post-release v3.8.25 CI hotfix — SBOM attach needs contents:write. |
||
|
|
35dbf0eea1 |
Release v3.8.25 (#3866)
* chore(release): continue v3.8.25 development cycle after main code-sync (r5) main fast-forwarded to release/v3.8.25 (#3863): unblocked Build+Docker via #3864, plus #3837 (mimocode proxy) and #3862 (trivy bump). This marker re-opens the umbrella PR for further v3.8.25 work. No version bump. * fix(db): persist the Keep-latest-backups retention setting (#3834) (#3867) * fix(oauth): clear GitLab Duo setup message instead of 500 (#3861) (#3868) * test(oauth): prove refresh_token preserved on real gemini-cli/antigravity dispatch (#3850) (#3869) * feat(compression-ui): unified compression config UI — per-engine pages + combos editor + menu + WS default-on (#3860) Integrated into release/v3.8.25 — feat(compression-ui): unified compression configuration UI (Compression Hub + per-engine Lite/Aggressive/Ultra pages + combos editor + sidebar entry + live-WS default-on). File-size re-baselined for sidebarVisibility.ts/chatCore.ts growth; orphan ws test relocated to a collected path. * docs(changelog): complete the v3.8.25 release notes + credit all contributors Audited every commit since v3.8.24 and filled the gaps the [3.8.25] section was missing: a New Features section (compression engines + Compression Studios #3848, compression UI #3860, injection-guard #3857, kiro discovery #3836, Veo #3839, mimocode proxy #3837, Arena ELO flag #3821), 9 more Fixed entries (#3811/#3807/#3759/#3849/#3838/#3835/#3814/#3820/#3819), a Security section (CCR IDOR #3859, supply-chain #3824), and an Internal/Quality section. Every contributor and issue reporter is now credited. * docs(changelog): restore + complete the v3.8.25 release notes Re-adds CHANGELOG.md (a prior server-side commit accidentally dropped it) with the complete, audited [3.8.25] section: New Features, the full Fixed list, Security & Hardening, and Internal/Quality — every contributor and issue reporter credited. * chore(release): finalize v3.8.25 — reconcile CHANGELOG + i18n mirrors, document OMNIROUTE_MAX_PENDING_MIGRATIONS, green the unit suite Release-gate reconciliation for v3.8.25: - CHANGELOG: dated 2026-06-14, linked #3826, rolled up file-size re-baselines (#3823/#3833), recorded the test-greening; re-synced all 41 i18n CHANGELOG mirrors. - Documented OMNIROUTE_MAX_PENDING_MIGRATIONS (#3416) in .env.example + ENVIRONMENT.md. - Greened the unit suite (was merged red on 4 CI shards): aligned 10 stale tests to this cycle's intended behavior (#3838/#3822/#3501/SOCKS5/Vertex-Express/Antigravity) and the same-provider 503 fall-through test; de-flaked the compression benchmark reproducibility and ServiceSupervisor crash tests. No production code changed. * ci(security): clear OpenSSF Scorecard code-scanning noise + harden workflow token permissions The Security tab held 155 open alerts, ALL from the advisory OpenSSF Scorecard tool (#3824) — supply-chain/posture scores, not code vulnerabilities — which drowned out real CodeQL findings. - scorecard.yml: stop uploading SARIF to the code-scanning tab (drop the upload-sarif step + the now-unused security-events: write). The run still produces the OpenSSF badge (publish_results) and a downloadable SARIF artifact. - TokenPermissions hardening (the high-severity, genuinely-valuable subset): set each workflow's top-level token to read-only and grant the exact writes at the job level that needs them — npm-publish (id-token/packages on publish jobs), docker-publish (packages on build), electron-release (contents on build/release, id-token/packages on publish-npm), build-fork (packages on build), claude (empty top-level; job grants its own). The 155 existing alerts were dismissed. Not adopting repo-wide SHA-pinning (143 PinnedDependencies advisories) — declined. * test(integration): align stale wiring/socks5 integration tests to this cycle's behavior These were red on the CI Integration job (pre-existing). No production code changed: - integration-wiring: the combos page no longer renders a per-page EmailPrivacyToggle (#3822 consolidated it into Settings → Appearance); the provider-detail test-result masking and upstream-proxy copy moved to decomposed components (#3501 BatchTestResultsModal / UpstreamProxyCard) — assertions now read the owning files. - api-routes-critical: SOCKS5 is now enabled by default (opt-out), so the disabled- rejection test must set ENABLE_SOCKS5_PROXY=false explicitly (an unset env now means enabled). (The ~32 live-Gemini integration tests are gated on OMNIROUTE_API_KEY and skip in CI; they only 'fail' locally when that key is present without a running server.)v3.8.25 |
||
|
|
b4180145e6 |
Merge release/v3.8.25 into main (#3863)
Code-sync release/v3.8.25 → main: unblocks main Build + Docker Hub (#3864 SUPPLY_CHAIN.md frontmatter) + mimocode per-account proxy (#3837) + trivy-action bump (#3862). i18n CHANGELOG drift left for /generate-release. Dev continues on release/v3.8.25. |
||
|
|
36baf77ad5 |
chore(deps): bump aquasecurity/trivy-action (#3862)
Integrated into release/v3.8.25 — chore(deps): bump aquasecurity/trivy-action 0.28.0→0.36.0 (supply-chain scan action, #3824 workflow). |