Commit Graph

1197 Commits

Author SHA1 Message Date
Diego Rodrigues de Sa e Souza
af2002a493 chore: reconcile the JxnLexn merge wave with the release tip (#13921)
Lands the combined-board reconciliation of today's JxnLexn wave as one follow-up: i18n fill for #12471/#13555's new keys (real vi translations), free-tier count 446→452, file-size rebaseline for #13556. check-new-key-coverage PASS; only the three pre-existing file-size reds remain.
2026-09-16 16:43:44 -03:00
Markus Hartung
1cf8e4bcc6 fix(db): auto-clean terminal batch checkpoints and expired file content (#12999)
Merged after a maintainer rework that kept every one of @hartmark's commits intact.

**What the rework added:** the auto-clean of terminal batch checkpoints and expired file content is gated behind a default-off feature flag (`BATCH_AND_FILE_AUTO_CLEANUP_ENABLED`, `defaultValue: "false"`, documented in `docs/reference/FEATURE_FLAGS.md` and described in all 66 locales) so the release default keeps today's behaviour and operators opt in; the DB handle leak in the test was fixed so the Node runner exits cleanly.

Validated as a combined board first (this PR merged with the 21 siblings of the same wave on the release tip): eslint with the frozen suppressions, typecheck:core, check:open-sse-typecheck, complexity, cognitive-complexity, changelog-integrity, i18n new-key coverage, docs-counts, docs-sync, migration-numbering, provider-consistency and a duplicate-identifier audit all green, plus 176 passing / 0 failing focused node:test cases across the 25 test files the wave touches and the dashboard test under Vitest (2/0). Then re-validated alone on the fresh tip before this merge: conflicts re-resolved, file sizes rebaselined for this PR's own growth, eslint and this PR's focused tests re-run.

Thank you — the cleanup itself is exactly the kind of maintenance that stops a data dir from growing forever.
2026-09-16 15:45:34 -03:00
Patryk Kopyciński
23c5772ccb feat: adaptive reasoning effort (auto) — gateway-resolved, per-turn pinned, all harnesses (#13448)
Merged after a maintainer rework that kept every one of @patrykkopycinski's commits intact — including the two refactors you pushed later (extracting the adaptive-effort wiring out of `chatCore.ts` and reading `x-omniroute-effort` inside the wiring module), which were merged into the rework rather than overwritten.

**What the rework added:** the adaptive-effort wiring is scoped to OpenAI-dispatch requests only (the claim in `docs/routing` was corrected to match), and `defaultReasoningEffort` was widened to accept `auto` explicitly instead of relying on a loose string.

Validated as a combined board first (this PR merged with the 21 siblings of the same wave on the release tip): eslint with the frozen suppressions, typecheck:core, check:open-sse-typecheck, complexity, cognitive-complexity, changelog-integrity, i18n new-key coverage, docs-counts, docs-sync, migration-numbering, provider-consistency and a duplicate-identifier audit all green, plus 176 passing / 0 failing focused node:test cases across the 25 test files the wave touches and the dashboard test under Vitest (2/0). Then re-validated alone on the fresh tip before this merge: conflicts re-resolved, file sizes rebaselined for this PR's own growth, eslint and this PR's focused tests re-run.

Thank you — gateway-resolved, per-turn pinned effort is a real feature, and the header contract makes it usable from every harness.
2026-09-16 15:18:36 -03:00
Markus Hartung
88d5e0cde6 fix(db): gate the auto-cleanup VACUUM on reclaimable space, not row count (#13079)
Merged after a maintainer rework that kept every one of @hartmark's commits intact.

**What the rework added:** the reclaimable-space gate for the auto-cleanup VACUUM sits behind a default-off feature flag so the release default is unchanged, with the flag documented in `docs/reference/FEATURE_FLAGS.md` and described in all 66 locales; the rest is your change as submitted.

Validated as a combined board first (this PR merged with the 21 siblings of the same wave on the release tip): eslint with the frozen suppressions, typecheck:core, check:open-sse-typecheck, complexity, cognitive-complexity, changelog-integrity, i18n new-key coverage, docs-counts, docs-sync, migration-numbering, provider-consistency and a duplicate-identifier audit all green, plus 176 passing / 0 failing focused node:test cases across the 25 test files the wave touches and the dashboard test under Vitest (2/0). Then re-validated alone on the fresh tip before this merge: conflicts re-resolved, file sizes rebaselined for this PR's own growth, eslint and this PR's focused tests re-run.

Thank you — gating VACUUM on reclaimable pages instead of row count is the right signal.
2026-09-16 15:09:07 -03:00
Patryk Kopyciński
b269821c83 fix(cursor): kv_after_text must not settle away a trailing exec_mcp tool call (#13627)
Merged. Settling on `kv_after_text` while a trailing `exec_mcp` call is still pending drops the tool call entirely — the client then sees a finished turn that never ran the tool. Correct place to fix it.

Validated as a combined board first (this PR merged with the 21 siblings of the same wave on the release tip): eslint with the frozen suppressions, typecheck:core, check:open-sse-typecheck, complexity, cognitive-complexity, changelog-integrity, i18n new-key coverage, docs-counts, docs-sync, migration-numbering, provider-consistency and a duplicate-identifier audit all green, plus 176 passing / 0 failing focused node:test cases across the 25 test files the wave touches and the dashboard test under Vitest (2/0). Then re-validated alone on the fresh tip before this merge: conflicts re-resolved, file sizes rebaselined for this PR's own growth, eslint and this PR's focused tests re-run.

Thank you.
2026-09-16 13:39:14 -03:00
Patryk Kopyciński
4621842d93 feat(reasoning): opt-in min output budget floor for thinking models (#12742)
Merged. Opt-in is what makes this safe to ship: thinking models that need a floor get one, everyone else sees no change in behaviour, and the env var is documented in `.env.example` and `ENVIRONMENT.md` rather than being folklore.

Validated as a combined board first (this PR merged with the 21 siblings of the same wave on the release tip): eslint with the frozen suppressions, typecheck:core, check:open-sse-typecheck, complexity, cognitive-complexity, changelog-integrity, i18n new-key coverage, docs-counts, docs-sync, migration-numbering, provider-consistency and a duplicate-identifier audit all green, plus 176 passing / 0 failing focused node:test cases across the 25 test files the wave touches and the dashboard test under Vitest (2/0). Then re-validated alone on the fresh tip before this merge: conflicts re-resolved, file sizes rebaselined for this PR's own growth, eslint and this PR's focused tests re-run.

Thank you.
2026-09-16 13:38:23 -03:00
Bob.Hou
e7999c477b fix(db): bound health scans and isolate native diagnostics (#13717)
Merged after a maintainer rework that kept every one of @HouMinXi's commits intact.

**What the rework added on top of the contribution:** the new DB health-check behaviour is gated behind a default-off feature flag (`src/shared/constants/featureFlagDefinitions.ts`, `defaultValue: "false"`), documented in `docs/reference/FEATURE_FLAGS.md` with the description key carried into all 66 locales, so the release default is unchanged and the new bounds only apply when an operator opts in. The optional-FTS5 migration set was reconciled by hand with the "180" entry that landed meanwhile (`src/lib/db/migrationRunner/constants.ts`).

**Carried from your rebased head:** the `/api/db/health` local-only classification in `src/server/authz/routeGuard.ts` plus its `routeGuard` assertion — `runManagedDbHealthCheck()` forks native diagnostics into a child process, so Hard Rules #15/#17 apply. Re-verified here: 37 pass / 0 fail.

Validated as a combined board first (this PR merged with the 21 siblings of the same wave on the release tip): eslint with the frozen suppressions, typecheck:core, check:open-sse-typecheck, complexity, cognitive-complexity, changelog-integrity, i18n new-key coverage, docs-counts, docs-sync, migration-numbering, provider-consistency and a duplicate-identifier audit all green, plus 176 passing / 0 failing focused node:test cases across the 25 test files the wave touches and the dashboard test under Vitest (2/0). Then re-validated alone on the fresh tip before this merge: conflicts re-resolved, file sizes rebaselined for this PR's own growth, eslint and this PR's focused tests re-run.

Thank you for the depth of this one — the resource-bounds suite and the sql.js startup/backup coverage are the kind of tests that keep a database layer honest.
2026-09-16 12:59:45 -03:00
Diego Rodrigues de Sa e Souza
9d9284417c fix(cli): translate the CLI for every locale (38 catalogs were nearly empty) (#13892)
sync-ui-keys --catalog=cli + blocking i18n:check-keys:cli gate; 65 CLI catalogs synced (52,000 strings, 0 __MISSING__, all 830 keys, placeholders verified). ⚠️ base-red inherited: #12732
2026-09-16 12:27:59 -03:00
Diego Rodrigues de Sa e Souza
5faf44f975 fix(docs): restore the env/docs contract broken by the #13679 vars (#13875)
`check:env-doc-sync` is failing on the release tip, which fails "Docs Gates
(fast-path)" on every open PR against release/v3.8.51 (base-red #13866).

Both gaps come from #13679:

- `OMNIROUTE_CLOUD_SYNC_ENFORCE_SIGNATURE` is read in src/lib/cloudSync.ts but
  was in neither .env.example nor ENVIRONMENT.md. Documented with the behaviour
  the code actually implements: opt-in rejection of an UNSIGNED response when no
  local secret is configured, default off for v3.8.x back-compat, and a present
  signature always verified — and always rejected when
  OMNIROUTE_CLOUD_SYNC_SECRET is unset — regardless of the flag.
- `CDP_PROXY_TOKEN` was in .env.example but missing from ENVIRONMENT.md. Added to
  the ChatGPT Web (Codex) table next to CHATGPT_WEB_CODEX_CDP_URL, in that
  section's language, describing the X-Omni-Cdp-Token header the sidecar expects
  and the compose-network isolation that applies when it is unset.

Docs only, no code change.

Verified on this branch: check:env-doc-sync reports all three directions in sync
(817 vars in .env.example, 834 in ENVIRONMENT.md); check:docs-sync passes;
check:docs-counts reports only pre-existing soft drift.
2026-09-16 10:40:27 -03:00
Bob.Hou
54f19c7742 feat(providers): fetch live xAI catalog for xai-oauth (#13518) 2026-09-16 08:08:49 -03:00
Bob.Hou
cdcde97c70 feat(providers): add Agnes AI (China) as agnes-cn on api.agnes-ai.cn (#13399) 2026-09-16 08:01:39 -03:00
Diego Rodrigues de Sa e Souza
694c1b74eb fix(cli): add POST /api/mcp/restart and mcp enable/disable subcommands (#13012) (#13770)
Merged in the 2026-09-16 sweep of the maintainer's own open PRs, at the owner's explicit instruction. No push was made to the PR branch: the merge took the head as the owning session left it (verified OPEN, non-draft and MERGEABLE against the release tip immediately before merging).
2026-09-16 06:17:04 -03:00
Diego Rodrigues de Sa e Souza
d7d010b102 feat(i18n): blocking key-completeness gate — every locale carries every en.json key (#13827)
New blocking gate `i18n:check-keys` (`scripts/i18n/check-key-completeness.mjs`): every locale catalog must carry exactly the key set of `en.json`, whatever the age of the key. The percentage and new-key gates let batch 1 (#13044) ship 43 keys short and batch 2 (#13660) 10 keys short. Wired into the i18n-ui-coverage job; documented in QUALITY_GATES.md and the i18n guide (post-merge re-sync, retranslation with pinned names).

⚠️ base-red inherited: #12732
2026-09-16 02:52:31 -03:00
Bob.Hou
6ef0f1c06c fix(memory): skip FTS rewrite on access-count updates (#13331)
Restores `memory_id` to the `memory_fts_au` trigger's `WHEN` clause and adds an FTS5 rebuild to the memory cleanup pass.

This repairs a regression that landed yesterday: migration 178 guarded the trigger with `old.content IS DISTINCT FROM new.content OR old.key IS DISTINCT FROM new.key`, which drops the `memory_id` term the insert path depends on. `createMemory` inserts the row, the AFTER INSERT trigger stores an auto-assigned FTS5 rowid, and the follow-up `UPDATE memories SET memory_id = rowid` is what re-syncs FTS — an update that touches neither `content` nor `key`. With 178 alone that update stopped firing, so newly created memories drifted out of the FTS index and keyword/hybrid search silently returned nothing for them. Migration 180 adds the third term back.

Maintainer note before merge: the cleanup half now runs `rebuild` on every pass rather than `optimize` only when rows were deleted — accepted as-is; it is bounded by the memory table size and the trigger fix removes the bloat source that motivated it.

Validated as a combined board first (this PR merged with the 11 siblings of the same batch on the release tip): eslint on every changed file with the suppressions file, typecheck:core, check:open-sse-typecheck, complexity, cognitive-complexity, changelog-integrity, i18n new-key coverage, docs-sync, migration-numbering, provider-consistency and a duplicate-identifier audit all green, plus 275 passing / 0 failing focused node:test cases across the 28 test files the batch touches. Then re-validated alone on the fresh tip before this merge: conflicts re-resolved, file sizes rebaselined for this PR's own growth, eslint and this PR's focused tests re-run.

Thanks @HouMinXi!
2026-09-16 02:49:32 -03:00
Bob.Hou
4902ac8128 fix(network): skip Chrome TLS impersonation for Groq (#13445)
Groq is now excluded from Chrome TLS impersonation in both the direct and the proxied dispatch branch, even when `TLS_FINGERPRINT_PROVIDERS` is unset or explicitly lists it — Cloudflare answers the spoofed fingerprint with 1010 `browser_signature_banned` (#13225).

Scope is contained: the whole path is behind `ENABLE_TLS_FINGERPRINT`, which defaults to off, so nothing changes for operators who never opted in.

Validated as a combined board first (this PR merged with the 11 siblings of the same batch on the release tip): eslint on every changed file with the suppressions file, typecheck:core, check:open-sse-typecheck, complexity, cognitive-complexity, changelog-integrity, i18n new-key coverage, docs-sync, migration-numbering, provider-consistency and a duplicate-identifier audit all green, plus 275 passing / 0 failing focused node:test cases across the 28 test files the batch touches. Then re-validated alone on the fresh tip before this merge: conflicts re-resolved, file sizes rebaselined for this PR's own growth, eslint and this PR's focused tests re-run.

Thanks @HouMinXi!
2026-09-16 01:54:00 -03:00
Dizzle
997cd4d509 fix(sse): stop retry wave on rate-limited 429 and drain 429 once (#13657)
The opencode executor classifies rate-limited 429 bodies (`classify429`, with real tests) and, when a whole account wave is exhausted, returns the last real upstream 429 — status, body, `Retry-After` and quota headers intact — so the provider error rules (monthly-quota cooldown) keep working.

Maintainer rework before merge (kept the idea, no default behavior change):
- The original stopped the cross-account wave at the first classified 429 and replaced the response with a synthetic one that dropped the body and headers; stopping early is now opt-in behind `OPENCODE_RATE_LIMITED_429_EARLY_STOP` (default off), the rate-limited account is still cooled down, the body is read as a bounded 8 KiB prefix from a clone and the original is never consumed, and the unused `status` input is gone.

Validated first on the combined board of all 38 PRs of this batch (10 merged as-is, 28 after the maintainer rework) on top of release/v3.8.51 c0f92ec: typecheck:core, check:open-sse-typecheck and check:dashboard-typecheck clean; ESLint clean on every changed file; file-size (rebaselined for the combined growth), complexity, cognitive-complexity, changelog-integrity, docs-counts, docs-sync, migration-numbering and i18n new-key gates green; 735 focused node:test cases with the only batch-caused failure (a flag-count assertion) fixed. Then re-validated alone on the fresh release tip right before this merge: ESLint on the changed files, typecheck:core, check:open-sse-typecheck, the file-size/complexity/changelog gates and this PR's own tests.

Thanks @maxmad64bis!
2026-09-15 22:01:56 -03:00
Dizzle
94d27e44fe fix(providers): stop parking Mistral connections on a bare 401 with no clear auth failure (#13609)
Behind the new `MISTRAL_AMBIGUOUS_401_SOFT_LOCKOUT` flag (default off), a bare Mistral 401 (`{"detail":"Unauthorized"}`, identical for a revoked key and an exhausted quota) gets a retryable cooldown instead of parking the connection as `expired`; after three soft strikes within an hour the next bare 401 parks it, so revocation still converges.

Maintainer rework before merge (kept the idea, no default behavior change):
- The predicate is shared with the connection-test module instead of duplicated; the squeezed 139-char line that dodged the file-size gate is formatted normally and the growth is rebaselined honestly with an annotation.

Validated first on the combined board of all 38 PRs of this batch (10 merged as-is, 28 after the maintainer rework) on top of release/v3.8.51 c0f92ec: typecheck:core, check:open-sse-typecheck and check:dashboard-typecheck clean; ESLint clean on every changed file; file-size (rebaselined for the combined growth), complexity, cognitive-complexity, changelog-integrity, docs-counts, docs-sync, migration-numbering and i18n new-key gates green; 735 focused node:test cases with the only batch-caused failure (a flag-count assertion) fixed. Then re-validated alone on the fresh release tip right before this merge: ESLint on the changed files, typecheck:core, check:open-sse-typecheck, the file-size/complexity/changelog gates and this PR's own tests.

Thanks @maxmad64bis!
2026-09-15 21:19:47 -03:00
Dizzle
13f44af6e5 fix(sse): pause failover dispatch after repeated transient upstream failures (#13615)
Behind the new `OPENCODE_TRANSIENT_FAILOVER_BACKOFF` flag (default off), after two consecutive transient upstream failures the opencode rotation pauses before each later account (1.5s, 3s, 6s, capped at 10s per request) instead of hammering the upstream.

Maintainer rework before merge (kept the idea, no default behavior change):
- The pause honors the client abort signal (no dispatch after a disconnect), the failed attempt's body is cancelled before sleeping, `transientRetryDelayMs` now uses its arguments, and the sleep is injectable so the tests run without real timers.

Validated first on the combined board of all 38 PRs of this batch (10 merged as-is, 28 after the maintainer rework) on top of release/v3.8.51 c0f92ec: typecheck:core, check:open-sse-typecheck and check:dashboard-typecheck clean; ESLint clean on every changed file; file-size (rebaselined for the combined growth), complexity, cognitive-complexity, changelog-integrity, docs-counts, docs-sync, migration-numbering and i18n new-key gates green; 735 focused node:test cases with the only batch-caused failure (a flag-count assertion) fixed. Then re-validated alone on the fresh release tip right before this merge: ESLint on the changed files, typecheck:core, check:open-sse-typecheck, the file-size/complexity/changelog gates and this PR's own tests.

Thanks @maxmad64bis!
2026-09-15 20:58:12 -03:00
Dizzle
e325888d78 fix(sse): fail over opencode request on refused-route 403 (#13498)
Behind the new `OPENCODE_USER_BLOCKED_ROTATION` flag (default off), a 403 or 451 carrying `user_blocked` on a proxied opencode account rotates at most once to the next account instead of being returned as-is.

Maintainer rework before merge (kept the idea, no default behavior change):
- 403 and 451 are handled by one predicate (the original returned 451 without rotation), the refused account gets a cooldown and joins the tried-set, and the response body of the attempt rotated away from is cancelled.

Validated first on the combined board of all 38 PRs of this batch (10 merged as-is, 28 after the maintainer rework) on top of release/v3.8.51 c0f92ec: typecheck:core, check:open-sse-typecheck and check:dashboard-typecheck clean; ESLint clean on every changed file; file-size (rebaselined for the combined growth), complexity, cognitive-complexity, changelog-integrity, docs-counts, docs-sync, migration-numbering and i18n new-key gates green; 735 focused node:test cases with the only batch-caused failure (a flag-count assertion) fixed. Then re-validated alone on the fresh release tip right before this merge: ESLint on the changed files, typecheck:core, check:open-sse-typecheck, the file-size/complexity/changelog gates and this PR's own tests.

Thanks @maxmad64bis!
2026-09-15 20:39:39 -03:00
Dizzle
d8c0448293 fix(opencode): rotate once when a Responses stream stalls before its first byte (#13484)
Behind the new `OPENCODE_RESPONSES_STALL_ROTATION` flag (default off): a streamed Responses reply with no first body byte within `RESPONSES_FIRST_BYTE_TIMEOUT_MS` (15s) cools the account and rotates once; a second stall fails fast instead of waiting the 80s readiness timeout.

Maintainer rework before merge (kept the idea, no default behavior change):
- The TLS first-byte watchdog from #12656 is restored byte for byte (the PR had changed its pump, timer and cancel); the stall guard lives in its own module.
- Proxy-less multi-account setups now rotate the same way as proxied ones (the original threw for them), a client abort during the wait rethrows instead of rotating, and the env var is documented as flag-only.

Validated first on the combined board of all 38 PRs of this batch (10 merged as-is, 28 after the maintainer rework) on top of release/v3.8.51 c0f92ec: typecheck:core, check:open-sse-typecheck and check:dashboard-typecheck clean; ESLint clean on every changed file; file-size (rebaselined for the combined growth), complexity, cognitive-complexity, changelog-integrity, docs-counts, docs-sync, migration-numbering and i18n new-key gates green; 735 focused node:test cases with the only batch-caused failure (a flag-count assertion) fixed. Then re-validated alone on the fresh release tip right before this merge: ESLint on the changed files, typecheck:core, check:open-sse-typecheck, the file-size/complexity/changelog gates and this PR's own tests.

Thanks @maxmad64bis!
2026-09-15 20:10:57 -03:00
Dizzle
5cf4316b67 fix(proxy-health): refused probe responses reset the consecutive-failure streak (#13608)
Behind the new `PROXY_HEALTH_BLOCKED_RESETS_STREAK` flag (default off), a probe the target refuses (401/403/429) resets the proxy's consecutive-failure streak, so a proxy that clearly relays is not marked dead by spaced-out real failures.

Maintainer rework before merge (kept the idea, no default behavior change):
- The original reversed the deliberate #10654 policy for everyone; with the flag off a refusal stays neutral, and the existing assertions are restored. The stale JSDoc and the wrong "any relayed response resets" comment are fixed (5xx stays inconclusive).
- The source-grep test became a real sweep test: a local relay answering 403 drives fail → blocked → fail with auto-disable, in both flag modes.

Validated first on the combined board of all 38 PRs of this batch (10 merged as-is, 28 after the maintainer rework) on top of release/v3.8.51 c0f92ec: typecheck:core, check:open-sse-typecheck and check:dashboard-typecheck clean; ESLint clean on every changed file; file-size (rebaselined for the combined growth), complexity, cognitive-complexity, changelog-integrity, docs-counts, docs-sync, migration-numbering and i18n new-key gates green; 735 focused node:test cases with the only batch-caused failure (a flag-count assertion) fixed. Then re-validated alone on the fresh release tip right before this merge: ESLint on the changed files, typecheck:core, check:open-sse-typecheck, the file-size/complexity/changelog gates and this PR's own tests.

Thanks @maxmad64bis!
2026-09-15 20:01:25 -03:00
Dizzle
ca312d57aa feat(proxies): show how many egress IPs actually served a proxy pool (#13581)
Behind the new `PROXY_POOL_EGRESS_OBSERVATION` flag (default off): a line under each proxy pool showing how many distinct egress IPs actually served it over 24h, backed by `GET /api/settings/proxies/pool/egress-observation`.

Maintainer rework before merge (kept the idea, no default behavior change):
- The route validates its query with Zod (unknown `scope` → 400 instead of silently `global`), error bodies go through `errorResponse()`, the OpenAPI entry documents security, parameters and responses, and the three UI strings exist in every locale.

Validated first on the combined board of all 38 PRs of this batch (10 merged as-is, 28 after the maintainer rework) on top of release/v3.8.51 c0f92ec: typecheck:core, check:open-sse-typecheck and check:dashboard-typecheck clean; ESLint clean on every changed file; file-size (rebaselined for the combined growth), complexity, cognitive-complexity, changelog-integrity, docs-counts, docs-sync, migration-numbering and i18n new-key gates green; 735 focused node:test cases with the only batch-caused failure (a flag-count assertion) fixed. Then re-validated alone on the fresh release tip right before this merge: ESLint on the changed files, typecheck:core, check:open-sse-typecheck, the file-size/complexity/changelog gates and this PR's own tests.

Thanks @maxmad64bis!
2026-09-15 19:48:52 -03:00
Dizzle
238cb1b076 feat(proxy-logs): keep the HTTP status the provider actually returned (#13580)
`proxy_logs` records `upstream_status`, the HTTP status the provider actually returned through the proxy, instead of only success/timeout/error.

Maintainer rework before merge (kept the idea, no default behavior change):
- The migration collided with the tip (177 was already taken): renumbered to `179_proxy_logs_upstream_status.sql`, the runner's already-applied check moved to `case "179"` (the old `"177"` would have skipped the tip's own 177), migration count bumped to 176 in README, AGENTS.md, llm.txt and its mirrors (operator-approved).
- A new test runs the real migration runner on the real SQL files and fails with the old case number.

Validated first on the combined board of all 38 PRs of this batch (10 merged as-is, 28 after the maintainer rework) on top of release/v3.8.51 c0f92ec: typecheck:core, check:open-sse-typecheck and check:dashboard-typecheck clean; ESLint clean on every changed file; file-size (rebaselined for the combined growth), complexity, cognitive-complexity, changelog-integrity, docs-counts, docs-sync, migration-numbering and i18n new-key gates green; 735 focused node:test cases with the only batch-caused failure (a flag-count assertion) fixed. Then re-validated alone on the fresh release tip right before this merge: ESLint on the changed files, typecheck:core, check:open-sse-typecheck, the file-size/complexity/changelog gates and this PR's own tests.

Thanks @maxmad64bis!
2026-09-15 18:26:15 -03:00
Dizzle
29d66cbf8c feat(proxies): stop re-serving a proxy that just failed (#13578)
Behind the new `PROXY_SKIP_RECENTLY_FAILED` flag (default off), pool rotation and the opencode account rotation remember a proxy that just failed (refused probe or 429) and skip it for a doubling cooldown instead of re-serving it immediately.

Maintainer rework before merge (kept the idea, no default behavior change):
- The original was on by default and re-queried the DB on every request while a member was set aside; selection now caches a refusal sequence number and re-runs the cascade once per set-aside event.
- `src/lib/db` no longer imports the heavy dispatcher for key normalization (a parity test guarantees the same key as `proxyConfigToUrl()`); `.env.example` and `ENVIRONMENT.md` document the default as false.

Validated first on the combined board of all 38 PRs of this batch (10 merged as-is, 28 after the maintainer rework) on top of release/v3.8.51 c0f92ec: typecheck:core, check:open-sse-typecheck and check:dashboard-typecheck clean; ESLint clean on every changed file; file-size (rebaselined for the combined growth), complexity, cognitive-complexity, changelog-integrity, docs-counts, docs-sync, migration-numbering and i18n new-key gates green; 735 focused node:test cases with the only batch-caused failure (a flag-count assertion) fixed. Then re-validated alone on the fresh release tip right before this merge: ESLint on the changed files, typecheck:core, check:open-sse-typecheck, the file-size/complexity/changelog gates and this PR's own tests.

Thanks @maxmad64bis!
2026-09-15 18:09:02 -03:00
Diego Rodrigues de Sa e Souza
df87e9363b fix(auth): close the JWT_SECRET bootstrap chain — real-peer loopback, obsidian always-protected, DATA_DIR vault refusal (#13791)
GHSA-7pq4-8pvv-rx7r (critical). Every link of the reported chain held on the
release tip:

1. First boot without JWT_SECRET generates one and writes it in cleartext to
   $DATA_DIR/server.env.
2. With no password configured, isAuthRequired() returned false for
   POST /api/settings/require-login unconditionally — before the loopback
   check — so any network peer could switch requireLogin off.
3. With requireLogin off, POST /api/settings/obsidian/webdav accepted an
   arbitrary vault root and echoed freshly minted Basic credentials.
4. The WebDAV file service is served by the custom Node layer before Next.js,
   outside the authz pipeline.
5. Pointing it at DATA_DIR reads server.env, and JWT_SECRET forges an
   `{"authenticated":true}` admin session.

A second, worse problem surfaced while verifying: isLoopbackRequest() decided
"loopback" from nextUrl.hostname / the Host header, which the client controls.
`Host: localhost` from a remote address made the whole fresh-install bootstrap
reachable, not just the write path.

Three cuts, plus the root cause:

- isLoopbackRequest() now reads the trusted peer: the token-stamped real TCP
  peer the custom server writes (peerStamp), then the pipeline's own locality
  verdict once a stamp token exists, then a real socket peer. The bootstrap
  write path honours the same constraint instead of returning false, and
  managementPolicy hands down the peerContext verdict explicitly, because at
  policy time the original request still carries client-supplied headers.
- Host is consulted only when the process has no stamp token at all — no
  stamping server in front, which in practice means route handlers invoked
  directly by the unit-test harness. Every supported runtime (run-next dev and
  start, standalone-server-ws for Docker, the npm CLI and Electron) calls
  ensurePeerStampToken() at boot, so there a signal-less request fails closed.
  Without this fallback ~340 route tests that call handlers with
  `new Request("http://localhost/…")` turned into 401s.
- /api/settings/obsidian joins ALWAYS_PROTECTED_API_PATHS: issuing and rotating
  reusable WebDAV credentials is credential export, the same rationale as the
  GHSA-62vw entry for the password reveal.
- enableObsidianVaultSync() refuses a vault that is, sits inside, or contains
  DATA_DIR, comparing realpath-resolved paths so a symlink cannot dodge it.

Tests are red-first: remote stamped peer → auth required on the bootstrap
write; Host: localhost plus a forged locality header from a non-loopback
stamped peer → 401 through the full pipeline; the local operator keeps the
first-password flow; obsidian inventory and DATA_DIR overlap cases.
2026-09-15 16:58:24 -03:00
Dizzle
87d9d82b37 fix(sse): fail over to sibling connection on stream early EOF (#13153)
Behind `STREAM_EARLY_EOF_SIBLING_FAILOVER_ENABLED` (default off): after the bounded same-connection retry is spent, a stream that closed early fails over exactly once to a sibling connection.

Maintainer rework before merge (kept the idea, no default behavior change):
- The PR's own failover test was red on its head: the `/v1/chat/completions` route's early-stream keepalive dropped the `X-OmniRoute-Selected-Connection-Id` header on the first cold request. Tests now drive `handleChat()` directly; the assertion was kept.
- "One hop" was one hop per connection (a 3-connection pool made 4 dispatches); it is now a single sibling hop per request, and when the pool runs out the original `STREAM_EARLY_EOF` 502 is returned instead of a generic `bad_gateway`, so combo-level detection keeps working. The source-regex timeout test became a behavioral one; flag description in all 59 locales.

Validated first on the combined board of all 38 PRs of this batch (10 merged as-is, 28 after the maintainer rework) on top of release/v3.8.51 c0f92ec: typecheck:core, check:open-sse-typecheck and check:dashboard-typecheck clean; ESLint clean on every changed file; file-size (rebaselined for the combined growth), complexity, cognitive-complexity, changelog-integrity, docs-counts, docs-sync, migration-numbering and i18n new-key gates green; 735 focused node:test cases with the only batch-caused failure (a flag-count assertion) fixed. Then re-validated alone on the fresh release tip right before this merge: ESLint on the changed files, typecheck:core, check:open-sse-typecheck, the file-size/complexity/changelog gates and this PR's own tests.

Thanks @maxmad64bis!
2026-09-15 16:49:06 -03:00
Dizzle
53ed8c4745 fix(stream-recovery): order-aware in-flight tool-call detection behind off-by-default flag (#13633)
Behind `STREAM_RECOVERY_TOOLCALL_ORDER_FIX` (default off), mid-stream continuation becomes tool-call safe: any tool call seen in the stream — in flight or finished — blocks a continuation, and an empty continuation stops after one attempt.

Maintainer rework before merge (kept the idea, no default behavior change):
- The empty-continuation short-circuit also ran with the flag off; it is now gated, so the flag-off path uses the whole budget exactly as before (regression test added).
- The latch re-arm that let a continuation fire after a completed `finish_reason: tool_calls` is gone; index-less tool calls on multi-choice payloads are now blocked too; ~150 lines of dead trace plumbing removed.

Validated first on the combined board of all 38 PRs of this batch (10 merged as-is, 28 after the maintainer rework) on top of release/v3.8.51 c0f92ec: typecheck:core, check:open-sse-typecheck and check:dashboard-typecheck clean; ESLint clean on every changed file; file-size (rebaselined for the combined growth), complexity, cognitive-complexity, changelog-integrity, docs-counts, docs-sync, migration-numbering and i18n new-key gates green; 735 focused node:test cases with the only batch-caused failure (a flag-count assertion) fixed. Then re-validated alone on the fresh release tip right before this merge: ESLint on the changed files, typecheck:core, check:open-sse-typecheck, the file-size/complexity/changelog gates and this PR's own tests.

Thanks @maxmad64bis!
2026-09-15 16:10:52 -03:00
Dizzle
611342609f fix(combo): return 502 for non-quota protected-priority stops (#13439)
Behind the new `PROTECTED_PRIORITY_INFRA_502_ENABLED` flag (default off), protected-priority combo stops caused by provably non-quota infrastructure (provider circuit open, predictive-TTFT latency) surface as 502 instead of a quota-looking 503.

Maintainer rework before merge (kept the idea, no default behavior change):
- The original branch made 502 the default for every stop, including model lockouts and cooldowns, and removed the #8133/#1731 provider-wide skip for 401/5xx without a connection id; both are restored with their regression tests untouched.
- Nineteen cases cover eight gate causes plus predictive latency, flag off and on.

Validated first on the combined board of all 38 PRs of this batch (10 merged as-is, 28 after the maintainer rework) on top of release/v3.8.51 c0f92ec: typecheck:core, check:open-sse-typecheck and check:dashboard-typecheck clean; ESLint clean on every changed file; file-size (rebaselined for the combined growth), complexity, cognitive-complexity, changelog-integrity, docs-counts, docs-sync, migration-numbering and i18n new-key gates green; 735 focused node:test cases with the only batch-caused failure (a flag-count assertion) fixed. Then re-validated alone on the fresh release tip right before this merge: ESLint on the changed files, typecheck:core, check:open-sse-typecheck, the file-size/complexity/changelog gates and this PR's own tests.

Thanks @maxmad64bis!
2026-09-15 15:46:57 -03:00
Dizzle
ac52d4d9ea fix(sse): omit synthetic Retry-After and mark retry provenance on drain path (#13672)
Behind the new `RETRY_AFTER_PROVENANCE_ENABLED` flag (default off): `unavailableResponse` omits the synthetic `Retry-After: 1` when there is no real retry signal, marks `retry_after_provenance` on its bodies, and both combo drain readers parse prose retry hints from plain-text bodies too. With the flag off, headers and bodies are exactly as before.

Maintainer rework before merge (kept the idea, no default behavior change):
- A past `Retry-After` date is no longer labelled as an upstream signal with `Retry-After: 1`; non-JSON bodies (HTML 502 pages) log at debug instead of warning on every request.
- The provenance claim is narrowed to responses built by `unavailableResponse`, documented in the flag row.

Validated first on the combined board of all 38 PRs of this batch (10 merged as-is, 28 after the maintainer rework) on top of release/v3.8.51 c0f92ec: typecheck:core, check:open-sse-typecheck and check:dashboard-typecheck clean; ESLint clean on every changed file; file-size (rebaselined for the combined growth), complexity, cognitive-complexity, changelog-integrity, docs-counts, docs-sync, migration-numbering and i18n new-key gates green; 735 focused node:test cases with the only batch-caused failure (a flag-count assertion) fixed. Then re-validated alone on the fresh release tip right before this merge: ESLint on the changed files, typecheck:core, check:open-sse-typecheck, the file-size/complexity/changelog gates and this PR's own tests.

Thanks @maxmad64bis!
2026-09-15 15:25:43 -03:00
Dizzle
45c54ca8aa fix(dashboard): require provider free-tier for the Free badge (#13645)
The provider-page Free badge can require a free tier the provider actually honors: behind the new `FREE_BADGE_REQUIRES_PROVIDER_FREE_TIER` flag (default off) the display-name "free" heuristic, non-boolean `free` fields and `:free` suffixes on registered providers without a documented free tier no longer light the badge. With the flag off the historical rule is unchanged.

Maintainer rework before merge (kept the idea, no default behavior change):
- `:free` models on free-tier providers and on compatible nodes (OpenRouter-style endpoints) keep the badge in both modes — the original change dropped them.
- Test D derives its provider set from `FREE_MODEL_BUDGETS` instead of a hard-coded allowlist; a vitest render covers both sections with the flag endpoint on, off and erroring.

Validated first on the combined board of all 38 PRs of this batch (10 merged as-is, 28 after the maintainer rework) on top of release/v3.8.51 c0f92ec: typecheck:core, check:open-sse-typecheck and check:dashboard-typecheck clean; ESLint clean on every changed file; file-size (rebaselined for the combined growth), complexity, cognitive-complexity, changelog-integrity, docs-counts, docs-sync, migration-numbering and i18n new-key gates green; 735 focused node:test cases with the only batch-caused failure (a flag-count assertion) fixed. Then re-validated alone on the fresh release tip right before this merge: ESLint on the changed files, typecheck:core, check:open-sse-typecheck, the file-size/complexity/changelog gates and this PR's own tests.

Thanks @maxmad64bis!
2026-09-15 13:57:39 -03:00
Diego Rodrigues de Sa e Souza
c8b24ffc30 fix(authz): gate the cli-tools status and skills execution routes to LOCAL_ONLY (#13745)
GHSA-35fw-cv32-2373 and GHSA-jx89-f37j-pq89 — the same defect class as
/api/acp/agents (GHSA-hf57): a route whose handler chain spawns a host process
was classified Tier 3 MANAGEMENT only, and requireManagementAuth() waives auth
when requireLogin=false. Hard Rules #15/#17 require the LOCAL_ONLY gate, which
runs on the stamped real peer before any auth check.

cli-tools (GHSA-35fw): 14 routes reach
getCliRuntimeStatus() -> locateCommand() -> runProcess("sh", ["-c",
'command -v -- "$1"']) -> spawn(), exactly like their six gated siblings
(forge/grok-build/jcode/qwen/omp/letta-settings):
all-statuses, status, and the claude/cline/codewhale/codex/crush/deepseek-tui/
droid/kilo/openclaw/pi/smelt-settings routes. The advisory counted 13; it
missed /api/cli-tools/detect, which is heavier — detectAllTools() runs
execFile(binary, ["--version"]) and execFile("which") per tool.

skills (GHSA-jx89): POST /api/skills/install stores the request's handlerCode
verbatim as the skill handler with no allowlist, so a value equal to a built-in
name (execute_command / eval_code) aliases the real sandboxed built-in;
POST /api/skills/executions then runs it. The sandbox is a real container, but
the spawn is transitive, which is why the 6A.8 source scan never flagged it.

Entries are exact paths, not a /api/cli-tools/ blanket prefix: apply, backups,
config, guide-settings, hermes-agent-settings, keys, logs, openclaw/auto-order
and codex-profiles do not spawn and remote dashboards use them. All 16 are
mirrored into SPAWN_CAPABLE_PREFIXES (no manage-scope bypass) and added to the
route-guard-membership roots so the gate enforces them from now on.

Functional trade-off, same one already accepted for grok/forge/jcode/qwen: a
dashboard served through a tunnel no longer shows the CLI Tools status badges.

Tests are red-first. Two existing negative controls pointed at routes that turn
out to spawn (/api/cli-tools/all-statuses, /api/skills/install); they now point
at routes that genuinely do not (/api/cli-tools/config, /api/skills/marketplace,
/api/skills/skillssh/install), so the non-over-gating assertions are kept.
2026-09-15 13:25:35 -03:00
Diego Rodrigues de Sa e Souza
e7f9fec251 fix(api): enforce API-key ownership on files and batches — null-owner records and anonymous listing (#13749)
GHSA-2jm2-mpx8-6523 and GHSA-m3hp-hq9g-fpmv, one root cause.

`getApiKeyRequestScope()` never rejects: with the default REQUIRE_API_KEY=false
the client-api policy admits both a missing and an invalid bearer as anonymous,
and the scope comes back `{ apiKeyId: null, isSessionAuth: false }`. The
`/v1/files` and `/v1/batches` routes then treated "null" as permissive in two
different ways:

- GHSA-m3hp — the list routes coerced `apiKeyId || undefined`, and the DB layer
  reads `undefined` as "no owner filter", so an anonymous or invalid-bearer
  caller got every tenant's file and batch metadata, the same unfiltered view as
  the operator's dashboard.
- GHSA-2jm2 — the single-record checks were `record.apiKeyId !== null && …`, so
  a record with no owner short-circuited to "allowed" for any caller: read,
  download raw content, delete, cancel, or use as a batch input. Null-owner
  records are common — every dashboard-session upload, and every batch output
  file inheriting a session batch's owner, which carries model responses.

`api_key_id` has existed since the table was created (migration 028), so a null
owner is not a legacy row; it is an unattributable write. No doc described it
as shared — API_REFERENCE says files are scoped per key — and batch_api.test.ts
pinned the by-id exposure as expected behaviour.

One rule now, in `_helpers/apiKeyScope.ts`:

- `canAccessOwnedRecord(scope, owner)`: a dashboard session is the instance
  operator and may act on any record; an API key acts on its own records only;
  a null owner is denied to every non-session caller. Applied to files
  GET / DELETE / content, batches GET / DELETE / cancel, and the batch-create
  input-file check.
- `resolveListScope(scope)`: an explicit union for list/count reads — scoped to
  the presented key (a key wins even alongside a session cookie), instance-wide
  only for a session without a key, and 401 otherwise, including for a bearer
  that does not resolve to a key. There is no default that widens a read.

This follows the GHSA-wvxc shape already used by the delete-completed sweep.

Behaviour change: the anonymous upload → batch → download flow no longer works
without an API key, because a null owner cannot be attributed.

Subsumes #13683: it moved `scopeCheck` into the shared helper so a session can
cancel any batch — kept, and its test ported — but it also kept null-owner
records open on the premise they predate ownership tracking, which migration
028 contradicts.

Tests are red-first. batch_api's by-id case is flipped to 404 with a negative
assertion; batch-deletion-route-logic now imports the real helper instead of a
local copy that had silently diverged from production; the two integration
tests present a real key, since their subject is limits and rate logging, not
auth.

Co-authored-by: Markus Hartung <mail@hartmark.se>
2026-09-15 13:25:13 -03:00
Dizzle
cb420db64b fix(call-logs): hide search rows without a live provider (#13641)
Search stats and recent searches stop surfacing ghost rows: NULL and `-` providers are always hidden, and, behind the new `SEARCH_STATS_HIDE_DELETED_CONNECTIONS` flag (default off), traffic of a keyed provider whose connection was deleted is hidden too. Totals use the same guard as the per-provider rows, so they always agree.

Maintainer rework before merge (kept the idea, no default behavior change):
- Keyless providers from the search registry (`duckduckgo-free`, `searxng-search`, anonymous `context7`) and providers served through a credential fallback (`perplexity-search` on a `perplexity` key) stay visible in both modes — the original filter dropped them because they have no `provider_connections` row.
- Tests use real registry ids and cover flag off (historical stats) and flag on, including the analytics route; #13281's changelog fragment restored.

Validated first on the combined board of all 38 PRs of this batch (10 merged as-is, 28 after the maintainer rework) on top of release/v3.8.51 c0f92ec: typecheck:core, check:open-sse-typecheck and check:dashboard-typecheck clean; ESLint clean on every changed file; file-size (rebaselined for the combined growth), complexity, cognitive-complexity, changelog-integrity, docs-counts, docs-sync, migration-numbering and i18n new-key gates green; 735 focused node:test cases with the only batch-caused failure (a flag-count assertion) fixed. Then re-validated alone on the fresh release tip right before this merge: ESLint on the changed files, typecheck:core, check:open-sse-typecheck, the file-size/complexity/changelog gates and this PR's own tests.

Thanks @maxmad64bis!
2026-09-15 12:48:06 -03:00
Diego Rodrigues de Sa e Souza
58f88a83e4 feat(i18n): 7 new locales — Hausa, Yoruba, Igbo, Amharic, Uzbek, Georgian, Armenian (66 locales) (#13727)
Batch 3 (last) of the locale-expansion plan: ha, yo, ig, am, uz, ka, hy on every surface — dashboard catalog, docs mirror (22-file core + llm.txt + CHANGELOG), CLI catalog, README flag block, locale tables and 🌐 language bars. Also closes the key gap the batch-1 (43 keys) and batch-2 (10 keys) catalogs carried since their base merges, fixes the Igbo "Model" copy and allowlists the Uzbek cognate. Translation-ratio baseline covers 65 locales.

⚠️ base-red inherited: #12732
2026-09-15 09:50:01 -03:00
Diego Rodrigues de Sa e Souza
d36251cf1c fix(ci): clear five uncovered base-reds on release/v3.8.51 — auto-combo catalog event-loop pin, unweighted quota-share, resilience key set, pack-gate stamp, synced-catalog env doc (#13678)
* fix(combo): rotate unweighted quota-share targets instead of pinning the first

The combo resolver turns an unset step weight into 0 (comboStructure.ts), and
#10881 made normalizeWeight treat 0 as disabled plus return definition order when
the total weight is 0. A quota-share combo without explicit weights therefore had
no DRR quanta and dispatched every request to its first target — the
combo-matrix/quota-share integration suite saw openai six times out of six.

An all-zero set is now an unweighted combo and shares evenly; an explicit 0 still
disables a target when its siblings are weighted.

Refs #12732

* fix(models): resolve auto-combo target metadata once per catalog build

#12046 derives vision/modalities for the built-in auto/* combos by resolving
catalog metadata for every target of every combo. The ~40 auto combos share one
candidate pool and the loop neither memoized nor yielded, so the #9147 fixture
(60 connections, 720 synced models) went from a ~4s build with a 167ms longest
event-loop gap to ~11s and a 860-1070ms gap on an idle box — past the 800ms
contract and past #12628's 8s cold-build bound, which is why the test came back
500 catalog_build_timeout on every release-green run.

Metadata depends only on the target's provider/model/connection scope within a
build, so memoize it per build and yield between misses. Same fixture: 2.3-3.1s
build, 56-72ms longest gap. The 9147 test is unchanged.

Refs #12732

* test(resilience): list credentialHealthCheck in the configuration-only key set

#12043 added credentialHealthCheck.intervalMinutes to DEFAULT_RESILIENCE_SETTINGS
and to the /api/resilience GET projection. It is operator configuration (the
background sweep cadence), not runtime breaker state, but the exact key-set
assertion was never updated, so resilience-http-e2e failed on the release tip.
The providerBreakers/runtime absence checks stay as they were.

Refs #12732

* fix(ci): stamp BUILD_SHA before the release-green pack gate validates

check:pack-artifact assembles dist/ through build:cli, which never writes
dist/BUILD_SHA (only build:release does). #12959 pointed the provenance ref at
HEAD, but the #10427 guard still stops at 'dist/BUILD_SHA is missing' before it
ever reaches the ancestry check — reproduced on tip + #13635 + #13436, the first
tree whose Turbopack build compiles.

ci.yml sequences build -> stamp -> validate; the validator now does the same in
both entry points, keeping PACK_GATE_ENV for the validate step. The guard is
unchanged: an unstamped dist/ or one built from another commit still fails.
On that tree the stamped gate passes: 'BUILD_SHA 5cb3ae5d9 is on the release line'.

Refs #12732

* docs(env): document OMNIROUTE_SYNCED_CATALOG_STALE_AFTER_MS

#13248 (#12849) added the override for when a connection's synced model list
stops being authoritative, but neither .env.example nor ENVIRONMENT.md lists it,
so the env/docs contract gate reports it as code-only. The other five vars that
gate reports are already added by #13635 and #13361; this touches a different
region of .env.example so it does not collide with either.

Refs #12732
2026-09-15 09:00:51 -03:00
Koosha Paridehpour
d6e62ae394 fix(free-tiers): remove obsolete Together $25 signup credit (#12526) (#13407)
Removes the obsolete Together $25 signup credit (`together-signup-credit`, 25M one-time tokens) from `FREE_MODEL_BUDGETS` and updates the Together notes in `FREE_TIERS.md` (#12526). Approved by the maintainer. Chosen over #12766, which targeted `main` and only touched the doc.

Maintainer additions: propagated the catalog change to every derived number instead of hand-estimating it. The regenerated budget card (`gen-budget-card-svg.mjs`) computes first month ~2.07B (the doc said ~2.08B), 443 catalog entries (436 active + 7 discontinued) and one-time credits ~601M. README, `FREE_TIERS.md`, `FREE-TIERS-GUIDE.md` (Together row removed) and `docs/diagrams/free-tier-budget.svg` were aligned to those values, and Prettier-formatted. `check:docs-counts` and the budget-card, docs-counts and free-catalog tests pass (99 cases).

Validated in one consolidated batch of this series (37 PRs boarded together on `release/v3.8.51`): `typecheck:core`, `check:open-sse-typecheck` and `check:dashboard-typecheck` clean; ESLint clean on every changed file; file-size, complexity, cognitive-complexity, changelog-integrity, docs-counts, docs-sync and migration-numbering gates green (only the pre-existing `open-sse/utils/stream.ts` file-size red remains, inherited from the base); 3,743 focused `node:test` cases plus 34 vitest cases green.

Thanks @KooshaPari!
2026-09-15 00:01:27 -03:00
Koosha Paridehpour
0b358dbf64 fix(backend): guard memory_fts_au trigger with WHEN clause to stop FTS bloat (#13326) (#13405)
Stops `memory_fts` bloat (#13326). The `memory_fts_au` trigger re-indexed the FTS row on every UPDATE of `memories`, including the `access_count` / `last_accessed_at` bumps from `recordMemoryAccess()`, so FTS data rows grew without bound. The trigger is recreated with `WHEN old.content IS DISTINCT FROM new.content OR old.key IS DISTINCT FROM new.key`, an `optimize` pass compacts existing tombstones, and `cleanupMemoryEntries()` runs `optimize` after retention deletes.

Maintainer additions: migration 177 was already taken by `177_provider_connection_synced_models_at.sql` (#13248), and a duplicate number aborts every DB open. Renumbered to `178_memory_fts_au_conditional.sql` and bumped the migration count 174→175 in README, AGENTS.md, `llm.txt` and its 58 locale mirrors (protected surfaces, operator-approved for this PR). The migration also applied cleanly on a real long-lived dev database. `check:migration-numbering`, `check:docs-counts` and `check-docs-sync` pass.

Validated in one consolidated batch of this series (37 PRs boarded together on `release/v3.8.51`): `typecheck:core`, `check:open-sse-typecheck` and `check:dashboard-typecheck` clean; ESLint clean on every changed file; file-size, complexity, cognitive-complexity, changelog-integrity, docs-counts, docs-sync and migration-numbering gates green (only the pre-existing `open-sse/utils/stream.ts` file-size red remains, inherited from the base); 3,743 focused `node:test` cases plus 34 vitest cases green.

Thanks @KooshaPari!
2026-09-14 23:58:27 -03:00
Koosha Paridehpour
918546acf2 fix(resilience): increase requestQueue.maxWaitMs default from 15s to 30s (#13553)
Raises the default request-queue wait (`RATE_LIMIT_MAX_WAIT_MS`) from 15s to 30s, so bursts behind a rate-limited provider queue a bit longer before being rejected (#13504). The env var still overrides it, and `executionMaxWaitMs` (the 10-minute execution backstop) is unchanged. Approved by the maintainer as a policy change.

Maintainer additions: updated the documented default in `.env.example` and `docs/reference/ENVIRONMENT.md` to match.

Validated in one consolidated batch of this series (37 PRs boarded together on `release/v3.8.51`): `typecheck:core`, `check:open-sse-typecheck` and `check:dashboard-typecheck` clean; ESLint clean on every changed file; file-size, complexity, cognitive-complexity, changelog-integrity, docs-counts, docs-sync and migration-numbering gates green (only the pre-existing `open-sse/utils/stream.ts` file-size red remains, inherited from the base); 3,743 focused `node:test` cases plus 34 vitest cases green.

Thanks @KooshaPari!
2026-09-14 23:53:18 -03:00
Koosha Paridehpour
222fe4314e docs(providers): add Microsoft 365 Copilot (BizChat) provider guide (#12779) (#13340)
Adds the Microsoft 365 Copilot (BizChat) provider guide for the existing `copilot-m365-web` provider (alias `m365copilot`, `open-sse/config/providers/registry/copilot-m365-web`), including the WebSocket credential capture flow, and links it from the docs index and the web-cookie guide (#12779).

Validated in one consolidated batch of this series (37 PRs boarded together on `release/v3.8.51`): `typecheck:core`, `check:open-sse-typecheck` and `check:dashboard-typecheck` clean; ESLint clean on every changed file; file-size, complexity, cognitive-complexity, changelog-integrity, docs-counts, docs-sync and migration-numbering gates green (only the pre-existing `open-sse/utils/stream.ts` file-size red remains, inherited from the base); 3,743 focused `node:test` cases plus 34 vitest cases green.

Thanks @KooshaPari!
2026-09-14 23:24:59 -03:00
Koosha Paridehpour
fc111dc196 feat(cli): make startup readiness budget configurable (#13369) (#13433)
The CLI readiness budget is configurable through `OMNIROUTE_READY_TIMEOUT_MS` or `omniroute serve --ready-timeout <ms>` (default unchanged at 60s). The timeout warning prints the budget it actually used and suggests a larger value, for slow cold starts such as Windows (#13369). Documented in `ENVIRONMENT.md` and `TROUBLESHOOTING.md`, with 11 resolver cases.

Validated in one consolidated batch of this series (37 PRs boarded together on `release/v3.8.51`): `typecheck:core`, `check:open-sse-typecheck` and `check:dashboard-typecheck` clean; ESLint clean on every changed file; file-size, complexity, cognitive-complexity, changelog-integrity, docs-counts, docs-sync and migration-numbering gates green (only the pre-existing `open-sse/utils/stream.ts` file-size red remains, inherited from the base); 3,743 focused `node:test` cases plus 34 vitest cases green.

Thanks @KooshaPari!
2026-09-14 23:24:46 -03:00
Diego Rodrigues de Sa e Souza
8f3621156d docs(i18n): sync provider and migration counts in the eight new llm.txt mirrors (#13674)
Unblocks the pre-commit docs-sync gate on the release tip.
2026-09-14 19:00:38 -03:00
Diego Rodrigues de Sa e Souza
0d13ef4fbb feat(i18n): 8 new locales — Kannada, Malayalam, Odia, Punjabi, Nepali, Sinhala, Burmese, Khmer (59 locales) (#13660)
Batch 2 of the locale expansion across the dashboard catalog, docs mirrors, CLI catalog, README, locale index and the site. 51 → 59 locales.

Also: the translator restores the ICU literal escape around angle placeholders, and the docs chunker splits oversized sections before translating.

⚠️ base-red inherited: #12732 — the eight red checks fail identically on unrelated PRs cut from the same base (e.g. #13197); every gate is green locally after merging the base.
2026-09-14 18:22:01 -03:00
Diego Rodrigues de Sa e Souza
963d3c46ef fix(ci): clear three base-reds on release/v3.8.51 — open-sse typecheck, docs provider count, agent-skills sync (#13216)
Merged after reconciling against the tip. The one conflict was `docs/reference/PROVIDER_REFERENCE.md`, a generated file — regenerated with `npm run gen:provider-reference` (358 unique IDs) rather than hand-merged.

The three base-reds this PR targets, re-checked on the reconciled tree:

- `check:docs-counts`: **exit 0** — provider count 356 → 358 in AGENTS.md, llm.txt, the i18n mirrors and the SVG diagrams was its last red
- `check:open-sse-typecheck`: OK, 0 errors
- `check:agent-skills-sync`: exit 0 (`skills/cli-tunnel/SKILL.md` → `tunnel create [type]`)
- `autoCombo.test.ts` under the mcp vitest config: 63/63
- ESLint over the changed files: exit 0

AGENTS.md, llm.txt and SKILL.md are agent-instruction surfaces; the operator approved them for this PR explicitly.
2026-09-14 14:13:45 -03:00
Diego Rodrigues de Sa e Souza
30bf6affe9 fix(nvidia): fail open when a synced model catalog goes stale (#12849) (#13248)
Merged after renumbering. `176_provider_connection_synced_models_at.sql` collided with `176_xp_action_counts.sql` (#12651), which made the migration runner abort on every DB open. Renamed to **177**; the doc count moves 173 → 174 across README.md, AGENTS.md, llm.txt and the i18n mirrors (operator-approved, 206 numeric substitutions and nothing else).

- `check:migration-numbering`: OK, 174 migrations, no duplicates
- `check:docs-counts` migrations: ✓
- 84/84 across the NVIDIA suite plus the seven DB-touching suites the collision had taken down
- ESLint and `typecheck:core`: exit 0

Heads-up for whoever lands next: **177 is claimed by eight other open PRs** (#13610, #13602, #13580, #13554, #13405, #13331, #13177, #13116) and 176 by #13373 and #13102. With this merged, all of them need to renumber at merge time — `check:migration-numbering` forbids new gaps, so the next free number is always the only valid one.

⚠️ base-red inherited: #12732
2026-09-14 14:09:14 -03:00
Diego Rodrigues de Sa e Souza
f7dbfc88c7 chore(tests): revive 51 vitest files parked behind a closed tracker (#13204) (#13233)
Merged after reconciling against the tip. `docs/architecture/QUALITY_GATES.md` conflicted: this PR adds the `check:vitest-exclusions` row while the tip had meanwhile rewritten the `check:model-lifecycle` description (#12535). Resolved to the tip's table plus the new row.

- `check:vitest-exclusions`: OK, 11 excluded files, each tracked and referenced
- ESLint over the changed files: exit 0
- `npm run test:vitest` (the config this PR edits): 472 of 473. The one red, `tests/unit/autoCombo/provider-family-combos.test.ts`, is not touched by this PR and passes 11/11 in isolation on both this branch and the pure tip — a load flake from the full run, not a regression.

⚠️ base-red inherited: #12732
2026-09-14 13:40:46 -03:00
Diego Rodrigues de Sa e Souza
cf2d29d2ad fix(memory): authenticate internal /v1/rerank loopback call (#12745) (#13268)
Merged as part of the owner batch of 2026-09-11.

This PR had a live worktree in another session, so it sat outside the main 39. Merged on your explicit call, validated first rather than taken on trust: boarded with the other 10 worktree-held PRs into a consolidated worktree off `release/v3.8.51`.

- ESLint over every changed file: no errors
- `typecheck:core` clean; `check:dashboard-typecheck` OK; `check:changelog-integrity` OK
- complexity 2821 / baseline 3218 and cognitive-complexity 1272 / baseline 1437
- 203 of 208 assertions green. The 5 remaining (`guide-settings-route` ×4, `hard-session-lease-bypass-inventory` ×1) reproduce on the pure tip with nothing from this batch applied.
- `imageGeneration.ts` rebaselined 3259 → 3293 for #12945's image-only-model guard, landed separately in #13392 so nothing was pushed onto a live branch.

⚠️ base-red inherited: #12732 — provider count 356 vs 358 and `open-sse/utils/stream.ts` 3115 > frozen 3098, both reproducing on the pure tip.
2026-09-11 22:29:25 -03:00
Diego Rodrigues de Sa e Souza
db6feb6fad fix(auth): dashboard session requires the authenticated claim — Cursor CLI tokens are not sessions (#13375)
Merged as part of the owner batch of 2026-09-11.

This PR had a live worktree in another session, so it sat outside the main 39. Merged on your explicit call, validated first rather than taken on trust: boarded with the other 10 worktree-held PRs into a consolidated worktree off `release/v3.8.51`.

- ESLint over every changed file: no errors
- `typecheck:core` clean; `check:dashboard-typecheck` OK; `check:changelog-integrity` OK
- complexity 2821 / baseline 3218 and cognitive-complexity 1272 / baseline 1437
- 203 of 208 assertions green. The 5 remaining (`guide-settings-route` ×4, `hard-session-lease-bypass-inventory` ×1) reproduce on the pure tip with nothing from this batch applied.
- `imageGeneration.ts` rebaselined 3259 → 3293 for #12945's image-only-model guard, landed separately in #13392 so nothing was pushed onto a live branch.

⚠️ base-red inherited: #12732 — provider count 356 vs 358 and `open-sse/utils/stream.ts` 3115 > frozen 3098, both reproducing on the pure tip.
2026-09-11 22:29:12 -03:00
Diego Rodrigues de Sa e Souza
b97bc59f4f fix(docker): require a per-session token on the VNC browser CDP bridge (#12571) (#13241)
Merged as part of the owner batch of 2026-09-11.

This PR had a live worktree in another session, so it sat outside the main 39. Merged on your explicit call, validated first rather than taken on trust: boarded with the other 10 worktree-held PRs into a consolidated worktree off `release/v3.8.51`.

- ESLint over every changed file: no errors
- `typecheck:core` clean; `check:dashboard-typecheck` OK; `check:changelog-integrity` OK
- complexity 2821 / baseline 3218 and cognitive-complexity 1272 / baseline 1437
- 203 of 208 assertions green. The 5 remaining (`guide-settings-route` ×4, `hard-session-lease-bypass-inventory` ×1) reproduce on the pure tip with nothing from this batch applied.
- `imageGeneration.ts` rebaselined 3259 → 3293 for #12945's image-only-model guard, landed separately in #13392 so nothing was pushed onto a live branch.

⚠️ base-red inherited: #12732 — provider count 356 vs 358 and `open-sse/utils/stream.ts` 3115 > frozen 3098, both reproducing on the pure tip.
2026-09-11 22:29:05 -03:00
Diego Rodrigues de Sa e Souza
1361a9dd88 fix(sse): add first-byte watchdog to the TLS-fingerprint transport (#12656) (#13272)
Merged as part of the 39-PR owner batch of 2026-09-11, validated as a unit.

Boarded into one consolidated worktree cut from `release/v3.8.51` with the other 38 — zero conflicts between them.

- ESLint over every changed file: no errors (the only finding was one suppression entry the batch emptied, pruned on #13243)
- `typecheck:core` clean; `check:dashboard-typecheck` OK (206 pre-existing, within baseline); `check:changelog-integrity` OK
- complexity 2821 / baseline 3218 and cognitive-complexity 1272 / baseline 1437 — both under baseline
- 256 assertions green: 246 under node:test and 10 under vitest, which is where `tests/unit/**/*.test.tsx` actually runs
- `check-file-size`: `chatCore.ts` rebaselined 6144 → 6146 for #13278 and #13276, annotated and landed on #13243

⚠️ base-red inherited: #12732 — the provider count (356 in the docs vs the 358 the modules define) and `open-sse/utils/stream.ts` at 3115 > frozen 3098 both reproduce on the pure tip with zero contribution from this batch.
2026-09-11 22:06:09 -03:00
Diego Rodrigues de Sa e Souza
57a37ed858 fix(docker): bind app/sidecar compose ports to loopback by default (#12568, #12578) (#13240)
Merged as part of the 39-PR owner batch of 2026-09-11, validated as a unit.

Boarded into one consolidated worktree cut from `release/v3.8.51` with the other 38 — zero conflicts between them.

- ESLint over every changed file: no errors (the only finding was one suppression entry the batch emptied, pruned on #13243)
- `typecheck:core` clean; `check:dashboard-typecheck` OK (206 pre-existing, within baseline); `check:changelog-integrity` OK
- complexity 2821 / baseline 3218 and cognitive-complexity 1272 / baseline 1437 — both under baseline
- 256 assertions green: 246 under node:test and 10 under vitest, which is where `tests/unit/**/*.test.tsx` actually runs
- `check-file-size`: `chatCore.ts` rebaselined 6144 → 6146 for #13278 and #13276, annotated and landed on #13243

⚠️ base-red inherited: #12732 — the provider count (356 in the docs vs the 358 the modules define) and `open-sse/utils/stream.ts` at 3115 > frozen 3098 both reproduce on the pure tip with zero contribution from this batch.
2026-09-11 22:05:14 -03:00