Skip to content

fix: bundle @omniroute/opencode-plugin inside omniroute + add 'setup opencode' CLI command - #3726

Merged
diegosouzapw merged 4 commits into
diegosouzapw:release/v3.8.23from
herjarsa:fix/opencode-plugin-integration
Jun 12, 2026
Merged

diegosouzapw merged 4 commits into
diegosouzapw:release/v3.8.23from
herjarsa:fix/opencode-plugin-integration

Conversation

@herjarsa

Copy link
Copy Markdown
Contributor

Problema

El @omniroute/opencode-plugin (issue #3711) nunca se compilaba como parte del pipeline de npm publish, y no existía un comando CLI para instalarlo en OpenCode. El usuario tenía que extraer el tarball y configurarlo manualmente.

Cambios

@omniroute/opencode-plugin/src/index.ts

  • Fix baseURL: La cadena de resolución ahora incluye _provider.options.baseURL como tercer eslabón (plugin opts → auth.json → provider config). Esto corrige el caso donde baseURL se setea via opencode.json o config hook y no era detectado, resultando en 0 modelos.
  • Diagnóstico: warnings claros cuando no hay baseURL resoluble.

scripts/build/prepublish.ts

  • Nuevo Step 8.8: Compila @omniroute/opencode-plugin via tsup durante el publish. El dist/ viaja dentro del tarball npm (el package.json raíz ya incluye "@omniroute/" en files). Falla con error claro si la compilación falla.

bin/cli/commands/setup-open-code.mjs (NUEVO)

  • Comando omniroute setup opencode que:
    1. Resuelve el plugin bundled desde el package omniroute instalado
    2. Detecta el directorio de configuración de OpenCode (XDG-aware, multi-plataforma)
    3. Copia el plugin a ~/.config/opencode/plugins/omniroute/
    4. Crea/actualiza opencode.json (idempotente, reemplaza entries legacy)
    5. Opcional: --auth ejecuta opencode auth login
  • runSetupOpenCodeCommand() exportada para testabilidad.

bin/cli/commands/setup.mjs

  • Importa y registra setup-open-code como subcomando de setup.

bin/cli/locales/en.json

  • String setup.opencode.

Verificación

  • @omniroute/opencode-plugin build OK + 257 tests pasan
  • Syntax check del nuevo comando CLI OK
  • Commit hooks (prettier, eslint, any-budget check) pasan
  • Branch basada en upstream/main

…pencode CLI command

Breaking the installation gap reported in issue diegosouzapw#3711: the opencode-plugin was never built as part of the npm publish pipeline, and there was no CLI command to wire it into an OpenCode install.

- baseURL resolution chain: add _provider.options.baseURL as third fallback
- Add diagnostic warnings when no baseURL is resolvable
- New Step 8.8 in prepublish: build @omniroute/opencode-plugin via tsup
- New 'omniroute setup opencode' CLI subcommand
- Idempotent opencode.json update, legacy plugin replacement
- Register as subcommand of 'setup' + add locale en string
@herjarsa
herjarsa requested a review from diegosouzapw as a code owner June 12, 2026 13:48

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a new CLI command omniroute setup opencode to automate the installation and registration of the bundled @omniroute/opencode-plugin into a local OpenCode installation, along with integration into the build pipeline. The reviewer feedback highlights several critical issues: a lack of unit or integration tests for the new CLI command (violating the repository style guide), a Windows-specific execution bug in spawnSync when running opencode, potential crashes when parsing empty or non-object opencode.json files, robust filtering of legacy auth plugins, and the risk of publishing stale build artifacts by skipping the plugin rebuild during prepublish.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +1 to +23
/**
* omniroute setup opencode — Wire the bundled @omniroute/opencode-plugin
* into a local OpenCode install.
*
* Closes the gap where `npm install -g omniroute` ships the plugin
* inside the omniroute package (`@omniroute/opencode-plugin/dist/`) but
* OpenCode discovers plugins via `~/.config/opencode/plugins/` or
* via entries in `opencode.json`. Without this command, the user has
* to extract the tarball and wire it up by hand (see the plugin README,
* "Install" section).
*
* What it does, in order:
* 1. Resolves the bundled plugin path (source + built dist).
* 2. Resolves the OpenCode config directory (XDG-aware).
* 3. Copies the built plugin into `<opencode>/plugins/omniroute/`.
* 4. Creates or updates `opencode.json` with a single `plugin` entry
* pointing at the local copy (so OC ≥1.15 picks it up).
* 5. Optionally runs `opencode auth login --provider omniroute`
* so the next `opencode` invocation already has the API key.
*
* Idempotent: re-running with the same `--provider-id` updates the
* entry in place (path + baseURL) without duplicating it.
*/

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The pull request introduces a new CLI command file under bin/cli/commands/setup-open-code.mjs and modifies bin/cli/commands/setup.mjs, but does not include any unit or integration tests. This violates the repository style guide rule which requires tests when changing production code in bin/.

References
  1. Always include tests when changing production code (src/, open-sse/, electron/, bin/). (link)

Comment on lines +221 to +224
const res = spawnSync(opencodeBin, ["auth", "login", "--provider", providerId], {
stdio: "inherit",
shell: false,
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

On Windows, executing batch files (.cmd, .bat) via spawnSync requires the shell option to be set to true (or isWin). Without it, the OS cannot execute the script directly, resulting in an ENOENT or ERROR_INVALID_FUNCTION error.

Suggested change
const res = spawnSync(opencodeBin, ["auth", "login", "--provider", providerId], {
stdio: "inherit",
shell: false,
});
const res = spawnSync(opencodeBin, ["auth", "login", "--provider", providerId], {
stdio: "inherit",
shell: isWin,
});

Comment on lines +156 to +166
let cfg = {};
if (existsSync(configPath)) {
try {
cfg = JSON.parse(readFileSync(configPath, "utf8"));
} catch (err) {
throw new Error(
`Failed to parse existing ${configPath}: ${err.message}\n` +
`Fix or remove the file manually, then re-run \`omniroute setup opencode\`.`
);
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

If opencode.json exists but is empty or contains non-object JSON (such as null or []), JSON.parse will succeed but cfg will not be a valid object. This can cause subsequent property accesses like cfg.plugin to throw a TypeError. We should validate that the parsed result is a non-null object.

Suggested change
let cfg = {};
if (existsSync(configPath)) {
try {
cfg = JSON.parse(readFileSync(configPath, "utf8"));
} catch (err) {
throw new Error(
`Failed to parse existing ${configPath}: ${err.message}\n` +
`Fix or remove the file manually, then re-run \`omniroute setup opencode\`.`
);
}
}
let cfg = {};
if (existsSync(configPath)) {
try {
const parsed = JSON.parse(readFileSync(configPath, "utf8"));
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
cfg = parsed;
}
} catch (err) {
throw new Error(
`Failed to parse existing ${configPath}: ${err.message}\n` +
`Fix or remove the file manually, then re-run \"omniroute setup opencode\".`
);
}
}

Comment on lines +187 to +200
const filtered = plugins.filter((p) => {
if (typeof p === "string") {
return !p.includes("opencode-omniroute-auth");
}
if (Array.isArray(p) && p[1] && typeof p[1] === "object") {
const pid = p[1].providerId;
if (pid === providerId) return false;
// Also drop the legacy auth plugin if it's there.
if (typeof p[0] === "string" && p[0].includes("opencode-omniroute-auth")) {
return false;
}
}
return true;
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

If a legacy opencode-omniroute-auth entry is registered as an array without options (e.g., ["opencode-omniroute-auth"]), the current filtering logic will skip it because p[1] is undefined. Refactoring the filter to check the first element of the array regardless of options makes the cleanup much more robust.

Suggested change
const filtered = plugins.filter((p) => {
if (typeof p === "string") {
return !p.includes("opencode-omniroute-auth");
}
if (Array.isArray(p) && p[1] && typeof p[1] === "object") {
const pid = p[1].providerId;
if (pid === providerId) return false;
// Also drop the legacy auth plugin if it's there.
if (typeof p[0] === "string" && p[0].includes("opencode-omniroute-auth")) {
return false;
}
}
return true;
});
const filtered = plugins.filter((p) => {
if (typeof p === "string") {
return !p.includes("opencode-omniroute-auth");
}
if (Array.isArray(p)) {
const pluginPath = p[0];
if (typeof pluginPath === "string" && pluginPath.includes("opencode-omniroute-auth")) {
return false;
}
if (p[1] && typeof p[1] === "object") {
const pid = p[1].providerId;
if (pid === providerId) return false;
}
}
return true;
});

Comment on lines +280 to +300
const pluginAlreadyBuilt = existsSync(opencodePluginDist) && existsSync(opencodePluginCjs);
if (!pluginAlreadyBuilt) {
console.log("\n 🔨 Building @omniroute/opencode-plugin (tsup)...");
try {
execFileSync(NPX_BIN, ["tsup"], {
cwd: opencodePluginSrc,
stdio: "inherit",
env: { ...process.env, NODE_ENV: "production" },
});
console.log(" ✅ @omniroute/opencode-plugin bundled to @omniroute/opencode-plugin/dist/");
} catch (err: any) {
console.error(" ❌ Failed to build @omniroute/opencode-plugin:", err.message);
console.error(" The published package would be missing the plugin dist.");
console.error(
" Run `cd @omniroute/opencode-plugin && npm install && npm run build` to debug."
);
process.exit(1);
}
} else {
console.log(" ✅ @omniroute/opencode-plugin dist/ already present (skipping rebuild)");
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

During a release/publish pipeline, we should always rebuild the plugin to ensure that any recent changes to @omniroute/opencode-plugin/src/index.ts are compiled into the published dist/. Skipping the build just because dist/index.js exists is dangerous because developers often have old build artifacts lying around in their local workspace.

  console.log("\n  🔨 Building @omniroute/opencode-plugin (tsup)...");
  try {
    execFileSync(NPX_BIN, ["tsup"], {
      cwd: opencodePluginSrc,
      stdio: "inherit",
      env: { ...process.env, NODE_ENV: "production" },
    });
    console.log("  ✅ @omniroute/opencode-plugin bundled to @omniroute/opencode-plugin/dist/");
  } catch (err: any) {
    console.error("  ❌ Failed to build @omniroute/opencode-plugin:", err.message);
    console.error("     The published package would be missing the plugin dist.");
    console.error(
      "     Run `cd @omniroute/opencode-plugin && npm install && npm run build` to debug."
    );
    process.exit(1);
  }

…d install flow

- CHANGELOG.md: Add Unreleased entries for diegosouzapw#3726 (bundled plugin + omniroute setup opencode) and diegosouzapw#3711 (baseURL resolution fix)
- @omniroute/opencode-plugin/README.md: Rewrite Install section — primary flow is now 'omniroute setup opencode --auth'. Manual install moved to subsection. Quick start updated to use relative path instead of npm package reference.
@diegosouzapw
diegosouzapw changed the base branch from main to release/v3.8.23 June 12, 2026 14:10
diegosouzapw and others added 2 commits June 12, 2026 11:10
…-url plumbing, plugin install in Step 8.8, Rule diegosouzapw#18 tests

- src/index.ts rebuilt on the release base with only the real change (the
  provider-config baseURL third link + clear warning) — drops ~1,250 lines
  of formatting churn so blame/history stay usable.
- setup opencode: '--base-url' is camelCased by Commander into baseUrl, but
  the runner read baseURL only — the flag was silently ignored. Runner now
  accepts both (failing-then-passing test included). Adds opts.configDir +
  OMNIROUTE_OPENCODE_PLUGIN_DIR overrides for testability; drops an unused
  import.
- prepublish Step 8.8: install the plugin's own devDependencies before tsup
  when node_modules is absent — the plugin is standalone (not a workspace),
  and tsup dts:true fails on a fresh CI checkout without them.
- tests/unit/cli-setup-opencode.test.ts: happy path honouring --base-url,
  idempotent re-run, legacy opencode-omniroute-auth cleanup (diegosouzapw#3711),
  missing-dist error path.

Validation: 4/4 new CLI tests, 257/257 plugin tests, plugin tsup build green.

Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com>
@diegosouzapw

Copy link
Copy Markdown
Owner

Thanks @herjarsa — this closes the #3711 gap properly: the plugin finally ships inside the omniroute tarball and omniroute setup opencode makes the install one command (XDG-aware, idempotent, legacy-entry cleanup — nicely engineered). 🎯

Pushed a review commit (e4fcf5b4, keeping you as author) with four adjustments:

  1. src/index.ts rebuilt on the release base — your diff carried ~1,250 lines of formatting churn; the real change (the provider.options.baseURL third link + clear warning) is now a 28-line diff, preserving blame and the fix(combo): stop premature context compaction — real auto-combo windows + per-target compression limit #3680 context handling.
  2. --base-url was silently ignored: Commander camelCases it into opts.baseUrl, but the runner only read opts.baseURL. Fixed with a failing-then-passing test.
  3. Step 8.8 hardening: the plugin is standalone (not a workspace), so a fresh CI checkout has no node_modules there — and tsup with dts: true needs the plugin's devDependencies. The step now installs them first (reproduced the failure locally before the fix).
  4. Rule fix(ci): add environment for npm token access #18 tests (cli-setup-opencode.test.ts): happy path honouring --base-url, idempotent re-run, legacy opencode-omniroute-auth cleanup, missing-dist error path.

Validation: 4/4 new CLI tests, 257/257 plugin tests, tsup build green. Ships in v3.8.23. 🚀

@diegosouzapw
diegosouzapw merged commit 5635a4a into diegosouzapw:release/v3.8.23 Jun 12, 2026
2 checks passed
@diegosouzapw diegosouzapw mentioned this pull request Jun 12, 2026
diegosouzapw added a commit that referenced this pull request Jun 12, 2026
- CHANGELOG: complete v3.8.23 section (28 bullets, 27 commits)
- fix(webdav): resolve promise on writeStream finish, not req end — eliminates
  intermittent 500 on PUT update (writeStream may not have flushed at rename time)
- test(autoCombo): stub DB calls from PR #3660 in tieredRotation.test.ts to prevent
  5s timeout in vitest (getModelIntelligenceBySource DB init path)
- chore(env-sync): add XDG_DATA_HOME + OMNIROUTE_OPENCODE_PLUGIN_DIR to IGNORE_FROM_CODE
  allowlist (introduced by PR #3726 setup-open-code.mjs, not OmniRoute config vars)
- chore(cli): regenerated bin/cli/api-commands/*.mjs (7 new, 27 updated)
diegosouzapw added a commit that referenced this pull request Jun 13, 2026
* chore(release): open v3.8.23 development cycle

* fix(anthropic): strip top_p when temperature is set to avoid 400 (#3691)

Integrated into release/v3.8.23

* fix(vertex): support Vertex AI Express-mode API keys (#3690)

Integrated into release/v3.8.23

* fix(stream): error on empty Claude SSE instead of synthetic success (#3689)

Integrated into release/v3.8.23

* fix(oauth): stop token-refresh invalidation loop + harden proxy resolution (#3692)

Integrated into release/v3.8.23

* docs: add FUNDING.yml and Support section to README (#3698)

Integrated into release/v3.8.23

* feat: gemini - handle known ratelimits (#3686)

Integrated into release/v3.8.23

* fix: stream combo fails over on empty content-filtered response (#3685) (#3702)

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

* fix(antigravity): preserve gemini-3.1-pro high/low budget tiers (#3696) (#3703)

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(auto-combo): add auto-updating model intelligence scoring (#3660)

Integrated into release/v3.8.23

* fix(gemini): context-mode fallback for signatureless tool calls (#3688) (#3704)

* chore(quality-gate): reconcile file-size baseline (27 files + providerLimits.ts) (#3705)

* feat(vertex): dynamic model discovery via Generative Language models API (#3712)

Integrated into release/v3.8.23. Vertex dynamic model discovery — surfaces image models (imagen-*, gemini-*-image), embeddings and audio from the live Generative Language catalog, with cached→static fallback and the shared parseGeminiModelsList helper. Validated: parser test 5/5, typecheck:core clean.

* fix(combo): gate reasoning token buffer (#3700)

Integrated into release/v3.8.23. Makes the #3588 reasoning token buffer safe and configurable: only inflates max_tokens when the model has a known, non-default output cap and the buffered value fits inside it; otherwise preserves/clamps the client limit. Adds the reasoningTokenBufferEnabled kill switch (default ON). Validated: combo-routing-engine 81/81, combo-config 25/25, combo-quality-validator-reasoning 12/12, phase1f 10/10, typecheck:core clean.

* refactor(#3501): god-component Phase 1g-1j — client 4062→3408 LOC (-654) (#3717)

Phase 1g-1j of #3501: client 4062→3408 LOC. Pure extraction (ProviderPlaygroundPanel, useCommandCodeAuth, useExternalLinkFlow+ExternalLinkModal, useAuthFileHandlers) + loadConnProxies ReferenceError fix + phase1f test path fix.

Co-authored-by: oyi77 <14921983+oyi77@users.noreply.github.com>

* refactor(#3501): god-component Phase 1k-1m — client 3408→2553 LOC (-855) (#3721)

Phase 1k-1m of #3501: client 3408→2553 LOC. Pure extraction (useModelImportHandlers+ImportProgressModal, useModelVisibilityHandlers, ProviderModelsSection).

Co-authored-by: oyi77 <14921983+oyi77@users.noreply.github.com>

* docs(changelog): restore #3590 bullet lost on the v3.8.20 release branch

The fix itself reached main pre-tag via cherry-pick #3591, but its changelog
bullet (commit e33fdd4) only ever existed on release/v3.8.20 after the
squash-merge. Restored under [3.8.20] per the 2026-06-12 release-branch
leftover audit (_tasks/release-audit/release-leftovers-audit-2026-06-12.md).

* fix(kiro): resolve quota for IAM Identity Center accounts missing a profileArn (#3722)

Integrated into release/v3.8.23

* refactor(#3501): god-component Phase 1n-1s — client 2553→1376 LOC (-1177) (#3725)

Phase 1n-1s of #3501: client 2553→1376 LOC. Pure extraction (ConnectionsListPanel, ConnectionsHeaderToolbar, ZedImportCard, BatchTestResultsModal, AdaptaTutorialModal, useApiKeySave + helpers).

Co-authored-by: oyi77 <14921983+oyi77@users.noreply.github.com>

* feat(model-lockout): settings UI, backend integration, error classification, and success-decay recovery (#3629)

Integrated into release/v3.8.23

* refactor(#3501): god-component Phase 1t — client 1376→781 LOC (≤800 TARGET REACHED ✅) (#3727)

Phase 1t of #3501: client 1376→781 LOC (≤800 reached). Original god-component 12,882→781 (−94%).

Co-authored-by: oyi77 <14921983+oyi77@users.noreply.github.com>

* fix: bundle @omniroute/opencode-plugin inside omniroute + add 'setup opencode' CLI command (#3726)

Integrated into release/v3.8.23

* feat(vertex): self-tracked USD spend since account added (#3724)

Integrated into release/v3.8.23

* fix(qwen-web): migrate to v2 chat API with full cookie-jar replay (#3288) (#3723)

Integrated into release/v3.8.23

* fix(sse): make safeLogEvents async — 'await' in a sync function broke every chatHelpers import

#3692 added a lazy 'await import(proxyEgress)' for egress-IP visibility inside
safeLogEvents, which is a sync function — an ES syntax error. It went unnoticed
because typecheck:core does not cover src/sse and no test in the merge gates
loaded chatHelpers via tsx; any consumer that did (chat-context-relay and
chat-route-coverage suites, integration harnesses) failed at module load with
'await can only be used inside an async function'.

safeLogEvents is fire-and-forget logging with an outer try/catch, so making it
async (and 'void'-ing the single chat.ts call site) preserves behavior exactly.

Validation: tests/unit/chat-context-relay.test.ts + chat-route-coverage.test.ts
went from failing-at-load to green (+14 tests destravados).

* fix(sse): remove cross-provider credential leak in emergency fallback + combo/proxy audit fixes (#3699)

Integrated into release/v3.8.23

* fix(executors): inject MiMoCode anti-abuse marker so free endpoint stops 403ing (#3728)

Integrated into release/v3.8.23

* fix(dashboard): repair "Test all models" — toast crash, status icons, auto-hide (#3729)

Integrated into release/v3.8.23

* chore(deps): bump actions/upload-artifact from 4 to 7 (#3735)

Integrated into release/v3.8.23 — aligns upload-artifact to v7 (already used across ci.yml).

* chore(deps): bump actions/cache from 4 to 5 (#3734)

Integrated into release/v3.8.23 — actions/cache v4→v5.

* chore(deps): bump actions/download-artifact from 4 to 8 (#3733)

Integrated into release/v3.8.23 — download-artifact v4→v8.

* feat(fallback): add OMNIROUTE_EMERGENCY_FALLBACK env switch (#3741)

Adds an OMNIROUTE_EMERGENCY_FALLBACK env switch to disable the emergency budget-exhaustion fallback (reroute to free nvidia/gpt-oss-120b). Default unchanged (enabled). Closes #3739, related #2879.

Integrated into release/v3.8.23.

* i18n: comprehensive zh-CN translation improvements (#3736)

Aligns zh-CN to en (hundreds of entries), translates batch-action labels + settings sidebar menu, adds categoryConfig/endpointTokenSaver keys, resolves __MISSING__ stubs. Sidebar/SidebarTab hardcoded strings replaced with t(). en.json purely additive (8 new sidebar.* keys, 0 removed); cli-i18n gate green.

Integrated into release/v3.8.23.

* chore(release): v3.8.23 — 2026-06-12

- CHANGELOG: complete v3.8.23 section (28 bullets, 27 commits)
- fix(webdav): resolve promise on writeStream finish, not req end — eliminates
  intermittent 500 on PUT update (writeStream may not have flushed at rename time)
- test(autoCombo): stub DB calls from PR #3660 in tieredRotation.test.ts to prevent
  5s timeout in vitest (getModelIntelligenceBySource DB init path)
- chore(env-sync): add XDG_DATA_HOME + OMNIROUTE_OPENCODE_PLUGIN_DIR to IGNORE_FROM_CODE
  allowlist (introduced by PR #3726 setup-open-code.mjs, not OmniRoute config vars)
- chore(cli): regenerated bin/cli/api-commands/*.mjs (7 new, 27 updated)

* fix(model-family): fallback lookup also tries bare model name with dots

getNextFamilyFallback normalized dots-to-hyphens ("gemini-3.1-pro-high" →
"gemini-3-1-pro-high") but MODEL_FAMILIES keys use the literal dot form. The
lookup always missed, returning null for any model whose dots are part of the
name rather than a version separator.

Fallback: try MODEL_FAMILIES[lookupKey] ?? MODEL_FAMILIES[bareModel] so both
naming conventions are covered. Fixes T30 test (pre-existing since v3.8.22).

* feat: expose API key cost drilldown + quota % used (#3742)

Adds all-time USD cost per API key in the API Key Manager, a per-key deep-link into the Cost Explorer (filtered + grouped by model), URL-param hydration of range/groupBy/apiKeyIds, and a '% used' quota display. Review adjustments: extracted URL-param parsers to a tested module (Rule #18), i18n'd the new strings (en + zh-CN), dropped the redundant webdav-handler entry already on release.

Integrated into release/v3.8.23.

* feat: add provider display modes — All / Configured / Compact (#3743)

Replaces the Providers page configured-only toggle with All/Configured/Compact display modes (Compact = flat deduped grid, no-auth last). Persists the preference and migrates the legacy localStorage key. Rebased onto release/v3.8.23.

Integrated into release/v3.8.23.

* fix(cache): scope semantic-cache signature to API key (#3740)

Adds the api_key_id dimension to generateSignature's SHA-256 hash so two callers with different API keys never receive each other's cached responses. Threads apiKeyId through checkSemanticCache + both write sites; migration 098 clears pre-existing key-less entries; unauthenticated requests stay isolated from keyed ones. 3 TDD tests.

Integrated into release/v3.8.23.

* fix(responses): apply OpenAI Responses API stream=false spec default (#3708)

resolveStreamFlag now applies the stream=false-when-omitted default for sourceFormat=openai-responses (same as the existing claude path), so spec-compliant /v1/responses upstreams that return JSON no longer fall through to the wildcard-Accept heuristic and trigger STREAM_EARLY_EOF / 502. Codex CLI (stream:true) and explicit text/event-stream clients unaffected.

Integrated into release/v3.8.23.

* chore(release): reconcile CI gates for v3.8.23

- file-size baseline: re-freeze 8 files grown by PRs #3742/#3743/#3740
  (cost drilldown, provider display modes, cache key isolation)
- ARCHITECTURE.md: update executor count 55→60 (check:docs-counts drift)
- .env.example: add OMNIROUTE_EMERGENCY_FALLBACK (#3741, env-doc-sync)
- CHANGELOG: add formatted bullets for #3742, #3743, #3708, #3740,
  model-family-fallback fix; remove duplicate raw ### Fixed section

* test: restore assert count to satisfy check:test-masking gate

Three test files had net assertion removals after behavior-changing PRs:
- chatcore-translation-paths: emergency fallback moved to routing layer
  (#3699) — add body error assertion + model-name guard
- executor-vertex-extended: non-JSON is now Express API key (#3690) —
  add projects/-path guard to the express-key URL test
- stream-utils: empty streams now emit error (#3685) — add code/message/
  status/completePayload guards to both passthrough and translate variants

All new assertions are meaningful (code enum value, 5xx range, non-empty
message, onComplete must-not-fire contract).

* fix(ci): move rtl-logical-classes test to ui/ so vitest:ui runner collects it

---------

Co-authored-by: Felipe Almeman <4226997+zhiru@users.noreply.github.com>
Co-authored-by: NOXX - Commiter <artur1992123@mail.ru>
Co-authored-by: Nick Sullivan <142708+TechNickAI@users.noreply.github.com>
Co-authored-by: Markus Hartung <mail@hartmark.se>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: PizzaV <103120356+pizzav-xyz@users.noreply.github.com>
Co-authored-by: Randi <55005611+rdself@users.noreply.github.com>
Co-authored-by: oyi77 <14921983+oyi77@users.noreply.github.com>
Co-authored-by: Chewji <126886556+Chewji9875@users.noreply.github.com>
Co-authored-by: Hernan Javier Ardila Sanchez <hjasgr@gmail.com>
Co-authored-by: Felipe Sartori <felipesartori.ti@gmail.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Zois Pagoulatos <zpagoulatos@hotmail.com>
Co-authored-by: sdfsdfw2 <167810361+sdfsdfw2@users.noreply.github.com>
Co-authored-by: Witroch4 <witalo_rocha@hotmail.com>
HouMinXi pushed a commit to HouMinXi/OmniRoute that referenced this pull request Aug 2, 2026
* chore(release): open v3.8.23 development cycle

* fix(anthropic): strip top_p when temperature is set to avoid 400 (diegosouzapw#3691)

Integrated into release/v3.8.23

* fix(vertex): support Vertex AI Express-mode API keys (diegosouzapw#3690)

Integrated into release/v3.8.23

* fix(stream): error on empty Claude SSE instead of synthetic success (diegosouzapw#3689)

Integrated into release/v3.8.23

* fix(oauth): stop token-refresh invalidation loop + harden proxy resolution (diegosouzapw#3692)

Integrated into release/v3.8.23

* docs: add FUNDING.yml and Support section to README (diegosouzapw#3698)

Integrated into release/v3.8.23

* feat: gemini - handle known ratelimits (diegosouzapw#3686)

Integrated into release/v3.8.23

* fix: stream combo fails over on empty content-filtered response (diegosouzapw#3685) (diegosouzapw#3702)

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

* fix(antigravity): preserve gemini-3.1-pro high/low budget tiers (diegosouzapw#3696) (diegosouzapw#3703)

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(auto-combo): add auto-updating model intelligence scoring (diegosouzapw#3660)

Integrated into release/v3.8.23

* fix(gemini): context-mode fallback for signatureless tool calls (diegosouzapw#3688) (diegosouzapw#3704)

* chore(quality-gate): reconcile file-size baseline (27 files + providerLimits.ts) (diegosouzapw#3705)

* feat(vertex): dynamic model discovery via Generative Language models API (diegosouzapw#3712)

Integrated into release/v3.8.23. Vertex dynamic model discovery — surfaces image models (imagen-*, gemini-*-image), embeddings and audio from the live Generative Language catalog, with cached→static fallback and the shared parseGeminiModelsList helper. Validated: parser test 5/5, typecheck:core clean.

* fix(combo): gate reasoning token buffer (diegosouzapw#3700)

Integrated into release/v3.8.23. Makes the diegosouzapw#3588 reasoning token buffer safe and configurable: only inflates max_tokens when the model has a known, non-default output cap and the buffered value fits inside it; otherwise preserves/clamps the client limit. Adds the reasoningTokenBufferEnabled kill switch (default ON). Validated: combo-routing-engine 81/81, combo-config 25/25, combo-quality-validator-reasoning 12/12, phase1f 10/10, typecheck:core clean.

* refactor(diegosouzapw#3501): god-component Phase 1g-1j — client 4062→3408 LOC (-654) (diegosouzapw#3717)

Phase 1g-1j of diegosouzapw#3501: client 4062→3408 LOC. Pure extraction (ProviderPlaygroundPanel, useCommandCodeAuth, useExternalLinkFlow+ExternalLinkModal, useAuthFileHandlers) + loadConnProxies ReferenceError fix + phase1f test path fix.

Co-authored-by: oyi77 <14921983+oyi77@users.noreply.github.com>

* refactor(diegosouzapw#3501): god-component Phase 1k-1m — client 3408→2553 LOC (-855) (diegosouzapw#3721)

Phase 1k-1m of diegosouzapw#3501: client 3408→2553 LOC. Pure extraction (useModelImportHandlers+ImportProgressModal, useModelVisibilityHandlers, ProviderModelsSection).

Co-authored-by: oyi77 <14921983+oyi77@users.noreply.github.com>

* docs(changelog): restore diegosouzapw#3590 bullet lost on the v3.8.20 release branch

The fix itself reached main pre-tag via cherry-pick diegosouzapw#3591, but its changelog
bullet (commit db04ef2) only ever existed on release/v3.8.20 after the
squash-merge. Restored under [3.8.20] per the 2026-06-12 release-branch
leftover audit (_tasks/release-audit/release-leftovers-audit-2026-06-12.md).

* fix(kiro): resolve quota for IAM Identity Center accounts missing a profileArn (diegosouzapw#3722)

Integrated into release/v3.8.23

* refactor(diegosouzapw#3501): god-component Phase 1n-1s — client 2553→1376 LOC (-1177) (diegosouzapw#3725)

Phase 1n-1s of diegosouzapw#3501: client 2553→1376 LOC. Pure extraction (ConnectionsListPanel, ConnectionsHeaderToolbar, ZedImportCard, BatchTestResultsModal, AdaptaTutorialModal, useApiKeySave + helpers).

Co-authored-by: oyi77 <14921983+oyi77@users.noreply.github.com>

* feat(model-lockout): settings UI, backend integration, error classification, and success-decay recovery (diegosouzapw#3629)

Integrated into release/v3.8.23

* refactor(diegosouzapw#3501): god-component Phase 1t — client 1376→781 LOC (≤800 TARGET REACHED ✅) (diegosouzapw#3727)

Phase 1t of diegosouzapw#3501: client 1376→781 LOC (≤800 reached). Original god-component 12,882→781 (−94%).

Co-authored-by: oyi77 <14921983+oyi77@users.noreply.github.com>

* fix: bundle @omniroute/opencode-plugin inside omniroute + add 'setup opencode' CLI command (diegosouzapw#3726)

Integrated into release/v3.8.23

* feat(vertex): self-tracked USD spend since account added (diegosouzapw#3724)

Integrated into release/v3.8.23

* fix(qwen-web): migrate to v2 chat API with full cookie-jar replay (diegosouzapw#3288) (diegosouzapw#3723)

Integrated into release/v3.8.23

* fix(sse): make safeLogEvents async — 'await' in a sync function broke every chatHelpers import

diegosouzapw#3692 added a lazy 'await import(proxyEgress)' for egress-IP visibility inside
safeLogEvents, which is a sync function — an ES syntax error. It went unnoticed
because typecheck:core does not cover src/sse and no test in the merge gates
loaded chatHelpers via tsx; any consumer that did (chat-context-relay and
chat-route-coverage suites, integration harnesses) failed at module load with
'await can only be used inside an async function'.

safeLogEvents is fire-and-forget logging with an outer try/catch, so making it
async (and 'void'-ing the single chat.ts call site) preserves behavior exactly.

Validation: tests/unit/chat-context-relay.test.ts + chat-route-coverage.test.ts
went from failing-at-load to green (+14 tests destravados).

* fix(sse): remove cross-provider credential leak in emergency fallback + combo/proxy audit fixes (diegosouzapw#3699)

Integrated into release/v3.8.23

* fix(executors): inject MiMoCode anti-abuse marker so free endpoint stops 403ing (diegosouzapw#3728)

Integrated into release/v3.8.23

* fix(dashboard): repair "Test all models" — toast crash, status icons, auto-hide (diegosouzapw#3729)

Integrated into release/v3.8.23

* chore(deps): bump actions/upload-artifact from 4 to 7 (diegosouzapw#3735)

Integrated into release/v3.8.23 — aligns upload-artifact to v7 (already used across ci.yml).

* chore(deps): bump actions/cache from 4 to 5 (diegosouzapw#3734)

Integrated into release/v3.8.23 — actions/cache v4→v5.

* chore(deps): bump actions/download-artifact from 4 to 8 (diegosouzapw#3733)

Integrated into release/v3.8.23 — download-artifact v4→v8.

* feat(fallback): add OMNIROUTE_EMERGENCY_FALLBACK env switch (diegosouzapw#3741)

Adds an OMNIROUTE_EMERGENCY_FALLBACK env switch to disable the emergency budget-exhaustion fallback (reroute to free nvidia/gpt-oss-120b). Default unchanged (enabled). Closes diegosouzapw#3739, related diegosouzapw#2879.

Integrated into release/v3.8.23.

* i18n: comprehensive zh-CN translation improvements (diegosouzapw#3736)

Aligns zh-CN to en (hundreds of entries), translates batch-action labels + settings sidebar menu, adds categoryConfig/endpointTokenSaver keys, resolves __MISSING__ stubs. Sidebar/SidebarTab hardcoded strings replaced with t(). en.json purely additive (8 new sidebar.* keys, 0 removed); cli-i18n gate green.

Integrated into release/v3.8.23.

* chore(release): v3.8.23 — 2026-06-12

- CHANGELOG: complete v3.8.23 section (28 bullets, 27 commits)
- fix(webdav): resolve promise on writeStream finish, not req end — eliminates
  intermittent 500 on PUT update (writeStream may not have flushed at rename time)
- test(autoCombo): stub DB calls from PR diegosouzapw#3660 in tieredRotation.test.ts to prevent
  5s timeout in vitest (getModelIntelligenceBySource DB init path)
- chore(env-sync): add XDG_DATA_HOME + OMNIROUTE_OPENCODE_PLUGIN_DIR to IGNORE_FROM_CODE
  allowlist (introduced by PR diegosouzapw#3726 setup-open-code.mjs, not OmniRoute config vars)
- chore(cli): regenerated bin/cli/api-commands/*.mjs (7 new, 27 updated)

* fix(model-family): fallback lookup also tries bare model name with dots

getNextFamilyFallback normalized dots-to-hyphens ("gemini-3.1-pro-high" →
"gemini-3-1-pro-high") but MODEL_FAMILIES keys use the literal dot form. The
lookup always missed, returning null for any model whose dots are part of the
name rather than a version separator.

Fallback: try MODEL_FAMILIES[lookupKey] ?? MODEL_FAMILIES[bareModel] so both
naming conventions are covered. Fixes T30 test (pre-existing since v3.8.22).

* feat: expose API key cost drilldown + quota % used (diegosouzapw#3742)

Adds all-time USD cost per API key in the API Key Manager, a per-key deep-link into the Cost Explorer (filtered + grouped by model), URL-param hydration of range/groupBy/apiKeyIds, and a '% used' quota display. Review adjustments: extracted URL-param parsers to a tested module (Rule diegosouzapw#18), i18n'd the new strings (en + zh-CN), dropped the redundant webdav-handler entry already on release.

Integrated into release/v3.8.23.

* feat: add provider display modes — All / Configured / Compact (diegosouzapw#3743)

Replaces the Providers page configured-only toggle with All/Configured/Compact display modes (Compact = flat deduped grid, no-auth last). Persists the preference and migrates the legacy localStorage key. Rebased onto release/v3.8.23.

Integrated into release/v3.8.23.

* fix(cache): scope semantic-cache signature to API key (diegosouzapw#3740)

Adds the api_key_id dimension to generateSignature's SHA-256 hash so two callers with different API keys never receive each other's cached responses. Threads apiKeyId through checkSemanticCache + both write sites; migration 098 clears pre-existing key-less entries; unauthenticated requests stay isolated from keyed ones. 3 TDD tests.

Integrated into release/v3.8.23.

* fix(responses): apply OpenAI Responses API stream=false spec default (diegosouzapw#3708)

resolveStreamFlag now applies the stream=false-when-omitted default for sourceFormat=openai-responses (same as the existing claude path), so spec-compliant /v1/responses upstreams that return JSON no longer fall through to the wildcard-Accept heuristic and trigger STREAM_EARLY_EOF / 502. Codex CLI (stream:true) and explicit text/event-stream clients unaffected.

Integrated into release/v3.8.23.

* chore(release): reconcile CI gates for v3.8.23

- file-size baseline: re-freeze 8 files grown by PRs diegosouzapw#3742/diegosouzapw#3743/diegosouzapw#3740
  (cost drilldown, provider display modes, cache key isolation)
- ARCHITECTURE.md: update executor count 55→60 (check:docs-counts drift)
- .env.example: add OMNIROUTE_EMERGENCY_FALLBACK (diegosouzapw#3741, env-doc-sync)
- CHANGELOG: add formatted bullets for diegosouzapw#3742, diegosouzapw#3743, diegosouzapw#3708, diegosouzapw#3740,
  model-family-fallback fix; remove duplicate raw ### Fixed section

* test: restore assert count to satisfy check:test-masking gate

Three test files had net assertion removals after behavior-changing PRs:
- chatcore-translation-paths: emergency fallback moved to routing layer
  (diegosouzapw#3699) — add body error assertion + model-name guard
- executor-vertex-extended: non-JSON is now Express API key (diegosouzapw#3690) —
  add projects/-path guard to the express-key URL test
- stream-utils: empty streams now emit error (diegosouzapw#3685) — add code/message/
  status/completePayload guards to both passthrough and translate variants

All new assertions are meaningful (code enum value, 5xx range, non-empty
message, onComplete must-not-fire contract).

* fix(ci): move rtl-logical-classes test to ui/ so vitest:ui runner collects it

---------

Co-authored-by: Felipe Almeman <4226997+zhiru@users.noreply.github.com>
Co-authored-by: NOXX - Commiter <artur1992123@mail.ru>
Co-authored-by: Nick Sullivan <142708+TechNickAI@users.noreply.github.com>
Co-authored-by: Markus Hartung <mail@hartmark.se>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: PizzaV <103120356+pizzav-xyz@users.noreply.github.com>
Co-authored-by: Randi <55005611+rdself@users.noreply.github.com>
Co-authored-by: oyi77 <14921983+oyi77@users.noreply.github.com>
Co-authored-by: Chewji <126886556+Chewji9875@users.noreply.github.com>
Co-authored-by: Hernan Javier Ardila Sanchez <hjasgr@gmail.com>
Co-authored-by: Felipe Sartori <felipesartori.ti@gmail.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Zois Pagoulatos <zpagoulatos@hotmail.com>
Co-authored-by: sdfsdfw2 <167810361+sdfsdfw2@users.noreply.github.com>
Co-authored-by: Witroch4 <witalo_rocha@hotmail.com>
Poid-ZA pushed a commit to Poid-ZA/OmniRoute that referenced this pull request Aug 5, 2026
* chore(release): open v3.8.23 development cycle

* fix(anthropic): strip top_p when temperature is set to avoid 400 (diegosouzapw#3691)

Integrated into release/v3.8.23

* fix(vertex): support Vertex AI Express-mode API keys (diegosouzapw#3690)

Integrated into release/v3.8.23

* fix(stream): error on empty Claude SSE instead of synthetic success (diegosouzapw#3689)

Integrated into release/v3.8.23

* fix(oauth): stop token-refresh invalidation loop + harden proxy resolution (diegosouzapw#3692)

Integrated into release/v3.8.23

* docs: add FUNDING.yml and Support section to README (diegosouzapw#3698)

Integrated into release/v3.8.23

* feat: gemini - handle known ratelimits (diegosouzapw#3686)

Integrated into release/v3.8.23

* fix: stream combo fails over on empty content-filtered response (diegosouzapw#3685) (diegosouzapw#3702)

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

* fix(antigravity): preserve gemini-3.1-pro high/low budget tiers (diegosouzapw#3696) (diegosouzapw#3703)

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(auto-combo): add auto-updating model intelligence scoring (diegosouzapw#3660)

Integrated into release/v3.8.23

* fix(gemini): context-mode fallback for signatureless tool calls (diegosouzapw#3688) (diegosouzapw#3704)

* chore(quality-gate): reconcile file-size baseline (27 files + providerLimits.ts) (diegosouzapw#3705)

* feat(vertex): dynamic model discovery via Generative Language models API (diegosouzapw#3712)

Integrated into release/v3.8.23. Vertex dynamic model discovery — surfaces image models (imagen-*, gemini-*-image), embeddings and audio from the live Generative Language catalog, with cached→static fallback and the shared parseGeminiModelsList helper. Validated: parser test 5/5, typecheck:core clean.

* fix(combo): gate reasoning token buffer (diegosouzapw#3700)

Integrated into release/v3.8.23. Makes the diegosouzapw#3588 reasoning token buffer safe and configurable: only inflates max_tokens when the model has a known, non-default output cap and the buffered value fits inside it; otherwise preserves/clamps the client limit. Adds the reasoningTokenBufferEnabled kill switch (default ON). Validated: combo-routing-engine 81/81, combo-config 25/25, combo-quality-validator-reasoning 12/12, phase1f 10/10, typecheck:core clean.

* refactor(diegosouzapw#3501): god-component Phase 1g-1j — client 4062→3408 LOC (-654) (diegosouzapw#3717)

Phase 1g-1j of diegosouzapw#3501: client 4062→3408 LOC. Pure extraction (ProviderPlaygroundPanel, useCommandCodeAuth, useExternalLinkFlow+ExternalLinkModal, useAuthFileHandlers) + loadConnProxies ReferenceError fix + phase1f test path fix.

Co-authored-by: oyi77 <14921983+oyi77@users.noreply.github.com>

* refactor(diegosouzapw#3501): god-component Phase 1k-1m — client 3408→2553 LOC (-855) (diegosouzapw#3721)

Phase 1k-1m of diegosouzapw#3501: client 3408→2553 LOC. Pure extraction (useModelImportHandlers+ImportProgressModal, useModelVisibilityHandlers, ProviderModelsSection).

Co-authored-by: oyi77 <14921983+oyi77@users.noreply.github.com>

* docs(changelog): restore diegosouzapw#3590 bullet lost on the v3.8.20 release branch

The fix itself reached main pre-tag via cherry-pick diegosouzapw#3591, but its changelog
bullet (commit e33fdd4) only ever existed on release/v3.8.20 after the
squash-merge. Restored under [3.8.20] per the 2026-06-12 release-branch
leftover audit (_tasks/release-audit/release-leftovers-audit-2026-06-12.md).

* fix(kiro): resolve quota for IAM Identity Center accounts missing a profileArn (diegosouzapw#3722)

Integrated into release/v3.8.23

* refactor(diegosouzapw#3501): god-component Phase 1n-1s — client 2553→1376 LOC (-1177) (diegosouzapw#3725)

Phase 1n-1s of diegosouzapw#3501: client 2553→1376 LOC. Pure extraction (ConnectionsListPanel, ConnectionsHeaderToolbar, ZedImportCard, BatchTestResultsModal, AdaptaTutorialModal, useApiKeySave + helpers).

Co-authored-by: oyi77 <14921983+oyi77@users.noreply.github.com>

* feat(model-lockout): settings UI, backend integration, error classification, and success-decay recovery (diegosouzapw#3629)

Integrated into release/v3.8.23

* refactor(diegosouzapw#3501): god-component Phase 1t — client 1376→781 LOC (≤800 TARGET REACHED ✅) (diegosouzapw#3727)

Phase 1t of diegosouzapw#3501: client 1376→781 LOC (≤800 reached). Original god-component 12,882→781 (−94%).

Co-authored-by: oyi77 <14921983+oyi77@users.noreply.github.com>

* fix: bundle @omniroute/opencode-plugin inside omniroute + add 'setup opencode' CLI command (diegosouzapw#3726)

Integrated into release/v3.8.23

* feat(vertex): self-tracked USD spend since account added (diegosouzapw#3724)

Integrated into release/v3.8.23

* fix(qwen-web): migrate to v2 chat API with full cookie-jar replay (diegosouzapw#3288) (diegosouzapw#3723)

Integrated into release/v3.8.23

* fix(sse): make safeLogEvents async — 'await' in a sync function broke every chatHelpers import

diegosouzapw#3692 added a lazy 'await import(proxyEgress)' for egress-IP visibility inside
safeLogEvents, which is a sync function — an ES syntax error. It went unnoticed
because typecheck:core does not cover src/sse and no test in the merge gates
loaded chatHelpers via tsx; any consumer that did (chat-context-relay and
chat-route-coverage suites, integration harnesses) failed at module load with
'await can only be used inside an async function'.

safeLogEvents is fire-and-forget logging with an outer try/catch, so making it
async (and 'void'-ing the single chat.ts call site) preserves behavior exactly.

Validation: tests/unit/chat-context-relay.test.ts + chat-route-coverage.test.ts
went from failing-at-load to green (+14 tests destravados).

* fix(sse): remove cross-provider credential leak in emergency fallback + combo/proxy audit fixes (diegosouzapw#3699)

Integrated into release/v3.8.23

* fix(executors): inject MiMoCode anti-abuse marker so free endpoint stops 403ing (diegosouzapw#3728)

Integrated into release/v3.8.23

* fix(dashboard): repair "Test all models" — toast crash, status icons, auto-hide (diegosouzapw#3729)

Integrated into release/v3.8.23

* chore(deps): bump actions/upload-artifact from 4 to 7 (diegosouzapw#3735)

Integrated into release/v3.8.23 — aligns upload-artifact to v7 (already used across ci.yml).

* chore(deps): bump actions/cache from 4 to 5 (diegosouzapw#3734)

Integrated into release/v3.8.23 — actions/cache v4→v5.

* chore(deps): bump actions/download-artifact from 4 to 8 (diegosouzapw#3733)

Integrated into release/v3.8.23 — download-artifact v4→v8.

* feat(fallback): add OMNIROUTE_EMERGENCY_FALLBACK env switch (diegosouzapw#3741)

Adds an OMNIROUTE_EMERGENCY_FALLBACK env switch to disable the emergency budget-exhaustion fallback (reroute to free nvidia/gpt-oss-120b). Default unchanged (enabled). Closes diegosouzapw#3739, related diegosouzapw#2879.

Integrated into release/v3.8.23.

* i18n: comprehensive zh-CN translation improvements (diegosouzapw#3736)

Aligns zh-CN to en (hundreds of entries), translates batch-action labels + settings sidebar menu, adds categoryConfig/endpointTokenSaver keys, resolves __MISSING__ stubs. Sidebar/SidebarTab hardcoded strings replaced with t(). en.json purely additive (8 new sidebar.* keys, 0 removed); cli-i18n gate green.

Integrated into release/v3.8.23.

* chore(release): v3.8.23 — 2026-06-12

- CHANGELOG: complete v3.8.23 section (28 bullets, 27 commits)
- fix(webdav): resolve promise on writeStream finish, not req end — eliminates
  intermittent 500 on PUT update (writeStream may not have flushed at rename time)
- test(autoCombo): stub DB calls from PR diegosouzapw#3660 in tieredRotation.test.ts to prevent
  5s timeout in vitest (getModelIntelligenceBySource DB init path)
- chore(env-sync): add XDG_DATA_HOME + OMNIROUTE_OPENCODE_PLUGIN_DIR to IGNORE_FROM_CODE
  allowlist (introduced by PR diegosouzapw#3726 setup-open-code.mjs, not OmniRoute config vars)
- chore(cli): regenerated bin/cli/api-commands/*.mjs (7 new, 27 updated)

* fix(model-family): fallback lookup also tries bare model name with dots

getNextFamilyFallback normalized dots-to-hyphens ("gemini-3.1-pro-high" →
"gemini-3-1-pro-high") but MODEL_FAMILIES keys use the literal dot form. The
lookup always missed, returning null for any model whose dots are part of the
name rather than a version separator.

Fallback: try MODEL_FAMILIES[lookupKey] ?? MODEL_FAMILIES[bareModel] so both
naming conventions are covered. Fixes T30 test (pre-existing since v3.8.22).

* feat: expose API key cost drilldown + quota % used (diegosouzapw#3742)

Adds all-time USD cost per API key in the API Key Manager, a per-key deep-link into the Cost Explorer (filtered + grouped by model), URL-param hydration of range/groupBy/apiKeyIds, and a '% used' quota display. Review adjustments: extracted URL-param parsers to a tested module (Rule diegosouzapw#18), i18n'd the new strings (en + zh-CN), dropped the redundant webdav-handler entry already on release.

Integrated into release/v3.8.23.

* feat: add provider display modes — All / Configured / Compact (diegosouzapw#3743)

Replaces the Providers page configured-only toggle with All/Configured/Compact display modes (Compact = flat deduped grid, no-auth last). Persists the preference and migrates the legacy localStorage key. Rebased onto release/v3.8.23.

Integrated into release/v3.8.23.

* fix(cache): scope semantic-cache signature to API key (diegosouzapw#3740)

Adds the api_key_id dimension to generateSignature's SHA-256 hash so two callers with different API keys never receive each other's cached responses. Threads apiKeyId through checkSemanticCache + both write sites; migration 098 clears pre-existing key-less entries; unauthenticated requests stay isolated from keyed ones. 3 TDD tests.

Integrated into release/v3.8.23.

* fix(responses): apply OpenAI Responses API stream=false spec default (diegosouzapw#3708)

resolveStreamFlag now applies the stream=false-when-omitted default for sourceFormat=openai-responses (same as the existing claude path), so spec-compliant /v1/responses upstreams that return JSON no longer fall through to the wildcard-Accept heuristic and trigger STREAM_EARLY_EOF / 502. Codex CLI (stream:true) and explicit text/event-stream clients unaffected.

Integrated into release/v3.8.23.

* chore(release): reconcile CI gates for v3.8.23

- file-size baseline: re-freeze 8 files grown by PRs diegosouzapw#3742/diegosouzapw#3743/diegosouzapw#3740
  (cost drilldown, provider display modes, cache key isolation)
- ARCHITECTURE.md: update executor count 55→60 (check:docs-counts drift)
- .env.example: add OMNIROUTE_EMERGENCY_FALLBACK (diegosouzapw#3741, env-doc-sync)
- CHANGELOG: add formatted bullets for diegosouzapw#3742, diegosouzapw#3743, diegosouzapw#3708, diegosouzapw#3740,
  model-family-fallback fix; remove duplicate raw ### Fixed section

* test: restore assert count to satisfy check:test-masking gate

Three test files had net assertion removals after behavior-changing PRs:
- chatcore-translation-paths: emergency fallback moved to routing layer
  (diegosouzapw#3699) — add body error assertion + model-name guard
- executor-vertex-extended: non-JSON is now Express API key (diegosouzapw#3690) —
  add projects/-path guard to the express-key URL test
- stream-utils: empty streams now emit error (diegosouzapw#3685) — add code/message/
  status/completePayload guards to both passthrough and translate variants

All new assertions are meaningful (code enum value, 5xx range, non-empty
message, onComplete must-not-fire contract).

* fix(ci): move rtl-logical-classes test to ui/ so vitest:ui runner collects it

---------

Co-authored-by: Felipe Almeman <4226997+zhiru@users.noreply.github.com>
Co-authored-by: NOXX - Commiter <artur1992123@mail.ru>
Co-authored-by: Nick Sullivan <142708+TechNickAI@users.noreply.github.com>
Co-authored-by: Markus Hartung <mail@hartmark.se>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: PizzaV <103120356+pizzav-xyz@users.noreply.github.com>
Co-authored-by: Randi <55005611+rdself@users.noreply.github.com>
Co-authored-by: oyi77 <14921983+oyi77@users.noreply.github.com>
Co-authored-by: Chewji <126886556+Chewji9875@users.noreply.github.com>
Co-authored-by: Hernan Javier Ardila Sanchez <hjasgr@gmail.com>
Co-authored-by: Felipe Sartori <felipesartori.ti@gmail.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Zois Pagoulatos <zpagoulatos@hotmail.com>
Co-authored-by: sdfsdfw2 <167810361+sdfsdfw2@users.noreply.github.com>
Co-authored-by: Witroch4 <witalo_rocha@hotmail.com>
tkgo11 pushed a commit to tkgo11/OmniRoute that referenced this pull request Sep 23, 2026
…opencode' CLI command (diegosouzapw#3726)

Integrated into release/v3.8.23
tkgo11 pushed a commit to tkgo11/OmniRoute that referenced this pull request Sep 23, 2026
- CHANGELOG: complete v3.8.23 section (28 bullets, 27 commits)
- fix(webdav): resolve promise on writeStream finish, not req end — eliminates
  intermittent 500 on PUT update (writeStream may not have flushed at rename time)
- test(autoCombo): stub DB calls from PR diegosouzapw#3660 in tieredRotation.test.ts to prevent
  5s timeout in vitest (getModelIntelligenceBySource DB init path)
- chore(env-sync): add XDG_DATA_HOME + OMNIROUTE_OPENCODE_PLUGIN_DIR to IGNORE_FROM_CODE
  allowlist (introduced by PR diegosouzapw#3726 setup-open-code.mjs, not OmniRoute config vars)
- chore(cli): regenerated bin/cli/api-commands/*.mjs (7 new, 27 updated)
muhamadgalihsaputra pushed a commit to niyatna/NiyatnaRoute that referenced this pull request Sep 27, 2026
* chore(release): open v3.8.23 development cycle

* fix(anthropic): strip top_p when temperature is set to avoid 400 (diegosouzapw#3691)

Integrated into release/v3.8.23

* fix(vertex): support Vertex AI Express-mode API keys (diegosouzapw#3690)

Integrated into release/v3.8.23

* fix(stream): error on empty Claude SSE instead of synthetic success (diegosouzapw#3689)

Integrated into release/v3.8.23

* fix(oauth): stop token-refresh invalidation loop + harden proxy resolution (diegosouzapw#3692)

Integrated into release/v3.8.23

* docs: add FUNDING.yml and Support section to README (diegosouzapw#3698)

Integrated into release/v3.8.23

* feat: gemini - handle known ratelimits (diegosouzapw#3686)

Integrated into release/v3.8.23

* fix: stream combo fails over on empty content-filtered response (diegosouzapw#3685) (diegosouzapw#3702)

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

* fix(antigravity): preserve gemini-3.1-pro high/low budget tiers (diegosouzapw#3696) (diegosouzapw#3703)

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(auto-combo): add auto-updating model intelligence scoring (diegosouzapw#3660)

Integrated into release/v3.8.23

* fix(gemini): context-mode fallback for signatureless tool calls (diegosouzapw#3688) (diegosouzapw#3704)

* chore(quality-gate): reconcile file-size baseline (27 files + providerLimits.ts) (diegosouzapw#3705)

* feat(vertex): dynamic model discovery via Generative Language models API (diegosouzapw#3712)

Integrated into release/v3.8.23. Vertex dynamic model discovery — surfaces image models (imagen-*, gemini-*-image), embeddings and audio from the live Generative Language catalog, with cached→static fallback and the shared parseGeminiModelsList helper. Validated: parser test 5/5, typecheck:core clean.

* fix(combo): gate reasoning token buffer (diegosouzapw#3700)

Integrated into release/v3.8.23. Makes the diegosouzapw#3588 reasoning token buffer safe and configurable: only inflates max_tokens when the model has a known, non-default output cap and the buffered value fits inside it; otherwise preserves/clamps the client limit. Adds the reasoningTokenBufferEnabled kill switch (default ON). Validated: combo-routing-engine 81/81, combo-config 25/25, combo-quality-validator-reasoning 12/12, phase1f 10/10, typecheck:core clean.

* refactor(diegosouzapw#3501): god-component Phase 1g-1j — client 4062→3408 LOC (-654) (diegosouzapw#3717)

Phase 1g-1j of diegosouzapw#3501: client 4062→3408 LOC. Pure extraction (ProviderPlaygroundPanel, useCommandCodeAuth, useExternalLinkFlow+ExternalLinkModal, useAuthFileHandlers) + loadConnProxies ReferenceError fix + phase1f test path fix.

Co-authored-by: oyi77 <14921983+oyi77@users.noreply.github.com>

* refactor(diegosouzapw#3501): god-component Phase 1k-1m — client 3408→2553 LOC (-855) (diegosouzapw#3721)

Phase 1k-1m of diegosouzapw#3501: client 3408→2553 LOC. Pure extraction (useModelImportHandlers+ImportProgressModal, useModelVisibilityHandlers, ProviderModelsSection).

Co-authored-by: oyi77 <14921983+oyi77@users.noreply.github.com>

* docs(changelog): restore diegosouzapw#3590 bullet lost on the v3.8.20 release branch

The fix itself reached main pre-tag via cherry-pick diegosouzapw#3591, but its changelog
bullet (commit a6b99843f) only ever existed on release/v3.8.20 after the
squash-merge. Restored under [3.8.20] per the 2026-06-12 release-branch
leftover audit (_tasks/release-audit/release-leftovers-audit-2026-06-12.md).

* fix(kiro): resolve quota for IAM Identity Center accounts missing a profileArn (diegosouzapw#3722)

Integrated into release/v3.8.23

* refactor(diegosouzapw#3501): god-component Phase 1n-1s — client 2553→1376 LOC (-1177) (diegosouzapw#3725)

Phase 1n-1s of diegosouzapw#3501: client 2553→1376 LOC. Pure extraction (ConnectionsListPanel, ConnectionsHeaderToolbar, ZedImportCard, BatchTestResultsModal, AdaptaTutorialModal, useApiKeySave + helpers).

Co-authored-by: oyi77 <14921983+oyi77@users.noreply.github.com>

* feat(model-lockout): settings UI, backend integration, error classification, and success-decay recovery (diegosouzapw#3629)

Integrated into release/v3.8.23

* refactor(diegosouzapw#3501): god-component Phase 1t — client 1376→781 LOC (≤800 TARGET REACHED ✅) (diegosouzapw#3727)

Phase 1t of diegosouzapw#3501: client 1376→781 LOC (≤800 reached). Original god-component 12,882→781 (−94%).

Co-authored-by: oyi77 <14921983+oyi77@users.noreply.github.com>

* fix: bundle @omniroute/opencode-plugin inside omniroute + add 'setup opencode' CLI command (diegosouzapw#3726)

Integrated into release/v3.8.23

* feat(vertex): self-tracked USD spend since account added (diegosouzapw#3724)

Integrated into release/v3.8.23

* fix(qwen-web): migrate to v2 chat API with full cookie-jar replay (diegosouzapw#3288) (diegosouzapw#3723)

Integrated into release/v3.8.23

* fix(sse): make safeLogEvents async — 'await' in a sync function broke every chatHelpers import

diegosouzapw#3692 added a lazy 'await import(proxyEgress)' for egress-IP visibility inside
safeLogEvents, which is a sync function — an ES syntax error. It went unnoticed
because typecheck:core does not cover src/sse and no test in the merge gates
loaded chatHelpers via tsx; any consumer that did (chat-context-relay and
chat-route-coverage suites, integration harnesses) failed at module load with
'await can only be used inside an async function'.

safeLogEvents is fire-and-forget logging with an outer try/catch, so making it
async (and 'void'-ing the single chat.ts call site) preserves behavior exactly.

Validation: tests/unit/chat-context-relay.test.ts + chat-route-coverage.test.ts
went from failing-at-load to green (+14 tests destravados).

* fix(sse): remove cross-provider credential leak in emergency fallback + combo/proxy audit fixes (diegosouzapw#3699)

Integrated into release/v3.8.23

* fix(executors): inject MiMoCode anti-abuse marker so free endpoint stops 403ing (diegosouzapw#3728)

Integrated into release/v3.8.23

* fix(dashboard): repair "Test all models" — toast crash, status icons, auto-hide (diegosouzapw#3729)

Integrated into release/v3.8.23

* chore(deps): bump actions/upload-artifact from 4 to 7 (diegosouzapw#3735)

Integrated into release/v3.8.23 — aligns upload-artifact to v7 (already used across ci.yml).

* chore(deps): bump actions/cache from 4 to 5 (diegosouzapw#3734)

Integrated into release/v3.8.23 — actions/cache v4→v5.

* chore(deps): bump actions/download-artifact from 4 to 8 (diegosouzapw#3733)

Integrated into release/v3.8.23 — download-artifact v4→v8.

* feat(fallback): add OMNIROUTE_EMERGENCY_FALLBACK env switch (diegosouzapw#3741)

Adds an OMNIROUTE_EMERGENCY_FALLBACK env switch to disable the emergency budget-exhaustion fallback (reroute to free nvidia/gpt-oss-120b). Default unchanged (enabled). Closes diegosouzapw#3739, related diegosouzapw#2879.

Integrated into release/v3.8.23.

* i18n: comprehensive zh-CN translation improvements (diegosouzapw#3736)

Aligns zh-CN to en (hundreds of entries), translates batch-action labels + settings sidebar menu, adds categoryConfig/endpointTokenSaver keys, resolves __MISSING__ stubs. Sidebar/SidebarTab hardcoded strings replaced with t(). en.json purely additive (8 new sidebar.* keys, 0 removed); cli-i18n gate green.

Integrated into release/v3.8.23.

* chore(release): v3.8.23 — 2026-06-12

- CHANGELOG: complete v3.8.23 section (28 bullets, 27 commits)
- fix(webdav): resolve promise on writeStream finish, not req end — eliminates
  intermittent 500 on PUT update (writeStream may not have flushed at rename time)
- test(autoCombo): stub DB calls from PR diegosouzapw#3660 in tieredRotation.test.ts to prevent
  5s timeout in vitest (getModelIntelligenceBySource DB init path)
- chore(env-sync): add XDG_DATA_HOME + OMNIROUTE_OPENCODE_PLUGIN_DIR to IGNORE_FROM_CODE
  allowlist (introduced by PR diegosouzapw#3726 setup-open-code.mjs, not OmniRoute config vars)
- chore(cli): regenerated bin/cli/api-commands/*.mjs (7 new, 27 updated)

* fix(model-family): fallback lookup also tries bare model name with dots

getNextFamilyFallback normalized dots-to-hyphens ("gemini-3.1-pro-high" →
"gemini-3-1-pro-high") but MODEL_FAMILIES keys use the literal dot form. The
lookup always missed, returning null for any model whose dots are part of the
name rather than a version separator.

Fallback: try MODEL_FAMILIES[lookupKey] ?? MODEL_FAMILIES[bareModel] so both
naming conventions are covered. Fixes T30 test (pre-existing since v3.8.22).

* feat: expose API key cost drilldown + quota % used (diegosouzapw#3742)

Adds all-time USD cost per API key in the API Key Manager, a per-key deep-link into the Cost Explorer (filtered + grouped by model), URL-param hydration of range/groupBy/apiKeyIds, and a '% used' quota display. Review adjustments: extracted URL-param parsers to a tested module (Rule diegosouzapw#18), i18n'd the new strings (en + zh-CN), dropped the redundant webdav-handler entry already on release.

Integrated into release/v3.8.23.

* feat: add provider display modes — All / Configured / Compact (diegosouzapw#3743)

Replaces the Providers page configured-only toggle with All/Configured/Compact display modes (Compact = flat deduped grid, no-auth last). Persists the preference and migrates the legacy localStorage key. Rebased onto release/v3.8.23.

Integrated into release/v3.8.23.

* fix(cache): scope semantic-cache signature to API key (diegosouzapw#3740)

Adds the api_key_id dimension to generateSignature's SHA-256 hash so two callers with different API keys never receive each other's cached responses. Threads apiKeyId through checkSemanticCache + both write sites; migration 098 clears pre-existing key-less entries; unauthenticated requests stay isolated from keyed ones. 3 TDD tests.

Integrated into release/v3.8.23.

* fix(responses): apply OpenAI Responses API stream=false spec default (diegosouzapw#3708)

resolveStreamFlag now applies the stream=false-when-omitted default for sourceFormat=openai-responses (same as the existing claude path), so spec-compliant /v1/responses upstreams that return JSON no longer fall through to the wildcard-Accept heuristic and trigger STREAM_EARLY_EOF / 502. Codex CLI (stream:true) and explicit text/event-stream clients unaffected.

Integrated into release/v3.8.23.

* chore(release): reconcile CI gates for v3.8.23

- file-size baseline: re-freeze 8 files grown by PRs diegosouzapw#3742/diegosouzapw#3743/diegosouzapw#3740
  (cost drilldown, provider display modes, cache key isolation)
- ARCHITECTURE.md: update executor count 55→60 (check:docs-counts drift)
- .env.example: add OMNIROUTE_EMERGENCY_FALLBACK (diegosouzapw#3741, env-doc-sync)
- CHANGELOG: add formatted bullets for diegosouzapw#3742, diegosouzapw#3743, diegosouzapw#3708, diegosouzapw#3740,
  model-family-fallback fix; remove duplicate raw ### Fixed section

* test: restore assert count to satisfy check:test-masking gate

Three test files had net assertion removals after behavior-changing PRs:
- chatcore-translation-paths: emergency fallback moved to routing layer
  (diegosouzapw#3699) — add body error assertion + model-name guard
- executor-vertex-extended: non-JSON is now Express API key (diegosouzapw#3690) —
  add projects/-path guard to the express-key URL test
- stream-utils: empty streams now emit error (diegosouzapw#3685) — add code/message/
  status/completePayload guards to both passthrough and translate variants

All new assertions are meaningful (code enum value, 5xx range, non-empty
message, onComplete must-not-fire contract).

* fix(ci): move rtl-logical-classes test to ui/ so vitest:ui runner collects it

---------

Co-authored-by: Felipe Almeman <4226997+zhiru@users.noreply.github.com>
Co-authored-by: NOXX - Commiter <artur1992123@mail.ru>
Co-authored-by: Nick Sullivan <142708+TechNickAI@users.noreply.github.com>
Co-authored-by: Markus Hartung <mail@hartmark.se>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: PizzaV <103120356+pizzav-xyz@users.noreply.github.com>
Co-authored-by: Randi <55005611+rdself@users.noreply.github.com>
Co-authored-by: oyi77 <14921983+oyi77@users.noreply.github.com>
Co-authored-by: Chewji <126886556+Chewji9875@users.noreply.github.com>
Co-authored-by: Hernan Javier Ardila Sanchez <hjasgr@gmail.com>
Co-authored-by: Felipe Sartori <felipesartori.ti@gmail.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Zois Pagoulatos <zpagoulatos@hotmail.com>
Co-authored-by: sdfsdfw2 <167810361+sdfsdfw2@users.noreply.github.com>
Co-authored-by: Witroch4 <witalo_rocha@hotmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants