Commit Graph

4573 Commits

Author SHA1 Message Date
Diego Rodrigues de Sa e Souza
21c1f540b3 feat(compression): relevance extractive engine — roadmap #7 (#5289)
* test(compression): failing tests for relevance engine scorer + apply

* feat(compression): relevance extractive engine — scores sentences against last user query

* test(compression): regression tests for relevance engine review fixes (threshold/multimodal/force-preserve/whitespace/query)

* fix(compression): relevance engine hardening from core review

- overlapThreshold was dead config (|| kept<budget made it unreachable) → real threshold gate
- multimodal: only compress when exactly one text block (was stamping joined text into every block)
- force-preserve sentences are now 'free' (don't consume budget) so they can't starve top-relevance
- preserve inter-sentence whitespace (\n\n survives) instead of flattening to single spaces
- ROOT-CAUSE: stop gating preserve on ultraHeuristic FORCE_PRESERVE_RE — it matches the period
  ending every sentence, so it force-preserved everything (no-op). New SENTENCE_PRESERVE_RE anchors
  on real signals (digits/URL/Error:/code/at-frame/path/key=value), mirroring #17's UNIT_PRESERVE_RE
- core-review suggestion to skip the query message: rejected (self-overlap already protects it;
  skipping would no-op the single-message RAG case) — documented + test updated

* docs(changelog): restore relevance engine bullet (#7, eaten by rebase)
2026-06-29 01:25:41 -03:00
Diego Rodrigues de Sa e Souza
fa94f38de5 feat(compression): hard-budget mode (compress to ≤ N tokens) — roadmap #17 (#5288)
* test(compression): TDD failing tests for hard-budget post-pass (#17)

node:test suite for applyHardBudget — targetTokens, targetRatio, no-op,
force-preserve, determinism, both-wins, techniquesUsed, integration seam.
All tests fail (module not yet created) — TDD red step.

* feat(compression): hard-budget post-pass (#17) — compress to exactly N tokens

- types.ts: add targetTokens? and targetRatio? to CompressionConfig after contextBudget
- hardBudget.ts: applyHardBudget(body, {targetTokens?,targetRatio?}) → CompressionResult;
  splits prose into sentences/lines, ranks by avg scoreToken ascending, drops lowest-
  saliency units until ≤ target; UNIT_PRESERVE_RE guards numbers/URLs/errors/code;
  targetTokens wins when both set; techniquesUsed:["hard-budget"]
- strategySelector.ts: hard-budget post-pass in runStackedCompression +
  runStackedCompressionAsync after engine loop, before finalizeStackedResult;
  gated on config.targetTokens || config.targetRatio; mergeStackStep + compressed=true

* fix(compression): preserve digit-less sensitive lines in hard-budget

UNIT_PRESERVE_RE only matched digits/URLs/error-headers/code-fences, so
stack-trace at-frames, key=value credential lines, and digit-less paths
were droppable and could be cut to hit the budget. Add specific anchors
(^\s*at\s, \/[\w.-]+\/, [A-Za-z_]\w*=\S) that never match a bare
end-of-sentence period (which would re-break the feature into a no-op).

Regression tests drive each unit to target=1 (drops every non-preserved
unit) and assert the sensitive line survives; plus a guard that plain
prose ending in a period stays droppable.

* fix(compression): distribute hard-budget across messages + warn when unreachable

Two related correctness fixes in applyHardBudget:

- Aggregate target was passed verbatim to compressText for EACH message,
  so an N-message body could come back ~N× over budget. Distribute the
  target proportionally per message (floor(target * msgTokens/total)) so
  the SUM stays <= target.

- When every unit is preserve-guarded (or a single oversized preserved
  unit), the result still exceeds target with no signal. Measure the
  result and push a validationWarnings entry when it remains over budget,
  so callers are not silently left over the limit.

Regression tests: a 4-message body with target 200 ends with TOTAL <= 200;
an all-numeric (all-preserved) body emits the 'could not reach target'
warning.

* fix(compression): run hard-budget post-pass when targetTokens/targetRatio is 0

The seam gate used `||`, so targetTokens:0 or targetRatio:0 (both falsy)
silently skipped the post-pass in runStackedCompression and
runStackedCompressionAsync. Switch to `!= null` so an explicit 0 still
engages the pass.

Regression test: applyStackedCompression with config.targetTokens:0 must
report 'hard-budget' in techniquesUsed.

* docs(changelog): restore hard-budget bullet (#17, eaten by rebase)
2026-06-29 01:24:38 -03:00
Diego Rodrigues de Sa e Souza
c8833fd3e0 fix(dashboard): keep disabled no-auth providers visible with in-place enable (#5183)
Partition no-auth entries instead of dropping blocked ones; disabled no-auth providers are surfaced in a Disabled group with an in-place Enable button (#5166/#5183). Extracted NoAuthProvidersSection to keep page.tsx under its size freeze.

Admin-merged: the only red checks (Unit/Coverage shard 2/8, Node-compat) are the pre-existing #4076 Dockerfile heap-ordering base-red on main — unrelated to this change (proven: fails locally on a branch that does not touch the Dockerfile or that test) and already de-brittled in the v3.8.40 release line.
2026-06-29 00:39:30 -03:00
Diego Rodrigues de Sa e Souza
43da1bb98c feat(compression): result memoization for deterministic engines (opt-in) — roadmap #21 (#5286)
roadmap #21: result memoization for deterministic engines (opt-in, default off). Fixed the cache-key under-specification (folded model+supportsVision so lite image-strip can't serve a wrong cached body across vision/non-vision targets) + regression test. Locally validated release-green. Integrated into release/v3.8.40.
2026-06-29 00:28:52 -03:00
Diego Rodrigues de Sa e Souza
a71157a1b8 feat(compression): inline transparency annotation — roadmap #18 (#5284)
roadmap #18: inline transparency annotation. Fixed a live 500 (→/× in the X-OmniRoute-Compression latin-1 header → ByteString throw) → ASCII-only + regression test building real Headers/Response. Locally validated release-green. Integrated into release/v3.8.40.
2026-06-29 00:24:23 -03:00
Diego Rodrigues de Sa e Souza
72f8bf21f1 feat(compression): saliency heatmap in the compression studio — roadmap #13 (#5285)
roadmap #13: saliency heatmap in the compression studio (opt-in, dry-run). Locally validated release-green (typecheck + heatmap tests + file-size). Integrated into release/v3.8.40.
2026-06-29 00:21:13 -03:00
Diego Rodrigues de Sa e Souza
9a89158414 feat(logging): call-log correlation ID (safe core subset of #5275) (#5279)
Call-log correlation ID core (migration 109 + storage + X-Correlation-Id header) — safe subset of #5275. Original work by @hartmark. Integrated into release/v3.8.40.
2026-06-29 00:19:09 -03:00
backryun
f5445a2e5e fix(providers): drop retired chatgpt-web/codex models (#5280)
Drop retired chatgpt-web/codex models (gpt-5.2, gpt-4.5) — registry/map/UI/docs/tests kept aligned. Integrated into release/v3.8.40.
2026-06-29 00:17:14 -03:00
Diego Rodrigues de Sa e Souza
f862074209 feat(compression): composite-command splitter for RTK detection — roadmap #16 (#5283)
roadmap #16: composite-command splitter for RTK detection (opt-in, fail-open). Integrated into release/v3.8.40.
2026-06-29 00:16:14 -03:00
Diego Rodrigues de Sa e Souza
9b0adabb17 fix(mcp): break tools.ts ↔ toolSearch.ts cycle (check:cycles red on release) (#5282)
Break the tools.ts ↔ toolSearch.ts import cycle (check:cycles red on release) via a leaf toolDefinition.ts. Integrated into release/v3.8.40.
2026-06-29 00:13:44 -03:00
fulorgnas
57f81f60d5 fix(grok-cli): strip unsupported params (presencePenalty, etc.) before sending (#5273)
Strip unsupported sampling params (presencePenalty/frequencyPenalty/logprobs/topLogprobs) before forwarding to Grok Build. Added regression test (Rule #18, TDD-verified). Integrated into release/v3.8.40.
2026-06-28 22:20:38 -03:00
Randi
1fd19c45ec fix(authz): require auth for v1beta client API (#5274)
Security: close /v1beta authz bypass — classify /v1beta + /api/v1beta aliases as CLIENT_API so Bearer auth is enforced. Validated authz TDD suite (classify/proxy-contract/pipeline/inventory). Integrated into release/v3.8.40.
2026-06-28 22:17:38 -03:00
Diego Rodrigues de Sa e Souza
5ac4637816 docs: reorganize docs/ + accuracy audit + drop Node 20 (rebased onto current release tip) (#5262)
* docs: move superpowers/research artifacts to isolated _tasks repo + docs tree cleanup

- Move docs/superpowers/{plans,specs} and docs/research/* into the gitignored,
  separately-versioned _tasks/ repo; untrack the two tracked research design docs.
- Add CLAUDE.md "Planning & Research Artifacts" section overriding the superpowers
  default save paths (docs/... -> _tasks/...); align REPOSITORY_MAP and
  DOCUMENTATION_OVERHAUL_PLAN with the new convention.
- Drop 4 now-obsolete /api/discovery/* entries from check-docs-symbols allowlist
  (stale-enforcement) and refresh code/spec path comments to _tasks/...
- Sweeps in concurrent docs-tree restructuring (root-level provider/guide docs,
  compression spec cleanup, .mcp.json.example removal).

* docs: reorganize docs/ tree + fix stale facts across ~26 docs

Phase A — reorganization:
- Move 7 orphan root docs into subfolders (providers/ created; TIERS+USAGE_QUOTA→guides/;
  plugins+PLUGIN_SDK→frameworks/); delete 8 obsolete/redundant docs (SUBMIT_PR superseded
  by CONTRIBUTING; DOCUMENTATION_OVERHAUL_PLAN; INCIDENT_RESPONSE/PERF_BUDGETS/THREAT_MODEL;
  3 ops snapshots). Rebuild README index (was missing ~40 files) + per-folder meta.json nav.
- Clean 14 dangling doc-path references in bin/ ops scripts, scripts/, workflow, tests;
  fix the dockerignore-docs-coverage required-docs path (PROVIDERS→providers/CLAUDE_WEB).

Phase B — content accuracy (verified against code, not the audit summary):
- Functional: ENVIRONMENT flag defaults (INPUT_SANITIZER/MCP_ENFORCE_SCOPES=true,
  COMPRESS_DESCRIPTIONS=false, dynamic heap); MCP-SERVER notion tool names (omniroute_*→
  notion_*) + counts 87→94; coverage gate 75/70→60/60/60/60 (RELEASE_CHECKLIST, COVERAGE_PLAN,
  ERROR_SANITIZATION, CONTRIBUTING); pre-push hook description; regenerate PROVIDER_REFERENCE (237).
- Count drift: providers 237, executors 70, migrations 106, db modules 94, oauth 19,
  strategies 17, MCP 94, flags 38, TS 6.0, open-sse ~900/services 294 across architecture/
  frameworks/ops docs; AUTO-COMBO 9→12 factors w/ correct DEFAULT_WEIGHTS; REASONING +2
  patterns; STEALTH UA defaults; AGENT_PROTOCOLS +cursor-cloud/list-capabilities;
  LANGUAGE_PACKS +id pack.
- Kept Node 20 (runtime guard accepts 20.20.2+; only engines is stricter) and MCP scopes=13
  (mcpScopes.ts) — both were correct in the docs; corrected only the attribution.

* docs: finish content refresh — compression engines, CLI_TOKEN merge, metadata sweep

- Compression: document the additional built-in engines (CCR, headroom, ionizer,
  session-dedup) in COMPRESSION_ENGINES; clarify LLMLingua-2 is the ultra-mode SLM
  backend + cross-ref the extra engines in EXTENDING_COMPRESSION; add the id
  (Indonesian) language pack to LANGUAGE_PACKS.
- AUTO-COMBO: replace the orphan 'How tiers fit' weight table (stale weights) with a
  pointer to the canonical 12-factor DEFAULT_WEIGHTS table.
- Security: merge CLI_TOKEN_AUTH.md (legacy 32-char SHA-256 format) into CLI_TOKEN.md
  as a 'Legacy format — still accepted' section (server accepts both HMAC + legacy),
  delete CLI_TOKEN_AUTH.md, drop it from the index + security nav.
- Metadata: bump stale frontmatter (version/lastUpdated) to 3.8.40/2026-06-28 across the
  doc set audited this pass, and normalize the in-body 'Last updated' header lines to match.

* fix(runtime): drop Node 20 from supported range + align all docs/diagrams/counts

- Node minimum is now 22 (aligned with package.json engines). SUPPORTED_NODE_RANGE in
  src/shared/utils/nodeRuntimeSupport.ts (and the bin/ mirror) drops the 20.x line →
  '>=22.22.2 <23 || >=24.0.0 <27'; getNodeRuntimeSupport now rejects Node 20 as
  unsupported-major. Test updated (TDD): node-runtime-support.test.ts asserts Node 20
  rejected. Docs aligned (TROUBLESHOOTING ×2, TERMUX, RELEASE_CHECKLIST, CODEBASE,
  CLI-TOOLS, README, llm.txt + 42 i18n llm.txt mirrors, skills/cli-serve).
- Diagrams regenerated: mcp-tools-87 -> mcp-tools-94 (34 base + pool 6 = 94) and
  auto-combo-9factor -> auto-combo-12factor (correct DEFAULT_WEIGHTS); SVGs re-rendered
  via mermaid-cli; doc refs + diagrams/README updated; fixed a pre-existing broken
  resilience-3layers image path.
- CLAUDE.md + AGENTS.md aligned to real counts (237 providers, 94 MCP tools / 34 base,
  106 migrations, 94 db modules, 12-factor auto-combo, 17 strategies); README provider
  count 231 -> 237; executor count corrected to 68 (provider executors, excl base/index)
  and OAuth to 18 across architecture docs. check:docs-all now passes (0 strict drift,
  0 broken links); removed dead .mcp.json.example doc link.

* fix(services): update installer Node hint to >=22.22.2 (aligned with dropped Node 20)

* docs: realign counts to current release tip after rebase

The release tip advanced while this work was in flight (Gemini CLI provider/executor
removed by #5246, plus other PRs). Re-counted against the current code and updated:
providers 237->236, executors 68->67, OAuth modules 18->17, open-sse services 294->298;
regenerated PROVIDER_REFERENCE.md (236). check:docs-all passes (0 strict drift).

* docs(changelog) + i18n: record Node 20 drop + fix nodeIncompatibleHint

- CHANGELOG: add [3.8.40] entries for the Node 20.x removal (runtime) and the docs
  reorganization/accuracy audit.
- i18n: nodeIncompatibleHint across all 42 locales no longer lists Node 20.x as
  supported (ASCII + CJK full-width variants), aligned with the dropped Node 20.

* fix(docs): repair CI breakages from the doc moves

- test: cli-plugin-system asserted docs/dev/plugins.md exists; the file moved to
  docs/frameworks/PLUGINS.md — point the test at the new path (Unit fast-path 2/2 fix).
- frontmatter: PLUGINS.md and PLUGIN_SDK.md moved into the fumadocs-indexed
  docs/frameworks/ which requires a 'title' frontmatter; the missing frontmatter
  failed the Next.js MDX build (dast-smoke 'invalid frontmatter'). Added frontmatter
  to both, plus the providers/ docs (consistency; that folder is not indexed).
2026-06-28 22:07:18 -03:00
Diego Rodrigues de Sa e Souza
898aac5771 feat(compression): QuantumLock cache-prefix stabilization (opt-in, default off) (#5260)
roadmap #11: QuantumLock cache-prefix stabilization (opt-in, default off). Locally validated release-green (typecheck + file-size + 47 quantumLock/analytics tests; restored withRiskGate import + preserved release CHANGELOG bullets). Integrated into release/v3.8.40.
2026-06-28 21:59:46 -03:00
Diego Rodrigues de Sa e Souza
9dc31c7e08 feat(compression): RTK semantic command-output renderers (opt-in) — roadmap #10 (#5268)
roadmap #10: RTK semantic command-output renderers (opt-in). Locally validated release-green (typecheck + file-size + 22 RTK/analytics tests). Integrated into release/v3.8.40.
2026-06-28 21:58:02 -03:00
Diego Rodrigues de Sa e Souza
bc00e32b15 fix(compression): record no-op compression runs so Stacked is visible in analytics (#4268) (#5277)
A compression_analytics row was written only on a net-positive saving
(chatCore: result.compressed || fallbackApplied || cavemanOutputModeApplied).
A Stacked RTK->Caveman pipeline that ran on already-compact context saved
nothing -> no row -> indistinguishable from 'never dispatched'
(byMode.stacked.count stayed flat while Ultra climbed).

Attempted-but-no-op runs are now recorded via writeCompressionSkip with a
skip_reason. A new skip_reason column (added through the existing
ensureCompressionAnalyticsColumns ALTER path) marks these rows; the analytics
summary excludes them from every saving aggregate (via a single successWhere =
'skip_reason IS NULL' filter, so historical totals/averages are byte-identical)
and surfaces them as per-mode 'skipped' plus totalSkipped / bySkipReason. The
Mode Breakdown shows 'N skipped (no-op)'.

TDD: compressionAnalytics.test.ts gains coverage that skip rows are counted
separately without polluting saving aggregates, and that a skip-only mode still
appears (count 0, skipped > 0). Fail-before proven by reverting the successWhere
exclusion (both new tests fail). typecheck:core + eslint clean; 17/17 suite green.
2026-06-28 21:53:48 -03:00
Diego Rodrigues de Sa e Souza
ba38e31ff4 fix(cli): wire --tray Unix path through the runtime systray2 loader (#4605) (#5276)
The wired tray path (serve.mjs -> tray/index.mjs -> traySystray.mjs) loaded
systray2 via an inline loader calling require("module") inside an ESM .mjs file
(package type:module), which throws ReferenceError: require is not defined and
was silently swallowed -> no tray, no diagnostic (regressed in v3.8.34). Even
when fixed, systray2 is not in node_modules; it is lazily installed into
~/.omniroute/runtime by trayRuntime.ts.

initSystrayUnix now delegates loading to trayRuntime.ts::loadSystray (async),
fixes the icon path (icon.png, not icons/icon.png), sets isTemplateIcon:false
(full-color icon rendered as a white square under macOS template mode), and
serve.mjs surfaces tray-start failures to stderr instead of swallowing them.

TDD: tray-systray-loader-4605.test.ts drives the loader seam via an injected
ctor (native binary can't run in unit tests) and asserts the menu/icon/template
invariants; fail-before confirmed by restoring the broken require() loader.
2026-06-28 21:39:05 -03:00
Diego Rodrigues de Sa e Souza
99b17f13b6 feat(mcp): omniroute_tool_search + one-line TS signatures — roadmap #4 (#5269)
roadmap #4: omniroute_tool_search + one-line TS signatures. Integrated into release/v3.8.40.
2026-06-28 21:30:23 -03:00
KooshaPari
35f373e10f fix(qoder): decouple shared job token exchange abort (#5265)
Integrated into release/v3.8.40
2026-06-28 20:54:28 -03:00
Diego Rodrigues de Sa e Souza
598bebae26 fix(agent-bridge): unwrap cloudcode-pa .request envelope for Antigravity IDE (#4294) (#5267) 2026-06-28 20:21:48 -03:00
Diego Rodrigues de Sa e Souza
2b0c129ab6 fix(dashboard): add GitHub releases fallback to Update Available lookup (#4100) (#5266) 2026-06-28 20:04:24 -03:00
fulorgnas
bce5d17e3c fix(grok-cli): accept full auth.json object in import-token endpoint (#5258)
Integrated into release/v3.8.40 — grok-cli import-token accepts full auth.json object; zod z.record key-type fixed; duplicate Docker hardening dropped (already in release).
2026-06-28 19:30:47 -03:00
KooshaPari
2513934877 fix(responses): keep shell tool caller-side (#5256)
Integrated into release/v3.8.40 — shell tool kept caller-side in Chat→Responses translation (complements #5250).
2026-06-28 19:19:21 -03:00
KooshaPari
8adb40e8d1 fix(proxy): scope fallback cache by target url (#5261)
Integrated into release/v3.8.40 — proxy fallback cache scoped by target URL (prevents cross-endpoint poisoning).
2026-06-28 19:15:46 -03:00
Diego Rodrigues de Sa e Souza
04ae5a72f7 feat(kilocode): anonymous no-auth access to free models (#4019) (#5259)
* feat(kilocode): anonymous no-auth access to free models (#4019)

Kilo's gateway serves its free tier without signup: an OpenAI-compatible
request to api.kilo.ai/api/openrouter authenticated with the literal API
key `anonymous` (Authorization: Bearer anonymous) plus an
X-KILOCODE-EDITORNAME header returns free models. Expose it the same way
opencode/mimocode do:

- flag the kilocode dashboard provider `anonymousFallback: true` so a
  request with no connected account synthesizes a noauth credential;
- add `anonymousApiKey` to the registry and have DefaultExecutor send it
  as the bearer token only when no real credential exists, so the OAuth
  paid path is untouched.

Regression test exercises buildHeaders for the anonymous, OAuth-token and
API-key cases plus the anonymousFallback flag (fails before the fix).
Reference implementation pointer courtesy of @Theadd (#4019).

* chore(quality): reconcile inherited file-size base-red for executor-codex.test.ts

The release tip carries tests/unit/executor-codex.test.ts at 1347 lines while
the frozen baseline still reads 1340 — 7 legit lines were added without
ratcheting, so check:file-size fails for every PR that branches off the tip
(proven: the gate already fails on HEAD~1, before this PR's #4019 commit).
Ratchet the testFrozen entry to the current count to unblock; the file itself
is untouched by this PR.

* test: regenerate provider translate-path golden for kilocode editor-name header (#4019)

The #4019 commit adds X-KILOCODE-EDITORNAME to the kilocode registry headers.
The all-providers translate-path golden snapshots per-provider headers, so it
must be regenerated. Diff is exactly the new header on kilocode's apiKey/oauth/
nonStream variants — Authorization (Bearer <TOK>) and every other provider are
unchanged.
2026-06-28 19:04:52 -03:00
yuna amelia
29321452a7 fix(deps): resolve install warnings and audit vulnerabilities (#5252)
Integrated into release/v3.8.40 — deprecated stub types removed, Trivy ignore-unfixed, js-yaml override; allowlist + file-size baseline reconciled.
2026-06-28 18:44:43 -03:00
Randi
c779be6532 fix: preserve provider request headers in logs (#5257)
Integrated into release/v3.8.40 — provider request headers preserved in logs; verified combo reads native Response.headers (no regression).
2026-06-28 18:43:51 -03:00
KooshaPari
b87b9abe75 fix(proxy): shorten fast-fail negative health cache (#5255)
Integrated into release/v3.8.40
2026-06-28 18:12:58 -03:00
Randi
fa7f81cf0b Remove discontinued Gemini CLI channel (#5246)
Integrated into release/v3.8.40 — Gemini CLI channel removed; Antigravity-path parseTextualReasoningTags guard restored (regression caught + fixed in-place, all antigravity/gemini tests green).
2026-06-28 18:12:54 -03:00
KooshaPari
503b1205c6 fix(codex): drop local_shell tool (no longer supported upstream) (#5250)
Integrated into release/v3.8.40 — codex local_shell drop validated (merge-result: eslint clean, 41/41 tests). FQG failure was stale base.
2026-06-28 17:32:10 -03:00
KooshaPari
cf8c161a8d fix(qoder): coalesce concurrent job token exchanges (#5254)
Integrated into release/v3.8.40
2026-06-28 17:28:02 -03:00
Diego Rodrigues de Sa e Souza
dbe44bb417 fix(combo): wire session stickiness into round-robin dispatch (#3825) (#5248)
sessionStickiness.ts (v3.8.36) keeps a sessionless multi-turn conversation pinned
to the same connection so the upstream prompt-cache stays warm, but the
round-robin handler (handleRoundRobinCombo) returned before reaching the
applySessionStickiness call used by the weighted/priority paths. Clients that send
no session id (Codex CLI, Claude Code, most OpenAI-compatible tools) therefore had
round-robin combos rotate to a different connection every turn -> prompt-cache
miss -> cold high-reasoning starts, intermittent 504s and throughput collapse
under concurrency (#3825).

Reuse the existing mechanism: when a sticky connection is bound to the
conversation and present in the current targets, start the round-robin rotation at
it (failover to the other targets is preserved), and (re)record the binding on
success. Different conversations still spread across connections on their first
turn -- only intra-conversation rotation is removed. A diagnostic load harness
confirmed request-dispatch CPU is not the bottleneck; this is a routing fix.

Adds an integration regression test driving the real handleComboChat: a sessionless
round-robin conversation re-pins across turns (fails before the fix), and distinct
conversations still spread (round-robin distribution preserved).
2026-06-28 17:07:08 -03:00
iamedwardngo
4c4dcbd6db 🛡️ Sentinel: Security hardening (#5241)
Harden docs i18n rendering: path-traversal guard (cookie-controlled locale confined to docs/i18n via pure resolveSafeI18nSectionDir) + markdown XSS sanitization (DOMPurify allowlist). Review fixes: declared dompurify dep + allowlist, cleaned sanitizer config, extracted+tested the real path helper, removed agent scratch. Thanks @iamedwardngo!
2026-06-28 15:38:49 -03:00
Diego Rodrigues de Sa e Souza
462bca68b2 feat(compression): risk-gate pre-pass — shield sensitive spans from lossy compression (#5243)
Risk-gate pre-pass — shields sensitive spans (PEM/secret/stack/k8s/migration/legal) from lossy compression via SENTINEL preserveSpans. Default off, fail-open, ReDoS-bounded patterns. strategySelector baseline rebaselined for the wrapper extraction.
2026-06-28 15:26:45 -03:00
backryun
0568c37eee fix(providers): refresh impersonation User-Agents + TLS profiles to current client versions (#5237)
Refresh impersonation UAs + TLS profiles to current client versions. Review fixes (perplexity UA kept on Firefox 148 to match firefox_148 TLS profile; golden snapshot regenerated; release file-size/complexity baselines reconciled) co-authored. Thanks @backryun.
2026-06-28 14:41:56 -03:00
Abhishek Divekar
d8a392a477 fix(command-code): omit max_tokens when client omits it; correct registry caps (#5221)
Integrated into release/v3.8.40 — corrective max_tokens part already cherry-picked (e8d13ec17); this brings the registry maxOutputTokens caps. Thanks @adivekar-utexas.
2026-06-28 13:36:22 -03:00
Abhishek Divekar
e8d13ec17b fix(command-code): omit max_tokens when client omits it (#5221)
Cherry-picked the corrective part of #5221 only: the executor stops fabricating
`max_tokens` (= per-model registry cap) when the client omits it, which caused
`400 "expected <=200000"` on /alpha/generate for high-cap models. An explicit
oversized client value is clamped to the 200k endpoint ceiling. The PR's registry
maxOutputTokens recaps (open-sse/config/providers/registry/command-code/index.ts)
are intentionally NOT included pending reconciliation; #5221 stays open for that.
2026-06-28 13:07:50 -03:00
Ardem2025
6f150cfd2c fix(antigravity): retry excluded accounts via fallback LRU (#5222)
Antigravity: retry excluded accounts via fallback LRU + family-inferred 429 cooldown. Test strengthened into a real LRU regression guard; cooldown constant extracted. Integrated into release/v3.8.40.
2026-06-28 13:05:27 -03:00
Diego Rodrigues de Sa e Souza
6143ffc021 fix(cli): ship scripts/build/runtime-env.mjs in npm package (#5227) (#5230)
serve.mjs imports scripts/build/runtime-env.mjs (added with the #5213 heap
auto-calibration fix) but it was missing from package.json files whitelist,
breaking every global npm install at startup. Add it to files and guard with
a regression test that asserts all bin/ runtime imports of scripts/ are
packaged.
2026-06-28 13:03:08 -03:00
KooshaPari
3ee53cd561 fix(mcp): forward HTTP auth to internal tool fetches (#5218)
Forward MCP HTTP auth to internal tool fetches via AsyncLocalStorage (#5211). Rebased onto release tip. Integrated into release/v3.8.40.
2026-06-28 12:58:50 -03:00
backryun
3ab9723a7b Add more Icons (#5220)
Add lobe provider icons + aliases; rebased onto release tip. Integrated into release/v3.8.40.
2026-06-28 12:55:42 -03:00
Randi
5f4cd53b6e Scope textual thinking tag extraction (#5224)
Scope textual thinking-tag extraction to tag-native model families; preserve GEMINI_CLI registration. Resubmit of #5216 without the regression. Integrated into release/v3.8.40.
2026-06-28 12:54:33 -03:00
Diego Rodrigues de Sa e Souza
7854b1eef1 chore(ci): Trivy advisory scan ignores unfixed CVEs (Security-tab noise) (#5235)
The advisory Trivy image scan uploaded every HIGH/CRITICAL into the
Security tab without ignore-unfixed, flooding it with ~150 unfixable
base-image OS CVEs (Debian trixie packages with no upstream patch,
overwhelmingly local-only and not reachable from the proxy request
surface). Operators cannot act on those, so they are pure noise.

Add ignore-unfixed:true to the advisory step so it mirrors the existing
CRITICAL blocking gate and surfaces only actionable, fixable
vulnerabilities. Wire trivyignores to a new repo-root .trivyignore that
documents the accepted-risk policy and is the single auditable home for
the rare fixable CVE we must temporarily accept (none at present).

Takes effect on the next release image build (Trivy only runs on tag
builds, not main pushes); fixed CVEs drop out of the SARIF and GitHub
auto-resolves the corresponding alerts.
2026-06-28 12:31:43 -03:00
Diego Rodrigues de Sa e Souza
051285a660 test(docker): skip comment lines in #4076 builder heap-ordering check (#5233)
The ordering assertion matched a builder-stage comment that mentions
'npm run build' (added with the v3.8.40 workspace-deps Docker fix), making it
false-fail even though the real RUN step correctly follows the NODE_OPTIONS
heap line. Match real ENV/RUN instructions only, ignoring '#' comment lines.
2026-06-28 12:29:45 -03:00
Diego Rodrigues de Sa e Souza
8da9e6ad3f chore(docker): harden base image against container-scan CVEs (#5229)
apt-get upgrade -y in the base stage pulls security-patched trixie
packages at build time, and npm install -g npm@latest refreshes the
globally-bundled undici/tar inside the npm CLI. Together these clear the
subset of GitHub container-scan CVE alerts that have an upstream fix
available.

None of the flagged CVEs are in the application dependency tree (app
already resolves undici@8.5.0 / tar@7.5.16, both fixed); they live in
the node:24-trixie-slim base layer and npm's own internals, and none are
reachable from the proxy request surface at runtime. CVEs without a
published fix (local-only TOCTOU, etc.) remain until the distro patches
them and the image is rebuilt.
2026-06-28 10:23:38 -03:00
Diego Rodrigues de Sa e Souza
c4280433c5 chore(release): open v3.8.40 development cycle 2026-06-28 08:08:33 -03:00
Diego Rodrigues de Sa e Souza
fd593c6e96 fix(docker): copy open-sse workspace manifest before npm ci (v3.8.39 image build) (#5223) v3.8.39 2026-06-28 07:39:27 -03:00
Diego Rodrigues de Sa e Souza
dc40911583 Release v3.8.39 (#5164)
* 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>
2026-06-28 06:58:29 -03:00
Diego Rodrigues de Sa e Souza
7b139fdb5e Release v3.8.38 (#5078)
* chore(release): open v3.8.38 development cycle

* fix(executors): strip client_metadata for cerebras and mistral (#4727)

Integrated into release/v3.8.38 (leva 5)

* fix(codebuddy): only send reasoning params when client requests reasoning (#5019)

Integrated into release/v3.8.38 (leva 5)

* fix(sse): keep streaming for forceStream providers when client requests JSON (#5021)

Integrated into release/v3.8.38 (leva 5)

* fix(sse): guard non-JSON SSE lines and duplicate [DONE] (#4937)

Integrated into release/v3.8.38 (leva 5)

* feat(blackbox): refresh provider model catalog (#4935)

Integrated into release/v3.8.38 (leva 5)

* fix(sse): dedupe case-variant Anthropic version/beta headers (#4846)

Integrated into release/v3.8.38 (leva 5)

* feat(sse): Kiro inline <thinking> stream splitter (#4911)

Integrated into release/v3.8.38 (leva 5)

* feat(cursor): parse Composer DeepSeek-style inline tool calls (#4912)

Integrated into release/v3.8.38 (leva 5)

* feat(proxy): auth-less host:port batch import (#4938)

Integrated into release/v3.8.38 (leva 5)

* fix(oauth): support Kiro IDC (organization) token import (#4944)

Integrated into release/v3.8.38 (leva 5)

* fix(translator): preserve cache_control for DashScope OpenAI-compat providers (port from 9router#2069) (#5013)

Integrated into release/v3.8.38 (leva 5)

* fix(tts): resolve Gemini TTS models from catalog (#4934)

Integrated into release/v3.8.38 (leva 5)

* fix(sse): don't cool down the connection on a self-inflicted upstream timeout (504) (#5064)

Integrated into release/v3.8.38 (leva 5)

* fix(sse): robust Anthropic /v1/messages streaming — real ping keepalive + client-disconnect guard (#5063)

Integrated into release/v3.8.38 (leva 5)

* feat(video): add Alibaba DashScope (wan2.7-t2v) provider (#5051)

Integrated into release/v3.8.38 (leva 5)

* fix: preserve model hidden flags (isHidden) across model sync (#5086)

Integrated into release/v3.8.38 (leva 5)

* fix(models): derive model discovery config from registry modelsUrl (#5087)

Integrated into release/v3.8.38 (leva 5)

* fix(compression): replace fileURLToPath(import.meta.url) with runtime anchors for standalone bundle (#5089)

Integrated into release/v3.8.38 (leva 5)

* feat(cc): add summarized thinking display toggle (#5055)

Integrated into release/v3.8.38 (leva 5)

* Harden selected API error responses (#5032)

Integrated into release/v3.8.38 (leva 5)

* chore(quality): rebaseline file-size for leva 5 PR batch drift

6 frozen files grew from merged leva-5 PRs (cursor #4912, kiro #4911,
videoGeneration #5051, default #4727, base #4846, chat #5064); all covered
by per-PR tests. See _rebaseline_2026_06_26_leva5 in the baseline.

* feat(compression): compression playground (Play + Compare tabs) in the studio (#5080)

Integrated into release/v3.8.38

* fix(combo): fail over on empty-content 502 instead of exhausting the provider (#5085) (#5104)

* fix(dashboard): surface detailed credential-validation error in add-connection modal (#5088) (#5106)

* feat(providers): allow local/private provider URLs by default with scoped metadata-safe guard (#5066) (#5107)

* fix(diagnostics): treat non-streaming Claude messages shape as valid output (#5108) (#5116)

* fix(db): translate pt-BR SQLite driver-fallback log lines to English (#5103) (#5115)

* fix(sse): repair release base-reds — malformed-response false positives + header casing + stale tests (#5117)

Repairs the release/v3.8.38 base-reds; unblocks #5078.

* chore(quality): rebaseline file-size for responseSanitizer (#5117) + AddApiKeyModal drift

* fix(translator): forward image tool_result blocks as image_url (#5100)

Base-reds fixed (#5117); image tool_result→image_url. Integrated into release/v3.8.38.

* fix(responses): default text.format for openai-compatible responses providers (#5101)

Base-reds fixed (#5117); default text.format + file-size rebaseline. Integrated into release/v3.8.38.

* feat(dashboard): expose Fusion judgeModel + fusionTuning in the combo editor (#5074)

Base-reds fixed (#5117); Fusion editor + file-size rebaseline. Integrated into release/v3.8.38.

* feat(quota): add opt-in Codex/Claude auto-ping keepalive (#5102)

Base-reds fixed (#5117); auto-ping keepalive + file-size rebaseline. Integrated into release/v3.8.38.

* test(release): relocate 2 orphan test files into the collected flat tests/unit dir (#5120)

Unblocks Lint (test-discovery) on #5078. Integrated into release/v3.8.38.

* fix(translator): preserve reasoning-replay reasoning_content + repair 3 release-green test reds (#5122)

Repairs 3 release-green test reds + test-masking; unblocks #5078.

* test(golden): redact live Node version from provider translate-path snapshot (#5125)

Final golden unblock for #5078.

* test(golden): redact OmniRoute app version from translate-path snapshot (#5126)

Coverage shard golden unblock for #5078.

* Ignore disconnect races during in-band stream error handling (#5007)

Integrated into release/v3.8.38

* Track final connection IDs in failover logs (#5016)

Integrated into release/v3.8.38

* fix(sse): convert Gemini body to OpenAI format in antigravity MITM handler (#4845)

Integrated into release/v3.8.38 (rebased on tip, CHANGELOG re-injected)

* feat(providers): add ZenMux Free session-cookie provider (#5105)

Integrated into release/v3.8.38 (rebased on tip, CHANGELOG re-injected)

* feat(dashboard): click-to-edit model alias in provider page (#5119)

Integrated into release/v3.8.38 (rebased on tip, i18n scope verified, CHANGELOG re-injected)

* feat(mcp): web-session robustness — cookie dedup (PR6) + browser-pool observability (PR7) (#3368) (#5121)

Integrated into release/v3.8.38 (rebased on tip; cookie-dedup branch extracted to findExistingCookieConnection helper → complexity-neutral; CHANGELOG added)

* fix(usage): dedupe request-usage logging and debounce stats (#4940)

Integrated into release/v3.8.38 (rebased on tip; DB-handle hang was stale-base artifact — resetDbInstance already closes the handle, test green 5/5; file-size drift consolidated at release; CHANGELOG re-injected)

* fix(dashboard): key model visibility toggle on canonical providerId (#5091)

Integrated into release/v3.8.38 (retargeted main→release; .tsx visibility-key test green 2/2)

* chore(deps): bump actions/cache from 5.0.5 to 6.0.0 (#5112)

Integrated into release/v3.8.38 (retargeted main→release; workflow-only actions/cache bump — unit failures were stale main base-reds)

* fix(streaming): harden long OpenAI-compatible SSE streams (#5124)

Integrated into release/v3.8.38 (rebased on tip; streamHandler conflict with #5007 disconnect-guard resolved — both coexist, stream-handler 22/22 green)

* feat: Add Grok Build (xAI) provider with OAuth import-token flow (#5020)

Integrated into release/v3.8.38 (rebased on tip; Hard Rule #11 fix — Grok public client_id now via resolvePublicCred(grok_id), 3 literals removed; grok-oauth 7/7 + check:public-creds green)

* feat(providers): add Factory (factory.ai) as a subscription gateway provider (#5065)

Integrated into release/v3.8.38 (rebased on tip; added factory registry test for PR Test Policy + fixed check:env-doc-sync phantom FACTORY_API_KEY; factory loads in PROVIDERS, no Zod issue — that flag was a false positive)

* chore(test): reconcile golden snapshot + apikey count for new providers

#5020 (grok-cli), #5065 (factory), #5105 (zenmux-free) added providers but did
not regenerate tests/snapshots/provider/translate-path.json (now +3 entries) nor
bump the APIKEY_PROVIDERS count (159->160 for the factory gateway). Test-only
reconciliation; no production change.

* fix(resilience): harden quota and model lockout edge cases (#5093)

Integrated into release/v3.8.38 (rebased on tip). TRUST-BUT-VERIFY: dropped the PR's 0dd7df641 'fix unit gates' commit which reverted #5122 reasoning-replay (preserveReasoningContent) + re-introduced #4849 O(n^2) growth, and restored 5 tests it had realigned. Kept only the 3 declared resilience fixes (quota cutoff guard, gemini MIME, model-lockout maxCooldownMs); 23/23 green.

* Hydrate quota cache and scope auto combo candidates (#5015)

Integrated into release/v3.8.38 (rebased on tip). Kept core quota-cache hydration + auto-combo candidate scoping + combos UI; dropped out-of-scope toolCloaking refactor (conflicted with #4813 stripEnumDescriptions — took tip) and the unrelated sse-auth test split. Added quota-cache-hydrate-5015 regression test (Rule #18); combo-account-allowlist 8/8 + hydration 2/2 green.

* chore(quality): reconcile complexity + file-size baselines for v3.8.38 owner-PR batch

complexity 1972->1978 (+6) and file-size providers.ts 1093->1107 / usageHistory.ts
934->983 — drift from the /review-prs merge batch (#4845/#5105/#5020/#4940/#5093/
#5015 + #5121 cookie-dedup helper extraction). check:complexity/check:file-size do
not run on the PR->release fast-path, so the branch accrued unmeasured; all legit
feature/fix growth, not regression. See per-key justifications in each baseline.

* fix(security): exact-host Anthropic baseUrl check (CodeQL js/incomplete-url-substring-sanitization #674) (#5130)

The anthropic-compatible Bearer-fallback gate decided whether a configured baseUrl
targeted the official api.anthropic.com host via a substring `.includes("api.anthropic.com")`.
A look-alike upstream such as `https://api.anthropic.com.evil.test` or
`https://evil.test/?x=api.anthropic.com` matched the substring and was wrongly treated as
official, suppressing the Bearer fallback meant for third-party gateways
(CodeQL #674, js/incomplete-url-substring-sanitization, high).

Replace the substring test with an exported `isOfficialAnthropicBaseUrl()` helper that
parses the URL and compares the hostname for exact equality. Empty baseUrl stays official;
scheme-less hosts are parsed with an assumed https://; an unparseable baseUrl falls back to
third-party (Bearer emitted) as the safer default. Behavior for legitimate official/third-party
baseUrls is unchanged.

Adds tests/unit/anthropic-official-baseurl-host.test.ts covering official, look-alike,
scheme-less, and unparseable inputs plus a static guard that the substring pattern is gone.

* fix(proxy): repair one-click Deno & Cloudflare relay deployments (#5128) (#5132)

* fix(services): embed WS proxy honours LIVE_WS_HOST; reject empty messages early (#5110) (#5133)

* fix(api): resolve /v1/models/{id} case-insensitively (#5082) (#5135)

* fix(providers): add MiniMax M3 & Nemotron 3 Ultra to Cline catalog (#3321) (#5136)

* fix(proxy): make SOCKS5 handshake timeout tunable via SOCKS_HANDSHAKE_TIMEOUT_MS (#5109) (#5137)

* feat(sidebar): add support for colored menu icons (#3812)

Integrated into release/v3.8.38 (recreated on tip — fork had unrelated history; added getSidebarIconAccent regression test, Rule #18). Clean 2-file UI feature.

* fix(providers): complete grok-cli OAuth wiring + zenmux-free web-session metadata

Base-red repair for #5020 (grok-cli) and #5105 (zenmux-free), surfaced by the
full CI on the release PR (#5078) — the PR->release fast-path does not run the
oauth-providers-config / web-session-credentials / provider-consistency gates.

- grok-cli: register in OAUTH_PROVIDERS (providers.ts canonical list, fixes
  check:provider-consistency), add OAUTH_PROVIDER_IDS.GROK_CLI + GROK_CLI_CONFIG
  in oauth constants (provider config now sourced there, not a local literal),
  align oauth-providers-config.test.ts (EXPECTED_PROVIDER_KEYS + config map).
- zenmux-free: declare its web-session credential requirement (full Cookie header)
  in WEB_SESSION_CREDENTIAL_REQUIREMENTS.

Local: oauth-providers-config 27/27, web-session-credentials 4/4, grok-cli-oauth
7/7, check:provider-consistency OK, +115 OAUTH_PROVIDERS tests green.

* Fix resilience settings page response mapping (#5139)

Integrated into release/v3.8.38. Thanks @rdself for the fix and the regression test.

* fix(kiro): retire claude-sonnet-4.5 from catalog + pin 400 model-unavailable test (#5140)

Extracted the real change from #5140 (the bot PR regenerated the entire
freeModelCatalog.data.ts + touched package-lock.json; only the targeted
edits are kept here):
- remove claude-sonnet-4.5 from the Kiro registry entry
- remove the matching kiro free-model catalog row
- pin Kiro's verbatim 400 "Invalid model..." to isModelUnavailableError

Closes #4484

* fix(sidebar): drop orphan `settings` accent color (typecheck:core red) (#5142)

SIDEBAR_ICON_ACCENTS is typed Partial<Record<HideableSidebarItemId, string>>,
but `settings` is not a hideable item id (only `settings-general`,
`settings-appearance`, … and `context-settings` exist; there is no item with
`id: "settings"`), so the accent was unreachable. It broke `typecheck:core`
on the release tip ("'settings' does not exist in type …", introduced by
#3812 colored menu icons). Removing the orphan key restores a clean
typecheck:core (rc=0).

* feat: salvage batch 2 — diagnostics null-guard (#5096) + observed quota reset windows (#5025) (#5141)

* fix(diagnostics): null-guard content blocks in detectMalformedNonStream

A null (or non-object) entry in a Claude-native `content` array made the
non-stream classifier throw `TypeError: Cannot read properties of null
(reading 'type')`, crashing the malformed-response detection path. Guard
before type-asserting each block: a null/non-object block is simply skipped.

Two regression tests added (null block among valid blocks → null; only-null
blocks → empty_choices).

Salvaged from closed PR #5096 (base-stale; only the defensive guard — the
Claude-shape recognition it also carried already landed via #5108).

Co-authored-by: herjarsa <herjarsa@users.noreply.github.com>

* feat(quota): persist observed provider quota reset windows

Adds `provider_quota_reset_events` (migration 108) + `db/quotaResetEvents.ts`
to record real upstream weekly-quota window transitions whenever a quota
refresh shows the reset rolling to a new cycle (different day, later resetAt).
`apiKeyUsageLimits` now prefers the observed window start over the inferred
`resetAt − 7d`, falling back to snapshot inference when no event is recorded
yet. `quotaCache.setQuotaCache` records the transition opportunistically.

`recordProviderQuotaResetEventIfChanged` only fires for the primary weekly
window (not daily/sonnet), is idempotent (INSERT OR IGNORE on the unique
window key), and no-ops when the reset didn't actually roll. 4 unit tests
(tests/unit/lib/quota-reset-events.test.ts).

Salvaged from closed PR #5025 (which bundled this with two unrelated
features + a colliding migration 104). Renumbered to 108; module re-exported
from localDb (Rule #2).

Co-authored-by: Witroch4 <175152067+Witroch4@users.noreply.github.com>

---------

Co-authored-by: herjarsa <herjarsa@users.noreply.github.com>
Co-authored-by: Witroch4 <175152067+Witroch4@users.noreply.github.com>

* docs(i18n): sync 3.8.38 CHANGELOG section to 41 mirrors (unblock docs-accuracy) (#5144)

The root CHANGELOG [3.8.38] section grew with this cycle's merged PRs, but the
docs/i18n/<lang>/CHANGELOG.md mirrors were not re-synced — drifting >25% in body
size and failing check:docs-sync (the "Docs accuracy" fast-gate step) for every
open PR against the release.

Ran scripts/release/sync-changelog-i18n.mjs 3.8.38 3.8.37 to copy the root
[3.8.38] section into all 41 mirrors. check:docs-all now passes (exit 0).

Sections are copied verbatim; the per-language translation pass runs at release
time via i18n:run — this only restores the size-sync the gate enforces.

* feat(compression): pure per-step fidelity checker (4 invariants, fail-open)

* feat(compression): fidelityGate config + rejected breakdown fields

* feat(compression): wire per-step fidelity gate into stacked pipeline (opt-in)

* feat(compression): preview route accepts fidelityGate flag (playground)

* feat(compression): playground fidelity-gate toggle + lane rejection display

* docs(compression): note fidelityGate advanced thresholds are intentionally API-omitted

* refactor(compression): extract fidelity-gate step helpers to shrink strategySelector (file-size gate)

bodyToText and gateAdvance moved to fidelityGateStep.ts; StackAccumulator exported.
strategySelector: 889->854 (-35). Residual +6 vs pre-Milestone-B frozen 848 is the
irreducible StackOptions.fidelityGate field + two stacked-loop dispatch reads + import.
Baseline updated to 854 with justification. No cycle introduced (import type only).
940 compression tests pass; typecheck clean.

* test(usage): wire usageHistoryDedup under unit runner brace-list (#5145)

Integrated into release/v3.8.38.

* feat: salvage batch from closed stale PRs (#5038, #5057, #5076) (#5138)

Integrated into release/v3.8.38.

* test(combo): deterministic routing-decision matrix for all 17 strategies (#5146)

Integrated into release/v3.8.38.

* feat(compression): fuzzy near-duplicate dedup (session-dedup 2nd pass + playground toggle) (#5143)

Integrated into release/v3.8.38.

* chore(quality): rebaseline file-size for sidebarVisibility.ts + chat.ts drift (#5147)

Mid-cycle drift on release/v3.8.38 from already-merged PRs that the fast-path
(PR->release skips check:file-size) let accumulate without a bump:

- src/shared/constants/sidebarVisibility.ts 1100->1198 (#3812 colored menu
  icons, per-item accent map; #5142 dropped one orphan, net still above frozen)
- src/sse/handlers/chat.ts 1560->1575 (#5064 self-inflicted-timeout cooldown
  skip + #5124 long OpenAI-compatible SSE hardening + #5110 embed-WS
  LIVE_WS_HOST honour / early empty-message reject)

Each covered by its own PR tests; structural shrink of chat.ts tracked in #3501.
Unblocks the Fast Quality Gates for PRs targeting release/v3.8.38.

* chore(release): finalize v3.8.38 CHANGELOG + cycle reconciliation

- Reconcile [3.8.38]: +18 bullets (compression fidelity-gate/fuzzy-dedup #5143,
  quota keepalive #5102, web-session robustness #5121, MiniMax/Nemotron #5136,
  model-visibility #5091, failover logs #5016, disconnect races #5007, sidebar
  orphan #5142, SRE playbooks salvage #5138, new Security #5130 + Maintenance roll-up)
- Credit salvaged-PR authors (@JxnLexn / @KooshaPari / @herjarsa / @Witroch4)
- Remove phantom bullet for CLOSED-not-merged #5092 (setup aggregator never landed)
- Fix isHidden bullet PR citation #4389 -> #5086 (@herjarsa)
- Back-fill forgotten v3.8.36 bullet: #5026 crypto.randomUUID ID-gen (@hamsa0x7)
- Sync 41 i18n CHANGELOG mirrors; README What's New -> v3.8.38
- Rebaseline cycle drift: eslint 3987->4002, cognitive 833->841, dead-exports
  345->346, cyclomatic 1978->1980 (file-size handled by #5147)

* fix(i18n): add missing English UI labels (#5153)

Integrated into release/v3.8.38

* Preserve non-stream reasoning fields for compatible clients (#5155)

Integrated into release/v3.8.38

* feat(compression): ionizer engine — lossy JSON-array sampling reversible via CCR (#5148)

Integrated into release/v3.8.38

* test(combo): gated live smoke for combo strategies (in-process + VPS HTTP) (#5151)

Integrated into release/v3.8.38

* test: refresh release expectations to match current code (#5150)

Integrated into release/v3.8.38 (test-only base-red alignment extracted from #5150)

---------

Co-authored-by: Éder Costa <eder.almeida.costa@gmail.com>
Co-authored-by: José Victor Ferreira <root@josevictor.me>
Co-authored-by: Hernan Javier Ardila Sanchez <hjasgr@gmail.com>
Co-authored-by: fulorgnas <46461624+fulorgnas@users.noreply.github.com>
Co-authored-by: Randi <55005611+rdself@users.noreply.github.com>
Co-authored-by: Jan Leon <Jan.gaschler@gmail.com>
Co-authored-by: R. Beltran <rbeltran8000@gmail.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: KooshaPari <42529354+KooshaPari@users.noreply.github.com>
Co-authored-by: Ramel Tecnologia - Rafa Martins <146174365+rafacpti23@users.noreply.github.com>
Co-authored-by: herjarsa <herjarsa@users.noreply.github.com>
Co-authored-by: Witroch4 <175152067+Witroch4@users.noreply.github.com>
v3.8.38
2026-06-27 09:07:12 -03:00
Diego Rodrigues de Sa e Souza
b3207ab010 fix(security): exact-host Anthropic baseUrl check (CodeQL js/incomplete-url-substring-sanitization #674) (#5129)
The anthropic-compatible Bearer-fallback gate decided whether a configured baseUrl
targeted the official api.anthropic.com host via a substring `.includes("api.anthropic.com")`.
A look-alike upstream such as `https://api.anthropic.com.evil.test` or
`https://evil.test/?x=api.anthropic.com` matched the substring and was wrongly treated as
official, suppressing the Bearer fallback meant for third-party gateways
(CodeQL #674, js/incomplete-url-substring-sanitization, high).

Replace the substring test with an exported `isOfficialAnthropicBaseUrl()` helper that
parses the URL and compares the hostname for exact equality. Empty baseUrl stays official;
scheme-less hosts are parsed with an assumed https://; an unparseable baseUrl falls back to
third-party (Bearer emitted) as the safer default. Behavior for legitimate official/third-party
baseUrls is unchanged.

Adds tests/unit/anthropic-official-baseurl-host.test.ts covering official, look-alike,
scheme-less, and unparseable inputs plus a static guard that the substring pattern is gone.
2026-06-26 23:14:27 -03:00