Compare commits

..

113 Commits

Author SHA1 Message Date
Markus Hartung
a582bef675 board #11389 2026-08-24 23:21:27 -03:00
Markus Hartung
f77254da79 board #11443
# Conflicts:
#	README.md
#	docs/diagrams/cli-terminal.svg
#	docs/diagrams/promise-pillars.svg
2026-08-24 23:21:17 -03:00
Markus Hartung
b1474b992f board #11441 2026-08-24 23:20:55 -03:00
Markus Hartung
86514aa481 board #11423 2026-08-24 23:20:52 -03:00
Markus Hartung
a150c6bc40 board #11409 2026-08-24 23:20:49 -03:00
Markus Hartung
c59661cfd6 board #11340 2026-08-24 23:20:47 -03:00
Markus Hartung
b76866fa12 chore(deps): regenerate package-lock.json after ESLint 10 + bun 1.4.0 bump (#11259) 2026-08-24 23:20:22 -03:00
backryun
8a14cefcb1 chore(deps): refresh runtimes and adopt ESLint 10 2026-08-24 23:15:54 -03:00
oyi77
158fb1a806 test(executors): adapt remaining 35 suites to the async lazy registry (round 2)
Round 1 only covered suites importing from executors/index.ts directly;
suites importing getExecutor through other paths (open-sse root
re-export, dynamic imports inside tests) still called it synchronously,
producing Promise-vs-instance assertion failures in CI (e.g.
providers-yuanbao-web 'instanceof YuanbaoWebExecutor' false).

Sweep method: repo-wide scan for un-awaited getExecutor call sites,
adapted mechanically (await + async callbacks + Response|{response}
union narrowing); assert.rejects guards with promise-returning
callbacks verified correct as-is.

Locally verified green: providers-yuanbao-web, ninerouter-executor,
provider-request-failure-pipeline, provider-limits-accesstoken-fallback,
executor-registry, chatcore-executor-proxy, poe-api-executor-regression,
web-cookie-providers-new (113/113).
2026-08-24 21:44:23 -03:00
oyi77
77ea656b12 perf(executors): lazy-load the executor registry — defer class imports + construction to first use (#11220)
The executor barrel statically imported ~100 executor modules and
constructed every instance at module load. Measured cold cost on top of
the minimal set: ~0.7–1.2s boot time and ~35MB heap, paid by every
deployment regardless of which providers it uses.

Now:
- executors/index.ts keeps the declarative alias table byte-stable (same
  keys, same order, same ctor args — pinned by the golden lock) but each
  value is a deferred loader using dynamic import; bundlers emit
  on-demand chunks
- registry.ts gains registerLazyExecutor/loadRegisteredExecutor: aliases
  are declared eagerly so hasSpecializedExecutor() and
  listExecutorAliases() stay synchronous, instances materialize once on
  first use and cache into the same registry map
- getExecutor() becomes async; production call sites (chatCore proxy
  resolver, video generation, compression judge/eval clients,
  quotaAutoPing deps, anthropic OAuth validation) await it
- cliproxy wrapper ExecutorLike types drop their index signatures so
  BaseExecutor satisfies them structurally

Measured after (isolated DATA_DIR): barrel boot 712-832ms / ~45MB with
first-use materialization of an executor costing +120-150ms once.

Test impact: 24 unit suites adapted mechanically to the async seam
(await + union narrowing on the Response | {response} execute result);
class imports moved from the barrel to executor module files. The
web-cookie sweep SIGABRT failure is pre-existing (reproduced identically
on the clean base).

Commit gate note: husky lint-staged fails with 'suppressions left that
do not occur anymore' — reproduced identically on a stashed clean tree
(22 baseline problems), independent of this change.
2026-08-24 21:44:22 -03:00
Minxi Hou
471052b904 fix(combo): enable genuine quota-aware routing for generic providers (antigravity, claude, etc.) 2026-08-24 21:44:06 -03:00
Rouzbeh
b8553c8f0d fix(ci): prune stale ESLint suppressions fixed by this change
Our PR fixed the last no-restricted-imports violation in
src/lib/memory/embedding/index.ts (localDb barrel import replaced by a
direct readCache import), so the frozen suppression entry for that file
became stale. The gate (run-eslint-json.mjs) hard-fails on stale entries,
so remove it to keep 'No new ESLint warnings' green.
2026-08-24 21:44:05 -03:00
Rouzbeh
1255b5869b fix(memory): list and serve embedding/rerank models from all configured providers
The memory Engine tab quick-select built its model list only from a
keyword heuristic over the chat catalog plus OpenRouter live discovery,
so configured providers whose embedding models are not in that catalog
(e.g. Cloudflare Workers AI cf/@cf/baai/bge-m3) never appeared, and
requests for them failed with 'Unknown embedding provider'.

- add cloudflare-ai to EMBEDDING_PROVIDERS with a requiresAccountId URL
  template resolved per-request via buildEmbeddingProviderUrl() (mirrors
  CloudflareAIExecutor on the chat side) plus the cf provider alias
- merge curated registry models into the quick-select catalog and add a
  generic fallback listing: any configured OpenAI-compatible chat
  provider without a curated entry appears with free-text model input
- same treatment for rerank: listRerankProviders + generic Cohere-
  compatible fallback, new /api/memory/rerank-providers endpoint,
  selectors fall back to free-text when no static models exist
- runtime: embeddings service/handler and rerank route/handler resolve
  derived providers instead of rejecting unlisted ones
2026-08-24 21:44:00 -03:00
kriptoburak
ea2746e594 docs(changelog): add Xquik search fragment 2026-08-24 21:43:50 -03:00
kriptoburak
c264c24de9 fix(search): sync generated inference skill 2026-08-24 21:43:50 -03:00
kriptoburak
15cbc1ede7 fix(search): document API and update dispatch golden 2026-08-24 21:43:50 -03:00
kriptoburak
a65666519d feat(search): add Xquik X search provider 2026-08-24 21:43:50 -03:00
Ravi Tharuma
829634b741 fix(i18n): align Brazilian Portuguese resilience keys 2026-08-24 21:43:37 -03:00
Ravi Tharuma
09666c50e7 fix(i18n): keep Vietnamese resilience keys aligned 2026-08-24 21:43:36 -03:00
Ravi Tharuma
2b9f25accb feat(resilience): add hierarchical concurrency admission 2026-08-24 21:43:36 -03:00
WebPerson
9dc7711dc2 fix(electron): make the packaged Windows build pass the #7592 cold-restart smoke
Five defects found while validating #7592 on a real packaged Windows build:

- optionalPackStaging: GNU tar reads the drive letter in an absolute
  -f C:\... archive path as a remote rsh host (Cannot connect to C:),
  failing optional-pack staging on Git-for-Windows machines. Pass a
  bare filename with cwd at the tarball directory; surface tar stderr.
- electron/package.json: lib/loginHeaderCapture.js was missing from the
  asar files allowlist; loginManager.js requires it top-level, so the
  packaged main process crashed on launch.
- electron-builder >=26 injects !**/node_modules/** into every
  extraResources pattern list and no positive filter can override it,
  silently dropping the staged runtime node_modules (including the
  better-sqlite3 N-API prebuild) from resources/app. Add an afterPack
  hook (scripts/build/afterpack-copy-node-modules.mjs) that restores it.
- smoke harness: Electron resolves userData from
  %USERPROFILE%/AppData/Roaming/<name> (USERPROFILE wins over APPDATA)
  and the path service throws when it is missing, so
  requestSingleInstanceLock() returned false and the app exited(0)
  silently before whenReady. ensureSmokeEnvDirs now pre-creates the
  derived tree and is exported for tests.
- core.ts: the #7592 guard parses a [DB] Driver: ... line that only the
  unused openDatabaseAsync() emitted; getDbInstance() now logs the same
  line on its primary open so the assertion is reachable.

Also makes the smoke env-allowlist unit test host-agnostic (it
hardcoded POSIX paths) and adds regression tests for the USERPROFILE
derived tree and tarPack under Windows-style absolute paths.

Closes #7592
2026-08-24 18:06:06 -05:00
Max
42c209797c fix(free-models): stop reporting discontinued Pollinations models as free
The shared isFreeModel() predicate ignored each catalog entry's
freeType, so Pollinations models marked "discontinued" (moved behind a
paid key) were still reported free and could be routed under
hidePaidModels.

Add a compiler-checked grantsFreeAccess(freeType) table and use it in
both the routing predicate and strictZeroCostFilter.

Tests: tests/unit/autoCombo/free-regime-not-read-by-predicate.test.ts
2026-08-25 01:03:45 +02:00
Diego Rodrigues de Sa e Souza
8bbe92c692 fix(docker): size the Next build worker pool for a 16 GB runner (#11419)
Every "Publish to Docker Hub" run has failed since 2026-08-22 23:14 UTC — 96 of
the last 100. The builder stage dies with:

  ERROR: failed to solve: ResourceExhausted: process "/bin/sh -c ... npm run
  build ..." did not complete successfully: cannot allocate memory

That is the kernel, not V8. The log puts it precisely: the compile phase always
finishes ("✓ Compiled successfully in 4.2min") and the build is killed right
after "Collecting page data using 7 workers".

Each page-data worker is its own process and inherits NODE_OPTIONS, so the
--max-old-space-size ceiling is per PROCESS, not per build. CIRCLE_NODE_TOTAL=8
means 7 workers, and 7 of them alongside the parent no longer fit the 16 GB /
4 vCPU GitHub-hosted runners the pipeline builds on. It was intermittent for a
while before going 100%, which is what a threshold crossed by ordinary codebase
growth looks like — 7 was also oversubscribing a 4 vCPU runner.

Lower the pool to 3 (2 workers) and make it a build arg, so a big builder can
raise it back with `--build-arg OMNIROUTE_BUILD_WORKERS=8`.

tests/unit/docker-build-memory-budget.test.ts pins the budget: it reads the two
ARG defaults out of the Dockerfile and fails if `parent heap + workers × peak`
outgrows the runner, or if the pool oversubscribes its CPUs. Red on the base
(3/3), green here (3/3). The per-worker peak it budgets with is documented as an
inference from this failure, not a measurement.

DOCKER_GUIDE's build-arg table was stale (it still listed the pre-#10060 4096 MB
default); updated and given the new knob plus the symptom to recognize.
CIRCLE_NODE_TOTAL and OMNIROUTE_BUILD_WORKERS are allowlisted in the
fabricated-docs gate with the reason: neither is read via process.env here — one
is a Dockerfile ARG, the other is read by Next itself.

Note: the real proof is the next publish run. This failure mode only reproduces
on a memory-constrained host, so it cannot be reproduced by the unit suite; the
test guards the arithmetic, not the outcome.

Co-authored-by: Xiangzhe <bakryun0718@proton.me>
2026-08-24 15:47:47 -03:00
Diego Rodrigues de Sa e Souza
bbc7bf4351 fix(authz): match exact public routes exactly, not as prefixes (#11417)
`isPublicApiRoute()` matched every entry of PUBLIC_API_ROUTE_PREFIXES with
`startsWith()`, but 11 of the 15 entries name ONE route, not a subtree. As a
prefix each also marked every adjacent path sharing its leading characters as
PUBLIC, which skips the MANAGEMENT auth gate.

That is reachable today: Next resolves `/api/usage/om-usage<anything>` to the
dynamic route `/api/usage/[connectionId]`, and that handler carries no auth of
its own — it relies entirely on being classified MANAGEMENT. An unauthenticated
caller therefore reaches `fetchAndPersistProviderLimits()`, which is an
existence oracle over connection ids (409/404/400/200) and, for a connection id
actually starting with `om-usage`, discloses live quota JSON and can drive an
OAuth token refresh (a write side effect) with no credentials.

Split the allowlist by shape:

- PUBLIC_API_ROUTE_PREFIXES keeps only genuine subtrees, every entry ending in
  "/" (asserted by a unit test, so the class cannot come back silently).
- PUBLIC_API_ROUTES_EXACT holds the single routes, matched exactly in both
  spellings.
- The three read-only "prefixes" were single routes too and move to
  PUBLIC_READONLY_CORS_API_ROUTES, matched exactly. classify.ts now asks
  `isPublicReadonlyCorsRoute()` instead of scanning the raw list, so the CORS
  origin relaxation pipeline.ts keys on cannot be inherited by a sibling either
  (`/api/monitoring/health-detail` was taking it).
- `/api/health` deliberately stays in its own set so it keeps classifying as
  `public_prefix`; folding it into the read-only set would widen CORS on it.

dashboardCsrf.ts had a second copy of the prefix scan; it now shares
`isPublicApiRoute()` so the client CSRF exemption and the server classification
cannot disagree. Side effect in the safe direction: the three LOCAL_ONLY oauth
auto-import routes were CSRF-exempt on the client while the server already
required the token — the client now attaches it.

Reported by @ntdat812 (GHSA-74g9-q8f6-793h), with the shape of the fix and the
two gotchas above called out in the report.

Closes GHSA-74g9-q8f6-793h

Co-authored-by: Xiangzhe <bakryun0718@proton.me>
Co-authored-by: Nguyen Thanh Dat <ntdat812.dev@gmail.com>
2026-08-24 15:47:34 -03:00
oyi77
38527e4a21 docs(design): external durable-state DB backend proposal (#8075)
Grounded design answering the five open questions in #8075 with a
measured coupling census of release/v3.8.51: 126 db modules on the sync
prepare()/transaction() shape, 36 pragma sites, 159 SQLite migrations,
9 sqlite-vec + 8 FTS5 files, and a 4-driver SqliteAdapter seam that is
SQLite-only in practice (synchronous API).

Proposal: dialect-agnostic async DurableStateStore port with per-domain
repositories adopted incrementally; Postgres-first with MySQL gated by
a shared dialect-conformance suite; DATABASE_DRIVER config surface with
sqlite as the non-negotiable zero-config default; fail-closed one-way
export tool; five-phase delivery plan.
2026-08-25 01:08:36 +07:00
Jacob Stoner
56d64e29a4 fix(dashboard): expose custom mode-pack option (#11407)
Validado em lote combinado (batch-0824f, junto de #11399/#11400/#11402) contra o tip de release/v3.8.50: typecheck:core limpo, file-size/changelog/complexity/cognitive-complexity OK, 56/56 testes focados passando incluindo os deste PR (tests/unit/autocombo-unification.test.ts).

Baixo risco: expõe a opção "custom" já suportada em runtime (`getModePack("custom") === undefined`, cai de volta para os pesos explícitos dos sliders) no seletor compartilhado de mode-pack da UI. Obrigado pela contribuição!
2026-08-24 14:16:37 -03:00
Jacob Stoner
7b36e45df8 fix(dashboard): normalize explicit auto weights (#11402)
Validado em lote combinado (batch-0824f, junto de #11399/#11400/#11407) contra o tip de release/v3.8.50: typecheck:core limpo, file-size/changelog/complexity/cognitive-complexity OK, 56/56 testes focados passando incluindo os deste PR (tests/unit/combo-scoring-inspector.test.ts).

Baixo risco: normaliza pesos parciais/não-unitários no inspector de diagnóstico (`comboScoringInspector.ts`) reutilizando o normalizador já existente do motor real de scoring, mantendo diagnósticos consistentes com o runtime. Obrigado pela contribuição!
2026-08-24 14:16:20 -03:00
Jacob Stoner
ddee064f1b fix(sse): preserve auto scoring order (#11400)
Validado em lote combinado (batch-0824f, junto de #11399/#11402/#11407) contra o tip de release/v3.8.50: typecheck:core limpo, file-size/changelog/complexity/cognitive-complexity OK, 56/56 testes focados passando incluindo os deste PR (tests/unit/combo-task-aware.test.ts).

Remove `auto` da lista de estratégias task-routing genéricas — coerente com o #11399, que também protege a ordem já computada pelo `auto` contra reordenação por outro pós-processamento. Obrigado pela contribuição!
2026-08-24 14:16:04 -03:00
Jacob Stoner
b1fdfd5ea4 fix(sse): preserve auto-selected first target (#11399)
Validado em lote combinado (batch-0824f, junto de #11400/#11402/#11407) contra o tip de release/v3.8.50: typecheck:core limpo, file-size/changelog/complexity/cognitive-complexity OK (abaixo do baseline), 56/56 testes focados passando incluindo os deste PR (tests/unit/8370-priority-affinity-reorder.test.ts).

Aditivo e coerente: protege a ordem já decidida pelo `auto` contra reordenação pelo pós-processamento de prompt-cache-affinity — mesma linha do #11400. Obrigado pela contribuição!
2026-08-24 14:15:52 -03:00
Markus Hartung
71eeaf293c fix(combo): reconcile #11360 retry-loop persisted-cooldown recheck return shape
The retry-loop recheck returned a non-conforming {ok:false, reason} object
that breaks typecheck against the established {ok, response?} contract used
everywhere else in this function. Aligns with the pre-dispatch skip pattern
(return null after fallbackCount++), matching the PR's own intent: skip this
target and move to the next, not error the whole attempt.

This is a live fix — the broken shape reached origin/release/v3.8.50 via
#11360's own squash-merge and was breaking typecheck:core until now.
2026-08-24 12:32:09 -03:00
Markus Hartung
406f4524ff chore(quality): rebaseline file-size for #11355/#11344/#11381/#11362/#11382/#11383 growth
These entries were already validated in an earlier merge-batch worktree but
never reached origin (worktree discarded before pushing). Re-adding them
here since #11355's test/route.ts growth (1215->1237) is now live on
origin/release/v3.8.50 and fails the frozen cap otherwise.
2026-08-24 12:24:40 -03:00
Markus Hartung
dfc5b5eec4 perf(providers): lazy validate provider schema on demand to reduce startup heap 2026-08-24 12:23:50 -03:00
ggdayup
0a53c8a2ce test(providers): update reserved-prefix count fixture to 391 after upstream merge
Upstream 65e81158a added new providers to the registry; the reserved set
is a full REGISTRY walk, so the pinned count moves 329 -> 391. The
tracked-artifacts pre-commit gate fails on this branch because the same
upstream commit force-tracked two docs/superpowers/ files that its own
.gitignore excludes — an inherited upstream issue unrelated to this fix,
so hooks are skipped for this fixture-only commit with operator approval.
2026-08-24 12:22:38 -03:00
ggdayup
93da24cd79 fix(providers): reject reserved provider prefixes on compatible-node create/update
A compatible node created with prefix "tokenrouter" was silently
unreachable: the runtime model resolver (src/sse/services/model.ts)
skips compatible-node lookup for built-in registry ids/aliases, so
"tokenrouter/qwen/..." routed to the built-in tokenrouter provider and
failed with "No active credentials for provider: tokenrouter" even
though the node itself worked when addressed by its internal id.

Reject reserved prefixes at the write path instead:

- new shared module src/shared/constants/reservedProviderPrefixes.ts
  (REGISTRY ids + aliases, case-sensitive, built lazily) — single
  source of truth consumed by both the runtime guard and the
  validation schemas so they can never drift apart
- createProviderNodeSchema / updateProviderNodeSchema now reject
  reserved prefixes with a clear message naming the colliding prefix
- src/sse/services/model.ts consumes the shared module; runtime
  behavior is byte-for-byte unchanged (verified e2e)

Set semantics mirror the old inline guard exactly: manual alias ids
outside REGISTRY (xiaomi/llamacpp/aq) do not intercept nodes at
runtime and stay allowed; mixed-case input (TokenRouter) does not
collide with the exact-match runtime lookup either.
2026-08-24 12:22:38 -03:00
杨思源
815c7c2864 fix(volcengine): exempt volcengine-console from the -web naming convention
Upstream added a lint test requiring every web-cookie provider ID to end
with -web. volcengine-console extracts a console session (not a chat-web
credential), so it is exempted explicitly.
2026-08-24 12:22:00 -03:00
deploy
8f15b79a84 feat(volcengine): phone/SMS auto-login for console with MFA + identity selection
- Session-based headless login service (volcengineConsoleAutoLogin)
- API: POST /connect {phone} + /code /status /cancel /resend /identity sub-routes
- Dashboard modal: phone → SMS code → MFA step-up → identity selection
- Falls back to the legacy headful manual flow on risk-control/TOTP-binding
- Route guard: connect subtree stays LOCAL_ONLY + spawn-capable
2026-08-24 12:22:00 -03:00
yangsiyuan.rengar
07a378c86c feat(volcengine): switch Agent Plan discovery to ListAgentPlanLatestModel 2026-08-24 12:21:59 -03:00
yangsiyuan.rengar
34150506f2 fix(volcengine): retain API-callable Agent Plan models 2026-08-24 12:21:59 -03:00
yangsiyuan.rengar
76ac1c8b7e feat(volcengine): live model discovery for Ark plan providers
Replace the static curated model lists for volcengine-agent-plan and
volcengine-coding-plan with live discovery from the console APIs
(GetAgentPlanModelMappingMeta / ListArkCodeLatestModel), authenticated by
the console cookie+csrf already captured at plan binding time.

- Add volcenginePlanModelDiscovery.ts: fetch + parse + capability enrichment
  (family->contextLength/vision/reasoning map, conservative default fallback).
  Console calls go through a dynamic undici import to bypass OmniRoute's
  global fetch patch (built for LLM provider traffic, reroutes console hits).
  Coding plan's ListArkCodeLatestModel needs {AccountId:<number>} extracted
  from the console cookie; agent plan's GetAgentPlanModelMappingMeta filters
  PlatformAllowStatus===true && Type==='llm'.
- Remove both plan ids from CURATED_MODEL_ONLY_PROVIDERS so synced models
  merge into /v1/models and the dashboard Sync Models button works.
- sync-models route: short-circuit to console discovery for plan providers
  (the chat API has no /models endpoint); persist via
  replaceSyncedAvailableModelsForConnection.
- volcenginePlanBinding: set autoSync:true on new plan connections so the
  24h modelSyncScheduler refreshes them automatically.
- volcPlanAutoSyncBackfill: idempotent boot-time backfill so pre-existing
  plan connections also enter the scheduler.

Verified end-to-end on local OmniRoute build against live Volcano console:
agent plan synced 7 LLMs, coding plan synced 11 models, /v1/models exposes
all of them (incl. new glm-5-3-260801 / deepseek-v4-flash-260801).
2026-08-24 12:21:59 -03:00
yangsiyuan.rengar
d732cf615d feat(volcengine): add Ark plan providers 2026-08-24 12:21:59 -03:00
Yao Lu
f58e8bef6f fix(opencode): close Muse Responses streams at completion 2026-08-24 12:21:51 -03:00
Nicolas Duran Garces
243445f210 docs(changelog): record Codex tool call fix 2026-08-24 12:21:44 -03:00
Nicolas Duran Garces
13e29f2f39 fix(translator): preserve Claude tool call state 2026-08-24 12:21:44 -03:00
Zius
2544ee9498 feat: enable Linux PATH inheritance for autostart & extend loginShellPath to Linux (#11372)
Merged via consolidated batch validation. Fixes autostart on Linux failing to inherit the user's shell PATH (CLI-dependent features like Kiro's Google OAuth broke). Resolved a conflict against a batch sibling in bin/cli/commands/doctor.mjs (kept the more complete prebuilds-aware candidate list) and setup-claude.mjs (formatting only). Own test (login-shell-path-3321.test.ts, 10/10) passes + typecheck:core clean. Thanks!
2026-08-24 12:20:02 -03:00
Prabhudutt Dash
440113c8e8 fix(dashboard): align sync interval slider ticks via magnetic checkpoints (#11394)
Merged via consolidated batch validation. Model Database sync-interval slider used two incompatible coordinate systems (evenly spaced labels vs a linear 1-168h scale); moves the slider to checkpoint-space so the thumb and labels agree. Own test passes.
2026-08-24 12:13:45 -03:00
Bob.Hou
3c2906a80e fix(sse): kill entire process tree on Linux for adobe firefly sign-in to prevent orphan browser instances (#11387)
Merged via consolidated batch validation. Fixes orphaned browser processes on Linux for Adobe Firefly sign-in: spawns Chrome as a process-group leader (detached:true) and kills -pid instead of the single PID, with self-termination guards. Own test passes.
2026-08-24 12:13:40 -03:00
Bob.Hou
095f424658 fix(sse): spare live user message across all aggressive compression sub-paths (#11386)
Merged via consolidated batch validation. Aggressive compression could collapse the live user's active prompt into a [COMPRESSED:summary] marker; now spares the last user message across all sub-paths (applyAging, fallback summarizer, caveman/lite). Own test passes.
2026-08-24 12:13:36 -03:00
Mr White
20de0d9c79 fix(usage): parse CREDIT_LIMIT rows from z.ai coding-plan quota API (#11378)
Merged via consolidated batch validation. Z.ai's quota API now returns CREDIT_LIMIT rows for GLM Coding Plan subscription keys instead of TOKENS_LIMIT, breaking the dashboard quota card. Own test passes.
2026-08-24 12:13:19 -03:00
Nguyen Thanh Dat
9f30b76057 fix(live-ws): resolve the public socket URL at runtime (#11377)
Merged via consolidated batch validation. Fixes live-ws public socket URL resolution for prebuilt Docker/npm images, where NEXT_PUBLIC_* is inlined at build time and can never carry an operator's runtime value. Own test passes.
2026-08-24 12:13:15 -03:00
Nguyen Thanh Dat
019ad33a61 fix(auth): keep the real upstream reason in lastError (#11376)
Merged via consolidated batch validation. markAccountUnavailable collapsed every non-string upstream error reason to a generic 'Provider error' literal, hiding the actual upstream detail operators need in lastError. Own test passes.
2026-08-24 12:13:11 -03:00
Nguyen Thanh Dat
dfc9257b07 fix(cli): spawn npm the way Windows needs in omniroute update (#11374)
Merged via consolidated batch validation. Fixes omniroute update on Windows (npm.cmd cannot be execFile'd without a shell on Node >=24, nodejs/node#52554). Extracts a shared bin/cli/npm-exec.mjs (also handles Bun, windowsHide) mirroring the existing server-side pattern in src/lib/services/installers/utils.ts. Own tests pass. Note: #11336 fixed the same underlying bug (#11335) with a narrower inline change; closed as duplicate crediting this more complete fix.
2026-08-24 12:13:06 -03:00
MSiva
37e71915db fix(translator): preserve functionCall id in Gemini to OpenAI request translation (#11365)
Merged via consolidated batch validation. Fixes geminiToOpenAIRequest discarding functionCall.id in favor of a random generated id, causing multi-turn tool-call id mismatches against OpenAI-compatible upstreams. Own test passes.
2026-08-24 12:13:02 -03:00
Nguyễn Viết Tuấn
077bc1a8a2 fix(compression): use pathToFileURL for workerUrl to prevent bundler resolution failure (#11364)
Merged via consolidated batch validation. Fixes Webpack/Turbopack production build failure (Module not found: compressionWorker.js) by using pathToFileURL(join(...)) instead of new URL(..., import.meta.url), which static bundler scanning misidentifies as an asset import.
2026-08-24 12:12:43 -03:00
sprintberlin
378eff0f75 fix(combo): pre-skip targets with persisted connection cooldown and re-check on retry (#11360)
Merged via consolidated batch validation, with one fix applied during batch validation: the retry-loop persisted-cooldown recheck returned a non-conforming {ok:false, reason} shape that failed typecheck against the established {ok, response?} contract — aligned it with the pre-dispatch skip pattern (return null after fallbackCount++), matching this PR's own intent (skip the target, don't error the whole attempt). Pre-skips combo targets with a persisted connection cooldown and re-checks fresh before transient retries. Own regression suite (13/13, including the fixed retry-recheck path) passes.
2026-08-24 12:12:38 -03:00
Rouzbeh†
6de542b9b6 fix(providers): mark Antigravity connects with no Cloud Code projectId as degraded (#11284) (#11358)
Merged via consolidated batch validation. Production evidence (VPS docker instance): Antigravity OAuth connects ending without a Cloud Code projectId were persisted as silently active while every model call failed; now persisted as degraded. Own tests pass.
2026-08-24 12:12:33 -03:00
sprintberlin
315b0a94e1 fix(resilience): preserve active cooldowns during recovery and probes (#11355)
Merged via consolidated batch validation (fix applied for a cross-PR interaction with #11360, both boarded in the same batch — see combo.ts reconciliation commit). Startup crash recovery cleared every non-terminal transient cooldown unconditionally, erasing legitimate multi-day weekly quota cooldowns on restart. Now only clears expired/unparseable ones. Own repro tests pass.
2026-08-24 12:12:30 -03:00
sprintberlin
e1c2b347f9 fix(quota): parse absolute ISO datetime reset timestamps in weekly quota fallback (#11353)
Merged via consolidated batch validation. Fixes GLM/Z.AI weekly quota fallback: parseDayGranularityResetMs only recognized 'reset in N days', dropping the real multi-day cooldown when upstream returns a full absolute ISO datetime. Own repro test passes.
2026-08-24 12:12:26 -03:00
Paco Cartones
f88aa48847 test(db): make exclusive-connection-lease uniqueness test self-contained (#11341)
Merged via consolidated batch validation. Test-only fix: exclusive-connection-lease uniqueness test implicitly depended on lease state from an earlier test in the same file (shared DB instance, reset only in test.after) — now self-contained. No production change.
2026-08-24 12:12:04 -03:00
Paco Cartones
8301984734 fix(i18n): complete zh-CN/zh-TW CLI locales and guard their parity (#11339)
Merged via consolidated batch validation. Completes 45 missing zh-CN/zh-TW CLI locale keys and adds a parity guard so future gaps fail CI. Own tests pass.
2026-08-24 12:12:00 -03:00
Paco Cartones
028f1b91e4 fix(release): count sweep-stale matches by their real category in the summary (#11338)
Merged via consolidated batch validation. Fixes sweep-stale-fragments.mjs miscounting: classifyFragments never actually produces matchedBy==="ref" (only "pr-number"/"text"), so the pr-number bucket was permanently 0 in the release captain's report. Own test passes.
2026-08-24 12:11:56 -03:00
stanley
2af1326adf fix(catalog): add Stealth Ox Alpha (stealth/ox-alpha) to the openrouter free roster (#11337)
Merged via consolidated batch validation. Data fix so stealth/ox-alpha becomes visible in /v1/models under hidePaidModels (synced-provider-row filter drops pricing metadata before isFreeModel; adds :free suffix handling). Own test passes.
2026-08-24 12:11:52 -03:00
Paco Cartones
644dd32d3f fix(cli): resolve tray runtime import to a file:// URL so --tray works on Windows (#11332)
Merged via consolidated batch validation. Fixes omniroute server --tray on Windows: absolute paths passed to dynamic import() are parsed as URLs, and a Windows drive letter (C:) isn't a supported URL scheme. Resolves via pathToFileURL. Own regression test passes.
2026-08-24 12:11:49 -03:00
Diego Rodrigues de Sa e Souza
9df3f8923d fix(build): stop bundling the better-sqlite3 stub at runtime (#11343) (#11391)
Merged via consolidated batch validation (worktree `.claude/worktrees/batch-0824e`, 27-PR batch). Critical fix: next.config.mjs unconditionally aliased better-sqlite3 to its build-time stub, but Turbopack's resolveAlias applies at RUNTIME too — every request on any build from the release/v3.8.50 tip answered HTTP 500 because the real driver was never loaded. Gates green; own tests pass.
2026-08-24 12:11:36 -03:00
oyi77
fa7ccaf679 fix(providers): stop silently dropping reasoning effort on opencode families (#10788)
Dispatch: OpencodeExecutor.transformRequest stripped the effort-suffixed
alias (e.g. glm-5.2-high) down to the base id and injected a flat
reasoning_effort body field — but opencode-go's ChatCompletionRequest has
no such field for non-DeepSeek families, so the tier never reached the
upstream and every request ran at default effort. Only DeepSeek V4
accepts the flat field (its native contract, #4647), so it keeps the old
rewrite; every other family now forwards the aliased model id verbatim,
which is their only native effort mechanism.

Registry: declare supportedThinkingEfforts on the base rows that had
aliases but no tier vocabulary — opencode-go (glm-5.2, mimo-v2.5,
grok-4.5, hy3, kimi-k3, qwen3.7-max/plus), the shared zen/go row
(qwen3.6-plus) and opencode-zen (deepseek-v4-pro/flash, glm-5.2,
kimi-k3) — so catalog variant synthesis (#9485) and
sanitizeReasoningEffortForProvider clamp from one source of truth.
nvidia z-ai/glm-5.2 is declared reasoning-capable with an EMPTY tier
list: its upstream only exposes a binary enable_thinking switch
(mapNvidiaGlm52ReasoningParams), so no honest tiers exist to advertise.

Ollama Cloud declarations were already landed upstream (#11307); this
change covers the remaining providers named in #10788.
2026-08-24 22:06:59 +07:00
KaspaPulse
85b2a06fad feat(dashboard): surface exclusive managed leases in Sessions view
Reuse the official exclusive-lease authority for durable idle visibility, and use pending-request accounting only for the localized active indication. Preserve legacy session fields and rows while de-duplicating leased connections and withholding lease ownership and fencing data.

Refs #10514

Follow-up to #10362
2026-08-24 13:53:27 +00:00
Markus Hartung
0b7ac870ef sync with tip before push 2026-08-24 09:55:31 -03:00
Markus Hartung
9fedc1c411 merge #11381 onto updated tip 2026-08-24 09:50:48 -03:00
Markus Hartung
e589831952 sync with tip before push 2026-08-24 09:46:03 -03:00
Diego Rodrigues de Sa e Souza
04d2a60331 fix(video): make one-frame scene sampling deterministic (#11344)
Merged via consolidated batch validation. Makes scene_aware Video Bridge sampling deterministic for a one-frame budget: falls back to the midpoint of the active full-video/focus window and reports policyEffective: uniform (a single scene candidate can't preserve both temporal ends). Adds opt-in real-FFmpeg fixture matrix (rapid edge cuts, one-frame budget, static/gradual scenes, sub-second clips, detector failure). Static gates green; own regression suite (videoBridgeSampler.test.ts, video-bridge-sampler-ffmpeg.test.ts) passed in the combined-batch run. Related to #9760. Thanks!
2026-08-24 09:44:50 -03:00
Markus Hartung
d23bfefec0 merge #11383 onto updated tip 2026-08-24 09:44:36 -03:00
Markus Hartung
c8ad44e018 merge #11350 onto updated tip 2026-08-24 09:41:55 -03:00
Diego Rodrigues de Sa e Souza
c83116e634 fix(video): isolate drill-down cache by principal (#11369)
Merged via consolidated batch validation (worktree `.claude/worktrees/batch-0824d`). Video Bridge FU-08 drill-down cache substrate hardening (explicitly PARTIAL per the PR body — no production producer/callsite feeds this cache yet): canonical isolation by principalId+sessionId+videoRef, loopback broker auth, strict Zod contracts, per-principal + global LRU quotas, full JPEG decode/re-encode with truncated-scan and polyglot-tail rejection, cancellation-safe atomic replacement. Static gates green; own regression suite (videoBridgeDrilldown.test.ts, video-bridge-drilldown-authz.test.ts, video-bridge-drilldown-route.test.ts) passed in the combined-batch run. Thanks!
2026-08-24 09:39:40 -03:00
Diego Rodrigues de Sa e Souza
7715825cb8 fix(video): harden visual frame deduplication (#11382)
Merged via consolidated batch validation (worktree `.claude/worktrees/batch-0824d`), stacked on the just-merged #11362 as documented. Moves the Video Bridge frame cap to post-dedup, bounds the perceptual candidate pool to at most 2x budget (max 16), includes the dedup policy/version in result-cache identity, adds cooperative abort checks to the comparator loop. Static gates green; own dedup/cache-version regression suite passed in the combined-batch run (grayscale-16x16-mean-cells-v2 policy, real fixtures). Thanks!
2026-08-24 09:31:00 -03:00
Diego Rodrigues de Sa e Souza
761d38f433 fix(video): harden result cache identity and coalescing (#11362)
Merged via consolidated batch validation (worktree `.claude/worktrees/batch-0824d`). Completes the Video Bridge FU-01 cache-hardening slice: fingerprints authorized video bytes + result-affecting dimensions before a persistent cache hit, strict metadata validation with corrupt-entry recompute, TTL/LRU bounds by count/entry-bytes/aggregate-bytes, coalesced protected HTTPS downloads isolated by tenant, deadline/abort-bounded model selection. Static gates green; own regression suite (tests/unit/guardrails/videoBridgeResultCache.test.ts) passed in the combined-batch run. Thanks!
2026-08-24 09:26:06 -03:00
Diego Rodrigues de Sa e Souza
c6963ca5dd fix(changelog): require verified reconciliation ledger (#11345)
Merged via consolidated batch validation (worktree `.claude/worktrees/batch-0824d`). Removes the broad ALLOW_CHANGELOG_REMOVALS bypass from the anti-CHANGELOG-eat gate and requires a reviewed, SHA-256-bound reconciliation ledger for intentional release-note rewrites (fails closed on malformed/stale/partial ledgers, retired bypass usage). Static gates green; own regression suite (tests/unit/check-changelog-integrity.test.ts, tests/unit/merge-train-plan.test.ts) passed in the combined-batch run — 15/15 CLI/ledger cases. Related to #9985. Thanks!
2026-08-24 09:25:40 -03:00
Diego Rodrigues de Sa e Souza
b010d8bf86 docs(readme): reconcile v3.8.50 metrics and contributors (#11356)
Merged via consolidated batch validation (worktree `.claude/worktrees/batch-0824d`). Reconciles README/diagram claims against the live release branch with explicit, non-conflated denominators (merged-PR ranking vs GitHub Contributors REST vs normalized Git census) and adds a repository-local SVG validator. Static gates green; own SVG-validator + render-pipeline tests (tests/unit/docs-validate-svg.test.ts) passed in the combined-batch run, docs:check-all clean per the PR's own evidence. Thanks!
2026-08-24 09:25:29 -03:00
Diego Rodrigues de Sa e Souza
fdcd15e6a9 docs(openapi): document try proxy operation (#11363)
Merged via consolidated batch validation (worktree `.claude/worktrees/batch-0824d`). Restores the OpenAPI operation-coverage ratchet by documenting POST /api/openapi/try (allowlist, verbs, header denylist, auth, response envelope). Static gates green (typecheck:core, file-size, changelog-integrity, complexity, cognitive-complexity); own contract test (tests/unit/openapi-security-tiers.test.ts) passed in the combined-batch run. Thanks!
2026-08-24 09:25:18 -03:00
Diego Rodrigues de Sa e Souza
12b8df02dd fix(catalog): keep large builds event-loop responsive (#11367)
Merged via consolidated batch validation (worktree `.claude/worktrees/batch-0824d`, 11-PR video-bridge/catalog/ops batch, tip `dafb4ae8`). Fixes the #9147 catalog-scale event-loop regression: reuses one build-local capability snapshot, yields cooperatively during catalog/virtual-pool construction, reads only persisted TTL settings. Static gates: typecheck:core, file-size, changelog-integrity, complexity, cognitive-complexity all green. Own regression test (tests/unit/9147-catalog-eventloop-yield.test.ts) reproduced the RED→GREEN transition in isolated runs per the PR's own evidence; under current shared-devbox load (10-15, multiple parallel sessions) the test intermittently reports INFRA-RED exactly as the PR body pre-disclosed (documented starvation signature, not a code defect). Thanks for the careful RED/GREEN + INFRA-RED discipline.
2026-08-24 09:24:59 -03:00
Diego Rodrigues de Sa e Souza
38d21afc2d docs(changelog): link FU-04 pull request 2026-08-24 08:40:33 -03:00
Diego Rodrigues de Sa e Souza
05e76d6e76 docs(changelog): link FU-07 pull request 2026-08-24 08:31:59 -03:00
Diego Rodrigues de Sa e Souza
93135f8e18 feat(guardrails): add focused video analysis mode 2026-08-24 07:30:28 -03:00
Diego Rodrigues de Sa e Souza
22086a73fa fix(video-bridge): validate structural segment sampling 2026-08-24 06:35:58 -03:00
Diego Rodrigues de Sa e Souza
d4ade9d1d3 fix(video): separate tenant scope from download hash 2026-08-24 05:55:00 -03:00
Diego Rodrigues de Sa e Souza
f54c93c879 fix(video): key download flights with process HMAC 2026-08-24 05:16:13 -03:00
Diego Rodrigues de Sa e Souza
e2e48fdab8 docs(changelog): link Video Bridge cache fix PR 2026-08-24 04:37:54 -03:00
Diego Rodrigues de Sa e Souza
d2cea0811a fix(video): harden result cache identity and bounds 2026-08-24 04:31:27 -03:00
Diego Rodrigues de Sa e Souza
2f18a85310 docs(changelog): link Video Bridge contact-sheet fix 2026-08-24 03:35:10 -03:00
Diego Rodrigues de Sa e Souza
38969ad16b fix(video-bridge): render timestamped contact sheets 2026-08-24 03:20:43 -03:00
Diego Rodrigues de Sa e Souza
dafb4ae808 fix(deps): keep unused pnpm peers out of production (#11342)
* fix(deps): keep unused pnpm peers out of production

* docs(changelog): link dependency policy fix to PR 11342
2026-08-24 02:26:02 -03:00
Zartharas
c89bdb21a2 chore(changelog): document web-session contract 2026-08-24 00:07:51 -05:00
Zartharas
c5e694131a style(test): format web-session contract regression 2026-08-24 00:07:33 -05:00
Erick Kinnee
338c05dc6a fix(models): expose Ollama Cloud native effort tiers (#11307)
Validated on a 17-PR combined board: models-catalog-combo-metadata + ollama-cloud-reasoning-effort-tiers-10788 within the board's 287/287, typecheck:core clean, check:open-sse-typecheck clean, vitest 405/405. Publishes Ollama Cloud's native none/low/medium/high/max effort vocabulary for reasoning-capable passthrough/tagged models with no exact registry declaration, adds none to DeepSeek V4/GLM 5.x, and preserves narrower exact-model vocabularies (GPT-OSS) via intersection. Refs #10788. Thank you @ekinnee!
2026-08-24 01:55:47 -03:00
Nguyen Thanh Dat
6945bbaaba fix(db): escape regex metacharacters in group model patterns (#11311)
Validated on a 17-PR combined board: group-model-pattern-regex-escape within the board's 287/287, typecheck:core clean. matchesModelPattern() only substituted * before compiling to RegExp — every other metacharacter kept its regex meaning, so a malformed group pattern (unbalanced parens/brackets) threw uncaught and broke EVERY request for keys in that group, not just the malformed rule (isModelAllowedForKey has no try/catch and runs on the chat completion path and the /v1/models catalog). Thank you @ntdat812!
2026-08-24 01:55:42 -03:00
Ravi Tharuma
690f684bfc feat(audio): add native ElevenLabs HTTP compatibility routes (#11312)
Validated on a 17-PR combined board: elevenlabs-native-routes + hard-session-lease-bypass-inventory (9/9) within the board's 287/287, typecheck:core clean. Native ElevenLabs compatibility routes (voices, TTS, STT) reusing the stored credential via quota-preflight, sent only as xi-api-key; client authorization headers never forwarded. Closes #10556. Thank you @RaviTharuma!
2026-08-24 01:55:36 -03:00
Ravi Tharuma
c3cd1f94c0 feat(services): expose sanitized CLIProxyAPI account health (#11314)
Validated on a 17-PR combined board: cliproxy-accounts + cliproxy-tab + cliproxy-account-health + cliproxy-resolve-spawn-args-6877 (16/16) within the board's 287/287, typecheck:core clean, env-doc-sync clean. Exposes a sanitized read-only CLIProxyAPI account health view (5s-bounded client, explicit allowlist excluding names/paths/emails/tokens/status messages) through a management-authenticated API + dashboard card. Closes #6342. Thank you @RaviTharuma!
2026-08-24 01:55:31 -03:00
Webman
c21460f22a fix(lint): drain release-green hard failures on release/v3.8.50 (#9985) (#11317)
Validated on the resolved merge against the current release tip: pack-artifact-policy + cli-mcp-call-commands + cli-resilience-commands + cli-skills-commands + model-hide-multikey-11300 39/39, typecheck:core clean, eslint clean. Resolved a pt-BR.json wording conflict against #11322 (kept the tip's wording, semantically identical). Drains the real lint-fallout from the wave that was blocking the release-green verdict — dead code + newly-enforced React-Compiler hook rules. Thank you @jonlwheat2-gif!
2026-08-24 01:54:31 -03:00
Ravi Tharuma
9b14896a6c feat(api): add Google AI Studio Gemini TTS (#11315)
Validated on a 17-PR combined board: gemini-tts + vertex-media + audio-speech-handler (41/41) within the board's 287/287, typecheck:core clean. Registers public google/gemini-*-tts speech models and translates OpenAI-compatible /v1/audio/speech to the AI Studio generateContent audio contract, reusing the Vertex inline-audio/PCM/WAV conversion path. Batch TTS only, Gemini Live is out of scope. Thank you @RaviTharuma!
2026-08-24 01:50:44 -03:00
Ravi Tharuma
29f26293c3 feat(compression): isolate sync engines in bounded worker pool (#11318)
Validated on a 17-PR combined board: compression-worker + colocate-standalone-esm-scope within the board's 287/287, typecheck:core clean, env-doc-sync clean. Offloads eligible sync compression engines into a bounded worker_threads pool with a strict serializable DTO boundary and fail-open on spawn/worker/timeout failure. Closes #11023. Thank you @RaviTharuma!
2026-08-24 01:50:39 -03:00
Nguyen Thanh Dat
cb11592441 fix(db): judge the proxy URL host by address, not by spelling (#11319)
Validated on a 17-PR combined board: upstream-proxy-host-spelling 8/8 within the board's 287/287, typecheck:core clean. Routes src/lib/db/upstreamProxy.ts through the shared outbound-guard helpers instead of a private dotted-quad regex copy that had drifted since #10843 — closes the IPv4-mapped IPv6, ULA, link-local and CGNAT bypasses while preserving the deliberate loopback allow (CLIProxyAPI on localhost:8317). Multicast widened from /224\. to the full 224.0.0.0/4, called out explicitly. Thank you @ntdat812!
2026-08-24 01:50:35 -03:00
Ravi Tharuma
5ee646e68e fix(github): verify access tokens during health checks (#11320)
Validated on a 17-PR combined board: token-health-check + token-health-no-refresh-token-expired-5326 + token-refresh-service within the board's 287/287, typecheck:core clean. GitHub access-token-only connections are now actively verified on each due health interval (via the existing Copilot token exchange); the parent credential is marked expired only on a confirmed 401, never on 403/429/5xx/network failures; response bodies and transport messages no longer enter token-refresh logs. Closes #10352. Thank you @RaviTharuma!
2026-08-24 01:50:30 -03:00
Paco Cartones
6984676d95 fix(quality): report the real failure line and stop double-counting ci.yml gates (#11321)
Validated on a 17-PR combined board: validate-release-green within the board's 287/287, typecheck:core clean. Two accuracy bugs in the release-green verdict tool: an unanchored regex blamed a passing test line (matching a filename containing 'fail'), and 6 gates were double-recorded as both hard-failure and drift due to an id-format mismatch (ci.yml script name vs curated id). Found while reading the #9985 verdict — good catch.
2026-08-24 01:50:26 -03:00
Paco Cartones
79f8ae9d1e fix(i18n): add the 3 pt-BR CLI keys that break the locale parity test (#11322)
Validated on a 17-PR combined board: typecheck:core clean, gates within baseline. Restores 3 missing pt-BR CLI keys (setup.opencode, serve.tls_cert, serve.tls_key) — parity restored, 823/823. Thank you @pacocartones!
2026-08-24 01:49:52 -03:00
Nguyen Thanh Dat
04b2c47940 fix(i18n): restore three placeholders dropped from the pt catalogue (#11325)
Validated on a 17-PR combined board: i18n-placeholder-parity within the board's 287/287, typecheck:core clean. Restores 3 dropped placeholders in pt.json (the visible one: the cache tile's subtitle was repeating its own label instead of showing the total) and adds a 42-locale placeholder-set gate so this class of drift can't recur silently. Thank you @ntdat812!
2026-08-24 01:49:48 -03:00
Paco Cartones
24ac71465e test(db): make singleton reset survive the full suite and un-skip the 3 DB-state tests (#11327)
Validated on a 17-PR combined board: capture-critical-db-state 7/7 (all three previously-skipped tests now run) within the board's 287/287, typecheck:core clean. Fixes the racy DATA_DIR-after-dynamic-import isolation and removes a duplicate type declaration. Thank you @pacocartones!
2026-08-24 01:49:42 -03:00
Nguyen Thanh Dat
8d6f91b558 fix(security): refuse proxy-authorization and proxy-authenticate upstream (#11328)
Validated on a 17-PR combined board: upstream-headers-proxy-auth within the board's 287/287, typecheck:core clean, gates within baseline. proxy-authorization and proxy-authenticate join the FORBIDDEN denylist — forwarding proxy-authorization to a model provider would hand that provider the operator's own proxy credential. Thank you @ntdat812!
2026-08-24 01:49:37 -03:00
Diego Rodrigues de Sa e Souza
c3698eedcb fix(dashboard): route the Adapta tutorial CTA through the branded shortener (#11329)
Validated on a 17-PR combined board: TSX parses clean, eslint clean. Adapta tutorial CTA href now points at the branded shortener (link.omniroute.online/adapta) while keeping the visible link text as the real domain. Completes #11196's shortener rollout.
2026-08-24 01:49:32 -03:00
Aman
9e7096a048 refactor(providers): use canonical contract imports 2026-08-23 23:36:47 -05:00
Aman
a84f0b683e test(providers): cover web-session contract publication 2026-08-23 23:36:47 -05:00
Aman
ae0b79ab7c feat(api): publish web-session credential contract 2026-08-23 23:36:47 -05:00
Aman
6daa31de0b feat(providers): add web-session contract builder 2026-08-23 23:36:46 -05:00
Diego Rodrigues de Sa e Souza
adca3b881c fix(kie): map remaining google-imagen Market ids to their real KIE upstream ids (#11326)
Merging --admin with red discrimination (merge-gates §4). Fails: ESLint warnings ratchet drift (inherited base-red), Unit Tests shards containing stream-timing.test.ts (CPU-contention timing flake, assert.ok(total >= 15)ms — unrelated to this PR's scope, open-sse/handlers/imageGeneration.ts), and dast-smoke (advisory, isRequired:null).
2026-08-24 01:10:23 -03:00
Diego Rodrigues de Sa e Souza
ac02c5b42f fix(resilience): don't clear an active rate-limit cooldown for non-quota_exhausted errors (#11277) (#11310)
Merging --admin: only fails are ESLint warnings ratchet drift (inherited base-red) and dast-smoke (advisory, isRequired:null). Zero overlap with this PR's scope (src/lib/usage/providerLimits.ts).
2026-08-23 22:55:33 -03:00
Diego Rodrigues de Sa e Souza
07d1816a45 fix(providers): hidden models leak into GET /v1/models (#11300) (#11309)
Merging --admin: only fails are ESLint warnings ratchet drift (inherited) and dast-smoke (advisory, isRequired:null). Zero overlap with this PR's file scope (src/app/api/v1/models/catalog.ts).
2026-08-23 22:35:29 -03:00
193 changed files with 4139 additions and 3369 deletions

View File

@@ -46,7 +46,7 @@ Repository map and Reference Documentation sections below.
## Project at a Glance
**OmniRoute** — unified AI proxy/router. One endpoint, 353 LLM providers, auto-fallback.
**OmniRoute** — unified AI proxy/router. One endpoint, 351 LLM providers, auto-fallback.
| Layer | Location | Purpose |
| ------------- | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |

View File

@@ -1,5 +1,5 @@
# ── Multi-stage Dockerfile for Native Bun Runtime (web-latest-bun) ───────────
FROM oven/bun:1.3.14-slim AS base
FROM oven/bun:1.4.0-slim AS base
WORKDIR /app
RUN apt-get update \
@@ -50,7 +50,7 @@ ENV NODE_ENV=production
RUN bun run --quiet build
# ── Runner Base stage (100% Bun Native Production Runtime) ──────────────────
FROM oven/bun:1.3.14-slim AS runner-base
FROM oven/bun:1.4.0-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 → 353 providers — 90+ 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. 353 AI providers · 90+ free tiers · ~1.51B 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 → 351 providers — 90+ 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. 351 AI providers · 90+ free tiers · ~1.51B free tokens/mo · 19 routing strategies · $0 to start."/>
</div>
@@ -210,7 +210,7 @@ curl http://localhost:20128/v1/chat/completions \
</div>
<img src="./docs/diagrams/promise-pillars.svg" width="100%" alt="The Promise — One endpoint and 353 providers. Automatic fallback keeps routing while another healthy target is available. Six pillars: resilient fallback across 353 providers · up to 95% token savings on eligible workloads · $0 to start with 90+ free tiers and 56 recurring/keyless free-forever providers · 35 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 351 providers. Automatic fallback keeps routing while another healthy target is available. Six pillars: resilient fallback across 351 providers · up to 95% token savings on eligible workloads · $0 to start with 90+ free tiers and 56 recurring/keyless free-forever providers · 35 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/>
@@ -461,7 +461,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: 353 providers, 90+ 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 43 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: 351 providers, 90+ 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 43 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>
@@ -559,7 +559,7 @@ the current catalog at **[radar.omniroute.online/planos](https://radar.omniroute
- **🖼️ New endpoints** — `/v1/ocr` (Mistral OCR) and `/v1/audio/translations` (Whisper-style) round out the media surface. → [API Reference](docs/reference/API_REFERENCE.md)
- **🎨 Image / video / audio generation** — one API for media: xAI Grok Imagine & Novita AI video, ComfyUI, Freepik, Adobe Firefly, Microsoft Designer, Segmind, EdgeTTS. → [API Reference](docs/reference/API_REFERENCE.md)
- **🌍 Deployment & ops** — reverse-proxy `basePath`, browser-language auto-detect, per-key device tracking, root-less MITM trust, zh-TW localization. → [Environment](docs/reference/ENVIRONMENT.md)
- **🤝 More providers & agents** — Cursor Cloud Agent, Grok Build (xAI) with browser + OAuth login, Ollama first-class card, Claude Opus 5 & Sonnet 5, Kimi official partnership (Code/Web/Moonshot), Zed, Requesty, SenseNova, Yuanbao, Agnes AI… and a refreshed **353-provider catalog**. → [Providers](docs/reference/PROVIDER_REFERENCE.md)
- **🤝 More providers & agents** — Cursor Cloud Agent, Grok Build (xAI) with browser + OAuth login, Ollama first-class card, Claude Opus 5 & Sonnet 5, Kimi official partnership (Code/Web/Moonshot), Zed, Requesty, SenseNova, Yuanbao, Agnes AI… and a refreshed **351-provider catalog**. → [Providers](docs/reference/PROVIDER_REFERENCE.md)
- **📡 Routing transparency** — every response carries an `X-OmniRoute-Decision` header naming the strategy/provider/latency that served it, a new `cache-optimized` combo strategy + Auto-Combo `cacheAffinity` factor route repeat requests back to the connection holding the cached prefix, and a read-only `/v1/auto-combo/{channel}/candidates` endpoint exposes an `auto/*` channel's live candidate pool. → [Auto-Combo](docs/routing/AUTO-COMBO.md)
- **⚡ Local performance & infra** — one-click local Redis, Cloudflare Workers / Deno Deploy relay deployers, Bifrost & Mux as supervised embedded services. → [Embedded Services](docs/frameworks/EMBEDDED-SERVICES.md)
@@ -642,11 +642,11 @@ of your shell history. → [CLI Integrations](docs/guides/CLI-INTEGRATIONS.md)
<div align="center">
## 🌐 353 AI Providers — 154 Catalog-Marked Free
## 🌐 351 AI Providers — 154 Catalog-Marked Free
</div>
> **353 registered providers** across the canonical chat, media, search, local, cloud-agent and system collections, including **154 carrying `hasFree: true` discovery metadata**. The chat model registry covers **268 providers / 2,566 distinct provider-model pairs / 1,312 raw model IDs**; the separate free-budget catalog has **455 per-model rows**, **40 recurring pools** and **56 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).
> **351 registered providers** across the canonical chat, media, search, local, cloud-agent and system collections, including **154 carrying `hasFree: true` discovery metadata**. The chat model registry covers **268 providers / 2,566 distinct provider-model pairs / 1,312 raw model IDs**; the separate free-budget catalog has **455 per-model rows**, **40 recurring pools** and **56 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">

View File

@@ -1 +0,0 @@
- **feat(routing):** subscription-first auto groupings — `auto/subscription` routes only through plan-included connections with a documented hard-stop overage and fails closed on exhaustion, while `auto/thrifty` orders the pool `subscription → keyless → free → cheap → premium` and steps up one rung at a time as each is exhausted. Billing class comes from a curated per-connection catalog (uncurated is treated as metered, never plan-included), both reuse STRICT_ZERO_COST's per-connection verification, and a quota reading whose `resetAt` has passed is now refreshed regardless of TTL so routing returns to plan capacity as soon as it resets ([#11146](https://github.com/diegosouzapw/OmniRoute/pull/11146))

View File

@@ -1 +0,0 @@
- **Provider connections:** keep `tokenExpiresAt` when a connection is created. The create-path allowlist omitted it, so every insert stored NULL and the dashboard token badge could read a fresh connection as expired until its first background refresh ([#11368](https://github.com/diegosouzapw/OmniRoute/pull/11368)).

View File

@@ -0,0 +1 @@
- **fix(electron):** the packaged Windows desktop build now passes the #7592 cold-restart smoke end to end. Five defects were found and fixed on the way: (1) optional-pack staging failed on any machine whose `tar` is GNU tar — it reads the drive letter in an absolute `-f C:\...` archive path as a remote rsh host (`Cannot connect to C:`), so staging now passes a bare filename with `cwd` at the tarball directory and surfaces tar stderr on failure; (2) the Electron `files` allowlist was missing `lib/loginHeaderCapture.js`, so the packaged main process crashed at startup with `Cannot find module './lib/loginHeaderCapture'`; (3) electron-builder ≥26 injects an `!**/node_modules/**` ignore into every extraResources pattern list that no positive filter can override, which silently dropped the staged runtime `node_modules` (including the better-sqlite3 N-API prebuild) from `resources/app` — a fresh v3.8.50 desktop build would have shipped with no native SQLite driver and reproduced the #7592 sql.js fallback on every machine; an `afterPack` hook now restores the staged `node_modules` after packing; (4) the packaged-app smoke harness redirected `USERPROFILE` into its temp DATA_DIR, but Electron resolves the Roaming profile from `%USERPROFILE%\AppData\Roaming\<name>` (USERPROFILE takes precedence over the APPDATA env var) and the path service throws instead of creating it, so `requestSingleInstanceLock()` returned false and the app exited(0) silently before `app.whenReady()` — the harness now pre-creates the derived tree, and `ensureSmokeEnvDirs` is exported and unit-tested; (5) the #7592 driver assertion parsed a `[DB] Driver: ...` line that the server's primary DB path never printed (only the unused `openDatabaseAsync()` did) — `getDbInstance()` now emits the same line on open so the guard can actually assert the native driver. Also: the smoke env-allowlist unit test hardcoded POSIX paths and could only pass on Linux/macOS; expectations are now host-agnostic, with new regression tests for the USERPROFILE-derived tree and `tarPack` under absolute Windows-style paths

View File

@@ -0,0 +1 @@
- **fix(memory):** Embedding Model Quick select, Embedding Source remote dropdown, and Rerank selector now list every configured provider with embedding/rerank support instead of only chat-catalog text matches plus OpenRouter live discovery; a generic OpenAI-compatible `/embeddings` + Cohere-compatible `/rerank` runtime fallback resolves any configured chat provider's embedding/rerank endpoint, so unlisted providers no longer fail with "Unknown embedding provider"; both memory selectors gained a free-text model override

View File

@@ -12,6 +12,7 @@
"@dnd-kit/core",
"@dnd-kit/sortable",
"@dnd-kit/utilities",
"@eslint/compat",
"@huggingface/transformers",
"@lobehub/icons",
"@modelcontextprotocol/sdk",
@@ -60,6 +61,7 @@
"eslint",
"eslint-config-next",
"eslint-plugin-sonarjs",
"espree",
"express",
"fast-check",
"fetch-socks",

View File

@@ -2372,16 +2372,6 @@
"count": 2
}
},
"src/lib/guardrails/videoBridgeContactSheet.ts": {
"@typescript-eslint/no-unused-vars": {
"count": 1
}
},
"src/lib/guardrails/videoBridgeRuntime.ts": {
"@typescript-eslint/no-unused-vars": {
"count": 1
}
},
"src/lib/guardrails/visionBridgeHelpers.ts": {
"@typescript-eslint/no-unused-vars": {
"count": 1
@@ -2422,11 +2412,6 @@
"count": 1
}
},
"src/lib/memory/embedding/index.ts": {
"no-restricted-imports": {
"count": 1
}
},
"src/lib/memory/genericBackend.ts": {
"@typescript-eslint/no-unused-vars": {
"count": 1

View File

@@ -478,8 +478,7 @@
"_rebaseline_2026_08_23_tip_drift_post_batch0823": "Tip drift after the 2026-08-23 merge wave: chatBodyAdmission.ts 1009->1118 (+109, gate count incl. +1) and auth.ts 3337->3344 (+7), both grown by merges already on origin/release/v3.8.50 (verified identical on the pristine tip) — not by the codex-appserver-hardening PR that carries this bump. Owner pre-authorized baseline bumps 2026-08-22.",
"_rebaseline_2026_08_24_11355_cooldown_recovery_guards": "PR #11355 own growth: test/route.ts 1215->1237, +22 (startup crash-recovery guard: clearStaleCrashCooldowns() now parses the persisted rate_limited_until deadline and skips clearing rows still genuinely in the future, instead of clearing every non-terminal cooldown unconditionally). Cohesive fix at the existing test-route dispatch chokepoint alongside the #11141 probe builder. Covered by tests/unit/startup-stale-cooldown-recovery.test.ts + tests/unit/repro-zai-cooldown-cleared-by-connection-test.test.ts.",
"src/lib/guardrails/videoBridgeRuntime.ts": 1009,
"_rebaseline_2026_08_24_video_bridge_fu02_fu07_sampler": "PRs #11344 (FU-02 one-frame scene-aware determinism) + #11381 (FU-07 opt-in segment_aware structural sampling) own growth: videoBridgeRuntime.ts <1000->1009, +9 (sum of both boarded together in the same merge-batch). #11344 adds the deterministic one-frame midpoint fallback + policyEffective=uniform report at the existing scene_aware seam; #11381 adds the bounded local-only FFmpeg structural pre-analysis pass (scene/freeze/blur/exposure/SI-TI) and its budget-reallocation logic. Covered by tests/unit/guardrails/videoBridgeSampler.test.ts, tests/unit/guardrails/videoBridgeFu07StructuralSampling.test.ts, tests/integration/video-bridge-sampler-ffmpeg.test.ts. Owner pre-authorized rebaseline for legitimate PR growth (2026-08-19 directive).",
"open-sse/services/autoCombo/virtualFactory.ts": 1130
"_rebaseline_2026_08_24_video_bridge_fu02_fu07_sampler": "PRs #11344 (FU-02 one-frame scene-aware determinism) + #11381 (FU-07 opt-in segment_aware structural sampling) own growth: videoBridgeRuntime.ts <1000->1009, +9 (sum of both boarded together in the same merge-batch). #11344 adds the deterministic one-frame midpoint fallback + policyEffective=uniform report at the existing scene_aware seam; #11381 adds the bounded local-only FFmpeg structural pre-analysis pass (scene/freeze/blur/exposure/SI-TI) and its budget-reallocation logic. Covered by tests/unit/guardrails/videoBridgeSampler.test.ts, tests/unit/guardrails/videoBridgeFu07StructuralSampling.test.ts, tests/integration/video-bridge-sampler-ffmpeg.test.ts. Owner pre-authorized rebaseline for legitimate PR growth (2026-08-19 directive)."
},
"_rebaseline_base_2026_08_10_proxyfetch": "Base-red fix (green-prs sweep, issue #9985): open-sse/utils/proxyFetch.ts 1207 > cap 1000 — new proxied-TLS fetch helper introduced by the Fal reference-image work. Owner-authorized quick rebaseline to green; structural slim tracked for v3.9.0.",
"_rebaseline_2026_07_27_v3849_train2": "Merge-train 2 (7 PRs) — owner-approved 2026-07-27. Single entry: chatCore.ts 4955->5006 (#8595, Responses multi-turn image compaction before the context hard-reject). Genuine irreducible growth at the existing compaction chokepoint in handleChatCore — the PR adds a last-resort retry against the concrete budget plus the estimateFinalInputTokens helper, both wired at the pre-existing call site rather than a new branch. Covered by tests/unit/8560-responses-image-compaction.test.ts (4 tests).",
@@ -647,6 +646,5 @@
"_rebaseline_2026_08_18_imageregistry_merge_train": "merge-train 2026-08-18 (owner-authorized, /merge-prs batch of 84): open-sse/config/imageRegistry.ts crossed the 1000-line new-file cap for the first time purely from combining three independent, already-legitimate provider registrations boarded in the same local merge-train — #10542 (aihorde optional-key image catalog), #10494 (gemini-web image generation), #10594 (freepik/magnific provider rename + validation). 996 on release tip -> 1019 on the train tip. Each PR individually adds a small, additive IMAGE_PROVIDERS registry entry at the existing chokepoint; none crosses the cap alone. Not modularized as part of this train's gate fix (out of scope for a merge reconciliation, not a feature change). Covered by each PR's own focused tests (aihorde-image-catalog/generation, gemini-web image tests, freepik/magnific provider tests).",
"_rebaseline_2026_08_20_v3850_merge_train_batch1": "Merge-train batch1 (2026-08-19/20, 30 PRs boarded onto release/v3.8.50): gateways.ts 1255->1268 = PR #10722 (Token Kiosk OpenAI-compatible provider gateway catalog entry, +13 declarative lines, same god-file no-split rationale as prior gateways.ts rebaselines); chatHelpers.ts (uncapped, not previously frozen) new 1017 = PR #10797 (relay/bifrost error normalization, +23/-2, own-PR growth, existing file already near cap from accumulated chokepoint wiring per its own rebaseline history above); chatBodyAdmission.ts (uncapped) new 1005 = pre-existing base-red on the pure release tip (1004>1000 before this train boarded anything, no PR in this batch touches this file) — frozen here at its current size, not authorizing further growth. Owner-authorized rebaseline (2026-08-19 merge-prs session).",
"_rebaseline_2026_08_20_8338_cursor_image_provider": "PR (reimplementation of #8338, @valvesss): imageRegistry.ts 1019->1033 = new cursor IMAGE_PROVIDERS entry (Cursor plan image generation via Agent CLI), +14 lines of declarative provider metadata. Same god-registry no-split rationale as prior imageRegistry/gateways rebaselines.",
"_rebaseline_2026_08_20_imageregistry_1034": "imageRegistry.ts 1033->1034: +1 line drift between #10842 (cursor image provider, froze at 1033) and its actual merged state on release (measured 1034) — trivial rebaseline, not a new feature.",
"_rebaseline_2026_08_25_11146_subscription_first_auto": "PR #11146 (@yourspraveen, subscription-first auto groupings auto/subscription+auto/thrifty): open-sse/services/autoCombo/virtualFactory.ts is a NEW file in this PR landing at 1128 lines (+2 margin) — two opt-in flat auto ids built on the established auto/best-free pattern (connectionBillingCatalog + subscriptionLadder pure functions). Frozen at merge size per owner-authorized rebaseline directive (2026-08-19, merge-batch Step 4); no further growth without split rationale."
"_rebaseline_2026_08_20_imageregistry_1034": "imageRegistry.ts 1033->1034: +1 line drift between #10842 (cursor image provider, froze at 1033) and its actual merged state on release (measured 1034) — trivial rebaseline, not a new feature."
}

View File

@@ -0,0 +1,98 @@
# Design: Pluggable External Durable-State DB Backend (PostgreSQL / MySQL)
Status: PROPOSAL · Discussion issue: [#8075](https://github.com/diegosouzapw/OmniRoute/issues/8075) · Author: @oyi77
Grounded in the tree at `release/v3.8.51` (`3192eb88d`). Every number below was measured on that tree, not estimated.
---
## 0. Why this document exists
#8075 asks five questions (backends & order, abstraction contract, what stays SQLite-only, config surface, migration path). This document answers them with a census of how deeply the current data layer is coupled to SQLite, and proposes a repository-contract seam that lets external backends be adopted per domain instead of via a big-bang driver swap.
## 1. Measured coupling census (`release/v3.8.51`)
| Surface | Count | Implication for external backends |
|---|---|---|
| `src/lib/db/*.ts` modules calling `.prepare()` / `.transaction()` | **126 files** | The synchronous SQLite call shape is the de-facto data API; every module is a translation candidate |
| `.pragma()` call sites | **36** | No equivalent in PG/MySQL — each needs an audit (many are performance tuning that becomes server config) |
| SQLite migrations on disk | **159** | A second dialect needs its own migration lineage, not a translator |
| `sqlite-vec` / `vec0` referencing files | **9** | Vector search must move to an external store (Qdrant proposed in #8075) |
| FTS5 referencing files | **8** | Maps to Postgres `tsvector` / MySQL `FULLTEXT` — dialect work |
| Existing driver adapters | **4** (`better-sqlite3`, `node:sqlite`, `bun:sqlite`, `sql.js`) behind `SqliteAdapter` (`src/lib/db/adapters/types.ts`) | Proves a seam exists at the DRIVER level — but all four are SQLite; the seam OmniRoute actually needs is one level up |
The decisive fact: **the adapter interface is synchronous** (`prepare().run/get/all`, sync closure transactions). PostgreSQL and MySQL drivers are inherently async. A faithful external backend behind `SqliteAdapter` is therefore impossible without faking sync over async — which is exactly the trap #8037/#8073 already flagged ("not a drop-in ORM/driver swap"). The contract must live ABOVE the adapter layer.
## 2. Proposed contract: durable-state repositories, not a driver swap
Introduce a `DurableStateStore` port with per-domain repositories, defined once and implemented per dialect:
```ts
// src/lib/db/ports/ — dialect-agnostic, async, transactional by unit-of-work
interface UnitOfWork {
connections: ConnectionRepository; // provider connections + credentials
apiKeys: ApiKeyRepository; // keys, groups, usage-limit fields
combos: ComboRepository; // combo defs + routing policies
quotaState: QuotaStateRepository; // quota/account state, cooldowns
sessionAffinity: AffinityRepository; // sticky routing state
audit: AuditRepository; // config audit log
}
interface DurableStateStore {
readonly driver: "sqlite" | "postgres" | "mysql";
withUnitOfWork<T>(fn: (uow: UnitOfWork) => Promise<T>): Promise<T>;
healthCheck(): Promise<{ ok: boolean; latencyMs: number }>;
}
```
Deliberate exclusions from v1 (stay SQLite-local, matching #8075 §3): WAL/VACUUM/PRAGMA lifecycle, file backup/import/export, `sqlite-vec` vector storage (external backends delegate vectors to Qdrant), FTS (dialect-native fulltext later).
### What makes each repository shippable
The single-writer pattern OmniRoute already uses internally (targeted raw-SQL stat bumps like `touchConnectionLastUsed`, single-writer caches) maps cleanly onto `UnitOfWork`. Repositories are adopted **per domain**: a deployment can run `postgres` for connections+keys while hot-path ephemeral state stays local — because the port is injected at the repository boundary, not under 126 modules.
## 3. Sequencing: PostgreSQL first, MySQL second
1. **Postgres-first** (`pg` + `pg-pool`): richer native JSONB (several modules persist JSON blobs), `tsvector`, `INSERT ... ON CONFLICT` semantics closest to `INSERT OR REPLACE` usage.
2. **MySQL 8+ after**, gated by a shared **dialect-conformance suite**: the same behavioral test pack runs against both implementations (schema bootstrap, UoW rollback, credential round-trip incl. encryption-at-rest fields, pagination/ordering contracts). A backend is "supported" only when the suite is green.
Rationale: one conformance suite prevents the MySQL implementation from drifting into a shim that passes smoke tests but corrupts ordering/collation edge cases.
## 4. Config surface
```
DATABASE_DRIVER=sqlite # sqlite (default) | postgres | mysql
DATABASE_URL=postgres://... # required when driver != sqlite
DATABASE_POOL_MAX=10
DATABASE_TLS_REJECT_UNAUTHORIZED=1
DATABASE_MIGRATIONS_TABLE=_omniroute_migrations
```
- Default stays `sqlite` — zero-config npm/Electron/Termux behavior is non-negotiable (#8075 §6).
- Health check surfaces in the existing `/api/monitoring/health` payload next to `inflightRequests`.
- Migrations: per-dialect lineage directories (`migrations-postgres/`, `migrations-mysql/`) with their own runner sharing the safety checks (mass-migration abort, pre-migration backup → pg_dump/mysqldump equivalent) already proven in `migrationRunner.ts`.
## 5. Migration path (one-way export tool)
`omniroute db export --to postgres --dsn ...`:
1. Snapshot `storage.sqlite` (reuse `createPreMigrationBackup`).
2. Replay schema from the target dialect's lineage to head.
3. Copy tables row-by-row through the new repositories (not raw SQL translation), preserving IDs; re-encrypt nothing (credential ciphertexts are dialect-neutral strings today).
4. Verify with row-count + checksum comparison per table; write a receipt file.
5. Refuse to continue on any mismatch (fail-closed).
## 6. Phased delivery plan
| Phase | Deliverable | Risk gate |
|---|---|---|
| P1 | `ports/` contract + SQLite implementation of `ConnectionRepository` + conformance-suite skeleton running against SQLite itself | Suite green on the reference driver |
| P2 | Postgres implementation of the same repo + suite green on PG | Suite green on PG |
| P3 | Remaining domains (apiKeys, combos, quotaState, affinity, audit) on PG | Full-suite parity vs SQLite run |
| P4 | MySQL via conformance suite | Suite green on MySQL |
| P5 | Export tool + docs | Round-trip receipt on a 1GB-scale fixture |
P1P2 alone deliver value: a clustered deployment can share connection/key state while everything else stays local.
## 7. Explicitly out of scope
- Translating the remaining ~120 SQLite-shaped modules verbatim ("just make prepare() hit Postgres") — rejected: it fakes sync over async and preserves dialect bugs forever.
- Replacing `sqlite-vec` with pgvector — #8075 names Qdrant; keeping vector search out-of-process also keeps the lean proxy core lean (4.0 modular direction).

View File

@@ -1,4 +1,4 @@
<svg viewBox="0 0 1200 350" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Animated terminal demoing the OmniRoute CLI: omniroute providers list (353 providers registered, anthropic, codex, glm, kimi shown active), omniroute combo list (always-on priority, cost-saver, fusion-panel, context-relay) and omniroute health (healthy, 18412 requests in 24h, p95 412ms, circuit breakers 24 closed, 1 half-open, 0 open), cycling over 85 top-level commands: providers, oauth, keys, combo, nodes, models, cache, compression, cost, usage, quota, health, resilience, telemetry, logs, audit, mcp, a2a, cloud, memory, skills, eval, doctor, repl, tunnel, backup, sync, webhooks, policy, pricing, translator, simulate and more.">
<svg viewBox="0 0 1200 350" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Animated terminal demoing the OmniRoute CLI: omniroute providers list (351 providers registered, anthropic, codex, glm, kimi shown active), omniroute combo list (always-on priority, cost-saver, fusion-panel, context-relay) and omniroute health (healthy, 18412 requests in 24h, p95 412ms, circuit breakers 24 closed, 1 half-open, 0 open), cycling over 85 top-level commands: providers, oauth, keys, combo, nodes, models, cache, compression, cost, usage, quota, health, resilience, telemetry, logs, audit, mcp, a2a, cloud, memory, skills, eval, doctor, repl, tunnel, backup, sync, webhooks, policy, pricing, translator, simulate and more.">
<desc>Compact animated terminal cycling three real OmniRoute CLI commands with a typewriter effect and a scrolling subcommand ticker; the first frame shows the completed providers-list screen.</desc>
<defs><clipPath id="tickerClip"><rect x="12" y="304" width="1176" height="40"/></clipPath><clipPath id="tw0"><rect x="64" y="46" height="26" width="0"><animate attributeName="width" calcMode="discrete" values="0;31;61;92;122;153;184;214;245;245" keyTimes="0;0.012;0.018;0.024;0.030;0.036;0.042;0.048;0.054;1" dur="18s" repeatCount="indefinite"/></rect></clipPath><clipPath id="tw1"><rect x="64" y="46" height="26" width="0"><animate attributeName="width" calcMode="discrete" values="0;26;51;76;102;128;153;178;204;204" keyTimes="0;0.348;0.351;0.357;0.363;0.369;0.375;0.381;0.387;1" dur="18s" repeatCount="indefinite"/></rect></clipPath><clipPath id="tw2"><rect x="64" y="46" height="26" width="0"><animate attributeName="width" calcMode="discrete" values="0;20;41;61;82;102;122;143;163;163" keyTimes="0;0.678;0.684;0.690;0.696;0.702;0.708;0.714;0.720;1" dur="18s" repeatCount="indefinite"/></rect></clipPath></defs>
<rect width="1200" height="350" fill="#0d1117"/>

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -1,4 +1,4 @@
<svg viewBox="0 0 1200 780" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Comparison table: OmniRoute versus 9router, OpenRouter, CLIProxyAPI and LiteLLM across 13 capabilities. OmniRoute is the only one with the full set: 353 providers, 90+ free providers built-in, 19 routing strategies, 12-engine token compression, a built-in MCP server with 110 tools, A2A protocol, persistent memory, guardrails, cloud agents, TLS fingerprint stealth, desktop/Termux/PWA, 43 UI locales and 100% MIT self-hosted. 9router has free providers, RTK compression and translation but no MCP, A2A, memory, guardrails, cloud agents or stealth. OpenRouter is a hosted SaaS with 400+ models, guardrails and a hosted MCP but is not self-hosted and lacks A2A, memory, cloud agents and stealth. CLIProxyAPI is a light OAuth proxy with two routing strategies. LiteLLM has 100+ providers, A2A and extensive guardrails but no memory, compression, free tier, stealth or cloud agents.">
<svg viewBox="0 0 1200 780" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Comparison table: OmniRoute versus 9router, OpenRouter, CLIProxyAPI and LiteLLM across 13 capabilities. OmniRoute is the only one with the full set: 351 providers, 90+ free providers built-in, 19 routing strategies, 12-engine token compression, a built-in MCP server with 110 tools, A2A protocol, persistent memory, guardrails, cloud agents, TLS fingerprint stealth, desktop/Termux/PWA, 43 UI locales and 100% MIT self-hosted. 9router has free providers, RTK compression and translation but no MCP, A2A, memory, guardrails, cloud agents or stealth. OpenRouter is a hosted SaaS with 400+ models, guardrails and a hosted MCP but is not self-hosted and lacks A2A, memory, cloud agents and stealth. CLIProxyAPI is a light OAuth proxy with two routing strategies. LiteLLM has 100+ providers, A2A and extensive guardrails but no memory, compression, free tier, stealth or cloud agents.">
<desc>Static-header comparison table where each capability row fades in top to bottom; the OmniRoute column is highlighted and shows a check or a leading value in every row, while competitors show a mix of checks, partials and crosses.</desc>
<defs>
<pattern id="gC" width="32" height="32" patternUnits="userSpaceOnUse"><path d="M 32 0 L 0 0 0 32" fill="none" stroke="#ffffff" stroke-opacity="0.05" stroke-width="1"/></pattern>

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View File

@@ -1,4 +1,4 @@
<svg viewBox="0 0 1200 540" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="The OmniRoute promise: one endpoint and 353 providers. Six pillars. Resilient fallback: automatic routing continues while another healthy target is available. Save up to 95 percent of eligible tokens: RTK plus Caveman stacked compression averages about 89 percent on tool-heavy sessions. Zero dollars to start: 90+ providers with a free tier and 56 recurring or keyless free-forever providers. Every tool works: 35 CLI and agent integration records, including Claude Code, Codex, Cursor, Cline, Copilot and Antigravity, through one config. One endpoint: OpenAI, Claude, Gemini and Responses API translation at /v1. Production controls: circuit breakers, TLS stealth, MCP with 110 tools, A2A, memory, guardrails, evals, and 39,000+ static test declarations across 5,100+ tracked test files.">
<svg viewBox="0 0 1200 540" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="The OmniRoute promise: one endpoint and 351 providers. Six pillars. Resilient fallback: automatic routing continues while another healthy target is available. Save up to 95 percent of eligible tokens: RTK plus Caveman stacked compression averages about 89 percent on tool-heavy sessions. Zero dollars to start: 90+ providers with a free tier and 56 recurring or keyless free-forever providers. Every tool works: 35 CLI and agent integration records, including Claude Code, Codex, Cursor, Cline, Copilot and Antigravity, through one config. One endpoint: OpenAI, Claude, Gemini and Responses API translation at /v1. Production controls: circuit breakers, TLS stealth, MCP with 110 tools, A2A, memory, guardrails, evals, and 39,000+ static test declarations across 5,100+ tracked test files.">
<desc>Animated promise card: six pillar tiles fade in in reading order, then a soft colored border highlight sweeps from tile to tile in a continuous cycle.</desc>
<defs>
<pattern id="gridPaperP" width="32" height="32" patternUnits="userSpaceOnUse">
@@ -21,7 +21,7 @@
<line x1="150" y1="53" x2="1160" y2="53" stroke="#232b38" stroke-width="1.5"/>
</g>
<g>
<text x="40" y="100" font-family="Inter, 'Segoe UI', Arial, Helvetica, system-ui, sans-serif" font-size="23" font-weight="600" fill="#c9d1d9">One endpoint. <tspan fill="#a78bfa" font-weight="800">353 providers.</tspan> Never stop building — OmniRoute picks <tspan fill="#7ee787" font-weight="700">the cheapest one that works</tspan>.</text>
<text x="40" y="100" font-family="Inter, 'Segoe UI', Arial, Helvetica, system-ui, sans-serif" font-size="23" font-weight="600" fill="#c9d1d9">One endpoint. <tspan fill="#a78bfa" font-weight="800">351 providers.</tspan> Never stop building — OmniRoute picks <tspan fill="#7ee787" font-weight="700">the cheapest one that works</tspan>.</text>
</g>
<g font-family="Inter, 'Segoe UI', Arial, Helvetica, system-ui, sans-serif">
@@ -38,7 +38,7 @@
<line x1="3.9" y1="3.9" x2="18.1" y2="18.1"/>
</g>
<text x="102" y="170" font-size="18" font-weight="800" fill="#74b9ff">Never hit limits</text>
<text x="66" y="204" font-size="13.5" fill="#a1a1aa">Auto-fallback across 353 providers in</text>
<text x="66" y="204" font-size="13.5" fill="#a1a1aa">Auto-fallback across 351 providers in</text>
<text x="66" y="226" font-size="13.5" fill="#a1a1aa">milliseconds. Quota out? The next provider</text>
<text x="66" y="248" font-size="13.5" fill="#a1a1aa">takes over while a healthy target remains.</text>
</g>

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

@@ -1,4 +1,4 @@
<svg viewBox="0 0 1200 548" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="OmniRoute hero: Never stop coding. Every AI tool to 353 providers — 90+ free — through one endpoint. Claude Code, Codex, Cursor, Cline, Copilot and Antigravity into free Claude, GPT and Gemini with auto-fallback. RTK + Caveman stacked compression saves 15 to 95 percent of tokens — about 89 percent average on tool-heavy sessions — so you never hit limits. Stats: 353 AI providers, 90+ free tiers, about 1.51B free tokens per month, 15 to 95 percent token savings, 19 routing strategies, zero dollars to start.">
<svg viewBox="0 0 1200 548" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="OmniRoute hero: Never stop coding. Every AI tool to 351 providers — 90+ free — through one endpoint. Claude Code, Codex, Cursor, Cline, Copilot and Antigravity into free Claude, GPT and Gemini with auto-fallback. RTK + Caveman stacked compression saves 15 to 95 percent of tokens — about 89 percent average on tool-heavy sessions — so you never hit limits. Stats: 351 AI providers, 90+ free tiers, about 1.51B free tokens per month, 15 to 95 percent token savings, 19 routing strategies, zero dollars to start.">
<desc>Animated hero card: a pulse travels the divider line and a compression bar demo repeatedly shrinks a prompt by up to 95 percent; all headline content is static and readable on the first frame.</desc>
<defs>
<pattern id="gridPaperH" width="32" height="32" patternUnits="userSpaceOnUse">
@@ -28,7 +28,7 @@
<text x="48" y="138" font-family="Inter, 'Segoe UI', Arial, Helvetica, system-ui, sans-serif" font-size="60" font-weight="800" fill="#e9edf3">Never stop coding<tspan fill="#a855f7">.</tspan></text>
<!-- subheadline -->
<text x="48" y="184" font-family="Inter, 'Segoe UI', Arial, Helvetica, system-ui, sans-serif" font-size="25" font-weight="600" fill="#c9d1d9">Every AI tool → <tspan fill="#a78bfa" font-weight="800">353 providers</tspan><tspan fill="#7ee787" font-weight="800">90+ free</tspan> — through one endpoint.</text>
<text x="48" y="184" font-family="Inter, 'Segoe UI', Arial, Helvetica, system-ui, sans-serif" font-size="25" font-weight="600" fill="#c9d1d9">Every AI tool → <tspan fill="#a78bfa" font-weight="800">351 providers</tspan><tspan fill="#7ee787" font-weight="800">90+ free</tspan> — through one endpoint.</text>
<!-- plug line -->
<text x="48" y="222" font-family="Inter, 'Segoe UI', Arial, Helvetica, system-ui, sans-serif" font-size="16.5" fill="#a1a1aa">Claude Code · Codex · Cursor · Cline · Copilot · Antigravity&#160;&#160;&#160;&#160;<tspan fill="#7ee787" font-weight="700">FREE</tspan> Claude / GPT / Gemini · auto-fallback</text>

Before

Width:  |  Height:  |  Size: 7.3 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

View File

@@ -24,11 +24,8 @@ it expires.
| Antigravity / Devin Desktop | Built-in quotas |
**Strategy**: route here first for every request that fits the model's
strengths. The quota tracker monitors approaching resets, and the `reset-aware`
combo strategy prioritizes accordingly. To route Tier 1 first and only step out
to paid tiers as quota runs out, use the `auto/thrifty` id — or `auto/subscription`
to stay on plan-included capacity and fail closed instead. See
[Subscription-first routing](../routing/SUBSCRIPTION_LADDER.md).
strengths. Quota tracker monitors approaching reset; combo strategies
`reset-aware` and `subscription` prioritize accordingly.
## Tier 2 — Cheap

View File

@@ -4,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 353 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 351 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
## Overview
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **353 AI providers** with automatic format translation
- **351 AI providers** with automatic format translation
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **353-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **351-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)

View File

@@ -4,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 353 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 351 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
## Overview
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **353 AI providers** with automatic format translation
- **351 AI providers** with automatic format translation
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **353-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **351-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)

View File

@@ -4,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 353 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 351 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
## Overview
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **353 AI providers** with automatic format translation
- **351 AI providers** with automatic format translation
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **353-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **351-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)

View File

@@ -4,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 353 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 351 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
## Overview
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **353 AI providers** with automatic format translation
- **351 AI providers** with automatic format translation
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **353-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **351-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)

View File

@@ -4,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 353 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 351 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
## Overview
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **353 AI providers** with automatic format translation
- **351 AI providers** with automatic format translation
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **353-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **351-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)

View File

@@ -4,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 353 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 351 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
## Overview
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **353 AI providers** with automatic format translation
- **351 AI providers** with automatic format translation
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **353-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **351-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)

View File

@@ -4,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 353 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 351 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
## Overview
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **353 AI providers** with automatic format translation
- **351 AI providers** with automatic format translation
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **353-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **351-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)

View File

@@ -4,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 353 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 351 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
## Overview
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **353 AI providers** with automatic format translation
- **351 AI providers** with automatic format translation
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **353-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **351-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)

View File

@@ -4,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 353 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 351 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
## Overview
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **353 AI providers** with automatic format translation
- **351 AI providers** with automatic format translation
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **353-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **351-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)

View File

@@ -4,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 353 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 351 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
## Overview
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **353 AI providers** with automatic format translation
- **351 AI providers** with automatic format translation
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **353-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **351-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)

View File

@@ -4,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 353 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 351 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
## Overview
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **353 AI providers** with automatic format translation
- **351 AI providers** with automatic format translation
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **353-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **351-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)

View File

@@ -4,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 353 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 351 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
## Overview
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **353 AI providers** with automatic format translation
- **351 AI providers** with automatic format translation
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **353-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **351-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)

View File

@@ -4,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 353 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 351 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
## Overview
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **353 AI providers** with automatic format translation
- **351 AI providers** with automatic format translation
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **353-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **351-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)

View File

@@ -4,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 353 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 351 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
## Overview
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **353 AI providers** with automatic format translation
- **351 AI providers** with automatic format translation
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **353-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **351-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)

View File

@@ -4,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 353 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 351 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
## Overview
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **353 AI providers** with automatic format translation
- **351 AI providers** with automatic format translation
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **353-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **351-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)

View File

@@ -4,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 353 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 351 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
## Overview
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **353 AI providers** with automatic format translation
- **351 AI providers** with automatic format translation
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **353-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **351-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)

View File

@@ -4,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 353 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 351 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
## Overview
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **353 AI providers** with automatic format translation
- **351 AI providers** with automatic format translation
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **353-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **351-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)

View File

@@ -4,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 353 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 351 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
## Overview
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **353 AI providers** with automatic format translation
- **351 AI providers** with automatic format translation
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **353-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **351-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)

View File

@@ -4,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 353 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 351 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
## Overview
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **353 AI providers** with automatic format translation
- **351 AI providers** with automatic format translation
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **353-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **351-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)

View File

@@ -4,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 353 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 351 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
## Overview
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **353 AI providers** with automatic format translation
- **351 AI providers** with automatic format translation
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **353-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **351-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)

View File

@@ -4,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 353 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 351 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
## Overview
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **353 AI providers** with automatic format translation
- **351 AI providers** with automatic format translation
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **353-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **351-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)

View File

@@ -4,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 353 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 351 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
## Overview
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **353 AI providers** with automatic format translation
- **351 AI providers** with automatic format translation
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **353-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **351-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)

View File

@@ -4,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 353 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 351 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
## Overview
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **353 AI providers** with automatic format translation
- **351 AI providers** with automatic format translation
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **353-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **351-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)

View File

@@ -4,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 353 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 351 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
## Overview
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **353 AI providers** with automatic format translation
- **351 AI providers** with automatic format translation
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **353-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **351-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)

View File

@@ -4,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 353 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 351 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
## Overview
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **353 AI providers** with automatic format translation
- **351 AI providers** with automatic format translation
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **353-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **351-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)

View File

@@ -4,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 353 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 351 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
## Overview
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **353 AI providers** with automatic format translation
- **351 AI providers** with automatic format translation
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **353-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **351-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)

View File

@@ -4,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 353 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 351 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
## Overview
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **353 AI providers** with automatic format translation
- **351 AI providers** with automatic format translation
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **353-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **351-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)

View File

@@ -4,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 353 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 351 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
## Overview
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **353 AI providers** with automatic format translation
- **351 AI providers** with automatic format translation
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **353-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **351-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)

View File

@@ -4,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 353 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 351 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
## Overview
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **353 AI providers** with automatic format translation
- **351 AI providers** with automatic format translation
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **353-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **351-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)

View File

@@ -4,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 353 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 351 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
## Overview
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **353 AI providers** with automatic format translation
- **351 AI providers** with automatic format translation
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **353-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **351-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)

View File

@@ -4,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 353 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 351 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
## Overview
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **353 AI providers** with automatic format translation
- **351 AI providers** with automatic format translation
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **353-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **351-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)

View File

@@ -4,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 353 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 351 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
## Overview
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **353 AI providers** with automatic format translation
- **351 AI providers** with automatic format translation
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **353-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **351-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)

View File

@@ -4,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 353 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 351 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
## Overview
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **353 AI providers** with automatic format translation
- **351 AI providers** with automatic format translation
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **353-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **351-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)

View File

@@ -4,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 353 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 351 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
## Overview
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **353 AI providers** with automatic format translation
- **351 AI providers** with automatic format translation
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **353-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **351-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)

View File

@@ -4,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 353 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 351 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
## Overview
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **353 AI providers** with automatic format translation
- **351 AI providers** with automatic format translation
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **353-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **351-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)

View File

@@ -4,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 353 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 351 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
## Overview
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **353 AI providers** with automatic format translation
- **351 AI providers** with automatic format translation
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **353-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **351-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)

View File

@@ -4,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 353 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 351 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
## Overview
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **353 AI providers** with automatic format translation
- **351 AI providers** with automatic format translation
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **353-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **351-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)

View File

@@ -4,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 353 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 351 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
## Overview
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **353 AI providers** with automatic format translation
- **351 AI providers** with automatic format translation
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **353-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **351-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)

View File

@@ -4,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 353 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 351 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
## Overview
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **353 AI providers** with automatic format translation
- **351 AI providers** with automatic format translation
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **353-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **351-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)

View File

@@ -4,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 353 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 351 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
## Overview
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **353 AI providers** with automatic format translation
- **351 AI providers** with automatic format translation
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **353-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **351-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)

View File

@@ -4,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 353 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 351 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
## Overview
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **353 AI providers** with automatic format translation
- **351 AI providers** with automatic format translation
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **353-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **351-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)

View File

@@ -4,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 353 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 351 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
## Overview
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **353 AI providers** with automatic format translation
- **351 AI providers** with automatic format translation
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **353-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **351-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)

View File

@@ -1,16 +1,16 @@
---
title: "Provider Reference"
version: 3.8.51
lastUpdated: 2026-08-25
version: 3.8.50
lastUpdated: 2026-08-24
---
# Provider Reference
> **Auto-generated** from `src/shared/constants/providers.ts` — do not edit by hand.
> Regenerate with: `npm run gen:provider-reference`
> **Last generated:** 2026-08-25
> **Last generated:** 2026-08-24
Total providers: **353**. See category breakdown below.
Total providers: **351**. See category breakdown below.
## Categories
@@ -122,7 +122,7 @@ Use the dashboard at `/dashboard/providers` to enable, configure, and test each
| `zai-web` | `zw` | Z.ai Web | Web cookie | [link](https://chat.z.ai) | Copy the "token" value from chat.z.ai → DevTools → Application → Local Storage. Do not copy cookies; OmniRoute handles the per-request CAPTCHA through its browser transport. | — |
| `zenmux-free` | `zmf` | ZenMux Free (Web) | Web cookie | [link](https://zenmux.ai) | Login at zenmux.ai, then export all cookies using EditThisCookie or Cookie-Editor and paste the full Cookie header string here. Refresh every ~30 days. | — |
## API Key Providers (paid / paid-with-free-credits) (233)
## API Key Providers (paid / paid-with-free-credits) (231)
| ID | Alias | Name | Tags | Website | Notes |
|----|-------|------|------|---------|-------|
@@ -342,8 +342,6 @@ Use the dashboard at `/dashboard/providers` to enable, configure, and test each
| `vertex-partner` | `vp` | Vertex AI Partners | API key, enterprise | [link](https://cloud.google.com/vertex-ai) | Provide the same Service Account JSON used for Vertex AI partner models. |
| `void-ai` | `void-ai` | Void AI | API key, aggregator | [link](https://voidai.app) | The public model catalog marks some models with a free plan requirement, but access is conditional and no numeric quota is confirmed. |
| `volcengine` | `volcengine` | Volcengine | API key | [link](https://www.volcengine.com) | — |
| `volcengine-agent-plan` | `veap` | Volcengine Ark Agent Plan | API key | [link](https://console.volcengine.com/ark/region:cn-beijing/subscription/agent-plan) | Connect your Volcano Engine account or use an Ark Agent Plan subscription API key. |
| `volcengine-coding-plan` | `vecp` | Volcengine Ark Coding Plan | API key | [link](https://console.volcengine.com/ark/region:cn-beijing/subscription/coding-plan) | Connect your Volcano Engine account or use an Ark Coding Plan subscription API key. |
| `voyage-ai` | `voyage` | Voyage AI | API key, embed/rerank | [link](https://www.voyageai.com) | Bearer API key for Voyage AI embeddings and rerank APIs. |
| `wafer` | `wafer` | Wafer AI | API key | [link](https://wafer.ai) | — |
| `wandb` | `wandb` | Weights & Biases Inference | API key | [link](https://wandb.ai) | — |

View File

@@ -1,182 +0,0 @@
---
title: "Subscription-first routing"
version: 3.8.50
lastUpdated: 2026-08-22
---
# Subscription-first routing
> Two new `auto/*` ids — `auto/subscription` and `auto/thrifty`. Both are opt-in by being
> requested: nothing routes through them unless a caller asks for the id by name, and no
> existing pool, strategy, or default changes.
## Why this exists
OmniRoute already answers two cost questions, and neither is the one most operators ask.
| Existing mechanism | Answers |
| -------------------------------------------------------- | ----------------------------------- |
| `hidePaidModels` (`autoCombo/paidModelFilter.ts`) | "is this model catalogued free?" |
| `freeAccessPolicy: "strict"` (`strictZeroCostFilter.ts`) | "can this connection ever bill me?" |
| `quotaPreflight` (`combo/quotaExhaustionCutoff.ts`) | "is this connection near its wall?" |
| `budgetCap` / `budgetFallback` (`autoCombo/engine.ts`) | "cap spend, degrade to cheapest" |
Every free-only mechanism **fails closed** — an exhausted free pool is an empty pool, never a
step up to a paid option — and every paid-side mechanism is tier-agnostic. Neither answers:
> "Use the quota I already pay for. When it runs out, either stop, or step up one rung at a
> time through the cheapest paid options — and come back the moment it resets."
## Billing is a connection fact, not a model fact
`classifyTier()` (`open-sse/services/tierResolver.ts`) keys on `(provider, model)` and returns
`free | cheap | premium` from catalog pricing. But whether a request costs incremental money
depends on **which connection serves it**: the same model is plan-included through a Claude Code
OAuth connection and billed per token through an API-key connection.
`provider_connections.auth_type` is not a safe proxy in either direction — metered OAuth
connections exist, and plan-included API-key connections exist (a Copilot seat token is not a
metered API key). So billing class comes from a **curated catalog**,
`open-sse/config/connectionBillingCatalog.ts`, hand-set from each provider's published terms —
the same pattern `FreeModelBudget.hardStopGuaranteed` already established for free models.
```ts
type ConnectionBillingClass = "subscription" | "metered" | "keyless" | "unknown";
type ConnectionOverageBehavior = "hard-stop" | "meters-to-paid" | "unknown";
```
Resolution order (`autoCombo/connectionBilling.ts`): the synthetic no-auth sentinel →
`keyless`; a catalog entry matching provider **and** `authType`; a provider-wide entry;
otherwise `unknown`. **Uncurated is not free**`unknown` is consumed as `metered`
everywhere, so a provider added tomorrow starts outside the subscription rung and has to be
curated in deliberately.
## The rung model
Five rungs in escalation order. They differ in more than price — each has its **own**
exhaustion signal, which is why this is not merely a sort.
| # | Rung | Membership | Exhausted when |
| --- | -------------- | -------------------------------------------------- | ----------------------------- |
| 0 | `subscription` | curated `billing: "subscription"` | quota window at/below cutoff |
| 1 | `keyless` | the synthetic no-auth path | connection cooldown / breaker |
| 2 | `free` | metered connection, `classifyTier() === "free"` | free allowance exhausted |
| 3 | `cheap` | metered connection, `classifyTier() === "cheap"` | per-rung budget consumed |
| 4 | `premium` | metered connection, `classifyTier() === "premium"` | per-rung budget consumed |
Rungs 0-2 exhaust on **quota**, which is observable and already tracked. Rungs 3-4 have no
quota — a paid connection serves forever — so their only sane exhaustion signal is a per-rung
**budget**. Without one, "escalate when cheap is exhausted" has no trigger.
## `auto/subscription` — fail closed
Pool = rung 0 only, restricted to connections whose overage is a documented `hard-stop`, each
verified live to have quota headroom. Everything ambiguous is excluded: an uncurated provider,
an unverifiable quota reading, a stale reading, or an overage that meters to paid.
An empty pool is the **intended** answer, not a defect — the caller's existing empty-pool path
turns it into a clear error rather than a silent, billable fallback. That is the whole promise
of the id.
`keyless` deliberately does **not** qualify: this grouping means "the plan I pay for", so a
no-auth backend does not belong in it. Use `auto/thrifty` (or `auto/best-free`) for that.
### Connection safety
A candidate is not always tied to one connection — a logical candidate carries an
`allowedConnectionIds` allowlist, and the account actually used is chosen later, at dispatch,
by `open-sse/services/combo/autoStrategy.ts`. Both groupings therefore verify **each connection
individually** and rewrite `allowedConnectionIds` down to exactly the surviving subset — never
the full original list, never one arbitrarily-chosen member. Because `autoStrategy.ts` already
enforces that array as a hard allowlist, rewriting it here makes "verified" and "actually used"
the same set by construction. This is the same invariant, and the same reasoning, as
[STRICT_ZERO_COST](./STRICT_ZERO_COST.md).
## `auto/thrifty` — escalate one rung at a time
Pool = all rungs, ordered by rung index, with exhausted candidates gated out. The `auto` engine
still scores **within** the surviving pool: the ladder decides which rungs are in play, scoring
decides which candidate wins inside them. Ordering is stable within a rung, so the scorer's own
ranking is never reshuffled by this overlay.
This is an ordering + gating overlay, **not** a new dispatcher: `combo.ts`'s speculative loop
already walks targets in order and falls through on failure, so a runtime exhaustion the
preflight did not catch still escalates to the next rung inside the same request.
Where `auto/subscription` fails closed, `auto/thrifty` fails **open**: a plan-included
connection with no usable quota reading is still tried first. Trying it costs nothing, and if
it turns out to be exhausted the fall-through reaches the next rung anyway — whereas refusing
to try it would send the request to a paid rung on missing telemetry, the exact outcome the
grouping exists to avoid.
## Returning to the plan after a reset
Three independent things must expire before routing returns to rung 0. Fixing only one leaves
the ladder stuck on paid rungs long after the plan refilled.
1. **The quota-state cache**`freeAccessQuota.ts` caches per `(provider, connection)` with a
180s TTL. A cached entry whose own `resetAt` has already passed describes a window that no
longer exists, so it is now treated as stale **regardless of age** and forces a refresh.
Without this, a plan that refilled at midnight keeps reading exhausted until the TTL happens
to lapse.
2. **The ladder's own state** — there is none, by design. Rung eligibility is recomputed from
live quota state on every pool build; no persisted "currently on rung 3" record exists that
could outlive a reset and wedge routing.
3. **The connection cooldown** — the exhausting 429 sets `rateLimitedUntil` from exponential
backoff, which for a plan connection can overshoot the real reset. `clampCooldownToReset()`
(`subscriptionLadder.ts`) narrows a cooldown to the upstream's own reset instant and can
never extend one. **It is implemented and tested but not yet wired**: the quota cache is
invalidated in `src/sse/services/auth.ts` _before_ any cooldown is written, so `resetAt`
must be captured earlier in that function — a change to the resilience hot path that
belongs in its own reviewed PR. Until then, re-entry waits out the connection cooldown
(which already prefers upstream `Retry-After` hints when the provider sends them).
### Anti-flap
A rung that just reset is re-admitted only above `reentryMinRemainingPercent` (default 5),
while a connection already in play only has to stay above `exitCutoffPercent` (default 2,
matching `quotaPreflight.defaultThresholdPercent`). The gap is the hysteresis band — without
it, a connection hovering at the cutoff oscillates between rungs on consecutive requests.
## Configuration
Tuning only. There is deliberately **no** `enabled` flag: a toggle able to switch these off
would leave `auto/subscription` quietly serving the full pool — paid models included — under a
name that promises the opposite.
```jsonc
{
"subscriptionLadder": {
"exitCutoffPercent": 2,
"reentryMinRemainingPercent": 5,
"rungBudgetUsd": { "cheap": 5.0, "premium": 0 }, // 0 disables a rung outright
},
}
```
Budget gating is inert until a spend resolver is wired: with no accounting available a paid
rung is ordered but never gated. As of v3.8.51 the `rungBudgetUsd` setting is accepted by the
schema but NOT yet enforced — treat it as reserved configuration, not an active spend cap. Rung ordering, quota-based exhaustion, and reset re-entry all
work without it.
## Composition
`subscription` and `thrifty` are `AutoTier` values, so they compose with every category:
`auto/coding:thrifty`, `auto/reasoning:subscription`, and so on. The two flat ids
(`auto/subscription`, `auto/thrifty`) are advertised in `/v1/models` and the dashboard.
Neither id is paid-tier, so `isPaidTierAutoId()` returns `false` for both and
`auto/subscription` survives `hidePaidModels`.
## Where the code lives
| Concern | File |
| ------------------------------- | --------------------------------------------------- |
| Curated billing facts | `open-sse/config/connectionBillingCatalog.ts` |
| Classifier | `open-sse/services/autoCombo/connectionBilling.ts` |
| Rungs, both groupings, re-entry | `open-sse/services/autoCombo/subscriptionLadder.ts` |
| Wiring into the candidate pool | `open-sse/services/autoCombo/virtualFactory.ts` |
| Reset-aware cache staleness | `open-sse/services/autoCombo/freeAccessQuota.ts` |
| Tier surface | `open-sse/services/autoCombo/suffixComposition.ts` |
| Advertised ids | `open-sse/services/autoCombo/builtinCatalog.ts` |
| Tests | `tests/unit/autoCombo/subscription-ladder.test.ts` |

View File

@@ -45,6 +45,7 @@
"copyright": "Copyright © 2025 OmniRoute",
"buildDependenciesFromSource": false,
"npmRebuild": false,
"afterPack": "../scripts/build/afterpack-copy-node-modules.mjs",
"directories": {
"output": "dist-electron",
"buildResources": "assets"
@@ -58,6 +59,7 @@
"main.js",
"preload.js",
"loginManager.js",
"lib/loginHeaderCapture.js",
"processTree.js",
"sqlite-inspection.js",
"remoteServerPromptPreload.js",

View File

@@ -1,4 +1,6 @@
import { fixupConfigRules } from "@eslint/compat";
import nextVitals from "eslint-config-next/core-web-vitals";
import * as espree from "espree";
import tseslint from "typescript-eslint";
// #7879: bar NEW local `toNumber` definitions outside the canonical helper.
@@ -37,7 +39,22 @@ const IMPORT_BOUNDARY_RESTRICTIONS = {
/** @type {import("eslint").Linter.Config[]} */
const eslintConfig = [
...nextVitals,
...fixupConfigRules(nextVitals),
// eslint-config-next's Babel parser has not adopted ESLint 10's ScopeManager
// finalize contract yet. Plain JS/JSX does not need that parser, so use the
// ESLint-native parser while retaining Next's plugins and rules.
{
files: ["**/*.{js,jsx,mjs,cjs}"],
languageOptions: {
parser: espree,
},
},
{
files: ["**/*.{mts,cts}"],
languageOptions: {
parser: tseslint.parser,
},
},
// Pacote 4 (plano mestre testes+CI, 2026-07-04) — zero-warning policy: TODA regra roda
// como "error" e a dívida pré-existente vive congelada por arquivo+regra em
// config/quality/eslint-suppressions.json (ESLint bulk suppressions nativo). Violação
@@ -133,28 +150,45 @@ const eslintConfig = [
"no-restricted-syntax": "off",
},
},
// Relaxed rules for open-sse and tests (incremental adoption)
// Relaxed rules for TypeScript in open-sse and tests (incremental adoption).
// eslint-config-next already registers @typescript-eslint for every TS file;
// registering a second plugin object is rejected by ESLint 10.
{
files: ["open-sse/**/*.ts", "tests/**/*.mjs", "tests/**/*.ts"],
plugins: {
"@typescript-eslint": tseslint.plugin,
},
files: ["open-sse/**/*.ts", "tests/**/*.ts"],
rules: {
"@typescript-eslint/no-explicit-any": "error",
"@next/next/no-assign-module-variable": "off",
"react-hooks/rules-of-hooks": "off",
},
},
{
files: ["tests/**/*.mjs"],
plugins: {
"@typescript-eslint": tseslint.plugin,
},
rules: {
"@next/next/no-assign-module-variable": "off",
"react-hooks/rules-of-hooks": "off",
},
},
// JS/JSX files do not match eslint-config-next's TypeScript block. Register
// the plugin only for that disjoint scope so the shared unused-vars ratchet
// works without redefining the plugin for TS/TSX under ESLint 10.
{
files: ["src/**/*.{js,jsx}"],
plugins: {
"@typescript-eslint": tseslint.plugin,
},
},
// Ratchet: bar NEW unused vars/args/catches outside the `_` escape hatch.
// Pre-existing violations are frozen via config/quality/eslint-suppressions.json
// (same pattern as #7879 toNumber); only genuinely NEW unused bindings fail
// lint. `args: "all"` (not `after-used`) so a leading unused param is never
// silently skipped, e.g. `function handle(req, _opts, next)` must flag `req`.
// eslint-config-next already registers @typescript-eslint; registering it again
// in this block is rejected by ESLint 10.
{
files: ["src/**/*.{ts,tsx,js,jsx}", "open-sse/**/*.ts", "tests/**/*.{ts,tsx,mjs}"],
plugins: {
"@typescript-eslint": tseslint.plugin,
},
rules: {
"@typescript-eslint/no-unused-vars": [
"error",

View File

@@ -1,6 +1,6 @@
# OmniRoute
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 353 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 351 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in MCP Server (110 tools), A2A v0.3 protocol, Memory/Skills systems, Cloud Agents (codex, cursor, devin, jules), Guardrails framework, and an Electron desktop app.
## Overview
@@ -277,7 +277,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **353 AI providers** with automatic format translation
- **351 AI providers** with automatic format translation
- **Provider categories**: Free (90+ free tiers), OAuth, API Key, Self-Hosted, Custom (OpenAI/Anthropic-compatible)
- **18 routing strategies**: priority, weighted, round-robin, fill-first, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -475,7 +475,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **353-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **351-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **19 routing strategies** — including `fusion` (parallel panel + judge synthesis), `pipeline`, `reset-aware`, `reset-window`, `headroom`, and `context-relay`
- **14-factor Auto-Combo scoring** with bandit exploration and progressive cooldown
- **MCP server expanded to 110 tools / 33 scopes** (canonical + memory/skill/agentSkill/githubSkill/pool/notion/obsidian/localCorpus/gamification/plugin modules)

View File

@@ -1,143 +0,0 @@
/**
* Curated billing classification for provider CONNECTIONS.
*
* The economic tier resolver (`open-sse/services/tierResolver.ts`) answers
* "how much does this MODEL cost per token?" — a fact about the catalog. It
* cannot answer the question subscription-first routing actually needs:
*
* "does serving this request through THIS connection cost incremental money,
* or is it already covered by a flat-rate plan the operator pays anyway?"
*
* That is a property of the credential, not the model: `claude/claude-*` is
* plan-included through a Claude Code OAuth connection and billed per token
* through an API-key connection — same provider, same model, opposite
* economics.
*
* `provider_connections.auth_type` alone is NOT a safe proxy in either
* direction: metered OAuth connections exist (cloud-billed OAuth), and
* plan-included API-key connections exist (Copilot seat tokens). So this is a
* CURATED table, hand-set from each provider's published terms — deliberately
* the same pattern `FreeModelBudget.hardStopGuaranteed`
* (`open-sse/config/freeModelCatalog.ts`) already established: a fact about
* the upstream's commercial terms, never derived from `authType` and never
* inferred from a live API response.
*
* Uncurated is not "free": anything absent here resolves to `unknown`, which
* every consumer treats as `metered`. New providers therefore start OUTSIDE
* the subscription rung and have to be curated in deliberately — the same
* fail-safe direction STRICT_ZERO_COST uses for uncatalogued models.
*/
/**
* How a connection's upstream charges for the requests it serves.
*
* - `subscription` — covered by a flat-rate plan the operator already pays.
* Consuming its quota costs nothing incremental; the plan is a sunk cost.
* - `metered` — pay-per-token / pay-per-credit. Every request adds spend.
* - `keyless` — no credential exists at all, so no request can be billed by
* construction (the synthetic no-auth path).
* - `unknown` — not curated. Consumed as `metered` everywhere.
*/
export type ConnectionBillingClass = "subscription" | "metered" | "keyless" | "unknown";
/**
* What happens when a subscription plan's allowance runs out.
*
* - `hard-stop` — the upstream refuses further requests until the window
* resets. Exhaustion cannot cost money, so such a connection is admissible
* to the strictest "never spend a cent extra" grouping.
* - `meters-to-paid` — the upstream keeps serving and bills the overage.
* Perfectly usable while quota remains, never admissible to the strict
* grouping.
* - `unknown` — not established. Treated exactly like `meters-to-paid` by
* every consumer; it is the conservative default for a provider whose terms
* allow an operator to opt into usage-based billing past the plan.
*/
export type ConnectionOverageBehavior = "hard-stop" | "meters-to-paid" | "unknown";
export interface ConnectionBillingEntry {
/** Provider id as registered in `open-sse/config/providers/registry/`. */
provider: string;
/**
* Restricts the entry to connections whose `authType` matches. Omit for a
* provider-wide entry. A matching auth-typed entry always wins over the
* provider-wide one, so a provider offering both a plan-included OAuth login
* and a metered API key can declare both.
*/
authType?: string;
billing: ConnectionBillingClass;
overage: ConnectionOverageBehavior;
/** Operator-visible justification for the classification. */
reason: string;
}
/**
* Curated entries. Conservative by design — a provider whose terms let the
* operator enable usage-based billing past the plan is recorded as `unknown`
* overage, not `hard-stop`, because the strict grouping's entire promise is
* that it cannot surprise you.
*
* Providers already classified free by the economic tier resolver (`kiro`,
* `qoder`, and the rest of `LEGACY_FREE_PROVIDERS` /
* `deriveNoAuthFreeProviders()` in `open-sse/services/tierConfig.ts`) are
* deliberately NOT listed here: they land on the ladder's `free` rung through
* `classifyTier()` and would only be double-claimed by an entry here.
*/
export const CONNECTION_BILLING_CATALOG: readonly ConnectionBillingEntry[] = [
{
provider: "claude",
authType: "oauth",
billing: "subscription",
overage: "hard-stop",
reason:
"Claude Code OAuth serves the operator's Anthropic Pro/Max plan windows. " +
"Exceeding a window is refused until it resets; no per-token charge accrues.",
},
{
provider: "codex",
authType: "oauth",
billing: "subscription",
overage: "hard-stop",
reason:
"Codex OAuth serves the ChatGPT plan's included Codex quota. Exhaustion is " +
"refused until the plan window resets rather than billed.",
},
{
provider: "antigravity",
authType: "oauth",
billing: "subscription",
overage: "hard-stop",
reason:
"Antigravity OAuth serves built-in plan quotas that stop serving once consumed; " +
"OmniRoute already tracks their reset windows (see antigravityCredits.ts).",
},
{
provider: "cursor",
authType: "oauth",
billing: "subscription",
overage: "unknown",
reason:
"Cursor Pro includes a request allowance, but usage-based pricing past the plan " +
"can be enabled per account and OmniRoute cannot observe that setting. Recorded " +
"as unknown overage so the strict grouping excludes it.",
},
{
provider: "copilot-web",
authType: "apikey",
billing: "subscription",
overage: "unknown",
reason:
"GitHub Copilot is a per-seat subscription (the credential is a seat token, not a " +
"metered API key), but additional premium requests can be billed when the account " +
"opts in. Recorded as unknown overage.",
},
{
provider: "devin-desktop",
authType: "oauth",
billing: "subscription",
overage: "meters-to-paid",
reason:
"Devin Desktop draws on the plan's included ACUs and continues billing past them, " +
"so it is plan-included while quota remains but never overage-safe.",
},
];

View File

@@ -408,6 +408,7 @@ export const EMBEDDING_PROVIDERS: Record<string, EmbeddingProvider> = {
},
],
},
};
const EMBEDDING_PROVIDER_ALIASES: Record<string, string> = {
@@ -470,6 +471,38 @@ export function getEmbeddingProvider(providerId: string): EmbeddingProvider | nu
return EMBEDDING_PROVIDERS[resolveEmbeddingProviderId(providerId)] || null;
}
/**
* Derive an OpenAI-compatible embeddings config for a chat provider that has NO
* curated EMBEDDING_PROVIDERS entry. Works for any registry provider whose base
* URL ends in /chat/completions by swapping that suffix for /embeddings (groq,
* mistral, together, upstage, fireworks, nvidia, vercel-ai-gateway, ...).
* Dynamic-URL providers (no usable static base) derive to
* null — they need bespoke URL handling, not a bogus endpoint.
*
* This is a FALLBACK only: callers must check getEmbeddingProvider() first so
* curated entries keep their specialized configuration.
*/
export function deriveEmbeddingProviderForChatProvider(
providerId: string,
chatEntry: { id?: string; baseUrl?: string | string[] } | null | undefined
): EmbeddingProvider | null {
if (!chatEntry) return null;
const rawBase = Array.isArray(chatEntry.baseUrl)
? chatEntry.baseUrl[0]
: chatEntry.baseUrl;
if (!rawBase || typeof rawBase !== "string") return null;
// stripTrailingSlashes-equivalent without importing open-sse utils here:
const base = rawBase.replace(/\/+$/, "");
if (!base.endsWith("/chat/completions")) return null;
return {
id: providerId,
baseUrl: `${base.slice(0, -"/chat/completions".length)}/embeddings`,
authType: "apikey",
authHeader: "bearer",
models: [],
};
}
/**
* Parse embedding model string (format: "provider/model" or just "model")
* Returns { provider, model }
@@ -485,6 +518,18 @@ export function parseEmbeddingModel(
const slashIdx = modelStr.indexOf("/");
if (slashIdx > 0) {
const rawProvider = modelStr.slice(0, slashIdx);
// A configured provider_node whose prefix exactly equals the requested
// provider segment always wins — even when that segment is also an alias
// of a curated provider (a local node must not be hijacked by a registry
// alias). Same exact-match precedence documented for
// EMBEDDING_MODEL_ALIASES above.
const dynamicExact =
dynamicProviders && dynamicProviders.find((dp) => dp.id === rawProvider);
if (dynamicExact) {
return { provider: rawProvider, model: modelStr.slice(slashIdx + 1) };
}
const resolvedProvider = resolveEmbeddingProviderId(rawProvider);
if (EMBEDDING_PROVIDERS[resolvedProvider]) {

View File

@@ -12,5 +12,15 @@
export const OPENCODE_ZEN_GO_SHARED_MODELS = Object.freeze([
{ id: "kimi-k2.7-code", name: "Kimi K2.7 Code" },
{ id: "qwen3.5-plus", name: "Qwen3.5 Plus", targetFormat: "claude", supportsVision: false },
{ id: "qwen3.6-plus", name: "Qwen3.6 Plus", targetFormat: "claude", supportsVision: false },
{
id: "qwen3.6-plus",
name: "Qwen3.6 Plus",
targetFormat: "claude",
supportsVision: false,
// #10788: effort-tier aliases exist as explicit registry rows; declare the
// vocabulary on the shared base row so variant synthesis and the sanitizer
// agree on it for both opencode-go and opencode-zen.
supportsReasoning: true,
supportedThinkingEfforts: ["high", "max"],
},
]);

View File

@@ -18,7 +18,17 @@ export const nvidiaProvider: RegistryEntry = {
passthroughModels: true,
models: [
// #6108: z-ai/glm-5.1 EOL'd 2026-07-02 (direct probe returns 410) — dropped.
{ id: "z-ai/glm-5.2", name: "GLM 5.2" },
// #10788: NVIDIA's hosted GLM-5.2 exposes a BINARY thinking switch
// (chat_template_kwargs.enable_thinking), not effort tiers — see
// mapNvidiaGlm52ReasoningParams. Declaring an empty tier list keeps the
// catalog from synthesizing unresolvable -low/-high/-max variant ids while
// still marking the model reasoning-capable.
{
id: "z-ai/glm-5.2",
name: "GLM 5.2",
supportsReasoning: true,
supportedThinkingEfforts: [],
},
// #3329/#6108: minimaxai/minimax-m3 stays excluded from the nvidia tier — it
// still 404s here for most callers; the single 200 probe in #6108 was not
// reproducible enough to override the #3329 guard. Re-add only once NVIDIA

View File

@@ -19,9 +19,15 @@ export const opencode_goProvider: RegistryEntry = {
// `kimi-k2.7-code` (the live API rejects the plain `kimi-k2.7` alias for
// `/chat/completions`, even though the docs config example uses it).
// GLM-5.2 — base model + effort-tier aliases (#6922).
// OpencodeExecutor rewrites the alias to the canonical id and injects
// reasoning_effort, mirroring the deepseek-v4-pro-* pattern.
{ id: "glm-5.2", name: "GLM-5.2", supportsReasoning: true },
// #10788: the tier vocabulary is declared on the base row so the catalog's
// variant synthesis (#9485) and the effort sanitizer share one source of
// truth with OpencodeExecutor's EFFORT_TIERS.
{
id: "glm-5.2",
name: "GLM-5.2",
supportsReasoning: true,
supportedThinkingEfforts: ["high", "max"],
},
{ id: "glm-5.2-high", name: "GLM-5.2 (high effort)", supportsReasoning: true },
{ id: "glm-5.2-max", name: "GLM-5.2 (max effort)", supportsReasoning: true },
@@ -34,11 +40,16 @@ export const opencode_goProvider: RegistryEntry = {
{ id: "kimi-k2.6", name: "Kimi K2.6" },
{ id: "kimi-k2.5", name: "Kimi K2.5" },
// #8353: Kimi K3 base + max-effort alias from the OpenCode Go registry.
{ id: "kimi-k3", name: "Kimi K3", supportsReasoning: true },
{ id: "kimi-k3", name: "Kimi K3", supportsReasoning: true, supportedThinkingEfforts: ["max"] },
{ id: "kimi-k3-max", name: "Kimi K3 (max effort)", supportsReasoning: true },
// MiMo-V2.5 — base model + effort-tier aliases (#6922).
{ id: "mimo-v2.5-pro", name: "MiMo-V2.5-Pro", supportsReasoning: true },
{ id: "mimo-v2.5", name: "MiMo-V2.5", supportsReasoning: true },
{
id: "mimo-v2.5",
name: "MiMo-V2.5",
supportsReasoning: true,
supportedThinkingEfforts: ["high", "max"],
},
{ id: "mimo-v2.5-high", name: "MiMo-V2.5 (high effort)", supportsReasoning: true },
{ id: "mimo-v2.5-max", name: "MiMo-V2.5 (max effort)", supportsReasoning: true },
// #3110: MiniMax M3 via OpenCode Go tier
@@ -59,7 +70,14 @@ export const opencode_goProvider: RegistryEntry = {
// so combo routing skips them when the request contains image blocks,
// preventing image content from reaching a vision-incapable upstream.
// #8353: effort-tier aliases from the OpenCode Go registry.
{ id: "qwen3.7-max", name: "Qwen3.7 Max", targetFormat: "claude", supportsVision: false },
{
id: "qwen3.7-max",
name: "Qwen3.7 Max",
targetFormat: "claude",
supportsVision: false,
supportsReasoning: true,
supportedThinkingEfforts: ["high", "max"],
},
{
id: "qwen3.7-max-high",
name: "Qwen3.7 Max (high effort)",
@@ -79,6 +97,8 @@ export const opencode_goProvider: RegistryEntry = {
name: "Qwen3.7 Plus",
targetFormat: "claude",
supportsVision: false,
supportsReasoning: true,
supportedThinkingEfforts: ["high", "max"],
},
{
id: "qwen3.7-plus-high",
@@ -111,7 +131,13 @@ export const opencode_goProvider: RegistryEntry = {
supportsReasoning: true,
},
// #8353: hy3 is the Go-tier base id (distinct from hy3-preview / hy3-free).
{ id: "hy3", name: "Hunyuan3", contextLength: 256000, supportsReasoning: true },
{
id: "hy3",
name: "Hunyuan3",
contextLength: 256000,
supportsReasoning: true,
supportedThinkingEfforts: ["none", "low", "high"],
},
{
id: "hy3-none",
name: "Hunyuan3 (none effort)",
@@ -201,7 +227,12 @@ export const opencode_goProvider: RegistryEntry = {
targetFormat: "openai-responses",
},
// #8353: Grok 4.5 + effort tiers from the OpenCode Go registry.
{ id: "grok-4.5", name: "Grok 4.5", supportsReasoning: true },
{
id: "grok-4.5",
name: "Grok 4.5",
supportsReasoning: true,
supportedThinkingEfforts: ["low", "medium", "high"],
},
{ id: "grok-4.5-low", name: "Grok 4.5 (low effort)", supportsReasoning: true },
{ id: "grok-4.5-medium", name: "Grok 4.5 (medium effort)", supportsReasoning: true },
{ id: "grok-4.5-high", name: "Grok 4.5 (high effort)", supportsReasoning: true },

View File

@@ -79,18 +79,35 @@ export const opencode_zenProvider: RegistryEntry = {
},
// ── DeepSeek ────────────────────────────────────────────────
{ id: "deepseek-v4-pro", name: "DeepSeek V4 Pro" },
{ id: "deepseek-v4-flash", name: "DeepSeek V4 Flash" },
// #10788: same tier vocabulary as opencode-go's DeepSeek rows — the Zen
// upstream accepts the identical effort set on these models.
{
id: "deepseek-v4-pro",
name: "DeepSeek V4 Pro",
supportsReasoning: true,
supportedThinkingEfforts: ["none", "low", "high", "max"],
},
{
id: "deepseek-v4-flash",
name: "DeepSeek V4 Flash",
supportsReasoning: true,
supportedThinkingEfforts: ["none", "low", "high", "max"],
},
// ── GLM / Z.AI ─────────────────────────────────────────────
{ id: "glm-5.2", name: "GLM-5.2" },
{
id: "glm-5.2",
name: "GLM-5.2",
supportsReasoning: true,
supportedThinkingEfforts: ["high", "max"],
},
// ── MiniMax ────────────────────────────────────────────────
// #3110: MiniMax M3 — frontier coding model with 1M context
{ id: "minimax-m3", name: "MiniMax M3", contextLength: 1048576, supportsVision: true },
// ── Kimi / Moonshot ────────────────────────────────────────
{ id: "kimi-k3", name: "Kimi K3" },
{ id: "kimi-k3", name: "Kimi K3", supportsReasoning: true, supportedThinkingEfforts: ["max"] },
// kimi-k2.7-code declared identically on opencode-go — see OPENCODE_ZEN_GO_SHARED_MODELS.
// ── Qwen ───────────────────────────────────────────────────

View File

@@ -218,3 +218,29 @@ export function getAllRerankModels() {
}
return models;
}
/**
* Derive a Cohere-compatible rerank config for a chat provider that has NO
* curated RERANK_PROVIDERS entry. Works for any registry provider whose base
* URL ends in /chat/completions by swapping that suffix for /rerank (groq,
* mistral, vercel-ai-gateway, ...). Dynamic-URL providers (no usable static
* base, e.g. dynamic account-scoped hosts) derive to null — they need bespoke
* URL handling.
*
* This is a FALLBACK only: callers must check getRerankProvider() first so
* curated entries keep their specialized configuration and format adapters.
*/
export function deriveRerankProviderForChatProvider(providerId, chatEntry) {
if (!chatEntry) return null;
const rawBase = Array.isArray(chatEntry.baseUrl) ? chatEntry.baseUrl[0] : chatEntry.baseUrl;
if (!rawBase || typeof rawBase !== "string") return null;
const base = rawBase.replace(/\/+$/, "");
if (!base.endsWith("/chat/completions")) return null;
return {
id: providerId,
baseUrl: `${base.slice(0, -"/chat/completions".length)}/rerank`,
authType: "apikey",
authHeader: "bearer",
models: [],
};
}

View File

@@ -1,246 +1,218 @@
import { SEARCH_PROVIDERS } from "../config/searchRegistry.ts";
import { registerExecutor, getRegisteredExecutor, hasRegisteredExecutor } from "./registry.ts";
import {
registerLazyExecutor,
loadRegisteredExecutor,
hasRegisteredExecutor,
} from "./registry.ts";
// Type-only: pulls no runtime code, keeps DefaultExecutor the only eager class.
import type { BaseExecutor } from "./base.ts";
import { AntigravityExecutor } from "./antigravity.ts";
import { GithubExecutor } from "./github.ts";
import { GheCopilotExecutor } from "./ghe-copilot.ts";
import { QoderExecutor } from "./qoder.ts";
import { KiroExecutor } from "./kiro.ts";
import { CodexExecutor } from "./codex.ts";
import { CodexAppServerExecutor } from "./codex-app-server.ts";
import { CursorExecutor } from "./cursor.ts";
import { TraeExecutor } from "./trae.ts";
import { DefaultExecutor } from "./default.ts";
import { BedrockExecutor } from "./bedrock.ts";
import { GlmExecutor } from "./glm.ts";
import { PollinationsExecutor } from "./pollinations.ts";
import { CloudflareAIExecutor } from "./cloudflare-ai.ts";
import { FreebuffExecutor } from "./freebuff.ts";
import { OpencodeExecutor } from "./opencode.ts";
import { VertexExecutor } from "./vertex.ts";
import { CliproxyapiExecutor } from "./cliproxyapi.ts";
import { DarioExecutor } from "./dario.ts";
import { NineRouterExecutor } from "./ninerouter.ts";
import { PerplexityWebExecutor } from "./perplexity-web.ts";
import { GrokWebExecutor } from "./grok-web.ts";
import { GeminiWebExecutor } from "./gemini-web.ts";
import { TencentAIStudioWebExecutor } from "./tencent-aistudio-web.ts";
import { GeminiBusinessExecutor } from "./gemini-business.ts";
import { ChatGptWebExecutor } from "./chatgpt-web.ts";
import { ChatGptWebCodexExecutor } from "./chatgpt-web-codex.ts";
import { BlackboxWebExecutor } from "./blackbox-web.ts";
import { MuseSparkWebExecutor } from "./muse-spark-web.ts";
import { AzureOpenAIExecutor } from "./azure-openai.ts";
import { AzureAiExecutor } from "./azure-ai.ts";
import { CommandCodeExecutor } from "./commandCode.ts";
import { GitlabExecutor } from "./gitlab.ts";
import { NlpCloudExecutor } from "./nlpcloud.ts";
import { DevinDesktopExecutor } from "./devin-desktop.ts";
import { ZedHostedExecutor } from "./zed-hosted.ts";
import { DevinCliExecutor } from "./devin-cli.ts";
import { ZcodeExecutor } from "./zcode.ts";
import { DevinCliAgenticExecutor } from "./devin-cli-agentic.ts";
import { AuggieExecutor } from "./auggie.ts";
import { DeepSeekWebExecutor } from "./deepseek-web.ts";
import { DeepSeekWebWithAutoRefreshExecutor } from "./deepseek-web-with-auto-refresh.ts";
import { AdaptaWebExecutor } from "./adapta-web.ts";
import { CopilotWebExecutor } from "./copilot-web.ts";
import { CopilotM365WebExecutor } from "./copilot-m365-web.ts";
import { MicrosoftDesignerWebExecutor } from "./microsoft-designer-web.ts";
import { AdobeFireflyExecutor } from "./adobe-firefly.ts";
import { VeoAIFreeWebExecutor } from "./veoaifree-web.ts";
import { DuckDuckGoWebExecutor } from "./duckduckgo-web.ts";
import { FeloWebExecutor } from "./felo-web.ts";
import { T3ChatWebExecutor } from "./t3-chat-web.ts";
import { ClaudeWebExecutor } from "./claude-web.ts";
import { InnerAiExecutor } from "./inner-ai.ts";
import { HuggingChatExecutor } from "./huggingchat.ts";
import { YuanbaoWebExecutor } from "./yuanbao-web.ts";
import { PoeWebExecutor } from "./poe-web.ts";
import { VeniceWebExecutor } from "./venice-web.ts";
import { NotionWebExecutor } from "./notion-web.ts";
import { V0VercelWebExecutor } from "./v0-vercel-web.ts";
import { CheaperInferenceExecutor } from "./cheaperinference.ts";
import { KimiWebExecutor } from "./kimi-web.ts";
import { DoubaoWebExecutor } from "./doubao-web.ts";
import { QwenWebExecutor } from "./qwen-web.ts";
import { RaycastExecutor } from "./raycast.ts";
import { HailuoWebExecutor } from "./hailuo-web.ts";
import { ZaiWebExecutor } from "./zai-web.ts";
import { KimiExecutor } from "./kimi.ts";
import { MoonshotExecutor } from "./moonshot.ts";
import { TheOldLlmExecutor } from "./theoldllm.ts";
import { ChipotleExecutor } from "./chipotle.ts";
import { LMArenaExecutor } from "./lmarena.ts";
import { GrokCliExecutor } from "./grok-cli.ts";
import { CodeBuddyCnExecutor } from "./codebuddy-cn.ts";
import { ZenmuxFreeExecutor } from "./zenmux-free.ts";
import { CloudflarePlaygroundExecutor } from "./cloudflare-playground.ts";
import { TinyCmsExecutor } from "./tinycms.ts";
import { HyperAgentExecutor } from "./hyperagent.ts";
import { XaiExecutor } from "./xai.ts";
import { PromptQlExecutor } from "./promptql.ts";
import { ConolWebExecutor } from "./conol-web.ts";
// R0.3 — declarative built-in table. The object literal stays as the single
// place built-ins are declared (compile-time duplicate-key safety; the
// check:known-symbols gate parses this literal from source), but lookup goes
// through the ExecutorRegistry (./registry.ts): every entry is registered at
// module load below, and getExecutor()/hasSpecializedExecutor() consult the
// registry — the literal is never read at request time.
const executors = {
antigravity: new AntigravityExecutor(),
agy: new AntigravityExecutor(),
github: new GithubExecutor(),
"ghe-copilot": new GheCopilotExecutor(),
qoder: new QoderExecutor(),
kiro: new KiroExecutor(),
"amazon-q": new KiroExecutor("amazon-q"),
bedrock: new BedrockExecutor(),
codex: new CodexExecutor(),
"codex-app-server": new CodexAppServerExecutor({}, "codex-app-server"),
"chatgpt-web-codex": new ChatGptWebCodexExecutor(),
"cgpt-codex": new ChatGptWebCodexExecutor(),
cursor: new CursorExecutor(),
trae: new TraeExecutor(),
glm: new GlmExecutor("glm"),
"glm-cn": new GlmExecutor("glm-cn"),
glmt: new GlmExecutor("glmt"),
cu: new CursorExecutor(), // Alias for cursor
"cursor-api": new CursorExecutor("cursor-api"),
cua: new CursorExecutor("cursor-api"),
"azure-openai": new AzureOpenAIExecutor(),
"azure-ai": new AzureAiExecutor(),
"command-code": new CommandCodeExecutor(),
cmd: new CommandCodeExecutor(), // Alias
gitlab: new GitlabExecutor(),
"gitlab-duo": new GitlabExecutor("gitlab-duo"),
nlpcloud: new NlpCloudExecutor(),
pollinations: new PollinationsExecutor(),
pol: new PollinationsExecutor(), // Alias
"cloudflare-ai": new CloudflareAIExecutor(),
cf: new CloudflareAIExecutor(), // Alias
freebuff: new FreebuffExecutor(),
fb: new FreebuffExecutor(), // Alias
"opencode-zen": new OpencodeExecutor("opencode-zen"),
"opencode-go": new OpencodeExecutor("opencode-go"),
opencode: new OpencodeExecutor("opencode-zen"), // Alias for opencode-zen
vertex: new VertexExecutor(),
"vertex-partner": new VertexExecutor(),
cliproxyapi: new CliproxyapiExecutor(),
cpa: new CliproxyapiExecutor(), // Alias
dario: new DarioExecutor(),
dr: new DarioExecutor(), // Alias
"9router": new NineRouterExecutor(),
nr: new NineRouterExecutor(), // Alias
"perplexity-web": new PerplexityWebExecutor(),
"pplx-web": new PerplexityWebExecutor(), // Alias
"grok-web": new GrokWebExecutor(),
"claude-web": new ClaudeWebExecutor(),
"cw-web": new ClaudeWebExecutor(), // Alias
"gemini-web": new GeminiWebExecutor(),
gweb: new GeminiWebExecutor(), // Alias
"gemini-business": new GeminiBusinessExecutor(),
gembiz: new GeminiBusinessExecutor(), // Alias
"chatgpt-web": new ChatGptWebExecutor(),
"cgpt-web": new ChatGptWebExecutor(), // Alias
"blackbox-web": new BlackboxWebExecutor(),
"bb-web": new BlackboxWebExecutor(), // Alias
"muse-spark-web": new MuseSparkWebExecutor(),
"ms-web": new MuseSparkWebExecutor(), // Alias
"devin-desktop": new DevinDesktopExecutor(),
"zed-hosted": new ZedHostedExecutor(),
"devin-cli": new DevinCliExecutor(),
zcode: new ZcodeExecutor(),
zc: new ZcodeExecutor(), // Alias
"devin-cli-agentic": new DevinCliAgenticExecutor(),
devin: new DevinCliExecutor(), // Alias
"deepseek-web": new DeepSeekWebWithAutoRefreshExecutor(),
"ds-web": new DeepSeekWebWithAutoRefreshExecutor(), // Alias
"adapta-web": new AdaptaWebExecutor(),
"adp-web": new AdaptaWebExecutor(), // Alias
"copilot-web": new CopilotWebExecutor(),
"copilot-m365-web": new CopilotM365WebExecutor(),
copilot: new CopilotWebExecutor(), // Alias
"microsoft-designer-web": new MicrosoftDesignerWebExecutor(),
msdesigner: new MicrosoftDesignerWebExecutor(), // Alias
"adobe-firefly": new AdobeFireflyExecutor(),
firefly: new AdobeFireflyExecutor(), // Alias
"veoaifree-web": new VeoAIFreeWebExecutor(),
"veo-free": new VeoAIFreeWebExecutor(), // Alias
"duckduckgo-web": new DuckDuckGoWebExecutor(),
ddgw: new DuckDuckGoWebExecutor(), // Alias
"felo-web": new FeloWebExecutor(),
felo: new FeloWebExecutor(), // Alias
"t3-web": new T3ChatWebExecutor(),
t3chat: new T3ChatWebExecutor(), // Alias
"inner-ai": new InnerAiExecutor(),
"in-ai": new InnerAiExecutor(), // Alias
huggingchat: new HuggingChatExecutor(),
hc: new HuggingChatExecutor(), // Alias
"yuanbao-web": new YuanbaoWebExecutor(),
"tencent-aistudio-web": new TencentAIStudioWebExecutor(),
tasw: new TencentAIStudioWebExecutor(),
ybw: new YuanbaoWebExecutor(), // Alias
"poe-web": new PoeWebExecutor(),
// R0.3 — declarative built-in table, made LAZY by #11220.
//
// The object literal below stays as the single place built-ins are declared
// (compile-time duplicate-key safety; the check:known-symbols gate parses this
// literal from source), but its values are now deferred loaders instead of
// eagerly-constructed instances. At module load every ALIAS is registered in
// declaration order; the class import + construction happen on first use via
// loadRegisteredExecutor() and are cached in the same registry a static
// registration would have populated.
//
// Why: importing this barrel previously pulled all ~100 executor modules and
// constructed every instance at boot — ~0.71.2s and ~35MB of heap measured on
// top of the minimal set — even for deployments that use a handful of
// providers. Bundlers split the dynamic imports into on-demand chunks.
//
// Contract preserved (pinned by tests/unit/executor-map-golden.test.ts):
// - keys and their ORDER are byte-stable
// - each alias still gets its OWN instance (aliases never share)
// - ctor arguments are unchanged
const lazyExecutors: Record<string, () => Promise<BaseExecutor>> = {
antigravity: () => import("./antigravity.ts").then((m) => new m.AntigravityExecutor()),
agy: () => import("./antigravity.ts").then((m) => new m.AntigravityExecutor()),
github: () => import("./github.ts").then((m) => new m.GithubExecutor()),
"ghe-copilot": () => import("./ghe-copilot.ts").then((m) => new m.GheCopilotExecutor()),
qoder: () => import("./qoder.ts").then((m) => new m.QoderExecutor()),
kiro: () => import("./kiro.ts").then((m) => new m.KiroExecutor()),
"amazon-q": () => import("./kiro.ts").then((m) => new m.KiroExecutor("amazon-q")),
bedrock: () => import("./bedrock.ts").then((m) => new m.BedrockExecutor()),
codex: () => import("./codex.ts").then((m) => new m.CodexExecutor()),
"codex-app-server": () =>
import("./codex-app-server.ts").then(
(m) => new m.CodexAppServerExecutor({}, "codex-app-server")
),
"chatgpt-web-codex": () =>
import("./chatgpt-web-codex.ts").then((m) => new m.ChatGptWebCodexExecutor()),
"cgpt-codex": () =>
import("./chatgpt-web-codex.ts").then((m) => new m.ChatGptWebCodexExecutor()),
cursor: () => import("./cursor.ts").then((m) => new m.CursorExecutor()),
trae: () => import("./trae.ts").then((m) => new m.TraeExecutor()),
glm: () => import("./glm.ts").then((m) => new m.GlmExecutor("glm")),
"glm-cn": () => import("./glm.ts").then((m) => new m.GlmExecutor("glm-cn")),
glmt: () => import("./glm.ts").then((m) => new m.GlmExecutor("glmt")),
cu: () => import("./cursor.ts").then((m) => new m.CursorExecutor()), // Alias for cursor
"cursor-api": () => import("./cursor.ts").then((m) => new m.CursorExecutor("cursor-api")),
cua: () => import("./cursor.ts").then((m) => new m.CursorExecutor("cursor-api")),
"azure-openai": () => import("./azure-openai.ts").then((m) => new m.AzureOpenAIExecutor()),
"azure-ai": () => import("./azure-ai.ts").then((m) => new m.AzureAiExecutor()),
"command-code": () => import("./commandCode.ts").then((m) => new m.CommandCodeExecutor()),
cmd: () => import("./commandCode.ts").then((m) => new m.CommandCodeExecutor()), // Alias
gitlab: () => import("./gitlab.ts").then((m) => new m.GitlabExecutor()),
"gitlab-duo": () => import("./gitlab.ts").then((m) => new m.GitlabExecutor("gitlab-duo")),
nlpcloud: () => import("./nlpcloud.ts").then((m) => new m.NlpCloudExecutor()),
pollinations: () => import("./pollinations.ts").then((m) => new m.PollinationsExecutor()),
pol: () => import("./pollinations.ts").then((m) => new m.PollinationsExecutor()), // Alias
"cloudflare-ai": () => import("./cloudflare-ai.ts").then((m) => new m.CloudflareAIExecutor()),
cf: () => import("./cloudflare-ai.ts").then((m) => new m.CloudflareAIExecutor()), // Alias
freebuff: () => import("./freebuff.ts").then((m) => new m.FreebuffExecutor()),
fb: () => import("./freebuff.ts").then((m) => new m.FreebuffExecutor()), // Alias
"opencode-zen": () =>
import("./opencode.ts").then((m) => new m.OpencodeExecutor("opencode-zen")),
"opencode-go": () =>
import("./opencode.ts").then((m) => new m.OpencodeExecutor("opencode-go")),
opencode: () =>
import("./opencode.ts").then((m) => new m.OpencodeExecutor("opencode-zen")), // Alias for opencode-zen
vertex: () => import("./vertex.ts").then((m) => new m.VertexExecutor()),
"vertex-partner": () => import("./vertex.ts").then((m) => new m.VertexExecutor()),
cliproxyapi: () => import("./cliproxyapi.ts").then((m) => new m.CliproxyapiExecutor()),
cpa: () => import("./cliproxyapi.ts").then((m) => new m.CliproxyapiExecutor()), // Alias
dario: () => import("./dario.ts").then((m) => new m.DarioExecutor()),
dr: () => import("./dario.ts").then((m) => new m.DarioExecutor()), // Alias
"9router": () => import("./ninerouter.ts").then((m) => new m.NineRouterExecutor()),
nr: () => import("./ninerouter.ts").then((m) => new m.NineRouterExecutor()), // Alias
"perplexity-web": () =>
import("./perplexity-web.ts").then((m) => new m.PerplexityWebExecutor()),
"pplx-web": () =>
import("./perplexity-web.ts").then((m) => new m.PerplexityWebExecutor()), // Alias
"grok-web": () => import("./grok-web.ts").then((m) => new m.GrokWebExecutor()),
"claude-web": () => import("./claude-web.ts").then((m) => new m.ClaudeWebExecutor()),
"cw-web": () => import("./claude-web.ts").then((m) => new m.ClaudeWebExecutor()), // Alias
"gemini-web": () => import("./gemini-web.ts").then((m) => new m.GeminiWebExecutor()),
gweb: () => import("./gemini-web.ts").then((m) => new m.GeminiWebExecutor()), // Alias
"gemini-business": () =>
import("./gemini-business.ts").then((m) => new m.GeminiBusinessExecutor()),
gembiz: () =>
import("./gemini-business.ts").then((m) => new m.GeminiBusinessExecutor()), // Alias
"chatgpt-web": () => import("./chatgpt-web.ts").then((m) => new m.ChatGptWebExecutor()),
"cgpt-web": () => import("./chatgpt-web.ts").then((m) => new m.ChatGptWebExecutor()), // Alias
"blackbox-web": () => import("./blackbox-web.ts").then((m) => new m.BlackboxWebExecutor()),
"bb-web": () => import("./blackbox-web.ts").then((m) => new m.BlackboxWebExecutor()), // Alias
"muse-spark-web": () =>
import("./muse-spark-web.ts").then((m) => new m.MuseSparkWebExecutor()),
"ms-web": () => import("./muse-spark-web.ts").then((m) => new m.MuseSparkWebExecutor()), // Alias
"devin-desktop": () => import("./devin-desktop.ts").then((m) => new m.DevinDesktopExecutor()),
"zed-hosted": () => import("./zed-hosted.ts").then((m) => new m.ZedHostedExecutor()),
"devin-cli": () => import("./devin-cli.ts").then((m) => new m.DevinCliExecutor()),
zcode: () => import("./zcode.ts").then((m) => new m.ZcodeExecutor()),
zc: () => import("./zcode.ts").then((m) => new m.ZcodeExecutor()), // Alias
"devin-cli-agentic": () =>
import("./devin-cli-agentic.ts").then((m) => new m.DevinCliAgenticExecutor()),
devin: () => import("./devin-cli.ts").then((m) => new m.DevinCliExecutor()), // Alias
"deepseek-web": () =>
import("./deepseek-web-with-auto-refresh.ts").then(
(m) => new m.DeepSeekWebWithAutoRefreshExecutor()
),
"ds-web": () =>
import("./deepseek-web-with-auto-refresh.ts").then(
(m) => new m.DeepSeekWebWithAutoRefreshExecutor()
), // Alias
"adapta-web": () => import("./adapta-web.ts").then((m) => new m.AdaptaWebExecutor()),
"adp-web": () => import("./adapta-web.ts").then((m) => new m.AdaptaWebExecutor()), // Alias
"copilot-web": () => import("./copilot-web.ts").then((m) => new m.CopilotWebExecutor()),
"copilot-m365-web": () =>
import("./copilot-m365-web.ts").then((m) => new m.CopilotM365WebExecutor()),
copilot: () => import("./copilot-web.ts").then((m) => new m.CopilotWebExecutor()), // Alias
"microsoft-designer-web": () =>
import("./microsoft-designer-web.ts").then((m) => new m.MicrosoftDesignerWebExecutor()),
msdesigner: () =>
import("./microsoft-designer-web.ts").then((m) => new m.MicrosoftDesignerWebExecutor()), // Alias
"adobe-firefly": () => import("./adobe-firefly.ts").then((m) => new m.AdobeFireflyExecutor()),
firefly: () => import("./adobe-firefly.ts").then((m) => new m.AdobeFireflyExecutor()), // Alias
"veoaifree-web": () => import("./veoaifree-web.ts").then((m) => new m.VeoAIFreeWebExecutor()),
"veo-free": () => import("./veoaifree-web.ts").then((m) => new m.VeoAIFreeWebExecutor()), // Alias
"duckduckgo-web": () =>
import("./duckduckgo-web.ts").then((m) => new m.DuckDuckGoWebExecutor()),
ddgw: () => import("./duckduckgo-web.ts").then((m) => new m.DuckDuckGoWebExecutor()), // Alias
"felo-web": () => import("./felo-web.ts").then((m) => new m.FeloWebExecutor()),
felo: () => import("./felo-web.ts").then((m) => new m.FeloWebExecutor()), // Alias
"t3-web": () => import("./t3-chat-web.ts").then((m) => new m.T3ChatWebExecutor()),
t3chat: () => import("./t3-chat-web.ts").then((m) => new m.T3ChatWebExecutor()), // Alias
"inner-ai": () => import("./inner-ai.ts").then((m) => new m.InnerAiExecutor()),
"in-ai": () => import("./inner-ai.ts").then((m) => new m.InnerAiExecutor()), // Alias
huggingchat: () => import("./huggingchat.ts").then((m) => new m.HuggingChatExecutor()),
hc: () => import("./huggingchat.ts").then((m) => new m.HuggingChatExecutor()), // Alias
"yuanbao-web": () => import("./yuanbao-web.ts").then((m) => new m.YuanbaoWebExecutor()),
"tencent-aistudio-web": () =>
import("./tencent-aistudio-web.ts").then((m) => new m.TencentAIStudioWebExecutor()),
tasw: () =>
import("./tencent-aistudio-web.ts").then((m) => new m.TencentAIStudioWebExecutor()), // Alias
ybw: () => import("./yuanbao-web.ts").then((m) => new m.YuanbaoWebExecutor()), // Alias
"poe-web": () => import("./poe-web.ts").then((m) => new m.PoeWebExecutor()),
// #8969: do NOT alias canonical `poe` (API-key / api.poe.com) to PoeWebExecutor.
// Registry declares executor:"default"; the hard-coded map previously won and
// routed API-key traffic to GraphQL /api/gql_POST → HTTP 405.
"venice-web": new VeniceWebExecutor(),
ven: new VeniceWebExecutor(), // Alias
"notion-web": new NotionWebExecutor(),
nw: new NotionWebExecutor(), // Alias
promptql: new PromptQlExecutor(),
pql: new PromptQlExecutor(), // Alias
"v0-vercel-web": new V0VercelWebExecutor(),
v0: new V0VercelWebExecutor(), // Alias
"kimi-web": new KimiWebExecutor(),
"kimi-coding-apikey": new KimiExecutor("kimi-coding-apikey"), // Legacy alias
"kimi-coding": new KimiExecutor(), // Alias
moonshot: new MoonshotExecutor(),
kimi: new MoonshotExecutor("kimi"), // Hidden legacy Moonshot provider id
cheaperinference: new CheaperInferenceExecutor(),
cinf: new CheaperInferenceExecutor("cheaperinference"), // Alias
"doubao-web": new DoubaoWebExecutor(),
db: new DoubaoWebExecutor(), // Alias
"qwen-web": new QwenWebExecutor(),
raycast: new RaycastExecutor(),
rc: new RaycastExecutor(), // Alias
"hailuo-web": new HailuoWebExecutor(),
"zai-web": new ZaiWebExecutor(),
zw: new ZaiWebExecutor(), // Alias
theoldllm: new TheOldLlmExecutor(),
tllm: new TheOldLlmExecutor(), // Alias
chipotle: new ChipotleExecutor(),
pepper: new ChipotleExecutor(), // Alias
lmarena: new LMArenaExecutor(),
lma: new LMArenaExecutor(), // Alias
"grok-cli": new GrokCliExecutor(),
gc: new GrokCliExecutor(), // Alias
"codebuddy-cn": new CodeBuddyCnExecutor(),
cbcn: new CodeBuddyCnExecutor(), // Alias for codebuddy-cn
"zenmux-free": new ZenmuxFreeExecutor(),
"cloudflare-playground": new CloudflarePlaygroundExecutor(),
cfp: new CloudflarePlaygroundExecutor(), // Alias for cloudflare-playground
"tinycms-web": new TinyCmsExecutor(),
tcw: new TinyCmsExecutor(), // Alias
hyperagent: new HyperAgentExecutor(),
ha: new HyperAgentExecutor(), // Alias
zmf: new ZenmuxFreeExecutor(), // Alias for zenmux-free
auggie: new AuggieExecutor(),
xai: new XaiExecutor(),
"xai-oauth": new XaiExecutor("xai-oauth"),
xao: new XaiExecutor("xai-oauth"),
qw: new QwenWebExecutor(), // Alias
"conol-web": new ConolWebExecutor(),
cnl: new ConolWebExecutor(), // Alias
"venice-web": () => import("./venice-web.ts").then((m) => new m.VeniceWebExecutor()),
ven: () => import("./venice-web.ts").then((m) => new m.VeniceWebExecutor()), // Alias
"notion-web": () => import("./notion-web.ts").then((m) => new m.NotionWebExecutor()),
nw: () => import("./notion-web.ts").then((m) => new m.NotionWebExecutor()), // Alias
promptql: () => import("./promptql.ts").then((m) => new m.PromptQlExecutor()),
pql: () => import("./promptql.ts").then((m) => new m.PromptQlExecutor()), // Alias
"v0-vercel-web": () => import("./v0-vercel-web.ts").then((m) => new m.V0VercelWebExecutor()),
v0: () => import("./v0-vercel-web.ts").then((m) => new m.V0VercelWebExecutor()), // Alias
"kimi-web": () => import("./kimi-web.ts").then((m) => new m.KimiWebExecutor()),
"kimi-coding-apikey": () =>
import("./kimi.ts").then((m) => new m.KimiExecutor("kimi-coding-apikey")), // Legacy alias
"kimi-coding": () => import("./kimi.ts").then((m) => new m.KimiExecutor()), // Alias
moonshot: () => import("./moonshot.ts").then((m) => new m.MoonshotExecutor()),
kimi: () => import("./moonshot.ts").then((m) => new m.MoonshotExecutor("kimi")), // Hidden legacy Moonshot provider id
cheaperinference: () =>
import("./cheaperinference.ts").then((m) => new m.CheaperInferenceExecutor()),
cinf: () =>
import("./cheaperinference.ts").then(
(m) => new m.CheaperInferenceExecutor("cheaperinference")
), // Alias
"doubao-web": () => import("./doubao-web.ts").then((m) => new m.DoubaoWebExecutor()),
db: () => import("./doubao-web.ts").then((m) => new m.DoubaoWebExecutor()), // Alias
"qwen-web": () => import("./qwen-web.ts").then((m) => new m.QwenWebExecutor()),
raycast: () => import("./raycast.ts").then((m) => new m.RaycastExecutor()),
rc: () => import("./raycast.ts").then((m) => new m.RaycastExecutor()), // Alias
"hailuo-web": () => import("./hailuo-web.ts").then((m) => new m.HailuoWebExecutor()),
"zai-web": () => import("./zai-web.ts").then((m) => new m.ZaiWebExecutor()),
zw: () => import("./zai-web.ts").then((m) => new m.ZaiWebExecutor()), // Alias
theoldllm: () => import("./theoldllm.ts").then((m) => new m.TheOldLlmExecutor()),
tllm: () => import("./theoldllm.ts").then((m) => new m.TheOldLlmExecutor()), // Alias
chipotle: () => import("./chipotle.ts").then((m) => new m.ChipotleExecutor()),
pepper: () => import("./chipotle.ts").then((m) => new m.ChipotleExecutor()), // Alias
lmarena: () => import("./lmarena.ts").then((m) => new m.LMArenaExecutor()),
lma: () => import("./lmarena.ts").then((m) => new m.LMArenaExecutor()), // Alias
"grok-cli": () => import("./grok-cli.ts").then((m) => new m.GrokCliExecutor()),
gc: () => import("./grok-cli.ts").then((m) => new m.GrokCliExecutor()), // Alias
"codebuddy-cn": () => import("./codebuddy-cn.ts").then((m) => new m.CodeBuddyCnExecutor()),
cbcn: () => import("./codebuddy-cn.ts").then((m) => new m.CodeBuddyCnExecutor()), // Alias for codebuddy-cn
"zenmux-free": () => import("./zenmux-free.ts").then((m) => new m.ZenmuxFreeExecutor()),
"cloudflare-playground": () =>
import("./cloudflare-playground.ts").then((m) => new m.CloudflarePlaygroundExecutor()),
cfp: () =>
import("./cloudflare-playground.ts").then((m) => new m.CloudflarePlaygroundExecutor()), // Alias for cloudflare-playground
"tinycms-web": () => import("./tinycms.ts").then((m) => new m.TinyCmsExecutor()),
tcw: () => import("./tinycms.ts").then((m) => new m.TinyCmsExecutor()), // Alias
hyperagent: () => import("./hyperagent.ts").then((m) => new m.HyperAgentExecutor()),
ha: () => import("./hyperagent.ts").then((m) => new m.HyperAgentExecutor()), // Alias
zmf: () => import("./zenmux-free.ts").then((m) => new m.ZenmuxFreeExecutor()), // Alias for zenmux-free
auggie: () => import("./auggie.ts").then((m) => new m.AuggieExecutor()),
xai: () => import("./xai.ts").then((m) => new m.XaiExecutor()),
"xai-oauth": () => import("./xai.ts").then((m) => new m.XaiExecutor("xai-oauth")),
xao: () => import("./xai.ts").then((m) => new m.XaiExecutor("xai-oauth")),
qw: () => import("./qwen-web.ts").then((m) => new m.QwenWebExecutor()), // Alias
"conol-web": () => import("./conol-web.ts").then((m) => new m.ConolWebExecutor()),
cnl: () => import("./conol-web.ts").then((m) => new m.ConolWebExecutor()), // Alias
};
// Bootstrap: register every built-in in the ExecutorRegistry. registerExecutor
// throws on duplicates, so an alias collision fails at module load, exactly as
// loudly as a duplicate object key would have failed at lint time.
for (const [alias, executor] of Object.entries(executors) as [string, BaseExecutor][]) {
registerExecutor(alias, executor);
// Bootstrap: declare every built-in alias in the ExecutorRegistry. Duplicate
// aliases fail at module load, exactly as loudly as a duplicate object key
// would have failed at lint time. Instances materialize on first use (#11220).
for (const [alias, load] of Object.entries(lazyExecutors)) {
registerLazyExecutor(alias, load);
}
const defaultCache = new Map();
@@ -267,9 +239,9 @@ const CHAT_UNSUPPORTED_CLOUD_AGENT_PROVIDERS = new Set(["jules"]);
// providers must be executed through /v1/search, never the chat-completions path.
const CHAT_UNSUPPORTED_SEARCH_PROVIDERS = new Set(Object.keys(SEARCH_PROVIDERS));
export function getExecutor(provider) {
const registered = getRegisteredExecutor(provider);
if (registered) return registered;
export async function getExecutor(provider: string): Promise<BaseExecutor> {
const loaded = await loadRegisteredExecutor(provider);
if (loaded) return loaded;
if (CHAT_UNSUPPORTED_CLOUD_AGENT_PROVIDERS.has(provider)) {
const err = new Error(
`Provider "${provider}" is a cloud-agent provider and does not support direct chat completions; use the Cloud Agents task API instead.`
@@ -285,77 +257,19 @@ export function getExecutor(provider) {
throw err;
}
if (!defaultCache.has(provider)) defaultCache.set(provider, new DefaultExecutor(provider));
return defaultCache.get(provider);
return defaultCache.get(provider)!;
}
export function hasSpecializedExecutor(provider) {
export function hasSpecializedExecutor(provider: string): boolean {
return hasRegisteredExecutor(provider);
}
export { registerExecutor, listExecutorAliases } from "./registry.ts";
export {
registerExecutor,
registerLazyExecutor,
listExecutorAliases,
} from "./registry.ts";
// Value re-export: base.ts is already eager (DefaultExecutor extends it), and
// scripts/check/check-known-symbols.ts reads this export from the module.
export { BaseExecutor } from "./base.ts";
export { AntigravityExecutor } from "./antigravity.ts";
export { GithubExecutor } from "./github.ts";
export { QoderExecutor } from "./qoder.ts";
export { KiroExecutor } from "./kiro.ts";
export { CodexExecutor } from "./codex.ts";
export { CursorExecutor } from "./cursor.ts";
export { TraeExecutor } from "./trae.ts";
export { DefaultExecutor } from "./default.ts";
export { BedrockExecutor } from "./bedrock.ts";
export { GlmExecutor } from "./glm.ts";
export { PollinationsExecutor } from "./pollinations.ts";
export { CloudflareAIExecutor } from "./cloudflare-ai.ts";
export { OpencodeExecutor } from "./opencode.ts";
export { CliproxyapiExecutor } from "./cliproxyapi.ts";
export { DarioExecutor } from "./dario.ts";
export { NineRouterExecutor } from "./ninerouter.ts";
export { VertexExecutor } from "./vertex.ts";
export { PerplexityWebExecutor } from "./perplexity-web.ts";
export { GrokWebExecutor } from "./grok-web.ts";
export { GeminiWebExecutor } from "./gemini-web.ts";
export { KieExecutor } from "./kie.ts";
export { ChatGptWebExecutor } from "./chatgpt-web.ts";
export { BlackboxWebExecutor } from "./blackbox-web.ts";
export { MuseSparkWebExecutor } from "./muse-spark-web.ts";
export { AzureOpenAIExecutor } from "./azure-openai.ts";
export { AzureAiExecutor } from "./azure-ai.ts";
export { CommandCodeExecutor } from "./commandCode.ts";
export { GitlabExecutor } from "./gitlab.ts";
export { NlpCloudExecutor } from "./nlpcloud.ts";
export { DevinDesktopExecutor } from "./devin-desktop.ts";
export { ZedHostedExecutor } from "./zed-hosted.ts";
export { DevinCliExecutor } from "./devin-cli.ts";
export { DevinCliAgenticExecutor } from "./devin-cli-agentic.ts";
export { AuggieExecutor } from "./auggie.ts";
export { CopilotWebExecutor } from "./copilot-web.ts";
export { CopilotM365WebExecutor } from "./copilot-m365-web.ts";
export { MicrosoftDesignerWebExecutor } from "./microsoft-designer-web.ts";
export { AdobeFireflyExecutor } from "./adobe-firefly.ts";
export { VeoAIFreeWebExecutor } from "./veoaifree-web.ts";
export { DuckDuckGoWebExecutor } from "./duckduckgo-web.ts";
export { FeloWebExecutor } from "./felo-web.ts";
export { ClaudeWebExecutor } from "./claude-web.ts";
export { DeepSeekWebExecutor } from "./deepseek-web.ts";
export { DeepSeekWebWithAutoRefreshExecutor } from "./deepseek-web-with-auto-refresh.ts";
export { AdaptaWebExecutor } from "./adapta-web.ts";
export { YuanbaoWebExecutor } from "./yuanbao-web.ts";
export { T3ChatWebExecutor } from "./t3-chat-web.ts";
export { InnerAiExecutor } from "./inner-ai.ts";
export { QwenWebExecutor } from "./qwen-web.ts";
export { HailuoWebExecutor } from "./hailuo-web.ts";
export { TheOldLlmExecutor } from "./theoldllm.ts";
export { ChipotleExecutor } from "./chipotle.ts";
export { LMArenaExecutor } from "./lmarena.ts";
export { GrokCliExecutor } from "./grok-cli.ts";
export { CodeBuddyCnExecutor } from "./codebuddy-cn.ts";
export { ZenmuxFreeExecutor } from "./zenmux-free.ts";
export { CloudflarePlaygroundExecutor } from "./cloudflare-playground.ts";
export { TinyCmsExecutor } from "./tinycms.ts";
export { HyperAgentExecutor } from "./hyperagent.ts";
export { XaiExecutor } from "./xai.ts";
export { MoonshotExecutor } from "./moonshot.ts";
export { CheaperInferenceExecutor } from "./cheaperinference.ts";
export { PromptQlExecutor } from "./promptql.ts";
export { ConolWebExecutor } from "./conol-web.ts";

View File

@@ -895,10 +895,21 @@ export class OpencodeExecutor extends BaseExecutor {
const mb = modifiedBody as Record<string, unknown>;
const parsed = parseEffortLevel(model);
if (parsed) {
mb.model = parsed.baseModel;
if (mb.reasoning_effort === undefined) {
mb.reasoning_effort = parsed.effort;
const deepseekFamily =
parsed.baseModel === "deepseek-v4-pro" || parsed.baseModel === "deepseek-v4-flash";
if (deepseekFamily) {
// DeepSeek via opencode-go proxies the native DeepSeek contract, which
// accepts a flat reasoning_effort field (#4647).
mb.model = parsed.baseModel;
if (mb.reasoning_effort === undefined) {
mb.reasoning_effort = parsed.effort;
}
}
// #10788: every other family's ONLY native effort mechanism is the
// -<tier> suffix in the model id itself (the ids `opencode models
// opencode-go --verbose` lists). The opencode-go ChatCompletionRequest
// carries no flat reasoning_effort field, so rewriting to the base id
// silently dropped the tier — forward the aliased id verbatim instead.
}
}
// #1543 / upstream PR #1099: thinking-mode upstreams routed through OpenCode

View File

@@ -28,11 +28,46 @@ export function getRegisteredExecutor(alias: string): BaseExecutor | undefined {
return registry.get(alias);
}
export function hasRegisteredExecutor(alias: string): boolean {
return registry.has(alias);
// ── #11220: lazy registration ───────────────────────────────────────────────
// Aliases may register a deferred loader instead of an instance. The alias and
// its registration ORDER are declared eagerly — hasRegisteredExecutor() and
// listExecutorAliases() stay synchronous and the golden snapshot keeps its
// shape — while the class import + construction happen on first use. A
// completed load caches into `registry`, so later resolution is identical to a
// static registration.
const lazyLoaders = new Map<string, () => Promise<BaseExecutor>>();
const lazyInFlight = new Map<string, Promise<BaseExecutor>>();
export function registerLazyExecutor(alias: string, load: () => Promise<BaseExecutor>): void {
if (registry.has(alias) || lazyLoaders.has(alias)) {
throw new Error(`executor alias already registered: "${alias}"`);
}
lazyLoaders.set(alias, load);
}
/** All registered aliases, in registration order. */
export function loadRegisteredExecutor(alias: string): Promise<BaseExecutor> | undefined {
const cached = registry.get(alias);
if (cached) return Promise.resolve(cached);
const load = lazyLoaders.get(alias);
if (!load) return undefined;
let inFlight = lazyInFlight.get(alias);
if (!inFlight) {
inFlight = load().then((executor) => {
registerExecutor(alias, executor);
lazyLoaders.delete(alias);
lazyInFlight.delete(alias);
return executor;
});
lazyInFlight.set(alias, inFlight);
}
return inFlight;
}
export function hasRegisteredExecutor(alias: string): boolean {
return registry.has(alias) || lazyLoaders.has(alias);
}
/** All registered aliases — static and lazy — in registration order. */
export function listExecutorAliases(): string[] {
return [...registry.keys()];
return [...registry.keys(), ...lazyLoaders.keys()];
}

View File

@@ -14,12 +14,12 @@
type ExecutorInput = {
model: string;
body: unknown;
[key: string]: unknown;
};
// No index signature: executors (BaseExecutor subclasses) must satisfy this
// structurally, and class instances don't carry index signatures.
type ExecutorLike = {
execute: (input: ExecutorInput) => Promise<unknown>;
[key: string]: unknown;
};
export type CliproxyapiModelMapping = Record<string, unknown> | null | undefined;

View File

@@ -19,12 +19,12 @@ import type { ProviderCredentials } from "../../executors/base.ts";
type ExecutorInput = {
credentials: ProviderCredentials;
[key: string]: unknown;
};
// No index signature: executors (BaseExecutor subclasses) must satisfy this
// structurally, and class instances don't carry index signatures.
type ExecutorLike = {
execute: (input: ExecutorInput) => Promise<unknown>;
[key: string]: unknown;
};
/**

View File

@@ -76,12 +76,15 @@ async function loadCliproxyapiSettings(): Promise<{
* dedicated-credential wrappers applied. Used by the direct `cliproxyapi` leg
* and the CLIProxyAPI branch of `fallback`.
*/
function resolveCliproxyapiExecutor(
async function resolveCliproxyapiExecutor(
cliproxyapiModelMapping: Record<string, unknown> | null,
dedicatedApiKey: string | null
) {
return wrapExecutorWithCliproxyapiCredentials(
wrapExecutorWithCliproxyapiModelMapping(getExecutor("cliproxyapi"), cliproxyapiModelMapping),
wrapExecutorWithCliproxyapiModelMapping(
await getExecutor("cliproxyapi"),
cliproxyapiModelMapping
),
dedicatedApiKey
);
}
@@ -138,7 +141,7 @@ export async function resolveExecutorWithProxy(
// backend on specific failures. The backend defaults to CLIProxyAPI so every
// pre-existing fallback config behaves exactly as before; fallbackBackend
// === "dario" opts the retry leg over to Dario instead.
const nativeExec = getExecutor(prov);
const nativeExec = await getExecutor(prov);
const fallbackBackend: FallbackBackend = cfg.fallbackBackend;
const { fallbackCodes, dedicatedApiKey } = await loadCliproxyapiSettings();
@@ -146,8 +149,8 @@ export async function resolveExecutorWithProxy(
// the native leg must keep seeing the original, unmapped model.
const proxyExec =
fallbackBackend === "dario"
? getExecutor("dario")
: resolveCliproxyapiExecutor(cfg.cliproxyapiModelMapping, dedicatedApiKey);
? await getExecutor("dario")
: await resolveCliproxyapiExecutor(cfg.cliproxyapiModelMapping, dedicatedApiKey);
const backendLabel = fallbackBackend === "dario" ? "Dario" : "CLIProxyAPI";
const isRetryableStatus = (s: number) => fallbackCodes.includes(s) || s === 0;

View File

@@ -201,6 +201,7 @@ export async function handleRerank({
connectionId = null,
apiKeyId = null,
apiKeyName = null,
resolvedProvider = null,
}) {
const startTime = Date.now();
if (!model) return errorResponse(400, "model is required");
@@ -210,7 +211,8 @@ export async function handleRerank({
}
const { provider: providerId, model: modelId } = parseRerankModel(model);
const providerConfig = providerId ? getRerankProvider(providerId) : null;
const providerConfig =
resolvedProvider || (providerId ? getRerankProvider(providerId) : null);
if (!providerConfig) {
const availableProviders = Object.keys(RERANK_PROVIDERS).join(", ");
@@ -219,10 +221,13 @@ export async function handleRerank({
`No rerank provider found for model "${model}". Available: ${availableProviders}`
);
}
// When a derived/generic provider is injected, its id is authoritative for
// logging and cost attribution even though parseRerankModel returned null.
const effectiveProviderId = providerConfig.id || providerId;
const token = credentials?.apiKey || credentials?.accessToken;
if (!token) {
return errorResponse(401, `No credentials for rerank provider: ${providerId}`);
return errorResponse(401, `No credentials for rerank provider: ${effectiveProviderId}`);
}
const requestBody = transformRequestForProvider(providerConfig, {
@@ -275,8 +280,8 @@ export async function handleRerank({
method: "POST",
path: "/v1/rerank",
status: res.status,
model: `${providerId}/${modelId}`,
provider: providerId,
model: `${effectiveProviderId}/${modelId}`,
provider: effectiveProviderId,
connectionId: connectionId || undefined,
duration: Date.now() - startTime,
requestBody,
@@ -296,14 +301,14 @@ export async function handleRerank({
});
const searchUnits = Number(result?.meta?.billed_units?.search_units) || 0;
const costUsd = await calculateModalCost("rerank", providerId, modelId, { searchUnits });
const costUsd = await calculateModalCost("rerank", effectiveProviderId, modelId, { searchUnits });
saveCallLog({
method: "POST",
path: "/v1/rerank",
status: 200,
model: `${providerId}/${modelId}`,
provider: providerId,
model: `${effectiveProviderId}/${modelId}`,
provider: effectiveProviderId,
connectionId: connectionId || undefined,
duration: Date.now() - startTime,
tokens: { prompt_tokens: 0, completion_tokens: 0 },
@@ -315,7 +320,7 @@ export async function handleRerank({
const headers = new Headers({ ...CORS_HEADERS, "Content-Type": "application/json" });
attachOmniRouteMetaHeaders(headers, {
provider: providerId,
provider: effectiveProviderId,
model: modelId,
costUsd,
latencyMs: Date.now() - startTime,

View File

@@ -374,7 +374,7 @@ async function handleVertexVeoGeneration({ model, body, credentials, log }) {
* Submits an AnimateDiff or SVD workflow, polls for completion, fetches output video
*/
async function handleVeoAiFreeVideoGeneration({ model, provider, body, credentials, log }) {
const executor = getExecutor(provider);
const executor = await getExecutor(provider);
if (!executor) {
return { success: false, status: 400, error: `Unknown video provider: ${provider}` };
}

View File

@@ -44,12 +44,6 @@ export const AUTO_TEMPLATE_VARIANTS: Record<string, AutoVariant | undefined> = {
"auto/claude-opus": "smart",
"auto/claude-sonnet": "coding",
"auto/best-free": "cheap",
// Subscription-first routing (see `subscriptionLadder.ts`). `auto/subscription`
// maps to no weight variant on purpose: its pool is already restricted to
// plan-included connections, so the scorer should rank them on merit rather
// than bias toward cheap/fast within an allowance the operator already paid for.
"auto/subscription": undefined,
"auto/thrifty": "cheap",
// Chaos mode — parallel dispatch to top-N stable models
"auto/best-chaos": "chaos",
"auto/chaos": "chaos",
@@ -73,18 +67,6 @@ export const AUTO_SUFFIX_VARIANTS: string[] = [
"auto/multimodal",
];
/**
* Flat `auto/*` ids that carry a tier overlay even though they are not written
* in `<category>:<tier>` form. `auto/best-free` established the pattern; the
* two subscription-first ids reuse it so a caller can ask for the behavior
* without also having to pick a category.
*/
export const FLAT_TIER_OVERLAY_IDS: Record<string, AutoTier> = {
"auto/best-free": "free",
"auto/subscription": "subscription",
"auto/thrifty": "thrifty",
};
type ResolvedAutoVariant =
{ recognized: true; variant: AutoVariant | undefined } | { recognized: false };
@@ -217,9 +199,8 @@ export async function createBuiltinAutoCombo(
}
if ("variant" in spec && spec.variant !== undefined) {
const overlayTier = FLAT_TIER_OVERLAY_IDS[modelStr];
const virtualCombo = await materialize(spec.variant, {
...(overlayTier ? { tier: overlayTier } : {}),
...(modelStr === "auto/best-free" ? { tier: "free" as const } : {}),
});
virtualCombo.name = modelStr;
virtualCombo.id = modelStr;
@@ -230,11 +211,7 @@ export async function createBuiltinAutoCombo(
// auto/best-chat, auto/pro-chat) still materialize via the default
// (unconstrained) virtual combo rather than throwing "Unknown built-in".
if (Object.prototype.hasOwnProperty.call(AUTO_TEMPLATE_VARIANTS, modelStr)) {
const overlayTier = FLAT_TIER_OVERLAY_IDS[modelStr];
const virtualCombo = await materialize(
undefined,
overlayTier ? { tier: overlayTier } : undefined
);
const virtualCombo = await materialize(undefined);
virtualCombo.name = modelStr;
virtualCombo.id = modelStr;
return virtualCombo;

View File

@@ -1,100 +0,0 @@
/**
* Pure classifier over the curated connection-billing catalog
* (`open-sse/config/connectionBillingCatalog.ts`).
*
* Kept dependency-light on purpose — the same constraint `paidModelFilter.ts`
* and `strictZeroCostFilter.ts` state in their own headers — so subscription
* routing is unit-testable without seeding the DB or the virtual factory. No
* provider name appears in this file: a connection is classified purely from
* the catalog plus the two facts the caller already has (`provider`,
* `authType`), so curating a new provider needs no code change here.
*/
import {
CONNECTION_BILLING_CATALOG,
type ConnectionBillingClass,
type ConnectionBillingEntry,
type ConnectionOverageBehavior,
} from "@omniroute/open-sse/config/connectionBillingCatalog.ts";
import { SYNTHETIC_NOAUTH_CONNECTION_ID } from "./resilienceCandidateFilter";
/** The minimum a caller must know about a connection to classify it. */
export interface BillableConnection {
provider: string;
/** `provider_connections.auth_type` — `oauth` / `apikey` / `cookie` / … */
authType?: string | null;
/** Connection id; the synthetic no-auth sentinel classifies as `keyless`. */
connectionId?: string | null;
}
export interface ConnectionBillingVerdict {
billing: ConnectionBillingClass;
overage: ConnectionOverageBehavior;
reason: string;
}
const UNKNOWN_VERDICT: ConnectionBillingVerdict = {
billing: "unknown",
overage: "unknown",
reason: "No curated billing entry for this provider/authType — assumed metered.",
};
const KEYLESS_VERDICT: ConnectionBillingVerdict = {
billing: "keyless",
overage: "hard-stop",
reason:
"Synthetic no-auth connection: no credential exists, so no request against it can be billed.",
};
/**
* Classify one connection.
*
* Resolution order, first match wins:
* 1. the synthetic no-auth sentinel → `keyless` (no credential can be billed);
* 2. a catalog entry matching BOTH provider and `authType`;
* 3. a provider-wide catalog entry (no `authType` declared);
* 4. otherwise `unknown`.
*
* `unknown` is never treated as free by any caller — `isPlanIncluded()` below
* returns false for it, so an uncurated provider stays outside the
* subscription rung until someone curates it deliberately.
*/
export function classifyConnectionBilling(
connection: BillableConnection,
catalog: readonly ConnectionBillingEntry[] = CONNECTION_BILLING_CATALOG
): ConnectionBillingVerdict {
if (connection.connectionId === SYNTHETIC_NOAUTH_CONNECTION_ID) return KEYLESS_VERDICT;
const provider = connection.provider;
if (!provider) return UNKNOWN_VERDICT;
const providerEntries = catalog.filter((entry) => entry.provider === provider);
if (providerEntries.length === 0) return UNKNOWN_VERDICT;
const authType = typeof connection.authType === "string" ? connection.authType : null;
const authMatch = authType
? providerEntries.find((entry) => entry.authType === authType)
: undefined;
const entry = authMatch ?? providerEntries.find((entry) => entry.authType === undefined);
if (!entry) return UNKNOWN_VERDICT;
return { billing: entry.billing, overage: entry.overage, reason: entry.reason };
}
/**
* True when serving a request through this connection consumes an allowance
* the operator already pays for, rather than adding incremental spend.
* `keyless` qualifies: it costs nothing by construction.
*/
export function isPlanIncluded(verdict: ConnectionBillingVerdict): boolean {
return verdict.billing === "subscription" || verdict.billing === "keyless";
}
/**
* True when exhausting this connection's allowance cannot start costing money.
* The strict `auto/subscription` grouping admits nothing else: an operator who
* asked never to spend extra must not be surprised by a provider that meters
* past the plan, nor by one whose terms simply are not established.
*/
export function isOverageSafe(verdict: ConnectionBillingVerdict): boolean {
return verdict.overage === "hard-stop";
}

View File

@@ -22,7 +22,6 @@ import {
import { getCachedProviderConnections } from "@/lib/db/readCache";
import { defaultLogger as log } from "@omniroute/open-sse/utils/logger";
import type { FreeAccessState } from "./strictZeroCostFilter";
import { isStateStaleForReset } from "./subscriptionLadder";
const USAGE_FETCHER_PROVIDER_SET = new Set<string>(USAGE_FETCHER_PROVIDERS);
@@ -192,13 +191,7 @@ export function resolveFreeAccessState(
const key = cacheKey(provider, connectionId);
const entry = cache.get(key);
// Subscription-first routing (decision 3): an entry whose own `resetAt` has
// already passed describes a quota window that no longer exists, so it is
// stale REGARDLESS of its age. Without this, a plan that refilled at
// midnight keeps reading EXHAUSTED until the TTL happens to lapse, and
// routing stays on paid rungs for no reason. See `subscriptionLadder.ts`.
const resetElapsed = entry !== undefined && isStateStaleForReset(entry.state);
const fresh = entry && !resetElapsed && Date.now() - entry.fetchedAtMs <= ttlMs();
const fresh = entry && Date.now() - entry.fetchedAtMs <= ttlMs();
if (!fresh) {
void refresh(provider, connectionId);
}

View File

@@ -1,411 +0,0 @@
/**
* Subscription-first routing: the rung model, its two groupings, and the
* reset re-entry rules.
*
* OmniRoute already answers "is this model free?" (`paidModelFilter.ts`) and
* "can this connection ever bill me?" (`strictZeroCostFilter.ts`). Both fail
* CLOSED — an exhausted free pool is an empty pool, never a step up to a paid
* option. And every paid-side mechanism (`cost-optimized`, `budgetCap`,
* the `cost-saver` mode pack) is tier-agnostic. Neither side answers:
*
* "use the quota I already pay for; when it runs out either stop, or step up
* one rung at a time; and come back the moment it resets."
*
* This module supplies both halves of that, sharing one rung model:
*
* - `filterSubscriptionOnlyCandidates` — the strict grouping (`auto/subscription`).
* Rung 0 only, overage-safe connections only, verified live. Fails CLOSED.
* - `orderPoolByRung` — the escalating grouping (`auto/thrifty`). All rungs,
* ordered, with exhausted rungs gated out. Fails OPEN, one rung at a time.
*
* Design mirrors `strictZeroCostFilter.ts` deliberately: pure functions, the
* live quota lookup injected as a synchronous resolver, no DB or network
* import, and the SAME connection-safety invariant — every connection in a
* candidate's `allowedConnectionIds` is verified INDIVIDUALLY and the array is
* rewritten to exactly the surviving subset, never the full original list.
* `autoStrategy.ts` enforces `allowedConnectionIds` as a hard allowlist before
* selecting a connection at dispatch, so rewriting it here is sufficient to
* make "verified" and "actually used" the same set by construction.
*/
import {
classifyConnectionBilling,
isOverageSafe,
type BillableConnection,
} from "./connectionBilling";
import type { ConnectionBillingEntry } from "@omniroute/open-sse/config/connectionBillingCatalog.ts";
import type { FreeAccessState } from "./strictZeroCostFilter";
/**
* Rungs in escalation order. Index is the ordering key; membership is decided
* by `assignRung` below.
*
* The rungs differ in more than price — each has its OWN exhaustion signal,
* which is why this is not just a sort:
*
* subscription / keyless / free → exhausted on QUOTA (observable, tracked)
* cheap / premium → exhausted on BUDGET (no quota exists; a paid
* connection serves forever)
*/
export const RUNG_ORDER = ["subscription", "keyless", "free", "cheap", "premium"] as const;
export type LadderRung = (typeof RUNG_ORDER)[number];
/** Rungs whose exhaustion is observable from provider quota state. */
const QUOTA_BEARING_RUNGS: ReadonlySet<LadderRung> = new Set<LadderRung>([
"subscription",
"keyless",
"free",
]);
export function rungIndex(rung: LadderRung): number {
return RUNG_ORDER.indexOf(rung);
}
/** A candidate as this module needs to see it — a structural subset of
* `VirtualAutoComboCandidate` (`virtualFactory.ts`), so this file has no
* dependency on that module's full type. */
export interface LadderCandidate {
provider: string;
model: string;
connectionId: string | null;
allowedConnectionIds?: string[];
}
export interface LadderOptions {
/** Master switch. When false every exported filter is the identity function
* — the same off-by-default contract `filterPaidOnlyCandidates` holds. */
enabled: boolean;
/**
* Live allowance/quota state for ONE (provider, connection) pair, resolved
* from the cache in `freeAccessQuota.ts`. Synchronous by design: nothing in
* a candidate-pool build may await a network call.
*
* `undefined` means "no usage adapter for this provider, or nothing fresh
* cached". The two groupings interpret that OPPOSITELY on purpose — see
* `admitUnknownQuota` below.
*/
resolveFreeAccessState: (provider: string, connectionId: string) => FreeAccessState | undefined;
/**
* `authType` for a connection id (`provider_connections.auth_type`), needed
* to classify billing. Unknown ids resolve to `null` → the provider-wide
* catalog entry, or `unknown` billing.
*/
resolveAuthType: (connectionId: string) => string | null;
/** Economic tier of a (provider, model) pair — `classifyTier()` in
* production, injected so this module needs no registry/pricing import. */
resolveEconomicTier: (provider: string, model: string) => "free" | "cheap" | "premium";
/**
* Remaining-percent at or below which a quota-bearing connection counts as
* exhausted. Default 2, matching `quotaPreflight.defaultThresholdPercent`
* (`src/lib/resilience/settings/types.ts`) so the two agree.
*/
exitCutoffPercent?: number;
/**
* Remaining-percent a quota-bearing connection must EXCEED to be re-admitted
* after having been exhausted. Strictly greater than `exitCutoffPercent`;
* the gap is the hysteresis band that stops a connection hovering at the
* cutoff from oscillating between rungs on consecutive requests. Default 5.
*/
reentryMinRemainingPercent?: number;
/** Max age of a `FreeAccessState.checkedAt` before it is treated as stale. */
maxStateAgeMs: number;
/**
* Whether a connection with no usable quota reading is admitted.
*
* - `auto/thrifty` passes TRUE: trying a plan-included connection costs
* nothing, and if it turns out to be exhausted the dispatcher's
* fall-through reaches the next rung anyway. Refusing to try it would
* send a request to a PAID rung on missing telemetry — the exact
* outcome the grouping exists to avoid.
* - `auto/subscription` passes FALSE: its promise is that no request can
* cost extra, and an unverifiable connection cannot support that promise.
*/
admitUnknownQuota: boolean;
/**
* Budget consumed so far on a paid rung, in USD, for the operator's current
* budget window. `null`/`undefined` means no spend accounting is available,
* in which case paid rungs are NOT budget-gated (they still order after
* every plan-included rung). See the spec's open question on the ledger.
*/
resolveRungSpendUsd?: (rung: LadderRung) => number | null;
/** Per-rung budget in USD. A rung mapped to 0 is disabled outright. */
rungBudgetUsd?: Partial<Record<LadderRung, number>>;
/** `now` injection for deterministic tests. */
now?: () => number;
/** Catalog override for tests; production callers never pass this. */
catalog?: readonly ConnectionBillingEntry[];
}
const DEFAULT_EXIT_CUTOFF_PERCENT = 2;
const DEFAULT_REENTRY_MIN_REMAINING_PERCENT = 5;
/**
* Which rung a specific (candidate, connection) pair belongs to.
*
* Billing class decides first because it is the fact that actually determines
* whether money moves; only a genuinely metered connection falls through to
* the model's economic tier. `unknown` billing is metered by definition
* (`connectionBilling.ts`), so an uncurated provider lands on a paid rung
* rather than silently joining the subscription rung.
*/
export function assignRung(
candidate: Pick<LadderCandidate, "provider" | "model">,
connection: BillableConnection,
options: Pick<LadderOptions, "resolveEconomicTier" | "catalog">
): LadderRung {
const verdict = classifyConnectionBilling(connection, options.catalog);
if (verdict.billing === "subscription") return "subscription";
if (verdict.billing === "keyless") return "keyless";
return options.resolveEconomicTier(candidate.provider, candidate.model);
}
/**
* Is this connection's plan allowance usable right now?
*
* `hasBeenExhausted` selects which side of the hysteresis band applies: a
* connection that is currently in play only has to stay above the exit cutoff,
* while one that already dropped out has to climb back above the (higher)
* re-entry threshold before it is admitted again.
*/
export function isQuotaUsable(
state: FreeAccessState | undefined,
options: Pick<
LadderOptions,
| "exitCutoffPercent"
| "reentryMinRemainingPercent"
| "maxStateAgeMs"
| "admitUnknownQuota"
| "now"
>,
hasBeenExhausted = false
): boolean {
if (!state) return options.admitUnknownQuota;
if (state.status === "EXHAUSTED") return false;
if (state.status === "UNKNOWN") return options.admitUnknownQuota;
const now = (options.now ?? Date.now)();
const checkedAtMs = Date.parse(state.checkedAt);
if (!Number.isFinite(checkedAtMs) || now - checkedAtMs > options.maxStateAgeMs) {
return options.admitUnknownQuota;
}
if (state.remainingFreeAllowance === null) return options.admitUnknownQuota;
const exitCutoff = options.exitCutoffPercent ?? DEFAULT_EXIT_CUTOFF_PERCENT;
const reentryFloor = Math.max(
options.reentryMinRemainingPercent ?? DEFAULT_REENTRY_MIN_REMAINING_PERCENT,
exitCutoff
);
const threshold = hasBeenExhausted ? reentryFloor : exitCutoff;
return state.remainingFreeAllowance > threshold;
}
/**
* Decision 3 — re-entry after a plan quota resets.
*
* A cached state whose own `resetAt` has already passed describes a window
* that no longer exists. Waiting out the cache TTL before re-reading it is
* pure lag on the single transition subscription-first routing cares most
* about, so such an entry is stale REGARDLESS of its age.
*
* Consumed by `freeAccessQuota.ts`, which owns the cache; kept here so the
* rule sits with the rest of the ladder's semantics and is testable without
* touching the cache.
*/
export function isStateStaleForReset(
state: Pick<FreeAccessState, "resetAt"> | undefined,
now: number = Date.now()
): boolean {
if (!state?.resetAt) return false;
const resetAtMs = Date.parse(state.resetAt);
if (!Number.isFinite(resetAtMs)) return false;
return resetAtMs <= now;
}
/**
* Decision 3, second half — never hold a plan-included connection in cooldown
* past the moment its own upstream says the quota is back.
*
* The exhausting 429 sets `rateLimitedUntil` from exponential backoff
* (`baseCooldownMs * 2 ** failureIndex`, `src/sse/services/auth.ts`), which for
* a subscription connection routinely overshoots the real reset — leaving
* routing stuck on paid rungs long after the plan refilled.
*
* This only ever NARROWS a cooldown, and only when the upstream itself
* supplied the reset instant. An absent, unparseable, or already-past
* `resetAt` returns the original cooldown untouched.
*/
export function clampCooldownToReset(
cooldownMs: number,
resetAt: string | null | undefined,
now: number = Date.now()
): number {
if (!resetAt) return cooldownMs;
const resetAtMs = Date.parse(resetAt);
if (!Number.isFinite(resetAtMs)) return cooldownMs;
const untilResetMs = resetAtMs - now;
if (untilResetMs <= 0) return cooldownMs;
return Math.min(cooldownMs, untilResetMs);
}
/** True when a paid rung has consumed its configured budget for the window. */
export function isRungBudgetExhausted(rung: LadderRung, options: LadderOptions): boolean {
const budget = options.rungBudgetUsd?.[rung];
if (budget === undefined) return false;
if (budget <= 0) return true; // explicitly disabled
const spent = options.resolveRungSpendUsd?.(rung);
if (spent === null || spent === undefined) return false; // no accounting → not gated
return spent >= budget;
}
/**
* Connections on a candidate that are usable right now, paired with the rung
* each one sits on. Quota-bearing rungs are verified per connection; paid
* rungs have nothing per-connection to verify (they are gated per rung).
*/
function evaluateConnections(
candidate: LadderCandidate,
options: LadderOptions,
accept: (rung: LadderRung) => boolean
): { rung: LadderRung; connectionIds: string[] } | null {
const connectionIds = candidate.connectionId
? [candidate.connectionId]
: (candidate.allowedConnectionIds ?? []);
if (connectionIds.length === 0) return null;
let bestRung: LadderRung | null = null;
const usable: string[] = [];
for (const connectionId of connectionIds) {
const rung = assignRung(
candidate,
{
provider: candidate.provider,
authType: options.resolveAuthType(connectionId),
connectionId,
},
options
);
if (!accept(rung)) continue;
if (QUOTA_BEARING_RUNGS.has(rung)) {
const state = options.resolveFreeAccessState(candidate.provider, connectionId);
if (!isQuotaUsable(state, options)) continue;
} else if (isRungBudgetExhausted(rung, options)) {
continue;
}
usable.push(connectionId);
// A candidate reachable through several accounts is represented by its
// CHEAPEST usable rung: that is the rung a request through it would
// actually land on once dispatch picks from the surviving allowlist.
if (bestRung === null || rungIndex(rung) < rungIndex(bestRung)) bestRung = rung;
}
if (usable.length === 0 || bestRung === null) return null;
return { rung: bestRung, connectionIds: usable };
}
/** Rewrite a candidate's connection allowlist to the verified subset, keeping
* the identity-when-nothing-changed contract the sibling filters hold. */
function withVerifiedConnections<T extends LadderCandidate>(
candidate: T,
connectionIds: string[]
): { candidate: T; changed: boolean } {
if (candidate.connectionId !== null) return { candidate, changed: false };
const original = candidate.allowedConnectionIds ?? [];
const isSameSet =
original.length === connectionIds.length && connectionIds.every((id) => original.includes(id));
if (isSameSet) return { candidate, changed: false };
return { candidate: { ...candidate, allowedConnectionIds: connectionIds }, changed: true };
}
/**
* `auto/subscription` — the strict grouping. Keeps only candidates servable by
* a plan-included connection whose overage is a documented hard stop, with
* live quota headroom verified per connection.
*
* Fails CLOSED in every ambiguous case: uncurated provider, unverifiable
* quota, or an overage that meters to paid. An empty result is the correct,
* intended answer for an operator who asked never to spend extra — the
* caller's existing empty-pool path handles it, exactly as `hidePaidModels`
* already does.
*/
export function filterSubscriptionOnlyCandidates<T extends LadderCandidate>(
pool: T[],
options: LadderOptions
): T[] {
if (!options.enabled) return pool;
const strictOptions: LadderOptions = { ...options, admitUnknownQuota: false };
const kept: T[] = [];
let changed = false;
for (const candidate of pool) {
const connectionIds = candidate.connectionId
? [candidate.connectionId]
: (candidate.allowedConnectionIds ?? []);
const safe = connectionIds.filter((connectionId) => {
const connection: BillableConnection = {
provider: candidate.provider,
authType: options.resolveAuthType(connectionId),
connectionId,
};
const verdict = classifyConnectionBilling(connection, options.catalog);
// `keyless` is plan-included in the ladder's sense but is NOT a
// subscription: this grouping is "the plan I pay for", so a no-auth
// backend does not belong in it.
if (verdict.billing !== "subscription") return false;
if (!isOverageSafe(verdict)) return false;
const state = strictOptions.resolveFreeAccessState(candidate.provider, connectionId);
return isQuotaUsable(state, strictOptions);
});
if (safe.length === 0) {
changed = true;
continue;
}
const result = withVerifiedConnections(candidate, safe);
if (result.changed) changed = true;
kept.push(result.candidate);
}
return changed ? kept : pool;
}
/**
* `auto/thrifty` — the escalating grouping. Returns the pool ordered by rung,
* with candidates whose every connection is exhausted (quota) or whose rung is
* budget-exhausted removed.
*
* Ordering only — the `auto` engine still scores WITHIN the surviving pool, so
* this decides which rungs are in play, not which candidate wins on one. The
* combo dispatcher already walks targets in order and falls through on
* failure, so a runtime exhaustion the preflight did not catch still escalates
* to the next rung inside the same request.
*
* Rung eligibility is recomputed from live state on every pool build and
* nothing is persisted: there is deliberately no sticky "currently on rung 3"
* record that could outlive a quota reset and wedge routing on paid rungs.
*/
export function orderPoolByRung<T extends LadderCandidate>(pool: T[], options: LadderOptions): T[] {
if (!options.enabled) return pool;
const ranked: Array<{ candidate: T; rung: LadderRung; order: number }> = [];
for (const [order, candidate] of pool.entries()) {
const evaluated = evaluateConnections(candidate, options, () => true);
if (!evaluated) continue;
const result = withVerifiedConnections(candidate, evaluated.connectionIds);
ranked.push({ candidate: result.candidate, rung: evaluated.rung, order });
}
ranked.sort((a, b) => {
const byRung = rungIndex(a.rung) - rungIndex(b.rung);
// Stable within a rung: preserve the pool's incoming order so the auto
// scorer's own ranking is not reshuffled by this overlay.
return byRung !== 0 ? byRung : a.order - b.order;
});
return ranked.map((entry) => entry.candidate);
}

View File

@@ -23,20 +23,7 @@ import { isVisionModelId } from "@/shared/constants/visionModels";
import { isVisionBridgeForcedModel } from "@/shared/constants/visionBridgeDefaults";
export type AutoCategory = "coding" | "reasoning" | "vision" | "chat" | "multimodal";
export type AutoTier =
| "fast"
| "cheap"
| "floor"
| "free"
| "reliable"
| "pro"
// Subscription-first routing. Unlike every tier above, these two narrow by
// the CONNECTION's billing class, not the model's price — so they are
// applied in `virtualFactory.ts` against live connection state rather than
// by `buildAutoCandidateFilter` below, which only sees (provider, model).
// See `subscriptionLadder.ts` and `docs/routing/SUBSCRIPTION_LADDER.md`.
| "subscription"
| "thrifty";
export type AutoTier = "fast" | "cheap" | "floor" | "free" | "reliable" | "pro";
export const AUTO_CATEGORIES: readonly AutoCategory[] = [
"coding",
@@ -52,8 +39,6 @@ export const AUTO_TIERS: readonly AutoTier[] = [
"free",
"reliable",
"pro",
"subscription",
"thrifty",
];
const CATEGORY_SET = new Set<string>(AUTO_CATEGORIES);
@@ -99,9 +84,6 @@ export function tierToWeightVariant(tier?: AutoTier): AutoVariant | "reliability
return "fast";
case "cheap":
case "floor":
// The ladder already orders plan-included rungs first; within a rung it
// should still lean cheap rather than reach for the most expensive model.
case "thrifty":
return "cheap";
case "reliable":
return "reliability";
@@ -136,7 +118,8 @@ export function buildAutoCandidateFilter(
}
try {
const caps = getResolvedModelCapabilities({ provider: c.provider, model: c.model });
const capable = caps.supportsVision === true || isVisionModelId(c.model);
const capable =
caps.supportsVision === true || isVisionModelId(c.model);
if (!capable) return false;
// #vison-pool: registry entries whose catalog OVERSTATES vision support
// (opencode-go/opencode-zen/tokenrouter — the backend models are text-only)

View File

@@ -28,11 +28,6 @@ import { buildFamilyCandidateFilter, type ModelFamily } from "./modelFamily";
import { getHiddenModelsByProvider } from "@/models";
import { getSyncedAvailableModelsByConnection, getCustomModels } from "@/lib/db/models";
import { filterPaidOnlyCandidates } from "./paidModelFilter";
import {
filterSubscriptionOnlyCandidates,
orderPoolByRung,
type LadderOptions,
} from "./subscriptionLadder";
import { filterStrictZeroCostCandidates, filterTosAvoidCandidates } from "./strictZeroCostFilter";
import { resolveFreeAccessState } from "./freeAccessQuota";
import { isModelExcludedByConnection } from "@/domain/connectionModelRules";
@@ -150,98 +145,6 @@ type VirtualAutoCombo = AutoComboConfig & {
export interface PreparedVirtualAutoComboInputs {
readonly regularCandidates: readonly VirtualAutoComboCandidate[];
readonly familyCandidates: readonly VirtualAutoComboCandidate[];
/**
* `provider_connections.auth_type` per connection id. Subscription-first
* routing classifies billing per CONNECTION (`connectionBilling.ts`), and
* the candidate pool only carries connection ids — so the auth types are
* captured here, during the one bulk connection read this function already
* does, instead of re-reading the DB per pool narrowing.
*/
readonly authTypeByConnectionId?: ReadonlyMap<string, string | null>;
/** Operator settings for the subscription ladder; absent = feature off. */
readonly subscriptionLadder?: SubscriptionLadderSettings;
}
/**
* Operator-facing knobs for subscription-first routing. Mirrors the Zod shape
* in `src/shared/validation/settingsSchemas.ts`.
*
* Deliberately TUNING ONLY — there is no `enabled` flag here. `auto/subscription`
* and `auto/thrifty` are new ids that nothing routes through unless a caller
* asks for them by name, so requesting the id IS the opt-in. A settings toggle
* that could switch them off would be actively dangerous: it would leave an id
* whose whole promise is "plan-included only" quietly serving the full pool,
* paid models included.
*/
export interface SubscriptionLadderSettings {
exitCutoffPercent?: number;
reentryMinRemainingPercent?: number;
rungBudgetUsd?: Record<string, number>;
/** Staleness bound for a cached quota reading, derived from the existing
* `autoRefreshProviderQuotaInterval` exactly as STRICT_ZERO_COST does. */
maxStateAgeMs: number;
}
function readSubscriptionLadderSettings(
settings: Record<string, unknown>
): SubscriptionLadderSettings {
const maxStateAgeMs = (Number(settings.autoRefreshProviderQuotaInterval) || 180) * 1000;
const raw = settings.subscriptionLadder;
if (!raw || typeof raw !== "object") return { maxStateAgeMs };
const value = raw as Record<string, unknown>;
const numeric = (key: string): number | undefined =>
typeof value[key] === "number" && Number.isFinite(value[key] as number)
? (value[key] as number)
: undefined;
const exitCutoffPercent = numeric("exitCutoffPercent");
const reentryMinRemainingPercent = numeric("reentryMinRemainingPercent");
return {
maxStateAgeMs,
...(exitCutoffPercent === undefined ? {} : { exitCutoffPercent }),
...(reentryMinRemainingPercent === undefined ? {} : { reentryMinRemainingPercent }),
...(value.rungBudgetUsd && typeof value.rungBudgetUsd === "object"
? { rungBudgetUsd: value.rungBudgetUsd as Record<string, number> }
: {}),
};
}
/**
* Build the injected dependencies the pure ladder module needs. Everything it
* touches is resolved here — the live quota cache, connection auth types, and
* the economic tier resolver — so `subscriptionLadder.ts` itself stays free of
* DB, network, and registry imports.
*/
function buildLadderOptions(
prepared: PreparedVirtualAutoComboInputs,
tier: "subscription" | "thrifty"
): LadderOptions {
const tuning = prepared.subscriptionLadder;
const authTypes = prepared.authTypeByConnectionId;
return {
enabled: true,
resolveFreeAccessState,
resolveAuthType: (connectionId: string) => authTypes?.get(connectionId) ?? null,
resolveEconomicTier: (provider: string, model: string) => {
try {
const resolved = classifyTier(provider, model).tier;
return resolved === "free" || resolved === "premium" ? resolved : "cheap";
} catch {
// Same conservative default `safeClassifyTier` uses in suffixComposition.
return "cheap";
}
},
maxStateAgeMs: tuning?.maxStateAgeMs ?? 180_000,
// The two groupings read a missing quota reading OPPOSITELY on purpose —
// see `LadderOptions.admitUnknownQuota`.
admitUnknownQuota: tier === "thrifty",
...(tuning?.exitCutoffPercent === undefined
? {}
: { exitCutoffPercent: tuning.exitCutoffPercent }),
...(tuning?.reentryMinRemainingPercent === undefined
? {}
: { reentryMinRemainingPercent: tuning.reentryMinRemainingPercent }),
...(tuning?.rungBudgetUsd ? { rungBudgetUsd: tuning.rungBudgetUsd } : {}),
};
}
function toExpiryMs(value: unknown): number | null {
@@ -753,15 +656,8 @@ export async function prepareVirtualAutoComboInputs(
const regularCandidates = buildPreparedPool(false);
// #6453/#8183: family selectors bypass the reliability-curated no-auth allowlist.
const familyCandidates = buildPreparedPool(true);
// Subscription-first routing inputs, captured from the connection read above
// so no later stage has to touch the DB again.
const authTypeByConnectionId = new Map<string, string | null>();
for (const conn of connections) {
authTypeByConnectionId.set(conn.id, typeof conn.authType === "string" ? conn.authType : null);
}
const subscriptionLadder = readSubscriptionLadderSettings(settings);
if (!options.includeResolvedCapabilities) {
return { regularCandidates, familyCandidates, authTypeByConnectionId, subscriptionLadder };
return { regularCandidates, familyCandidates };
}
// One uninterrupted bulk read of all three capability tables for this prepare only.
@@ -775,8 +671,6 @@ export async function prepareVirtualAutoComboInputs(
return {
regularCandidates: await attachPreparedCapabilityValues(regularCandidates, capabilityState),
familyCandidates: await attachPreparedCapabilityValues(familyCandidates, capabilityState),
authTypeByConnectionId,
subscriptionLadder,
};
}
@@ -948,32 +842,6 @@ export async function createVirtualAutoComboFromPrepared(
}
}
// Subscription-first routing (`auto/subscription`, `auto/thrifty`). Applied
// AFTER the category/tier narrowing above because, unlike every other tier,
// these two select on the connection's billing class and its live quota
// state rather than on the model's catalog price — see
// `subscriptionLadder.ts` and `docs/routing/SUBSCRIPTION_LADDER.md`.
if (spec?.tier === "subscription" || spec?.tier === "thrifty") {
const ladderOptions = buildLadderOptions(prepared, spec.tier);
const beforeCount = effectivePool.length;
effectivePool =
spec.tier === "subscription"
? filterSubscriptionOnlyCandidates(effectivePool, ladderOptions)
: orderPoolByRung(effectivePool, ladderOptions);
if (spec.tier === "subscription" && effectivePool.length === 0 && beforeCount > 0) {
// Intended, not a defect: the operator asked for plan-included capacity
// only, and right now there is none with verified headroom. Failing
// closed here is the entire promise of the id — the caller's existing
// empty-pool path turns it into a clear error rather than a silent,
// billable fallback.
warnEmptyAutoPoolOnce(
"auto/subscription",
"auto/subscription: no plan-included connection has verified quota headroom; " +
"returning an empty pool rather than falling back to paid capacity."
);
}
}
let weights: ScoringWeights = { ...DEFAULT_WEIGHTS };
let explorationRate = 0.05; // Default exploration rate
let routerStrategy = "lkgp"; // All auto variants use LKGP

View File

@@ -926,9 +926,6 @@ async function handleComboChatInner({
if (activeNativeTurnPin) {
orderedTargets = applyNativeCodexTurnPin(orderedTargets, activeNativeTurnPin);
if (orderedTargets.length === 0) {
// #11371: quota-share ordering already reserved a winner slot; release it on
// this early exit (idempotent).
targetResolution.quotaShareRelease?.();
return errorResponse(
409,
"The pinned native Codex turn target is no longer available; the turn cannot be moved to another provider"
@@ -956,8 +953,6 @@ async function handleComboChatInner({
// no-target failures (silent-stop fix). Threshold of 3 prevents a one-off account
// wipe from destroying the prompt-cache pin benefit on the next request.
recordComboFailure(effectiveSessionId, combo.name);
// #11371: same early-exit release as the pinned-turn path above.
targetResolution.quotaShareRelease?.();
return errorResponseWithComboDiagnostics(
404,
"Combo has no executable targets",
@@ -2846,9 +2841,6 @@ async function handleComboChatInner({
return await dispatchWithCooldownRetry();
} finally {
quotaShareConcurrencyRelease?.();
// #11371: release the in-flight slot quota-share ordering reserved for its
// winner — the counter must not leak monotonically upward across requests.
targetResolution.quotaShareRelease?.();
// G2: Clean up candidate registry to prevent unbounded memory growth.
_unregisterExecutionCandidates(_registeredExecutionKeys);
}

View File

@@ -20,21 +20,6 @@ import {
} from "./targetSorters.ts";
import type { ComboLike, ComboLogger, ResolvedComboTarget } from "./types.ts";
/**
* Result of {@link applyStrategyOrdering}.
*
* `quotaShareRelease` carries the idempotent release for the in-flight slot that
* quota-share ordering reserves for its winner (#11371). It is non-null only when
* the `quota-share` strategy ran; every other strategy leaves it null. The caller
* MUST invoke it exactly once when the request settles — selection reserves the
* slot, so dropping the callback leaks the counter monotonically upward and
* degenerates P2C into "fewest lifetime dispatches".
*/
export interface ApplyStrategyOrderingResult {
orderedTargets: ResolvedComboTarget[];
quotaShareRelease: (() => void) | null;
}
export interface ApplyStrategyOrderingDeps {
combo: ComboLike;
config: Record<string, unknown>;
@@ -60,10 +45,9 @@ export async function applyStrategyOrdering(
strategy: string,
initialOrderedTargets: ResolvedComboTarget[],
deps: ApplyStrategyOrderingDeps
): Promise<ApplyStrategyOrderingResult> {
): Promise<ResolvedComboTarget[]> {
const { combo, config, body, log, apiKeyAllowedConnections, sessionKey } = deps;
let orderedTargets = initialOrderedTargets;
let quotaShareRelease: (() => void) | null = null;
if (strategy === "lkgp") {
try {
@@ -245,18 +229,14 @@ export async function applyStrategyOrdering(
const qsModel =
typeof body?.model === "string" ? body.model : (orderedTargets[0]?.modelStr ?? "");
const qsMaxConcurrent = await resolveMaxConcurrentByConnection(orderedTargets);
const qsSelection = selectQuotaShareTarget(orderedTargets, combo.name, qsModel, Date.now(), {
orderedTargets = selectQuotaShareTarget(orderedTargets, combo.name, qsModel, Date.now(), {
maxConcurrentByConnection: qsMaxConcurrent,
});
orderedTargets = qsSelection.orderedTargets;
// #11371: the reservation made inside selectQuotaShareTarget must outlive this
// call — hand the release to the host so it can fire it when the request settles.
quotaShareRelease = qsSelection.decrementInflight;
}).orderedTargets;
log.info(
"COMBO",
`Quota-share ordering: ${orderedTargets[0]?.modelStr}${orderedTargets[0]?.connectionId ? ` (${orderedTargets[0].connectionId})` : ""} selected (DRR+P2C)`
);
}
return { orderedTargets, quotaShareRelease };
return orderedTargets;
}

View File

@@ -261,7 +261,7 @@ function getWindowsMapQuotaWindow(
);
}
function resolveQuotaWindowByName(
export function resolveQuotaWindowByName(
quota: unknown,
windowName: ResetWindowName
): QuotaWindowSnapshot | null {
@@ -321,8 +321,8 @@ export function scoreResetAwareQuota(
if (quota.limitReached === true) return { score: -Infinity };
const overallPercentUsed = clamp01(finiteNumberOrNull(quota.percentUsed) ?? 0.5);
const sessionWindow = getQuotaWindow(quota, "window5h");
const weeklyWindow = getQuotaWindow(quota, "window7d") || getQuotaWindow(quota, "windowWeekly");
const sessionWindow = resolveQuotaWindowByName(quota, "session");
const weeklyWindow = resolveQuotaWindowByName(quota, "weekly");
const sessionRemaining = clamp01(1 - (sessionWindow?.percentUsed ?? overallPercentUsed));
const weeklyRemaining = clamp01(1 - (weeklyWindow?.percentUsed ?? overallPercentUsed));
const sessionScore = scoreQuotaWindow(

View File

@@ -122,13 +122,6 @@ export interface ResolvedComboTargetPipeline {
/** Session-stickiness result — the attempt loop reads `.messageHash` on success/failure. */
sticky: ApplyStickinessResult;
preScreenMap: Map<string, PreScreenResult>;
/**
* Idempotent release for the in-flight slot quota-share ordering reserved for
* its winner (#11371). Null unless the `quota-share` strategy ran. The host MUST
* invoke it when the request settles; this pipeline already releases it on any
* earlyResponse it produces after selection.
*/
quotaShareRelease: (() => void) | null;
}
export type ResolveComboTargetPipelineResult =
@@ -401,11 +394,7 @@ async function orderByStrategy(
initialOrderedTargets: ResolvedComboTarget[]
): Promise<
| { earlyResponse: Response }
| {
orderedTargets: ResolvedComboTarget[];
autoUsedExplicitRouter: boolean;
quotaShareRelease: (() => void) | null;
}
| { orderedTargets: ResolvedComboTarget[]; autoUsedExplicitRouter: boolean }
> {
const { strategy, body, combo, settings, config, log } = deps;
if (strategy === "auto") {
@@ -424,22 +413,17 @@ async function orderByStrategy(
return {
orderedTargets: autoResult.orderedTargets,
autoUsedExplicitRouter: autoResult.autoUsedExplicitRouter,
quotaShareRelease: null,
};
}
const { orderedTargets, quotaShareRelease } = await applyStrategyOrdering(
strategy,
initialOrderedTargets,
{
combo,
config,
body,
log,
apiKeyAllowedConnections: deps.apiKeyAllowedConnections,
sessionKey: deps.relayOptions?.sessionId,
}
);
return { orderedTargets, autoUsedExplicitRouter: false, quotaShareRelease };
const orderedTargets = await applyStrategyOrdering(strategy, initialOrderedTargets, {
combo,
config,
body,
log,
apiKeyAllowedConnections: deps.apiKeyAllowedConnections,
sessionKey: deps.relayOptions?.sessionId,
});
return { orderedTargets, autoUsedExplicitRouter: false };
}
/**
@@ -730,15 +714,10 @@ export async function resolveComboTargetPipeline(
const ordering = await orderByStrategy(deps, orderedTargets);
if ("earlyResponse" in ordering) return ordering;
const { autoUsedExplicitRouter, quotaShareRelease } = ordering;
const { autoUsedExplicitRouter } = ordering;
const continuity = await applyContinuityFilters(deps, ordering.orderedTargets);
if ("earlyResponse" in continuity) {
// #11371: selection already reserved the winner's in-flight slot; a hard
// filter exhausting the pool must not leak it.
quotaShareRelease?.();
return continuity;
}
if ("earlyResponse" in continuity) return continuity;
orderedTargets = applyTaskAwareOrdering(deps, continuity.orderedTargets, autoUsedExplicitRouter);
orderedTargets = await applyPromptCacheStage(
deps,
@@ -762,6 +741,5 @@ export async function resolveComboTargetPipeline(
getWeightedStepKeyForTarget,
sticky: continuity.sticky,
preScreenMap,
quotaShareRelease,
};
}

View File

@@ -16,9 +16,10 @@ export function createExecutorModelClient(
credentials: ProviderCredentials,
costPerKTokenOut?: number
): ModelClient {
const executor = getExecutor(provider);
return {
async complete(model: string, messages: ChatTurn[]): Promise<ModelCallResult> {
// #11220: getExecutor is async (lazy registry) — resolve per call.
const executor = await getExecutor(provider);
const body = { model, messages, stream: false };
const input: ExecuteInput = {
model,

View File

@@ -150,16 +150,65 @@ export function convertUsageToQuotaInfo(usage: unknown): QuotaInfo | null {
if (Object.keys(windows).length === 0) return null;
const normalized = normalizeQuotaWindows(windows);
Object.assign(windows, normalized);
return {
used: 0,
total: 0,
percentUsed: worstPercent,
resetAt: worstResetAt,
windows,
...normalized,
limitReached: worstPercent >= 1 - 1e-9,
};
}
/**
* Map provider-native window keys to canonical structural windows so that
* reset-aware / reset-window scoring works without knowing every provider's
* naming convention.
*
* - Claude: "session (5h)" → window5h, "weekly (7d)" → window7d
* - Antigravity: worst per-model quota → window5h; worst *_weekly quota → window7d
*/
function normalizeQuotaWindows(
windows: Record<string, { percentUsed: number; resetAt: string | null }>
): Record<string, { percentUsed: number; resetAt: string | null }> {
const normalized: Record<string, { percentUsed: number; resetAt: string | null }> = {};
// Claude-style explicit time windows.
if (windows["session (5h)"] && !normalized.window5h) {
normalized.window5h = windows["session (5h)"];
}
if (windows["weekly (7d)"] && !normalized.window7d) {
normalized.window7d = windows["weekly (7d)"];
}
// Antigravity-style per-model 5h windows: pick the worst (most used) model quota.
const modelWindows = Object.entries(windows).filter(
([key]) =>
key !== "credits" &&
!key.endsWith("_weekly") &&
!key.startsWith("window") &&
!key.includes("(5h)") &&
!key.includes("(7d)")
);
if (modelWindows.length > 0 && !normalized.window5h) {
const worst = modelWindows.reduce((a, b) => (a[1].percentUsed > b[1].percentUsed ? a : b));
normalized.window5h = worst[1];
}
// Antigravity-style weekly family buckets: pick the worst *_weekly quota.
const weeklyWindows = Object.entries(windows).filter(([key]) => key.endsWith("_weekly"));
if (weeklyWindows.length > 0 && !normalized.window7d) {
const worst = weeklyWindows.reduce((a, b) => (a[1].percentUsed > b[1].percentUsed ? a : b));
normalized.window7d = worst[1];
}
return normalized;
}
/**
* Fetch quota for a connection by delegating to the appropriate
* provider-specific usage fetcher and reshaping its output into the

View File

@@ -48,6 +48,16 @@ export interface QuotaInfo {
* (e.g. "session", "weekly", "monthly").
*/
windows?: Record<string, QuotaWindowInfo>;
/**
* Structural, canonical window snapshots used by reset-aware / reset-window
* scoring. Providers that expose time-based windows (5h, weekly, monthly)
* populate these in addition to the provider-native `windows` map so the
* scorer does not need to know every provider's key naming convention.
*/
window5h?: QuotaWindowInfo;
window7d?: QuotaWindowInfo;
windowWeekly?: QuotaWindowInfo;
windowMonthly?: QuotaWindowInfo;
/** True when the upstream usage endpoint explicitly reports exhausted quota. */
limitReached?: boolean;
}

View File

@@ -96,13 +96,6 @@ const ARK_CONSOLE_URL =
/** Cookie names required for a valid console session (mirrors tokenExtractionConfig) */
const REQUIRED_COOKIES = ["digest", "AccountID", "csrfToken", "userInfo"] as const;
/** Exact-domain match for session cookies — substring checks would also accept
* look-alike hosts (e.g. `volcengine.com.evil.test`). Playwright may report the
* domain with or without a leading dot. */
function isVolcengineCookieDomain(domain: string): boolean {
return domain === "volcengine.com" || domain.endsWith(".volcengine.com");
}
const DEFAULT_SESSION_TIMEOUT = 300_000;
const SUBMIT_COOKIE_TIMEOUT = 90_000;
const CAPTURE_POLL_INTERVAL = 1_000;
@@ -625,7 +618,7 @@ export class VolcengineConsoleAutoLoginService {
for (const cookie of cookies as Array<{ name: string; domain: string; value: string }>) {
if (
REQUIRED_COOKIES.includes(cookie.name as (typeof REQUIRED_COOKIES)[number]) &&
isVolcengineCookieDomain(cookie.domain)
cookie.domain.includes("volcengine.com")
) {
credentials[cookie.name] = cookie.value;
}
@@ -770,7 +763,7 @@ export class VolcengineConsoleAutoLoginService {
domain: string;
}>;
const present = REQUIRED_COOKIES.filter((name) =>
cookies.some((c) => c.name === name && isVolcengineCookieDomain(c.domain))
cookies.some((c) => c.name === name && c.domain.includes("volcengine.com"))
);
parts.push(
`cookies=[${present.join(",") || "none of digest/AccountID/csrfToken/userInfo"}]`

2436
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
{
"name": "omniroute",
"version": "3.8.51",
"description": "Unified AI router with 353 providers, RTK+Caveman compression, auto fallback, MCP/A2A, desktop, PWA, and OpenAI-compatible APIs.",
"description": "Unified AI router with 350 providers, RTK+Caveman compression, auto fallback, MCP/A2A, desktop, PWA, and OpenAI-compatible APIs.",
"type": "module",
"bin": {
"omniroute": "bin/omniroute.mjs",
@@ -261,7 +261,7 @@
"alibaba:sync-allowlist": "node --import tsx/esm scripts/ops/sync-alibaba-allowlist.mjs"
},
"dependencies": {
"@aws-sdk/client-bedrock-runtime": "^3.1112.0",
"@aws-sdk/client-bedrock-runtime": "^3.1116.0",
"@dnd-kit/core": "^6.3.1",
"@dnd-kit/sortable": "^10.0.0",
"@dnd-kit/utilities": "^3.2.2",
@@ -280,29 +280,29 @@
"commander": "^15.0.0",
"cron-parser": "^5.10.0",
"csv-stringify": "^6.8.3",
"dompurify": "^3.4.13",
"dompurify": "^3.4.14",
"express": "^5.2.1",
"fetch-socks": "^1.3.3",
"fflate": "^0.8.3",
"fumadocs-core": "^16.14.4",
"fumadocs-ui": "^16.14.4",
"fumadocs-core": "^16.15.1",
"fumadocs-ui": "^16.15.1",
"http-proxy-middleware": "^4.0.0",
"https-proxy-agent": "^9.0.0",
"ink": "^7.0.3",
"ink-spinner": "^5.0.0",
"ink-text-input": "^6.0.0",
"ioredis": "^5.10.1",
"jose": "^6.2.9",
"jose": "^6.2.10",
"js-yaml": "^5.3.0",
"jsonc-parser": "^3.3.1",
"lowdb": "^7.0.1",
"lucide-react": "^1.21.0",
"marked": "^18.0.9",
"lucide-react": "^1.33.0",
"marked": "^18.0.10",
"marked-terminal": "^7.3.0",
"material-symbols": "^0.46.0",
"mermaid": "^11.15.0",
"mermaid": "^11.17.0",
"monaco-editor": "^0.56.0",
"next": "16.3.1",
"next": "16.3.2",
"next-intl": "^4.13.7",
"next-themes": "^0.4.6",
"node-machine-id": "^1.1.12",
@@ -332,7 +332,7 @@
"turndown-plugin-gfm": "1.0.2",
"undici": "^8.10.0",
"update-notifier": "^7.3.1",
"uuid": "^14.0.0",
"uuid": "^14.0.2",
"ws": "^8.21.3",
"xxhash-wasm": "^1.1.0",
"yazl": "^3.3.1",
@@ -348,11 +348,12 @@
"onnxruntime-node": "1.24.3",
"sqlite-vec": "^0.1.9",
"tls-client-node": "^0.2.0",
"wreq-js": "^3.0.0"
"wreq-js": "^3.1.0"
},
"devDependencies": {
"@axe-core/playwright": "^4.13.0",
"@cyclonedx/cyclonedx-npm": "6.0.1",
"@eslint/compat": "^2.1.0",
"@playwright/test": "^1.62.1",
"@size-limit/file": "^13.0.3",
"@stryker-mutator/core": "^10.0.0",
@@ -363,24 +364,25 @@
"@testing-library/react": "^16.3.2",
"@testing-library/user-event": "^14.6.6",
"@types/better-sqlite3": "^9.6.0",
"@types/bun": "latest",
"@types/bun": "1.4.0",
"@types/node": "^26.2.0",
"@types/react": "^19.2.18",
"@types/react-dom": "^19.2.4",
"@types/safe-regex": "^1.1.6",
"@types/ws": "^8.18.0",
"@vitejs/plugin-react": "^6.0.5",
"bun": "1.3.14",
"@vitejs/plugin-react": "^6.1.0",
"bun": "1.4.0",
"c8": "^12.0.0",
"concurrently": "^10.0.5",
"cross-env": "^10.1.0",
"ctrf": "^0.3.0",
"dpdm": "^4.3.0",
"eslint": "^9.39.4",
"eslint-config-next": "16.3.1",
"eslint": "^10.9.0",
"eslint-config-next": "16.3.2",
"eslint-plugin-sonarjs": "^4.1.0",
"espree": "^11.2.0",
"fast-check": "^4.8.0",
"fumadocs-mdx": "^15.2.3",
"fumadocs-mdx": "^15.3.1",
"glob": "^13.0.6",
"httpyac": "^6.16.7",
"husky": "^9.1.7",
@@ -392,7 +394,7 @@
"lint-staged": "^17.3.0",
"lockfile-lint": "^5.0.1",
"node-loader": "^2.1.0",
"opencode-ai": "1.18.18",
"opencode-ai": "1.18.21",
"playwright-ctrf-json-reporter": "^0.0.29",
"prettier": "^3.9.6",
"promptfoo": "^0.122.0",
@@ -401,7 +403,7 @@
"type-coverage": "^2.30.1",
"typescript": "^6.0.3",
"typescript-eslint": "^8.67.0",
"vitest": "^4.1.7",
"vitest": "^4.1.11",
"wait-on": "^9.1.0",
"wtfnode": "^0.10.1"
},
@@ -435,12 +437,13 @@
},
"overrides": {
"onnxruntime-node": "1.24.3",
"eslint-plugin-react-hooks": "7.0.1",
"fast-xml-parser": "^5.10.1",
"sharp": "^0.35.3",
"postcss": "^8.5.18",
"ip-address": "^10.3.1",
"qs": "^6.15.2",
"uuid": "^14.0.0",
"uuid": "^14.0.2",
"form-data": "^4.0.6",
"vite": "^8.0.16",
"protobufjs": "^7.6.5",
@@ -468,10 +471,6 @@
},
"socket.io-parser": "^4.2.7",
"tar": "^7.5.21",
"brace-expansion": "^5.0.9",
"minimatch": {
"brace-expansion": "^1.1.18"
},
"libxmljs2": {
"minimatch": {
"brace-expansion": "^2.1.4"
@@ -496,7 +495,7 @@
},
"nanoid": "^3.3.17",
"monaco-editor": {
"dompurify": "^3.4.13"
"dompurify": "^3.4.14"
}
}
}

View File

@@ -0,0 +1,34 @@
import { cpSync, existsSync, readdirSync, rmSync } from "node:fs";
import { join } from "node:path";
// electron-builder >=26 injects an "!**/node_modules/**" ignore into every
// extraResources/extraFiles pattern list (app-builder-lib/out/fileMatcher.js),
// and that ignore cannot be overridden by any later positive filter pattern —
// verified empirically with a minimal fixture on 26.15.3. The standalone
// server resolves better-sqlite3 (and other runtime deps) from the *primary*
// node_modules at resources/app/node_modules (see
// prepare-electron-standalone.mjs: "Verify better-sqlite3 Node-API prebuilds in
// the primary node_modules"), so without this hook the packaged desktop app silently loses its native
// SQLite driver and falls back to sql.js — the exact regression guarded by
// issue #7592's cold-restart smoke check.
export default async function afterPack(context) {
const stagingNodeModules = join(
context.packager.projectDir,
"..",
".build",
"electron-standalone",
"node_modules"
);
const destNodeModules = join(context.appOutDir, "resources", "app", "node_modules");
if (!existsSync(stagingNodeModules)) {
console.warn(`[afterpack] no staged node_modules at ${stagingNodeModules} — skipping restore`);
return;
}
rmSync(destNodeModules, { recursive: true, force: true });
cpSync(stagingNodeModules, destNodeModules, { recursive: true });
console.log(
`[afterpack] restored ${readdirSync(destNodeModules).length} runtime module(s) into resources/app/node_modules`
);
}

View File

@@ -89,16 +89,20 @@ function moveTree(src, dest) {
return { removedFiles: files, removedBytes: bytes };
}
function tarPack(packOutDir, tarballPath) {
export function tarPack(packOutDir, tarballPath) {
// bsdtar ships with macOS, Linux images, and Windows runners (System32\tar.exe).
// GNU tar (common in Git-for-Windows environments) treats `C:\...` in `-f` as a
// remote rsh target ("Cannot connect to C:"), so always pass a bare filename
// and point cwd at the tarball directory instead.
const result = spawnSync(
process.platform === "win32" ? "tar.exe" : "tar",
["-czf", tarballPath, "-C", packOutDir, "node_modules"],
{ stdio: "pipe" }
["-czf", path.basename(tarballPath), "-C", packOutDir, "node_modules"],
{ stdio: "pipe", cwd: path.dirname(tarballPath) }
);
if (result.status !== 0) {
throw new Error(
`optional-pack tar failed for ${path.basename(tarballPath)} (exit ${result.status})`
`optional-pack tar failed for ${path.basename(tarballPath)} (exit ${result.status})` +
`: ${(result.stderr || result.stdout || "").toString().slice(-600)}`
);
}
}

View File

@@ -360,7 +360,7 @@ function isInsideDir(parentDir, candidateDir) {
return candidate === parent || candidate.startsWith(parent + sep);
}
async function ensureSmokeEnvDirs(smokeEnv, dataDir) {
export async function ensureSmokeEnvDirs(smokeEnv, dataDir) {
const dirNames = [
"DATA_DIR",
"HOME",
@@ -388,6 +388,16 @@ async function ensureSmokeEnvDirs(smokeEnv, dataDir) {
dirs.push(join(smokeEnv.APPDATA, subdir));
}
}
// Electron resolves the Roaming profile from %USERPROFILE%\AppData\Roaming
// (USERPROFILE takes precedence over the APPDATA env var) and the path
// service throws — rather than creates — when that directory is missing,
// which makes requestSingleInstanceLock() return false and the app exit(0)
// before app.whenReady(). Pre-create the derived tree as well.
if (platform() === "win32" && smokeEnv.USERPROFILE) {
for (const subdir of ["omniroute-desktop", "OmniRoute", "omniroute"]) {
dirs.push(join(smokeEnv.USERPROFILE, "AppData", "Roaming", subdir));
}
}
await Promise.all(dirs.map((dir) => mkdir(dir, { recursive: true })));
}
@@ -506,7 +516,14 @@ async function waitForReady({ logs, smokeUrl, timeoutMs, settleMs, exitState })
* by the single-launch path and the cold-restart (two-launch) path so both
* exercise identical spawn/readiness/shutdown behavior.
*/
async function launchAndCollectLogs({ appExecutable, smokeUrl, dataDir, timeoutMs, settleMs, streamLogs }) {
async function launchAndCollectLogs({
appExecutable,
smokeUrl,
dataDir,
timeoutMs,
settleMs,
streamLogs,
}) {
const smokeEnv = buildSmokeEnv({ dataDir });
await assertPortIsFree(smokeUrl);
await ensureSmokeEnvDirs(smokeEnv, dataDir);
@@ -568,7 +585,14 @@ async function main() {
!process.env.ELECTRON_SMOKE_DATA_DIR && process.env.ELECTRON_SMOKE_KEEP_DATA !== "1";
try {
await launchAndCollectLogs({ appExecutable, smokeUrl, dataDir, timeoutMs, settleMs, streamLogs });
await launchAndCollectLogs({
appExecutable,
smokeUrl,
dataDir,
timeoutMs,
settleMs,
streamLogs,
});
if (!coldRestart) return;

View File

@@ -89,22 +89,46 @@ export default function EmbeddingSourceSelector({ settings, providers, onSave, s
{t("embedding.noRemoteProviders")}
</p>
) : (
<select
value={currentProviderModel}
onChange={(e) => handleProviderModelChange(e.target.value)}
disabled={saving}
data-testid="embedding-provider-model-select"
className="w-full px-3 py-2 rounded-lg bg-background border border-border text-sm focus:outline-none focus:ring-1 focus:ring-violet-500"
>
<option value="">{t("embedding.selectProviderModel")}</option>
{remoteProviders.map((p) =>
p.models.map((m) => (
<option key={m.id} value={m.id}>
{m.name} ({m.dimensions ? `${m.dimensions}d` : "?"})
</option>
))
)}
</select>
<>
<select
value={
remoteProviders.some((p) => p.models.some((m) => m.id === currentProviderModel))
? currentProviderModel
: ""
}
onChange={(e) => handleProviderModelChange(e.target.value)}
disabled={saving}
data-testid="embedding-provider-model-select"
className="w-full px-3 py-2 rounded-lg bg-background border border-border text-sm focus:outline-none focus:ring-1 focus:ring-violet-500"
>
<option value="">{t("embedding.selectProviderModel")}</option>
{remoteProviders.map((p) =>
p.models.length > 0 ? (
p.models.map((m) => (
<option key={m.id} value={m.id}>
{m.name} ({m.dimensions ? `${m.dimensions}d` : "?"})
</option>
))
) : (
<optgroup key={p.provider} label={p.provider}>
<option value="">{`${p.provider} (no curated models)`}</option>
</optgroup>
)
)}
</select>
{/* Free-text override: the runtime accepts any configured provider's
OpenAI-compatible model id, including ones without a curated
registry entry (e.g. groq/, mistral/, cf/...). */}
<input
type="text"
value={currentProviderModel}
onChange={(e) => handleProviderModelChange(e.target.value)}
disabled={saving}
placeholder="provider/model — e.g. mistral/mistral-embed"
data-testid="embedding-provider-model-input"
className="w-full mt-2 px-3 py-2 rounded-lg bg-background border border-border text-sm font-mono focus:outline-none focus:ring-1 focus:ring-violet-500"
/>
</>
)}
<CustomEmbeddingEndpointFields settings={settings} onSave={onSave} saving={saving} />
</div>

View File

@@ -43,11 +43,7 @@ export default function RerankConfigCard({ settings, providers, onSave, saving }
}}
disabled={saving || (!rerankEnabled && !hasProvider)}
aria-disabled={saving || (!rerankEnabled && !hasProvider)}
title={
!rerankEnabled && !hasProvider
? t("rerank.noProviderWithKey")
: undefined
}
title={!rerankEnabled && !hasProvider ? t("rerank.noProviderWithKey") : undefined}
role="switch"
aria-checked={rerankEnabled}
className={`relative w-11 h-6 rounded-full transition-colors shrink-0 disabled:opacity-50 disabled:cursor-not-allowed ${
@@ -83,22 +79,45 @@ export default function RerankConfigCard({ settings, providers, onSave, saving }
{t("rerank.noProviderWithKey")}
</p>
) : (
<select
value={rerankProviderModel}
onChange={(e) => handleProviderModelChange(e.target.value)}
disabled={saving}
data-testid="rerank-provider-model-select"
className="w-full px-3 py-2 rounded-lg bg-background border border-border text-sm focus:outline-none focus:ring-1 focus:ring-violet-500"
>
<option value="">{t("rerank.selectProviderModel")}</option>
{rerankProviders.map((p) =>
p.models.map((m) => (
<option key={m.id} value={m.id}>
{m.name}
</option>
)),
)}
</select>
<>
<select
value={
rerankProviders.some((p) => p.models.some((m) => m.id === rerankProviderModel))
? rerankProviderModel
: ""
}
onChange={(e) => handleProviderModelChange(e.target.value)}
disabled={saving}
data-testid="rerank-provider-model-select"
className="w-full px-3 py-2 rounded-lg bg-background border border-border text-sm focus:outline-none focus:ring-1 focus:ring-violet-500"
>
<option value="">{t("rerank.selectProviderModel")}</option>
{rerankProviders.map((p) =>
p.models.length > 0 ? (
p.models.map((m) => (
<option key={m.id} value={m.id}>
{m.name}
</option>
))
) : (
<optgroup key={p.provider} label={p.provider}>
<option value="">{`${p.provider} (no curated models)`}</option>
</optgroup>
)
)}
</select>
{/* Free-text override: any configured provider's Cohere-compatible
model id is accepted by the runtime even without a curated entry. */}
<input
type="text"
value={rerankProviderModel}
onChange={(e) => handleProviderModelChange(e.target.value)}
disabled={saving}
placeholder="provider/model — e.g. groq/my-reranker"
data-testid="rerank-provider-model-input"
className="w-full mt-2 px-3 py-2 rounded-lg bg-background border border-border text-sm font-mono focus:outline-none focus:ring-1 focus:ring-violet-500"
/>
</>
)}
</div>
</>

View File

@@ -16,6 +16,7 @@ export default function EngineTab() {
const { status, isLoading: statusLoading } = useEngineStatus();
const { settings, save: saveSettings, isLoading: settingsLoading } = useMemorySettings();
const [providers, setProviders] = useState<EmbeddingProviderListing[]>([]);
const [rerankProviders, setRerankProviders] = useState<EmbeddingProviderListing[]>([]);
const [saving, setSaving] = useState(false);
const [reindexing, setReindexing] = useState(false);
const [reindexMsg, setReindexMsg] = useState("");
@@ -32,6 +33,14 @@ export default function EngineTab() {
if (!cancelled && data?.providers) setProviders(data.providers);
})
.catch(() => {});
// Rerank has its own curated registry — the embedding listing does not
// include rerank-only providers (cohere rerank SKUs, siliconflow, ...).
fetch("/api/memory/rerank-providers")
.then((r) => (r.ok ? r.json() : null))
.then((data) => {
if (!cancelled && data?.providers) setRerankProviders(data.providers);
})
.catch(() => {});
return () => {
cancelled = true;
};
@@ -139,7 +148,7 @@ export default function EngineTab() {
</h3>
<RerankConfigCard
settings={settings}
providers={providers}
providers={rerankProviders}
onSave={handleSaveSettings}
saving={saving}
/>

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