Commit Graph

1450 Commits

Author SHA1 Message Date
Diego Rodrigues de Sa e Souza
d1df82eca0 feat(quality): 6A.1+6A.2 — test-discovery gate, 135 orphan tests re-wired, 2 production bug fixes, vitest in CI (#3536)
check-test-discovery gate (TDD; 195 orphans found, 135 re-wired into the node runner, 60 frozen+annotated). Triage fixed 2 real production bugs: missing BYPASS_PREFIX_NOT_ALLOWED zod refine (spawn-capable prefixes accepted into the bypass list, Hard Rules #15/#17) and resetDbInstance not firing stateReset resetters (stale schema memo → 503 instead of 403; also hit backup-restore). New test-vitest CI job: test:vitest blocking (146/146), test:vitest:ui informational (14 pre-existing fails, triage 2026-06-16).
2026-06-09 20:23:04 -03:00
Diego Rodrigues de Sa e Souza
5f2722bd6c feat(quality): quality-gate ratchet + anti-hallucination/rule-enforcement guardrails (Phases 0-6) (#3471)
* feat(quality): generic ratchet comparator (multi-metric, regression-only)

* chore(ci): Fase 0 quality-gate fixes — reconcile coverage gate (40->60), tier npm audit, wire orphaned contract gates, re-enable cheap husky pre-commit

* feat(quality): ratchet engine (collector + frozen baseline + CI job) and provider-consistency gate

- collect-metrics.mjs: emits quality-metrics.json (ESLint warnings + coverage when present)
- quality-baseline.json: frozen baseline (eslintWarnings=3482, regression-only)
- ci.yml: quality-gate job (ratchet + step summary + artifact) and check:provider-consistency in lint job
- check-provider-consistency.ts: every REGISTRY id must be a canonical provider (found krutrim half-registered → allowlisted as known pre-existing, blocks any NEW orphan)
- TDD: 9 tests (5 ratchet + 4 provider-consistency)

* feat(quality): Fase 2 anti-hallucination gates — fetch-targets, openapi-routes, deps allowlist

- check-fetch-targets: every dashboard fetch(/api/...) resolves to a real route.ts; found 7 pre-existing dashboard->route mismatches frozen as KNOWN_MISSING for triage
- check-openapi-routes: every openapi.yaml path resolves to a real route; found 1 stale spec entry (agent-bridge agents/{id}/state) frozen as KNOWN_STALE_SPEC
- check-deps: anti-slopsquatting allowlist (105 deps); new deps need explicit human-reviewed entry
- all wired into CI lint/docs jobs; TDD +12 tests (21 total across 5 gates)

* docs(quality): add quality-gates report + implementation plan to repo root

* feat(quality): Fase 3a — file-size ratchet (freeze 91 files >800 LOC, cap 800 for new)

- check-file-size.mjs: frozen files can only shrink; new files must be <= cap (kills the next 12k-line god-component)
- file-size-baseline.json: 91 files frozen at current LOC (largest 12883)
- wired into CI lint job; TDD 5 tests; --update ratchets the baseline down on shrink

* feat(quality): Fase 3b — duplication ratchet (jscpd@4, baseline 5.72%)

- check-duplication.mjs: runs jscpd@4 (pinned; v5 is an incompatible Rust rewrite) over src+open-sse, fails if duplication % rises vs frozen baseline (5.72%, measured: 1358 clones / 22967 dup lines). Targets the executor copy-paste (48/50 override execute() wholesale)
- wired into the parallel quality-gate CI job (off the lint critical path); TDD 4 tests; --update ratchets down
- snapshot now complete: coverage ~82.6%, eslint 3482 (98.5% no-explicit-any), duplication 5.72%, 91 files >800 LOC

* feat(quality): Fase 4a — anti test-masking gate

- check-test-masking.mjs: for each MODIFIED test file in a PR, flags net assert removal + new assert.ok(true) tautologies (base...HEAD diff). Directly enforces CLAUDE.md 'never weaken asserts to go green'
- wired into pr-test-policy CI job (reuses base fetch); no-op outside PR; TDD 5 tests

* feat(quality): Fase 4b — coverage ratchet (conservative floors, CI consumes merged coverage)

- quality-baseline.json: coverage.{statements,lines,functions,branches} floors (80/80/82/73, real ~82.58/82.58/84.23/75.22 with margin; tighten via --update after a green main run)
- check-quality-ratchet.mjs: --allow-missing (local quality:gate skips coverage.* without a coverage run; CI runs strict)
- ci.yml quality-gate job: needs test-coverage + downloads merged coverage-report so the ratchet enforces 'coverage cannot drop'
- TDD +1 test (6 total)

* feat(quality): Fase 6 — 8 new gates (Rule #11/#12, migrations, known-symbols, route-guard, complexity, docs-symbols, db-rules)

Deterministic gates, each freezing pre-existing violations in a documented allowlist (ratchet) so they pass now and block only NEW regressions:
- check-error-helper (Rule #12): 7 executors/handlers forwarding raw err.message frozen
- check-public-creds (Rule #11): 5 literal client_ids (Claude/Codex/Qwen/Kimi/Copilot) frozen
- check-migration-numbering: gaps 026/055 + dup 041 frozen (prevents the git-rm-deleted-migration incident)
- check-known-symbols: 93 executors conformance + 15 combo strategies + 18 translator pairs
- check-route-guard-membership (#15/#17): all 25 spawn-capable routes verified local-only (0 gaps)
- check-complexity: cyclomatic>15 / fn-length>80 ratchet (baseline 1739)
- check-docs-symbols: 30 stale doc /api refs frozen (docs hallucination)
- check-db-rules (#2/#5): 25 unexported db modules + 15 raw-SQL routes frozen
Wired into CI (lint / docs-sync-strict / quality-gate jobs). 115 TDD tests, all green. ESLint ratchet held at 3482.

* docs(quality): Phase 7 plan (security/dead-code/mutation/community tooling) — GATED to 2026-06-16

Stored, not active. 7 suggested gates + all discussed OSS/Community tools (SonarQube Community + osv-scanner + CodeQL + knip + sonarjs + type-coverage + lockfile-lint + Stryker + size-limit + axe-core + semcheck + agent-lsp + Qlty). Activation gate: do not start before 2026-06-16 (use Phases 0-6 in production for 1 week, validate in practice, then evolve).
2026-06-09 17:09:54 -03:00
Diego Rodrigues de Sa e Souza
8169b97d84 Release v3.8.18 (#3482)
* chore(release): open v3.8.18 development cycle

* fix(catalog): stop Codex CLI model-catalog refresh from erroring (#3481)

Codex's model-catalog refresh (codex_models_manager) does
GET /v1/models?client_version=<v> and decodes a JSON object with a
TOP-LEVEL `models` array. OmniRoute answers in the OpenAI-standard
`{object,data}` shape, so codex fails with "missing field `models`"
and logs "failed to refresh available models" on every startup.

Detect codex clients via the `originator` / `user-agent` = `codex_*`
headers they send and add an EMPTY top-level `models: []` so the decode
succeeds. Non-codex OpenAI clients keep the byte-identical `{object,data}`
response.

The array is intentionally empty: codex replaces its built-in per-model
agent prompt (`base_instructions`, ~21k chars) with whatever a populated
entry carries for the selected model, so emitting our catalog would drop
the agent prompt to nothing and break codex's agent behaviour (verified
empirically against codex 0.137). An empty list keeps codex on its
built-in model info — same inference as before, minus the error.

Validated end-to-end with the real handler against codex 0.137:
"failed to refresh available models" → 0 occurrences, instructions
preserved (built-in Codex agent prompt, not empty).

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* chore: ignore quality reports and local prompt artifacts

Add generated quality gate reports, metrics files, and local setup prompt
artifacts to .gitignore to prevent committing environment-specific or
temporary files.

* fix(provider): detect Responses API format when body has `input` but … (#3490)

Integrated into release/v3.8.18

* fix(sse): normalize numeric provider ids to strings (#3451)

Integrated into release/v3.8.18

* feat(browserPool): resolve Playwright proxy from proxy_registry DB (#3492)

Integrated into release/v3.8.18

* fix(theoldllm): generate X-Request-Token server-side, drop Playwright (#3491)

Integrated into release/v3.8.18

* feat(plugins): add lifecycle hooks and theme-manager plugin (#3473)

Integrated into release/v3.8.18

* fix(combo): parallel pre-screen + circuit-breaker fast-exit for priority combos (#3169)

Integrated into release/v3.8.18

* feat(ui): unifi active and finished requests into single view #1422 (#3401)

Integrated into release/v3.8.18

* docs(changelog): record #3401, #3473, #3492, #3490, #3451, #3491, #3169 under v3.8.18

* feat(docs): add doc accuracy gate + refresh AGENTS.md counts (#3510)

Integrated into release/v3.8.18

* fix(sse): drop empty-choices chunks without usage instead of injecting retry text (#3513)

PR #3422 ('allow OpenAI usage-only empty choices chunks') reintroduced the
assistant-content injection '[OmniRoute] Upstream returned an empty response.
Please retry.' for empty `choices: []` chunks that carry no valid usage. Clients
(Goose/opencode) feed that text back as a turn and spin in a retry loop -- the
exact regression #3400 had fixed by dropping the chunk.

Restore the drop behavior for the no-usage case while preserving #3422's
standards-compliant forwarding of usage-only `include_usage` final chunks.
Realign the mislabeled stream-utils test (it asserted the injection) and add a
dedicated regression guard.

Reported-by: @mochizzan
Refs: #3502, #3388, #3400, #3422

* fix(authz): fall back to URL token when Authorization isn't a usable Bearer (#3504)

Integrated into release/v3.8.18

* fix(playground): authenticate via session, test key policy by id (#3503)

Integrated into release/v3.8.18

* docs(changelog): record #3510, #3504, #3503 under v3.8.18

* fix: llama base url normalization (#3519)

* docs(changelog): reconcile v3.8.18 — add #3519, #3513, #3435-repair, gitignore chore (full commit↔changelog coverage)

* fix(opencode-plugin): bound regex quantifiers in normaliseFreeLabel (polynomial-ReDoS)

CodeQL js/polynomial-redos: unbounded \s* before an anchored \s*$ allowed
O(n²) backtracking on attacker-influenced display names. Bounded to {0,8}/{1,8}
(ample for any real label spacing). Plugin builds + 254 tests green.

* fix(types): restore clean typecheck:core for v3.8.18 release gate

- getPendingRequests() typed to real shape (was widened to object) → fixes
  unknown 'count' in the unified-requests view (#3401)
- streamChunks log payload cast to its declared type (callLogs.ts)
- preScreenTargets aligned to canonical IsModelAvailable signature (#3169),
  Promise.resolve-normalized so .catch never hits a bare boolean

All 5 gates green: lint(0 err) + typecheck:core + cycles + docs-all + unit + vitest(146).

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Andrey Borodulin <borodulin@gmail.com>
Co-authored-by: Dmitrii Safronov <zimniy@cyberbrain.cc>
Co-authored-by: Paijo <14921983+oyi77@users.noreply.github.com>
Co-authored-by: PizzaV <103120356+pizzav-xyz@users.noreply.github.com>
Co-authored-by: Markus Hartung <mail@hartmark.se>
Co-authored-by: Felipe Almeman <4226997+zhiru@users.noreply.github.com>
2026-06-09 15:56:24 -03:00
diegosouzapw
259486afb5 chore(release): v3.8.17 — 2026-06-09
CHANGELOG: 8 features, 15 bug fixes, 6 maintenance entries (29 bullets / 32 commits since v3.8.16).
i18n: sync [3.8.17] section to all 41 locale CHANGELOG files.

fix(translator): strip empty reasoning_content on non-tool-call kimi-k2 messages (#3433 regression)
fix(translator): update placeholder assertion for non-empty cache-miss behaviour (test alignment)
fix(executor): lmarena.ts return wrapper shape {response,url,headers,transformedBody} (#3421 regression)
test: align lmarena-provider + tool-request-sanitization to corrected executor contract
2026-06-09 01:51:51 -03:00
Randi
500197846d Add model catalog name feature flag (#3464)
Integrated into release/v3.8.17
2026-06-08 23:58:28 -03:00
Randi
5c3545b045 Add Endpoint Token Saver visibility setting (#3461)
Integrated into release/v3.8.17
2026-06-08 23:36:26 -03:00
Hevener Ancelmo Pereira
1cc2313a4f fix(command-code): align CLI version header (#3462)
Integrated into release/v3.8.17
2026-06-08 23:35:08 -03:00
Randi
49c11f0cea fix(browser): avoid bundling optional cloakbrowser import (#3460)
Integrated into release/v3.8.17
2026-06-08 23:34:07 -03:00
Diego Rodrigues de Sa e Souza
4f38167964 fix(catalog): surface imported models on no-auth providers in /api/v1/models (#3200) (#3463)
The custom-models loop in getUnifiedModelsResponse gated every model through hasEligibleConnectionForModel(getConnectionsForProvider(...)). no-auth providers (theoldllm, etc.) never create DB connection rows, so that returned [] and the gate dropped every imported/custom model for them — the Playground dropdown showed nothing for imported models while built-in/custom models on auth providers worked. Built-in models survived because they go through providerSupportsModel(), which already has a no-auth bypass (#2798).

The custom-model gate now applies the same no-auth bypass, keeping the eligibility check (with parentProviderType) intact for auth providers.

Co-authored-by: tjengbudi <tjengbudi@users.noreply.github.com>
Co-authored-by: a2belugin <a2belugin@users.noreply.github.com>
2026-06-08 22:53:22 -03:00
Diego Rodrigues de Sa e Souza
ff65652cdf fix(claude): respect client anthropic-beta instead of forcing thinking/effort betas (#3415) (#3458)
Claude Code -> claude-opus-4-8 turns intermittently died with 'tool call could not be parsed (retry also failed)'. OmniRoute's claude identity cloak rebuilt the anthropic-beta header from scratch and unconditionally forced interleaved-thinking-2025-05-14 (+ advanced-tool-use / effort for heavy agents), even when the client never negotiated them. The forced interleaved-thinking conflicts with tool_choice-forced turns, producing malformed opus tool_use streams (and sibling 400 'Thinking may not be enabled when tool_choice forces tool use').

selectBetaFlags now takes the client's inbound anthropic-beta: when present, thinking/effort betas are only emitted if the client requested them. Opaque clients (no header — the OAuth cloak path) keep the full set unchanged, so existing behavior and the #2454 model-tier gating are preserved.

Co-authored-by: Forcerecon <Forcerecon@users.noreply.github.com>
2026-06-08 20:59:38 -03:00
Diego Rodrigues de Sa e Souza
cc850122e3 fix(translator): strip function_call.id for Vertex AI provider (#3440) (#3457)
Vertex AI's FunctionCall/FunctionResponse protos have no id field; emitting it made Vertex reject tool calls with 400 'Unknown name id'. The id is now stripped only when the routed provider is vertex/vertex-partner (threaded via credentials._provider), preserving it for the public Gemini API where Gemini 3+ uses it for signature matching.

Co-authored-by: nullbytef0x <nullbytef0x@users.noreply.github.com>
2026-06-08 20:54:20 -03:00
sdfsdfw2
42887b65b2 feat: add connection pagination, health filter, batch delete confirmation, and custom banned keywords (#3454)
Integrated into release/v3.8.17
2026-06-08 20:41:06 -03:00
Paijo
1a98dfe8ed test(auto-combo): cover same-provider connection identity (#3378)
Integrated into release/v3.8.17
2026-06-08 19:10:40 -03:00
M.M
003e6a80b7 feat(plugin+api): auto combos + free model quota display + /api/combos/auto (#3435)
Integrated into release/v3.8.17
2026-06-08 19:08:17 -03:00
ViFigueiredo
617a648088 fix(translator): use non-empty reasoning_content placeholder on cache miss instead of empty string (#3433)
Integrated into release/v3.8.17
2026-06-08 19:04:36 -03:00
Ardem2025
da273d37e2 fix(stream): resolve index mismatch in textual tool-call slicing and deduplicate containsTextualToolCallMarker (#3413)
Integrated into release/v3.8.17
2026-06-08 18:50:53 -03:00
Hernan Javier Ardila Sanchez
dbd70ddd1f fix(catalog): make combos auto-compute context_length for any provider id form (#3417)
Integrated into release/v3.8.17
2026-06-08 18:50:21 -03:00
Xiangzhe
89a76d8c1c fix(stream): allow OpenAI usage-only empty choices chunks (#3422)
Integrated into release/v3.8.17
2026-06-08 18:50:05 -03:00
Paijo
2427df2f2c feat: add Gemini Business provider (Phase 2C of #3368) (#3436)
Integrated into release/v3.8.17
2026-06-08 18:43:30 -03:00
Paijo
07a81c8a40 feat: add LMArena provider (Phase 2A of #3368) (#3421)
Integrated into release/v3.8.17
2026-06-08 18:43:10 -03:00
Paijo
ea0c0d8499 feat: add ZenMux provider (Phase 2B of #3368) (#3429)
Integrated into release/v3.8.17
2026-06-08 18:42:50 -03:00
ReqX
1e4185edac fix(analytics): scope SQL named params per query context (#3446) (#3447)
Integrated into release/v3.8.17
2026-06-08 18:42:12 -03:00
Muhammad Nabil Muyassar Rahman
b3372e46c4 fix(command-code): revert chat endpoint to /alpha/generate and fix model sync discovery (#3432)
Integrated into release/v3.8.17
2026-06-08 18:41:52 -03:00
Dmitrii Safronov
fc437ddecd fix(sse): normalize provider ids to strings (#3427)
Integrated into release/v3.8.17
2026-06-08 18:41:33 -03:00
diegosouzapw
ed3c188881 fix(e2e): use .first() on Close button to avoid strict-mode violation (2 elements) 2026-06-08 13:37:00 -03:00
diegosouzapw
11bd96ec5c fix(e2e): dismiss import-models modal after adding connection (sync-models mock + close) 2026-06-08 13:12:37 -03:00
diegosouzapw
f112bc966f fix(e2e): wait for add-dialog close before clicking Edit (backdrop race) 2026-06-08 12:44:30 -03:00
diegosouzapw
3ea416350e fix(tests+ci): update 42→43 skill count, fix E2E artifact path
- Unit/integration tests: update hardcoded 42→43 in 7 test files
  (agentSkillTools-mcp, agentSkills-catalog, agentSkills-generator,
  agent-skills-content, agent-skills-discovery, listCapabilities-a2a)
  to match the 43rd skill (config-codex-cli) added in the previous commit.
- Include CONFIG_SKILL_IDS in integration content test ALL_IDS so
  skills/config-codex-cli/ is no longer "unexpected".
- listCapabilities.ts: change totalSkills from literal 42 to catalog.length
  so it adapts to catalog growth automatically.
- computeCoverage assertions: include config.have in totalSkills check.
- CI: switch E2E artifact from upload-artifact path (ambiguous stripping)
  to explicit tar archive. Fixes "Could not find a production build in
  ./.build/next" — the previous approach's download path was double-nested
  (.build/next/next/...) due to upload-artifact LCA computation. tar -czf
  stores .build/next/... relative to CWD; tar -xzf restores them verbatim.
- Also exclude .build/next/cache from the tar to keep archive lean.
- feat(translator): strip client_metadata in Responses→Chat translation
  (Mistral 422 extra_forbidden fix); add regression test.
2026-06-08 10:41:00 -03:00
diegosouzapw
1012603a1b fix(ci+tests): fix E2E artifact (exclude 558MB standalone/node_modules, cp after download) and update skill count to 43 2026-06-08 10:13:24 -03:00
diegosouzapw
b145e41a42 fix(tests): align test suite to post-#3355/#3366/#3399 behavior
- Remove 429 from PROVIDER_BREAKER_FAILURE_STATUSES; 429 belongs to
  connection cooldown, not whole-provider breaker (CLAUDE.md §resilience).
  PR #3366 correctly added 429 to PROVIDER_FAILURE_ERROR_CODES in
  accountFallback.ts (combo infinite-retry fix) but the parallel change
  to chat.ts was wrong — the integration test from v3.8.10 confirms this.

- Align stream-utils tests to PR #3399 (SYNTHETIC_CLAUDE_EMPTY_RESPONSE_TEXT
  → "", message.content → null) and PR #3355 (malformed tool-call buffer
  now emitted as plain text, not suppressed).

- Align services-branch-hardening test to PR #3399 (pinnedModel always
  null from applyComboAgentMiddleware; server-side session pinning replaced
  client-side <omniModel> tag extraction).

- Align combo-routing-engine context-cache tests to PR #3399 (no <omniModel>
  tag in output, no X-OmniRoute-Model header, priority routing unchanged).
2026-06-08 09:07:50 -03:00
Diego Rodrigues de Sa e Souza
67d79f6c44 fix(sanitizer+stream): tighten textual tool-call detection, flush partial buffer (#3355) (#3410) 2026-06-08 02:14:08 -03:00
Diego Rodrigues de Sa e Souza
e0615a8194 fix(executor): strip trailing assistant text for Mistral (user-last required) (#3396) (#3409) 2026-06-08 02:09:20 -03:00
Diego Rodrigues de Sa e Souza
f688d1150f fix(mitm): getMitmStatus stub returns graceful status in Docker (#3390) (#3408) 2026-06-08 02:05:44 -03:00
Paijo
ecdd5a36eb feat: add REST API for session pool health (dashboard interface) (#3404)
Integrated into release/v3.8.16
2026-06-08 01:25:38 -03:00
Paijo
71f6e8d312 feat: add bulk web-session credential import endpoint (#3403)
Integrated into release/v3.8.16
2026-06-08 01:24:27 -03:00
Diego Rodrigues de Sa e Souza
c9663d4f84 fix(sse): eliminate race window in usageTokenBuffer settings update (#3405)
Integrated into release/v3.8.16
2026-06-08 01:23:37 -03:00
k0valik
4c420b015d fix: server-side context cache pinning, stop proxy message leaks, persist context_cache_protection toggle (#3399)
Integrated into release/v3.8.16
2026-06-08 00:55:42 -03:00
Hernan Javier Ardila Sanchez
452e6cc937 feat(vision-bridge): auto-route to fastest vision model (#3377)
Integrated into release/v3.8.16
2026-06-08 00:51:29 -03:00
Tubagus
48ed42c6c3 fix(providers): refresh model list after provider sync (#3402)
Integrated into release/v3.8.16
2026-06-08 00:49:12 -03:00
Paijo
6df38155a4 feat: add web-session pool observability (MCP tool + health-matrix) (#3395)
Integrated into release/v3.8.16
2026-06-08 00:48:01 -03:00
Paijo
5b72dc6250 feat: adaptive keepalive threshold for web-session providers (#3397)
Integrated into release/v3.8.16
2026-06-08 00:47:34 -03:00
Tubagus
4adc1d087f fix(stream): drop empty choices chunks instead of emitting retry text (#3400)
Integrated into release/v3.8.16
2026-06-08 00:47:08 -03:00
Ardem2025
ee061d7a6d fix(stream): solve false positive textual tool-call marker truncation using emitted content state (#3382)
Integrated into release/v3.8.16
2026-06-08 00:46:20 -03:00
Paijo
8505e0f2b7 fix(account-fallback): preserve provider cooldown dedupe state (#3381)
Integrated into release/v3.8.16
2026-06-08 00:45:29 -03:00
Diego Rodrigues de Sa e Souza
929caeb910 Release v3.8.15 (#3373)
* chore(release): open v3.8.15 development cycle

Version bump 3.8.14 -> 3.8.15 (root + electron + open-sse + openapi + lockfiles)
and seed the v3.8.15 changelog placeholder (root + 41 i18n mirrors).

* fix(catalog): add getTokenLimit fallback for combo targets with unknown context (#3369)

Integrated into release/v3.8.15. Fixes applied on the contributor's branch: removed duplicate JSDoc opening in accountFallback.ts and dropped a test asserting unreachable catalog behavior (models with no registry/spec/synced source are filtered before the getTokenLimit fallback at catalog.ts:499).

* fix(combo): add 429 to PROVIDER_FAILURE_ERROR_CODES to prevent infinite retry loop (#3366)

Integrated into release/v3.8.15. Comment block reconciled on the contributor's branch to remove the contradictory 'intentionally excluded' text that remained from the original code.

* fix(auto-combo): include no-auth providers declaratively (#3365)

Integrated into release/v3.8.15. Cleanup applied on contributor's branch: removed duplicate migration 095 (already exists from PR #3338), reverted CHANGELOG.md and i18n changelogs to release versions (release process owns these), dropped package version-bump noise from stale fork base. Core feature — declarative no-auth via serviceKinds metadata, declarative VEO as 'video' provider, anonymousFallback flag for opencode-zen/opencode-go — integrated cleanly.

* fix(migrations): restore 095_provider_node_custom_headers migration

The squash merge of PR #3365 accidentally deleted this migration because
the cleanup commit on the contributor's branch included 'git rm' for the
file (which was a duplicate on their branch). The migration was merged
in v3.8.14 via PR #3338 and must be present in the release branch.

Restoring from git history.

* fix: update Command Code base URL from /alpha/ to /provider/v1/ (#3372)

Integrated into release/v3.8.15.

* feat(error-rules): provider-specific error classification with scope (#3370)

Integrated into release/v3.8.15. PR has genuine value beyond #3369: (1) getProviderErrorRuleMatch now accepts native Headers objects from fetch(); (2) checkFallbackError also uses the provider rule registry — the real end-to-end wiring in the combo fallback path; (3) S4 end-to-end test proving the wiring fires. Merge commit on contributor branch resolved the add/add conflict by taking the #3370 version throughout.

* fix(auto-combo): validate web-session credentials (#3371)

Integrated into release/v3.8.15. Core feature: provider-aware web-session credential validation — hasUsableWebSessionCredential() replaces the broad Object.keys check in virtualFactory.ts, ensuring only sessions with the required storageKeys are included in auto-combo. Cleanup: removed duplicate 095 migration, reverted CHANGELOG/i18n, dropped package bump noise.

* fix(migrations): restore 095_provider_node_custom_headers (deleted again by #3371 squash)

Same issue as after #3365: git rm in the contributor cleanup commit
was included in the squash, deleting this migration from release.
Permanent fix needed: use 'git checkout origin/release -- <file>'
instead of 'git rm' when cleaning up duplicate files in contributor branches.

* fix(kiro): probe Windows %APPDATA%\kiro\storage.db in auto-import (#3363) (#3375)

Integrated into release/v3.8.15. Test fix applied: kiro-windows-auto-import-3363.test.ts now sets DATA_DIR to a fresh temp dir before importing app modules, ensuring isAuthRequired() sees an empty settings DB (no password → auth not required). This fixed test 4 (synthetic SQLite) which was getting 401 due to settings DB state leakage.

* chore(release): finalize v3.8.15 changelog — 2026-06-07

---------

Co-authored-by: Hernan Javier Ardila Sanchez <hjasgr@gmail.com>
Co-authored-by: Paijo <14921983+oyi77@users.noreply.github.com>
Co-authored-by: Muhammad Nabil Muyassar Rahman <65392758+TapZe@users.noreply.github.com>
Co-authored-by: kiro-agent[bot] <245459735+kiro-agent[bot]@users.noreply.github.com>
2026-06-07 12:16:33 -03:00
Diego Rodrigues de Sa e Souza
000d60b907 test(translator): align gemini-2.5-flash maxOutputTokens cap to 65536 (#3358) (#3367)
#3358 added the gemini-2.5-flash model spec with its real 65536 max-output cap
(previously the model had no spec and fell to an 8192 default). The Claude→Gemini
clamp test still asserted 8192, so it failed deterministically — the single real
failure behind the v3.8.14 CI red (Unit Tests 3/8, Coverage Shard 3/8, Node
24/26 Compatibility 1/2 all hit this one test; E2E 5/6 was fail-fast collateral).
2026-06-07 08:12:33 -03:00
Diego Rodrigues de Sa e Souza
7db430a352 Release v3.8.14 (#3340)
* chore(release): open v3.8.14 development cycle

Version bump 3.8.13 -> 3.8.14 (root + electron + open-sse + openapi + lockfiles).
Seed the v3.8.14 changelog with the four post-tag hotfixes that shipped to
Docker/Electron in v3.8.13 but missed the immutable npm 3.8.13 (#3336 SSRF /
CodeQL #323, #3334/#3335/#3339 Electron packaging). i18n CHANGELOG mirrors get
the in-progress placeholder section.

* feat: add per-provider custom headers support for OpenAI/Anthropic-compatible nodes (#3338)

Integrated into release/v3.8.14

* fix: Kiro Builder ID token import fails with Bad credentials (#3333)

Integrated into release/v3.8.14 — adds Builder ID cached-creds + OIDC refresh path for Kiro token import, with regression tests (#3333).

* Improve code quality: auto-pr/docstrings-1780792063 (#3337)

Integrated into release/v3.8.14 — docstring for context analytics route re-export.

* fix(catalog): remove minimaxai/minimax-m3 from NVIDIA NIM tier (404 upstream) (#3329) (#3341)

NVIDIA NIM does not host minimaxai/minimax-m3 — every request returns
404 page not found, while sibling minimaxai/minimax-m2.7 on the same provider
works. Advertising a model that 404s is a catalog bug; remove it from the nvidia
tier (it remains on the tiers that actually serve MiniMax M3). Re-add only once
NVIDIA serves it.

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

* fix(cli): write OpenCode config to ~/.config on all platforms incl. Windows (#3330) (#3343)

resolveOpencodeConfigDir used %APPDATA% on Windows, but OpenCode reads its
config from XDG ~/.config/opencode/ on every platform (on Windows:
%USERPROFILE%\.config\opencode\, NOT %APPDATA%). So a Windows user who
configured OpenCode via the dashboard had the file written where OpenCode never
looks — it silently had no effect.

Use the XDG path (XDG_CONFIG_HOME || ~/.config) unconditionally. Update the UI
note + route JSDoc, and flip the three tests that encoded the old %APPDATA%
behavior (t40 per-platform + card-note, cli-runtime-extended getCliConfigPaths).

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

* fix(proxy): make auto-selection fallback opt-in (#3332) (#3344)

selectWorkingProxyFallback (Step 11 of resolveProxyForConnection) listed ALL
registry proxies, ignoring assignments and per-connection proxy_enabled, and
returned the first working one with level:'autoSelect'. So a single proxy added
to the registry silently became a global fallback for every connection's traffic.

Gate it behind a new PROXY_AUTO_SELECT_ENABLED feature flag (default off): the
fallback now no-ops unless the operator opts in. No registry proxy becomes a
silent global default anymore.

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

* fix(sse): treat MiniMax M3 as multimodal so vision isn't stripped (#3328) (#3342)

MiniMax M3 via the opencode provider (oc/minimax-m3-free) appeared blind:
image inputs didn't reach the model, while the same model in Cline could
see them. Verified empirically that MiniMax M3 on the opencode upstream IS
multimodal -- a base64 image is described correctly (it returns 403 only
for remote image URLs, which it doesn't accept).

Root cause: OmniRoute treated MiniMax M3 as a non-vision model in two
places, so when compression was active the image was replaced with a text
placeholder before dispatch:
- compression's modelSupportsVision() heuristic (lite.ts) only matched
  gpt-4/4o/claude-3/gemini/vision -- minimax was absent -> replaceImageUrls
  stripped the image.
- the opencode minimax-m3-free catalog entry lacked supportsVision, so the
  combo vision-capability gate could also exclude/mishandle it.

Add 'minimax-m3' to the vision heuristic and supportsVision: true to the
opencode minimax-m3-free entry. TDD: a failing-then-passing test in
compression/lite.test.ts proves replaceImageUrls now keeps images for
minimax-m3 ids, plus a registry assertion mirroring the #2822 qwen test.

Reported-by: @mikmaneggahommie

* docs(i18n): translate 25 core documentation files to Indonesian (#3348)

Integrated into release/v3.8.14 — Indonesian i18n docs.

* fix(review): resolve /review-reviews battery findings (LEDGER-1..11) on v3.8.14 (#3350)

Integrated into release/v3.8.14 — /review-reviews battery hardening (LEDGER-1..11) for #3338 custom-headers + #3333 kiro, plus cycle-test drift fixes (#3329/#3330/#3332).

* fix(provider-proxy): honor per-account proxy toggles (#3349)

Integrated into release/v3.8.14 — honor per-account proxy toggles + auto-fallback opt-in via PROXY_AUTO_SELECT_ENABLED.

* fix(dashboard): remove duplicate Distribute Proxies button on provider page (#3352)

* fix(providers): reduce proxy label noise (#3346)

Integrated into release/v3.8.14 — reduce proxy label noise + a11y (aria-label/sr-only).

* fix(duckduckgo): restore bare Response contract and rebase onto release/v3.8.14 (#3323)

Integrated into release/v3.8.14 — browser-backed cookie providers (duckduckgo/claude-web) with restored executor contract + unit tests.

* fix(noauth): expose only usable model aliases (#3345)

Integrated into release/v3.8.14 — noauth usable-alias filtering + registry alias plumbing (veo-free).

* fix(dashboard): stop infinite config-load loop on Hermes Agent detail page (#3353)

* fix(electron): tree-kill the server on exit/update to release the omniroute.exe lock (#3347) (#3354)

* chore(release): finalize v3.8.14 changelog + clear release-gate drift

- CHANGELOG: finalize the v3.8.14 section (date, full New Features/Bug Fixes/
  Maintenance coverage of all 16 cycle commits, Contributors hall of 12).
- docs: document OMNIROUTE_BROWSER_POOL + WEB_COOKIE_USE_BROWSER (#3323) in
  .env.example + ENVIRONMENT.md; regenerate the id/llm.txt strict mirror (#3348
  had translated it; llm.txt mirrors must match root).
- test(proxy-fetch): #3323 made tlsClient.available a computed getter — stub it
  via Object.defineProperty instead of assignment (5 tests were red on the base).

* fix(translator): coerce Gemini functionDeclaration parameters to an OBJECT schema (#3357) (#3360)

* fix(gemini): resolve truncation/suppression of false positive textual tool call markers in backticks (#3358)

Integrated into release/v3.8.14 — Gemini/Antigravity textual tool-call marker normalization (no false-positive suppression + split-chunk buffering).

* docs(changelog): add #3358 Gemini textual tool-call normalization to v3.8.14

* fix(dashboard): surface real analytics error instead of generic placeholder (#3356) (#3361)

The Analytics page discarded the server's error body on a non-OK response and
rendered a generic "An error occurred", so users (and maintainers) could not see
why /api/usage/analytics 500'd after an upgrade. Now the route returns the real
reason via buildErrorBody (sanitized, Hard Rule #12) and the page surfaces it via
a new readFetchErrorMessage helper that handles both the OpenAI-style and legacy
error shapes.

Reported-by: @superti4r

---------

Co-authored-by: PizzaV <103120356+pizzav-xyz@users.noreply.github.com>
Co-authored-by: Someres <168349709+quanturbo@users.noreply.github.com>
Co-authored-by: Dong Mengzhe <154944819+Lang-Qiu@users.noreply.github.com>
Co-authored-by: mikmaneggahommie <mikmaneggahommie@users.noreply.github.com>
Co-authored-by: abdulkadirozyurt <abdulkadirozyurt@users.noreply.github.com>
Co-authored-by: hertznsk <hertznsk@users.noreply.github.com>
Co-authored-by: Krisna Santosa <54174372+KrisnaSantosa15@users.noreply.github.com>
Co-authored-by: Randi <55005611+rdself@users.noreply.github.com>
Co-authored-by: Wilson <pedbookmed@gmail.com>
Co-authored-by: Paijo <14921983+oyi77@users.noreply.github.com>
Co-authored-by: Ardem2025 <ardemb22@gmail.com>
2026-06-07 07:20:02 -03:00
Diego Rodrigues de Sa e Souza
630baa6c18 fix(electron): swallow auto-updater check rejection to avoid unhandled rejection (#3339)
checkForUpdates() is fired unawaited from a setTimeout at startup. The
underlying autoUpdater.checkForUpdates() rejects on a 404 (release update
manifest not published yet), offline, or rate-limit — and the uncaught
rejection surfaced as an "Unhandled Rejection", which the packaged-app smoke
test treats as fatal (failed the macOS-intel v3.8.13 build; passed elsewhere
only by timing race). The autoUpdater "error" event still notifies the user;
wrap the await so the promise rejection never escapes. Adds a regression test.
2026-06-06 21:42:30 -03:00
Diego Rodrigues de Sa e Souza
df2379053e fix(security): use trusted internal origin for provider auto-sync self-fetch (CodeQL #323 SSRF) (#3336)
POST /api/providers fires a credential-bearing self-fetch to the new
connection's /sync-models route (forwarding the management cookie + internal
sync auth headers). #3267 built that origin from new URL(request.url).origin —
the client-controlled Host header — so a (management-authenticated) caller
could redirect the internal request to an arbitrary host, exfiltrating the
internal sync auth token (CodeQL js/request-forgery, critical, alert #323).

Derive the origin from the trusted loopback/env-pinned base URL via a new
getModelSyncInternalBaseUrl() helper (same source the model-sync scheduler
already uses), never from the incoming request. Adds a regression test.
2026-06-06 21:20:26 -03:00
Diego Rodrigues de Sa e Souza
9535fa52a6 fix(startup): correct autoRefreshDaemon import alias (@/ -> @omniroute/open-sse) (#3292) (#3335)
instrumentation-node.ts imported the #3292 cookie auto-refresh daemon via
"@/open-sse/services/autoRefreshDaemon". The @/ alias maps to src/, but the
daemon lives in the open-sse workspace, so the import resolved to the
non-existent src/open-sse/... and threw "Cannot find module" at runtime in the
built standalone. A try/catch made it non-fatal (the daemon silently never
ran), which kept typecheck and the dev server green, but the packaged Electron
app's strict startup-log smoke test failed on the "Cannot find module" line.

Use the correct @omniroute/open-sse alias, plus a regression test banning
@/open-sse/* imports across src/.
2026-06-06 21:15:43 -03:00