Compare commits

...

123 Commits

Author SHA1 Message Date
diegosouzapw
ce0b2bb122 docs(agents): advance the documented Bun pin to 1.4.2
#13661 bumped the exact `bun` devDependency (and `@types/bun`) from 1.4.0
to 1.4.2, and #12977 moved the `oven/bun` image to 1.4.2-slim. AGENTS.md
still documented 1.4.0, so the guide disagreed with package.json for
every agent reading it as authority.

Version string only. The surrounding policy is deliberately unchanged:
Bun stays confined to the allow-listed gate/generator scripts plus the
`test:bun:db` smoke suite, and Node remains the only supported runtime.

Operator approved editing this protected surface for this change.
2026-09-17 02:28:20 -03:00
dependabot[bot]
602161d7bc deps: bump the development group across 1 directory with 15 updates (#13661)
Bumps the development group with 15 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [@eslint/compat](https://github.com/eslint/rewrite/tree/HEAD/packages/compat) | `2.1.0` | `2.1.1` |
| [@playwright/test](https://github.com/microsoft/playwright) | `1.62.1` | `1.63.0` |
| [@testing-library/user-event](https://github.com/testing-library/user-event) | `14.6.6` | `14.6.7` |
| [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) | `26.4.0` | `26.5.1` |
| [bun](https://github.com/oven-sh/bun) | `1.4.0` | `1.4.2` |
| [@types/bun](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/bun) | `1.4.0` | `1.4.2` |
| [eslint](https://github.com/eslint/eslint) | `10.9.1` | `10.10.0` |
| [eslint-config-next](https://github.com/vercel/next.js/tree/HEAD/packages/eslint-config-next) | `16.3.3` | `16.3.5` |
| [fast-check](https://github.com/dubzzz/fast-check/tree/HEAD/packages/fast-check) | `4.9.0` | `4.10.0` |
| [knip](https://github.com/webpro-nl/knip/tree/HEAD/packages/knip) | `6.34.0` | `6.35.1` |
| [lint-staged](https://github.com/lint-staged/lint-staged) | `17.4.1` | `17.5.1` |
| [opencode-ai](https://github.com/anomalyco/opencode) | `1.18.25` | `1.18.30` |
| [promptfoo](https://github.com/promptfoo/promptfoo) | `0.122.2` | `0.123.0` |
| [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint) | `8.69.0` | `8.70.0` |
| [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest) | `4.1.11` | `5.0.0` |



Updates `@eslint/compat` from 2.1.0 to 2.1.1
- [Release notes](https://github.com/eslint/rewrite/releases)
- [Changelog](https://github.com/eslint/rewrite/blob/main/packages/compat/CHANGELOG.md)
- [Commits](https://github.com/eslint/rewrite/commits/compat-v2.1.1/packages/compat)

Updates `@playwright/test` from 1.62.1 to 1.63.0
- [Release notes](https://github.com/microsoft/playwright/releases)
- [Commits](https://github.com/microsoft/playwright/compare/v1.62.1...v1.63.0)

Updates `@testing-library/user-event` from 14.6.6 to 14.6.7
- [Release notes](https://github.com/testing-library/user-event/releases)
- [Changelog](https://github.com/testing-library/user-event/blob/main/CHANGELOG.md)
- [Commits](https://github.com/testing-library/user-event/compare/v14.6.6...v14.6.7)

Updates `@types/node` from 26.4.0 to 26.5.1
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

Updates `bun` from 1.4.0 to 1.4.2
- [Release notes](https://github.com/oven-sh/bun/releases)
- [Commits](https://github.com/oven-sh/bun/compare/bun-v1.4.0...bun-v1.4.2)

Updates `@types/bun` from 1.4.0 to 1.4.2
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/bun)

Updates `eslint` from 10.9.1 to 10.10.0
- [Release notes](https://github.com/eslint/eslint/releases)
- [Commits](https://github.com/eslint/eslint/compare/v10.9.1...v10.10.0)

Updates `eslint-config-next` from 16.3.3 to 16.3.5
- [Release notes](https://github.com/vercel/next.js/releases)
- [Commits](https://github.com/vercel/next.js/commits/v16.3.5/packages/eslint-config-next)

Updates `fast-check` from 4.9.0 to 4.10.0
- [Release notes](https://github.com/dubzzz/fast-check/releases)
- [Changelog](https://github.com/dubzzz/fast-check/blob/main/packages/fast-check/CHANGELOG.md)
- [Commits](https://github.com/dubzzz/fast-check/commits/v4.10.0/packages/fast-check)

Updates `knip` from 6.34.0 to 6.35.1
- [Release notes](https://github.com/webpro-nl/knip/releases)
- [Commits](https://github.com/webpro-nl/knip/commits/knip@6.35.1/packages/knip)

Updates `lint-staged` from 17.4.1 to 17.5.1
- [Release notes](https://github.com/lint-staged/lint-staged/releases)
- [Changelog](https://github.com/lint-staged/lint-staged/blob/main/CHANGELOG.md)
- [Commits](https://github.com/lint-staged/lint-staged/compare/v17.4.1...v17.5.1)

Updates `opencode-ai` from 1.18.25 to 1.18.30
- [Release notes](https://github.com/anomalyco/opencode/releases)
- [Commits](https://github.com/anomalyco/opencode/compare/v1.18.25...v1.18.30)

Updates `promptfoo` from 0.122.2 to 0.123.0
- [Release notes](https://github.com/promptfoo/promptfoo/releases)
- [Changelog](https://github.com/promptfoo/promptfoo/blob/main/CHANGELOG.md)
- [Commits](https://github.com/promptfoo/promptfoo/compare/0.122.2...0.123.0)

Updates `typescript-eslint` from 8.69.0 to 8.70.0
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.70.0/packages/typescript-eslint)

Updates `vitest` from 4.1.11 to 5.0.0
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Changelog](https://github.com/vitest-dev/vitest/blob/main/docs/releases.md)
- [Commits](https://github.com/vitest-dev/vitest/commits/v5.0.0/packages/vitest)

---
updated-dependencies:
- dependency-name: "@eslint/compat"
  dependency-version: 2.1.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development
- dependency-name: "@playwright/test"
  dependency-version: 1.63.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development
- dependency-name: "@testing-library/user-event"
  dependency-version: 14.6.7
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development
- dependency-name: "@types/bun"
  dependency-version: 1.4.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development
- dependency-name: "@types/node"
  dependency-version: 26.5.1
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development
- dependency-name: bun
  dependency-version: 1.4.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development
- dependency-name: eslint
  dependency-version: 10.10.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development
- dependency-name: eslint-config-next
  dependency-version: 16.3.4
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development
- dependency-name: fast-check
  dependency-version: 4.10.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development
- dependency-name: knip
  dependency-version: 6.35.1
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development
- dependency-name: lint-staged
  dependency-version: 17.5.1
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development
- dependency-name: opencode-ai
  dependency-version: 1.18.30
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development
- dependency-name: promptfoo
  dependency-version: 0.123.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development
- dependency-name: typescript-eslint
  dependency-version: 8.70.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development
- dependency-name: vitest
  dependency-version: 5.0.0
  dependency-type: direct:development
  update-type: version-update:semver-major
  dependency-group: development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-17 02:26:48 -03:00
dependabot[bot]
856051901d deps: bump oven/bun from 1.4.0-slim to 1.4.2-slim (#12977)
Bumps oven/bun from 1.4.0-slim to 1.4.2-slim.

---
updated-dependencies:
- dependency-name: oven/bun
  dependency-version: 1.4.2-slim
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-17 02:26:20 -03:00
Diego Rodrigues de Sa e Souza
3d5baf13f4 fix(providers): strip Vertex doc script blocks whose end tag carries junk (#13936)
The Vertex model-docs HTML is converted to plain text before the table
parser reads context-window and token-limit numbers out of the cells.
The script/style removal pass required the end tag to be `</script\s*>`,
but the HTML spec closes the element on `</script\t\n foo>` too. Such a
block survived the pass; the generic `<[^>]+>` strip below then removed
both tags and kept the script BODY, so text that only ever existed inside
a script became cell text the number parser trusts.

Accept any end tag that starts with `</script`/`</style` followed by a
tag-name boundary, matching what a browser does.

CodeQL js/bad-tag-filter, alert #1007.
2026-09-16 23:57:14 -03:00
dependabot[bot]
f89e188a9f deps: bump js-yaml (#13212)
Merged as-is — single-file lockfile bump in `/electron`, 3 lines, transitive resolution only.

js-yaml 4.3.2 hard-limits the merge-sequence size and counts empty mappings toward `maxTotalMergeKeys` (nodeca/js-yaml#797) — CPU-DoS hardening, which is worth taking promptly and carries no API surface change.

Verified against the tip before merging: `electron/package-lock.json` is the only file touched.
2026-09-16 23:17:20 -03:00
Ravi Tharuma
416c736bb9 fix(oauth): soft-fail Claude refresh so CredentialHealth is not sticky-dead (#13185)
Merged after boarding with #13426 into one worktree cut from `release/v3.8.51` (both verified as ancestors of the combined HEAD before validating).

**Evidence**
- Your own test plus **every sibling** in the module — 14 files across `tokenHealthCheck*`, `token-health-check*`, `credential-health*` and `issue-13470-token-refresh-proxy-bypass`: **72/72 pass** on the combined tree. Running the siblings and not just the PR's own file is deliberate: this PR changes sweep-path state that several of those files exercise independently.
- Gates: `check-changelog-integrity` PASS, `check-complexity` PASS (2842 vs baseline 3218), `check-cognitive-complexity` PASS (1284 vs 1437), `typecheck:core` PASS.

**Reconciled — one real gate violation, fixed in your branch (d9164886)**

`check-file-size` genuinely tripped on this PR: `src/lib/tokenHealthCheck.ts` goes 1214 → 1221, past a frozen ceiling of 1218 that had only 4 lines of headroom. Attributed by measuring both sides, not assumed — the tip is at 1214 with no violation. Rebaselined the ceiling to 1221 with a dated justification key, since the growth *is* the fix: preserving the `refresh_token` and telling a transient failure apart from a dead credential needs extra state on the sweep path that cannot leave the module without breaking its internal API.

One thing that looked like your problem and is not, recorded so nobody re-raises it: `open-sse/executors/codex.ts: 1529 > 1528` shows up when the gate runs on your branch. Your branch carries an older merge of the release where that file was longer; the tip has it at 1524, your diff never touches it, and it does not survive the squash.

Thanks, @RaviTharuma — a sticky-dead `CredentialHealth` is the worst failure mode here, because a transient refresh blip permanently parks a working account and nothing ever retries it. Driving the real provider through two consecutive sweeps and re-reading the DB row in between is the right way to prove the state actually clears.
2026-09-16 23:10:24 -03:00
Ravi Tharuma
683a552fcb fix(opencode-plugin): report disk-cache age in the stale-fallback warning (#13426)
Merged after boarding with #13185 into one worktree cut from `release/v3.8.51` (both verified as ancestors of the combined HEAD before validating).

**Evidence**
- Full `@omniroute/opencode-plugin` suite — not just this PR's file: **373/373 pass** across all 26 test files.
- Gates on the combined tree: `check-changelog-integrity` PASS, `check-complexity` PASS (2842 vs baseline 3218), `check-cognitive-complexity` PASS (1284 vs 1437), `typecheck:core` PASS.
- `check-file-size`: no violation attributable to this PR.

**One note for the record.** On a first run the suite reported `scaffold: built ESM default export resolves with the v1 plugin shape` failing with `ERR_MODULE_NOT_FOUND … dist/index.js`. That was a fresh worktree without the plugin built, not a defect in this PR — after `npm run build` in the workspace, all 373 pass. Flagging it because that failure reads exactly like a PR bug and could easily be misattributed to you on a future run.

Thanks, @RaviTharuma — putting the snapshot age in the warning turns "using stale disk cache" from a fact into something an operator can act on, and you covered `snapshotAgeLabel` with both the numeric-`writtenAt` and missing-`writtenAt` cases rather than only the happy path.
2026-09-16 22:56:38 -03:00
Nguyen Thanh Dat
5acac8021d fix(redis): namespace warmup circuit-breaker keys with REDIS_KEY_PREFIX (#13328)
The warmup scheduler's circuit-breaker keys were written to Redis without `REDIS_KEY_PREFIX`, so they escaped OmniRoute's namespace and could collide with another app sharing the instance — the one Redis surface the prefix wasn't reaching. Probe: 2/2 pass in `tests/unit/lib/warmupScheduler/redisCircuitBreakerStorePrefix.test.ts`, covering both the prefixed case and the unset/blank case where keys must stay unchanged.

**Batch validation** — boarded with the other 10 PRs of your batch into one worktree cut from `release/v3.8.51`; every PR verified as an ancestor of the combined HEAD before validating.

- Focused tests across all 11 PRs: **104/104 pass** on the combined tree.
- Gates on the combined tree: `check-changelog-integrity` PASS, `check-complexity` PASS, `check-cognitive-complexity` PASS, `typecheck:core` PASS, `check:open-sse-typecheck` PASS.
- `check-file-size` is red, but reproduces with byte-identical line counts on the pure `release/v3.8.51` tip (`open-sse/handlers/imageGeneration.ts` 3304, `open-sse/services/combo/roundRobinCombo.ts` 1221, `open-sse/utils/stream.ts` 3115). Inherited base-red, nothing added by this batch — it is also why this PR's "Fast Quality Gates" check was red.

**Reconciled** — this PR was `CONFLICTING`. The conflict was in `docs/reference/ENVIRONMENT.md` and purely additive: the release tip had inserted `APP_BIND_HOST` / `QDRANT_BIND_HOST` / `BIFROST_BIND_HOST` rows directly above the `REDIS_KEY_PREFIX` row you edited. Kept both sides — the tip's three new rows and your updated description naming the warmup circuit breaker — then merged the current release branch in (120a92f6) and re-ran your focused test on the reconciled tree: 2/2 pass. No line of your diff was dropped.

Thanks, @datrixlab — you also updated `.env.example`, `docs/ops/REDIS_PRODUCTION_CONFIG.md` and `ENVIRONMENT.md` alongside the code, which is why the only thing left to do here was a mechanical conflict resolution.
2026-09-16 21:09:38 -03:00
Nguyen Thanh Dat
7ad270ca7a fix(translator): send a Claude tool_result image to Gemini as inlineData (#13335)
A Claude `tool_result` carrying an image was not translated into Gemini `inlineData`, so the image was dropped from the conversation. Probe on your head: 3/3 pass.

Thanks, @datrixlab.

**Batch validation** — boarded with the other 10 PRs of your batch into one worktree cut from `release/v3.8.51`; every PR verified as an ancestor of the combined HEAD before validating.

- Focused tests across all 11 PRs: **104/104 pass** on the combined tree.
- Gates on the combined tree: `check-changelog-integrity` PASS, `check-complexity` PASS, `check-cognitive-complexity` PASS, `typecheck:core` PASS, `check:open-sse-typecheck` PASS.
- `check-file-size` is red, but reproduces with byte-identical line counts on the pure `release/v3.8.51` tip (`open-sse/handlers/imageGeneration.ts` 3304, `open-sse/services/combo/roundRobinCombo.ts` 1221, `open-sse/utils/stream.ts` 3115). Inherited base-red, nothing added by this batch — it is also why this PR's "Fast Quality Gates" check was red.
2026-09-16 21:03:57 -03:00
Nguyen Thanh Dat
c1338f1e78 fix(translator): pair id-less Gemini tool results with their call (#13334)
Gemini tool results without an id could not be paired with their originating call, so the pairing fell apart on any history that omitted ids. Probe on your head: 24/24 pass across `gemini-tool-result-without-id` and the existing `v1beta-gemini-tool-calling-6222` suite; the combined run reconfirmed both plus the antigravity path.

Thanks, @datrixlab — extracting `geminiToolCallIds.ts` as a shared helper instead of duplicating the pairing logic across `gemini-to-openai`, `antigravity-to-openai` and the v1beta converter is what keeps the three from drifting apart later.

**Batch validation** — boarded with the other 10 PRs of your batch into one worktree cut from `release/v3.8.51`; every PR verified as an ancestor of the combined HEAD before validating.

- Focused tests across all 11 PRs: **104/104 pass** on the combined tree.
- Gates on the combined tree: `check-changelog-integrity` PASS, `check-complexity` PASS, `check-cognitive-complexity` PASS, `typecheck:core` PASS, `check:open-sse-typecheck` PASS.
- `check-file-size` is red, but reproduces with byte-identical line counts on the pure `release/v3.8.51` tip (`open-sse/handlers/imageGeneration.ts` 3304, `open-sse/services/combo/roundRobinCombo.ts` 1221, `open-sse/utils/stream.ts` 3115). Inherited base-red, nothing added by this batch — it is also why this PR's "Fast Quality Gates" check was red.
2026-09-16 21:03:40 -03:00
Nguyen Thanh Dat
84149b93e3 fix(translator): keep tool_choice "none" instead of turning it into "auto" (#13333)
Claude accepts `tool_choice: "none"` natively, but the translator was rewriting it to `"auto"` in both directions — a caller explicitly forbidding tool use got tools offered anyway. Probe on your head: 36/36 pass across `translator-openai-to-claude` and `translator-claude-to-openai`.

Thanks, @datrixlab — doing both directions in one PR is right; a one-sided fix here would have been worse than none.

**Batch validation** — boarded with the other 10 PRs of your batch into one worktree cut from `release/v3.8.51`; every PR verified as an ancestor of the combined HEAD before validating.

- Focused tests across all 11 PRs: **104/104 pass** on the combined tree.
- Gates on the combined tree: `check-changelog-integrity` PASS, `check-complexity` PASS, `check-cognitive-complexity` PASS, `typecheck:core` PASS, `check:open-sse-typecheck` PASS.
- `check-file-size` is red, but reproduces with byte-identical line counts on the pure `release/v3.8.51` tip (`open-sse/handlers/imageGeneration.ts` 3304, `open-sse/services/combo/roundRobinCombo.ts` 1221, `open-sse/utils/stream.ts` 3115). Inherited base-red, nothing added by this batch — it is also why this PR's "Fast Quality Gates" check was red.
2026-09-16 21:03:25 -03:00
Nguyen Thanh Dat
53e3e23c13 fix(api): stop DISABLE_SQLITE_AUTO_BACKUP from turning off Redis rate limiting (#13329)
A backup flag was being used as a proxy for test mode, so `DISABLE_SQLITE_AUTO_BACKUP` also disabled Redis rate limiting — two unrelated concerns riding one variable. Probe on your head: 3/3 + 13/13 pass across the new test and the existing rate-limiter suite.

Thanks, @datrixlab — catching that the existing rate-limiter tests still pass is what shows this untangled the two without changing the intended behavior of either.

**Batch validation** — boarded with the other 10 PRs of your batch into one worktree cut from `release/v3.8.51`; every PR verified as an ancestor of the combined HEAD before validating.

- Focused tests across all 11 PRs: **104/104 pass** on the combined tree.
- Gates on the combined tree: `check-changelog-integrity` PASS, `check-complexity` PASS, `check-cognitive-complexity` PASS, `typecheck:core` PASS, `check:open-sse-typecheck` PASS.
- `check-file-size` is red, but reproduces with byte-identical line counts on the pure `release/v3.8.51` tip (`open-sse/handlers/imageGeneration.ts` 3304, `open-sse/services/combo/roundRobinCombo.ts` 1221, `open-sse/utils/stream.ts` 3115). Inherited base-red, nothing added by this batch — it is also why this PR's "Fast Quality Gates" check was red.
2026-09-16 21:03:09 -03:00
Nguyen Thanh Dat
f781fd038b fix(cli): let restart honor PORT like serve and dashboard (#13327)
Same fix pattern #7049 already applied to `dashboard`: dropping the Commander default `20128` on `restart --port` lets `opts.port` stay undefined so `runServe()`s `opts.port ?? process.env.PORT ?? "20128"` fallback can actually reach `PORT`. 3 files, +36/-1.

Thanks, @datrixlab — matching the existing precedent instead of inventing a new mechanism made this trivial to review.

**Batch validation** — boarded with the other 10 PRs of your batch into one worktree cut from `release/v3.8.51`; every PR verified as an ancestor of the combined HEAD before validating.

- Focused tests across all 11 PRs: **104/104 pass** on the combined tree.
- Gates on the combined tree: `check-changelog-integrity` PASS, `check-complexity` PASS, `check-cognitive-complexity` PASS, `typecheck:core` PASS, `check:open-sse-typecheck` PASS.
- `check-file-size` is red, but reproduces with byte-identical line counts on the pure `release/v3.8.51` tip (`open-sse/handlers/imageGeneration.ts` 3304, `open-sse/services/combo/roundRobinCombo.ts` 1221, `open-sse/utils/stream.ts` 3115). Inherited base-red, nothing added by this batch — it is also why this PR's "Fast Quality Gates" check was red.
2026-09-16 21:02:51 -03:00
Nguyen Thanh Dat
bb45fb8ede fix(providers): let a dashboard OFF for private provider URLs beat the env opt-in (#13323)
Confirmed on the tip: `src/shared/network/outboundUrlGuardPolicy.ts` only checked `isTrueValue(dbValue)`, so an explicit dashboard OFF fell through to the env opt-in instead of overriding it — the operator turning something off in the UI had no effect. Probe on your head: 7/7 pass.

Thanks, @datrixlab — an explicit OFF in the UI losing to an env var is the kind of thing that erodes trust in the whole settings surface.

**Batch validation** — boarded with the other 10 PRs of your batch into one worktree cut from `release/v3.8.51`; every PR verified as an ancestor of the combined HEAD before validating.

- Focused tests across all 11 PRs: **104/104 pass** on the combined tree.
- Gates on the combined tree: `check-changelog-integrity` PASS, `check-complexity` PASS, `check-cognitive-complexity` PASS, `typecheck:core` PASS, `check:open-sse-typecheck` PASS.
- `check-file-size` is red, but reproduces with byte-identical line counts on the pure `release/v3.8.51` tip (`open-sse/handlers/imageGeneration.ts` 3304, `open-sse/services/combo/roundRobinCombo.ts` 1221, `open-sse/utils/stream.ts` 3115). Inherited base-red, nothing added by this batch — it is also why this PR's "Fast Quality Gates" check was red.
2026-09-16 21:02:35 -03:00
Nguyen Thanh Dat
840ae79612 fix(cli): read commander's negated --no-* flags as opts.<name> === false (#13322)
Commander exposes a negated `--no-x` flag as `opts.x === false`, not as `opts.noX`, so every `--no-*` flag on `chat`, `contexts` and `serve` was being read as undefined and silently ignored. Probe on your head: 5/5 pass in `tests/unit/cli-negated-flags.test.ts` — a real Commander parser with mocked fetch and a temp `DATA_DIR`, exercising all three commands end to end rather than asserting on the parser in isolation.

Thanks, @datrixlab.

**Batch validation** — boarded with the other 10 PRs of your batch into one worktree cut from `release/v3.8.51`; every PR verified as an ancestor of the combined HEAD before validating.

- Focused tests across all 11 PRs: **104/104 pass** on the combined tree.
- Gates on the combined tree: `check-changelog-integrity` PASS, `check-complexity` PASS, `check-cognitive-complexity` PASS, `typecheck:core` PASS, `check:open-sse-typecheck` PASS.
- `check-file-size` is red, but reproduces with byte-identical line counts on the pure `release/v3.8.51` tip (`open-sse/handlers/imageGeneration.ts` 3304, `open-sse/services/combo/roundRobinCombo.ts` 1221, `open-sse/utils/stream.ts` 3115). Inherited base-red, nothing added by this batch — it is also why this PR's "Fast Quality Gates" check was red.
2026-09-16 21:02:19 -03:00
Nguyen Thanh Dat
466d0306be fix(resilience): parse RFC 3339 and fractional-second rate-limit resets (#13321)
Rate-limit reset headers arriving as RFC 3339 or with fractional seconds were not parsed, so the reset hint was silently dropped and the generic cooldown applied instead. Probe on your head: 10/10 pass in `tests/unit/ratelimitmanager-headers-split.test.ts`, including your 3 new RFC3339/fractional/unix-timestamp cases.

Thanks, @datrixlab — covering all three shapes in one pass, rather than only the one you hit, is why this needed no follow-up.

**Batch validation** — boarded with the other 10 PRs of your batch into one worktree cut from `release/v3.8.51`; every PR verified as an ancestor of the combined HEAD before validating.

- Focused tests across all 11 PRs: **104/104 pass** on the combined tree.
- Gates on the combined tree: `check-changelog-integrity` PASS, `check-complexity` PASS, `check-cognitive-complexity` PASS, `typecheck:core` PASS, `check:open-sse-typecheck` PASS.
- `check-file-size` is red, but reproduces with byte-identical line counts on the pure `release/v3.8.51` tip (`open-sse/handlers/imageGeneration.ts` 3304, `open-sse/services/combo/roundRobinCombo.ts` 1221, `open-sse/utils/stream.ts` 3115). Inherited base-red, nothing added by this batch — it is also why this PR's "Fast Quality Gates" check was red.
2026-09-16 21:02:04 -03:00
Nguyen Thanh Dat
3f239717b5 fix(sse): keep a round-robin target's concurrency limit through a cooldown (#13320)
Verified on the tip that `getGate()` unconditionally overwrites `gate.max` (`open-sse/services/rateLimitSemaphore.ts:54-66`), so a target coming out of cooldown lost its configured concurrency limit. Probe on your head: 3/3 pass.

Thanks, @datrixlab.

**Batch validation** — boarded with the other 10 PRs of your batch into one worktree cut from `release/v3.8.51`; every PR verified as an ancestor of the combined HEAD before validating.

- Focused tests across all 11 PRs: **104/104 pass** on the combined tree.
- Gates on the combined tree: `check-changelog-integrity` PASS, `check-complexity` PASS, `check-cognitive-complexity` PASS, `typecheck:core` PASS, `check:open-sse-typecheck` PASS.
- `check-file-size` is red, but reproduces with byte-identical line counts on the pure `release/v3.8.51` tip (`open-sse/handlers/imageGeneration.ts` 3304, `open-sse/services/combo/roundRobinCombo.ts` 1221, `open-sse/utils/stream.ts` 3115). Inherited base-red, nothing added by this batch — it is also why this PR's "Fast Quality Gates" check was red.
2026-09-16 21:01:48 -03:00
Nguyen Thanh Dat
3bcb181cc2 fix(quota): apply the equal-split fallback in the pool usage snapshot (#13159)
The equal-split fallback was applied when computing allocations but not when building the pool usage snapshot, so `sqliteQuotaStore.ts:201` still read `totalWeight > 0 ? alloc.weight : 0` — a zero-weight pool reported every member at 0 instead of its equal share. Probe on your head: 9/9 pass in `tests/unit/quota-pool-usage-equal-split.test.ts`, with the bug confirmed unfixed on the tip.

Thanks, @datrixlab — fixing the snapshot path and not just the allocation path is the part that makes the dashboard numbers agree with the enforcement.

**Batch validation** — boarded with the other 10 PRs of your batch into one worktree cut from `release/v3.8.51`; every PR verified as an ancestor of the combined HEAD before validating.

- Focused tests across all 11 PRs: **104/104 pass** on the combined tree.
- Gates on the combined tree: `check-changelog-integrity` PASS, `check-complexity` PASS, `check-cognitive-complexity` PASS, `typecheck:core` PASS, `check:open-sse-typecheck` PASS.
- `check-file-size` is red, but reproduces with byte-identical line counts on the pure `release/v3.8.51` tip (`open-sse/handlers/imageGeneration.ts` 3304, `open-sse/services/combo/roundRobinCombo.ts` 1221, `open-sse/utils/stream.ts` 3115). Inherited base-red, nothing added by this batch — it is also why this PR's "Fast Quality Gates" check was red.
2026-09-16 21:01:24 -03:00
Dmitry Kuznetsov
a2c6f7188d fix(antigravity): keep unreported quota fraction unknown (#7138)
Merged after batch validation on a combined worktree cut from `release/v3.8.51` with #9944 and #9908.

**Evidence**
- Focused tests: 36/36 pass on the combined tree, including `convertUsageToQuotaInfo skips Antigravity quota entries with an unknown fraction` and the `#6295` regression that guards the same class of bug on another provider.
- Gates on the combined tree: `check-complexity` PASS, `check-cognitive-complexity` PASS, `typecheck:core` PASS, `check-changelog-integrity` PASS.
- The red `check-file-size` reproduces byte-identical on the pure `release/v3.8.51` tip — inherited base-red, not from this PR. The red CI run here dates from 2026-09-15 against an older base.

Thanks, @Ardem2025 — this is the smallest diff of your batch and arguably the one with the widest blast radius avoided. Writing `remainingPercentage: 0` for an unreported fraction made "we don't know" numerically indistinguishable from "fully exhausted" to every downstream consumer of the quota cache; omitting the field so preflight fails open is the correct read of the upstream's silence.
2026-09-16 19:59:58 -03:00
Dmitry Kuznetsov
00860f3278 fix(antigravity): rotate image accounts on explicit quota exhaustion (#9908)
Merged after batch validation on a combined worktree cut from `release/v3.8.51` with #9944 and #7138.

**Evidence**
- Focused tests: 36/36 pass on the combined tree, including your 4 classification-boundary cases in `tests/unit/antigravity-image-credential-retry.test.ts` (Antigravity quota-exhausted `429` rotates; generic `RESOURCE_EXHAUSTED`, ordinary image rate-limit and non-Antigravity `429` stay terminal).
- Gates on the combined tree: `check-complexity` PASS, `check-cognitive-complexity` PASS, `typecheck:core` PASS, `check-changelog-integrity` PASS.
- The red `check-file-size` reproduces byte-identical on the pure `release/v3.8.51` tip — inherited base-red, not from this PR. The red CI run on this PR dates from 2026-09-15 against an older base.

**Related dispositions**
- #8053 is being closed in your favour: it chased the same account-rotation goal across 3 files plus a new `routingInstrumentation.ts`, while its `AbortSignal` half was already superseded on the tip by independent work. This PR does the same job in 31 lines of production code by reusing the existing `classify429` engine.

Thanks, @Ardem2025 — the deliberate narrowness here is the reason this merged and the bigger version didn't. Gating rotation on `provider === "antigravity" && status === 429 && classify429() === "quota_exhausted"` keeps non-idempotent image generation from being retried on ordinary rate limits, and you proved each negative case rather than just the happy path.
2026-09-16 19:59:43 -03:00
Dmitry Kuznetsov
2f0a01d75c fix(oauth): expose manual Codex callback entry (#9944)
Merged after batch validation on a combined worktree cut from `release/v3.8.51` with #9908 and #7138.

**Evidence**
- Focused tests: 36/36 pass on the combined tree (`oauth-modal-codex-lan-ip-8046`, `antigravity-image-credential-retry`, `antigravity-usage-service`, `generic-quota-fetcher`), including the 2 pre-existing anchor tests that assert `codex` stays in `PKCE_CALLBACK_SERVER_PROVIDERS` and that the `localhost:1455` redirect URI is untouched.
- Gates on the combined tree: `check-complexity` PASS, `check-cognitive-complexity` PASS, `typecheck:core` PASS, `check-changelog-integrity` PASS.
- `check-file-size` is red, but reproduces byte-identical on the pure `release/v3.8.51` tip (`imageGeneration.ts`, `roundRobinCombo.ts`, `stream.ts`) — inherited base-red, not from this PR.

**Reconciled**
- `changelog.d/fixes/codex-manual-loopback-action.md` did not start with a markdown bullet, which is the one thing `check-changelog-integrity` failed on. Fixed in your branch (d3dbb5b) so the fragment convention holds; nothing else in your diff was touched.

Thanks for this one, @Ardem2025 — exposing the manual callback entry that already existed in the code instead of adding a new flow is exactly the right shape for the LAN/remote case, and keeping every PKCE/state check untouched made it easy to verify.
2026-09-16 19:59:25 -03:00
Jan Leon
f1e7148c19 fix(routing): preserve reasoning overrides across transports and fallbacks (#13556)
Merged. The failure mode was concrete — a matched reasoning rule dropped on native Responses/Anthropic paths, model-suffix/account defaults, or fallback preparation, and `_omnirouteReasoningRule` leaking upstream as `Unsupported parameter` — and the fix is carried in the request-local credential context through dispatch, refreshed credentials and fallbacks, with forced effort winning over defaults and client-forged markers dropped at ingress. The 11-case integration suite exercises the real routing/translation modules.

Validated as a combined board first (this PR merged with the 4 siblings of the JxnLexn wave on the release tip): eslint with the frozen suppressions, typecheck:core, check:open-sse-typecheck, complexity, cognitive-complexity, changelog-integrity, docs-sync, migration-numbering, provider-consistency and a duplicate-identifier audit all green, plus 77 passing / 0 failing focused node:test cases across the test files the wave touches. The wave's i18n fill (new keys carried to all 66 locales), free-tier doc counts and file-size rebaseline land in one follow-up PR right after the wave, as with #13904.

Thank you — and for keeping this a runtime-only change with the editor and service-tier work in their own PRs.
2026-09-16 17:01:51 -03:00
Jan Leon
a928ea8762 fix(models): reconcile provider dashboards with active live catalogs (#13434)
Merged. The NVIDIA 116-vs-82 discrepancy is the visible symptom; the fix is the right one — reuse the existing `liveCatalogAuthoritative` policy on the dashboard listing instead of a provider-specific filter, refresh after a removals-only import, keep the last confirmed snapshot on a failed refresh, and apply the same membership rule to the OpenRouter/compatible/passthrough row builders so static fallbacks cannot resurrect retired rows. Operator custom models and overrides preserved.

Validated as a combined board first (this PR merged with the 4 siblings of the JxnLexn wave on the release tip): eslint with the frozen suppressions, typecheck:core, check:open-sse-typecheck, complexity, cognitive-complexity, changelog-integrity, docs-sync, migration-numbering, provider-consistency and a duplicate-identifier audit all green, plus 77 passing / 0 failing focused node:test cases across the test files the wave touches. The wave's i18n fill (new keys carried to all 66 locales), free-tier doc counts and file-size rebaseline land in one follow-up PR right after the wave, as with #13904.

Thank you — checking the projection against 14 production catalog snapshots is the kind of evidence that makes a listing change safe to land.
2026-09-16 16:56:28 -03:00
Jan Leon
bd8a12f304 fix(providers): fetch live Qwen and Alibaba Token Plan catalogs (#13299)
Merged. Both Token Plan providers had no `modelsUrl` and no discovery config, so Sync Models never even tried a live request. Fetching the public Personal Plan catalog through `safeOutboundFetch` with fixed hosts, no inference keys and no cookies, validating the gateway envelope and reusing the DashScope text-model classifier keeps this narrow and safe; a failed or media-only result falls back without touching the previous catalog.

Validated as a combined board first (this PR merged with the 4 siblings of the JxnLexn wave on the release tip): eslint with the frozen suppressions, typecheck:core, check:open-sse-typecheck, complexity, cognitive-complexity, changelog-integrity, docs-sync, migration-numbering, provider-consistency and a duplicate-identifier audit all green, plus 77 passing / 0 failing focused node:test cases across the test files the wave touches. The wave's i18n fill (new keys carried to all 66 locales), free-tier doc counts and file-size rebaseline land in one follow-up PR right after the wave, as with #13904.

Thank you.
2026-09-16 16:50:42 -03:00
Diego Rodrigues de Sa e Souza
af2002a493 chore: reconcile the JxnLexn merge wave with the release tip (#13921)
Lands the combined-board reconciliation of today's JxnLexn wave as one follow-up: i18n fill for #12471/#13555's new keys (real vi translations), free-tier count 446→452, file-size rebaseline for #13556. check-new-key-coverage PASS; only the three pre-existing file-size reds remain.
2026-09-16 16:43:44 -03:00
Jan Leon
a16705344e feat(dashboard): add a dedicated API-key routing editor (#13555)
Merged. Moving rule editing out of the permissions modal into `/dashboard/api-manager/routing` fixes the real problem (a cramped modal for something with conditions, effects and three target kinds), and the contract tests pin what matters: persisted fields round-trip, combo names match without rewriting off-catalog IDs, failed saves stay editable, unsaved drafts survive key switches, and writes are disabled after a failed load. Rule evaluation and authorization are untouched.

Validated as a combined board first (this PR merged with the 4 siblings of the JxnLexn wave on the release tip): eslint with the frozen suppressions, typecheck:core, check:open-sse-typecheck, complexity, cognitive-complexity, changelog-integrity, docs-sync, migration-numbering, provider-consistency and a duplicate-identifier audit all green, plus 77 passing / 0 failing focused node:test cases across the test files the wave touches. The wave's i18n fill (new keys carried to all 66 locales), free-tier doc counts and file-size rebaseline land in one follow-up PR right after the wave, as with #13904.

Thank you.
2026-09-16 16:38:04 -03:00
Jan Leon
502e614850 fix(vertex): discover and route partner models correctly (#12471)
Merged. Root cause first: `publishers.models.list` was called with `pageSize=1000` against Google's hard maximum of 300, so every publisher answered 400 and discovery silently fell back to the stale static catalog. On top of that the PR separates API-key from Service-Account capabilities correctly (keys cannot list Model Garden — project-scoped curated catalog; SA tokens can — live catalog), stops treating the expected generativelanguage rejection of a Service Account as a discovery failure, replaces the speculative partner IDs with documented MaaS IDs, and rejects OAuth client-config JSON with a clear message instead of a misleading one.

The 5 ESLint errors flagged during the earlier fix sweep were fixed in your own follow-up commits; the branch was reconciled with the release tip and the 42 locale files were checked for lost keys before this merge.

Validated as a combined board first (this PR merged with the 4 siblings of the JxnLexn wave on the release tip): eslint with the frozen suppressions, typecheck:core, check:open-sse-typecheck, complexity, cognitive-complexity, changelog-integrity, docs-sync, migration-numbering, provider-consistency and a duplicate-identifier audit all green, plus 77 passing / 0 failing focused node:test cases across the test files the wave touches. The wave's i18n fill (new keys carried to all 66 locales), free-tier doc counts and file-size rebaseline land in one follow-up PR right after the wave, as with #13904.

Thank you — the credential-capability distinction and the retired/non-chat filtering are what make the Vertex listing trustworthy.
2026-09-16 16:37:41 -03:00
Patryk Kopyciński
c1ed830eb9 chore(lint): prune 3 obsolete eslint suppressions (#13359)
Merged. Your later commits (re-prune on the current base, then restoring the live `react-hooks/immutability` suppressions the first prune had wrongly dropped) were the right call, and they are carried intact.

Maintainer note: a repo-wide `eslint` run on today's release tip (the full validation for a suppressions prune) found **0 code errors** and only more entries that had gone stale since your base — the maintainer branch carries that same prune to the current tip on top of your commits. Removals only, nothing added.

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

Thank you for chasing the false-positive prune to the end instead of leaving the live suppressions out.
2026-09-16 15:56:36 -03:00
Markus Hartung
1cf8e4bcc6 fix(db): auto-clean terminal batch checkpoints and expired file content (#12999)
Merged after a maintainer rework that kept every one of @hartmark's commits intact.

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

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

Thank you — the cleanup itself is exactly the kind of maintenance that stops a data dir from growing forever.
2026-09-16 15:45:34 -03:00
Patryk Kopyciński
c4293e28e2 fix(cursor): recover Kimi tool calls emitted as history narration (#12723)
Merged. Kimi emitting tool calls as history narration is a provider quirk we have to absorb rather than pass through; recovering them keeps the tool contract intact for clients that never see the quirk.

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

Thank you.
2026-09-16 15:34:54 -03:00
Patryk Kopyciński
f653a6f94e fix(dispatch): strip every _omniroute* internal marker before upstream send (#13355)
Merged. Internal `_omniroute*` markers must never reach an upstream: at best they are noise in someone else's logs, at worst they change the upstream's parse. Stripping every one of them before the send is the right invariant.

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

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

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

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

Thank you — gateway-resolved, per-turn pinned effort is a real feature, and the header contract makes it usable from every harness.
2026-09-16 15:18:36 -03:00
Patryk Kopyciński
421d1ff912 fix(devin): fall back to CLI probe when the HTTP API rejects a CLI-format key (#13617)
Merged after a maintainer rework that kept every one of @patrykkopycinski's commits intact, including the changelog fragment you added afterwards.

**What the rework added:** the `eslint-suppressions.json` diff was corrected (the PR had dropped live entries) and a test now proves the CLI-probe fallback path is actually taken when the HTTP API rejects a CLI-format key — before, the fallback existed but nothing exercised it.

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

Thank you.
2026-09-16 15:15:16 -03:00
Markus Hartung
88d5e0cde6 fix(db): gate the auto-cleanup VACUUM on reclaimable space, not row count (#13079)
Merged after a maintainer rework that kept every one of @hartmark's commits intact.

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

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

Thank you — gating VACUUM on reclaimable pages instead of row count is the right signal.
2026-09-16 15:09:07 -03:00
Diego Rodrigues de Sa e Souza
86b8bbfd3e fix(tests): bump the APIKEY_PROVIDERS tripwire to 241 (Agnes AI China) (#13905)
`APIKEY_PROVIDERS merges the 6 family files into 240 entries` fails on the
release tip, taking a unit-test shard red on every open PR.

Agnes AI China (#13399, cdcde97c7) added one `apikey/regional` entry, so the
real count is 241 — confirmed at runtime: `Object.keys(APIKEY_PROVIDERS).length`
is 241 and includes `agnes-cn`.

The hardcoded number is deliberate, not an oversight: the test is a tripwire for
silent loss or duplication across the six family files, and each bump is
documented in the header with the provider and the PR that caused it. Kept that
convention rather than deriving the count at runtime, which would make the test
assert nothing.
2026-09-16 13:48:13 -03:00
Patryk Kopyciński
63070bec32 fix(semantic-cache): never cache a truncated completion (#7) (#12885)
Merged. Caching a truncated completion poisons the entry for every later exact-match read — and the analysis in the description is right that the exact-zero cache-read gate is what made it reachable. Refusing the write on both store paths, while keeping `stop`, `tool_calls` and unknown/missing reasons cacheable, is the narrow version of this fix.

Maintainer note: the PR was opened against `main` and its branch had drifted far enough that GitHub reported 1289 changed files. Your single commit was rebased onto the active release tip with authorship intact (nothing else carried over), the PR was retargeted to `release/v3.8.51`, and `tests/unit/semantic-cache-no-truncated-writes.test.ts` re-run there: 3 pass / 0 fail.

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

Thank you.
2026-09-16 13:46:04 -03:00
Diego Rodrigues de Sa e Souza
68b9fc21a5 chore: reconcile the 2026-09-16 merge wave with the release tip (#13904)
Lands the combined-board reconciliation of today's 22-PR wave as one follow-up: i18n fill for #13115's two new keys (65 locales, real translation in vi) and the file-size rebaseline for cursor.ts (#13627) and chatHelpers.ts (#13879). check-new-key-coverage PASS; only the three pre-existing file-size reds remain on the tip.
2026-09-16 13:42:04 -03:00
Patryk Kopyciński
9250a231c5 test(adobe-firefly): lock in the browser-spawn guard with a regression test (#13358)
Merged. Test-only, and the right kind: a browser-spawn guard is precisely the thing that gets removed by accident during a refactor, and nothing was holding it in place until now.

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

Thank you.
2026-09-16 13:39:45 -03:00
Markus Hartung
76b037c2e7 fix(combo): stop retrying an unrecoverable request-shape error across the whole fallback chain (#13338)
Merged. Retrying an unrecoverable request-shape error across the whole fallback chain burns every target on an error that will never change — the caller waits longer for the same failure. Stopping at the first one is right.

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

Thank you.
2026-09-16 13:39:29 -03:00
Patryk Kopyciński
b269821c83 fix(cursor): kv_after_text must not settle away a trailing exec_mcp tool call (#13627)
Merged. Settling on `kv_after_text` while a trailing `exec_mcp` call is still pending drops the tool call entirely — the client then sees a finished turn that never ran the tool. Correct place to fix it.

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

Thank you.
2026-09-16 13:39:14 -03:00
Markus Hartung
3df5d50619 fix(gemini): never send a functionCall-opening turn with no preceding user turn (#13573)
Merged. Gemini rejecting a turn that opens with a `functionCall` and no preceding user turn is a contract we have to honour on our side; building the turn correctly is cheaper than reading the upstream 400 that comes back.

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

Thank you.
2026-09-16 13:38:56 -03:00
Markus Hartung
c099892ac5 fix(embeddings): log server-side when a provider can't be resolved (#13687)
Merged. A provider that cannot be resolved should never be a silent no-op on the server side — the operator is the only one who can act on it, and until now only the caller saw anything.

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

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

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

Thank you.
2026-09-16 13:38:23 -03:00
Patryk Kopyciński
9cc077ef55 fix(providers): resolve unsupportedParams via model aliases so K3 stops 400ing on temperature (#13037)
Merged. Resolving `unsupportedParams` through the model aliases is the fix that generalises — K3 stops 400ing on `temperature`, and any other alias of a model with the same restriction is covered by construction instead of by a second patch later.

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

Thank you.
2026-09-16 13:37:57 -03:00
Markus Hartung
4591476141 fix(providers): lazily load chatgpt-web-codex admin helpers in PUT /api/providers/[id] (#13071)
Merged. Loading the chatgpt-web-codex admin helpers lazily keeps a rarely used path off `PUT /api/providers/[id]`'s cost — the kind of change that only shows up as latency nobody can explain.

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

Thank you.
2026-09-16 13:37:39 -03:00
Markus Hartung
ac32cea4f0 fix(logs): recover concatenated JSON objects in the Provider Event Stream viewer (#13115)
Merged. Concatenated JSON objects in the Provider Event Stream are a real wire shape, not a corruption — recovering them in the viewer instead of dropping the batch makes the tool trustworthy when it matters.

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

Thank you.
2026-09-16 13:37:23 -03:00
Sahil Daswani
ff493da805 fix: match compatible-provider models owned by public prefix (#13831)
* fix: match compatible-provider models owned by public prefix

Resolves #13829

The provider-scoped /v1/providers/{provider}/models route filters
unified-catalog rows by internal provider ID. For compatible provider
nodes, the catalog emits the configured public prefix in owned_by, so
all valid models were dropped and the endpoint returned an empty list.

Resolve the compatible node's prefix and accept it alongside the
internal ID when filtering and when stripping the prefix from returned
model ids.

* chore(quality): satisfy the format and lint-suppression gates for #13829

Two gate-only touch-ups on top of the fix, no behaviour change:

- prettier --check rejected tests/unit/provider-models-v1-route.test.ts over a
  double blank line before a test block.
- typing the map callback removed the file's only `any`, which left the frozen
  entry in config/quality/eslint-suppressions.json unused; the lint gate fails
  on a stale suppression, so it is pruned.

Both were found by running the gates locally, because this fork PR's workflow
runs are still awaiting maintainer approval and only the semgrep check had run.

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

---------

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
Co-authored-by: sahildaswani <sahildaswani@users.noreply.github.com>
2026-09-16 13:37:11 -03:00
Markus Hartung
bc36b1d1aa fix(i18n): stop swallowed FORMATTING_ERROR from showing raw keys/garbled text (#12995)
Merged. A swallowed `FORMATTING_ERROR` surfacing as raw keys or garbled text is exactly the failure mode i18n is supposed to prevent; the user sees the plumbing. Good catch.

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

Thank you.
2026-09-16 13:37:07 -03:00
Markus Hartung
7dbe850daa fix(providers): honor operator-set endpoint overrides for local models (#13078)
Merged. An operator-set endpoint override that is ignored for local models is the worst kind of setting — it looks applied and is not. Honouring it is the whole fix.

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

Thank you.
2026-09-16 13:36:52 -03:00
lorenzozane
5847c43922 fix(resilience): name collision keys, surface header drops, retry embeds, skip far-reset pings (#13766)
Merged. Four small, independently justified changes, each with its own regression test — naming both sides of a case-insensitive key collision, surfacing the dropped-header count to the caller instead of only to the log, one retry before a memory is left unvectorized, and skipping warm pings for a window whose reset is more than 24h out. The first-seen-wins resolution and the caller-visible behaviour of everything else are unchanged.

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

Thank you for keeping each of the four minimal and commented — that is what made this reviewable as one PR.
2026-09-16 13:36:35 -03:00
lorenzozane
c8d102179d fix(docker): smoke-test images before promoting latest (#13761)
Merged. The ordering is what makes this fix real: the version manifests are published first, the smoke test runs against the published image, and only then are the floating `latest` tags promoted — so a broken build can no longer become `latest`. Both Dockerfiles carry a `HEALTHCHECK`, and the 30×2s window covers the 15s `start-period`.

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

Thanks for closing #11835 properly instead of just adding a check after the fact.
2026-09-16 13:36:18 -03:00
lorenzozane
20c6d89c2d fix(admission): measure request bodies with the active cost budget (#13762)
Merged. Good catch: the feature extractor was silently capped by the size estimator's 256 KiB default, so every request above it reported the same flattened body/token estimate and admission decided on a number that was not the request. Deriving the measurement limit from the active cost budget — runtime overrides included — is the right place to fix it.

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

Thank you.
2026-09-16 13:36:08 -03:00
lorenzozane
9ce43850b7 fix(compression): preserve case, tags and negations through llmlingua engine (#13768)
Merged. Shielding XML tags and negation/absolute words from an uncased backend, restoring source casing and edge whitespace on re-stitch, and failing open on an empty reply are each the difference between "compressed" and "changed the meaning". The 6 fidelity tests make the negation list something a future reviewer can argue with in code instead of in the abstract.

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

Thank you.
2026-09-16 13:36:02 -03:00
Diego Rodrigues de Sa e Souza
80828fc88a fix(sse): say when an API key's allowlist is what hid every connection (#13879)
#13832. A user reported that `nvidia` and `openrouter` — added after the
initial setup — always failed chat with `No active credentials for provider: X`,
while on the same instance and the same minute `/api/providers/{id}/test`
returned valid and `/sync-models` pulled 82 models.

Reproducing the resolution chain on the tip shows no defect in it: a connection
created exactly as `POST /api/providers` creates one resolves for every model
tried, and creation order is irrelevant — the query is `provider = ? AND
is_active = 1`, there is no boot-time registry and no migration that backfills
only older rows.

The three-line AUTH log the reporter pasted is reachable from exactly one place:
the pool arriving EMPTY at the key-policy filter. Every post-query skip produces
a different message ("all N accounts unavailable"). So the connections exist and
are active; the calling key's `allowed_connections` / quota scope removed them —
the shape you get from a key minted before those providers existed, which is
also why the older providers on that key keep working.

The real defect is that nothing ever said so. `/test` and `/sync-models` address
a connection by id and never consult the key's scope, so they cannot contradict
it, and the one log line that hinted at the filter became `debug` in #11937.

`getProviderCredentials` now counts the connections it had before applying the
key policy and, when that filter is what emptied the pool, returns
`{ blockedByKeyPolicy, blockedCount }` instead of a bare null. `handleNoCredentials`
turns it into a 403 naming the allowlist and the fix, alongside the existing
allRateLimited/allExpired branches. 403, not 401: the credential is valid, this
principal just may not use it.

Test is red-first in tests/unit/chat-helpers.test.ts (it asserts the status, the
count and that the message names the gate).

This does not close the report on its own — it makes the next occurrence
self-explanatory. The reporter still needs to confirm their key's
allowed_connections/allowed_quotas.
2026-09-16 13:35:49 -03:00
Diego Rodrigues de Sa e Souza
ba274b616a fix(providers): validate Zylo keys against the chat route, not its open catalog (#13877)
#13828. `zylo-api` is registered as OpenAI-compatible, so the generic probe
validated a key with `GET /v1/models` and returned on the first 2xx. Zylo serves
that route WITHOUT authentication — it answers 200 with no Authorization header
at all, and 200 for a bogus key — so the account-setup dialog greened any
string. The first request Zylo actually authenticates is the user's own model
test, which comes back `401 {"error":"Key not found: zk-…"}`.

Running the production validator against a fake key returned `{valid:true}`
before this change.

Two corrections to the report: nothing passes a key value where a key name is
expected — there is no such lookup — and that 401 text is Zylo's own, not
OmniRoute's. The defect is a false-green validation, which is worse: an invalid
key is stored as working and only fails later, at the model level.

`POST /v1/chat/completions` is authenticated, so a single probe there is the
correct auth check — the remedy already applied to dify (#11002) and bytez
(#5422). Registered under both `zylo-api` and the `zylo` alias, matching the
adobe-firefly/firefly pair, so a connection stored under the alias does not fall
back to the open-catalog probe.

Tests are red-first: a key the chat route rejects must not validate, the catalog
route must not be consulted at all, a key it accepts still validates, and the
alias takes the same path. The first and third failed before the fix.

Not in scope, reported separately: Zylo's catalog is not OpenAI-shaped
(`{text:[…],image:[…]}`), so model sync yields 0 models.
2026-09-16 13:35:33 -03:00
lorenzozane
309b635740 fix(providers): bypass web-search fallback for Antigravity target (#13447) (#13765)
Merged. The repro in the description is exactly right — rewriting a native `web_search` tool into `omniroute_web_search` for a client that never declared it makes streaming Responses clients disconnect. Mirroring the existing Gemini bypass keeps the change to the one target that needs it, and the suite goes from 20 pass / 3 fail on base to 23 / 0 with the fix.

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

Thank you.
2026-09-16 13:29:05 -03:00
lorenzozane
515bf8ce3b test(embeddings): cover custom embeddings authorization (#13763)
Merged. This is not covered by the existing `embeddings-auth.test.ts`, which asserts the *inbound* auth; yours records the *outbound* contract — the key configured on a custom OpenAI-compatible connection must leave as `Authorization: Bearer <key>`, with the mock returning 401 when it does not. That contract had no guard until now.

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

Thank you.
2026-09-16 13:21:48 -03:00
lorenzozane
1510fb0d62 test(combo): gate-level regression for stale persisted-cooldown skip (#13694) (#13767)
Merged. Test-only and worth it: the production mechanism is already fixed on this base (#12168 grace-bounded the persisted-`unavailable` skip, #12899 fixed combo-name allow-listing), but nothing stopped it from regressing. Your cases pin both directions — a stale/orphan persisted cooldown proceeds through the gates, a genuinely fresh one still skips.

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

Thank you.
2026-09-16 13:14:37 -03:00
lorenzozane
ed2e22197a fix(providers): add missing modelsUrl for qwen-cloud-token-plan (#13764)
Merged. One missing field, one very concrete symptom: without `modelsUrl` the import silently fell back to the local catalog with "API unavailable". The test asserts both the registry entry and the derivation, so a future registry edit cannot drop it again.

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

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

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

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

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

Thank you for the depth of this one — the resource-bounds suite and the sql.js startup/backup coverage are the kind of tests that keep a database layer honest.
2026-09-16 12:59:45 -03:00
Diego Rodrigues de Sa e Souza
9d9284417c fix(cli): translate the CLI for every locale (38 catalogs were nearly empty) (#13892)
sync-ui-keys --catalog=cli + blocking i18n:check-keys:cli gate; 65 CLI catalogs synced (52,000 strings, 0 __MISSING__, all 830 keys, placeholders verified). ⚠️ base-red inherited: #12732
2026-09-16 12:27:59 -03:00
Diego Rodrigues de Sa e Souza
9e36dde2f7 feat(i18n): retranslate-site rewrites the verbatim-English leaves of the site catalogs (#13886)
scripts/i18n/retranslate-site.mjs + untranslatable-site-keys.json (22 keys) + tests; the run landed on the site as OmniRouteSite#8 (2,059 leaves, mean English residue 10.3 % → 6.3 %). ⚠️ base-red inherited: #12732
2026-09-16 12:19:50 -03:00
Diego Rodrigues de Sa e Souza
060f331264 fix(i18n): pt-BR review pass over the retranslated leaves (172 corrections) + review-locale script (#13885)
Reviewer pass (native-speaker prompt) over the 1,865 pt-BR leaves retranslated in #13782: 172 corrections applied; new scripts/i18n/review-locale.mjs with tests. ⚠️ base-red inherited: #12732
2026-09-16 12:19:40 -03:00
Diego Rodrigues de Sa e Souza
7f496c79d0 fix(ci): drain three base reds blocking every PR — generated SKILL.md, stryker registry, env/docs contract (#13834)
* docs(skills): regenerate omni-settings SKILL.md for the egress-observation endpoint

* fix(ci): register provider-401-ambiguous-runtime test in stryker tap.testFiles

* fix(ci): drain current base drift — regenerate cli-mcp SKILL.md, register 4 stryker tests

* fix(ci): document OMNIROUTE_CLOUD_SYNC_ENFORCE_SIGNATURE and CDP_PROXY_TOKEN (env/docs contract)
2026-09-16 12:00:53 -03:00
Diego Rodrigues de Sa e Souza
1cb4c82071 fix(ci): bound the forgotten-sibling report so an advisory step stops failing the job (#13889)
"Fast Quality Gates" is red on every open PR against release/v3.8.51. The failing
step is `forgotten-sibling-tests`, which is explicitly advisory — its own output
says "Report-only calibration: these findings do not fail the job" — yet it exits 1.

When a PR diff touches a hub module (`open-sse/config/providerRegistry.ts` in the
current reds), the analysis walks every import edge in the repo and multiplies each
consumer by its candidate tests. The result reaches millions of rows, and
`lines.join("\n")` then exceeds V8's maximum string length. The throw lands in
main()'s catch, which exits 1 — so an advisory report takes the whole job down.

Measured with a synthetic hub cross-product, before the change:

  3,000,000 findings -> a 435 MB report string (no throw, but absurd)
  4,500,000 findings -> Invalid string length   (the CI failure, verbatim)

After: the same 4,500,000 findings render as 27 KB.

The fix bounds only the ENUMERATION. The header keeps the exact totals, so the
signal ("this diff has N unreviewed sibling tests") is unchanged; at most 200 rows
per section are listed, followed by a line naming how many were withheld. The JSON
artifact gets the same treatment (5,000 items per array) plus an explicit `totals`
object, since `JSON.stringify` would throw on the same input for the same reason.

`markdown()` is exported so the bound is testable without a CI-sized diff.
2026-09-16 11:45:40 -03:00
Diego Rodrigues de Sa e Souza
5faf44f975 fix(docs): restore the env/docs contract broken by the #13679 vars (#13875)
`check:env-doc-sync` is failing on the release tip, which fails "Docs Gates
(fast-path)" on every open PR against release/v3.8.51 (base-red #13866).

Both gaps come from #13679:

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

Docs only, no code change.

Verified on this branch: check:env-doc-sync reports all three directions in sync
(817 vars in .env.example, 834 in ENVIRONMENT.md); check:docs-sync passes;
check:docs-counts reports only pre-existing soft drift.
2026-09-16 10:40:27 -03:00
Diego Rodrigues de Sa e Souza
ec98525326 fix(tests): list dist/httpClientAbortGuard.mjs in the pack-artifact policy assertion (#13872)
Merged on local evidence: the assertion is red on a pristine release/v3.8.51 (19 pass / 1 fail) and green here (20/0). One-line test fix; the packaging policy itself is untouched.
2026-09-16 10:10:32 -03:00
Bob.Hou
0cc0169360 fix(db): give conversation_turn_nodes its own 1-day retention (#13344) 2026-09-16 08:23:17 -03:00
Bob.Hou
b4f51b2e9e fix(providers): restore grok-4.6/4.5 default reasoning effort (#13628) 2026-09-16 08:15:21 -03:00
Bob.Hou
54f19c7742 feat(providers): fetch live xAI catalog for xai-oauth (#13518) 2026-09-16 08:08:49 -03:00
Bob.Hou
cdcde97c70 feat(providers): add Agnes AI (China) as agnes-cn on api.agnes-ai.cn (#13399) 2026-09-16 08:01:39 -03:00
Diego Rodrigues de Sa e Souza
19b9d05e08 fix(providers): xAI translators drop legacy function_call and zero total_tokens (#12692, #12700) (#13753)
Merged in the 2026-09-16 sweep of the maintainer's own open PRs, at the owner's explicit instruction. No push was made to the PR branch: the merge took the head as the owning session left it (verified OPEN, non-draft and MERGEABLE against the release tip immediately before merging).
2026-09-16 06:18:37 -03:00
Diego Rodrigues de Sa e Souza
47032e7769 fix(providers): correct Magnific key validation probe path (#12927) (#13754)
Merged in the 2026-09-16 sweep of the maintainer's own open PRs, at the owner's explicit instruction. No push was made to the PR branch: the merge took the head as the owning session left it (verified OPEN, non-draft and MERGEABLE against the release tip immediately before merging).
2026-09-16 06:18:22 -03:00
Diego Rodrigues de Sa e Souza
8939ccbae3 fix(sse): dynamic-specifier require for wreq-js in Codex WS transport (#12491) (#13756)
Merged in the 2026-09-16 sweep of the maintainer's own open PRs, at the owner's explicit instruction. No push was made to the PR branch: the merge took the head as the owning session left it (verified OPEN, non-draft and MERGEABLE against the release tip immediately before merging).
2026-09-16 06:17:57 -03:00
Diego Rodrigues de Sa e Souza
5ff85c6db6 fix(oauth): fall back to public Code Suggestions on any GitLab Duo direct_access 403 (#12958) (#13758)
Merged in the 2026-09-16 sweep of the maintainer's own open PRs, at the owner's explicit instruction. No push was made to the PR branch: the merge took the head as the owning session left it (verified OPEN, non-draft and MERGEABLE against the release tip immediately before merging).
2026-09-16 06:17:42 -03:00
Diego Rodrigues de Sa e Souza
8f6205e36a fix(providers): backfill combo context limit from snapshot after cold start (#13000) (#13759)
Merged in the 2026-09-16 sweep of the maintainer's own open PRs, at the owner's explicit instruction. No push was made to the PR branch: the merge took the head as the owning session left it (verified OPEN, non-draft and MERGEABLE against the release tip immediately before merging).
2026-09-16 06:17:27 -03:00
Diego Rodrigues de Sa e Souza
694c1b74eb fix(cli): add POST /api/mcp/restart and mcp enable/disable subcommands (#13012) (#13770)
Merged in the 2026-09-16 sweep of the maintainer's own open PRs, at the owner's explicit instruction. No push was made to the PR branch: the merge took the head as the owning session left it (verified OPEN, non-draft and MERGEABLE against the release tip immediately before merging).
2026-09-16 06:17:04 -03:00
Diego Rodrigues de Sa e Souza
9578eeb380 fix(sse): widen max_tokens/tool_use exemption to lone empty text blocks (#12968) (#13771)
Merged in the 2026-09-16 sweep of the maintainer's own open PRs, at the owner's explicit instruction. No push was made to the PR branch: the merge took the head as the owning session left it (verified OPEN, non-draft and MERGEABLE against the release tip immediately before merging).
2026-09-16 06:16:49 -03:00
Diego Rodrigues de Sa e Souza
da58ce590f fix(skills): repair nested malformed schemas in injected skill tools (#13022) (#13772)
Merged in the 2026-09-16 sweep of the maintainer's own open PRs, at the owner's explicit instruction. No push was made to the PR branch: the merge took the head as the owning session left it (verified OPEN, non-draft and MERGEABLE against the release tip immediately before merging).
2026-09-16 06:16:34 -03:00
Diego Rodrigues de Sa e Souza
9342e4bf68 fix(sse): accept Responses API custom tool_choice (#13122) (#13775)
Merged in the 2026-09-16 sweep of the maintainer's own open PRs, at the owner's explicit instruction. No push was made to the PR branch: the merge took the head as the owning session left it (verified OPEN, non-draft and MERGEABLE against the release tip immediately before merging).
2026-09-16 06:16:18 -03:00
Diego Rodrigues de Sa e Souza
aeba6b1a04 fix(routing): publish dashboard events from the round-robin combo loop (#13089) (#13776)
Merged in the 2026-09-16 sweep of the maintainer's own open PRs, at the owner's explicit instruction. No push was made to the PR branch: the merge took the head as the owning session left it (verified OPEN, non-draft and MERGEABLE against the release tip immediately before merging).
2026-09-16 06:16:02 -03:00
Diego Rodrigues de Sa e Souza
0dbd7f47d2 fix(sse): classify missing Chromium as a Z.ai host/config cooldown (#13232) (#13777)
Merged in the 2026-09-16 sweep of the maintainer's own open PRs, at the owner's explicit instruction. No push was made to the PR branch: the merge took the head as the owning session left it (verified OPEN, non-draft and MERGEABLE against the release tip immediately before merging).
2026-09-16 06:15:47 -03:00
Diego Rodrigues de Sa e Souza
74e44d7630 fix(db): defer process.exit(0) by a macrotask on graceful shutdown (#13306) (#13778)
Merged in the 2026-09-16 sweep of the maintainer's own open PRs, at the owner's explicit instruction. No push was made to the PR branch: the merge took the head as the owning session left it (verified OPEN, non-draft and MERGEABLE against the release tip immediately before merging).
2026-09-16 06:15:31 -03:00
Diego Rodrigues de Sa e Souza
0459c6dc0a fix(cli): surface fatal [STARTUP] boot diagnostics without --log (#13314) (#13779)
Merged in the 2026-09-16 sweep of the maintainer's own open PRs, at the owner's explicit instruction. No push was made to the PR branch: the merge took the head as the owning session left it (verified OPEN, non-draft and MERGEABLE against the release tip immediately before merging).
2026-09-16 06:15:15 -03:00
Diego Rodrigues de Sa e Souza
94220af289 fix(providers): stop zed-hosted claude-haiku-4-5 thinking from inflating max_tokens (#13364) (#13780)
Merged in the 2026-09-16 sweep of the maintainer's own open PRs, at the owner's explicit instruction. No push was made to the PR branch: the merge took the head as the owning session left it (verified OPEN, non-draft and MERGEABLE against the release tip immediately before merging).
2026-09-16 06:15:00 -03:00
Diego Rodrigues de Sa e Souza
05b44fa48e fix(db): stop backoff-reset from busting the model catalog cache (#13389) (#13783)
Merged in the 2026-09-16 sweep of the maintainer's own open PRs, at the owner's explicit instruction. No push was made to the PR branch: the merge took the head as the owning session left it (verified OPEN, non-draft and MERGEABLE against the release tip immediately before merging).
2026-09-16 06:14:46 -03:00
Diego Rodrigues de Sa e Souza
f5501cf9a3 fix(providers): gemini-web no longer drops system instructions or the tool contract (#13380) (#13784)
Merged in the 2026-09-16 sweep of the maintainer's own open PRs, at the owner's explicit instruction. No push was made to the PR branch: the merge took the head as the owning session left it (verified OPEN, non-draft and MERGEABLE against the release tip immediately before merging).
2026-09-16 06:14:31 -03:00
Diego Rodrigues de Sa e Souza
57f41f5dd1 fix(sse): frame post-keepalive /v1/responses stream errors with a type field (#13431) (#13785)
Merged in the 2026-09-16 sweep of the maintainer's own open PRs, at the owner's explicit instruction. No push was made to the PR branch: the merge took the head as the owning session left it (verified OPEN, non-draft and MERGEABLE against the release tip immediately before merging).
2026-09-16 06:14:14 -03:00
Diego Rodrigues de Sa e Souza
d2fadb01bc fix(db): reconcile INCREMENTAL auto_vacuum drift via the vacuum scheduler (#13432) (#13786)
Merged in the 2026-09-16 sweep of the maintainer's own open PRs, at the owner's explicit instruction. No push was made to the PR branch: the merge took the head as the owning session left it (verified OPEN, non-draft and MERGEABLE against the release tip immediately before merging).
2026-09-16 06:13:59 -03:00
Diego Rodrigues de Sa e Souza
842c32f6f0 fix(compression): keep tool messages through lite redundant-remove (#13429) (#13787)
Merged in the 2026-09-16 sweep of the maintainer's own open PRs, at the owner's explicit instruction. No push was made to the PR branch: the merge took the head as the owning session left it (verified OPEN, non-draft and MERGEABLE against the release tip immediately before merging).
2026-09-16 06:13:44 -03:00
Diego Rodrigues de Sa e Souza
ad4d286c85 fix(sse): forward cache-creation tokens through the responses usage hop (#13472) (#13790)
Merged in the 2026-09-16 sweep of the maintainer's own open PRs, at the owner's explicit instruction. No push was made to the PR branch: the merge took the head as the owning session left it (verified OPEN, non-draft and MERGEABLE against the release tip immediately before merging).
2026-09-16 06:13:29 -03:00
Diego Rodrigues de Sa e Souza
edeb76b96f fix(sse): stop PII sanitizer splicing OpenRouter metadata into content (#13488) (#13792)
Merged in the 2026-09-16 sweep of the maintainer's own open PRs, at the owner's explicit instruction. No push was made to the PR branch: the merge took the head as the owning session left it (verified OPEN, non-draft and MERGEABLE against the release tip immediately before merging).
2026-09-16 06:13:14 -03:00
Diego Rodrigues de Sa e Souza
5d1f4687eb fix(sse): fail closed on background token-refresh for dead proxy pools (#13470) (#13793)
Merged in the 2026-09-16 sweep of the maintainer's own open PRs, at the owner's explicit instruction. No push was made to the PR branch: the merge took the head as the owning session left it (verified OPEN, non-draft and MERGEABLE against the release tip immediately before merging).
2026-09-16 06:12:59 -03:00
Diego Rodrigues de Sa e Souza
2e2dff79f2 fix(cli): redraw Windows tray icon with dark outline + ship icon.ico (#13535) (#13797)
Merged in the 2026-09-16 sweep of the maintainer's own open PRs, at the owner's explicit instruction. No push was made to the PR branch: the merge took the head as the owning session left it (verified OPEN, non-draft and MERGEABLE against the release tip immediately before merging).
2026-09-16 06:12:43 -03:00
Diego Rodrigues de Sa e Souza
2bbe6e575b fix(sse): stop unhydrated compatible connections routing to the real OpenAI/Anthropic API (#13452) (#13798)
Merged in the 2026-09-16 sweep of the maintainer's own open PRs, at the owner's explicit instruction. No push was made to the PR branch: the merge took the head as the owning session left it (verified OPEN, non-draft and MERGEABLE against the release tip immediately before merging).
2026-09-16 06:12:28 -03:00
Diego Rodrigues de Sa e Souza
68927fc711 fix(providers): MiniMax-M3 reasoning leaks into delta.content instead of reasoning_content (#13558) (#13799)
Merged in the 2026-09-16 sweep of the maintainer's own open PRs, at the owner's explicit instruction. No push was made to the PR branch: the merge took the head as the owning session left it (verified OPEN, non-draft and MERGEABLE against the release tip immediately before merging).
2026-09-16 06:12:12 -03:00
Diego Rodrigues de Sa e Souza
675ab1875e fix(api): persist hideAutoCombos/hideNoThinkVariants in settings schema (#13562) (#13800)
Merged in the 2026-09-16 sweep of the maintainer's own open PRs, at the owner's explicit instruction. No push was made to the PR branch: the merge took the head as the owning session left it (verified OPEN, non-draft and MERGEABLE against the release tip immediately before merging).
2026-09-16 06:11:56 -03:00
Diego Rodrigues de Sa e Souza
0ba661ed97 fix(providers): surface real Antigravity upstream error detail (#13591) (#13801)
Merged in the 2026-09-16 sweep of the maintainer's own open PRs, at the owner's explicit instruction. No push was made to the PR branch: the merge took the head as the owning session left it (verified OPEN, non-draft and MERGEABLE against the release tip immediately before merging).
2026-09-16 06:11:41 -03:00
Diego Rodrigues de Sa e Souza
61d6152699 fix(usage): thread real error/exit-code through callLogs worker failOpen (#13597) (#13802)
Merged in the 2026-09-16 sweep of the maintainer's own open PRs, at the owner's explicit instruction. No push was made to the PR branch: the merge took the head as the owning session left it (verified OPEN, non-draft and MERGEABLE against the release tip immediately before merging).
2026-09-16 06:11:27 -03:00
Diego Rodrigues de Sa e Souza
d7d518a873 fix(api): record audio transcription/translation/speech requests in call_logs (#13544) (#13803)
Merged in the 2026-09-16 sweep of the maintainer's own open PRs, at the owner's explicit instruction. No push was made to the PR branch: the merge took the head as the owning session left it (verified OPEN, non-draft and MERGEABLE against the release tip immediately before merging).
2026-09-16 06:11:13 -03:00
Diego Rodrigues de Sa e Souza
79c4197f07 fix(security): reject unverifiable X-Cloud-Sig in cloud-sync HMAC check (#13679 PR A) (#13804)
Merged in the 2026-09-16 sweep of the maintainer's own open PRs, at the owner's explicit instruction. No push was made to the PR branch: the merge took the head as the owning session left it (verified OPEN, non-draft and MERGEABLE against the release tip immediately before merging).
2026-09-16 06:10:58 -03:00
Diego Rodrigues de Sa e Souza
b33c00b0b6 fix(db): cap per-request batch sweep and guard shared files (#13680, #13681) (#13805)
Merged in the 2026-09-16 sweep of the maintainer's own open PRs, at the owner's explicit instruction. No push was made to the PR branch: the merge took the head as the owning session left it (verified OPEN, non-draft and MERGEABLE against the release tip immediately before merging).
2026-09-16 06:10:43 -03:00
Diego Rodrigues de Sa e Souza
9e53fadc47 fix(sse): recognize bare delta.reasoning in combo streaming quality-gate peek (#13620) (#13806)
Merged in the 2026-09-16 sweep of the maintainer's own open PRs, at the owner's explicit instruction. No push was made to the PR branch: the merge took the head as the owning session left it (verified OPEN, non-draft and MERGEABLE against the release tip immediately before merging).
2026-09-16 06:10:28 -03:00
Diego Rodrigues de Sa e Souza
771c4ce513 fix(providers): echo reasoning_content for bai DeepSeek thinking-mode follow-ups (#13599) (#13807)
Merged in the 2026-09-16 sweep of the maintainer's own open PRs, at the owner's explicit instruction. No push was made to the PR branch: the merge took the head as the owning session left it (verified OPEN, non-draft and MERGEABLE against the release tip immediately before merging).
2026-09-16 06:10:13 -03:00
Diego Rodrigues de Sa e Souza
3fd2440f8a fix(sse): stop re-prepending Kiro tool docs onto every subsequent turn (#13652) (#13808)
Merged in the 2026-09-16 sweep of the maintainer's own open PRs, at the owner's explicit instruction. No push was made to the PR branch: the merge took the head as the owning session left it (verified OPEN, non-draft and MERGEABLE against the release tip immediately before merging).
2026-09-16 06:09:58 -03:00
Diego Rodrigues de Sa e Souza
8e3d06bd9f fix(security): isolate CDP proxy network + auth gate (#13679 PR F) (#13811)
Merged in the 2026-09-16 sweep of the maintainer's own open PRs, at the owner's explicit instruction. No push was made to the PR branch: the merge took the head as the owning session left it (verified OPEN, non-draft and MERGEABLE against the release tip immediately before merging).
2026-09-16 06:09:44 -03:00
Diego Rodrigues de Sa e Souza
c06ac9aafa fix(security): remove literal secrets from podman manifest, block CHANGEME remote login (#13679) (#13812)
Merged in the 2026-09-16 sweep of the maintainer's own open PRs, at the owner's explicit instruction. No push was made to the PR branch: the merge took the head as the owning session left it (verified OPEN, non-draft and MERGEABLE against the release tip immediately before merging).
2026-09-16 06:09:29 -03:00
Diego Rodrigues de Sa e Souza
4de71978b3 fix(security): random per-process self-loop admission bearer (#13679) (#13813)
Merged in the 2026-09-16 sweep of the maintainer's own open PRs, at the owner's explicit instruction. No push was made to the PR branch: the merge took the head as the owning session left it (verified OPEN, non-draft and MERGEABLE against the release tip immediately before merging).
2026-09-16 06:09:15 -03:00
Diego Rodrigues de Sa e Souza
5574eab259 fix(api): restore function_call name on non-streaming /v1/responses (#12370) (#13824)
Merged in the 2026-09-16 sweep of the maintainer's own open PRs, at the owner's explicit instruction. No push was made to the PR branch: the merge took the head as the owning session left it (verified OPEN, non-draft and MERGEABLE against the release tip immediately before merging).
2026-09-16 06:08:58 -03:00
Diego Rodrigues de Sa e Souza
7ca64796ed chore(ci): clear the last two release/v3.8.51 base-reds (agent-skills sync + stryker tap.testFiles) (#13826)
Merged in the 2026-09-16 sweep of the maintainer's own open PRs, at the owner's explicit instruction. No push was made to the PR branch: the merge took the head as the owning session left it (verified OPEN, non-draft and MERGEABLE against the release tip immediately before merging).
2026-09-16 06:08:44 -03:00
Bob.Hou
e7214c72fc ci(acceptance): emit a shadow release-acceptance report next to release-green (#13701)
Adds a shadow release-acceptance report alongside release-green: an inventory/reduce/oracle pipeline under `scripts/quality/release-acceptance/` with a JSON schema, fixtures and a workflow that uploads the report as an artifact.

Contained by design, which is why it merges as-is: it runs only on push to `release/v*` and on manual dispatch (never on pull requests), the step is `continue-on-error`, `permissions: contents: read`, `persist-credentials: false`, and it consumes no secrets. Nothing in the product changes; the report is advisory until we decide to promote it.

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

Thanks @HouMinXi!
2026-09-16 03:17:52 -03:00
Bob.Hou
f36fcd8c31 fix(resilience): extend process crash guard to combo hedge cancels and upstream fetch failures (#13636)
Closes a real process-killer: the direct-response start timeout could fire after the fetch promise had already settled, and aborting at that point delivered the abort reason to a promise nobody was awaiting — Node promotes that to an `unhandledRejection` → `uncaughtException` and the process dies (#12861). The timer is now a no-op once the attempt has settled, and the same guard is extended to combo hedge cancels and upstream fetch failures.

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

Thanks @HouMinXi!
2026-09-16 03:03:07 -03:00
Diego Rodrigues de Sa e Souza
d7d010b102 feat(i18n): blocking key-completeness gate — every locale carries every en.json key (#13827)
New blocking gate `i18n:check-keys` (`scripts/i18n/check-key-completeness.mjs`): every locale catalog must carry exactly the key set of `en.json`, whatever the age of the key. The percentage and new-key gates let batch 1 (#13044) ship 43 keys short and batch 2 (#13660) 10 keys short. Wired into the i18n-ui-coverage job; documented in QUALITY_GATES.md and the i18n guide (post-merge re-sync, retranslation with pinned names).

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

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

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

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

Thanks @HouMinXi!
2026-09-16 02:49:32 -03:00
Bob.Hou
c68b7f58c2 fix(gemini): send thinkingLevel for 3.8 Flash so thoughts stop eating the output cap (#13463)
Gemini 3.8 requests now carry `thinkingLevel` instead of a numeric `thinkingBudget` in both request translators, and `includeThoughts` is no longer injected unless the client asked for it — the numeric budget was being spent on thoughts and truncating the visible answer (`finish_reason=length` with the reply cut short).

Scoped to model ids matching `gemini-3.8` (including the `agy/` and `antigravity/` surfaces); 2.5, 3.1 and 3.7 are untouched, verified at each of the four write sites.

Maintainer note: on the Antigravity envelope path a request without an explicit `max_tokens` no longer keeps `maxOutputTokens` (the guard there reads `thinkingBudget`, which is now absent). The upstream picks the limit in that case; accepted.

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

Thanks @HouMinXi!
2026-09-16 02:37:32 -03:00
Bob.Hou
d6a0460bd6 fix(providers): declare Agnes official thinking effort tiers (#13655)
Declares the accepted thinking-effort tiers per Agnes chat model (2.0/2.5: none/low/medium/high/max; 3.0 adds minimal/xhigh), so the generic declared-tier clamp maps `xhigh`/`off` onto values the upstream accepts instead of forwarding them verbatim and collecting a 400.

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

Thanks @HouMinXi!
2026-09-16 02:23:53 -03:00
Bob.Hou
d589e6b76e fix(providers): clamp SenseNova DeepSeek V4 Flash effort to high (#13626)
SenseNova DeepSeek V4 Flash now clamps `xhigh`/`max` down to `high` before the generic max-tier rewrite, and the model stops advertising `xhigh` — both values were rejected upstream. The clamp covers the first-party `sensenova` provider and openai-compatible connections that address the model as `snova/deepseek-v4-flash`.

Explicitly preserved: `sensenova/glm-5.2` and `deepseek-v4-flash` on `cmd`/`opencode-go`/`ollama-cloud` keep `max`.

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

Thanks @HouMinXi!
2026-09-16 02:20:58 -03:00
Bob.Hou
2b3847e775 fix(sse): strip trailing assistant prefill on official Claude OAuth (#13572)
Adds `claude` to the providers whose trailing text-only assistant turn is stripped before dispatch — official Claude rejects assistant prefill with `400 This model does not support assistant message prefill`.

Maintainer note: the strip applies to the whole `claude` provider family (API key as well as OAuth), matching what the Vertex-hosted Claude path (`open-sse/executors/antigravity.ts`), the Copilot path (`open-sse/executors/github.ts`) and the MITM handler already do unconditionally.

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

Thanks @HouMinXi!
2026-09-16 02:18:20 -03:00
Bob.Hou
67f6c039f3 fix(embeddings): send stored API key on private-host embeddings nodes (#13398)
When an embeddings provider is classified `authType: "none"` and no credentials resolved, the service now looks up the stored connection and promotes to bearer if it holds a key — so private-host/CGNAT embeddings nodes that do require a key stop being called anonymously (#13234).

Security posture holds: `isNoAuthLocalEmbeddingHost` is `isPrivateHost(hostname) && !isCloudMetadataHost(hostname)`, so cloud metadata addresses never reach `authType: "none"` and therefore never reach the new branch; the key only ever goes to the host the operator configured on that connection.

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

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

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

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

Thanks @HouMinXi!
2026-09-16 01:54:00 -03:00
Bob.Hou
ce98c30cfb fix(combos): stop Gemini thinking from failing dashboard combo tests (#13560)
Two related fixes: the combo health probe sends `reasoning_effort: "none"` for Gemini-family models so the probe budget is not spent on thinking, and `detectMalformedNonStream` stops classifying a response with `finish_reason` `length`/`tool_calls`/`content_filter` and empty content as `empty_choices`.

The second half is the important one: it brings the post-translation check in line with `isEmptyContentResponse` (`open-sse/services/errorClassifier.ts`, `LEGIT_EMPTY_OPENAI_FINISH`), which already treated those finish reasons as legitimate. Until now a response could pass the pre-translation check and still be rewritten into a synthetic 502 afterwards — for every non-streaming completion, not just combo probes.

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

Thanks @HouMinXi!
2026-09-16 01:47:32 -03:00
Bob.Hou
79ebbb525f fix(codex): whitelist reasoning object keys before the wire (#13643)
The Codex executor now whitelists the wire `reasoning` object to `effort`/`summary` before dispatch instead of spreading whatever the client sent, and maps `reasoning.enabled === false` to `effort: "none"` when no more specific effort was requested. OpenRouter-style keys (`enabled`, `max_tokens`, `exclude`) were reaching the Responses API and 400-ing the whole combo target with `Unknown parameter: 'reasoning.<key>'`.

The precedence chain keeps an explicit per-request effort ahead of `enabled: false`, and the strip matches the siblings already removed in the same function (`truncation`, `user`, `prompt_cache_retention`).

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

Thanks @HouMinXi!
2026-09-16 01:38:55 -03:00
Dizzle
d4835c512c fix(sse): skip already-refused route per request on 429 (#13795)
On a 429 the opencode executor now records the refused proxy's key in the request-local tried-set, exactly like the 403/451, 5xx, stall and network arms already did — so a second account sharing that same proxy is not dialed and refused again before the loop reaches a genuinely different route (direct, or another proxy).

Reviewed against the tip that already carries your 38 merges from this evening: this is additive to the flags that landed today (`OPENCODE_RATE_LIMITED_429_EARLY_STOP`, `PROXY_SKIP_RECENTLY_FAILED`, `OPENCODE_USER_BLOCKED_ROTATION`, `OPENCODE_TRANSIENT_FAILOVER_BACKOFF`) and does not double-skip when combined with them; direct accounts have a null proxy key and correctly record nothing.

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

Thanks @maxmad64bis!
2026-09-16 01:17:56 -03:00
688 changed files with 78688 additions and 3706 deletions

View File

@@ -112,8 +112,9 @@ DISABLE_SQLITE_AUTO_BACKUP=false
# Example: redis://localhost:6379 (or redis://redis:6379 in Docker)
# REDIS_URL=redis://localhost:6379
# Namespace prefix for ALL OmniRoute Redis keys (rate limiter + auth cache +
# quota store). Prevents key collisions when OmniRoute shares a Redis instance
# with other apps (e.g. on 127.0.0.1:6379). Default when unset: omniroute:
# quota store + warmup circuit breaker). Prevents key collisions when OmniRoute
# shares a Redis instance with other apps (e.g. on 127.0.0.1:6379). Default when
# unset: omniroute:
# REDIS_KEY_PREFIX=omniroute:
# Host interface docker-compose publishes the Redis sidecar on.
# Default: 127.0.0.1 (loopback only). The compose Redis runs WITHOUT
@@ -1040,6 +1041,12 @@ PROVIDER_LIMITS_SYNC_SPACING_MS=1500
# Used by: src/lib/jobs/reasoningCacheCleanupJob.ts.
#OMNIROUTE_REASONING_CACHE_CLEANUP_INTERVAL_MS=1800000
# Opt-in minimum output budget (tokens) for reasoning models (#10281 follow-up).
# When set, a caller max_tokens in [256, floor) on a thinking-capable model is
# raised to the floor so reasoning tokens cannot consume the whole budget
# (zero-content finish_reason=length turns). Unset = never enlarge client budgets (#9507).
#OMNIROUTE_REASONING_MIN_BUDGET=4096
# Spend write batcher cadence (ms) and buffer size before forced flush.
# Used by: src/lib/spend/batchWriter.ts. Defaults: 60000 ms / 1000 entries.
#OMNIROUTE_SPEND_FLUSH_INTERVAL_MS=60000
@@ -1155,12 +1162,34 @@ PROVIDER_LIMITS_SYNC_SPACING_MS=1500
#OMNIROUTE_WAL_GUARD_MAX_MB=256
# Minimum rows a cleanup must delete before the post-cleanup VACUUM runs. Default: 1000.
# 0 always vacuums when rows were freed. Used by: src/lib/db/cleanup.ts.
# 0 always vacuums when rows were freed. The post-cleanup VACUUM also runs when the
# reclaimable-space threshold below is met, whichever comes first (either signal fires it).
# Used by: src/lib/db/cleanup.ts::shouldVacuumAfterCleanup().
#OMNIROUTE_VACUUM_MIN_DELETED_ROWS=1000
# Minimum reclaimable space (MB) that alone justifies a full-database VACUUM after a
# cleanup, even when the row-count threshold above was not met (a handful of oversized
# blob rows can free far more space than thousands of tiny rows). VACUUM is synchronous
# and blocks the entire process. Default: 100. 0 always vacuums after any deletion.
# Used by: src/lib/db/cleanup.ts::getVacuumMinReclaimableBytes().
#OMNIROUTE_VACUUM_MIN_RECLAIMABLE_MB=100
# Explicit path to sql-wasm.wasm for the sql.js fallback adapter. Default: auto-detect.
# Used by: src/lib/db/adapters/sqljsAdapter.ts.
#OMNIROUTE_SQLJS_WASM_PATH=
# Days a terminal (completed/failed/cancelled/expired) Batch API job's checkpoints,
# referenced files, and row are kept by the automatic cleanup sweep. Default: 30
# (matches OpenAI's own Batch API output retention window). Only takes effect once
# BATCH_AND_FILE_AUTO_CLEANUP_ENABLED is turned on.
# Used by: src/lib/db/cleanup.ts::getBatchRetentionDays().
#OMNIROUTE_BATCH_RETENTION_DAYS=30
# Let the automatic cleanup sweep delete terminal Batch API jobs (and their
# checkpoints) past OMNIROUTE_BATCH_RETENTION_DAYS, and clear the content of
# uploaded files past their own expires_at. Off by default: every existing
# install keeps this data exactly as before until an operator opts in.
# Used by: src/lib/db/cleanup.ts (feature flag; see docs/reference/FEATURE_FLAGS.md).
#BATCH_AND_FILE_AUTO_CLEANUP_ENABLED=false
# Skip the Redis-backed auth cache used by API key lookups (forces DB reads).
# Used by: src/lib/db/apiKeys.ts. Set to 1 to disable. Default: enabled.
@@ -2357,6 +2386,10 @@ APP_LOG_TO_FILE=true
# Cursor stream idle timeout (ms). Default: 300000 (5 min).
# Used by: open-sse/executors/cursor.ts.
# CURSOR_STREAM_TIMEOUT_MS=300000
# Grace window (ms) after a composer kv_after_text soft terminator when bytes remain
# buffered — gives a trailing exec_mcp tool call time to complete its frame. 2s covers
# every exec_mcp-behind-kv ordering observed live.
# CURSOR_KV_GRACE_MS=2000
# Cursor tool-commit directive toggle. Default-on: when a request declares
# tools, a directive is prepended so composer-2.5 reliably issues tool calls
@@ -2748,6 +2781,13 @@ APP_LOG_TO_FILE=true
# tokens (accessToken / refreshToken / providerSpecificData). Default OFF —
# only non-credential metadata is synced. See docs/security/SOCKET_DEV_FINDINGS.md §5.
# OMNIROUTE_CLOUD_SYNC_SECRETS=false
#
# Set to "true" to reject an UNSIGNED Cloud sync response when no local secret
# is configured (#13679). Default OFF keeps v3.8.x back-compat for peers that
# have not rotated in a shared secret yet; v3.9 flips the default to enforced.
# A signature that IS present is always verified, and always rejected when
# OMNIROUTE_CLOUD_SYNC_SECRET is unset, regardless of this flag.
# OMNIROUTE_CLOUD_SYNC_ENFORCE_SIGNATURE=false
# ─── Zed import legacy compat (v3.8.6) ──────────────────────────────────────
# Set to "true" to fall back to the v3.8.5 one-step "import everything from
@@ -3043,6 +3083,13 @@ QUOTA_STORE_DRIVER=sqlite
# CHATGPT_WEB_CODEX_CHROME_PATH=/usr/bin/chromium
# CHROME_PATH=/usr/bin/chromium
# CHATGPT_WEB_CODEX_CDP_URL=http://chatgpt-web-codex-browser:9223
# CDP_PROXY_TOKEN required by docker/chatgpt-web-codex-browser/cdp-proxy.mjs (#13679):
# when set, every request to the CDP proxy sidecar must present it as an
# `X-Omni-Cdp-Token` header. Left unset, the proxy keeps forwarding requests
# unauthenticated (network isolation via docker-compose.yml's dedicated
# `chatgpt-web-codex-net` is the default mitigation). Generate with:
# `openssl rand -hex 32`
# CDP_PROXY_TOKEN=
# CHATGPT_WEB_CODEX_TUNNEL_ID=tunnel_0123456789abcdef0123456789abcdef
# CHATGPT_WEB_CODEX_RUNTIME_KEY=
# CHATGPT_WEB_CODEX_CONNECTOR_NAME=OmniRoute Codex v2

View File

@@ -533,6 +533,20 @@ jobs:
env:
BASE_REF: ${{ github.base_ref && format('origin/{0}', github.base_ref) || '' }}
run: node scripts/i18n/check-new-key-coverage.mjs
# Absolute complement of the two gates above: every locale must carry exactly the key
# set of en.json, whatever the age of the key. A locale batch is generated from the
# en.json of the day the branch is cut and translates for days while the base keeps
# adding keys — the batch PR adds no key itself, so the new-key gate stays silent and
# 43 absent keys out of ~13,000 still read 99.7 % coverage. Incident 2026-09-15:
# batch 1 (#13044) landed 43 keys short in nine locales, batch 2 (#13660) 10 keys short
# in eight. Fix is `sync-ui-keys --locale=<codes> --translate-markers`.
- name: i18n key completeness (every locale carries every en.json key)
run: node scripts/i18n/check-key-completeness.mjs
# Same gate for the CLI catalogs (bin/cli/locales). check:cli-i18n only compares
# pt-BR / zh-CN / zh-TW; 38 locales shipped with 124 of 830 keys for months
# (audit 2026-09-16) and the CLI silently fell back to English for them.
- name: i18n key completeness (CLI catalogs)
run: node scripts/i18n/check-key-completeness.mjs --catalog=cli
# #8038: cheap glossary/protected-terms consistency gate —
# complements i18n-ui-coverage (key parity) and the ICU `i18n` job below

View File

@@ -411,16 +411,12 @@ jobs:
path: /tmp/digests/bun-web
merge-multiple: true
- name: Create Docker Hub manifest
- name: Create Docker Hub version manifests
run: |
set -euo pipefail
create_manifest() {
local image="$1" suffix="$2" dir="$3" optional="${4:-}"
local tags=(-t "${image}:${VERSION}${suffix}")
if [ "$PROMOTE_LATEST" = "true" ]; then
tags+=(-t "${image}:latest${suffix}")
fi
local refs=()
while IFS= read -r digest_file; do
refs+=("${image}@sha256:$(basename "$digest_file")")
@@ -433,7 +429,7 @@ jobs:
echo "No image digests in $dir" >&2
exit 1
fi
docker buildx imagetools create "${tags[@]}" "${refs[@]}"
docker buildx imagetools create -t "${image}:${VERSION}${suffix}" "${refs[@]}"
}
create_manifest "${IMAGE_NAME}" "" /tmp/digests/base
@@ -441,16 +437,12 @@ jobs:
create_manifest "${IMAGE_NAME}" "-bun" /tmp/digests/bun-base optional
create_manifest "${IMAGE_NAME}" "-web-bun" /tmp/digests/bun-web optional
- name: Create GHCR manifest
- name: Create GHCR version manifests
run: |
set -euo pipefail
create_manifest() {
local image="$1" suffix="$2" dir="$3" optional="${4:-}"
local tags=(-t "${image}:${VERSION}${suffix}")
if [ "$PROMOTE_LATEST" = "true" ]; then
tags+=(-t "${image}:latest${suffix}")
fi
local refs=()
while IFS= read -r digest_file; do
refs+=("${image}@sha256:$(basename "$digest_file")")
@@ -463,7 +455,7 @@ jobs:
echo "No image digests in $dir" >&2
exit 1
fi
docker buildx imagetools create "${tags[@]}" "${refs[@]}"
docker buildx imagetools create -t "${image}:${VERSION}${suffix}" "${refs[@]}"
}
create_manifest "${GHCR_IMAGE_NAME}" "" /tmp/digests/base
@@ -471,6 +463,59 @@ jobs:
create_manifest "${GHCR_IMAGE_NAME}" "-bun" /tmp/digests/bun-base optional
create_manifest "${GHCR_IMAGE_NAME}" "-web-bun" /tmp/digests/bun-web optional
- name: Smoke-test published Docker image
if: needs.prepare.outputs.version != 'main'
run: |
set -euo pipefail
container="omniroute-smoke-${VERSION//[^a-zA-Z0-9_.-]/-}"
trap 'docker rm -f "$container" >/dev/null 2>&1 || true' EXIT
docker run --detach --name "$container" "${IMAGE_NAME}:${VERSION}"
for attempt in $(seq 1 30); do
status="$(docker inspect --format '{{.State.Health.Status}}' "$container")"
if [ "$status" = "healthy" ]; then
exit 0
fi
if [ "$status" = "unhealthy" ] || [ "$(docker inspect --format '{{.State.Status}}' "$container")" = "exited" ]; then
docker logs "$container"
exit 1
fi
sleep 2
done
docker logs "$container"
exit 1
- name: Promote Docker Hub latest tags
if: needs.prepare.outputs.promote_latest == 'true'
run: |
set -euo pipefail
promote_tag() {
local suffix="$1"
docker buildx imagetools create -t "${IMAGE_NAME}:latest${suffix}" "${IMAGE_NAME}:${VERSION}${suffix}"
}
promote_tag ""
promote_tag "-web"
for suffix in -bun -web-bun; do
if docker buildx imagetools inspect "${IMAGE_NAME}:${VERSION}${suffix}" >/dev/null 2>&1; then
promote_tag "$suffix"
fi
done
- name: Promote GHCR latest tags
if: needs.prepare.outputs.promote_latest == 'true'
run: |
set -euo pipefail
promote_tag() {
local suffix="$1"
docker buildx imagetools create -t "${GHCR_IMAGE_NAME}:latest${suffix}" "${GHCR_IMAGE_NAME}:${VERSION}${suffix}"
}
promote_tag ""
promote_tag "-web"
for suffix in -bun -web-bun; do
if docker buildx imagetools inspect "${GHCR_IMAGE_NAME}:${VERSION}${suffix}" >/dev/null 2>&1; then
promote_tag "$suffix"
fi
done
- name: Inspect image
if: needs.prepare.outputs.version != 'main'
run: |

View File

@@ -23,7 +23,7 @@
"scripts": {
"build": "tsup",
"clean": "rm -rf dist",
"test": "node --import tsx/esm --test tests/scaffold.test.ts tests/auth.test.ts tests/options-schema.test.ts tests/multi-instance.test.ts tests/fetch-interceptor.test.ts tests/telemetry.test.ts tests/provider.test.ts tests/gemini-sanitize.test.ts tests/combos.test.ts tests/config-shim.test.ts tests/features.test.ts tests/feature-defaults.test.ts tests/usable-combo.test.ts tests/disk-snapshot-perms.test.ts tests/fork-features.test.ts tests/auto-combo-context.test.ts tests/provider-id-routing.test.ts tests/management-read-token.test.ts tests/auto-sync.test.ts tests/model-allowlist.test.ts tests/log-level.test.ts tests/effort-tier-variants.test.ts tests/naming.test.ts tests/free-budget-magnitude.test.ts tests/models-fetcher.test.ts",
"test": "node --import tsx/esm --test tests/scaffold.test.ts tests/auth.test.ts tests/options-schema.test.ts tests/multi-instance.test.ts tests/fetch-interceptor.test.ts tests/telemetry.test.ts tests/provider.test.ts tests/gemini-sanitize.test.ts tests/combos.test.ts tests/config-shim.test.ts tests/features.test.ts tests/feature-defaults.test.ts tests/usable-combo.test.ts tests/disk-snapshot-perms.test.ts tests/fork-features.test.ts tests/auto-combo-context.test.ts tests/provider-id-routing.test.ts tests/management-read-token.test.ts tests/auto-sync.test.ts tests/model-allowlist.test.ts tests/log-level.test.ts tests/effort-tier-variants.test.ts tests/naming.test.ts tests/free-budget-magnitude.test.ts tests/models-fetcher.test.ts tests/issue-13000-cold-start-combo-limit.test.ts",
"prepublishOnly": "npm run clean && npm run build && npm test"
},
"keywords": [

View File

@@ -4631,9 +4631,21 @@ export function buildStaticProviderEntry(
.map((m) => m.max_output_tokens)
.filter((v): v is number => typeof v === "number" && v > 0);
if (contextValues.length > 0 && outputValues.length > 0) {
// Prefer the server-computed aggregate (accounts for explicit
// context_length overrides and members outside memberEntries, e.g.
// not yet resolved in /v1/models) over the raw Math.min(member)
// lower bound. Mirrors mapComboToModelV2's limit.context logic
// (#13000) so the static catalog and the dynamic hook agree.
const preferredContext =
typeof combo.computed_context_length === "number" && combo.computed_context_length > 0
? combo.computed_context_length
: contextValues.length > 0
? Math.min(...contextValues)
: undefined;
if (preferredContext !== undefined && outputValues.length > 0) {
entry.limit = {
context: Math.min(...contextValues),
context: preferredContext,
output: Math.min(...outputValues),
};
}
@@ -5511,6 +5523,32 @@ export function createOmniRouteConfigHook(
const modelsFetchOk = !modelsFetchThrew && localRawModels.length > 0;
// Snapshot backfill for computed_context_length: a live /api/combos
// response can come back without this field (server hasn't finished
// recomputing it yet, e.g. just after a restart) even though the
// combo's members and identity are otherwise unchanged. When that
// happens, prefer the last-known-good value from the warm disk
// snapshot over the Math.min(member) fallback in
// mapComboToModelV2() — never overwrite any other combo field
// (models/name/etc.) with stale data, only this one derived number.
if (warmSnapshot) {
const snapshotComboById = new Map(warmSnapshot.rawCombos.map((c) => [c.id, c]));
for (const combo of localRawCombos) {
const hasLive =
typeof combo.computed_context_length === "number" &&
combo.computed_context_length > 0;
if (hasLive) continue;
const stale = snapshotComboById.get(combo.id);
if (
stale &&
typeof stale.computed_context_length === "number" &&
stale.computed_context_length > 0
) {
combo.computed_context_length = stale.computed_context_length;
}
}
}
// Disk-cache fallback (cold first run, no warm snapshot): when the
// live fetch returned no models AND features.diskCache !== false,
// hydrate from the last-known-good snapshot so OC still surfaces a
@@ -5518,9 +5556,17 @@ export function createOmniRouteConfigHook(
if (modelsFetchThrew && wantDiskCache && !warmSnapshot) {
const snapshot = await diskSnapshotReader(resolved.providerId, snapshotFingerprint);
if (snapshot && snapshot.rawModels.length > 0) {
// Report snapshot age like the warm-startup path already does:
// "stale" alone reads as a transient blip, so a week-old catalog
// is indistinguishable from a five-minute-old one.
const snapshotAge = snapshot.writtenAt;
const snapshotAgeLabel =
typeof snapshotAge === "number"
? `${Math.round((Date.now() - snapshotAge) / 3_600_000)}h`
: "unknown";
logAt(
"warn",
`config shim: /v1/models unreachable; using stale disk cache (${snapshot.rawModels.length} models)`
`config shim: /v1/models unreachable; using stale disk cache (${snapshot.rawModels.length} models, age ${snapshotAgeLabel})`
);
localRawModels = snapshot.rawModels;
localRawCombos = snapshot.rawCombos;

View File

@@ -481,10 +481,7 @@ test("config: combos fetcher throws → emit models-only catalog (no combos in m
];
assert.ok(entry);
const ids = Object.keys(entry.models).sort();
assert.deepEqual(ids, [
"claude-sonnet-4-6",
"gemini-3-flash",
]);
assert.deepEqual(ids, ["claude-sonnet-4-6", "gemini-3-flash"]);
assert.equal(entry.models["claude-tier"], undefined, "no combo entry");
assert.ok(
logger.entries.some((e) => String(e[0]).includes("/api/combos fetch failed")),
@@ -1041,11 +1038,7 @@ test("config: features.enrichment=false skips enrichment fetch + keeps raw-id na
];
assert.ok(entry);
assert.equal(enrichmentFetcher.callCount(), 0, "enrichment fetch suppressed by feature flag");
assert.equal(
entry.models["claude-sonnet-4-6"].name,
"claude-sonnet-4-6",
"raw id retained"
);
assert.equal(entry.models["claude-sonnet-4-6"].name, "claude-sonnet-4-6", "raw id retained");
});
test("config: enrichment fetcher throws → soft-fail (warn + raw-id static catalog)", async () => {
@@ -1068,11 +1061,7 @@ test("config: enrichment fetcher throws → soft-fail (warn + raw-id static cata
"opencode-omniroute"
];
assert.ok(entry, "static block still published on enrichment failure");
assert.equal(
entry.models["claude-sonnet-4-6"].name,
"claude-sonnet-4-6",
"raw id retained"
);
assert.equal(entry.models["claude-sonnet-4-6"].name, "claude-sonnet-4-6", "raw id retained");
assert.equal(enrichmentFetcher.callCount(), 1);
assert.ok(
logger.entries.some((e) => String(e[0]).includes("/api/pricing/models fetch failed")),
@@ -1270,10 +1259,7 @@ test("config: diskCache hydrates stale snapshot when /v1/models throws", async (
const entry = (input as { provider: Record<string, OmniRouteStaticProviderEntry> }).provider[
"opencode-omniroute"
];
assert.ok(
entry.models["claude-sonnet-4-6"],
"stale snapshot hydrated into static block"
);
assert.ok(entry.models["claude-sonnet-4-6"], "stale snapshot hydrated into static block");
assert.equal(
entry.models["claude-sonnet-4-6"].name,
"Claude Sonnet 4.6 (cached)",
@@ -1281,14 +1267,95 @@ test("config: diskCache hydrates stale snapshot when /v1/models throws", async (
);
assert.equal(writes, 0, "disk write skipped when live fetch failed");
assert.ok(
logger.entries.some((e) =>
String(e[0]).includes("using stale disk cache") ||
String(e[0]).includes("warm startup from disk snapshot")
logger.entries.some(
(e) =>
String(e[0]).includes("using stale disk cache") ||
String(e[0]).includes("warm startup from disk snapshot")
),
"disk-cache hydration breadcrumb emitted"
);
});
// The stale-fallback branch (`modelsFetchThrew && wantDiskCache && !warmSnapshot`)
// only runs when the warm-startup read found nothing — a snapshot can appear on
// disk between that first read and the live fetch failing (e.g. another OC
// process instance wrote one concurrently). A stateful reader simulates that:
// empty on the warm-startup read, populated by the time the fallback re-reads.
function emptyThenSnapshotReader(
snapshot: Omit<
Awaited<ReturnType<typeof import("../src/index.js").defaultDiskSnapshotReader>> & object,
never
>
): typeof import("../src/index.js").defaultDiskSnapshotReader {
let calls = 0;
return (async () => {
calls++;
return calls === 1 ? undefined : snapshot;
}) as typeof import("../src/index.js").defaultDiskSnapshotReader;
}
test("config: stale-fallback warning reports the disk snapshot age in hours", async () => {
const readAuthJson = stubReadAuthJson({
"opencode-omniroute": { type: "api", key: "sk-test", baseURL: "https://or.example/v1" },
});
const fetcher = throwingModelsFetcher();
const combosFetcher = stubCombosFetcher([]);
const logger = captureWarn();
const writtenAt = Date.now() - 2 * 3_600_000; // 2h old
const diskSnapshotReader = emptyThenSnapshotReader({
rawModels: [MODEL_CLAUDE],
rawCombos: [],
rawEnrichment: new Map(),
rawCompressionCombos: [],
rawConnections: [],
writtenAt,
});
const hook = createOmniRouteConfigHook(
{ providerId: "omniroute", features: { diskCache: true } },
{ readAuthJson, fetcher, combosFetcher, diskSnapshotReader, logger }
);
await hook(makeInput());
assert.ok(
logger.entries.some((e) => String(e[0]).includes("using stale disk cache (1 models, age 2h)")),
"stale-fallback warning includes the computed snapshot age"
);
});
test('config: stale-fallback warning falls back to "unknown" age without writtenAt', async () => {
const readAuthJson = stubReadAuthJson({
"opencode-omniroute": { type: "api", key: "sk-test", baseURL: "https://or.example/v1" },
});
const fetcher = throwingModelsFetcher();
const combosFetcher = stubCombosFetcher([]);
const logger = captureWarn();
const diskSnapshotReader = emptyThenSnapshotReader({
rawModels: [MODEL_CLAUDE],
rawCombos: [],
rawEnrichment: new Map(),
rawCompressionCombos: [],
rawConnections: [],
});
const hook = createOmniRouteConfigHook(
{ providerId: "omniroute", features: { diskCache: true } },
{ readAuthJson, fetcher, combosFetcher, diskSnapshotReader, logger }
);
await hook(makeInput());
assert.ok(
logger.entries.some((e) =>
String(e[0]).includes("using stale disk cache (1 models, age unknown)")
),
'stale-fallback warning falls back to "unknown" when writtenAt is absent'
);
});
test("config: cached rawEnrichment from earlier provider hook is reused (no refetch)", async () => {
const readAuthJson = stubReadAuthJson({
"opencode-omniroute": { type: "api", key: "sk-shared", baseURL: "https://or.example/v1" },
@@ -1376,10 +1443,7 @@ test("config: providerTag (default-on) prepends '<provider> - ' to enriched raw-
"opencode-omniroute"
];
assert.ok(entry);
assert.equal(
entry.models["claude-sonnet-4-6"].name,
"Claude - Claude Sonnet 4.6"
);
assert.equal(entry.models["claude-sonnet-4-6"].name, "Claude - Claude Sonnet 4.6");
assert.equal(entry.models["gemini-3-flash"].name, "Gemini - Gemini 3 Flash");
// Combos stay untouched — `Combo: ` prefix already conveys multi-upstream.
assert.equal(entry.models["claude-tier"].name, "Claude Tier");
@@ -1495,10 +1559,7 @@ test("config: providerTag is idempotent — second hook call doesn't double-suff
const entryA = (inputA as { provider: Record<string, OmniRouteStaticProviderEntry> }).provider[
"opencode-omniroute"
];
assert.equal(
entryA.models["claude-sonnet-4-6"].name,
"Claude - Claude Sonnet 4.6"
);
assert.equal(entryA.models["claude-sonnet-4-6"].name, "Claude - Claude Sonnet 4.6");
// Second invocation (cache hit) — name must still be single-suffixed.
const inputB = makeInput();
@@ -1506,10 +1567,7 @@ test("config: providerTag is idempotent — second hook call doesn't double-suff
const entryB = (inputB as { provider: Record<string, OmniRouteStaticProviderEntry> }).provider[
"opencode-omniroute"
];
assert.equal(
entryB.models["claude-sonnet-4-6"].name,
"Claude - Claude Sonnet 4.6"
);
assert.equal(entryB.models["claude-sonnet-4-6"].name, "Claude - Claude Sonnet 4.6");
});
// ────────────────────────────────────────────────────────────────────────────

View File

@@ -0,0 +1,221 @@
/**
* Repro for #13000: combo context limits fall back to Math.min(member)
* instead of using computed_context_length after cold start — no disk
* snapshot fallback.
*
* Scenario (mirrors the report): a warm disk snapshot holds the combo with
* its correct server-computed `computed_context_length` (245000, from all 6
* members). After a restart, the live refresh's combos fetch returns the
* SAME combo but without `computed_context_length` (e.g. the value hasn't
* propagated yet), and the live models fetch only resolves 2 of the 6
* members (the rest not yet in /v1/models). The background refresh then
* republishes the provider block built from this degraded live data,
* downgrading a previously-known-good 245000 limit to Math.min(163840,
* 1_000_000) = 163840 — exactly the member-minimum described in the issue.
*/
import test from "node:test";
import assert from "node:assert/strict";
import type { Config } from "@opencode-ai/plugin";
import {
createOmniRouteConfigHook,
_resetInflightRefresh,
type OmniRouteAutoCombosFetcher,
type OmniRouteCombosFetcher,
type OmniRouteCompressionMetaFetcher,
type OmniRouteEnrichmentFetcher,
type OmniRouteFetchCache,
type OmniRouteModelsFetcher,
type OmniRouteProvidersFetcher,
type OmniRouteRawCombo,
type OmniRouteRawModelEntry,
type OmniRouteReadAuthJson,
type OmniRouteStaticProviderEntry,
type OmniRouteDiskSnapshotReader,
type OmniRouteDiskSnapshotWriter,
} from "../src/index.js";
test.beforeEach(() => {
_resetInflightRefresh();
});
function stubReadAuthJson(value: Record<string, unknown>): OmniRouteReadAuthJson {
return async () => value as never;
}
function authStub() {
return stubReadAuthJson({
"opencode-omniroute": {
type: "api",
key: "sk-test",
baseURL: "https://or.example.com/v1",
},
});
}
function makeInput(): Config {
return { provider: {} } as unknown as Config;
}
// The two members resolvable in the degraded live /v1/models response.
const MEMBER_DEEPSEEK: OmniRouteRawModelEntry = {
id: "deepseek-v4-pro",
capabilities: { tool_calling: true, reasoning: true, vision: false, thinking: false },
context_length: 163_840,
max_output_tokens: 64_000,
input_modalities: ["text"],
output_modalities: ["text"],
};
const MEMBER_GLM: OmniRouteRawModelEntry = {
id: "glm-5.2",
capabilities: { tool_calling: true, reasoning: true, vision: false, thinking: false },
context_length: 1_000_000,
max_output_tokens: 16_384,
input_modalities: ["text"],
output_modalities: ["text"],
};
// The other member that IS present once the server is fully warm.
const MEMBER_GLM_53_HIGH: OmniRouteRawModelEntry = {
id: "GLM-5.3-high",
capabilities: { tool_calling: true, reasoning: true, vision: false, thinking: false },
context_length: 245_000,
max_output_tokens: 128_000,
input_modalities: ["text"],
output_modalities: ["text"],
};
const COMBO_MODELS: OmniRouteRawCombo["models"] = [
{ kind: "model", model: "deepseek-v4-pro", weight: 25 },
{ kind: "model", model: "glm-5.2", weight: 25 },
{ kind: "model", model: "GLM-5.3-high", weight: 50 },
];
test("issue #13000: warm combo limit (245000) survives a degraded post-restart refresh instead of downgrading to Math.min(member)", async () => {
const warmSnapshot: Omit<import("../src/index.js").OmniRouteFetchCacheEntry, "expiresAt"> = {
rawModels: [MEMBER_DEEPSEEK, MEMBER_GLM, MEMBER_GLM_53_HIGH],
rawCombos: [
{
id: "orchestrator",
name: "orchestrator",
models: COMBO_MODELS,
computed_context_length: 245_000,
},
],
rawAutoCombos: [],
rawEnrichment: new Map(),
rawCompressionCombos: [],
rawConnections: [],
};
const fetcher: OmniRouteModelsFetcher = async () => [MEMBER_DEEPSEEK, MEMBER_GLM];
const combosFetcher: OmniRouteCombosFetcher = async () => [
{
id: "orchestrator",
name: "orchestrator",
models: COMBO_MODELS,
// computed_context_length intentionally omitted.
},
];
const autoCombosFetcher: OmniRouteAutoCombosFetcher = async () => [];
const enrichmentFetcher: OmniRouteEnrichmentFetcher = async () => new Map();
const compressionMetaFetcher: OmniRouteCompressionMetaFetcher = async () => [];
const providersFetcher: OmniRouteProvidersFetcher = async () => [];
const diskSnapshotReader: OmniRouteDiskSnapshotReader = async () => warmSnapshot;
const diskSnapshotWriter: OmniRouteDiskSnapshotWriter = async () => {};
const sharedCache: OmniRouteFetchCache = new Map();
const hook = createOmniRouteConfigHook(
{ providerId: "omniroute", modelCacheTtl: 60_000 },
{
readAuthJson: authStub(),
fetcher,
combosFetcher,
autoCombosFetcher,
enrichmentFetcher,
compressionMetaFetcher,
providersFetcher,
diskSnapshotReader,
diskSnapshotWriter,
cache: sharedCache,
}
);
const input = makeInput();
await hook(input);
// Let the detached background refresh (degraded live data) complete and
// republish the block.
await new Promise((r) => setTimeout(r, 100));
const entryAfter = (input as { provider: Record<string, OmniRouteStaticProviderEntry> }).provider[
"opencode-omniroute"
];
const comboModelAfter = entryAfter.models["orchestrator"];
assert.ok(comboModelAfter, "combo model still published after refresh");
assert.equal(
comboModelAfter.limit.context,
245_000,
`expected the combo limit to stay at the known-good 245000, but got ${comboModelAfter.limit.context} ` +
`(Math.min(member) fallback — the exact bug described in #13000)`
);
});
test("issue #13000 (control): no warm snapshot exists — Math.min(member) fallback is still used (expected, documented behavior)", async () => {
const fetcher: OmniRouteModelsFetcher = async () => [MEMBER_DEEPSEEK, MEMBER_GLM];
const combosFetcher: OmniRouteCombosFetcher = async () => [
{
id: "orchestrator",
name: "orchestrator",
models: COMBO_MODELS,
// computed_context_length intentionally omitted.
},
];
const autoCombosFetcher: OmniRouteAutoCombosFetcher = async () => [];
const enrichmentFetcher: OmniRouteEnrichmentFetcher = async () => new Map();
const compressionMetaFetcher: OmniRouteCompressionMetaFetcher = async () => [];
const providersFetcher: OmniRouteProvidersFetcher = async () => [];
// No prior snapshot on disk.
const diskSnapshotReader: OmniRouteDiskSnapshotReader = async () => undefined;
const diskSnapshotWriter: OmniRouteDiskSnapshotWriter = async () => {};
const sharedCache: OmniRouteFetchCache = new Map();
const hook = createOmniRouteConfigHook(
{ providerId: "omniroute", modelCacheTtl: 60_000 },
{
readAuthJson: authStub(),
fetcher,
combosFetcher,
autoCombosFetcher,
enrichmentFetcher,
compressionMetaFetcher,
providersFetcher,
diskSnapshotReader,
diskSnapshotWriter,
cache: sharedCache,
}
);
const input = makeInput();
await hook(input);
const entryAfter = (input as { provider: Record<string, OmniRouteStaticProviderEntry> }).provider[
"opencode-omniroute"
];
const comboModelAfter = entryAfter.models["orchestrator"];
assert.ok(comboModelAfter, "combo model published on cold first run");
// No snapshot to backfill from — Math.min(163840, 1_000_000) = 163840.
assert.equal(
comboModelAfter.limit.context,
163_840,
"pure cold start with no snapshot must keep using the Math.min(member) fallback"
);
});

View File

@@ -46,7 +46,7 @@ Repository map and Reference Documentation sections below.
## Project at a Glance
**OmniRoute** — unified AI proxy/router. One endpoint, 358 LLM providers, auto-fallback.
**OmniRoute** — unified AI proxy/router. One endpoint, 359 LLM providers, auto-fallback.
| Layer | Location | Purpose |
| ------------- | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
@@ -664,7 +664,7 @@ focused checks, and use a Conventional Commit message (for example, `docs: slim
## Environment
- **Runtime**: Node.js ≥22.22.2 <23 || ≥24.0.0 <27, ES Modules. This is the **only supported** runtime for the published `omniroute` CLI, the server, and the test suites (`node:test` + vitest) — `engines.node` is authoritative and end users never need Bun. A **best-effort `bun:sqlite` compatibility path** exists so a global Bun install (`bun install -g omniroute`) can start without `better-sqlite3` (driver adapter + Bun-aware process spawning); it is **not** a supported runtime — no support guarantees — and every Bun-specific runtime change MUST preserve the Node driver/fallback chain and ship a Bun test (`test:bun:db`) or an explicit reason why the path is Node-only.
- **Bun (build/dev script runner + compatibility smoke only)**: Bun `1.4.0` is pinned as an **exact devDependency** (provisioned through the existing `npm ci` via the lockfile's `@oven/bun-*` platform binaries — no `setup-bun`/ad-hoc install). It is used **only** to execute a small, allow-listed set of TypeScript **gate/generator scripts** (replacing `node --import tsx` for startup speed): the CI checks `check:provider-consistency`, `check:compression-budget`, `check:known-symbols`, and the non-CI `gen:provider-reference`, `bench:compression` — plus the focused `test:bun:db` compatibility smoke suite for the best-effort `bun:sqlite` path. **Do NOT** widen Bun to `npm install`, the build (`build:cli*`), `check:pack-artifact`, the supported published runtime, or the main test runners — those stay on Node. Any new Bun-invoking gate/generator script must be validated byte-identical against its `node --import tsx` output first. After pulling the lockfile change, run `npm install` so `bun` resolves locally (a stale `node_modules` will fail those scripts with `bun: not found`).
- **Bun (build/dev script runner + compatibility smoke only)**: Bun `1.4.2` is pinned as an **exact devDependency** (provisioned through the existing `npm ci` via the lockfile's `@oven/bun-*` platform binaries — no `setup-bun`/ad-hoc install). It is used **only** to execute a small, allow-listed set of TypeScript **gate/generator scripts** (replacing `node --import tsx` for startup speed): the CI checks `check:provider-consistency`, `check:compression-budget`, `check:known-symbols`, and the non-CI `gen:provider-reference`, `bench:compression` — plus the focused `test:bun:db` compatibility smoke suite for the best-effort `bun:sqlite` path. **Do NOT** widen Bun to `npm install`, the build (`build:cli*`), `check:pack-artifact`, the supported published runtime, or the main test runners — those stay on Node. Any new Bun-invoking gate/generator script must be validated byte-identical against its `node --import tsx` output first. After pulling the lockfile change, run `npm install` so `bun` resolves locally (a stale `node_modules` will fail those scripts with `bun: not found`).
- **TypeScript**: 6.0+, target ES2022, module esnext, resolution bundler
- **Path aliases**: `@/*` → `src/`, `@omniroute/open-sse` → `open-sse/`, `@omniroute/open-sse/*` → `open-sse/*`
- **Default port**: 20128 (API + dashboard on same port)

View File

@@ -1,5 +1,5 @@
# ── Multi-stage Dockerfile for Native Bun Runtime (web-latest-bun) ───────────
FROM oven/bun:1.4.0-slim AS base
FROM oven/bun:1.4.2-slim AS base
WORKDIR /app
RUN apt-get update \
@@ -56,7 +56,7 @@ ENV NODE_ENV=production
RUN bun run --quiet build
# ── Runner Base stage (100% Bun Native Production Runtime) ──────────────────
FROM oven/bun:1.4.0-slim AS runner-base
FROM oven/bun:1.4.2-slim AS runner-base
LABEL org.opencontainers.image.title="omniroute" \
org.opencontainers.image.description="Unified AI proxy — route any LLM through one endpoint (Bun Native)" \

View File

@@ -7,7 +7,7 @@
# 🚀 OmniRoute — The Free AI Gateway
<img src="./docs/diagrams/readme-hero.svg" width="100%" alt="OmniRoute — Never stop coding. Every AI tool → 358 providers — 150+ free — through one endpoint. Claude Code, Codex, Cursor, Cline, Copilot & Antigravity into FREE Claude / GPT / Gemini with auto-fallback. RTK + Caveman stacked compression saves 1595% tokens (~89% avg) — never hit limits. 358 AI providers · 150+ free tiers · ~1.47B free tokens/mo · 19 routing strategies · $0 to start."/>
<img src="./docs/diagrams/readme-hero.svg" width="100%" alt="OmniRoute — Never stop coding. Every AI tool → 359 providers — 150+ free — through one endpoint. Claude Code, Codex, Cursor, Cline, Copilot & Antigravity into FREE Claude / GPT / Gemini with auto-fallback. RTK + Caveman stacked compression saves 1595% tokens (~89% avg) — never hit limits. 359 AI providers · 150+ free tiers · ~1.47B free tokens/mo · 19 routing strategies · $0 to start."/>
</div>
@@ -17,9 +17,9 @@
</div>
> Stacking free tiers by hand is painful — dozens of SDKs, dozens of rate limits, and no idea how much you actually have. OmniRoute catalogs **443 free-tier entries across 34 recurring pool keys** and computes the token headline from the **16 pools with a published positive monthly budget plus five per-model Groq caps**, deduplicated by shared pool. Quotas that only open after a regional identity check (today: ModelScope) are shown apart, +~6M behind regional identity verification, and never summed into the headline. The result stays visible on the dashboard (`/dashboard/free-tiers`).
> Stacking free tiers by hand is painful — dozens of SDKs, dozens of rate limits, and no idea how much you actually have. OmniRoute catalogs **452 free-tier entries across 34 recurring pool keys** and computes the token headline from the **16 pools with a published positive monthly budget plus five per-model Groq caps**, deduplicated by shared pool. Quotas that only open after a regional identity check (today: ModelScope) are shown apart, +~6M behind regional identity verification, and never summed into the headline. The result stays visible on the dashboard (`/dashboard/free-tiers`).
<img src="./docs/diagrams/free-tier-budget.svg" width="100%" alt="OmniRoute free-tier budget card: ~1.47B free tokens per month steady, up to ~2.07B in the first month with signup credits, from 34 documented recurring pool keys covering 443 cataloged free-tier entries behind one endpoint. Honest pool-deduped math — each shared pool counted once, including 16 recurring pools with a published positive monthly token budget plus five per-model Groq caps; 13 providers are marked avoid in the terms-risk catalog so you decide. Budget bar includes Mistral 1B, Nara 210M, LLM7 150M, Groq 30M (five per-model caps) and smaller pools, plus first-month signup credits and permanently-free no-token-cap providers surfaced separately so they never inflate the headline. Live used/remaining on /dashboard/free-tiers."/>
<img src="./docs/diagrams/free-tier-budget.svg" width="100%" alt="OmniRoute free-tier budget card: ~1.47B free tokens per month steady, up to ~2.07B in the first month with signup credits, from 34 documented recurring pool keys covering 452 cataloged free-tier entries behind one endpoint. Honest pool-deduped math — each shared pool counted once, including 16 recurring pools with a published positive monthly token budget plus five per-model Groq caps; 13 providers are marked avoid in the terms-risk catalog so you decide. Budget bar includes Mistral 1B, Nara 210M, LLM7 150M, Groq 30M (five per-model caps) and smaller pools, plus first-month signup credits and permanently-free no-token-cap providers surfaced separately so they never inflate the headline. Live used/remaining on /dashboard/free-tiers."/>
> Animated summary of the live `/dashboard/free-tiers` page. Full methodology (pool dedupe, credit tiers, provider terms): **[docs/reference/FREE_TIERS.md](docs/reference/FREE_TIERS.md)**.
>
@@ -233,7 +233,7 @@ curl http://localhost:20128/v1/chat/completions \
</div>
<img src="./docs/diagrams/promise-pillars.svg" width="100%" alt="The Promise — One endpoint and 358 providers. Automatic fallback keeps routing while another healthy target is available. Six pillars: resilient fallback across 358 providers · up to 95% token savings on eligible workloads · $0 to start with 150+ free tiers and 52 recurring/keyless free-forever providers · 36 CLI/agent integrations through one config · OpenAI, Claude, Gemini and Responses API compatibility at /v1 · production controls including circuit breakers, TLS stealth, MCP 110 tools, A2A, memory, guardrails, evals and 39,000+ static test declarations across 5,100+ tracked test files."/>
<img src="./docs/diagrams/promise-pillars.svg" width="100%" alt="The Promise — One endpoint and 359 providers. Automatic fallback keeps routing while another healthy target is available. Six pillars: resilient fallback across 359 providers · up to 95% token savings on eligible workloads · $0 to start with 150+ free tiers and 53 recurring/keyless free-forever providers · 36 CLI/agent integrations through one config · OpenAI, Claude, Gemini and Responses API compatibility at /v1 · production controls including circuit breakers, TLS stealth, MCP 110 tools, A2A, memory, guardrails, evals and 39,000+ static test declarations across 5,100+ tracked test files."/>
<br/>
<br/>
@@ -486,7 +486,7 @@ All **19** strategies — mix & match per combo step:
</div>
<img src="./docs/diagrams/comparison-table.svg" width="100%" alt="What sets OmniRoute apart — a dated feature snapshot vs 9router, OpenRouter, CLIProxyAPI and LiteLLM across 13 capabilities. OmniRoute: 358 providers, 150+ free tiers built in, 19 routing strategies, 12-engine token compression, built-in MCP server with 110 tools, A2A agent protocol, persistent memory, guardrails, cloud agents, TLS fingerprint stealth, Desktop/Termux/PWA and 42 i18n UI locales. OmniRoute is MIT-licensed and self-hostable. Competitor capabilities and counts may change; see the linked methodology."/>
<img src="./docs/diagrams/comparison-table.svg" width="100%" alt="What sets OmniRoute apart — a dated feature snapshot vs 9router, OpenRouter, CLIProxyAPI and LiteLLM across 13 capabilities. OmniRoute: 359 providers, 150+ free tiers built in, 19 routing strategies, 12-engine token compression, built-in MCP server with 110 tools, A2A agent protocol, persistent memory, guardrails, cloud agents, TLS fingerprint stealth, Desktop/Termux/PWA and 42 i18n UI locales. OmniRoute is MIT-licensed and self-hostable. Competitor capabilities and counts may change; see the linked methodology."/>
<sub>📊 Full methodology &amp; per-feature detail vs 9router, OpenRouter, CLIProxyAPI &amp; LiteLLM → [`docs/comparison/OMNIROUTE_VS_ALTERNATIVES.md`](docs/comparison/OMNIROUTE_VS_ALTERNATIVES.md)</sub>
@@ -672,7 +672,7 @@ of your shell history. → [CLI Integrations](docs/guides/CLI-INTEGRATIONS.md)
</div>
> **352 registered providers** across the canonical chat, media, search, local, cloud-agent and system collections, including **152 carrying `hasFree: true` discovery metadata**. The chat model registry covers **229 providers / 2,554 distinct provider-model pairs / 1,283 raw model IDs**; the separate free-budget catalog has **443 per-model rows**, **34 recurring pools** and **52 recurring/keyless free-forever providers**. These are different denominators by design; definitions and pool-deduped calculations live in the [Provider Reference](docs/reference/PROVIDER_REFERENCE.md) and [Free Tiers](docs/reference/FREE_TIERS.md).
> **352 registered providers** across the canonical chat, media, search, local, cloud-agent and system collections, including **152 carrying `hasFree: true` discovery metadata**. The chat model registry covers **229 providers / 2,554 distinct provider-model pairs / 1,283 raw model IDs**; the separate free-budget catalog has **443 per-model rows**, **34 recurring pools** and **53 recurring/keyless free-forever providers**. These are different denominators by design; definitions and pool-deduped calculations live in the [Provider Reference](docs/reference/PROVIDER_REFERENCE.md) and [Free Tiers](docs/reference/FREE_TIERS.md).
<div align="center">
@@ -1331,7 +1331,7 @@ Métricas canônicas em 2026-08-24: **1.029 vídeos únicos** · **11.132.922 vi
<tr><td nowrap><b><a href="docs/architecture/RESILIENCE_GUIDE.md">Resilience Guide</a></b></td><td>Circuit breakers, cooldowns, queue, anti-thundering herd, TLS spoofing</td></tr>
<tr><td nowrap><b><a href="docs/routing/AUTO-COMBO.md">Auto-Combo Engine</a></b></td><td>16-factor scoring, mode packs, self-healing</td></tr>
<tr><td nowrap><b><a href="docs/ops/PROXY_GUIDE.md">Proxy Guide</a></b></td><td>3-level proxy system, 1proxy marketplace, registry CRUD</td></tr>
<tr><td nowrap><b><a href="docs/reference/FREE_TIERS.md">Free Tiers</a></b></td><td>Consolidated directory: 34 documented recurring pools / 443 cataloged free-tier entries</td></tr>
<tr><td nowrap><b><a href="docs/reference/FREE_TIERS.md">Free Tiers</a></b></td><td>Consolidated directory: 34 documented recurring pools / 452 cataloged free-tier entries</td></tr>
<tr><td nowrap><b><a href="docs/guides/FEATURES.md">Features Gallery</a></b></td><td>Visual dashboard tour with screenshots</td></tr>
<tr><td nowrap><b><a href="docs/architecture/CODEBASE_DOCUMENTATION.md">Codebase Documentation</a></b></td><td>Beginner-friendly codebase walkthrough</td></tr>
</table>

View File

@@ -79,7 +79,8 @@ export async function runChatCommand(promptArg, opts, cmd) {
const data = await response.json();
const text = extractText(data, opts.responsesApi);
if (!opts.noHistory) {
// Commander stores `--no-history` as `history === false`, never as `noHistory`.
if (opts.history !== false && opts.noHistory !== true) {
appendHistory({ prompt, model: opts.model, latencyMs, usage: data.usage, response: text });
}

View File

@@ -248,7 +248,9 @@ export function registerContexts(program) {
.option("--no-secrets", "Omit API keys from export")
.action(async (opts, cmd) => {
const cfg = loadContexts();
const out = opts.noSecrets ? redactContextSecrets(cfg) : JSON.parse(JSON.stringify(cfg));
// Commander stores `--no-secrets` as `secrets === false`, never as `noSecrets`.
const redact = opts.secrets === false || opts.noSecrets === true;
const out = redact ? redactContextSecrets(cfg) : JSON.parse(JSON.stringify(cfg));
const json = JSON.stringify(out, null, 2);
if (opts.out) {
const { writeFileSync } = await import("node:fs");

View File

@@ -9,6 +9,8 @@ function truncate(v, len = 60) {
return s.length > len ? s.slice(0, len - 1) + "…" : s;
}
const VALID_MCP_TRANSPORTS = ["stdio", "sse", "streamable-http"];
const mcpToolSchema = [
{ key: "name", header: "Tool", width: 36 },
{
@@ -43,6 +45,25 @@ export function registerMcp(program) {
if (exitCode !== 0) process.exit(exitCode);
});
mcp
.command("enable")
.description(t("mcp.enable.description"))
.option("--transport <transport>", t("mcp.enable.transport"))
.action(async (opts, cmd) => {
const globalOpts = cmd.parent.optsWithGlobals();
const exitCode = await runMcpEnableCommand({ ...opts, output: globalOpts.output });
if (exitCode !== 0) process.exit(exitCode);
});
mcp
.command("disable")
.description(t("mcp.disable.description"))
.action(async (opts, cmd) => {
const globalOpts = cmd.parent.optsWithGlobals();
const exitCode = await runMcpDisableCommand({ ...opts, output: globalOpts.output });
if (exitCode !== 0) process.exit(exitCode);
});
// 5.1 — mcp call + mcp scopes
mcp
.command("call <tool> [argsJson]")
@@ -61,10 +82,15 @@ export function registerMcp(program) {
? JSON.parse(argsPositional)
: {};
const exitCode = await runMcpCallCommand(tool, args, {
...opts,
stream: opts.stream,
}, globalOpts);
const exitCode = await runMcpCallCommand(
tool,
args,
{
...opts,
stream: opts.stream,
},
globalOpts
);
if (exitCode !== 0) process.exit(exitCode);
});
@@ -127,7 +153,9 @@ async function mcpJsonRpcCall(tool, args, { stream = false, globalOpts = {} } =
if (!initRes.ok) {
const text = await initRes.text().catch(() => "");
process.stderr.write(`MCP initialize failed: HTTP ${initRes.status}${text ? `${text}` : ""}\n`);
process.stderr.write(
`MCP initialize failed: HTTP ${initRes.status}${text ? `${text}` : ""}\n`
);
return 1;
}
@@ -227,6 +255,7 @@ export async function runMcpStatusCommand(opts = {}) {
});
if (!res.ok) {
console.log(t("mcp.stopped"));
console.log(t("mcp.stoppedHint"));
return 0;
}
@@ -240,6 +269,9 @@ export async function runMcpStatusCommand(opts = {}) {
const transport = status.transport || "stdio";
const online = status.online ?? status.running;
console.log(online ? t("mcp.running", { transport }) : t("mcp.stopped"));
if (!online && status.enabled === false) {
console.log(t("mcp.stoppedHint"));
}
if (status.toolsCount !== undefined) console.log(` Tools: ${status.toolsCount}`);
if (status.scopes?.length) {
console.log(" Scopes:");
@@ -270,10 +302,76 @@ export async function runMcpRestartCommand(opts = {}) {
console.log(t("mcp.restarted"));
return 0;
}
console.error(t("common.error", { message: `HTTP ${res.status}` }));
const body = await res.json().catch(() => null);
const message = body?.error || `HTTP ${res.status}`;
console.error(t("common.error", { message }));
return 1;
} catch (err) {
console.error(t("common.error", { message: err instanceof Error ? err.message : String(err) }));
return 1;
}
}
export async function runMcpEnableCommand(opts = {}) {
const serverUp = await isServerUp();
if (!serverUp) {
console.error(t("common.serverOffline"));
return 1;
}
if (opts.transport && !VALID_MCP_TRANSPORTS.includes(opts.transport)) {
console.error(
t("common.error", {
message: `Invalid transport '${opts.transport}'. Valid: ${VALID_MCP_TRANSPORTS.join(", ")}`,
})
);
return 1;
}
try {
const body = { mcpEnabled: true };
if (opts.transport) body.mcpTransport = opts.transport;
const res = await apiFetch("/api/settings", {
method: "PATCH",
body,
retry: false,
acceptNotOk: true,
});
if (!res.ok) {
console.error(t("common.error", { message: `HTTP ${res.status}` }));
return 1;
}
console.log(t("mcp.enabled"));
return 0;
} catch (err) {
console.error(t("common.error", { message: err instanceof Error ? err.message : String(err) }));
return 1;
}
}
export async function runMcpDisableCommand(opts = {}) {
const serverUp = await isServerUp();
if (!serverUp) {
console.error(t("common.serverOffline"));
return 1;
}
try {
const res = await apiFetch("/api/settings", {
method: "PATCH",
body: { mcpEnabled: false },
retry: false,
acceptNotOk: true,
});
if (!res.ok) {
console.error(t("common.error", { message: `HTTP ${res.status}` }));
return 1;
}
console.log(t("mcp.disabled"));
return 0;
} catch (err) {
console.error(t("common.error", { message: err instanceof Error ? err.message : String(err) }));
return 1;
}
}

View File

@@ -6,7 +6,8 @@ export function registerRestart(program) {
program
.command("restart")
.description(t("restart.description"))
.option("--port <port>", t("serve.port"), "20128")
// No Commander default: runServe() falls back to PORT, then 20128 (#7049).
.option("--port <port>", t("serve.port"))
.action(async (opts) => {
const exitCode = await runRestartCommand(opts);
if (exitCode !== 0) process.exit(exitCode);

View File

@@ -281,7 +281,8 @@ export async function runServe(opts = {}) {
return runDaemon(serverJs, env, memoryLimit, dashboardPort, apiPort);
}
if (opts.noRecovery) {
// Commander stores `--no-recovery` as `recovery === false`, never as `noRecovery`.
if (opts.recovery === false || opts.noRecovery === true) {
return runWithoutRecovery(
serverJs,
env,

View File

@@ -30,7 +30,7 @@
"opencode": "ከOpenCode ጋር የተካተተውን @omniroute/opencode-plugin ጫን እና አዋቅር"
},
"doctor": {
"title": "OmniRoute Doctor",
"title": "OmniRoute ዶክተር",
"dbOk": "የውሂብ ጎታ፦ ደህና ({path})",
"dbMissing": "የውሂብ ጎታ፦ አልተጀመረም — `omniroute setup` ያስኪዱ",
"portOk": "ወደብ {port}፦ ይገኛል",
@@ -256,6 +256,7 @@
"max_restarts": "ከመተው በፊት በ30s ውስጥ የሚፈቀደው ከፍተኛ የብልሽት ዳግም መጀመር ብዛት (ነባሪ፦ 2)",
"tray": "በስርዓት ትሪ ውስጥ አስጀምር (ለዴስክቶፕ ብቻ፣ በፈቃድ)",
"no_tray": "የስርዓት ትሪ አዶን አሰናክል",
"ready_timeout": "የዝግጁነት ምርመራ ጊዜ ማብቂያ በሚሊሰከንድ (እንዲሁም OMNIROUTE_READY_TIMEOUT_MS፣ ነባሪ 60000)",
"tls_cert": "HTTPSን ለማቅረብ የTLS ሰርተፍኬት (PEM) ዱካ (OMNIROUTE_TLS_CERTም ጭምር)",
"tls_key": "HTTPSን ለማቅረብ የTLS የግል ቁልፍ (PEM) ዱካ (OMNIROUTE_TLS_KEYም ጭምር)"
},
@@ -347,6 +348,16 @@
"running": "MCP ሰርቨር እየሰራ ነው ({transport})",
"stopped": "MCP ሰርቨር ቆሟል።",
"restarted": "MCP ሰርቨር እንደገና ተጀምሯል።",
"stoppedHint": "ለማብራት `omniroute mcp enable`ን ያሂዱ።",
"enabled": "MCP አገልጋይ ነቅቷል።",
"disabled": "MCP አገልጋይ ተሰናክሏል።",
"enable": {
"description": "MCP አገልጋዩን አንቃ",
"transport": "ጥቅም ላይ የሚውል ማጓጓዣ፦ stdio|sse|streamable-http"
},
"disable": {
"description": "MCP አገልጋዩን አሰናክል"
},
"call": {
"description": "የMCP መሣሪያን በቀጥታ ይጥሩ",
"args": "የJSON ነጋሪ እሴቶች ኦብጀክት (በቦታው)",

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -256,6 +256,7 @@
"max_restarts": "Μέγιστος αριθμός επανεκκινήσεων λόγω σφάλματος εντός 30 δευτερολέπτων πριν την οριστική διακοπή (προεπιλογή: 2)",
"tray": "Εκκίνηση στην περιοχή ειδοποιήσεων (μόνο για επιτραπέζιους υπολογιστές, προαιρετικό)",
"no_tray": "Απενεργοποίηση εικονιδίου περιοχής ειδοποιήσεων",
"ready_timeout": "Χρονικό όριο λήξης του ελέγχου ετοιμότητας σε ms (επίσης OMNIROUTE_READY_TIMEOUT_MS, προεπιλογή 60000)",
"tls_cert": "Διαδρομή προς πιστοποιητικό TLS (PEM) για εξυπηρέτηση HTTPS (επίσης OMNIROUTE_TLS_CERT)",
"tls_key": "Διαδρομή προς το ιδιωτικό κλειδί TLS (PEM) για εξυπηρέτηση HTTPS (επίσης OMNIROUTE_TLS_KEY)"
},
@@ -347,6 +348,16 @@
"running": "Ο διακομιστής MCP εκτελείται ({transport})",
"stopped": "Ο διακομιστής MCP διακόπηκε.",
"restarted": "Ο διακομιστής MCP επανεκκινήθηκε.",
"stoppedHint": "Εκτελέστε `omniroute mcp enable` για να το ενεργοποιήσετε.",
"enabled": "Ο διακομιστής MCP ενεργοποιήθηκε.",
"disabled": "Ο διακομιστής MCP απενεργοποιήθηκε.",
"enable": {
"description": "Ενεργοποίηση του διακομιστή MCP",
"transport": "Μέθοδος μεταφοράς προς χρήση: stdio|sse|streamable-http"
},
"disable": {
"description": "Απενεργοποίηση του διακομιστή MCP"
},
"call": {
"description": "Άμεση κλήση εργαλείου MCP",
"args": "Αντικείμενο ορισμάτων JSON (ενσωματωμένο)",
@@ -1093,7 +1104,7 @@
}
},
"combo": {
"title": "Combos",
"title": "Συνδυασμοί",
"switched": "Ενεργό combo: {name}",
"created": "Δημιουργήθηκε combo: {name}",
"deleted": "Διαγράφηκε combo: {name}",

View File

@@ -348,6 +348,16 @@
"running": "MCP server running ({transport})",
"stopped": "MCP server stopped.",
"restarted": "MCP server restarted.",
"stoppedHint": "Run `omniroute mcp enable` to turn it on.",
"enabled": "MCP server enabled.",
"disabled": "MCP server disabled.",
"enable": {
"description": "Enable the MCP server",
"transport": "Transport to use: stdio|sse|streamable-http"
},
"disable": {
"description": "Disable the MCP server"
},
"call": {
"description": "Invoke an MCP tool directly",
"args": "JSON arguments object (inline)",

File diff suppressed because it is too large Load Diff

View File

@@ -30,7 +30,7 @@
"opencode": "Installi ja seadista OpenCode'i jaoks kaasas olev @omniroute/opencode-plugin"
},
"doctor": {
"title": "OmniRoute Doctor",
"title": "OmniRoute'i diagnostika",
"dbOk": "Andmebaas: korras ({path})",
"dbMissing": "Andmebaas: pole lähtestatud — käivita `omniroute setup`",
"portOk": "Port {port}: saadaval",
@@ -256,6 +256,7 @@
"max_restarts": "Maksimaalne krahhijärgsete taaskäivituste arv 30 sekundi jooksul enne alla andmist (vaikimisi: 2)",
"tray": "Käivita süsteemisalves (ainult töölaual, valikuline)",
"no_tray": "Keela süsteemisalve ikoon",
"ready_timeout": "Valmisolekukontrolli ajalõpp millisekundites (ka OMNIROUTE_READY_TIMEOUT_MS, vaikimisi 60000)",
"tls_cert": "TLS-sertifikaadi (PEM) tee HTTPS-i teenindamiseks (ka OMNIROUTE_TLS_CERT)",
"tls_key": "TLS privaatvõtme (PEM) tee HTTPS-i teenindamiseks (ka OMNIROUTE_TLS_KEY)"
},
@@ -347,6 +348,16 @@
"running": "MCP server töötab ({transport})",
"stopped": "MCP server peatatud.",
"restarted": "MCP server taaskäivitatud.",
"stoppedHint": "Selle sisselülitamiseks käivitage `omniroute mcp enable`.",
"enabled": "MCP-server on lubatud.",
"disabled": "MCP-server on keelatud.",
"enable": {
"description": "Luba MCP-server",
"transport": "Kasutatav transport: stdio|sse|streamable-http"
},
"disable": {
"description": "Keela MCP-server"
},
"call": {
"description": "Käivita MCP tööriist otse",
"args": "JSON argumentide objekt (otseselt sisestatud)",

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -256,6 +256,7 @@
"max_restarts": "Uaslíon atosuithe titim laistigh de 30 sula bhfágann tú suas (réamhshocrú: 2)",
"tray": "Tosaigh i mbosca córas (leicsitheoir amháin, roghnach)",
"no_tray": "Díchumasaigh deilbhín bosca córas",
"ready_timeout": "Teorainn ama an tseiceála ullmhachta i ms (OMNIROUTE_READY_TIMEOUT_MS freisin, réamhshocrú 60000)",
"tls_cert": "Cosán go dtí deimhniú TLS (PEM) chun HTTPS a sheirbhísiú (freisin OMNIROUTE_TLS_CERT)",
"tls_key": "Cosán go dtí eochair phríobháideach TLS (PEM) chun HTTPS a sheirbhísiú (freisin OMNIROUTE_TLS_KEY)"
},
@@ -347,6 +348,16 @@
"running": "Tá freastalaí MCP ag rith ({transport})",
"stopped": "Tá freastalaí MCP stoptha.",
"restarted": "Tá freastalaí MCP atosaíte.",
"stoppedHint": "Rith `omniroute mcp enable` chun é a chur ar siúl.",
"enabled": "Freastalaí MCP cumasaithe.",
"disabled": "Freastalaí MCP díchumasaithe.",
"enable": {
"description": "Cumasaigh an freastalaí MCP",
"transport": "Iompar le húsáid: stdio|sse|streamable-http"
},
"disable": {
"description": "Díchumasaigh an freastalaí MCP"
},
"call": {
"description": "Glaoigh ar uirlis MCP go díreach",
"args": "Réimse argóintí JSON (inlíne)",

File diff suppressed because it is too large Load Diff

View File

@@ -256,6 +256,7 @@
"max_restarts": "Matsakaicin sake farawa bayan rushewa cikin daƙiƙa 30 kafin a haƙura (tsoho: 2)",
"tray": "Fara a tiren tsarin (na kwamfutar tebur kawai, sai an zaɓa)",
"no_tray": "Kashe gunkin tiren tsarin",
"ready_timeout": "Lokacin ƙarewar gwajin shiri a ms (har ila yau OMNIROUTE_READY_TIMEOUT_MS, tsoho 60000)",
"tls_cert": "Hanyar zuwa takardar shaidar TLS (PEM) don samar da HTTPS (haka kuma OMNIROUTE_TLS_CERT)",
"tls_key": "Hanyar zuwa maɓallin sirri na TLS (PEM) don samar da HTTPS (haka kuma OMNIROUTE_TLS_KEY)"
},
@@ -347,6 +348,16 @@
"running": "Sabar MCP tana aiki ({transport})",
"stopped": "An dakatar da sabar MCP.",
"restarted": "An sake kunna sabar MCP.",
"stoppedHint": "Gudanar da `omniroute mcp enable` don kunna shi.",
"enabled": "An kunna sabar MCP.",
"disabled": "An kashe sabar MCP.",
"enable": {
"description": "Kunna sabar MCP",
"transport": "Hanyar jigilar da za a yi amfani da ita: stdio|sse|streamable-http"
},
"disable": {
"description": "Kashe sabar MCP"
},
"call": {
"description": "Kira kayan aikin MCP kai tsaye",
"args": "Abun hujjojin JSON (a cikin layi)",
@@ -475,7 +486,7 @@
}
},
"tunnel": {
"title": "Tunnels",
"title": "Ramuka",
"listDescription": "Jera tunnels masu aiki",
"createDescription": "Ƙirƙiri tunnel",
"created": "An ƙirƙiri tunnel: {url}",

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -256,6 +256,7 @@
"max_restarts": "Maksimalni broj ponovnih pokretanja nakon pada unutar 30 s prije odustajanja (zadano: 2)",
"tray": "Pokretanje u programskoj traci (samo za stolna računala, po izboru)",
"no_tray": "Onemogući ikonu programske trake",
"ready_timeout": "Vremensko ograničenje provjere spremnosti u ms (također OMNIROUTE_READY_TIMEOUT_MS, zadano 60000)",
"tls_cert": "Putanja do TLS certifikata (PEM) za posluživanje HTTPS-a (također OMNIROUTE_TLS_CERT)",
"tls_key": "Putanja do TLS privatnog ključa (PEM) za posluživanje HTTPS-a (također OMNIROUTE_TLS_KEY)"
},
@@ -347,6 +348,16 @@
"running": "MCP poslužitelj je pokrenut ({transport})",
"stopped": "MCP poslužitelj je zaustavljen.",
"restarted": "MCP poslužitelj je ponovo pokrenut.",
"stoppedHint": "Pokrenite `omniroute mcp enable` da biste ga uključili.",
"enabled": "MCP poslužitelj omogućen.",
"disabled": "MCP poslužitelj onemogućen.",
"enable": {
"description": "Omogući MCP poslužitelj",
"transport": "Prijenos koji će se koristiti: stdio|sse|streamable-http"
},
"disable": {
"description": "Onemogući MCP poslužitelj"
},
"call": {
"description": "Izravno pozovi MCP alat",
"args": "JSON objekt argumenata (unutarnji)",

File diff suppressed because it is too large Load Diff

View File

@@ -256,6 +256,7 @@
"max_restarts": "Վթարից հետո վերագործարկումների առավելագույն քանակը 30 վրկ-ի ընթացքում՝ մինչև փորձերը դադարեցնելը (լռելյայն՝ 2)",
"tray": "Գործարկել համակարգային սկուտեղում (միայն աշխատասեղանի տարբերակում, ըստ ցանկության)",
"no_tray": "Անջատել համակարգային սկուտեղի պատկերակը",
"ready_timeout": "Պատրաստության ստուգման սպասաժամը՝ մվ-ով (նաև OMNIROUTE_READY_TIMEOUT_MS, լռելյայն՝ 60000)",
"tls_cert": "HTTPS սպասարկելու համար TLS վկայագրի (PEM) ուղին (նաև՝ OMNIROUTE_TLS_CERT)",
"tls_key": "HTTPS սպասարկելու համար TLS գաղտնի բանալու (PEM) ուղին (նաև՝ OMNIROUTE_TLS_KEY)"
},
@@ -347,6 +348,16 @@
"running": "MCP սերվերը գործարկված է ({transport})",
"stopped": "MCP սերվերը կանգնեցվել է։",
"restarted": "MCP սերվերը վերագործարկվել է։",
"stoppedHint": "Այն միացնելու համար գործարկեք `omniroute mcp enable` հրամանը։",
"enabled": "MCP սերվերը միացված է։",
"disabled": "MCP սերվերն անջատված է։",
"enable": {
"description": "Միացնել MCP սերվերը",
"transport": "Օգտագործվող փոխադրման եղանակը՝ stdio|sse|streamable-http"
},
"disable": {
"description": "Անջատել MCP սերվերը"
},
"call": {
"description": "Անմիջապես կանչել MCP գործիք",
"args": "JSON արգումենտների օբյեկտ (ներտողային)",

File diff suppressed because it is too large Load Diff

View File

@@ -256,6 +256,7 @@
"max_restarts": "Ọnụọgụ mmalitegharị kachasị elu mgbe ọ dara n'ime 30s tupu ịkwụsị mbọ (ndabara: 2)",
"tray": "Malite na tray sistemụ (naanị na desktọpụ, ma ọ bụrụ na ahọrọ ya)",
"no_tray": "Gbanyụọ akara ngosi tray sistemụ",
"ready_timeout": "Oge njedebe nyocha ịdị njikere na ms (yana OMNIROUTE_READY_TIMEOUT_MS, ndabara bụ 60000)",
"tls_cert": "Ụzọ gaa na asambodo TLS (PEM) iji nye HTTPS (nakwa OMNIROUTE_TLS_CERT)",
"tls_key": "Ụzọ gaa na igodo nzuzo TLS (PEM) iji nye HTTPS (nakwa OMNIROUTE_TLS_KEY)"
},
@@ -347,6 +348,16 @@
"running": "Sava MCP na-arụ ọrụ ({transport})",
"stopped": "Sava MCP akwụsịla.",
"restarted": "Amalitegharịrị sava MCP.",
"stoppedHint": "Gbaa `omniroute mcp enable` iji gbanye ya.",
"enabled": "Agbanyela sava MCP.",
"disabled": "Agbanyụọla sava MCP.",
"enable": {
"description": "Gbanye sava MCP",
"transport": "Usoro mbufe a ga-eji: stdio|sse|streamable-http"
},
"disable": {
"description": "Gbanyụọ sava MCP"
},
"call": {
"description": "Kpọọ ngwa MCP ozugbo",
"args": "Ihe arụmụka JSON (n'ime ahịrị)",

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -256,6 +256,7 @@
"max_restarts": "30 წამში ავარიული გათიშვის შემდეგ ხელახალი გაშვებების მაქსიმალური რაოდენობა დანებებამდე (ნაგულისხმევი: 2)",
"tray": "სისტემურ უჯრაში გაშვება (მხოლოდ დესკტოპისთვის, სურვილისამებრ)",
"no_tray": "სისტემური უჯრის ხატულის გამორთვა",
"ready_timeout": "მზადყოფნის შემოწმების მოლოდინის დრო მილიწამებში (ასევე OMNIROUTE_READY_TIMEOUT_MS, ნაგულისხმევად 60000)",
"tls_cert": "HTTPS-ისთვის TLS სერტიფიკატის (PEM) გზა (ასევე OMNIROUTE_TLS_CERT)",
"tls_key": "HTTPS-ისთვის TLS პირადი გასაღების (PEM) გზა (ასევე OMNIROUTE_TLS_KEY)"
},
@@ -347,6 +348,16 @@
"running": "MCP სერვერი მუშაობს ({transport})",
"stopped": "MCP სერვერი გაჩერდა.",
"restarted": "MCP სერვერი გადაიტვირთა.",
"stoppedHint": "მის ჩასართავად გაუშვით `omniroute mcp enable`.",
"enabled": "MCP სერვერი ჩართულია.",
"disabled": "MCP სერვერი გამორთულია.",
"enable": {
"description": "MCP სერვერის ჩართვა",
"transport": "გამოსაყენებელი ტრანსპორტი: stdio|sse|streamable-http"
},
"disable": {
"description": "MCP სერვერის გამორთვა"
},
"call": {
"description": "MCP ხელსაწყოს პირდაპირ გამოძახება",
"args": "JSON არგუმენტების ობიექტი (ჩაშენებული)",

View File

@@ -86,7 +86,7 @@
}
},
"keys": {
"title": "API Keys",
"title": "សោ API",
"addDescription": "បន្ថែម ឬធ្វើបច្ចុប្បន្នភាព API key សម្រាប់អ្នកផ្តល់សេវា",
"listDescription": "រាយ API keys ដែលបានកំណត់រចនាសម្ព័ន្ធទាំងអស់",
"removeDescription": "លុប API key សម្រាប់អ្នកផ្តល់សេវា",
@@ -226,7 +226,7 @@
"description": "ផ្ញើ chat prompt មួយលើកទៅ OmniRoute",
"file": "អាន prompt ពីឯកសារ",
"stdin": "អាន prompt ពី stdin",
"system": "System prompt",
"system": "ប្រូមប្រព័ន្ធ",
"model": "Model ID (លំនាំដើម៖ auto)",
"max_tokens": "ចំនួន token អតិបរមាក្នុងការឆ្លើយតប",
"temperature": "សីតុណ្ហភាព sampling (02)",
@@ -256,6 +256,7 @@
"max_restarts": "ចំនួនអតិបរមានៃការចាប់ផ្តើមឡើងវិញបន្ទាប់ពីគាំងក្នុងរយៈពេល 30s មុនពេលបោះបង់ (លំនាំដើម៖ 2)",
"tray": "ចាប់ផ្ដើមក្នុងថាសប្រព័ន្ធ (សម្រាប់កុំព្យូទ័រលើតុប៉ុណ្ណោះ និងត្រូវជ្រើសរើសបើក)",
"no_tray": "បិទរូបតំណាងថាសប្រព័ន្ធ",
"ready_timeout": "រយៈពេលអស់ពេលនៃការត្រួតពិនិត្យភាពរួចរាល់ គិតជាមិល្លីវិនាទី (ក៏អាចប្រើ OMNIROUTE_READY_TIMEOUT_MS ផងដែរ, លំនាំដើម 60000)",
"tls_cert": "ផ្លូវទៅកាន់វិញ្ញាបនបត្រ TLS (PEM) សម្រាប់បម្រើ HTTPS (ក៏ជា OMNIROUTE_TLS_CERT ផងដែរ)",
"tls_key": "ផ្លូវទៅកាន់សោឯកជន TLS (PEM) សម្រាប់បម្រើ HTTPS (ក៏ជា OMNIROUTE_TLS_KEY ផងដែរ)"
},
@@ -347,6 +348,16 @@
"running": "ម៉ាស៊ីនមេ MCP កំពុងដំណើរការ ({transport})",
"stopped": "ម៉ាស៊ីនមេ MCP បានឈប់ដំណើរការ។",
"restarted": "ម៉ាស៊ីនមេ MCP បានចាប់ផ្តើមឡើងវិញ។",
"stoppedHint": "ដំណើរការ `omniroute mcp enable` ដើម្បីបើកវា។",
"enabled": "ម៉ាស៊ីនមេ MCP ត្រូវបានបើក។",
"disabled": "ម៉ាស៊ីនមេ MCP ត្រូវបានបិទ។",
"enable": {
"description": "បើកម៉ាស៊ីនមេ MCP",
"transport": "មធ្យោបាយបញ្ជូនដែលត្រូវប្រើ៖ stdio|sse|streamable-http"
},
"disable": {
"description": "បិទម៉ាស៊ីនមេ MCP"
},
"call": {
"description": "ហៅប្រើឧបករណ៍ MCP ដោយផ្ទាល់",
"args": "អាប់ជិចអាគុយម៉ង់ JSON (ក្នុងបន្ទាត់)",

View File

@@ -256,6 +256,7 @@
"max_restarts": "ಬಿಟ್ಟುಕೊಡುವ ಮೊದಲು 30s ಒಳಗೆ ಕ್ರ್ಯಾಶ್ನಿಂದ ಗರಿಷ್ಠ ಮರುಪ್ರಾರಂಭಗಳ ಸಂಖ್ಯೆ (ಡೀಫಾಲ್ಟ್: 2)",
"tray": "ಸಿಸ್ಟಮ್ ಟ್ರೇಯಲ್ಲಿ ಪ್ರಾರಂಭಿಸಿ (ಡೆಸ್ಕ್ಟಾಪ್ ಮಾತ್ರ, ಆಯ್ಕೆ ಮಾಡಿದಲ್ಲಿ)",
"no_tray": "ಸಿಸ್ಟಮ್ ಟ್ರೇ ಐಕಾನ್ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿ",
"ready_timeout": "ಸಿದ್ಧತಾ ಪ್ರೋಬ್ ಕಾಲಮಿತಿ ಮಿಲಿಸೆಕೆಂಡುಗಳಲ್ಲಿ (OMNIROUTE_READY_TIMEOUT_MS ಕೂಡ, ಡೀಫಾಲ್ಟ್ 60000)",
"tls_cert": "HTTPS ಒದಗಿಸಲು TLS ಪ್ರಮಾಣಪತ್ರದ (PEM) ಪಥ (OMNIROUTE_TLS_CERT ಸಹ)",
"tls_key": "HTTPS ಒದಗಿಸಲು TLS ಖಾಸಗಿ ಕೀಲಿಯ (PEM) ಪಥ (OMNIROUTE_TLS_KEY ಸಹ)"
},
@@ -347,6 +348,16 @@
"running": "MCP ಸರ್ವರ್ ಚಾಲನೆಯಲ್ಲಿದೆ ({transport})",
"stopped": "MCP ಸರ್ವರ್ ನಿಲ್ಲಿಸಲಾಗಿದೆ.",
"restarted": "MCP ಸರ್ವರ್ ಮರುಪ್ರಾರಂಭಿಸಲಾಗಿದೆ.",
"stoppedHint": "ಇದನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಲು `omniroute mcp enable` ಅನ್ನು ಚಲಾಯಿಸಿ.",
"enabled": "MCP ಸರ್ವರ್ ಸಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ.",
"disabled": "MCP ಸರ್ವರ್ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ.",
"enable": {
"description": "MCP ಸರ್ವರ್ ಅನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಿ",
"transport": "ಬಳಸಬೇಕಾದ ಟ್ರಾನ್ಸ್ಪೋರ್ಟ್: stdio|sse|streamable-http"
},
"disable": {
"description": "MCP ಸರ್ವರ್ ಅನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿ"
},
"call": {
"description": "MCP ಟೂಲ್ ಅನ್ನು ನೇರವಾಗಿ ಆಹ್ವಾನಿಸಿ",
"args": "JSON ಆರ್ಗ್ಯುಮೆಂಟ್ಗಳ ಆಬ್ಜೆಕ್ಟ್ (ಇನ್ಲೈನ್)",

File diff suppressed because it is too large Load Diff

View File

@@ -256,6 +256,7 @@
"max_restarts": "Didžiausias paleidimų iš naujo po strigties skaičius per 30 s prieš nutraukiant bandymus (numatyta: 2)",
"tray": "Paleisti sistemos dėkle (tik darbalaukio programoje, pasirenkama)",
"no_tray": "Išjungti sistemos dėklo piktogramą",
"ready_timeout": "Parengties patikros skirtasis laikas ms (taip pat OMNIROUTE_READY_TIMEOUT_MS, numatytoji reikšmė 60000)",
"tls_cert": "Kelias į TLS sertifikatą (PEM), skirtą HTTPS teikti (taip pat OMNIROUTE_TLS_CERT)",
"tls_key": "Kelias į privatųjį TLS raktą (PEM), skirtą HTTPS teikti (taip pat OMNIROUTE_TLS_KEY)"
},
@@ -347,6 +348,16 @@
"running": "MCP serveris veikia ({transport})",
"stopped": "MCP serveris sustabdytas.",
"restarted": "MCP serveris paleistas iš naujo.",
"stoppedHint": "Norėdami jį įjungti, paleiskite `omniroute mcp enable`.",
"enabled": "MCP serveris įjungtas.",
"disabled": "MCP serveris išjungtas.",
"enable": {
"description": "Įjungti MCP serverį",
"transport": "Naudotinas perdavimo būdas: stdio|sse|streamable-http"
},
"disable": {
"description": "Išjungti MCP serverį"
},
"call": {
"description": "Tiesiogiai iškviesti MCP įrankį",
"args": "JSON argumentų objektas (įterptasis)",

View File

@@ -256,6 +256,7 @@
"max_restarts": "Maksimālās avāriju restartēšanas 30 sekunžu laikā pirms padoties (noklusējums: 2)",
"tray": "Sākt sistēmas tray (tikai darbvirsmas, izvēlēties)",
"no_tray": "Atspējot sistēmas tray ikonu",
"ready_timeout": "Gatavības pārbaudes taimauts milisekundēs (arī OMNIROUTE_READY_TIMEOUT_MS, noklusējuma vērtība 60000)",
"tls_cert": "Ceļš uz TLS sertifikātu (PEM) HTTPS apkalpošanai (arī OMNIROUTE_TLS_CERT)",
"tls_key": "Ceļš uz TLS privāto atslēgu (PEM) HTTPS apkalpošanai (arī OMNIROUTE_TLS_KEY)"
},
@@ -347,6 +348,16 @@
"running": "MCP serveris darbojas ({transport})",
"stopped": "MCP serveris apturēts.",
"restarted": "MCP serveris restartēts.",
"stoppedHint": "Lai to ieslēgtu, palaidiet `omniroute mcp enable`.",
"enabled": "MCP serveris ir iespējots.",
"disabled": "MCP serveris ir atspējots.",
"enable": {
"description": "Iespējot MCP serveri",
"transport": "Izmantojamais transports: stdio|sse|streamable-http"
},
"disable": {
"description": "Atspējot MCP serveri"
},
"call": {
"description": "Izsaukt MCP rīku tieši",
"args": "JSON argumentu objekts (iekšējais)",

View File

@@ -256,6 +256,7 @@
"max_restarts": "ഉപേക്ഷിക്കുന്നതിന് മുമ്പ് 30s-നുള്ളിൽ അനുവദിക്കുന്ന പരമാവധി ക്രാഷ് പുനരാരംഭങ്ങൾ (ഡിഫോൾട്ട്: 2)",
"tray": "സിസ്റ്റം ട്രേയിൽ ആരംഭിക്കുക (ഡെസ്ക്ടോപ്പിൽ മാത്രം, സ്വമേധയാ തിരഞ്ഞെടുക്കാവുന്നത്)",
"no_tray": "സിസ്റ്റം ട്രേ ഐക്കൺ പ്രവർത്തനരഹിതമാക്കുക",
"ready_timeout": "റെഡിനസ് പ്രോബ് സമയപരിധി ms-ൽ (OMNIROUTE_READY_TIMEOUT_MS എന്നതും, സ്ഥിരസ്ഥിതി 60000)",
"tls_cert": "HTTPS നൽകുന്നതിനുള്ള TLS സർട്ടിഫിക്കറ്റിന്റെ (PEM) പാത (OMNIROUTE_TLS_CERT-ലും)",
"tls_key": "HTTPS നൽകുന്നതിനുള്ള TLS സ്വകാര്യ കീയുടെ (PEM) പാത (OMNIROUTE_TLS_KEY-ലും)"
},
@@ -347,6 +348,16 @@
"running": "MCP സെർവർ പ്രവർത്തിക്കുന്നു ({transport})",
"stopped": "MCP സെർവർ നിർത്തി.",
"restarted": "MCP സെർവർ പുനരാരംഭിച്ചു.",
"stoppedHint": "ഇത് പ്രവർത്തനക്ഷമമാക്കാൻ `omniroute mcp enable` പ്രവർത്തിപ്പിക്കുക.",
"enabled": "MCP സെർവർ പ്രവർത്തനക്ഷമമാക്കി.",
"disabled": "MCP സെർവർ പ്രവർത്തനരഹിതമാക്കി.",
"enable": {
"description": "MCP സെർവർ പ്രവർത്തനക്ഷമമാക്കുക",
"transport": "ഉപയോഗിക്കേണ്ട ട്രാൻസ്പോർട്ട്: stdio|sse|streamable-http"
},
"disable": {
"description": "MCP സെർവർ പ്രവർത്തനരഹിതമാക്കുക"
},
"call": {
"description": "ഒരു MCP ടൂൾ നേരിട്ട് പ്രവർത്തിപ്പിക്കുക",
"args": "JSON ആർഗ്യുമെന്റുകളുടെ ഒബ്ജക്റ്റ് (ഇൻലൈൻ)",

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -256,6 +256,7 @@
"max_restarts": "L-ogħla numru ta' restarts wara crash fi żmien 30s qabel ma jċedi (default: 2)",
"tray": "Bda' fil-caffettar tal-pajjiż (biss desktop, għażla)",
"no_tray": "Iddiżattiva l-ikona tal-caffettar tal-pajjiż",
"ready_timeout": "Limitu taż-żmien tal-verifika tat-tħejjija f'ms (ukoll OMNIROUTE_READY_TIMEOUT_MS, valur predefinit 60000)",
"tls_cert": "Triq għal ċertifikat TLS (PEM) biex isservi HTTPS (ukoll OMNIROUTE_TLS_CERT)",
"tls_key": "Triq għas-sieqa privata tal-TLS (PEM) biex isservi HTTPS (ukoll OMNIROUTE_TLS_KEY)"
},
@@ -347,6 +348,16 @@
"running": "Servier MCP qed jaħdem ({transport})",
"stopped": "Servier MCP waqaf.",
"restarted": "Servier MCP restartat.",
"stoppedHint": "Ħaddem `omniroute mcp enable` biex tattivah.",
"enabled": "Is-server MCP huwa attivat.",
"disabled": "Is-server MCP huwa diżattivat.",
"enable": {
"description": "Attiva s-server MCP",
"transport": "Trasport li għandu jintuża: stdio|sse|streamable-http"
},
"disable": {
"description": "Iddiżattiva s-server MCP"
},
"call": {
"description": "Sejjaħ għodda MCP direttament",
"args": "Oġġett argomenti JSON (internament)",

View File

@@ -26,7 +26,7 @@
"testFailed": "Provider စမ်းသပ်မှု မအောင်မြင်ပါ: {error}",
"loginEnabled": "လော့ဂ်အင်: ဖွင့်ထားသည် (စကားဝှက်ကို အပ်ဒိတ်လုပ်ပြီး)",
"loginDisabled": "လော့ဂ်အင်: ပိတ်ထားသည်",
"providerInfo": "Provider: {info}",
"providerInfo": "ပံ့ပိုးသူ: {info}",
"opencode": "OpenCode အတွက် တွဲဖက်ပါရှိသော @omniroute/opencode-plugin ကို ထည့်သွင်းပြီး စီစဉ်သတ်မှတ်ရန်"
},
"doctor": {
@@ -41,7 +41,7 @@
"warnings": "သတိပေးချက် {count} ခု — အပေါ်တွင် ကြည့်ပါ။"
},
"providers": {
"title": "Providers",
"title": "ပံ့ပိုးသူများ",
"noProviders": "မည်သည့် provider ကိုမျှ စီစဉ်သတ်မှတ်မထားပါ။ လုပ်ဆောင်ရန်: omniroute setup",
"testing": "{name} ကို စမ်းသပ်နေသည်...",
"available": "အသုံးပြုနိုင်သော provider {count} ခု",
@@ -86,7 +86,7 @@
}
},
"keys": {
"title": "API Keys",
"title": "API သော့များ",
"addDescription": "ဝန်ဆောင်မှုပေးသူတစ်ခုအတွက် API key ကို ထည့်ရန် သို့မဟုတ် အပ်ဒိတ်လုပ်ရန်",
"listDescription": "ပြင်ဆင်သတ်မှတ်ထားသော API key အားလုံးကို စာရင်းပြုစုရန်",
"removeDescription": "ဝန်ဆောင်မှုပေးသူတစ်ခုအတွက် API key ကို ဖယ်ရှားရန်",
@@ -152,7 +152,7 @@
"file": "ဖိုင်မှ prompt ကို ဖတ်ပါ",
"stdin": "stdin မှ prompt ကို ဖတ်ပါ",
"model": "Model ID (မူလသတ်မှတ်ချက်: auto)",
"system": "System prompt",
"system": "စနစ်ညွှန်ကြားချက်",
"combo": "သတ်မှတ်ထားသော combo တစ်ခုကို အမည်ဖြင့် မဖြစ်မနေ အသုံးပြုပါ",
"max_tokens": "Response အတွင်း အများဆုံး token အရေအတွက်",
"responses_api": "/v1/chat/completions အစား /v1/responses ကို အသုံးပြုပါ",
@@ -226,7 +226,7 @@
"description": "OmniRoute သို့ တစ်ကြိမ်တည်းသုံး chat prompt ပို့ရန်",
"file": "ဖိုင်မှ prompt ကို ဖတ်ရန်",
"stdin": "stdin မှ prompt ကို ဖတ်ရန်",
"system": "System prompt",
"system": "စနစ်ညွှန်ကြားချက်",
"model": "Model ID (မူလသတ်မှတ်ချက်: auto)",
"max_tokens": "တုံ့ပြန်ချက်တွင် ပါဝင်မည့် token အရေအတွက် အများဆုံး",
"temperature": "နမူနာရွေးချယ်မှု temperature (02)",
@@ -256,6 +256,7 @@
"max_restarts": "လက်လျှော့မတိုင်မီ 30s အတွင်း ပျက်သွား၍ ပြန်လည်စတင်နိုင်သည့် အများဆုံးအကြိမ်ရေ (မူလသတ်မှတ်ချက်: 2)",
"tray": "စနစ် tray တွင် စတင်ပါ (desktop အတွက်သာ၊ ရွေးချယ်ဖွင့်သုံးရန်)",
"no_tray": "စနစ် tray အိုင်ကွန်ကို ပိတ်ပါ",
"ready_timeout": "အဆင်သင့်ဖြစ်မှု စစ်ဆေးချက်၏ အချိန်ကုန်ဆုံးကာလကို ms ဖြင့် သတ်မှတ်ပါ (OMNIROUTE_READY_TIMEOUT_MS လည်းဖြစ်ပြီး မူလတန်ဖိုးမှာ 60000 ဖြစ်သည်)",
"tls_cert": "HTTPS ဖြင့် ဝန်ဆောင်မှုပေးရန် TLS certificate (PEM) သို့ လမ်းကြောင်း (OMNIROUTE_TLS_CERT လည်းဖြစ်သည်)",
"tls_key": "HTTPS ဖြင့် ဝန်ဆောင်မှုပေးရန် TLS private key (PEM) သို့ လမ်းကြောင်း (OMNIROUTE_TLS_KEY လည်းဖြစ်သည်)"
},
@@ -347,6 +348,16 @@
"running": "MCP ဆာဗာ လည်ပတ်နေသည် ({transport})",
"stopped": "MCP ဆာဗာ ရပ်တန့်သွားပါပြီ။",
"restarted": "MCP ဆာဗာကို ပြန်လည်စတင်ပြီးပါပြီ။",
"stoppedHint": "၎င်းကို ဖွင့်ရန် `omniroute mcp enable` ကို လုပ်ဆောင်ပါ။",
"enabled": "MCP ဆာဗာကို ဖွင့်ထားသည်။",
"disabled": "MCP ဆာဗာကို ပိတ်ထားသည်။",
"enable": {
"description": "MCP ဆာဗာကို ဖွင့်ရန်",
"transport": "အသုံးပြုမည့် ပို့ဆောင်မှုနည်းလမ်း: stdio|sse|streamable-http"
},
"disable": {
"description": "MCP ဆာဗာကို ပိတ်ရန်"
},
"call": {
"description": "MCP ကိရိယာတစ်ခုကို တိုက်ရိုက် ခေါ်ယူအသုံးပြုရန်",
"args": "JSON အငြင်းပွားဖွယ်ရာ အရာဝတ္ထု (စာကြောင်းအတွင်း)",
@@ -633,7 +644,7 @@
"content": "Memory စာသားအကြောင်းအရာ",
"file": "ဖိုင်မှ အကြောင်းအရာကို ဖတ်ရန်",
"type": "Memory အမျိုးအစား (မူလသတ်မှတ်ချက်: user)",
"metadata": "JSON metadata object",
"metadata": "JSON မက်တာဒေတာ အရာဝတ္ထု",
"api_key": "API key နှင့် ချိတ်ဆက်ရန်"
},
"clear": {
@@ -667,7 +678,7 @@
},
"start": {
"description": "provider တစ်ခုအတွက် OAuth ခွင့်ပြုချက် flow ကို စတင်ရန်",
"provider": "Provider ID (gemini, copilot, cursor, …)",
"provider": "ပံ့ပိုးသူ ID (gemini, copilot, cursor, …)",
"no_browser": "URL ကိုသာ ဖော်ပြရန် — browser ကို မဖွင့်ပါနှင့်",
"import_system": "စက်တွင်း system config မှ အထောက်အထားများကို အလိုအလျောက် ထည့်သွင်းရန်",
"social": "Social login provider (google|github) — kiro အတွက် လိုအပ်သည်",
@@ -704,7 +715,7 @@
"prompt_file": "ဖိုင်မှ prompt ကို ဖတ်ရန်",
"repo": "လုပ်ဆောင်စရာအတွက် clone ပြုလုပ်မည့် repository URL",
"branch": "အသုံးပြုမည့် branch အမည်",
"metadata": "JSON metadata object"
"metadata": "JSON မက်တာဒေတာ အရာဝတ္ထု"
},
"list": {
"description": "agent လုပ်ဆောင်စရာများကို စာရင်းပြုရန်",
@@ -1268,7 +1279,7 @@
"description": "LLM ဖြင့် အပြန်အလှန် ဆွေးနွေးမှုအကြိမ်များစွာ ပြုလုပ်နိုင်သော REPL",
"model": "အသုံးပြုမည့် မော်ဒယ် (ပုံသေ- auto)",
"combo": "အသုံးပြုမည့် Combo အမည်",
"system": "System prompt",
"system": "စနစ်ညွှန်ကြားချက်",
"resume": "သိမ်းဆည်းထားသော session ကို အမည်ဖြင့် ပြန်လည်စတင်ရန်"
},
"plugin": {

View File

@@ -226,11 +226,11 @@
"description": "OmniRoute मा एकपटकको chat prompt पठाउनुहोस्",
"file": "file बाट prompt पढ्नुहोस्",
"stdin": "stdin बाट prompt पढ्नुहोस्",
"system": "System prompt",
"system": "प्रणाली प्रम्प्ट",
"model": "Model ID (पूर्वनिर्धारित: auto)",
"max_tokens": "response मा अधिकतम tokens",
"temperature": "sampling temperature (02)",
"top_p": "Top-p nucleus sampling",
"top_p": "Top-p न्युक्लियस नमुना चयन",
"reasoning_effort": "तर्क प्रयासको स्तर (low|medium|high)",
"thinking_budget": "विस्तारित सोचाइको token बजेट",
"combo": "नामद्वारा कुनै विशिष्ट combo प्रयोग गर्न बाध्य पार्नुहोस्",
@@ -256,6 +256,7 @@
"max_restarts": "प्रयास छोड्नुअघि 30s भित्र क्र्यासपछि पुनः सुरु गर्ने अधिकतम सङ्ख्या (पूर्वनिर्धारित: 2)",
"tray": "सिस्टम ट्रेमा सुरु गर्नुहोस् (डेस्कटपमा मात्र, स्वैच्छिक)",
"no_tray": "सिस्टम ट्रे आइकन असक्षम गर्नुहोस्",
"ready_timeout": "मिलिसेकेन्डमा तत्परता प्रोबको समयसीमा (OMNIROUTE_READY_TIMEOUT_MS पनि, पूर्वनिर्धारित 60000)",
"tls_cert": "HTTPS सेवा दिन TLS प्रमाणपत्र (PEM) को पथ (OMNIROUTE_TLS_CERT पनि)",
"tls_key": "HTTPS सेवा दिन TLS निजी कुञ्जी (PEM) को पथ (OMNIROUTE_TLS_KEY पनि)"
},
@@ -347,6 +348,16 @@
"running": "MCP सर्भर चलिरहेको छ ({transport})",
"stopped": "MCP सर्भर रोकियो।",
"restarted": "MCP सर्भर पुनः सुरु गरियो।",
"stoppedHint": "यसलाई सक्रिय गर्न `omniroute mcp enable` चलाउनुहोस्।",
"enabled": "MCP सर्भर सक्रिय गरियो।",
"disabled": "MCP सर्भर निष्क्रिय गरियो।",
"enable": {
"description": "MCP सर्भर सक्रिय गर्नुहोस्",
"transport": "प्रयोग गर्ने ट्रान्सपोर्ट: stdio|sse|streamable-http"
},
"disable": {
"description": "MCP सर्भर निष्क्रिय गर्नुहोस्"
},
"call": {
"description": "MCP उपकरणलाई सिधै आह्वान गर्नुहोस्",
"args": "JSON आर्गुमेन्ट अब्जेक्ट (इनलाइन)",

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -256,6 +256,7 @@
"max_restarts": "ହାର ମାନିବା ପୂର୍ବରୁ 30s ମଧ୍ୟରେ ସର୍ବାଧିକ କ୍ରାଶ୍ ପୁନଃଆରମ୍ଭ ସଂଖ୍ୟା (ଡିଫଲ୍ଟ: 2)",
"tray": "ସିଷ୍ଟମ୍ ଟ୍ରେରେ ଆରମ୍ଭ କରନ୍ତୁ (କେବଳ ଡେସ୍କଟପ୍, ଇଚ୍ଛାଧୀନ)",
"no_tray": "ସିଷ୍ଟମ୍ ଟ୍ରେ ଆଇକନ୍ ଅକ୍ଷମ କରନ୍ତୁ",
"ready_timeout": "ମିଲିସେକେଣ୍ଡରେ ପ୍ରସ୍ତୁତି ପ୍ରୋବ୍ର ସମୟସୀମା (OMNIROUTE_READY_TIMEOUT_MS ମଧ୍ୟ, ଡିଫଲ୍ଟ 60000)",
"tls_cert": "HTTPS ପରିବେଷଣ ପାଇଁ TLS ସର୍ଟିଫିକେଟ୍ (PEM)ର ପଥ (OMNIROUTE_TLS_CERT ମଧ୍ୟ)",
"tls_key": "HTTPS ପରିବେଷଣ ପାଇଁ TLS ବ୍ୟକ୍ତିଗତ କୀ (PEM)ର ପଥ (OMNIROUTE_TLS_KEY ମଧ୍ୟ)"
},
@@ -347,6 +348,16 @@
"running": "MCP ସର୍ଭର ଚାଲୁଛି ({transport})",
"stopped": "MCP ସର୍ଭର ବନ୍ଦ ହୋଇଛି।",
"restarted": "MCP ସର୍ଭର ପୁନଃଚାଲୁ ହୋଇଛି।",
"stoppedHint": "ଏହାକୁ ସକ୍ରିୟ କରିବା ପାଇଁ `omniroute mcp enable` ଚଲାନ୍ତୁ।",
"enabled": "MCP ସର୍ଭର୍ ସକ୍ରିୟ ହୋଇଛି।",
"disabled": "MCP ସର୍ଭର୍ ନିଷ୍କ୍ରିୟ ହୋଇଛି।",
"enable": {
"description": "MCP ସର୍ଭର୍କୁ ସକ୍ରିୟ କରନ୍ତୁ",
"transport": "ବ୍ୟବହାର କରିବାକୁ ଥିବା ପରିବହନ: stdio|sse|streamable-http"
},
"disable": {
"description": "MCP ସର୍ଭର୍କୁ ନିଷ୍କ୍ରିୟ କରନ୍ତୁ"
},
"call": {
"description": "ସିଧାସଳଖ ଏକ MCP ଟୁଲ୍ ଆହ୍ୱାନ କରନ୍ତୁ",
"args": "JSON ଆର୍ଗୁମେଣ୍ଟ ଅବଜେକ୍ଟ (ଇନ୍‌ଲାଇନ୍)",

View File

@@ -256,6 +256,7 @@
"max_restarts": "ਹਾਰ ਮੰਨਣ ਤੋਂ ਪਹਿਲਾਂ 30s ਦੇ ਅੰਦਰ ਕ੍ਰੈਸ਼ ਤੋਂ ਬਾਅਦ ਮੁੜ-ਚਾਲੂ ਕਰਨ ਦੀ ਵੱਧ ਤੋਂ ਵੱਧ ਗਿਣਤੀ (ਡਿਫਾਲਟ: 2)",
"tray": "ਸਿਸਟਮ ਟਰੇ ਵਿੱਚ ਸ਼ੁਰੂ ਕਰੋ (ਸਿਰਫ਼ ਡੈਸਕਟਾਪ, ਚੋਣਵਾਂ)",
"no_tray": "ਸਿਸਟਮ ਟਰੇ ਆਈਕਨ ਅਸਮਰੱਥ ਕਰੋ",
"ready_timeout": "ਰੈਡੀਨੈੱਸ ਪ੍ਰੋਬ ਦਾ ਟਾਈਮਆਉਟ ਮਿਲੀਸਕਿੰਟਾਂ ਵਿੱਚ (OMNIROUTE_READY_TIMEOUT_MS ਵੀ, ਡਿਫਾਲਟ 60000)",
"tls_cert": "HTTPS ਸਰਵ ਕਰਨ ਲਈ TLS ਸਰਟੀਫਿਕੇਟ (PEM) ਦਾ ਪਾਥ (OMNIROUTE_TLS_CERT ਵੀ)",
"tls_key": "HTTPS ਸਰਵ ਕਰਨ ਲਈ TLS ਨਿੱਜੀ ਕੁੰਜੀ (PEM) ਦਾ ਪਾਥ (OMNIROUTE_TLS_KEY ਵੀ)"
},
@@ -347,6 +348,16 @@
"running": "MCP ਸਰਵਰ ਚੱਲ ਰਿਹਾ ਹੈ ({transport})",
"stopped": "MCP ਸਰਵਰ ਰੋਕਿਆ ਗਿਆ।",
"restarted": "MCP ਸਰਵਰ ਮੁੜ ਚਾਲੂ ਕੀਤਾ ਗਿਆ।",
"stoppedHint": "ਇਸਨੂੰ ਚਾਲੂ ਕਰਨ ਲਈ `omniroute mcp enable` ਚਲਾਓ।",
"enabled": "MCP ਸਰਵਰ ਚਾਲੂ ਹੈ।",
"disabled": "MCP ਸਰਵਰ ਬੰਦ ਹੈ।",
"enable": {
"description": "MCP ਸਰਵਰ ਚਾਲੂ ਕਰੋ",
"transport": "ਵਰਤਣ ਲਈ ਟ੍ਰਾਂਸਪੋਰਟ: stdio|sse|streamable-http"
},
"disable": {
"description": "MCP ਸਰਵਰ ਬੰਦ ਕਰੋ"
},
"call": {
"description": "ਕਿਸੇ MCP ਟੂਲ ਨੂੰ ਸਿੱਧੇ ਚਲਾਓ",
"args": "JSON ਆਰਗੂਮੈਂਟ ਆਬਜੈਕਟ (ਇਨਲਾਈਨ)",

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -30,7 +30,7 @@
"opencode": "Instala e configura o plugin @omniroute/opencode-plugin incluído para o OpenCode"
},
"doctor": {
"title": "OmniRoute Doctor",
"title": "Diagnóstico do OmniRoute",
"dbOk": "Banco de dados: OK ({path})",
"dbMissing": "Banco de dados: não inicializado — execute `omniroute setup`",
"portOk": "Porta {port}: disponível",
@@ -256,6 +256,7 @@
"max_restarts": "Máximo de reinícios em 30s antes de desistir (padrão: 2)",
"tray": "Mostrar ícone na bandeja do sistema (apenas desktop, opt-in)",
"no_tray": "Desabilitar ícone na bandeja do sistema",
"ready_timeout": "Tempo limite da verificação de prontidão em ms (também OMNIROUTE_READY_TIMEOUT_MS, padrão 60000)",
"tls_cert": "Caminho para um certificado TLS (PEM) para servir HTTPS (também OMNIROUTE_TLS_CERT)",
"tls_key": "Caminho para a chave privada TLS (PEM) para servir HTTPS (também OMNIROUTE_TLS_KEY)"
},
@@ -301,7 +302,7 @@
"noServer": "Servidor não está em execução. Inicie com: omniroute serve",
"title": "Saúde",
"status": "Status: {status}",
"uptime": "Uptime: {uptime}",
"uptime": "Tempo de atividade: {uptime}",
"requests": "Requisições (24h): {count}",
"cost": "Custo (24h): ${cost}"
},
@@ -344,6 +345,19 @@
},
"mcp": {
"title": "Servidor MCP",
"running": "Servidor MCP em execução ({transport})",
"stopped": "Servidor MCP parado.",
"restarted": "Servidor MCP reiniciado.",
"stoppedHint": "Execute `omniroute mcp enable` para ativá-lo.",
"enabled": "Servidor MCP ativado.",
"disabled": "Servidor MCP desativado.",
"enable": {
"description": "Ativar o servidor MCP",
"transport": "Transporte a ser usado: stdio|sse|streamable-http"
},
"disable": {
"description": "Desativar o servidor MCP"
},
"call": {
"description": "Invocar uma ferramenta MCP diretamente",
"args": "Objeto JSON de argumentos (inline)",
@@ -371,10 +385,7 @@
},
"audit": {
"description": "Log de auditoria MCP (alias para audit --source mcp)"
},
"running": "Servidor MCP em execução ({transport})",
"stopped": "Servidor MCP parado.",
"restarted": "Servidor MCP reiniciado."
}
},
"a2a": {
"skills": {
@@ -1093,7 +1104,7 @@
}
},
"combo": {
"title": "Combos",
"title": "Combinações",
"switched": "Combo ativo: {name}",
"created": "Combo criado: {name}",
"deleted": "Combo removido: {name}",
@@ -1268,7 +1279,7 @@
"description": "REPL interativo multi-turn com LLM",
"model": "Modelo a usar (padrão: auto)",
"combo": "Nome do combo a usar",
"system": "System prompt",
"system": "Prompt do sistema",
"resume": "Retomar sessão salva pelo nome"
},
"plugin": {

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -256,6 +256,7 @@
"max_restarts": "අත්හැරීමට පෙර තත්පර 30ක් තුළ සිදු කළ හැකි උපරිම බිඳවැටීම්-පසු නැවත ආරම්භ කිරීම් ගණන (පෙරනිමිය: 2)",
"tray": "පද්ධති ට්රේ තුළ ආරම්භ කරන්න (ඩෙස්ක්ටොප් සඳහා පමණි, කැමැත්තෙන් සක්රිය කළ යුතුය)",
"no_tray": "පද්ධති ට්රේ නිරූපකය අක්රිය කරන්න",
"ready_timeout": "සූදානම්දැයි පරීක්ෂා කිරීමේ කල් ඉකුත්වීම ms වලින් (OMNIROUTE_READY_TIMEOUT_MS ද අදාළ වේ, පෙරනිමිය 60000)",
"tls_cert": "HTTPS සේවය සැපයීම සඳහා TLS සහතිකයකට (PEM) ඇති මාර්ගය (OMNIROUTE_TLS_CERT ද අදාළ වේ)",
"tls_key": "HTTPS සේවය සැපයීම සඳහා TLS පුද්ගලික යතුරට (PEM) ඇති මාර්ගය (OMNIROUTE_TLS_KEY ද අදාළ වේ)"
},
@@ -347,6 +348,16 @@
"running": "MCP සේවාදායකය ක්රියාත්මකයි ({transport})",
"stopped": "MCP සේවාදායකය නවතා ඇත.",
"restarted": "MCP සේවාදායකය නැවත ආරම්භ කරන ලදී.",
"stoppedHint": "එය සක්රීය කිරීමට `omniroute mcp enable` ධාවනය කරන්න.",
"enabled": "MCP සේවාදායකය සක්රීය කර ඇත.",
"disabled": "MCP සේවාදායකය අක්රීය කර ඇත.",
"enable": {
"description": "MCP සේවාදායකය සක්රීය කරන්න",
"transport": "භාවිත කළ යුතු ප්රවාහන ක්රමය: stdio|sse|streamable-http"
},
"disable": {
"description": "MCP සේවාදායකය අක්රීය කරන්න"
},
"call": {
"description": "MCP මෙවලමක් සෘජුවම කැඳවන්න",
"args": "JSON තර්ක වස්තුව (පේළිය තුළ)",
@@ -1093,7 +1104,7 @@
}
},
"combo": {
"title": "Combos",
"title": "සංයෝජන",
"switched": "සක්රිය combo එක: {name}",
"created": "Combo එක සාදන ලදී: {name}",
"deleted": "Combo එක මකා දමන ලදී: {name}",

File diff suppressed because it is too large Load Diff

View File

@@ -256,6 +256,7 @@
"max_restarts": "Največje število ponovnih zagonov po sesutju v 30 s, preden se poskušanje opusti (privzeto: 2)",
"tray": "Zaženi v sistemski vrstici (samo za namizne sisteme, po izbiri)",
"no_tray": "Onemogoči ikono sistemske vrstice",
"ready_timeout": "Časovna omejitev preverjanja pripravljenosti v ms (tudi OMNIROUTE_READY_TIMEOUT_MS, privzeto 60000)",
"tls_cert": "Pot do potrdila TLS (PEM) za streženje prek HTTPS (tudi OMNIROUTE_TLS_CERT)",
"tls_key": "Pot do zasebnega ključa TLS (PEM) za streženje prek HTTPS (tudi OMNIROUTE_TLS_KEY)"
},
@@ -347,6 +348,16 @@
"running": "Strežnik MCP deluje ({transport})",
"stopped": "Strežnik MCP je ustavljen.",
"restarted": "Strežnik MCP je znova zagnan.",
"stoppedHint": "Za vklop zaženite `omniroute mcp enable`.",
"enabled": "Strežnik MCP je omogočen.",
"disabled": "Strežnik MCP je onemogočen.",
"enable": {
"description": "Omogoči strežnik MCP",
"transport": "Prenos, ki naj se uporabi: stdio|sse|streamable-http"
},
"disable": {
"description": "Onemogoči strežnik MCP"
},
"call": {
"description": "Neposredno prikliči orodje MCP",
"args": "Objekt argumentov JSON (v vrstici)",

View File

@@ -256,6 +256,7 @@
"max_restarts": "Maksimalan broj ponovnih pokretanja pri padu u toku 30s pre odustajanja (podrazumevano: 2)",
"tray": "Покретање у системској траци (само десктоп, опционо)",
"no_tray": "Онемогући икону у системској траци",
"ready_timeout": "Временско ограничење провере спремности у ms (такође OMNIROUTE_READY_TIMEOUT_MS, подразумевано 60000)",
"tls_cert": "Путања до TLS сертификата (PEM) за HTTPS (такође OMNIROUTE_TLS_CERT)",
"tls_key": "Путања до TLS приватног кључа (PEM) за HTTPS (такође OMNIROUTE_TLS_KEY)"
},
@@ -300,7 +301,7 @@
"description": "Провери здравље сервера и статус компоненти",
"noServer": "Сервер није покренут. Покрените са: omniroute serve",
"title": "Здравље",
"status": "Status: {status}",
"status": "Статус: {status}",
"uptime": "Vreme rada: {uptime}",
"requests": "Zahtevi (24h): {count}",
"cost": "Trošak (24h): ${cost}"
@@ -347,6 +348,16 @@
"running": "MCP server je pokrenut ({transport})",
"stopped": "MCP server je zaustavljen.",
"restarted": "MCP server je ponovo pokrenut.",
"stoppedHint": "Покрените `omniroute mcp enable` да бисте га укључили.",
"enabled": "MCP сервер је омогућен.",
"disabled": "MCP сервер је онемогућен.",
"enable": {
"description": "Омогући MCP сервер",
"transport": "Протокол за пренос: stdio|sse|streamable-http"
},
"disable": {
"description": "Онемогући MCP сервер"
},
"call": {
"description": "Direktno pozovi MCP alat",
"args": "JSON objekat argumenata (inline)",
@@ -810,7 +821,7 @@
}
},
"program": {
"description": "OmniRoute — Smart AI Router with Auto Fallback",
"description": "OmniRoute — паметни AI рутер са аутоматским пребацивањем на резервну опцију",
"version": "Прикажи верзију и изађи",
"output": "Формат излаза (table, json, jsonl, csv)",
"quiet": "Сакрий небитан излаз",

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -256,6 +256,7 @@
"max_restarts": "Taslim bolishdan oldin 30 soniya ichidagi nosozlikdan keyingi qayta ishga tushirishlarning maksimal soni (standart: 2)",
"tray": "Tizim treyida ishga tushirish (faqat ish stoli versiyasida, ixtiyoriy)",
"no_tray": "Tizim treyi belgisini ochirish",
"ready_timeout": "Tayyorlik tekshiruvi uchun kutish vaqti, ms (shuningdek OMNIROUTE_READY_TIMEOUT_MS, standart qiymat 60000)",
"tls_cert": "HTTPS orqali xizmat korsatish uchun TLS sertifikati (PEM) yoli (shuningdek, OMNIROUTE_TLS_CERT)",
"tls_key": "HTTPS orqali xizmat korsatish uchun TLS maxfiy kaliti (PEM) yoli (shuningdek, OMNIROUTE_TLS_KEY)"
},
@@ -347,6 +348,16 @@
"running": "MCP serveri ishlamoqda ({transport})",
"stopped": "MCP serveri toʻxtatildi.",
"restarted": "MCP serveri qayta ishga tushirildi.",
"stoppedHint": "Uni yoqish uchun `omniroute mcp enable` buyrugʻini ishga tushiring.",
"enabled": "MCP serveri yoqildi.",
"disabled": "MCP serveri oʻchirildi.",
"enable": {
"description": "MCP serverini yoqish",
"transport": "Foydalaniladigan transport: stdio|sse|streamable-http"
},
"disable": {
"description": "MCP serverini oʻchirish"
},
"call": {
"description": "MCP vositasini bevosita chaqirish",
"args": "JSON argumentlar obyekti (ichki)",

File diff suppressed because it is too large Load Diff

View File

@@ -256,6 +256,7 @@
"max_restarts": "Iye ìtunbẹ̀rẹ̀ lẹ́yìn ìkọ̀lù tó pọ̀ jù láàárín 30s kí ó tó jáwọ́ (àìyípadà: 2)",
"tray": "Bẹ̀rẹ̀ nínú atẹ eto (dẹ́sítọ́ọ̀pù nìkan, àṣàyàn ni)",
"no_tray": "Pa àmì atẹ eto rẹ́",
"ready_timeout": "Àkókò ìdádúró tó pọ̀ jù fún àyẹ̀wò ìmúrasílẹ̀ ní ms (bákan náà OMNIROUTE_READY_TIMEOUT_MS, àìyípadà 60000)",
"tls_cert": "Ọ̀nà sí ìwé-ẹ̀rí TLS kan (PEM) láti pèsè HTTPS (bákan náà OMNIROUTE_TLS_CERT)",
"tls_key": "Ọ̀nà sí kọ́kọ́rọ́ àdáni TLS (PEM) láti pèsè HTTPS (bákan náà OMNIROUTE_TLS_KEY)"
},
@@ -347,6 +348,16 @@
"running": "Olùpín MCP ń ṣiṣẹ́ ({transport})",
"stopped": "Olùpín MCP ti dá iṣẹ́ dúró.",
"restarted": "A ti tún olùpín MCP bẹ̀rẹ̀.",
"stoppedHint": "Ṣe `omniroute mcp enable` láti mú un ṣiṣẹ́.",
"enabled": "A ti mú olupin MCP ṣiṣẹ́.",
"disabled": "A ti pa olupin MCP.",
"enable": {
"description": "Mú olupin MCP ṣiṣẹ́",
"transport": "Ọ̀nà ìgbéṣùmọ̀ tí a ó lò: stdio|sse|streamable-http"
},
"disable": {
"description": "Pa olupin MCP"
},
"call": {
"description": "Pe ohun èlò MCP kan ní tààrà",
"args": "Ohun àríyànjiyàn JSON (níbẹ̀-níbẹ̀)",

View File

@@ -14,18 +14,6 @@
"jsonOpt": "以 JSON 格式输出",
"yesOpt": "跳过确认"
},
"program": {
"description": "OmniRoute — 具有自动故障转移的智能 AI 路由器",
"version": "打印版本并退出",
"output": "输出格式table, json, jsonl, csv",
"quiet": "禁止非必要输出",
"no_color": "禁用彩色输出",
"timeout": "HTTP 请求超时(毫秒)",
"api_key": "OmniRoute 服务器的 API 密钥",
"base_url": "OmniRoute 服务器的基础 URL",
"context": "此命令使用的服务器上下文/配置文件",
"lang": "设置 CLI 显示语言(覆盖 OMNIROUTE_LANG"
},
"setup": {
"title": "OmniRoute 设置",
"passwordPrompt": "管理员密码",
@@ -145,6 +133,20 @@
"listTitle": "{days} 天内过期的密钥:"
}
},
"authExport": {
"description": "导出已解密的提供者凭据(仅限本地,明文输出)",
"idOpt": "仅导出与此 id/名称/提供者匹配的连接",
"formatOpt": "输出格式json 或 env",
"outOpt": "将输出写入文件而非标准输出(以 0600 权限写入)",
"forceOpt": "确认你了解此操作会打印/写入明文密钥",
"warning": "⚠ 此操作会打印/写入已解密的明文 API 密钥和 OAuth 令牌。请确保你的屏幕、shell 历史记录以及任何输出文件保持私密。",
"confirmHeading": "⚠ 警告:此操作会以明文导出已解密的提供者凭据",
"confirmBody": "此命令会为所选连接解密并打印/写入 apiKey、accessToken、refreshToken 和\nidToken。请将输出视为机密。",
"confirmFooter": "如需确认,请运行:\n omniroute auth export --force",
"missingKey": "导出凭据需要 STORAGE_ENCRYPTION_KEY。",
"notFound": "未找到连接:{id}",
"invalidFormat": "无效格式:{format}。请使用 json 或 env。"
},
"stream": {
"description": "使用 SSE 检查模式流式传输聊天响应",
"file": "从文件读取提示",
@@ -302,7 +304,7 @@
"status": "状态:{status}",
"uptime": "运行时间:{uptime}",
"requests": "请求数24h{count}",
"cost": "成本24h"
"cost": "成本24h${cost}"
},
"quota": {
"description": "显示提供者配额使用情况",
@@ -346,6 +348,16 @@
"running": "MCP 服务器正在运行({transport}",
"stopped": "MCP 服务器已停止。",
"restarted": "MCP 服务器已重启。",
"stoppedHint": "运行 `omniroute mcp enable` 以启用它。",
"enabled": "MCP 服务器已启用。",
"disabled": "MCP 服务器已禁用。",
"enable": {
"description": "启用 MCP 服务器",
"transport": "要使用的传输方式stdio|sse|streamable-http"
},
"disable": {
"description": "禁用 MCP 服务器"
},
"call": {
"description": "直接调用 MCP 工具",
"args": "JSON 参数对象(内联)",
@@ -808,6 +820,18 @@
"event": "要模拟的事件类型默认request.completed"
}
},
"program": {
"description": "OmniRoute — 具有自动故障转移的智能 AI 路由器",
"version": "打印版本并退出",
"output": "输出格式table, json, jsonl, csv",
"quiet": "禁止非必要输出",
"no_color": "禁用彩色输出",
"timeout": "HTTP 请求超时(毫秒)",
"api_key": "OmniRoute 服务器的 API 密钥",
"base_url": "OmniRoute 服务器的基础 URL",
"context": "此命令使用的服务器上下文/配置文件",
"lang": "设置 CLI 显示语言(覆盖 OMNIROUTE_LANG"
},
"files": {
"description": "管理文件(上传、列出、获取、下载、删除)",
"list": {
@@ -908,6 +932,11 @@
"model": "按模型筛选"
}
},
"radar": {
"description": "检查并同步本地 Radar 目录订阅源",
"status": "显示本地 Radar 设置和订阅源缓存状态",
"sync": "通过本地服务器同步目录、推荐、优惠和 Intel"
},
"resilience": {
"description": "检查和管理弹性机制",
"status": {
@@ -1235,8 +1264,8 @@
"description": "管理 OmniRoute 开机自启Linuxsystemd 用户服务)",
"enable": "启用开机自启",
"disable": "禁用开机自启",
"status": "显示自启状态",
"toggle": "切换开机自启"
"toggle": "切换开机自启",
"status": "显示自启状态"
},
"runtime": {
"description": "管理本地运行时依赖",
@@ -1263,25 +1292,6 @@
"update": "更新已安装的插件",
"scaffold": "搭建新的插件模板"
},
"authExport": {
"description": "导出已解密的提供者凭据(仅限本地,明文输出)",
"idOpt": "仅导出与此 id/名称/提供者匹配的连接",
"formatOpt": "输出格式json 或 env",
"outOpt": "将输出写入文件而非标准输出(以 0600 权限写入)",
"forceOpt": "确认你了解此操作会打印/写入明文密钥",
"warning": "⚠ 此操作会打印/写入已解密的明文 API 密钥和 OAuth 令牌。请确保你的屏幕、shell 历史记录以及任何输出文件保持私密。",
"confirmHeading": "⚠ 警告:此操作会以明文导出已解密的提供者凭据",
"confirmBody": "此命令会为所选连接解密并打印/写入 apiKey、accessToken、refreshToken 和\nidToken。请将输出视为机密。",
"confirmFooter": "如需确认,请运行:\n omniroute auth export --force",
"missingKey": "导出凭据需要 STORAGE_ENCRYPTION_KEY。",
"notFound": "未找到连接:{id}",
"invalidFormat": "无效格式:{format}。请使用 json 或 env。"
},
"radar": {
"description": "检查并同步本地 Radar 目录订阅源",
"status": "显示本地 Radar 设置和订阅源缓存状态",
"sync": "通过本地服务器同步目录、推荐、优惠和 Intel"
},
"launch": {
"description": "启动指向 OmniRoute 的 Claude Code本地或远程使用 --profile",
"token": "Claude 客户端应发送的令牌ANTHROPIC_AUTH_TOKEN",

View File

@@ -14,18 +14,6 @@
"jsonOpt": "以 JSON 格式輸出",
"yesOpt": "跳過確認"
},
"program": {
"description": "OmniRoute — 具有自動故障轉移的智慧 AI 路由器",
"version": "列印版本並退出",
"output": "輸出格式table, json, jsonl, csv",
"quiet": "禁止非必要輸出",
"no_color": "停用彩色輸出",
"timeout": "HTTP 請求超時(毫秒)",
"api_key": "OmniRoute 伺服器的 API 金鑰",
"base_url": "OmniRoute 伺服器的基礎 URL",
"context": "此命令使用的伺服器上下文/配置檔案",
"lang": "設定 CLI 顯示語言(覆蓋 OMNIROUTE_LANG"
},
"setup": {
"title": "OmniRoute 設定",
"passwordPrompt": "管理員密碼",
@@ -145,6 +133,20 @@
"listTitle": "{days} 天內過期的金鑰:"
}
},
"authExport": {
"description": "匯出已解密的提供者憑據(僅限本機,明文輸出)",
"idOpt": "僅匯出與此 id/名稱/提供者相符的連線",
"formatOpt": "輸出格式json 或 env",
"outOpt": "將輸出寫入檔案而非標準輸出(以 0600 權限寫入)",
"forceOpt": "確認你了解此操作會列印/寫入明文密鑰",
"warning": "⚠ 此操作會列印/寫入已解密的明文 API 金鑰和 OAuth 令牌。請確保你的螢幕、shell 歷史記錄以及任何輸出檔案保持私密。",
"confirmHeading": "⚠ 警告:此操作會以明文匯出已解密的提供者憑據",
"confirmBody": "此命令會為所選連線解密並列印/寫入 apiKey、accessToken、refreshToken 和\nidToken。請將輸出視為機密。",
"confirmFooter": "如需確認,請執行:\n omniroute auth export --force",
"missingKey": "匯出憑據需要 STORAGE_ENCRYPTION_KEY。",
"notFound": "找不到連線:{id}",
"invalidFormat": "無效格式:{format}。請使用 json 或 env。"
},
"stream": {
"description": "使用 SSE 檢查模式流式傳輸聊天響應",
"file": "從檔案讀取提示",
@@ -302,7 +304,7 @@
"status": "狀態:{status}",
"uptime": "執行時間:{uptime}",
"requests": "請求數24h{count}",
"cost": "成本24h"
"cost": "成本24h${cost}"
},
"quota": {
"description": "顯示提供者配額使用情況",
@@ -346,6 +348,16 @@
"running": "MCP 伺服器正在執行({transport}",
"stopped": "MCP 伺服器已停止。",
"restarted": "MCP 伺服器已重啟。",
"stoppedHint": "執行 `omniroute mcp enable` 以啟用它。",
"enabled": "MCP 伺服器已啟用。",
"disabled": "MCP 伺服器已停用。",
"enable": {
"description": "啟用 MCP 伺服器",
"transport": "要使用的傳輸方式stdio|sse|streamable-http"
},
"disable": {
"description": "停用 MCP 伺服器"
},
"call": {
"description": "直接呼叫 MCP 工具",
"args": "JSON 引數物件(內聯)",
@@ -808,6 +820,18 @@
"event": "要模擬的事件型別預設request.completed"
}
},
"program": {
"description": "OmniRoute — 具有自動故障轉移的智慧 AI 路由器",
"version": "列印版本並退出",
"output": "輸出格式table, json, jsonl, csv",
"quiet": "禁止非必要輸出",
"no_color": "停用彩色輸出",
"timeout": "HTTP 請求超時(毫秒)",
"api_key": "OmniRoute 伺服器的 API 金鑰",
"base_url": "OmniRoute 伺服器的基礎 URL",
"context": "此命令使用的伺服器上下文/配置檔案",
"lang": "設定 CLI 顯示語言(覆蓋 OMNIROUTE_LANG"
},
"files": {
"description": "管理檔案(上傳、列出、獲取、下載、刪除)",
"list": {
@@ -908,6 +932,11 @@
"model": "按模型篩選"
}
},
"radar": {
"description": "檢查並同步本機 Radar 目錄訂閱來源",
"status": "顯示本機 Radar 設定和訂閱來源快取狀態",
"sync": "透過本機伺服器同步目錄、推薦、優惠和 Intel"
},
"resilience": {
"description": "檢查和管理彈性機制",
"status": {
@@ -1235,8 +1264,8 @@
"description": "管理 OmniRoute 開機自啟Linuxsystemd 使用者服務)",
"enable": "啟用開機自啟",
"disable": "停用開機自啟",
"status": "顯示自啟狀態",
"toggle": "切換開機自啟"
"toggle": "切換開機自啟",
"status": "顯示自啟狀態"
},
"runtime": {
"description": "管理本地執行時依賴",
@@ -1263,25 +1292,6 @@
"update": "更新已安裝的外掛",
"scaffold": "搭建新的外掛模板"
},
"authExport": {
"description": "匯出已解密的提供者憑據(僅限本機,明文輸出)",
"idOpt": "僅匯出與此 id/名稱/提供者相符的連線",
"formatOpt": "輸出格式json 或 env",
"outOpt": "將輸出寫入檔案而非標準輸出(以 0600 權限寫入)",
"forceOpt": "確認你了解此操作會列印/寫入明文密鑰",
"warning": "⚠ 此操作會列印/寫入已解密的明文 API 金鑰和 OAuth 令牌。請確保你的螢幕、shell 歷史記錄以及任何輸出檔案保持私密。",
"confirmHeading": "⚠ 警告:此操作會以明文匯出已解密的提供者憑據",
"confirmBody": "此命令會為所選連線解密並列印/寫入 apiKey、accessToken、refreshToken 和\nidToken。請將輸出視為機密。",
"confirmFooter": "如需確認,請執行:\n omniroute auth export --force",
"missingKey": "匯出憑據需要 STORAGE_ENCRYPTION_KEY。",
"notFound": "找不到連線:{id}",
"invalidFormat": "無效格式:{format}。請使用 json 或 env。"
},
"radar": {
"description": "檢查並同步本機 Radar 目錄訂閱來源",
"status": "顯示本機 Radar 設定和訂閱來源快取狀態",
"sync": "透過本機伺服器同步目錄、推薦、優惠和 Intel"
},
"launch": {
"description": "啟動指向 OmniRoute 的 Claude Code本機或遠端使用 --profile",
"token": "Claude 用戶端應傳送的令牌ANTHROPIC_AUTH_TOKEN",

View File

@@ -14,6 +14,7 @@ import { stopProcessGracefully } from "../../../src/shared/platform/windowsProce
import {
isFatalInstrumentationHookFailure,
formatAndroidInstrumentationFailureHint,
isFatalStartupDiagnostic,
} from "../utils/ensureAndroidCacheDir.mjs";
const CRASH_LOG_LINES = 50;
@@ -55,12 +56,14 @@ export class ServerSupervisor {
this.child = null;
this.isShuttingDown = false;
this.instrumentationFailureHintPrinted = false;
this.fatalStartupDiagnosticPrinted = false;
}
start() {
this.startedAt = Date.now();
this.crashLog = [];
this.instrumentationFailureHintPrinted = false;
this.fatalStartupDiagnosticPrinted = false;
const showLog = process.env.OMNIROUTE_SHOW_LOG === "1";
// #6321: stdout used to be discarded (`"ignore"`) whenever `--log`/OMNIROUTE_SHOW_LOG
@@ -99,6 +102,15 @@ export class ServerSupervisor {
)
);
}
// #13314: surface any `[STARTUP] Fatal:`-guarded boot diagnostic
// immediately, even without --log — otherwise it is only buffered and
// reaches the operator on exit/crash, which never happens when the
// HTTP listener still comes up after the fatal failure (every route
// then 500s with zero visible diagnostic anywhere).
if (!this.fatalStartupDiagnosticPrinted && isFatalStartupDiagnostic(text)) {
this.fatalStartupDiagnosticPrinted = true;
process.stderr.write(text.endsWith("\n") ? text : `${text}\n`);
}
};
if (this.child.stdout) {

BIN
bin/cli/tray/icon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 713 B

After

Width:  |  Height:  |  Size: 286 B

View File

@@ -105,6 +105,27 @@ export function isFatalInstrumentationHookFailure(text) {
return /Unsupported platform:\s*android/i.test(text);
}
/**
* Detect any fatal boot-time diagnostic guarded by the `[STARTUP] Fatal:`
* prefix (`src/instrumentation-node.ts::ensureDbReadyForBoot()`,
* `src/instrumentation.ts::register()`, and any future guard using the same
* marker). #13314: in the default `omniroute serve` mode (no `--log`),
* `ServerSupervisor` only buffers stdout/stderr and flushes it to the real
* console on exit/crash/readiness-timeout — so if the HTTP listener still
* comes up after a fatal boot diagnostic was already printed (e.g. the
* better-sqlite3 / node:sqlite driver cascade failing hard), the operator
* sees "OmniRoute is running!" with zero visible diagnostic anywhere, and
* every route 500s. This generalizes the #10028 Android/Termux carve-out to
* every `[STARTUP] Fatal:` guard, not just that one platform-specific string.
*
* @param {string} text
* @returns {boolean}
*/
export function isFatalStartupDiagnostic(text) {
if (!text) return false;
return /^\[STARTUP\] Fatal:/m.test(text);
}
/**
* Operator-facing hint when that instrumentation failure shows up in child
* output — defense in depth if prep was skipped or a future Next.js probe

View File

@@ -0,0 +1 @@
- **feat(reasoning):** adaptive reasoning effort (`auto`) — the gateway resolves the thinking budget per user turn from deterministic request-shape signals (stateless per-turn pin) instead of forwarding a literal `auto`, applied at the gateway pre-translation for any harness (Claude Code, Cursor, Codex, opencode, Hermes) whose request dispatches to an OpenAI Chat-Completions-shaped upstream (`targetFormat === FORMATS.OPENAI``reasoning_effort` is an OpenAI-shaped field, so a Claude- or Gemini-targeted request is unaffected). Opt in via `X-OmniRoute-Effort: auto` or a model's `defaultReasoningEffort: "auto"` (now a valid `ModelSpec` value); any explicit client reasoning field always wins ([#13448](https://github.com/diegosouzapw/OmniRoute/pull/13448))

View File

@@ -0,0 +1 @@
- **feat(dashboard):** Add a dedicated, full-width API-key routing editor with explicit model/combo choices, searchable selectors and protection for unsaved rule drafts. ([#13555](https://github.com/diegosouzapw/OmniRoute/pull/13555)) — thanks @JxnLexn

View File

@@ -0,0 +1 @@
- **feat(flags):** add `DB_HEALTHCHECK_STARTUP_DEFERRED_ENABLED` (default off) — opt-in deferral of the startup DB health/integrity check past process boot via `setImmediate`; off keeps the pre-#13717 behavior of blocking `getDbInstance()` until the check has already run (#13717).

View File

@@ -0,0 +1 @@
- **feat(i18n):** `retranslate-site` rewrites the site catalogs' verbatim-English leaves (2,059 across 63 catalogs; mean English residue 10.3 % → 6.3 %, the rest being brand names kept on purpose). (#13886)

View File

@@ -0,0 +1 @@
- feat(providers): **Added Agnes AI (China) as `agnes-cn` pointed at `https://api.agnes-ai.cn/v1`. Keys issued for `apihub.agnes-ai.com` stay on the existing `agnes` card. Live `/v1/models` on that host lists `agnes-3.0-flash` (same id as intl); the CN seed matches 2.0/2.5/3.0 and not retired 1.5.**

View File

@@ -0,0 +1 @@
- **feat(providers):** share the existing `api.x.ai/v1/models` discovery config with `xai-oauth` so SuperGrok OAuth connections pick up new Grok ids without a registry seed edit.

View File

@@ -0,0 +1 @@
- fix(api): restore the `name` field on non-streaming `/v1/responses` `function_call` output items — a plain (non-namespace) tool call's identity restore was blindly applying the `_toolNameMap` alias-table fallback as a `{namespace, name}` object, silently blanking `name` to `undefined` (dropped entirely by JSON.stringify) and leaving Codex unable to dispatch the call, so it re-narrated its intent in a loop instead (#12370)

View File

@@ -0,0 +1 @@
- **fix(db):** give `conversation_turn_nodes` its own independent retention knob (`retention.conversationTurnNodes`, default 30 days — matching `callLogs` so upgrading changes nothing until an operator overrides it) instead of sharing `callLogs`, and sweep orphaned `agentic_conversations` after the nodes expire (#12453).

View File

@@ -0,0 +1 @@
- **fix(sse):** Codex WebSocket transport (including the app-server) no longer fails to load in the Next.js standalone Docker runtime — the wreq-js loader now resolves its module name dynamically instead of a literal Turbopack could rewrite to an unreachable build-time symlink (#12491) — thanks @marshalfevzi

View File

@@ -0,0 +1 @@
- **fix(providers):** xAI requests no longer silently drop an assistant tool call sent in the legacy OpenAI `function_call` shape (instead of `tool_calls[]`) — the call is now translated into the xAI request the same way modern tool calls are (#12692) — thanks @soroush5

View File

@@ -0,0 +1 @@
- **fix(providers):** xAI responses no longer report `total_tokens`/`totalTokenCount` as `0` when upstream usage uses the legacy `prompt_tokens`/`completion_tokens` names instead of `input_tokens`/`output_tokens` (#12700) — thanks @soroush5

View File

@@ -0,0 +1 @@
- **fix(cursor):** Kimi-k3 / kimi-k3-high on the Cursor provider sometimes emit tool calls by imitating the executor's own history narration ("Assistant called tool … with arguments: …") instead of using structured tool calls, so clients received raw narration text plus native generation delimiters with `finish_reason: "stop"` — and the leaked turn compounded on every subsequent request via history re-send; the cursor executor now detects this shape and reassembles it into a structured `tool_calls` entry in both streaming and non-streaming finalization paths, gated on "no structured tool calls yet" so healthy turns are untouched ([#12723](https://github.com/diegosouzapw/OmniRoute/pull/12723))

View File

@@ -0,0 +1 @@
- **fix(providers):** correct Magnific API key validation, which reported every valid key as invalid due to a GET probe against a POST-only endpoint (#12927) — thanks @hubo1989

View File

@@ -0,0 +1 @@
- **fix(providers):** GitLab Duo Retest and chat requests now fall back to the public Code Suggestions endpoint for ANY `direct_access` 403 (not only the "direct connections are disabled" tenant-config message), and surface the real upstream error body instead of a generic "Access denied" when both endpoints reject the token (#12958) — thanks @Rahulsharma0810

View File

@@ -0,0 +1 @@
- **fix(sse):** stop misclassifying a truncated Anthropic-compatible `max_tokens` probe response (`content:[{type:"text",text:""}]`) as an empty upstream response (#12968) — thanks @pranay-gpt

Some files were not shown because too many files have changed in this diff Show More