Compare commits

..

3 Commits

Author SHA1 Message Date
Markus Hartung
bfeb4cfe15 docs(providers): regenerate PROVIDER_REFERENCE.md after porting #11538 2026-08-26 08:16:39 -03:00
Felix Wunderlich
afae88f577 feat(providers): opper logo, aggregator tag, regenerated reference and golden snapshot
(cherry picked from commit 9aacef7d6a)
2026-08-26 08:16:35 -03:00
Felix Wunderlich
f5f6158193 feat(providers): add Opper as an API-key gateway provider
Added Opper as an API-key gateway provider to OmniRoute. Five files changed:

1. `src/shared/constants/providers/apikey/gateways.ts` — new `opper` entry inserted immediately before `requesty` (same family: multi-model gateway, `passthroughModels: true`), matching the exact field shape of `requesty` and `openrouter`.
2. `open-sse/config/providers/registry/opper/index.ts` — new registry file using `buildOpenAiCompatibleRegistryEntry`, identical pattern to `requesty/index.ts`; base URL `https://api.opper.ai/v3/compat/chat/completions`, models URL `https://api.opper.ai/v3/compat/models`, empty static seed (live catalog via passthrough).
3. `open-sse/config/providers/index.ts` — import + REGISTRY key `opper` added adjacent to `requesty`.
4. `tests/unit/opper-provider.test.ts` — four unit tests mirroring `requesty-provider.test.ts`: catalog entry shape, registry entry shape, empty static seed, and passthrough model-id acceptance.
5. `changelog.d/features/opper-provider.md` — changelog fragment following the project's `changelog.d/` convention (never editing `CHANGELOG.md` directly).

(cherry picked from commit d976823672)
2026-08-26 08:16:31 -03:00
260 changed files with 700 additions and 8637 deletions

View File

@@ -925,11 +925,6 @@ NEXT_PUBLIC_ENABLE_SOCKS5_PROXY=true
# web_fetch). Default: 60000. Used by: open-sse/mcp-server/fetchTimeout.ts
# OMNIROUTE_MCP_UPSTREAM_TIMEOUT_MS=60000
# Maximum number of local-corpus index instances cached in memory.
# Used by: src/lib/localCorpus/configured.ts — bounds the LRU cache of
# LocalCorpusIndex objects (one per indexed root directory). Default: 5.
# OMNIROUTE_CORPUS_CACHE_SIZE=5
# Model catalog sync interval in hours.
# Used by: src/shared/services/modelSyncScheduler.ts — periodic model refresh.
# Default: 24
@@ -3067,9 +3062,3 @@ QUOTA_STORE_DRIVER=sqlite
# without a configured budget are always considered affordable. Requires the
# provider_quota_state table (migration 148).
# OMNIROUTE_QUOTA_AWARE_ROUTING=0
# ─── LOCAL CORPUS (opt-in document index) ───
# Size of the in-memory LRU index cache for the local document corpus used by
# corpus-aware retrieval. Higher values keep more index entries hot.
# Used by: src/lib/localCorpus/configured.ts
# OMNIROUTE_CORPUS_CACHE_SIZE=5

View File

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

View File

@@ -23,11 +23,27 @@ const ALIAS_UPPER_MAX_CHARS = 5;
// ── Auto Combo Types ─────────────────────────────────────────────────────
export type AutoVariant = "coding" | "fast" | "cheap" | "offline" | "smart" | "lkgp";
export type AutoVariant =
| "coding"
| "fast"
| "cheap"
| "offline"
| "smart"
| "lkgp";
export const AUTO_VARIANTS: AutoVariant[] = ["coding", "fast", "cheap", "offline", "smart", "lkgp"];
export const AUTO_VARIANTS: AutoVariant[] = [
"coding",
"fast",
"cheap",
"offline",
"smart",
"lkgp",
];
export const AUTO_VARIANT_DESCRIPTIONS: Record<AutoVariant | "default", string> = {
export const AUTO_VARIANT_DESCRIPTIONS: Record<
AutoVariant | "default",
string
> = {
default: "Best provider via scoring",
coding: "Quality-first for code tasks",
fast: "Latency-optimized routing",
@@ -67,15 +83,24 @@ function titleCaseAlias(alias: string): string {
* 3. Neither → undefined.
*/
export function shortProviderLabel(
enrichment: { providerDisplayName?: string; providerAlias?: string } | undefined
enrichment:
| { providerDisplayName?: string; providerAlias?: string }
| undefined,
): string | undefined {
if (!enrichment) return undefined;
const raw =
typeof enrichment.providerDisplayName === "string" ? enrichment.providerDisplayName.trim() : "";
typeof enrichment.providerDisplayName === "string"
? enrichment.providerDisplayName.trim()
: "";
if (raw.length > 0 && raw.length <= PROVIDER_LABEL_MAX_CHARS) return raw;
const alias = typeof enrichment.providerAlias === "string" ? enrichment.providerAlias.trim() : "";
const alias =
typeof enrichment.providerAlias === "string"
? enrichment.providerAlias.trim()
: "";
if (alias.length > 0) {
return alias.length <= ALIAS_UPPER_MAX_CHARS ? alias.toUpperCase() : titleCaseAlias(alias);
return alias.length <= ALIAS_UPPER_MAX_CHARS
? alias.toUpperCase()
: titleCaseAlias(alias);
}
// Long displayName with no alias to fall back on: keep the long label
// rather than dropping the provider prefix entirely.
@@ -106,33 +131,10 @@ export function normaliseFreeLabel(name: string): string {
// ── Free Budget Formatting ────────────────────────────────────────────────
/** Scales, largest first, so the unit is chosen by descending magnitude. */
const TOKEN_UNITS = [
[1e9, "B"],
[1e6, "M"],
[1e3, "K"],
] as const;
/**
* Format a token count as a short magnitude string: `25M`, `1.5K`, `999`.
*
* The unit has to be picked from the value that will actually be *printed*,
* not from the raw input. `toFixed(1)` rounds to the nearest tenth, so at the
* K scale 999_950 and above render as `1000.0` — and by then the M branch has
* already been skipped, producing `1000K` for a number that is `1M`. The same
* carry turns just under a billion into `1000M`. When the rounded value reaches
* the next scale, re-render at that scale instead.
*/
function fmtTokens(n: number): string {
for (let i = 0; i < TOKEN_UNITS.length; i++) {
const [scale, suffix] = TOKEN_UNITS[i]!;
if (n < scale) continue;
const value = Number((n / scale).toFixed(1));
// `Number()` also drops a trailing `.0`, which the previous regex did.
if (value < 1000 || i === 0) return `${value}${suffix}`;
const [nextScale, nextSuffix] = TOKEN_UNITS[i - 1]!;
return `${Number((n / nextScale).toFixed(1))}${nextSuffix}`;
}
if (n >= 1e9) return (n / 1e9).toFixed(1).replace(/\.0$/, "") + "B";
if (n >= 1e6) return (n / 1e6).toFixed(1).replace(/\.0$/, "") + "M";
if (n >= 1e3) return (n / 1e3).toFixed(1).replace(/\.0$/, "") + "K";
return String(n);
}
@@ -182,11 +184,15 @@ export function formatFreeBudget(params: {
*/
export function formatAutoComboName(
variant: AutoVariant | undefined,
candidateCount?: number
candidateCount?: number,
): string {
const label = variant ? variant.charAt(0).toUpperCase() + variant.slice(1) : "Default";
const label = variant
? variant.charAt(0).toUpperCase() + variant.slice(1)
: "Default";
const count =
typeof candidateCount === "number" && candidateCount > 0 ? ` (${candidateCount}p)` : "";
typeof candidateCount === "number" && candidateCount > 0
? ` (${candidateCount}p)`
: "";
return `Auto: ${label}${count}`;
}

View File

@@ -1,78 +0,0 @@
/**
* Magnitude-crossover regression for the free-budget suffix
* (`formatFreeBudget` -> `fmtTokens` in @omniroute/opencode-plugin/src/naming.ts).
*
* `fmtTokens` picked its unit from the raw input and then rounded with
* `toFixed(1)`. Rounding can carry a value into the next magnitude *after* that
* branch has been skipped, so 999_950..999_999 rendered as "1000K" rather than
* "1M", and just under a billion rendered as "1000M" rather than "1B".
*
* These budgets are not always round numbers: `monthlyTokens` is derived from the
* remote Radar feed (`tokensPerMonth`) and can be replaced wholesale by a
* user-local override, so the crossover band is reachable with real data.
*
* Kept in its own file rather than added to naming.test.ts so this does not
* collide with the coverage being added for `formatFreeBudget` in #11660.
*/
import { test } from "node:test";
import assert from "node:assert/strict";
import { formatFreeBudget } from "../src/naming.js";
/** `recurring-daily` is the shortest path from a token count to a rendered suffix. */
const daily = (monthlyTokens: number) =>
formatFreeBudget({ freeType: "recurring-daily", monthlyTokens }).replace(" tokens/day", "");
test("fmtTokens: a rounded K value that reaches 1000 is promoted to M", () => {
// 999_950 is the true boundary, not 999_999: toFixed(1) rounds to the nearest
// tenth, so 999.95K is the first value that carries to "1000.0".
assert.equal(daily(999_950), "1M");
assert.equal(daily(999_999), "1M");
});
test("fmtTokens: a rounded M value that reaches 1000 is promoted to B", () => {
assert.equal(daily(999_950_000), "1B");
assert.equal(daily(999_999_999), "1B");
});
test("fmtTokens: values just below the rounding boundary keep their own unit", () => {
// The promotion must not fire early — 999.9K still rounds to 999.9, not 1000.
assert.equal(daily(999_949), "999.9K");
assert.equal(daily(999_499), "999.5K");
assert.equal(daily(999_499_999), "999.5M");
});
test("fmtTokens: ordinary magnitudes are unchanged", () => {
assert.equal(daily(0), "0");
assert.equal(daily(999), "999");
assert.equal(daily(1_000), "1K");
assert.equal(daily(1_500), "1.5K");
assert.equal(daily(1_000_000), "1M");
assert.equal(daily(1_500_000), "1.5M");
assert.equal(daily(25_000_000), "25M");
assert.equal(daily(1_234_567), "1.2M");
assert.equal(daily(1_000_000_000), "1B");
assert.equal(daily(2_500_000_000), "2.5B");
});
test("fmtTokens: B is the top unit, so a carry there has nowhere to go", () => {
// Deliberately pinned: promoting past B would need a unit that does not exist,
// so "1000B" is the intended output rather than an oversight.
assert.equal(daily(999_999_999_999), "1000B");
});
test("formatFreeBudget: the promotion applies to every token-bearing branch", () => {
assert.equal(
formatFreeBudget({ freeType: "recurring-monthly", monthlyTokens: 999_999 }),
"1M tokens/month"
);
assert.equal(
formatFreeBudget({ freeType: "recurring-credit", creditTokens: 999_999 }),
"1M credits"
);
assert.equal(
formatFreeBudget({ freeType: "one-time-initial", creditTokens: 999_999 }),
"1M credits (one-time)"
);
});

View File

@@ -1,81 +0,0 @@
/**
* Tests for `formatFreeBudget` (@omniroute/opencode-plugin/src/naming.ts):
* formats a free-tier model's budget info into a short human-readable
* suffix, branching on `freeType`.
*/
import { test } from "node:test";
import assert from "node:assert/strict";
import { formatFreeBudget, type FreeModelFreeType } from "../src/naming.js";
test("formatFreeBudget: recurring-daily formats tokens/day", () => {
assert.equal(
formatFreeBudget({ freeType: "recurring-daily", monthlyTokens: 25_000_000 }),
"25M tokens/day"
);
});
test("formatFreeBudget: recurring-monthly formats tokens/month", () => {
assert.equal(
formatFreeBudget({ freeType: "recurring-monthly", monthlyTokens: 1_000_000 }),
"1M tokens/month"
);
});
test("formatFreeBudget: recurring-credit formats credits", () => {
assert.equal(
formatFreeBudget({ freeType: "recurring-credit", creditTokens: 10_000_000 }),
"10M credits"
);
});
test("formatFreeBudget: one-time-initial formats credits with (one-time) suffix", () => {
assert.equal(
formatFreeBudget({ freeType: "one-time-initial", creditTokens: 1_000_000 }),
"1M credits (one-time)"
);
});
test("formatFreeBudget: keyless has no token/credit args", () => {
assert.equal(formatFreeBudget({ freeType: "keyless" }), "(keyless)");
});
test("formatFreeBudget: discontinued has no token/credit args", () => {
assert.equal(formatFreeBudget({ freeType: "discontinued" }), "(discontinued)");
});
test("formatFreeBudget: missing token/credit counts default to 0", () => {
assert.equal(
formatFreeBudget({ freeType: "recurring-daily" }),
"0 tokens/day"
);
});
test("formatFreeBudget: unrecognised freeType falls through to the default branch", () => {
// `freeType` is populated from catalog data at runtime, so a value the
// build doesn't know about is reachable even though TypeScript treats the
// `default:` arm as dead code for a well-typed caller.
assert.equal(
formatFreeBudget({ freeType: "some-future-type" as FreeModelFreeType }),
""
);
});
test("formatFreeBudget: sub-1K token count is not abbreviated", () => {
assert.equal(
formatFreeBudget({ freeType: "recurring-daily", monthlyTokens: 500 }),
"500 tokens/day"
);
});
test("formatFreeBudget: the 999_999 rounding wart is fixed — promotes to 1M", () => {
// `toFixed(1)` rounds 999999/1e3 up to "1000.0" before the `>= 1e6` threshold
// check has a chance to apply. fmtTokens now promotes a rounded-up "1000" in
// any unit to the next unit up, so this correctly reads "1M" instead of the
// old "1000K" wart.
assert.equal(
formatFreeBudget({ freeType: "recurring-daily", monthlyTokens: 999_999 }),
"1M tokens/day"
);
});

View File

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

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 → 356 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. 356 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 → 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."/>
</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 356 providers. Automatic fallback keeps routing while another healthy target is available. Six pillars: resilient fallback across 356 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 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."/>
<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: 356 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: 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."/>
<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 **356-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 **353-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,7 +642,7 @@ of your shell history. → [CLI Integrations](docs/guides/CLI-INTEGRATIONS.md)
<div align="center">
## 🌐 356 AI Providers — 154 Catalog-Marked Free
## 🌐 353 AI Providers — 154 Catalog-Marked Free
</div>

View File

@@ -1 +0,0 @@
- **feat(search):** Add AnySearch free web search + URL extract (webFetch) with typed results, credential validation, REST routing, and MCP selection - fallback-only

View File

@@ -1 +0,0 @@
- **feat(providers):** add **Nimble** as a web-search and web-fetch provider (`nimble-search`) — `/v1/search` routes to Nimble's search API at `lite` depth (locale, freshness and include/exclude domain filters mapped onto the shared request contract), and `/v1/web/fetch` routes to Nimble Extract, which covers all four fetch formats (`markdown`, `html`, `links`, `screenshot`) from a single call. One API key serves both surfaces.

View File

@@ -1 +0,0 @@
- **sse:** fix LiveWS/embed-WS servers crashing at startup under the Node/tsx runtime — `liveServer.ts`, `embedWsProxy.ts` and `apiBridgeServer.ts` imported `@/shared/utils/httpClientAbortGuard` without the `.mjs` extension, so the client-abort crash guard added by [#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556) was unreachable and every dependent test failed with `ERR_MODULE_NOT_FOUND` ([#11556](https://github.com/diegosouzapw/OmniRoute/pull/11556)).

View File

@@ -1 +0,0 @@
- **resilience:** restore the expired-connection retry-budget probe in the token-health sweep — the `!isGitHubAccessTokenOnlyConnection` carve-out reintroduced by #11608 contradicted the boundary pinned by #11592, so a GitHub connection parked at `expired` with retry budget remaining was never probed and could never self-heal ([#11592](https://github.com/diegosouzapw/OmniRoute/pull/11592)).

View File

@@ -1 +0,0 @@
- fix(handoff): enforce provider allowlist for universal handoff (#11602) — universal handoff now skips summarization when the selected summary model's provider is not included in the configured provider allowlist.

View File

@@ -1 +0,0 @@
- **fix(dashboard):** Show a stable error state when Search Analytics returns an HTTP error or malformed data ([#11603](https://github.com/diegosouzapw/OmniRoute/pull/11603)) — thanks @pacocartones

View File

@@ -1 +0,0 @@
- **test(gamification):** pin the aggregate profile level to the XP-derived semantics of #11604`getAggregateXp()` now derives `currentLevel` from the summed XP (`calculateLevel(sum)`), not `MAX(stored current_level)`, and the #3484 fixture levels are aligned with the XP curve ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)).

View File

@@ -1 +0,0 @@
- **fix(dashboard):** Keep the Profile level and progress aligned with aggregate XP, including bounded handling for invalid totals ([#11604](https://github.com/diegosouzapw/OmniRoute/pull/11604)) — thanks @pacocartones

View File

@@ -1 +0,0 @@
- **fix(dashboard):** Prevent locked hidden badges from revealing their icon or opening private badge details before they are earned ([#11605](https://github.com/diegosouzapw/OmniRoute/pull/11605)) — thanks @pacocartones

View File

@@ -1 +0,0 @@
- **fix(dashboard):** Restore keyboard focus after shared modals close and cancel delayed autofocus during cleanup ([#11607](https://github.com/diegosouzapw/OmniRoute/pull/11607)) — thanks @pacocartones

View File

@@ -1 +0,0 @@
- **fix(dashboard):** Enable Enter and Space activation for clickable data-table rows without hijacking nested controls ([#11610](https://github.com/diegosouzapw/OmniRoute/pull/11610)) — thanks @pacocartones

View File

@@ -1 +0,0 @@
- **fix(autoCombo,sse):** vendor-retired catalog ids are dropped from the auto-combo candidate pool and no longer win on leftover `arena_elo` / `user_override` rows; `getModelLifecycleDecision` consults `model-lifecycle.json` (prefix-stripped) so aggregator traffic is not `untracked` for ids the snapshot already knows ([#11625](https://github.com/diegosouzapw/OmniRoute/issues/11625))

View File

@@ -1 +0,0 @@
- **test(chatcore):** move Codex/Claude combo fixtures off models the lifecycle guard now rejects — `gpt-5.1-codex`/`gpt-5-codex` are vendor-retired (snapshot, #11626) and `claude-3-5-sonnet-20241022` is shut down, so native-passthrough and combo-fallback tests switched to `gpt-5.6-sol` and `claude-sonnet-4.6` ([#11626](https://github.com/diegosouzapw/OmniRoute/pull/11626)).

View File

@@ -1 +0,0 @@
- fix(opencode-plugin): stop a free-tier budget that rounds up across a magnitude from rendering as `1000K`/`1000M` in the model picker — `fmtTokens` chose its unit from the raw token count and then rounded with `toFixed(1)`, so 999,950999,999 printed as `1000K` rather than `1M` and just under a billion printed as `1000M` rather than `1B` ([#11684](https://github.com/diegosouzapw/OmniRoute/pull/11684))

View File

@@ -1 +0,0 @@
- **docs:** sync the canonical provider count 354 → 356 across `README.md`, `AGENTS.md`, `llm.txt` (+ 42 i18n mirrors), the four README SVG diagrams, `docs/reference/PROVIDER_REFERENCE.md` (regenerated) and the `package.json` description after Opper (#11629) and 1min.ai (#11631) boarded the catalog — closes the `check:docs-counts-sync` strict drifts that kept `release/v3.8.51` red ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)).

View File

@@ -1 +0,0 @@
- **test(providers):** update count-derived assertions after the v3.8.51 provider additions — `APIKEY_PROVIDERS` 233 → 235 (Opper #11629 + 1min.ai #11631), reserved-prefix REGISTRY walk 395 → 398, `WEB_FETCH_PROVIDERS` now includes `nimble-search` (#11620), and the provider translate-path golden snapshot regenerated ([#11449](https://github.com/diegosouzapw/OmniRoute/issues/11449)).

View File

@@ -1 +0,0 @@
- **test(sse):** bump the hard-lease connection-query inventory for `src/lib/tokenHealthCheck.ts` to 2 — the verify-only web-cookie sweep added by #11495 split the single `getProviderConnections` call into oauth + cookie variants, which the frozen inventory had not tracked ([#11495](https://github.com/diegosouzapw/OmniRoute/pull/11495)).

View File

@@ -1 +0,0 @@
- **test(opencode-plugin):** add unit test coverage for `formatFreeBudget()` naming helper ([#11660](https://github.com/diegosouzapw/OmniRoute/pull/11660)) — thanks @f9td56dbgh-hub

View File

@@ -2790,6 +2790,11 @@
"count": 3
}
},
"src/mitm/dns/dnsConfig.ts": {
"@typescript-eslint/no-unused-vars": {
"count": 1
}
},
"src/mitm/dns/provision.ts": {
"@typescript-eslint/no-unused-vars": {
"count": 1
@@ -2921,6 +2926,11 @@
"count": 1
}
},
"src/shared/middleware/chatBodyAdmission.ts": {
"@typescript-eslint/no-unused-vars": {
"count": 1
}
},
"src/shared/services/apiKeyResolver.ts": {
"no-restricted-imports": {
"count": 1
@@ -3083,6 +3093,11 @@
"count": 1
}
},
"src/sse/services/auth.ts": {
"@typescript-eslint/no-unused-vars": {
"count": 1
}
},
"src/sse/services/model.ts": {
"no-restricted-imports": {
"count": 2
@@ -3430,7 +3445,7 @@
},
"tests/integration/skills-pipeline.test.ts": {
"@typescript-eslint/no-explicit-any": {
"count": 15
"count": 14
},
"@typescript-eslint/no-unused-vars": {
"count": 2

View File

@@ -229,8 +229,7 @@
"tests/unit/vscode-token-routes.test.ts": 1633,
"tests/unit/executor-antigravity.test.ts": 1427,
"tests/unit/guardrails/videoBridgeResultCache.test.ts": 1040,
"_rebaseline_2026_08_24_video_bridge_fu01_fu03_fu04_result_cache_tests": "PRs #11362 (FU-01 cache hardening) + #11382 (FU-03 visual dedup policy identity) + #11383 (FU-04 focused analysis mode) own test growth: videoBridgeResultCache.test.ts <1000->1040, +40 (sum of three stacked PRs boarded together in the same merge-batch, each adding its own cache-identity assertions on the shared result-cache seam). Owner pre-authorized rebaseline for legitimate PR growth (2026-08-19 directive).",
"tests/integration/skills-pipeline.test.ts": 1010
"_rebaseline_2026_08_24_video_bridge_fu01_fu03_fu04_result_cache_tests": "PRs #11362 (FU-01 cache hardening) + #11382 (FU-03 visual dedup policy identity) + #11383 (FU-04 focused analysis mode) own test growth: videoBridgeResultCache.test.ts <1000->1040, +40 (sum of three stacked PRs boarded together in the same merge-batch, each adding its own cache-identity assertions on the shared result-cache seam). Owner pre-authorized rebaseline for legitimate PR growth (2026-08-19 directive)."
},
"_rebaseline_2026_06_09": "Re-baseline consciente pre-release v3.8.19: 9 arquivos cresceram durante o ciclo (features mergeadas: RequestLoggerV2 +281 request-logger rework, stream +101, combo +73, chatCore +45, catalog +32 fable-5/catalog-flag, callLogs +4, accountFallback +2, usageHistory novo 840) + core.ts +7 (fix resetAllDbModuleState, PR 3536). A catraca segue valendo destes valores — proximo crescimento falha. Decisao: encolher (esp. RequestLoggerV2/chatCore) e a issue #3501 ficam para o ciclo seguinte.",
"_rebaseline_2026_06_11_phase1f": "Phase 1f (#3501): ProviderDetailPageClient.tsx 4948→4062 (-886 LOC); 3 novos hooks extraídos. useProviderConnections.ts=954 acima do cap=800 — justificado: extração direta do god-component (zero lógica nova), própria redução do cliente supera o custo. useProviderSettings.ts=263 e useProviderModels.ts=154 já abaixo do cap.",
@@ -311,7 +310,7 @@
"_rebaseline_2026_07_27_v3849_train1h": "Merge-train 1H (31 PRs) — owner-approved 2026-07-27. Two distinct causes, kept separate on purpose: (1) GENUINE irreducible growth at existing chokepoints — providerLimits/auth (#8632 Kimi quota-reset recovery), rateLimitManager (#8616 idle wedged limiters), models-catalog-route.test (#8610 OpenCode Go effort aliases); (2) COLLISION with #8585, which banked shrinks measured on the pre-train release tip while 30 sibling PRs in the SAME train grew those files again — chat/accountFallback (#8628), chatCore (#8613), videoGeneration (#8581), imageGeneration. The zero-headroom frozen entries cannot absorb either. Ceilings re-pinned to the post-merge tip; #8612 (also in this train) automates shrink-banking so this self-inflicted drift stops recurring. Detail: src/lib/usage/providerLimits.ts 1006->1013 (#8632); src/sse/services/auth.ts 2492->2508 (#8632); open-sse/services/rateLimitManager.ts 1014->1060 (#8616); src/sse/handlers/chat.ts 1842->1845 (#8628); open-sse/handlers/chatCore.ts 4939->4955 (#8613); open-sse/handlers/imageGeneration.ts 3100->3101 ((sem PR — teto do #8585)); open-sse/handlers/videoGeneration.ts 1038->1063 (#8581); open-sse/services/accountFallback.ts 1965->1966 (#8628); tests/unit/models-catalog-route.test.ts 1608->1636 (#8610)",
"frozen": {
"_rebaseline_2026_08_20_10878_10799_provider_health_probes": "PRs #10878 (unsupported OpenAI-like validation probes stay neutral) + #10799 (preserve credential health on inconclusive NVIDIA-timeout/Antigravity-400 probes) own growth: src/app/api/providers/[id]/test/route.ts 946->1025 (+79, sum of both boarded together). Both add narrowly-scoped classification branches at the existing test-route dispatch chokepoint (unsupported-capability skip, credential-inconclusive detection) rather than new files, mirroring the prior 2026_06_27_5193 rebaseline of the same file. Covered by tests/unit/provider-validation-unsupported-neutral.test.ts + tests/unit/provider-health-inconclusive-probes.test.ts.",
"src/app/api/providers/[id]/test/route.ts": 1262,
"src/app/api/providers/[id]/test/route.ts": 1237,
"_rebaseline_2026_08_23_11141_oauth_400_recovery": "PR #11141 (HouMinXi) own growth: test/route.ts 1025->1215 (+190, the reactive-400 recovery path — a fully rebuilt probe for refresh+retry on refreshable non-rotating connections, with inconclusive-status preservation and rotating-provider exclusion; all growth is the new probe builder + guards at the existing test-route dispatch, extraction would split the retry flow mid-logic). Covered by tests/unit/oauth-400-recovery.test.ts (8, bug-injection proof). Owner pre-authorized baseline bumps 2026-08-22.",
"_rebaseline_2026_06_22_4644_deepseek_web_tools": "PR #4644 (BugsBag/robust deepseek-web tool-call parsing): open-sse/executors/deepseek-web.ts 1117->1125 (+8). The new agentic tool-call path emits surrounding text + reasoning before tool_calls and swaps to the dedicated deepseekWebTools.ts parser; the +8 lines are cohesive wiring at the existing transformSSE chokepoint (the parser itself lives in the new deepseekWebTools.ts file, already under cap). The PR's own fast-gate (PR->release) does not run check:file-size, so this surfaced only at release reconcile. Covered by tests/unit/deepseek-web-tools-variants.test.ts + deepseek-web-tools-execute.test.ts.",
"_rebaseline_2026_06_23_4712_deepseek_web_tool_results": "PR for #4712 (deepseek-web drops role:tool): open-sse/executors/deepseek-web.ts 1125->1148 (+23). messagesToPrompt() now folds role:\"tool\" results into the single-prompt transcript (recovering the tool name from the preceding assistant tool_calls by tool_call_id) instead of silently dropping them; the lines are cohesive wiring inside the existing function. Covered by tests/unit/deepseek-web-tool-result-prompt-4712.test.ts.",
@@ -436,7 +435,7 @@
"src/shared/components/analytics/charts.tsx": 1346,
"src/shared/services/cliRuntime.ts": 1459,
"src/sse/handlers/chat.ts": 2493,
"src/sse/services/auth.ts": 3432,
"src/sse/services/auth.ts": 3346,
"_rebaseline_2026_08_24_lasterror_provider_error_detail": "PR (ntdat812) own growth: src/sse/services/auth.ts 3344->3346 (+2). One line is the import of describeUpstreamFailure from @/shared/utils/upstreamError, which replaces the string-only collapse `typeof errorText === \"string\" ? errorText.slice(0, 100) : \"Provider error\"` at the single markAccountUnavailable chokepoint (net 0 lines there) — the logic itself lives in upstreamError.ts, next to the extractErrorMessage it reuses, so nothing else moved into this file. The second line is the repo's own lint-staged prettier pass splitting a pre-existing two-statements-on-one-line at getProviderCredentials (`invalidateManagedLease(...); log.warn(...)`); it re-applies on any commit that touches this file, so it is not separable from the change. Covered by tests/unit/provider-error-detail-lastError.test.ts.",
"_rebaseline_2026_08_23_11186_synced_inventory_routing": "PR #11186 (pacocartones) own growth: src/sse/services/auth.ts 3260->3337 (+77, loadAdvertisedModelsForSelfHostedConnections + the modelNotAdvertised candidate-filter predicate — pins chat routing to the connection whose synced inventory actually advertises the model, fixing spurious model-not-found on multi-host self-hosted setups; at the existing credential-selection chokepoint, not extractable without splitting the selection flow). Covered by tests/unit/chat-routing-synced-inventory-11089.test.ts. Owner pre-authorized baseline bumps 2026-08-22.",
"tests/unit/account-fallback-service.test.ts": 2044,
@@ -460,7 +459,7 @@
"_rebaseline_2026_08_22_11156_enter_check_disabled": "PR #11156 (rqzbeh) own growth: AddApiKeyModal.tsx 1080->1082 (+2, Enter keydown handler now mirrors the isCheckDisabled condition — owner-requested post-merge polish from #11056; the rest of the diff is Prettier reflow). Covered by tests/unit/ui/add-api-key-modal-enter-key.test.tsx (jsdom render test, Enter dispatch assertions).",
"src/app/(dashboard)/dashboard/providers/[id]/hooks/useProviderConnections.ts": 1051,
"src/shared/components/ModelSelectModal.tsx": 1138,
"src/shared/constants/providers/apikey/gateways.ts": 1330,
"src/shared/constants/providers/apikey/gateways.ts": 1321,
"open-sse/vendor/codex-chatgpt-web/bridge.ts": 1387,
"_rebaseline_2026_08_11_v3850_merge_storm_provider_registry": "DRIFT do merge-storm 2026-08-11 (99 PRs mergeados no release/v3.8.50). AddApiKeyModal.tsx (PR #8949 ChatGPT Web provider) e useProviderConnections.ts/ModelSelectModal.tsx (PRs #9011 combo test-all, #9499 image combos) = UI nova legitima acima do cap; gateways.ts = god-file de catalogo de providers que cresceu com PRs #9009/#9421/#9468/#9594 (qualquer split arriscaria corromper o merge de novo — o proprio PR #9421 quebrou o arquivo); bridge.ts (PR #8949) = ponte Chromium vendored; proxyFetch.ts 1207->1220 = drift herdado de merges. Owner autorizou rebaseline com anotacao (2026-08-11).",
"src/lib/modelCapabilities.ts": 1072,
@@ -480,9 +479,7 @@
"_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,
"src/lib/cloudflaredTunnel.ts": 1078,
"src/shared/components/RequestLoggerDetail.tsx": 1018
"open-sse/services/autoCombo/virtualFactory.ts": 1130
},
"_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).",
@@ -651,8 +648,5 @@
"_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_26_mergebatch_v3851_batch1": "/merge-batch 2026-08-26 (v3.8.51): three legitimate growths from this batch. #11448 src/app/api/providers/[id]/test/route.ts 1237->1262 (auto-test-on-create wiring). #11495 src/sse/services/auth.ts 3346->3376 (web-cookie health-sweep verify-only path). #11561 src/lib/cloudflaredTunnel.ts new named-tunnel mode, lands at 1078 (+78 over the 1000 new-file cap) for the CLOUDFLARED_CONFIG named-tunnel flow (login->create->route dns config parsing + readiness detection). Owner-authorized rebaseline per merge-batch Step 4 (2026-08-19 directive); no further growth without split rationale.",
"_rebaseline_2026_08_26_mergebatch_v3851_batch2": "/merge-batch 2026-08-26 (v3.8.51) batch 2: three legitimate growths. #11083 src/shared/components/RequestLoggerDetail.tsx new-file cap, lands at 1018 (+18 over 1000) — copy-all button for request detail modal. #11631 src/shared/constants/providers/apikey/gateways.ts 1321->1330 (1min.ai gateway entry). #11628 src/sse/services/auth.ts 3376->3432 (credential-health isolation from model failures). Owner-authorized rebaseline per merge-batch Step 4 (2026-08-19 directive); no further growth without split rationale.",
"_rebaseline_2026_08_26_mergebatch_v3851_batch5": "/merge-batch 2026-08-26 (v3.8.51) batch 5: #11642 tests/integration/skills-pipeline.test.ts new regression test for the configured-provider-over-fallback search selection (#11524), lands at 1010 lines (+10 over the 1000 new-file testCap). Owner-authorized rebaseline per merge-batch Step 4 (2026-08-19 directive); no further growth without split rationale."
"_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."
}

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 (356 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 (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.">
<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: 356 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: 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.">
<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 356 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 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.">
<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">356 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">353 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 356 providers in</text>
<text x="66" y="204" font-size="13.5" fill="#a1a1aa">Auto-fallback across 353 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 356 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: 356 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 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.">
<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">356 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">353 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

@@ -57,7 +57,7 @@ New tab for extracting content from a URL via `POST /v1/web/fetch` (created in p
- Submit → fetch → render `ScrapeResult.tsx`.
- `ScrapeResult` renders markdown preview + raw toggle.
- Cap: if response body > **256 KB**, UI shows `(truncated, view raw)` and opens raw in a Monaco modal (D21).
- Metadata panel: provider (firecrawl/jina-reader/tavily-search/tinyfish/nimble-search/anysearch-search), latency, cost, response size, links count.
- Metadata panel: provider (firecrawl/jina-reader/tavily-search/tinyfish), latency, cost, response size, links count.
- Uses `useScrapeFetch.ts` hook.
### Compare Tab
@@ -105,7 +105,15 @@ Runs the same query/URL across up to **4 providers in parallel** (D22):
`ProviderCatalog.tsx` exposes the full provider list from `GET /api/search/providers`
(extended in F4 to include fetch providers):
| `kind` | `"search"` (20 providers) or `"fetch"` (firecrawl, jina-reader, tavily-search, tinyfish, nimble-search, anysearch-search) |
| Field | Source |
| ------------------------------ | ------------------------------------------------------------------------------------------ |
| `id`, `name` | `searchRegistry.ts` |
| `kind` | `"search"` (12 providers) or `"fetch"` (firecrawl, jina-reader, tavily-search, tinyfish) |
| `costPerQuery` | Registry data |
| `freeMonthlyQuota` | Registry data |
| `searchTypes` / `fetchFormats` | Registry data |
| `status` | `"configured"` / `"missing"` / `"rate_limited"` — derived at runtime from credential store |
| `configureHref` | `/dashboard/providers` |
The status is **derived at request time** by checking whether credentials exist and whether
all keys are currently in cooldown.
@@ -128,7 +136,7 @@ Only one backend change was needed for this feature:
`src/app/api/search/providers/route.ts` was extended to:
- Include all 6 fetch providers (`firecrawl`, `jina-reader`, `tavily-search`, `tinyfish`, `nimble-search`, `anysearch-search`) in the array.
- Include all 4 fetch providers (`firecrawl`, `jina-reader`, `tavily-search`, `tinyfish`) in the array.
- Add `kind: "search" | "fetch"` to every item.
- Add `status: "configured" | "missing" | "rate_limited"` derived from live credential state.
- Maintain backward compatibility — existing fields (`id`, `name`, etc.) unchanged.

View File

@@ -4,8 +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 356 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 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.
## Overview
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **356 AI providers** with automatic format translation
- **353 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
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **353-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,8 +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 356 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 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.
## Overview
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **356 AI providers** with automatic format translation
- **353 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
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **353-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,8 +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 356 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 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.
## Overview
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **356 AI providers** with automatic format translation
- **353 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
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **353-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,8 +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 356 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 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.
## Overview
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **356 AI providers** with automatic format translation
- **353 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
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **353-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,8 +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 356 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 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.
## Overview
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **356 AI providers** with automatic format translation
- **353 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
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **353-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,8 +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 356 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 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.
## Overview
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **356 AI providers** with automatic format translation
- **353 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
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **353-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,8 +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 356 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 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.
## Overview
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **356 AI providers** with automatic format translation
- **353 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
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **353-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,8 +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 356 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 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.
## Overview
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **356 AI providers** with automatic format translation
- **353 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
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **353-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,8 +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 356 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 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.
## Overview
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **356 AI providers** with automatic format translation
- **353 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
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **353-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,8 +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 356 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 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.
## Overview
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **356 AI providers** with automatic format translation
- **353 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
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **353-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,8 +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 356 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 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.
## Overview
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **356 AI providers** with automatic format translation
- **353 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
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **353-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,8 +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 356 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 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.
## Overview
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **356 AI providers** with automatic format translation
- **353 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
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **353-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,8 +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 356 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 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.
## Overview
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **356 AI providers** with automatic format translation
- **353 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
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **353-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,8 +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 356 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 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.
## Overview
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **356 AI providers** with automatic format translation
- **353 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
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **353-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,8 +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 356 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 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.
## Overview
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **356 AI providers** with automatic format translation
- **353 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
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **353-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,8 +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 356 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 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.
## Overview
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **356 AI providers** with automatic format translation
- **353 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
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **353-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,8 +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 356 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 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.
## Overview
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **356 AI providers** with automatic format translation
- **353 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
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **353-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,8 +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 356 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 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.
## Overview
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **356 AI providers** with automatic format translation
- **353 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
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **353-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,8 +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 356 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 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.
## Overview
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **356 AI providers** with automatic format translation
- **353 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
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **353-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,8 +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 356 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 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.
## Overview
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **356 AI providers** with automatic format translation
- **353 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
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **353-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,8 +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 356 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 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.
## Overview
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **356 AI providers** with automatic format translation
- **353 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
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **353-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,8 +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 356 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 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.
## Overview
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **356 AI providers** with automatic format translation
- **353 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
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **353-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,8 +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 356 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 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.
## Overview
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **356 AI providers** with automatic format translation
- **353 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
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **353-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,8 +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 356 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 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.
## Overview
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **356 AI providers** with automatic format translation
- **353 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
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **353-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,8 +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 356 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 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.
## Overview
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **356 AI providers** with automatic format translation
- **353 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
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **353-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,8 +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 356 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 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.
## Overview
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **356 AI providers** with automatic format translation
- **353 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
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **353-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,8 +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 356 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 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.
## Overview
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **356 AI providers** with automatic format translation
- **353 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
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **353-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,8 +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 356 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 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.
## Overview
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **356 AI providers** with automatic format translation
- **353 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
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **353-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,8 +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 356 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 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.
## Overview
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **356 AI providers** with automatic format translation
- **353 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
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **353-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,8 +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 356 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 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.
## Overview
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **356 AI providers** with automatic format translation
- **353 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
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **353-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,8 +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 356 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 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.
## Overview
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **356 AI providers** with automatic format translation
- **353 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
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **353-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,8 +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 356 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 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.
## Overview
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **356 AI providers** with automatic format translation
- **353 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
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **353-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,8 +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 356 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 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.
## Overview
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **356 AI providers** with automatic format translation
- **353 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
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **353-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,8 +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 356 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 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.
## Overview
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **356 AI providers** with automatic format translation
- **353 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
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **353-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,8 +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 356 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 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.
## Overview
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **356 AI providers** with automatic format translation
- **353 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
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **353-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,8 +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 356 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 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.
## Overview
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **356 AI providers** with automatic format translation
- **353 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
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **353-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,8 +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 356 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 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.
## Overview
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **356 AI providers** with automatic format translation
- **353 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
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **353-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,8 +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 356 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 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.
## Overview
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **356 AI providers** with automatic format translation
- **353 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
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **353-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,8 +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 356 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 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.
## Overview
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **356 AI providers** with automatic format translation
- **353 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
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **353-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,8 +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 356 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 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.
## Overview
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **356 AI providers** with automatic format translation
- **353 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
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **353-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,8 +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 356 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 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.
## Overview
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **356 AI providers** with automatic format translation
- **353 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
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **353-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,8 +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 356 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 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.
## Overview
@@ -282,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **356 AI providers** with automatic format translation
- **353 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
@@ -480,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **353-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

@@ -1199,8 +1199,6 @@ paths:
Searches the web, news, or X through a configured provider. Set `provider`
to `xquik-search` to use Xquik for X search. The aliases `xquik` and
`xquik_search` resolve to the same provider.
AnySearch (`anysearch-search`, aliases `anysearch` / `anysearch_search`)
provides free fallback-only web search.
security:
- BearerAuth: []
requestBody:

View File

@@ -1,45 +0,0 @@
# AnySearch provider integration (PR #11690)
Template: xquik #11370 (merged in release/v3.8.51). Posture: `fallbackOnly`.
Upstream proposal: issue diegosouzapw/OmniRoute#11637.
## 1. Service ground truth (official docs, cross-checked)
- Base URL: `https://api.anysearch.com` (REST) + `POST /mcp` (MCP, JSON-RPC 2.0).
- `POST /v1/search` - params: `query` (required), `max_results` (1-10, default 10), `tag` (`{domain}.{sub_domain}` vertical routing), `zone` (cn/intl), `language`, `params` (structured vertical fields), `format` (json/markdown).
- `GET /v1/sub-domains?domain=...` - capability catalog, does NOT count against quota.
- `POST /v1/extract` - fetch/extract `{url, title, content}`; strict JSON body, 16 KiB cap.
- `POST /v1/auth/email/register` - single-call registration, returns one-time plaintext key `as_sk_...`.
- Auth: optional `Authorization: Bearer <as_sk_...>`; anonymous degrades to per-IP limits consuming the daily free quota; invalid key returns 401/403 with NO silent anonymous fallback.
- Free tier: 1000 requests/day, 20 QPS per key. Paid tier: unpriced (Coming Soon).
- Response envelope: success `{code: 0, message: "success", request_id, data}`; failure `{code: -1, message}`. Auth/quota errors carry no structured `error_code` (the message text is the signal); extract-specific errors do carry `error_code` (`invalid_extract_url`, `extract_failed`). No `Retry-After` header on 429.
## 2. Touch set (~10 layers, mirrors the xquik footprint)
| Layer | File | Change |
| --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
| Registry entry | `open-sse/config/searchRegistry.ts` | `anysearch-search` entry + aliases `anysearch`, `anysearch_search` |
| Executor | `open-sse/handlers/search/anysearchSearch.ts` | `buildAnysearchSearchRequest`, `normalizeAnysearchSearchResponse`, `AnysearchSearchEnvelopeError` |
| Dispatch maps | `open-sse/handlers/search.ts` + `searchProxy.ts` | request-builder map + response-normalizer map; envelope error -> 402 (quota) / 502 (other) |
| Fetch executor | `open-sse/executors/anysearch-fetch.ts` + `open-sse/handlers/webFetch.ts` | `POST /v1/extract`; union + `WEB_FETCH_PROVIDERS` + dispatch case; quota envelope -> 402 |
| UI catalog | `src/shared/constants/providers/search.ts` | metadata entry (`serviceKinds: ["webSearch", "webFetch"]`); authHint documents the 1000/day free tier |
| Credential validation | `src/lib/providers/validation/searchProviders.ts` | `SEARCH_VALIDATOR_CONFIGS["anysearch-search"]` (Bearer probe) |
| MCP | `open-sse/mcp-server/schemas/tools.ts` | fetch enum + web_search description |
| API schema | `src/shared/validation/schemas/apiV1.ts`, `docs/openapi.yaml` | alias canonicalization + provider enum |
| Docs | `docs/reference/PROVIDER_REFERENCE.md`, `docs/frameworks/SEARCH_TOOLS_STUDIO.md`, `changelog.d/features/anysearch-search-provider.md` | consistency copies |
| Tests | `tests/unit/anysearch-search-provider.test.ts` (8 cases), `tests/unit/executor-anysearch-fetch.test.ts` (2 cases), `search-registry.test.ts`, `search-route.test.ts`, `tests/integration/search-providers-catalog.test.ts`, `tests/snapshots/executors/dispatch-rules.json` | mirror xquik suite; catalog counts 20 search / 6 fetch (coexisting with nimble-search) |
## 3. Decisions (all reached 2026-08-26)
1. **Routing posture: `fallbackOnly`.** Rationale: a cost-0 free provider must never dominate automatic cost routing; explicit selection and failover return are unaffected. Precedent: merged xquik entry.
2. **Scope: webSearch + webFetch via `POST /v1/extract` in v1.** Aligned with the tavily/exa dual-capability mental model. Vertical surfaces (`tag`, `sub-domains`, `batch_search`) stay out of scope — there is no IR for vertical params today.
3. **Quota display: `freeMonthlyQuota: 0`** (xquik-style conservative display). The real allowance (1000 req/day, daily reset) is carried in the UI catalog `authHint` copy instead. Rationale: quota display must match reset semantics; converting a daily cap to a monthly-equivalent (30000) is a false promise the UI cannot honor.
4. **searchTypes: `["web"]` only.** No public evidence of a news/images vertical in the AnySearch API; gateways integrating APIs without a documented vertical (e.g. Google Custom Search) expose web only rather than silently mapping news -> web.
5. **Key posture:** keyless works; an invalid key returns 401/403 and is never silently downgraded to anonymous (mirrors the upstream contract).
6. **Deliberate exclusions:** `FETCH_BACKEND_TO_PROVIDER` / `FetchInterceptionBackend` (chat interception stays firecrawl/jina/tavily); `ANONYMOUS_CAPABLE_WEB_FETCH_PROVIDERS` (anonymous remote endpoints share one IP-limited pool per egress IP, so keyless extract is opt-in, not an auto-routing candidate); `QUOTA_STATUS_PROVIDERS` (AnySearch signals quota via 429/envelope, not 402/403 status).
## 4. References
- xquik template: commit a0ceccc, PR #11370 (in-tree at release/v3.8.51).
- AnySearch official docs: https://anysearch.com/docs, https://anysearch.com/pricing; MCP catalog mcpservers.org/servers/anysearch-ai/anysearch-mcp-server; skill repo github.com/anysearch-ai/anysearch-skill.
- Industry mental model: LiteLLM search docs (registry + unified search() + Perplexity-spec IR), open-webui web search (built-in providers, search_web/fetch_url dual tools), Dify tool plugin pattern; cost-fallback ladder: self-hosted -> free quota -> paid.

View File

@@ -529,7 +529,7 @@ Web/search provider abstraction (Tavily, Brave, Exa, Serper, etc.).
## Web Fetch API
Extract content from a URL via a configured web-fetch provider (Firecrawl, Jina
Reader, Tavily Extract, TinyFish Fetch, Nimble Extract).
Reader, Tavily Extract, TinyFish Fetch).
| Method | Path | Description |
| ------ | --------------- | --------------------------------------------------------- |
@@ -538,8 +538,7 @@ Reader, Tavily Extract, TinyFish Fetch, Nimble Extract).
**Auth:** Bearer API key (`extractApiKey` + `isValidApiKey`). Policy enforced via `enforceApiKeyPolicy`.
**Quota-aware fallback (#8297):** when no explicit `provider` is given, the pool
(`firecrawl``jina-reader``tavily-search``tinyfish``nimble-search`) is
walked in fixed
(`firecrawl``jina-reader``tavily-search``tinyfish`) is walked in fixed
priority order (fill-first) — a rate-limited-but-configured provider is skipped
instead of short-circuiting the request, and a retryable/quota upstream failure
(HTTP 429 always; 402/403 for Firecrawl/Tavily/TinyFish quota-style free tiers —
@@ -662,21 +661,11 @@ refusal. On success:
{
"allowed": true,
// present only when the key opted into per-key usage limits (daily/weekly USD):
"personal": {
"dailySpentUsd": 1.25,
"dailyLimitUsd": 5,
"dailyResetAtIso": "…",
"weeklySpentUsd": 8,
"weeklyLimitUsd": 20,
"weeklyResetAtIso": "…" /* */,
},
"personal": { "dailySpentUsd": 1.25, "dailyLimitUsd": 5, "dailyResetAtIso": "…", "weeklySpentUsd": 8, "weeklyLimitUsd": 20, "weeklyResetAtIso": "…" /* */ },
// the selected provider quota snapshot, or null when nothing is cached yet:
"provider": { "connectionId": "…", "provider": "claude", "plan": "…", "quotas": {/* */} },
"provider": { "connectionId": "…", "provider": "claude", "plan": "…", "quotas": { /* */ } },
// every connection's snapshot, so a UI can render several providers side by side:
"providers": [
{ "connectionId": "…", "provider": "claude" /* */ },
{ "provider": "codex" /* */ },
],
"providers": [ { "connectionId": "…", "provider": "claude", /* */ }, { "provider": "codex", /* */ } ]
}
```
@@ -685,7 +674,7 @@ On refusal (`401` bad key / `403` not allowed) the same route returns
(key allowed, nothing learned yet) is a different state from a refusal, and only the JSON form
distinguishes them.
**Auth:** the caller's own Bearer API key, validated with `isValidApiKey` — this is _not_ the
**Auth:** the caller's own Bearer API key, validated with `isValidApiKey` — this is *not* the
management surface (`/api/keys/…`), which stays behind `requireManagementAuth`.
---

View File

@@ -512,7 +512,6 @@ detection above).
| `OMNIROUTE_MCP_DESCRIPTION_COMPRESSION` | `rtk` | `open-sse/mcp-server/descriptionCompressor.ts` | Compression algorithm/profile. Disable values: `0`, `false`, `off`. |
| `OMNIROUTE_MCP_FETCH_TIMEOUT_MS` | `10000` | `open-sse/mcp-server/fetchTimeout.ts` | Abort budget (ms) for MCP-server internal management reads (health, resilience, combos, quota, usage). |
| `OMNIROUTE_MCP_UPSTREAM_TIMEOUT_MS` | `60000` | `open-sse/mcp-server/fetchTimeout.ts` | Abort budget (ms) for MCP hops that wait on a provider (`route_request`, `web_search`, `web_fetch`). |
| `OMNIROUTE_CORPUS_CACHE_SIZE` | `5` | `src/lib/localCorpus/configured.ts` | Maximum number of local-corpus index instances cached in memory (LRU, one per indexed root directory). Clamped to a minimum of `1`. |
| `MODEL_SYNC_INTERVAL_HOURS` | `24` | `src/shared/services/modelSyncScheduler.ts` | Model catalog sync interval in hours. |
| `PROVIDER_LIMITS_SYNC_INTERVAL_MINUTES` | `70` | `src/lib/usage/providerLimits.ts` | Provider rate-limit and quota polling interval. |
| `PROVIDER_LIMITS_SYNC_SPACING_MS` | `1500` | `src/lib/usage/providerLimits.ts` | Gap (ms) between consecutive OAuth quota fetches in a bulk sync; OAuth connections are fetched one at a time to avoid bursting an upstream. `0` opts out (concurrent). |

View File

@@ -10,7 +10,7 @@ lastUpdated: 2026-08-26
> Regenerate with: `npm run gen:provider-reference`
> **Last generated:** 2026-08-26
Total providers: **356**. See category breakdown below.
Total providers: **354**. 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) (235)
## API Key Providers (paid / paid-with-free-credits) (234)
| ID | Alias | Name | Tags | Website | Notes |
|----|-------|------|------|---------|-------|
@@ -277,7 +277,6 @@ Use the dashboard at `/dashboard/providers` to enable, configure, and test each
| `oci` | `oci` | OCI Generative AI | API key, enterprise | [link](https://www.oracle.com/artificial-intelligence/generative-ai) | Use your OCI Generative AI API key or IAM bearer token. Base URL can be https://inference.generativeai.<region>.oci.oraclecloud.com/openai/v1/. |
| `ofoxai` | `ofoxai` | OfoxAI | API key, aggregator | [link](https://ofox.ai) | The current catalog advertises 10+ free models without a public numeric quota; review upstream provenance, retention and training terms before production use. |
| `ollama-cloud` | `ollamacloud` | Ollama Cloud | API key | [link](https://ollama.com/settings/keys) | — |
| `oneminai` | `1min` | 1min.AI | API key | [link](https://1min.ai) | Create an API key at https://docs.1min.ai/docs/api/create-api-key, then paste it here. |
| `openadapter` | `oad` | OpenAdapter | API key | [link](https://openadapter.dev) | Use your OpenAdapter API key in Authorization: Bearer sk-cv-<key>. Fully OpenAI-compatible. API base URL: https://api.openadapter.in/v1. |
| `openai` | `openai` | OpenAI | API key | [link](https://platform.openai.com) | — |
| `opencode-go` | `opencode-go` | OpenCode Go | API key | [link](https://opencode.ai/go) | — |
@@ -381,7 +380,7 @@ Use the dashboard at `/dashboard/providers` to enable, configure, and test each
| `vllm` | `vllm` | vLLM | Local, self-hosted | [link](https://github.com/vllm-project/vllm) | API key optional. Configure the local vLLM OpenAI-compatible base URL (default: http://localhost:8000/v1). |
| `xinference` | `xinference` | XInference | Local, self-hosted | [link](https://inference.readthedocs.io) | API key optional. Configure the local XInference OpenAI-compatible base URL (default: http://localhost:9997/v1). |
## Search Providers (16)
## Search Providers (15)
| ID | Alias | Name | Tags | Website | Notes |
|----|-------|------|------|---------|-------|
@@ -391,14 +390,12 @@ Use the dashboard at `/dashboard/providers` to enable, configure, and test each
| `firecrawl` | `fc` | Firecrawl | Search | [link](https://firecrawl.dev) | API key from firecrawl.dev/app/api-keys (or set your self-hosted Firecrawl base URL) |
| `google-pse-search` | `google-pse` | Google Programmable Search | Search | [link](https://developers.google.com/custom-search/v1/overview) | Requires a Google API key and your Programmable Search Engine ID (cx) |
| `linkup-search` | `linkup` | Linkup Search | Search | [link](https://docs.linkup.so) | Bearer API key from the Linkup dashboard |
| `nimble-search` | `nimble` | Nimble Search | Search | [link](https://docs.nimbleway.com/nimble-sdk/web-tools/search) | Bearer API key from the Nimble dashboard |
| `ollama-search` | `ollama-search` | Ollama Search | Search | [link](https://ollama.com/settings/keys) | Same API key as Ollama Cloud (from ollama.com/settings/keys) |
| `perplexity-search` | `pplx-search` | Perplexity Search | Search | [link](https://docs.perplexity.ai/guides/search-quickstart) | Same API key as Perplexity (pplx-...) |
| `searchapi-search` | `searchapi` | SearchAPI | Search | [link](https://www.searchapi.io/docs/google) | API key from SearchAPI (query param or Bearer auth) |
| `searxng-search` | `searxng` | SearXNG Search | Search | [link](https://docs.searxng.org) | API key is optional. Set your SearXNG base URL. Some instances may require a bearer token for access. |
| `serper-search` | `serper-search` | Serper Search | Search | [link](https://serper.dev) | API key from serper.dev dashboard |
| `tavily-search` | `tavily-search` | Tavily Search | Search | [link](https://tavily.com) | API key from app.tavily.com (format: tvly-...) |
| `anysearch-search` | `anysearch` | AnySearch | Search | [link](https://anysearch.com/docs) | Optional API key (as_sk_...). Free public web search for agents; 1000 req/day per key, shared with extract. Fallback-only. |
| `x-search` | `x_search` | X Search (Grok) | Search | [link](https://docs.x.ai/developers/tools/x-search) | SuperGrok OAuth (xai-oauth) or xAI API key. This is Grok X Search, not the X Developer MCP. |
| `xquik-search` | `xquik` | Xquik X Search | Search | [link](https://docs.xquik.com) | Xquik API key (xq_...). Search is metered per returned post; the catalog estimate uses 5 results. |
| `youcom-search` | `youcom-search` | You.com Search | Search | [link](https://you.com/business/api/) | X-API-Key from the You.com platform dashboard |
@@ -445,7 +442,7 @@ Use the dashboard at `/dashboard/providers` to enable, configure, and test each
- Catalog: [`src/shared/constants/providers.ts`](../../src/shared/constants/providers.ts)
- Registry (per-model details): [`open-sse/config/providerRegistry.ts`](../../open-sse/config/providerRegistry.ts)
- Executors: [`open-sse/executors/`](../../open-sse/executors/) (111 implementations)
- Executors: [`open-sse/executors/`](../../open-sse/executors/) (109 implementations)
- Translators: [`open-sse/translator/`](../../open-sse/translator/)
## See Also

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 356 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 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.
## Overview
@@ -277,7 +277,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **356 AI providers** with automatic format translation
- **353 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
- **356-provider catalog** with 90+ free tiers, one-click account imports, and bulk key add
- **353-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,17 +0,0 @@
/**
* Nimble shared client constants.
*
* Nimble asks every integration to identify itself with a stable client-source
* header so calls can be attributed to the host product. Both Nimble surfaces
* in OmniRoute — search (`POST /v1/search`, wired in open-sse/handlers/search.ts)
* and fetch (`POST /v1/extract`, open-sse/executors/nimble-fetch.ts) — send it,
* and both read the value from here so the two can never drift apart.
*
* Docs: https://docs.nimbleway.com/api-reference/introduction
*/
/** Header Nimble uses to attribute a request to the calling product. */
export const NIMBLE_CLIENT_SOURCE_HEADER = "X-Client-Source";
/** The value OmniRoute sends. Do not vary it per surface or per request. */
export const NIMBLE_CLIENT_SOURCE = "omniroute";

View File

@@ -63,7 +63,6 @@ import { nubeProvider } from "./registry/nube/index.ts";
import { clinepassProvider } from "./registry/clinepass/index.ts";
import { sparkdeskProvider } from "./registry/sparkdesk/index.ts";
import { nlpcloudProvider } from "./registry/nlpcloud/index.ts";
import { oneminaiProvider } from "./registry/oneminai/index.ts";
import { nvidiaProvider } from "./registry/nvidia/index.ts";
import { api_airforceProvider } from "./registry/api-airforce/index.ts";
import { mistralProvider } from "./registry/mistral/index.ts";
@@ -333,7 +332,6 @@ export const REGISTRY: Record<string, RegistryEntry> = {
clinepass: clinepassProvider,
sparkdesk: sparkdeskProvider,
nlpcloud: nlpcloudProvider,
oneminai: oneminaiProvider,
nvidia: nvidiaProvider,
"api-airforce": api_airforceProvider,
mistral: mistralProvider,

View File

@@ -1,30 +0,0 @@
import type { RegistryEntry } from "../../shared.ts";
// 1min.ai (docs.1min.ai) — a chat aggregator exposing many upstream models
// through one custom API. Not OpenAI-compatible at the wire level (single
// `promptObject.prompt` string instead of a `messages` array, real SSE with
// event:/data: framing instead of raw text deltas, "API-KEY" auth header
// instead of Authorization: Bearer) — see open-sse/executors/oneminai.ts for
// the request/response translation. `format: "openai"` here describes the
// client-facing surface OmniRoute exposes, not 1min.ai's actual wire format.
export const oneminaiProvider: RegistryEntry = {
id: "oneminai",
alias: "1min",
format: "openai",
executor: "default",
baseUrl: "https://api.1min.ai/api/chat-with-ai",
authType: "apikey",
authHeader: "api-key",
// The model catalog is loaded dynamically per-account/plan on 1min.ai's own
// dashboard rather than published as a stable public list, so only the
// model shown in every one of 1min.ai's own docs examples is statically
// catalogued; passthroughModels lets any other slug the account has access
// to be used by id.
passthroughModels: true,
liveCatalogAuthoritative: false,
// No tool/function-calling, JSON mode, or vision support is wired up by the
// executor's translation (1min.ai's attachments.images/files feature would
// need separate Asset API upload plumbing this provider doesn't implement).
unsupportedParams: ["tools", "tool_choice", "functions", "function_call", "response_format"],
models: [{ id: "gpt-4o-mini", name: "GPT-4o Mini" }],
};

View File

@@ -116,27 +116,6 @@ export const SEARCH_PROVIDERS: Record<string, SearchProviderConfig> = {
cacheTTLMs: 5 * 60 * 1000,
},
// Nimble also serves POST /v1/web/fetch through the same credential — see
// open-sse/executors/nimble-fetch.ts.
"nimble-search": {
id: "nimble-search",
name: "Nimble Search",
baseUrl: "https://sdk.nimbleway.com/v1/search",
method: "POST",
authType: "apikey",
authHeader: "bearer",
costPerQuery: 0.005,
freeMonthlyQuota: 0,
searchTypes: ["web", "news"],
defaultMaxResults: 5,
maxMaxResults: 100,
// Kept below GLOBAL_TIMEOUT_MS (handlers/search.ts) so a stalled Nimble call
// still leaves budget for the failover provider instead of burning the whole
// request window.
timeoutMs: 10_000,
cacheTTLMs: 5 * 60 * 1000,
},
firecrawl: {
id: "firecrawl",
name: "Firecrawl",
@@ -370,26 +349,6 @@ export const SEARCH_PROVIDERS: Record<string, SearchProviderConfig> = {
cacheTTLMs: 5 * 60 * 1000,
fallbackOnly: true,
},
// Free public web search for AI agents (https://anysearch.com). fallback-only:
// a cost-0 provider never overrides configured paid providers in automatic
// selection. max_results is capped at 10 upstream.
"anysearch-search": {
id: "anysearch-search",
name: "AnySearch",
baseUrl: "https://api.anysearch.com/v1/search",
method: "POST",
authType: "apikey",
authHeader: "bearer",
costPerQuery: 0,
freeMonthlyQuota: 0, // free tier is 1000 req/day (daily reset, not monthly) — 0 matches the xquik convention; the daily figure lives in the UI catalog authHint
searchTypes: ["web"],
defaultMaxResults: 5,
maxMaxResults: 10,
timeoutMs: 10_000,
cacheTTLMs: 5 * 60 * 1000,
fallbackOnly: true,
},
};
/**
@@ -439,8 +398,6 @@ export const SEARCH_PROVIDER_ALIASES: Record<string, string> = {
x: "x-search",
xquik: "xquik-search",
xquik_search: "xquik-search",
anysearch: "anysearch-search",
anysearch_search: "anysearch-search",
};
export function resolveSearchProviderId(providerId: string): string {

View File

@@ -1,109 +0,0 @@
/**
* AnySearch Web Fetch Executor
*
* Fetches readable content from a URL using the AnySearch Extract API.
* POST https://api.anysearch.com/v1/extract
*
* Free tier: 1000 requests/day per key, shared with /v1/search. Bearer auth
* is optional upstream - keyless calls use the lower anonymous tier. Routing
* stays key-gated; anonymity only applies at call time.
* Docs: https://anysearch.com/docs
*/
import { sanitizeErrorMessage, buildErrorBody } from "../utils/error.ts";
import type { WebFetchResult, WebFetchFormat, WebFetchCredentials } from "../handlers/webFetch.ts";
const ANYSEARCH_EXTRACT_URL = "https://api.anysearch.com/v1/extract";
const ANYSEARCH_TIMEOUT_MS = 30_000;
interface AnysearchFetchOptions {
url: string;
format: WebFetchFormat;
includeMetadata: boolean;
credentials: WebFetchCredentials;
}
/**
* Execute an AnySearch extract request.
* The upstream contract is strict JSON: a single { url } object with no
* unknown fields, capped at 16 KiB - do not add request fields here.
*/
export async function anysearchFetch(opts: AnysearchFetchOptions): Promise<WebFetchResult> {
// format is accepted but unused: AnySearch extract always returns markdown-ish text
// (mirroring the context7 pattern of accepting the field without rejecting).
const { url, includeMetadata, credentials } = opts;
const controller = new AbortController();
const timeoutId = setTimeout(() => {
const err = new Error(`anysearch-fetch timeout after ${ANYSEARCH_TIMEOUT_MS}ms`);
err.name = "TimeoutError";
controller.abort(err);
}, ANYSEARCH_TIMEOUT_MS);
try {
const response = await fetch(ANYSEARCH_EXTRACT_URL, {
method: "POST",
headers: {
"Content-Type": "application/json",
...(credentials.apiKey ? { Authorization: `Bearer ${credentials.apiKey}` } : {}),
},
body: JSON.stringify({ url }),
signal: controller.signal,
});
if (!response.ok) {
const rawError = await response.text().catch(() => `HTTP ${response.status}`);
const msg = sanitizeErrorMessage(`AnySearch error ${response.status}: ${rawError}`);
const body = buildErrorBody(response.status, msg);
return { success: false, status: response.status, error: body.error.message };
}
const data = (await response.json()) as Record<string, unknown>;
// Envelope: { code, message, data: { url, title, content } } - tolerate the
// enveloped and flat shapes before giving up.
const envelope =
data.data && typeof data.data === "object" && !Array.isArray(data.data)
? (data.data as Record<string, unknown>)
: data;
const code = typeof data.code === "number" ? data.code : 0;
if (code !== 0) {
const errorMsg = String(data.error_code ?? data.message ?? code);
// Quota-shaped envelope errors map to 402 (failover-eligible), mirroring
// the search-side AnysearchSearchEnvelopeError → 402 pattern in anysearchSearch.ts.
const isQuota = /quota|exceed|limit|balance|credit|exhaust/i.test(errorMsg);
const status = isQuota ? 402 : 422;
const msg = sanitizeErrorMessage(`AnySearch extract failed: ${errorMsg}`);
const body = buildErrorBody(status, msg);
return { success: false, status, error: body.error.message };
}
const content = String(envelope.content ?? "");
const title = envelope.title != null ? String(envelope.title) : null;
const metadata = includeMetadata ? { title, description: null } : null;
return {
success: true,
data: {
provider: "anysearch-search",
url,
content,
links: [],
metadata,
screenshot_url: null,
},
};
} catch (err: unknown) {
if (err instanceof Error && err.name === "AbortError") {
const body = buildErrorBody(504, "AnySearch request timed out");
return { success: false, status: 504, error: body.error.message };
}
const msg =
err instanceof Error ? sanitizeErrorMessage(err.message) : sanitizeErrorMessage(String(err));
const body = buildErrorBody(502, msg);
return { success: false, status: 502, error: body.error.message };
} finally {
clearTimeout(timeoutId);
}
}

View File

@@ -16,7 +16,6 @@ import { prepareToolMessages, buildToolAwareResult } from "../translator/webTool
import type { Session } from "../services/sessionPool/session.ts";
import { tryBackedChat } from "../services/browserBackedChat.ts";
import { sanitizeErrorMessage } from "../utils/error.ts";
import { normalizeSystemRole } from "../services/roleNormalizer.ts";
// Issue #6999: Lightweight circuit breaker for the DuckDuckGo executor.
// After CB_THRESHOLD consecutive failures (429, 5xx, or network errors),
@@ -560,17 +559,8 @@ export class DuckDuckGoWebExecutor extends BaseExecutor {
}
}
// #ddgw defense-in-depth: duckchat/v1/chat accepts only user/assistant roles.
// Normalize after catalog resolution so the effective upstream model is used.
// This also shields the system tool prompt injected by prepareToolMessages.
const normalizedMessages = normalizeSystemRole(
messages,
"duckduckgo-web",
upstreamModel
) as typeof messages;
const sendChat = async (vqdHeaders: DuckDuckGoAuthHeaders): Promise<Response> => {
const payload = buildDuckDuckGoPayload(upstreamModel, normalizedMessages);
const payload = buildDuckDuckGoPayload(upstreamModel, messages);
const response = await fetch(CHAT_URL, {
method: "POST",
headers: mergeHeadersCaseInsensitive(
@@ -796,7 +786,10 @@ export class DuckDuckGoWebExecutor extends BaseExecutor {
try {
return {
vqd4: retry.vqd4,
vqdHash1: await solveDuckDuckGoChallenge(retry.vqdHash1, FAKE_HEADERS["User-Agent"]),
vqdHash1: await solveDuckDuckGoChallenge(
retry.vqdHash1,
FAKE_HEADERS["User-Agent"]
),
status: retry.status,
retryAfter: retry.retryAfter,
};

View File

@@ -60,8 +60,6 @@ const lazyExecutors: Record<string, () => Promise<BaseExecutor>> = {
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()),
oneminai: () => import("./oneminai.ts").then((m) => new m.OneMinAiExecutor()),
"1min": () => import("./oneminai.ts").then((m) => new m.OneMinAiExecutor()), // Alias
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()),

View File

@@ -243,14 +243,6 @@ export function resolveKiroRegion(
// kiroRuntimeHost from this executor keep working.
export { kiroRuntimeHost };
/**
* Status codes for which trying the next candidate endpoint may succeed where the
* current one failed (auth/profile mismatch, not a payload problem). Mirrors
* 9router's KIRO_ENDPOINT_FALLBACK_STATUSES — a 400 (malformed body) is deliberately
* excluded since resending the same body to another host cannot fix it.
*/
const KIRO_ENDPOINT_FALLBACK_STATUSES = new Set([401, 403, 404]);
/**
* KiroExecutor - Executor for Kiro AI (AWS CodeWhisperer)
* Uses AWS CodeWhisperer streaming API with AWS EventStream binary format
@@ -342,47 +334,17 @@ export class KiroExecutor extends BaseExecutor {
// Center accounts (e.g. eu-central-1) are rejected by the default us-east-1 host; only the
// regional endpoint accepts the region-bound token + profileArn.
const region = resolveKiroRegion(credentials);
const regionalUrl = `${kiroRuntimeHost(region)}/generateAssistantResponse`;
// The Kiro IDE's own branded gateway (runtime.*.kiro.dev) only exists for
// us-east-1 and only accepts Kiro OIDC/social tokens — it rejects
// TokenType=API_KEY and external-IdP/IdC SSO tokens outright (403 "bearer
// token invalid"), so those auth methods go straight to the region-resolved
// CodeWhisperer/Amazon Q surface (mirrors 9router's getOrderedBaseUrls in
// open-sse/executors/kiro.js). For everything else, try the branded gateway
// first — it is the surface the native Kiro IDE itself talks to — and fall
// back to the raw AWS host on an auth/profile-shaped failure.
const authMethod =
typeof credentials.providerSpecificData?.authMethod === "string"
? credentials.providerSpecificData.authMethod
: undefined;
const isCodeWhispererOnly =
authMethod === "api_key" || authMethod === "idc" || isExternalIdpAuthMethod(authMethod);
const candidateUrls =
region === "us-east-1" && !isCodeWhispererOnly
? ["https://runtime.us-east-1.kiro.dev/generateAssistantResponse", regionalUrl]
: [regionalUrl];
const url = `${kiroRuntimeHost(region)}/generateAssistantResponse`;
const headers = this.buildHeaders(credentials, stream);
mergeUpstreamExtraHeaders(headers, upstreamExtraHeaders);
const transformedBody = await this.transformRequest(model, body, stream, credentials);
const requestBody = JSON.stringify(transformedBody);
let response!: Response;
let url = candidateUrls[0];
for (let i = 0; i < candidateUrls.length; i++) {
url = candidateUrls[i];
response = await fetch(url, {
method: "POST",
headers,
body: requestBody,
signal,
});
const hasFallback = i + 1 < candidateUrls.length;
if (response.ok || !hasFallback || !KIRO_ENDPOINT_FALLBACK_STATUSES.has(response.status)) {
break;
}
}
const response = await fetch(url, {
method: "POST",
headers,
body: JSON.stringify(transformedBody),
signal,
});
if (!response.ok) {
return { response, url, headers, transformedBody };

View File

@@ -1,222 +0,0 @@
/**
* Nimble Web Fetch Executor
*
* Fetches content from a URL using the Nimble Extract API.
* POST https://sdk.nimbleway.com/v1/extract
*
* Extract returns the requested formats side by side under `data`, so one call
* covers every OmniRoute fetch format: `markdown`, `html`, `links` and
* `screenshot` (base64 PNG, surfaced as a data URL).
*
* Docs: https://docs.nimbleway.com/nimble-sdk/web-tools/extract/quickstart
*/
import { sanitizeErrorMessage, buildErrorBody } from "../utils/error.ts";
import { NIMBLE_CLIENT_SOURCE, NIMBLE_CLIENT_SOURCE_HEADER } from "../config/nimble.ts";
import type { WebFetchResult, WebFetchFormat, WebFetchCredentials } from "../handlers/webFetch.ts";
const NIMBLE_EXTRACT_URL = "https://sdk.nimbleway.com/v1/extract";
const NIMBLE_TIMEOUT_MS = 30_000;
/** Max characters kept from an HTML <title> / meta description. */
const META_MAX_CHARS = 500;
// These run over untrusted upstream HTML, so every quantifier is over a character
// class that cannot cross its own terminator — no backtracking (see AGENTS.md →
// "Regex Security"). Length is capped by META_MAX_CHARS after the match, not by the
// quantifier: bounding the capture instead would make an over-long title fail to
// match at all rather than truncate.
const TITLE_RE = /<title[^>]{0,200}>([^<]*)<\/title>/i;
// One pattern per quote style. A shared ["'] class for the closing delimiter would
// cut a double-quoted description at its first apostrophe ("Don't miss…" → "Don").
const META_DESCRIPTION_DOUBLE_RE =
/<meta[^>]{0,200}name=["']description["'][^>]{0,200}content="([^"]*)"/i;
const META_DESCRIPTION_SINGLE_RE =
/<meta[^>]{0,200}name=["']description["'][^>]{0,200}content='([^']*)'/i;
/** Map an OmniRoute fetch format onto the Nimble Extract format name. */
function mapFormat(format: WebFetchFormat): string {
switch (format) {
case "html":
return "html";
case "links":
return "links";
case "screenshot":
return "screenshot";
case "markdown":
default:
return "markdown";
}
}
function readString(source: Record<string, unknown>, key: string): string {
const value = source[key];
return value != null ? String(value) : "";
}
/**
* Pull a title / description out of raw HTML.
* Extract has no dedicated metadata format, so metadata is only available when
* the caller asked for it and we requested `html` alongside their format.
*/
function parseHtmlMetadata(html: string): { title: string | null; description: string | null } {
if (!html) return { title: null, description: null };
const title = TITLE_RE.exec(html)?.[1]?.trim().slice(0, META_MAX_CHARS);
const rawDescription =
META_DESCRIPTION_DOUBLE_RE.exec(html)?.[1] ?? META_DESCRIPTION_SINGLE_RE.exec(html)?.[1];
const description = rawDescription?.trim().slice(0, META_MAX_CHARS);
return {
title: title ? title : null,
description: description ? description : null,
};
}
interface NimbleFetchOptions {
url: string;
format: WebFetchFormat;
includeMetadata: boolean;
credentials: WebFetchCredentials;
}
/**
* Execute a Nimble Extract request.
*/
export async function nimbleFetch(opts: NimbleFetchOptions): Promise<WebFetchResult> {
const { url, format, includeMetadata, credentials } = opts;
if (!credentials.apiKey) {
const body = buildErrorBody(401, "Nimble API key required");
return { success: false, status: 401, error: body.error.message };
}
const requested = mapFormat(format);
// `links` always comes back so WebFetchResponse.links is populated; `html` is
// added only when the caller asked for metadata, since it is the sole source
// of a page title/description.
const formats = [...new Set([requested, "links", ...(includeMetadata ? ["html"] : [])])];
const requestBody: Record<string, unknown> = {
url,
formats,
// Extract types `render` as `boolean | "auto"`; "auto" lets Nimble select the
// driver per target domain rather than forcing a browser on every static page.
render: "auto",
};
const controller = new AbortController();
const timeoutId = setTimeout(() => {
const err = new Error(`nimble-fetch timeout after ${NIMBLE_TIMEOUT_MS}ms`);
err.name = "TimeoutError";
controller.abort(err);
}, NIMBLE_TIMEOUT_MS);
try {
const response = await fetch(NIMBLE_EXTRACT_URL, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${credentials.apiKey}`,
[NIMBLE_CLIENT_SOURCE_HEADER]: NIMBLE_CLIENT_SOURCE,
},
body: JSON.stringify(requestBody),
signal: controller.signal,
});
if (!response.ok) {
// Nimble returns a plain-text body on auth failures, so this must be
// sanitized before it can reach a response.
const rawError = await response.text().catch(() => `HTTP ${response.status}`);
const msg = sanitizeErrorMessage(`Nimble error ${response.status}: ${rawError}`);
const body = buildErrorBody(response.status, msg);
return { success: false, status: response.status, error: body.error.message };
}
const payload = (await response.json()) as Record<string, unknown>;
// A 200 can still carry a failed extraction — Extract reports the target's own
// outcome in the envelope. Without this the caller would get an empty document
// marked successful, and the pool in /v1/web/fetch would never fall through to
// the next provider.
const taskStatus = typeof payload.status === "string" ? payload.status : "";
const taskStatusCode = typeof payload.status_code === "number" ? payload.status_code : null;
if (
(taskStatus && taskStatus !== "success") ||
(taskStatusCode !== null && taskStatusCode >= 400)
) {
const detail = taskStatus || `status ${taskStatusCode}`;
const msg = sanitizeErrorMessage(`Nimble extraction did not succeed: ${detail}`);
const body = buildErrorBody(502, msg);
return { success: false, status: 502, error: body.error.message };
}
const data = (payload.data as Record<string, unknown> | null) ?? {};
const rawLinks = data.links;
const links: string[] = Array.isArray(rawLinks) ? rawLinks.map((l) => String(l)) : [];
const screenshot = readString(data, "screenshot");
const screenshotUrl =
format === "screenshot" && screenshot
? screenshot.startsWith("data:")
? screenshot
: `data:image/png;base64,${screenshot}`
: null;
// The requested format must actually be present. An absent key means Extract
// succeeded but produced nothing for what the caller asked for; returning an
// empty document as a success would stop /v1/web/fetch from trying the next
// provider. An empty *value* is legitimate (a genuinely blank page) and passes.
if (!(requested in data)) {
const msg = sanitizeErrorMessage(`Nimble returned no ${requested} content for the request`);
const body = buildErrorBody(502, msg);
return { success: false, status: 502, error: body.error.message };
}
let content: string;
switch (format) {
case "html":
content = readString(data, "html");
break;
case "links":
content = JSON.stringify(links);
break;
case "screenshot":
content = "";
break;
case "markdown":
default:
content = readString(data, "markdown");
break;
}
const metadata = includeMetadata ? parseHtmlMetadata(readString(data, "html")) : null;
return {
success: true,
data: {
provider: "nimble-search",
url,
content,
links,
metadata,
screenshot_url: screenshotUrl,
},
};
} catch (err: unknown) {
// The abort reason above is named "TimeoutError"; an external abort surfaces as
// "AbortError". Both must reach the 504 branch.
if (err instanceof Error && (err.name === "TimeoutError" || err.name === "AbortError")) {
const body = buildErrorBody(504, "Nimble request timed out");
return { success: false, status: 504, error: body.error.message };
}
const msg =
err instanceof Error ? sanitizeErrorMessage(err.message) : sanitizeErrorMessage(String(err));
const body = buildErrorBody(502, msg);
return { success: false, status: 502, error: body.error.message };
} finally {
clearTimeout(timeoutId);
}
}

View File

@@ -1,313 +0,0 @@
import { randomUUID } from "node:crypto";
import {
BaseExecutor,
mergeUpstreamExtraHeaders,
type ExecuteInput,
type ProviderCredentials,
} from "./base.ts";
import { PROVIDERS } from "../config/constants.ts";
import { buildErrorBody } from "../utils/error.ts";
type JsonRecord = Record<string, unknown>;
type OpenAIMessage = {
role?: string;
content?: unknown;
};
const CHAT_URL = "https://api.1min.ai/api/chat-with-ai";
const ROLE_LABELS: Record<string, string> = {
system: "System",
developer: "System",
user: "User",
assistant: "Assistant",
};
function asRecord(value: unknown): JsonRecord {
return value && typeof value === "object" && !Array.isArray(value) ? (value as JsonRecord) : {};
}
function extractTextContent(content: unknown): string {
if (typeof content === "string") return content;
if (!Array.isArray(content)) return "";
return content
.map((part) => {
if (!part || typeof part !== "object") return "";
const item = part as Record<string, unknown>;
return item.type === "text" && typeof item.text === "string" ? item.text : "";
})
.filter((text) => text.length > 0)
.join("\n");
}
/**
* 1min.ai's Chat with AI API takes one `promptObject.prompt` string, not an
* OpenAI `messages` array — multi-turn context is normally carried server-side
* via `promptObject.conversationId` (see docs.1min.ai/docs/api/chat-with-ai-api),
* which requires a prior POST /api/conversations call and a stable conversation
* identity that stateless OpenAI-compatible clients don't provide. Rather than
* half-implement that, a single user message passes through unchanged and
* multi-turn history is flattened into a labeled transcript.
*/
export function buildPrompt(messages: OpenAIMessage[] | undefined): string {
const list = Array.isArray(messages) ? messages : [];
if (list.length === 1 && list[0]?.role === "user") {
return extractTextContent(list[0].content);
}
return list
.map((message) => {
const role = String(message?.role || "user").toLowerCase();
const text = extractTextContent(message?.content);
const label = ROLE_LABELS[role] || role;
return `${label}: ${text}`;
})
.filter((line) => line.length > 0)
.join("\n\n");
}
function buildSseChunk(data: unknown): string {
return `data: ${JSON.stringify(data)}\n\n`;
}
function buildOpenAiJsonCompletion(content: string, model: string, id: string, created: number): Response {
return new Response(
JSON.stringify({
id,
object: "chat.completion",
created,
model,
choices: [{ index: 0, message: { role: "assistant", content }, finish_reason: "stop" }],
// 1min.ai's response shape carries no token-usage fields.
usage: { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 },
}),
{ status: 200, headers: { "Content-Type": "application/json" } }
);
}
function toOpenAiErrorResponse(status: number, message: string, upstreamDetails?: unknown): Response {
return new Response(JSON.stringify(buildErrorBody(status, message, upstreamDetails)), {
status,
headers: { "Content-Type": "application/json" },
});
}
/**
* Parse 1min.ai's real Server-Sent Events (event: content|result|done|error,
* data: {...}) from the upstream Response body and re-emit them as standard
* OpenAI chat.completion.chunk SSE.
*/
function translateSseStream(upstreamBody: ReadableStream<Uint8Array>, model: string, id: string, created: number): ReadableStream<Uint8Array> {
const decoder = new TextDecoder();
const encoder = new TextEncoder();
return new ReadableStream<Uint8Array>({
async start(controller) {
controller.enqueue(
encoder.encode(
buildSseChunk({
id,
object: "chat.completion.chunk",
created,
model,
choices: [{ index: 0, delta: { role: "assistant" }, finish_reason: null }],
})
)
);
const reader = upstreamBody.getReader();
let buffer = "";
let finished = false;
const finish = () => {
if (finished) return;
finished = true;
controller.enqueue(
encoder.encode(
buildSseChunk({
id,
object: "chat.completion.chunk",
created,
model,
choices: [{ index: 0, delta: {}, finish_reason: "stop" }],
})
)
);
controller.enqueue(encoder.encode("data: [DONE]\n\n"));
controller.close();
};
const emitContent = (text: string) => {
if (!text) return;
controller.enqueue(
encoder.encode(
buildSseChunk({
id,
object: "chat.completion.chunk",
created,
model,
choices: [{ index: 0, delta: { content: text }, finish_reason: null }],
})
)
);
};
// SSE event framing: "event:"/"data:" lines, blank-line separated records.
const processEvent = (eventText: string) => {
let eventType = "message";
const dataLines: string[] = [];
for (const rawLine of eventText.split("\n")) {
if (rawLine.startsWith("event:")) {
eventType = rawLine.slice(6).trim();
} else if (rawLine.startsWith("data:")) {
dataLines.push(rawLine.slice(5).trim());
}
}
const data = dataLines.join("\n");
if (eventType === "content") {
try {
const parsed = asRecord(JSON.parse(data));
if (typeof parsed.content === "string") emitContent(parsed.content);
} catch {
// Ignore malformed content events rather than surfacing partial JSON.
}
} else if (eventType === "error") {
emitContent(`\n[1min.ai error: ${data}]`);
finish();
} else if (eventType === "done") {
finish();
}
// "result" carries the final full aiRecord, redundant with the content
// events already streamed — intentionally ignored.
};
try {
while (!finished) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
let separatorIndex = buffer.indexOf("\n\n");
while (separatorIndex !== -1) {
processEvent(buffer.slice(0, separatorIndex));
buffer = buffer.slice(separatorIndex + 2);
separatorIndex = buffer.indexOf("\n\n");
}
}
if (!finished && buffer.trim()) processEvent(buffer);
finish();
} catch (error) {
controller.error(error);
} finally {
reader.releaseLock();
}
},
});
}
export class OneMinAiExecutor extends BaseExecutor {
constructor() {
super("oneminai", PROVIDERS["oneminai"] || { format: "openai", baseUrl: CHAT_URL });
}
buildUrl(_model: string, stream: boolean): string {
return stream ? `${CHAT_URL}?isStreaming=true` : CHAT_URL;
}
buildHeaders(credentials: ProviderCredentials | null): Record<string, string> {
const key = credentials?.apiKey || credentials?.accessToken || "";
return {
"Content-Type": "application/json",
"API-KEY": key,
};
}
transformRequest(model: string, body: unknown): JsonRecord {
const payload = asRecord(body);
const messages = Array.isArray(payload.messages) ? (payload.messages as OpenAIMessage[]) : [];
return {
type: "UNIFY_CHAT_WITH_AI",
model,
promptObject: { prompt: buildPrompt(messages) },
};
}
async execute({ model, body, stream, credentials, signal, upstreamExtraHeaders }: ExecuteInput) {
const url = this.buildUrl(model, stream);
const headers = this.buildHeaders(credentials);
mergeUpstreamExtraHeaders(headers, upstreamExtraHeaders);
const payload = this.transformRequest(model, body);
const id = `chatcmpl-oneminai-${randomUUID()}`;
const created = Math.floor(Date.now() / 1000);
try {
this.assertOutboundUrlAllowed(url);
const response = await fetch(url, {
method: "POST",
headers,
body: JSON.stringify(payload),
signal,
});
if (!response.ok) {
const errorText = await response.text();
let message = `1min.ai API failed with status ${response.status}`;
try {
const parsed = asRecord(JSON.parse(errorText));
const err = asRecord(parsed.error);
if (typeof err.message === "string") message = err.message;
} catch {
if (errorText) message = errorText;
}
return {
response: toOpenAiErrorResponse(response.status, message),
url,
headers,
transformedBody: payload,
};
}
if (stream) {
if (!response.body) {
return {
response: toOpenAiErrorResponse(502, "1min.ai returned an empty stream"),
url,
headers,
transformedBody: payload,
};
}
return {
response: new Response(translateSseStream(response.body, model, id, created), {
status: 200,
headers: { "Content-Type": "text/event-stream" },
}),
url,
headers,
transformedBody: payload,
};
}
const json = asRecord(await response.json());
const aiRecord = asRecord(json.aiRecord);
const detail = asRecord(aiRecord.aiRecordDetail);
const resultObject = Array.isArray(detail.resultObject) ? detail.resultObject : [];
const content = resultObject.filter((part): part is string => typeof part === "string").join("");
return {
response: buildOpenAiJsonCompletion(content, model, id, created),
url,
headers,
transformedBody: payload,
};
} catch (error) {
const message = error instanceof Error ? error.message : String(error || "Unknown error");
return {
response: toOpenAiErrorResponse(502, `1min.ai fetch error: ${message}`),
url,
headers,
transformedBody: payload,
};
}
}
}
export default OneMinAiExecutor;

View File

@@ -90,19 +90,13 @@ async function resolveZaiBrowserAttachments(
> {
try {
// Browser-page upload: keep the original bytes/mimeType (no Cursor wire prep).
// EncodedImage.mimeType is optional on the wire type, but every producer
// reachable here (decodeDataUrl / fetchImageBytes) validates an image/*
// string before pushing; the fallback only satisfies the attachment type.
const images = await resolveCursorImages(imageUrls, { prepareForWire: false });
return {
attachments: images.map((image, index) => {
const mimeType = image.mimeType ?? "image/jpeg";
return {
name: zaiImageFileName(mimeType, index),
mimeType,
buffer: image.data,
};
}),
attachments: images.map((image, index) => ({
name: zaiImageFileName(image.mimeType, index),
mimeType: image.mimeType,
buffer: image.data,
})),
};
} catch (error) {
const message =

View File

@@ -704,9 +704,8 @@ export async function handleChatCore({
const recordKeyHealthStatus = (
status: number,
creds: Record<string, unknown> | null | undefined,
transport?: string,
failureDetail?: string
): void => recordKeyHealthStatusFor(status, creds, log, transport, failureDetail);
transport?: string
): void => recordKeyHealthStatusFor(status, creds, log, transport);
// ── Phase 9.2: Idempotency check ──
// Resolve the idempotency key once here and reuse it at the Phase 9.2 save site below,
// rather than re-deriving it. (#3821-review LEDGER-6)
@@ -2984,10 +2983,7 @@ export async function handleChatCore({
const dedupRequestBody = { ...translatedBody, model: `${provider}/${model}`, stream };
const dedupEnabled = shouldDeduplicate(dedupRequestBody);
// Namespaced by the calling API key: dedup hands the SAME response object to
// every joiner, so a shared hash across keys is a cross-principal response
// leak (GHSA-6c7w-56xp-wpc6).
const dedupHash = dedupEnabled ? computeRequestHash(dedupRequestBody, apiKeyInfo?.id) : null;
const dedupHash = dedupEnabled ? computeRequestHash(dedupRequestBody) : null;
const executeProviderRequest = async (modelToCall = effectiveModel, allowDedup = false) => {
const execute = async () => {
@@ -3178,20 +3174,11 @@ export async function handleChatCore({
});
if (
stream &&
(res.response.ok ||
res.response.status === HTTP_STATUS.UNAUTHORIZED ||
res.response.status === HTTP_STATUS.FORBIDDEN) &&
res.response.status === 401 &&
executionConnectionId &&
!(await shouldIsolateProbeFailures())
) {
const failureDetail = res.response.ok
? ""
: await res.response
.clone()
.text()
.catch(() => "");
recordKeyHealthStatus(res.response.status, execCreds, res.transport, failureDetail);
recordKeyHealthStatus(401, execCreds);
}
if (isModelScope() && res.response.status === 429 && attempts < maxAttempts - 1) {
@@ -3542,6 +3529,13 @@ export async function handleChatCore({
// Non-stream: release semaphore immediately after reading full response body.
const status = rawResult.response.status;
// Use execution credentials captured during request processing
if (
rawResult._executionCredentials?.connectionId &&
rawResult._executionCredentials?.apiKey
) {
recordKeyHealthStatus(status, rawResult._executionCredentials, rawResult.transport);
}
releaseRawResultAccountSemaphore =
typeof rawResult._accountSemaphoreRelease === "function"
? rawResult._accountSemaphoreRelease
@@ -3567,19 +3561,6 @@ export async function handleChatCore({
contentType,
upstreamStream
);
// Use the exact execution credential selected for this request. Model capability
// failures stay in routing telemetry; authoritative success only recovers this key.
if (
rawResult._executionCredentials?.connectionId &&
(rawResult._executionCredentials.apiKey || rawResult._executionCredentials.accessToken)
) {
recordKeyHealthStatus(
status,
rawResult._executionCredentials,
rawResult.transport,
status >= 400 ? payload : ""
);
}
releaseRawResultAccountSemaphore();
releaseRawResultAccountSemaphore = () => {};
@@ -4260,84 +4241,79 @@ export async function handleChatCore({
`[provider] Node ${errorConnectionId} probe ${errorType} (${statusCode}) — connection stays active`
);
} else {
// Kimi's 403 says "billing cycle" for both an exhausted subscription and a
// temporary request window. Read its official usage endpoint before making
// the connection terminal: a non-zero Weekly quota plus an empty Ratelimit
// window must recover automatically at the reported reset time.
let kimiRateLimitResetAt: string | null = null;
if (provider === "kimi-coding") {
try {
const { fetchAndPersistProviderLimits } =
await import("@/lib/usage/providerLimits");
const { usage } = await fetchAndPersistProviderLimits(
errorConnectionId,
"manual"
);
kimiRateLimitResetAt = getKimiTemporaryRateLimitResetAt(usage);
} catch {
// Preserve the existing quota handling when Kimi's usage endpoint is unavailable.
}
// Kimi's 403 says "billing cycle" for both an exhausted subscription and a
// temporary request window. Read its official usage endpoint before making
// the connection terminal: a non-zero Weekly quota plus an empty Ratelimit
// window must recover automatically at the reported reset time.
let kimiRateLimitResetAt: string | null = null;
if (provider === "kimi-coding") {
try {
const { fetchAndPersistProviderLimits } =
await import("@/lib/usage/providerLimits");
const { usage } = await fetchAndPersistProviderLimits(errorConnectionId, "manual");
kimiRateLimitResetAt = getKimiTemporaryRateLimitResetAt(usage);
} catch {
// Preserve the existing quota handling when Kimi's usage endpoint is unavailable.
}
}
// Providers with per-model quotas — lock the model only, not the connection
const quotaCooldownMs = kimiRateLimitResetAt
? Math.max(new Date(kimiRateLimitResetAt).getTime() - Date.now(), 0)
: retryAfterMs || COOLDOWN_MS.rateLimit;
const accountSemaphoreKey = resolveAccountSemaphoreKey({
provider,
model: currentModel,
connectionId: errorConnectionId,
credentials,
// Providers with per-model quotas — lock the model only, not the connection
const quotaCooldownMs = kimiRateLimitResetAt
? Math.max(new Date(kimiRateLimitResetAt).getTime() - Date.now(), 0)
: retryAfterMs || COOLDOWN_MS.rateLimit;
const accountSemaphoreKey = resolveAccountSemaphoreKey({
provider,
model: currentModel,
connectionId: errorConnectionId,
credentials,
});
if (accountSemaphoreKey) {
markAccountSemaphoreBlocked(accountSemaphoreKey, quotaCooldownMs);
}
if (kimiRateLimitResetAt) {
await updateProviderConnection(errorConnectionId, {
testStatus: "unavailable",
rateLimitedUntil: kimiRateLimitResetAt,
backoffLevel: 0,
lastErrorType: PROVIDER_ERROR_TYPES.RATE_LIMITED,
lastError: message,
errorCode: statusCode,
});
if (accountSemaphoreKey) {
markAccountSemaphoreBlocked(accountSemaphoreKey, quotaCooldownMs);
}
if (kimiRateLimitResetAt) {
await updateProviderConnection(errorConnectionId, {
testStatus: "unavailable",
rateLimitedUntil: kimiRateLimitResetAt,
backoffLevel: 0,
lastErrorType: PROVIDER_ERROR_TYPES.RATE_LIMITED,
console.warn(
`[provider] Node ${errorConnectionId} Kimi request window exhausted (${statusCode}) — retrying after ${kimiRateLimitResetAt}`
);
} else if (isModelScope() && errorConnectionId) {
const lockFn = provider === "antigravity" ? lockExactModel : lockModel;
lockFn(provider, errorConnectionId, model, "quota_exhausted", quotaCooldownMs);
console.warn(
`[provider] Node ${errorConnectionId} ModelScope model quota exhausted (${statusCode}) for ${model} - ${Math.ceil(quotaCooldownMs / 1000)}s (connection stays active)`
);
} else if (
lockModelIfPerModelQuota(
provider,
errorConnectionId,
model,
"quota_exhausted",
quotaCooldownMs
)
) {
const quotaScope = getQuotaScopeLabelForProvider(provider, model);
console.warn(
`[provider] Node ${errorConnectionId} ${quotaScope}-only quota exhausted (${statusCode}) for ${model} - ${Math.ceil(quotaCooldownMs / 1000)}s (cooldown_scope=${quotaScope}, ttl_source=${retryAfterMs ? "upstream" : "inferred"}, connection stays active)`
);
} else {
await writeTerminalStatus(
errorConnectionId,
{
testStatus: "credits_exhausted",
lastError: message,
errorCode: statusCode,
});
console.warn(
`[provider] Node ${errorConnectionId} Kimi request window exhausted (${statusCode}) — retrying after ${kimiRateLimitResetAt}`
);
} else if (isModelScope() && errorConnectionId) {
const lockFn = provider === "antigravity" ? lockExactModel : lockModel;
lockFn(provider, errorConnectionId, model, "quota_exhausted", quotaCooldownMs);
console.warn(
`[provider] Node ${errorConnectionId} ModelScope model quota exhausted (${statusCode}) for ${model} - ${Math.ceil(quotaCooldownMs / 1000)}s (connection stays active)`
);
} else if (
lockModelIfPerModelQuota(
provider,
errorConnectionId,
model,
"quota_exhausted",
quotaCooldownMs
)
) {
const quotaScope = getQuotaScopeLabelForProvider(provider, model);
console.warn(
`[provider] Node ${errorConnectionId} ${quotaScope}-only quota exhausted (${statusCode}) for ${model} - ${Math.ceil(quotaCooldownMs / 1000)}s (cooldown_scope=${quotaScope}, ttl_source=${retryAfterMs ? "upstream" : "inferred"}, connection stays active)`
);
} else {
await writeTerminalStatus(
errorConnectionId,
{
testStatus: "credits_exhausted",
lastError: message,
lastErrorType: errorType,
errorCode: String(statusCode),
},
"production"
);
console.warn(
`[provider] Node ${errorConnectionId} exhausted quota (${statusCode})`
);
}
lastErrorType: errorType,
errorCode: String(statusCode),
},
"production"
);
console.warn(`[provider] Node ${errorConnectionId} exhausted quota (${statusCode})`);
}
} // close probeIsolated3 else
}
} else if (errorType === PROVIDER_ERROR_TYPES.UNAUTHORIZED) {

View File

@@ -6,13 +6,12 @@
* handleChatCore. Translates an upstream HTTP status into the in-memory key-health state
* (apiKeyRotator) for the connection's currently-selected key, and persists the change to the
* provider connection so it survives process restarts:
* - genuine 401/403 credential rejection → record a failure (warning, then invalid at the
* threshold), always persisted.
* - 401 → record a failure (warning, then invalid at the threshold), always persisted.
* - 402 → terminal (insufficient balance); mark the current key invalid immediately (#5239),
* persisted on the active→invalid transition.
* - 2xx → record a success, persisted only when recovering from a warning/invalid state.
* Model availability failures remain model/routing telemetry even when an upstream reports them
* with 401/403. Any other status only refreshes the tracked extra-key set.
* Any other status only refreshes the tracked extra-key set. The handler binds its `log` once and
* delegates here, keeping the existing call sites unchanged.
*/
import {
@@ -22,7 +21,6 @@ import {
trackConnectionExtraKeys,
type KeyHealth,
} from "../../services/apiKeyRotator.ts";
import { isModelUnavailableError } from "../../services/modelFamilyFallback.ts";
import { updateProviderConnection } from "@/lib/db/providers";
type KeyHealthLog = {
@@ -30,38 +28,11 @@ type KeyHealthLog = {
error?: (tag: string, message: string) => void;
} | null;
const CREDENTIAL_FAILURE_PATTERNS = [
/\b(?:invalid|incorrect|expired|missing|revoked)\s+api[\s_-]?key\b/i,
/\bapi[\s_-]?key\s+(?:is\s+)?(?:invalid|incorrect|expired|missing|revoked|not\s+valid)\b/i,
/\bauthentication[\s_-]+(?:failed|error|required)\b/i,
/\b(?:invalid|expired|missing|revoked)\s+(?:token|credentials?|bearer)\b/i,
/\bunauthorized\b/i,
/\bnot\s+authenticated\b/i,
/\bforbidden\b/i,
/\baccess\s+denied\b/i,
];
function isModelCapabilityFailure(status: number, failureDetail: string): boolean {
if (!failureDetail) return false;
const normalizedDetail = failureDetail.replace(/[_-]+/g, " ");
// Model-family fallback already owns these phrases. Use a model-capable status for
// classification because some aggregators misreport the same model rejection as 401.
return isModelUnavailableError(status === 401 ? 403 : status, normalizedDetail);
}
function isCredentialFailure(status: number, failureDetail: string): boolean {
if (status !== 401 && status !== 403) return false;
if (isModelCapabilityFailure(status, failureDetail)) return false;
if (status === 401) return true;
return CREDENTIAL_FAILURE_PATTERNS.some((pattern) => pattern.test(failureDetail));
}
export function recordKeyHealthStatus(
status: number,
creds: Record<string, unknown> | null | undefined,
log?: KeyHealthLog,
transport?: string,
failureDetail = ""
transport?: string
): void {
// CLIProxyAPI owns a shared external credential pool. Its auth failures cannot be
// attributed to the native OmniRoute connection selected before proxy dispatch.
@@ -84,11 +55,11 @@ export function recordKeyHealthStatus(
trackConnectionExtraKeys(connId, extraKeys);
if (isCredentialFailure(status, failureDetail)) {
if (status === 401) {
const updatedHealth = recordKeyFailure(connId, currentKeyId);
log?.warn?.(
"AUTH",
`${status} on connection ${connId.slice(0, 8)} - key marked as failed (failure #${updatedHealth.failures})`
`401 on connection ${connId.slice(0, 8)} - key marked as failed (failure #${updatedHealth.failures})`
);
// Persist health status to DB on every failure (not just invalid transitions)

View File

@@ -9,7 +9,6 @@ import { randomUUID } from "crypto";
* youcom-search, searxng-search, ollama-search, zai-search, jina-search,
* duckduckgo-free, x-search (Grok / SuperGrok X Search — explicit or search_type "x")
* and xquik-search (direct X API search — explicit or credentialed fallback)
* and anysearch-search (free public web search — fallback-only)
*
* Request format:
* {
@@ -25,14 +24,12 @@ import {
isUnconfiguredLoopbackSearchProvider,
type SearchProviderConfig,
} from "../config/searchRegistry.ts";
import { NIMBLE_CLIENT_SOURCE, NIMBLE_CLIENT_SOURCE_HEADER } from "../config/nimble.ts";
import { buildPerplexityRequest, parsePerplexitySearchOptions } from "./search/perplexitySearch.ts";
import * as fcSearch from "./search/firecrawlSearch.ts";
import { type FirecrawlSearchEnvelope } from "./search/firecrawlSearch.ts";
import { buildJinaSearchRequest, extractJinaSearchItems } from "./search/jinaSearch.ts";
import * as xSearch from "./search/xSearch.ts";
import * as xquikSearch from "./search/xquikSearch.ts";
import * as anysearchSearch from "./search/anysearchSearch.ts";
import { freeWebSearch } from "../services/freeWebSearch.ts";
import { saveCallLog } from "@/lib/usageDb";
import { safeOutboundFetch } from "@/shared/network/safeOutboundFetch";
@@ -478,38 +475,6 @@ function buildTavilyRequest(
};
}
function buildNimbleRequest(
config: SearchProviderConfig,
params: SearchRequestParams
): { url: string; init: RequestInit } {
if (!params.token) throw new Error("Nimble Search requires an API key");
const { includes, excludes } = parseDomainFilter(params.domainFilter);
const body: Record<string, unknown> = {
query: params.query,
max_results: Math.min(params.maxResults, config.maxMaxResults),
search_depth: "lite",
output_format: "plain_text",
focus: params.searchType === "news" ? "news" : "general",
};
if (params.country) body.country = params.country.toUpperCase();
if (params.language) body.locale = params.language;
if (params.timeRange && params.timeRange !== "any") body.time_range = params.timeRange;
if (includes.length) body.include_domains = includes.slice(0, 50);
if (excludes.length) body.exclude_domains = excludes.slice(0, 50);
return {
url: resolveSearchBaseUrl(config, params),
init: {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${params.token}`,
[NIMBLE_CLIENT_SOURCE_HEADER]: NIMBLE_CLIENT_SOURCE,
},
body: JSON.stringify(body),
},
};
}
function buildGooglePseRequest(
config: SearchProviderConfig,
params: SearchRequestParams
@@ -742,7 +707,6 @@ const requestBuilders: Record<string, SearchRequestBuilder> = {
"perplexity-search": buildPerplexityRequest,
"exa-search": buildExaRequest,
"tavily-search": buildTavilyRequest,
"nimble-search": buildNimbleRequest,
firecrawl: fcSearch.buildFirecrawlSearchRequest,
"google-pse-search": buildGooglePseRequest,
"linkup-search": buildLinkupRequest,
@@ -753,7 +717,6 @@ const requestBuilders: Record<string, SearchRequestBuilder> = {
"jina-search": buildJinaSearchRequest,
"x-search": xSearch.buildXSearchRequest,
"xquik-search": xquikSearch.buildXquikSearchRequest,
"anysearch-search": anysearchSearch.buildAnysearchSearchRequest,
};
function buildRequest(
@@ -865,47 +828,6 @@ function normalizeTavilyResponse(
return { results, totalResults: results.length };
}
interface NimbleSearchItem {
title?: string;
url?: string;
description?: string;
content?: string;
}
interface NimbleSearchEnvelope {
results?: NimbleSearchItem[];
total_results?: number;
}
function normalizeNimbleResponse(
data: unknown,
_query: string,
_searchType: string
): { results: SearchResult[]; totalResults: number | null } {
const now = new Date().toISOString();
const envelope = (data ?? {}) as NimbleSearchEnvelope;
if (!Array.isArray(envelope.results)) return { results: [], totalResults: null };
const results = envelope.results.map((item, idx) =>
makeResult(
"nimble-search",
{
title: item.title,
url: item.url,
snippet: item.description || item.content?.slice(0, 300) || "",
full_text: item.content || undefined,
text_format: "text",
},
idx,
now
)
);
return {
results,
totalResults:
typeof envelope.total_results === "number" ? envelope.total_results : results.length,
};
}
function normalizeGooglePseResponse(
data: any,
_query: string,
@@ -1361,7 +1283,6 @@ const responseNormalizers: Record<string, SearchResponseNormalizer> = {
"perplexity-search": normalizePerplexityResponse,
"exa-search": normalizeExaResponse,
"tavily-search": normalizeTavilyResponse,
"nimble-search": normalizeNimbleResponse,
firecrawl: (data: FirecrawlSearchEnvelope, _query: string, searchType: string) =>
fcSearch.normalizeFirecrawlSearchResponse(data, searchType, makeResult),
"google-pse-search": normalizeGooglePseResponse,
@@ -1373,7 +1294,6 @@ const responseNormalizers: Record<string, SearchResponseNormalizer> = {
"jina-search": normalizeJinaSearchResponse,
"x-search": normalizeXSearchResponse,
"xquik-search": (data) => xquikSearch.normalizeXquikSearchResponse(data, makeResult),
"anysearch-search": (data) => anysearchSearch.normalizeAnysearchSearchResponse(data, makeResult),
};
function normalizeResponse(

View File

@@ -1,167 +0,0 @@
/** AnySearch-backed web search for the unified search gateway. */
import { z } from "zod";
import type { SearchProviderConfig } from "../../config/searchRegistry.ts";
import type { SearchResult } from "../search.ts";
export const ANYSEARCH_SEARCH_PROVIDER_ID = "anysearch-search";
/**
* Thrown by normalizeAnysearchSearchResponse when the upstream envelope
* reports failure: HTTP 200 with `{ code: -1, error_code?, message? }`.
* A quota-shaped message maps to 402 (failover, mirroring the webFetch
* QUOTA_STATUS_PROVIDERS pattern); any other non-zero code maps to 502
* instead of silently degrading to an empty result set.
*/
export class AnysearchSearchEnvelopeError extends Error {
constructor(
public readonly code: number,
public readonly quota: boolean,
message: string
) {
super(message);
this.name = "AnysearchSearchEnvelopeError";
}
}
const QUOTA_SIGNAL = /quota|exceed|limit|balance|credit|exhaust/i;
export function detectAnysearchEnvelopeError(data: unknown): AnysearchSearchEnvelopeError | null {
if (!data || typeof data !== "object" || Array.isArray(data)) return null;
const record = data as Record<string, unknown>;
const code = record.code;
if (typeof code !== "number" || code === 0) return null;
const message =
(typeof record.message === "string" && record.message) ||
(typeof record.error === "string" && record.error) ||
(typeof record.error_code === "string" && record.error_code) ||
`AnySearch envelope error (code ${code})`;
return new AnysearchSearchEnvelopeError(code, QUOTA_SIGNAL.test(message), message);
}
export interface AnysearchSearchParams {
query: string;
maxResults: number;
token?: string;
providerOptions?: Record<string, unknown>;
providerSpecificData?: Record<string, unknown>;
}
export interface AnysearchSearchItem {
title?: string;
url: string;
snippet?: string;
score?: number;
publishedAt?: string;
}
type MakeResult = (
providerId: string,
item: {
title?: string;
url?: string;
snippet?: string;
score?: number;
published_at?: string;
source_type?: string;
},
index: number,
now: string
) => SearchResult;
// Upstream REST envelope: { code: 0, message: "success", data: { results: [...] } }
// with a server-generated request_id echoed in the X-Request-ID header. The
// published docs truncate before pinning the exact results field name, so
// extraction tolerates the plausible shapes and skips rows that cannot become
// citations (no url).
const AnysearchItemSchema = z
.object({
title: z.string().optional(),
url: z.string().optional(),
snippet: z.string().optional(),
summary: z.string().optional(),
score: z.number().optional(),
date: z.string().optional(),
published_at: z.string().optional(),
})
.passthrough();
export function extractAnysearchSearchItems(
data: unknown,
maxResults: number
): AnysearchSearchItem[] {
if (!data || typeof data !== "object") return [];
const record = data as Record<string, unknown>;
const inner =
record.data && typeof record.data === "object" && !Array.isArray(record.data)
? (record.data as Record<string, unknown>)
: {};
const candidates: unknown[] = [
inner.results,
record.results,
inner.items,
record.items,
Array.isArray(record.data) ? record.data : undefined,
];
const rows = candidates.find((c): c is unknown[] => Array.isArray(c)) ?? [];
const items: AnysearchSearchItem[] = [];
for (const row of rows) {
const parsed = AnysearchItemSchema.safeParse(row);
if (!parsed.success || !parsed.data.url) continue;
items.push({
title: parsed.data.title,
url: parsed.data.url,
snippet: parsed.data.snippet ?? parsed.data.summary,
score: parsed.data.score,
publishedAt: parsed.data.published_at ?? parsed.data.date,
});
if (items.length >= maxResults) break;
}
return items;
}
export function buildAnysearchSearchRequest(
config: SearchProviderConfig,
params: AnysearchSearchParams
): { url: string; init: RequestInit } {
// Upstream hard cap: max_results 1-10.
const maxResults = Math.min(Math.max(Math.trunc(params.maxResults) || 5, 1), 10);
return {
url: config.baseUrl.replace(/\/+$/, ""),
init: {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
...(params.token ? { Authorization: `Bearer ${params.token}` } : {}),
},
body: JSON.stringify({ query: params.query, max_results: maxResults }),
},
};
}
export function normalizeAnysearchSearchResponse(
data: unknown,
makeResult: MakeResult
): { results: SearchResult[]; totalResults: number } {
const envelopeError = detectAnysearchEnvelopeError(data);
if (envelopeError) throw envelopeError;
const now = new Date().toISOString();
const items = extractAnysearchSearchItems(data, 10);
const results = items.map((item, index) =>
makeResult(
ANYSEARCH_SEARCH_PROVIDER_ID,
{
title: item.title || item.url,
url: item.url,
snippet: item.snippet ?? "",
score: item.score,
published_at: item.publishedAt,
source_type: "web",
},
index,
now
)
);
return { results, totalResults: results.length };
}

View File

@@ -119,11 +119,7 @@ export interface ProviderFetchResult {
results: SearchResult[];
answer: null;
usage: { queries_used: number; search_cost_usd: number };
metrics: {
response_time_ms: number;
upstream_latency_ms: number;
total_results_available: number | null;
};
metrics: { response_time_ms: number; upstream_latency_ms: number; total_results_available: number | null };
errors: [];
};
}
@@ -164,9 +160,7 @@ export interface ExecuteProviderFetchParams {
* This is the single chokepoint tryProvider() delegates to after building
* the request and resolving the proxy — keeps search.ts to wiring only.
*/
export async function executeProviderFetch(
p: ExecuteProviderFetchParams
): Promise<ProviderFetchResult> {
export async function executeProviderFetch(p: ExecuteProviderFetchParams): Promise<ProviderFetchResult> {
const { config, url, init, controller, timer, query, searchType, maxResults, startTime } = p;
const { connectionId, proxy, proxyLevel, log, normalize } = p;
const emitEvent = (status: string) =>
@@ -196,11 +190,7 @@ export async function executeProviderFetch(
if (log) {
log.error("SEARCH", `${config.id} error ${response.status}: ${errorText.slice(0, 200)}`);
}
logCall({
status: response.status,
duration: Date.now() - startTime,
error: errorText.slice(0, 500),
});
logCall({ status: response.status, duration: Date.now() - startTime, error: errorText.slice(0, 500) });
await emitEvent("error");
return {
success: false,
@@ -241,20 +231,6 @@ export async function executeProviderFetch(
} catch (err: unknown) {
clearTimeout(timer);
const error = err instanceof Error ? err : new Error(String(err));
// Envelope-level provider failure surfaced by a normalizer (e.g. AnySearch
// `{ code: -1 }`): not a transport fault. Quota signals map to 402 so
// quota-aware failover treats them as exhausted; anything else is 502.
if (error.name === "AnysearchSearchEnvelopeError") {
const quota = (error as { quota?: boolean }).quota === true;
const status = quota ? 402 : 502;
const safeMsg = sanitizeErrorMessage(error.message) || "provider envelope error";
if (log) {
log.error("SEARCH", `${config.id} envelope error: ${safeMsg}`);
}
logCall({ status, duration: Date.now() - startTime, error: safeMsg });
await emitEvent("error");
return { success: false, status, error: `Search provider ${config.id}: ${safeMsg}` };
}
const isTimeout = error.name === "AbortError";
const safeMsg = sanitizeErrorMessage(error.message) || "fetch failed";
if (log) {

View File

@@ -3,7 +3,6 @@
*
* Handles POST /v1/web/fetch requests.
* Dispatches to a web-fetch provider executor (Firecrawl, Jina Reader, Tavily, or TinyFish).
* Also AnySearch extract for free public fetch.
*
* Request format:
* {
@@ -22,21 +21,12 @@ import { firecrawlFetch } from "../executors/firecrawl-fetch.ts";
import { jinaReaderFetch } from "../executors/jina-reader-fetch.ts";
import { tavilyFetch } from "../executors/tavily-fetch.ts";
import { tinyfishFetch } from "../executors/tinyfish-fetch.ts";
import { nimbleFetch } from "../executors/nimble-fetch.ts";
import { anysearchFetch } from "../executors/anysearch-fetch.ts";
export type WebFetchFormat = "markdown" | "html" | "links" | "screenshot";
export interface WebFetchRequest {
url: string;
provider?:
| "firecrawl"
| "jina-reader"
| "tavily-search"
| "tinyfish"
| "context7"
| "nimble-search"
| "anysearch-search";
provider?: "firecrawl" | "jina-reader" | "tavily-search" | "tinyfish" | "context7";
format?: WebFetchFormat;
depth?: 0 | 1 | 2;
wait_for_selector?: string;
@@ -70,9 +60,7 @@ export const WEB_FETCH_PROVIDERS = Object.freeze([
"jina-reader",
"tavily-search",
"tinyfish",
"anysearch-search",
"context7",
"nimble-search",
] as const);
// Derived from the array — adding a provider to WEB_FETCH_PROVIDERS
// automatically widens the union; they cannot drift apart.
@@ -150,21 +138,6 @@ export async function handleWebFetch(
includeMetadata,
credentials,
});
case "anysearch-search":
return await anysearchFetch({
url: req.url,
format,
includeMetadata,
credentials,
});
case "nimble-search":
return await nimbleFetch({
url: req.url,
format,
includeMetadata,
credentials,
});
case "context7":
// Context7 returns llms.txt text only: html/links/screenshot formats are

View File

@@ -509,7 +509,7 @@ export const webSearchOutput = z.object({
export const webSearchTool: McpToolDefinition<typeof webSearchInput, typeof webSearchOutput> = {
name: "omniroute_web_search",
description:
"Performs a web search using OmniRoute's search gateway. Supports multiple providers (Serper, Brave, Perplexity, Exa, Tavily, AnySearch, Google PSE, Linkup, SearchAPI, SearXNG) with automatic failover. Returns search results with titles, URLs, snippets, and position data. Not X/Twitter — use omniroute_x_search for that.",
"Performs a web search using OmniRoute's search gateway. Supports multiple providers (Serper, Brave, Perplexity, Exa, Tavily, Google PSE, Linkup, SearchAPI, SearXNG) with automatic failover. Returns search results with titles, URLs, snippets, and position data. Not X/Twitter — use omniroute_x_search for that.",
inputSchema: webSearchInput,
outputSchema: webSearchOutput,
scopes: ["execute:search"],
@@ -557,15 +557,7 @@ export const webFetchInput = z.object({
.min(1, "URL is required")
.describe("The URL to fetch content from"),
provider: z
.enum([
"firecrawl",
"jina-reader",
"tavily-search",
"tinyfish",
"context7",
"nimble-search",
"anysearch-search",
])
.enum(["firecrawl", "jina-reader", "tavily-search", "tinyfish", "context7"])
.optional()
.describe(
"Specific fetch provider to use (default: first available). " +

View File

@@ -694,14 +694,7 @@ async function handleXSearch(args: {
async function handleWebFetch(args: {
url: string;
provider?:
| "firecrawl"
| "jina-reader"
| "tavily-search"
| "tinyfish"
| "context7"
| "nimble-search"
| "anysearch-search";
provider?: "firecrawl" | "jina-reader" | "tavily-search" | "tinyfish" | "context7";
format?: "markdown" | "html" | "links" | "screenshot";
include_metadata?: boolean;
depth?: number;

View File

@@ -30,16 +30,6 @@ const BOOTSTRAP_TIMEOUT_MS = 8_000;
const ONBOARD_TIMEOUT_MS = 15_000;
const DEFAULT_TIER_ID = "legacy-tier";
// onboardUser is a Long-Running Operation: Google frequently answers the
// first call with {"done": false} (no cloudaicompanionProject field yet) and
// expects the SAME request re-sent every couple of seconds until the
// operation settles with {"done": true, response: {...}}. Treating the
// first "done:false" response as "no project" (BYOP) misclassifies a normal
// in-progress onboarding as "bring your own project" and permanently caches
// that wrong verdict. Poll bounded, matching 9router's onboardUser().
const ONBOARD_POLL_MAX_ATTEMPTS = 5;
const ONBOARD_POLL_INTERVAL_MS = 2_000;
/** Ordered list of loadCodeAssist endpoint URLs. */
export function getAntigravityLoadCodeAssistUrls(): string[] {
return ANTIGRAVITY_BOOTSTRAP_BASE_URLS.map((base) => `${base}${LOAD_CODE_ASSIST_PATH}`);
@@ -172,31 +162,10 @@ async function tryLoadCodeAssist(
return { projectId: null, tierId: DEFAULT_TIER_ID };
}
/**
* Extract the project id from a settled ({done:true}) onboardUser response body.
* The documented LRO shape nests it under `response.cloudaicompanionProject`
* (matches 9router's onboardUser and Google's Operation envelope), but some
* observed responses put it at the top level — check both.
*/
function extractProjectIdFromOnboardResponse(data: Record<string, unknown> | null): string | null {
const nested = (data?.response as Record<string, unknown> | undefined)?.cloudaicompanionProject;
const project = nested ?? data?.cloudaicompanionProject;
if (typeof project === "string") {
const id = project.trim();
return id || null;
}
if (project && typeof project === "object") {
const id = (project as Record<string, unknown>).id;
if (typeof id === "string" && id.trim()) return id.trim();
}
return null;
}
/**
* Attempt onboardUser to create a Cloud Code project for the account.
* Called when loadCodeAssist returns no project — the account has never
* been onboarded. Polls the same endpoint on {done:false} responses (an
* in-progress LRO) before concluding anything about the account.
* been onboarded. Returns true if any endpoint reports success.
*/
async function tryOnboardUser(
accessToken: string,
@@ -207,74 +176,47 @@ async function tryOnboardUser(
): Promise<AntigravityOnboardStatus> {
const urls = getAntigravityOnboardUrls();
const headers = getAntigravityContentHeaders(clientProfile, accessToken);
const body = JSON.stringify({
tier_id: tierId,
metadata: getAntigravityLoadCodeAssistMetadata(),
});
for (const url of urls) {
for (let attempt = 1; attempt <= ONBOARD_POLL_MAX_ATTEMPTS; attempt++) {
if (signal?.aborted) throw signal.reason;
try {
const timeoutSignal = AbortSignal.timeout(ONBOARD_TIMEOUT_MS);
const response = await fetchImpl(url, {
method: "POST",
headers,
body,
signal: signal ? AbortSignal.any([signal, timeoutSignal]) : timeoutSignal,
});
if (signal?.aborted) throw signal.reason;
try {
const timeoutSignal = AbortSignal.timeout(ONBOARD_TIMEOUT_MS);
const response = await fetchImpl(url, {
method: "POST",
headers,
body: JSON.stringify({
tier_id: tierId,
metadata: getAntigravityLoadCodeAssistMetadata(),
}),
signal: signal ? AbortSignal.any([signal, timeoutSignal]) : timeoutSignal,
});
if (!response.ok) {
if (response.ok) {
// Accounts Google expects to Bring Their Own Project: onboardUser
// returns 200 without a `cloudaicompanionProject` in the body — no
// automatic project creation for standard-tier/personal accounts
// (tracked in #8491). Detect that so we can fail fast with a clear
// instruction instead of retrying forever or fabricating an id that
// Google later rejects with a delayed 429 RESOURCE_EXHAUSTED.
const body = await response.text().catch(() => "");
if (body && !/cloudaicompanionProject/.test(body)) {
console.warn(
`[models] antigravity onboardUser failed at ${url} (${response.status}) — trying next`
`[models] antigravity onboardUser done but no project in response at ${url} — Google BYOP (user-defined GCP project) required`
);
break;
return "requires_manual_project";
}
const data = (await response.json().catch(() => null)) as Record<string, unknown> | null;
// Only an EXPLICIT `done: false` means "in-progress LRO, poll again".
// A proper Google Operation always carries `done` when it is one; a
// response with `done` absent entirely (e.g. `{}`) is not an LRO in
// progress — it's Google's immediate, settled "no project" answer for
// BYOP accounts (#8491) and must fall through to that classification
// on the first attempt, same as before this polling was added.
if (data?.done === false) {
// In-progress LRO — Google hasn't decided (project created, or
// BYOP required) yet. Re-send the same request after a short wait.
if (attempt < ONBOARD_POLL_MAX_ATTEMPTS) {
console.warn(
`[models] antigravity onboardUser at ${url} not done yet (attempt ${attempt}/${ONBOARD_POLL_MAX_ATTEMPTS}) — waiting`
);
await new Promise((resolve) => setTimeout(resolve, ONBOARD_POLL_INTERVAL_MS));
continue;
}
console.warn(
`[models] antigravity onboardUser at ${url} still not done after ${ONBOARD_POLL_MAX_ATTEMPTS} attempts — treating as failed`
);
break;
}
// done:true — Google has settled the operation. Accounts Google
// expects to Bring Their Own Project answer with done:true and no
// cloudaicompanionProject — no automatic project creation for
// standard-tier/personal accounts (tracked in #8491). Only now is it
// safe to draw that conclusion.
if (extractProjectIdFromOnboardResponse(data)) {
return "onboarded";
}
console.warn(
`[models] antigravity onboardUser done but no project in response at ${url} — Google BYOP (user-defined GCP project) required`
);
return "requires_manual_project";
} catch (error) {
if (signal?.aborted || (error instanceof Error && error.name === "AbortError")) {
throw signal?.reason ?? error;
}
const msg = error instanceof Error ? error.message : String(error);
console.warn(`[models] antigravity onboardUser threw for ${url}: ${msg} — trying next`);
break;
return "onboarded";
}
console.warn(
`[models] antigravity onboardUser failed at ${url} (${response.status}) — trying next`
);
} catch (error) {
if (signal?.aborted || (error instanceof Error && error.name === "AbortError")) {
throw signal?.reason ?? error;
}
const msg = error instanceof Error ? error.message : String(error);
console.warn(`[models] antigravity onboardUser threw for ${url}: ${msg} — trying next`);
}
}
return "failed";

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