Compare commits

..

1 Commits

Author SHA1 Message Date
Markus Hartung
61db75e3e5 chore(quality): rebaseline file-size for #11448/#11495/#11561 growth 2026-08-26 08:17:43 -03:00
191 changed files with 522 additions and 5510 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

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

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 @@
- 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 @@
- **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

@@ -3093,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

View File

@@ -435,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": 3376,
"_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,
@@ -459,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,8 +480,7 @@
"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
"src/lib/cloudflaredTunnel.ts": 1078
},
"_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,6 +650,5 @@
"_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_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."
}

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 (354 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: 354 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 354 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">354 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 354 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 354 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: 354 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">354 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), 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
@@ -108,7 +108,7 @@ Runs the same query/URL across up to **4 providers in parallel** (D22):
| Field | Source |
| ------------------------------ | ------------------------------------------------------------------------------------------ |
| `id`, `name` | `searchRegistry.ts` |
| `kind` | `"search"` or `"fetch"` (firecrawl, jina-reader, tavily-search, tinyfish, nimble-search) |
| `kind` | `"search"` (12 providers) or `"fetch"` (firecrawl, jina-reader, tavily-search, tinyfish) |
| `costPerQuery` | Registry data |
| `freeMonthlyQuota` | Registry data |
| `searchTypes` / `fetchFormats` | Registry data |
@@ -136,7 +136,7 @@ Only one backend change was needed for this feature:
`src/app/api/search/providers/route.ts` was extended to:
- Include every fetch provider (`firecrawl`, `jina-reader`, `tavily-search`, `tinyfish`, `nimble-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,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 354 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
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **354 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
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **354-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,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 354 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
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **354 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
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **354-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,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 354 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
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **354 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
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **354-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,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 354 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
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **354 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
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **354-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,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 354 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
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **354 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
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **354-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,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 354 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
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **354 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
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **354-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,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 354 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
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **354 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
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **354-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,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 354 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
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **354 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
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **354-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,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 354 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
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **354 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
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **354-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,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 354 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
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **354 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
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **354-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,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 354 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
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **354 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
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **354-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,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 354 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
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **354 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
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **354-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,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 354 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
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **354 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
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **354-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,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 354 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
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **354 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
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **354-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,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 354 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
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **354 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
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **354-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,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 354 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
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **354 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
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **354-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,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 354 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
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **354 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
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **354-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,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 354 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
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **354 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
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **354-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,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 354 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
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **354 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
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **354-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,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 354 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
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **354 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
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **354-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,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 354 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
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **354 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
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **354-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,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 354 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
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **354 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
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **354-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,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 354 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
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **354 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
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **354-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,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 354 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
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **354 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
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **354-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,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 354 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
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **354 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
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **354-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,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 354 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
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **354 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
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **354-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,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 354 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
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **354 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
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **354-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,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 354 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
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **354 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
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **354-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,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 354 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
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **354 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
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **354-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,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 354 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
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **354 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
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **354-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,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 354 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
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **354 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
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **354-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,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 354 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
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **354 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
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **354-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,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 354 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
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **354 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
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **354-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,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 354 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
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **354 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
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **354-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,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 354 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
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **354 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
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **354-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,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 354 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
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **354 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
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **354-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,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 354 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
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **354 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
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **354-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,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 354 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
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **354 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
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **354-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,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 354 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
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **354 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
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **354-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,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 354 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
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **354 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
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **354-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,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 354 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
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **354 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
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **354-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,7 +4,7 @@
---
> OmniRoute is a free, open-source AI Gateway that acts as a universal API proxy for multi-provider LLMs. It provides smart routing, automatic fallback, load balancing, and format translation across 354 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
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **354 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
@@ -479,7 +479,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **354-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

@@ -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

@@ -380,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 |
|----|-------|------|------|---------|-------|
@@ -390,7 +390,6 @@ 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) |
@@ -443,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/) (110 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 354 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
- **354 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
- **354-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",

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

@@ -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)
@@ -3175,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) {
@@ -3539,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
@@ -3564,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 = () => {};
@@ -4257,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

@@ -24,7 +24,6 @@ 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";
@@ -476,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
@@ -740,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,
@@ -862,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,
@@ -1358,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,

View File

@@ -21,14 +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";
export type WebFetchFormat = "markdown" | "html" | "links" | "screenshot";
export interface WebFetchRequest {
url: string;
provider?:
"firecrawl" | "jina-reader" | "tavily-search" | "tinyfish" | "context7" | "nimble-search";
provider?: "firecrawl" | "jina-reader" | "tavily-search" | "tinyfish" | "context7";
format?: WebFetchFormat;
depth?: 0 | 1 | 2;
wait_for_selector?: string;
@@ -63,7 +61,6 @@ export const WEB_FETCH_PROVIDERS = Object.freeze([
"tavily-search",
"tinyfish",
"context7",
"nimble-search",
] as const);
// Derived from the array — adding a provider to WEB_FETCH_PROVIDERS
// automatically widens the union; they cannot drift apart.
@@ -142,14 +139,6 @@ export async function handleWebFetch(
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
// unsupported, and the format field is validated/ignored below.

View File

@@ -557,7 +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"])
.enum(["firecrawl", "jina-reader", "tavily-search", "tinyfish", "context7"])
.optional()
.describe(
"Specific fetch provider to use (default: first available). " +

View File

@@ -694,8 +694,7 @@ async function handleXSearch(args: {
async function handleWebFetch(args: {
url: string;
provider?:
"firecrawl" | "jina-reader" | "tavily-search" | "tinyfish" | "context7" | "nimble-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";

View File

@@ -31,10 +31,7 @@ const MAX_CONNECTION_EXTRA_KEYS = 500;
*/
export function trackConnectionExtraKeys(connectionId: string, extraKeys: string[]): void {
const validExtras = extraKeys.filter((k) => typeof k === "string" && k.trim().length > 0);
if (
!_connectionExtraKeys.has(connectionId) &&
_connectionExtraKeys.size >= MAX_CONNECTION_EXTRA_KEYS
) {
if (!_connectionExtraKeys.has(connectionId) && _connectionExtraKeys.size >= MAX_CONNECTION_EXTRA_KEYS) {
const oldest = _connectionExtraKeys.keys().next().value;
if (oldest !== undefined) _connectionExtraKeys.delete(oldest);
}
@@ -311,29 +308,6 @@ export function syncHealthFromDB(connectionId: string, health?: Record<string, K
}
}
/** Recover one authoritatively validated key without changing sibling-key health. */
export function recoverKeyHealth(
connectionId: string,
keyId: string,
providerSpecificData: unknown
): Record<string, unknown> | undefined {
const data =
providerSpecificData && typeof providerSpecificData === "object"
? (providerSpecificData as Record<string, unknown>)
: {};
const health = data.apiKeyHealth as Record<string, KeyHealth> | undefined;
const currentHealth = health?.[keyId];
if (!currentHealth || (currentHealth.status === "active" && currentHealth.failures === 0)) {
return undefined;
}
syncHealthFromDB(connectionId, health);
return {
...data,
apiKeyHealth: { ...health, [keyId]: recordKeySuccess(connectionId, keyId) },
};
}
/**
* Reset the rotation index for a connection.
* Call this when a key fails (401/403) to skip the bad key next time.

View File

@@ -10,9 +10,6 @@
* 2b. Layers 1-2 retried against the base model this id inherits quality scores
* from, when `resolveScoresAs` resolves one (#11489). Reported as
* `<source>:inherited`.
* 2c. If the request id or its scoresAs base is vendor-retired (#11625), layers
* 13 are skipped so a leftover arena row cannot short-circuit the layer-3
* veto, and a dead *codex id cannot keep the coding wildcard boost.
* 3. Models.dev tier — derived from `model_capabilities` table capability data,
* with a vendor-lifecycle veto (#11508: a retired id never earns a tier
* score) and the same base-model inheritance as layers 12
@@ -46,7 +43,6 @@ import {
} from "../../../src/lib/db/modelIntelligence.ts";
import { readFileSync } from "node:fs";
import { resolveScoresAs } from "./scoresAs.ts";
import { isVendorRetiredId } from "../modelLifecycle.ts";
// #11508 — vendor lifecycle snapshot (#11507). An id the vendor has retired
// must never earn a capability-derived tier score: models.dev keeps listing
@@ -467,60 +463,48 @@ export function getTaskFitness(model: string, taskType: string): number {
return getTaskFitnessWithSource(model, taskType).score;
}
function isFitnessRetired(modelId: string): boolean {
if (isVendorRetiredId(modelId)) return true;
const { base, via } = resolveScoresAs(modelId);
return via !== null && isVendorRetiredId(base);
}
export function getTaskFitnessWithSource(
model: string,
taskType: string
): { score: number; source: string } {
const normalizedModel = model.toLowerCase();
const normalizedTask = taskType.toLowerCase();
const fitnessRetired = isFitnessRetired(normalizedModel);
if (!fitnessRetired) {
const userOverride = queryModelIntelligence(normalizedModel, normalizedTask, "user_override");
if (userOverride !== null) {
return { score: userOverride, source: "user_override" };
}
const arenaElo = queryModelIntelligence(normalizedModel, normalizedTask, "arena_elo");
if (arenaElo !== null) {
return { score: arenaElo, source: "arena_elo" };
}
// Layers 1-2, retried against the base model this id inherits quality from
// (#11489). Every DB-backed source publishes scores for BASE models only, so
// a variant id — an effort suffix (`gpt-5.6-sol-xhigh`), a vendor alias
// (`gpt-5.6`), a `-free` tier marker (`mimo-v2.5-free`, #4517) — misses both
// literal lookups and used to fall all the way to the wildcard 0.5, losing
// every comparison against a base model that happens to be benchmarked.
// The score is inherited VERBATIM: the 12-factor scoring already prices cost
// and latency per variant, so there is no basis for inventing an effort
// delta. `:inherited` keeps the indirection visible to callers.
const inherited = lookupInheritedFitness(normalizedModel, normalizedTask);
if (inherited !== null) {
return inherited;
}
const tierScore = getModelsDevTierFitness(normalizedModel, normalizedTask);
if (tierScore !== null) {
return { score: tierScore, source: "models_dev_tier" };
}
const staticScore = lookupStaticFitnessTable(normalizedModel, normalizedTask);
if (staticScore !== null) {
return { score: staticScore, source: "fitness_table" };
}
return { score: lookupWildcardBoosts(normalizedModel, normalizedTask), source: "wildcard_boost" };
const userOverride = queryModelIntelligence(normalizedModel, normalizedTask, "user_override");
if (userOverride !== null) {
return { score: userOverride, source: "user_override" };
}
// Retired: 0.5 is "no evidence", never a quality claim and never a *codex boost.
return { score: 0.5, source: "wildcard_boost" };
const arenaElo = queryModelIntelligence(normalizedModel, normalizedTask, "arena_elo");
if (arenaElo !== null) {
return { score: arenaElo, source: "arena_elo" };
}
// Layers 1-2, retried against the base model this id inherits quality from
// (#11489). Every DB-backed source publishes scores for BASE models only, so
// a variant id — an effort suffix (`gpt-5.6-sol-xhigh`), a vendor alias
// (`gpt-5.6`), a `-free` tier marker (`mimo-v2.5-free`, #4517) — misses both
// literal lookups and used to fall all the way to the wildcard 0.5, losing
// every comparison against a base model that happens to be benchmarked.
// The score is inherited VERBATIM: the 12-factor scoring already prices cost
// and latency per variant, so there is no basis for inventing an effort
// delta. `:inherited` keeps the indirection visible to callers.
const inherited = lookupInheritedFitness(normalizedModel, normalizedTask);
if (inherited !== null) {
return inherited;
}
const tierHit = getModelsDevTierFitnessWithSource(normalizedModel, normalizedTask);
if (tierHit !== null) {
return tierHit;
}
const staticScore = lookupStaticFitnessTable(normalizedModel, normalizedTask);
if (staticScore !== null) {
return { score: staticScore, source: "fitness_table" };
}
return { score: lookupWildcardBoosts(normalizedModel, normalizedTask), source: "wildcard_boost" };
}
/**
@@ -540,7 +524,6 @@ function lookupInheritedFitness(
const { base, via } = resolveScoresAs(normalizedModel);
if (via === null || base === normalizedModel) return null;
const normalizedBase = base.toLowerCase();
if (isVendorRetiredId(normalizedBase)) return null;
for (const source of ["user_override", "arena_elo"] as const) {
const score = queryModelIntelligence(normalizedBase, normalizedTask, source);

View File

@@ -70,7 +70,6 @@ import { resolveProviderId } from "../../src/shared/constants/providers.ts";
import * as semaphore from "./rateLimitSemaphore.ts";
import { getCircuitBreaker } from "../../src/shared/utils/circuitBreaker";
import { parseModel } from "./model.ts";
import { rejectRetiredAutoComboCandidates } from "./modelLifecycle.ts";
import { createComboContext } from "./combo/context.ts";
import { phaseComboSetup } from "./combo/comboSetup.ts";
import { checkCredentialGate, logCredentialSkip } from "./credentialGate.ts";
@@ -628,14 +627,11 @@ export async function buildAutoCandidates(
})
);
// Filter out candidates whose model is hidden by the user in the dashboard,
// then drop vendor-retired ids so auto-combo cannot pick them (#11625).
return rejectRetiredAutoComboCandidates(
candidates.filter((c) => {
const hiddenModels = hiddenModelsMap.get(c.provider);
return !hiddenModels?.has(c.model);
})
);
// Filter out candidates whose model is hidden by the user in the dashboard
return candidates.filter((c) => {
const hiddenModels = hiddenModelsMap.get(c.provider);
return !hiddenModels?.has(c.model);
});
}
// Context-cache pin health gate — moved to combo/dispatchPrelude.ts alongside the

View File

@@ -230,10 +230,7 @@ function formatMessagesForPrompt(messages: MessageLike[]): string {
.join("\n\n");
}
export function selectMessagesForSummary(
messages: MessageLike[],
maxMessages: number
): MessageLike[] {
export function selectMessagesForSummary(messages: MessageLike[], maxMessages: number): MessageLike[] {
const validMessages = messages.filter((m) => m && typeof m === "object");
const system = validMessages.filter(
(m) => typeof m.role === "string" && (m.role === "system" || m.role === "developer")
@@ -629,15 +626,6 @@ async function generateUniversalHandoffAsync(options: {
const summaryPrompt = HANDOFF_PROMPT_TEMPLATE.replace("{HISTORY}", historyText);
const summaryModel = options.handoffModel || options.currModel;
if (options.providerAllowlist.length > 0) {
const slashIdx = summaryModel.indexOf("/");
const modelProvider = slashIdx > 0 ? summaryModel.slice(0, slashIdx) : "";
if (modelProvider && !options.providerAllowlist.includes(modelProvider)) {
return;
}
}
const summaryBody: Record<string, unknown> = {
model: summaryModel,
messages: [{ role: "user", content: summaryPrompt }],

View File

@@ -4,14 +4,7 @@
* Replacement model IDs are migration guidance only. This module never rewrites a
* request: shutdown models are rejected, deprecated models remain callable until
* their shutdown date, and untracked models pass through unchanged.
*
* Dated OpenAI rows in MODEL_LIFECYCLE_RECORDS stay provider-scoped (a warn-before-
* shutdown date on `openai` must not leak onto an aggregator that still serves the
* id). Snapshot `status: "retired"` ids from config/quality/model-lifecycle.json
* are id-scoped and prefix-stripped (#11625): `openai/gpt-5.2-codex` on openrouter
* is the same retired vendor id as `gpt-5.2-codex`.
*/
import { readFileSync } from "node:fs";
export const OPENAI_MODEL_DEPRECATIONS_URL = "https://developers.openai.com/api/docs/deprecations";
@@ -135,67 +128,6 @@ function shutdownTimestamp(shutdownAt: string): number {
return Date.parse(`${shutdownAt}T00:00:00.000Z`);
}
const SNAPSHOT_URL = new URL("../../config/quality/model-lifecycle.json", import.meta.url);
const SNAPSHOT_SOURCE = "config/quality/model-lifecycle.json";
type VendorRetiredEntry = {
vendor?: string;
status?: string;
retiredOn?: string | null;
replacement?: string | null;
};
let _retiredIds: Set<string> | null = null;
let _retiredEntries: Map<string, VendorRetiredEntry> | null = null;
function loadVendorRetiredSnapshot(): {
ids: Set<string>;
entries: Map<string, VendorRetiredEntry>;
} {
if (_retiredIds && _retiredEntries) return { ids: _retiredIds, entries: _retiredEntries };
const ids = new Set<string>();
const entries = new Map<string, VendorRetiredEntry>();
try {
const parsed = JSON.parse(readFileSync(SNAPSHOT_URL, "utf8")) as {
retired?: Record<string, VendorRetiredEntry>;
};
for (const [id, entry] of Object.entries(parsed.retired ?? {})) {
if (entry?.status !== "retired") continue;
const key = id.toLowerCase();
ids.add(key);
entries.set(key, entry);
}
} catch {
// Snapshot missing → no id-scoped veto. Dated OpenAI rows still apply.
}
_retiredIds = ids;
_retiredEntries = entries;
return { ids, entries };
}
/** True when `modelId` or its last `vendor/` path segment is `status: "retired"` in the snapshot. */
export function isVendorRetiredId(modelId: string | null | undefined): boolean {
if (typeof modelId !== "string" || modelId.length === 0) return false;
const lower = modelId.toLowerCase();
const { ids } = loadVendorRetiredSnapshot();
if (ids.has(lower)) return true;
const slash = lower.lastIndexOf("/");
return slash !== -1 && ids.has(lower.slice(slash + 1));
}
function lookupVendorRetiredEntry(modelId: string): VendorRetiredEntry | null {
const lower = modelId.toLowerCase();
const { entries } = loadVendorRetiredSnapshot();
return entries.get(lower) ?? entries.get(lower.slice(lower.lastIndexOf("/") + 1)) ?? null;
}
/** Drop auto-combo candidates whose model id the vendor has retired (#11625). */
export function rejectRetiredAutoComboCandidates<T extends { model: string }>(
candidates: readonly T[]
): T[] {
return candidates.filter((candidate) => !isVendorRetiredId(candidate.model));
}
export function getModelLifecycleDecision(
provider: string | null | undefined,
model: string | null | undefined,
@@ -206,24 +138,6 @@ export function getModelLifecycleDecision(
const record = RECORDS_BY_KEY.get(lifecycleKey(normalizedProvider, normalizedModel));
if (!record) {
if (isVendorRetiredId(normalizedModel)) {
const entry = lookupVendorRetiredEntry(normalizedModel);
const replacementId =
typeof entry?.replacement === "string" && entry.replacement.length > 0
? entry.replacement
: null;
return {
provider: normalizedProvider,
model: normalizedModel,
status: "shutdown",
action: "reject",
shutdownAt: typeof entry?.retiredOn === "string" ? entry.retiredOn : null,
replacement: replacementId
? { provider: entry?.vendor ?? "", model: replacementId }
: null,
source: SNAPSHOT_SOURCE,
};
}
return {
provider: normalizedProvider,
model: normalizedModel,
@@ -256,8 +170,7 @@ export function formatModelLifecycleMessage(decision: ModelLifecycleDecision): s
? ` Use "${decision.replacement.provider}/${decision.replacement.model}" instead.`
: "";
if (decision.status === "shutdown") {
const when = decision.shutdownAt ? ` was shut down on ${decision.shutdownAt}` : " has been retired by its vendor";
return `Model "${modelRef}"${when} and cannot be routed automatically.${replacement}`;
return `Model "${modelRef}" was shut down on ${decision.shutdownAt} and cannot be routed automatically.${replacement}`;
}
return `Model "${modelRef}" is deprecated and is scheduled to shut down on ${decision.shutdownAt}.${replacement}`;
}

View File

@@ -2,6 +2,7 @@ import { register } from "../registry.ts";
import { FORMATS } from "../formats.ts";
import {
DEFAULT_SAFETY_SETTINGS,
tryParseJSON,
cleanJSONSchemaForAntigravity,
} from "../helpers/geminiHelper.ts";
import { buildGeminiTools, sanitizeGeminiToolName } from "../helpers/geminiToolsSanitizer.ts";
@@ -185,6 +186,13 @@ export function claudeToGeminiRequest(model, body, stream, credentials = null) {
.map((c) => (c.type === "text" ? c.text : JSON.stringify(c)))
.join("\n");
}
let parsedContent = tryParseJSON(content);
if (parsedContent === null) {
parsedContent = { result: content };
} else if (typeof parsedContent !== "object") {
parsedContent = { result: parsedContent };
}
const toolUseId = block.tool_use_id;
const name = toolUseNames[toolUseId] || "unknown";
@@ -202,7 +210,7 @@ export function claudeToGeminiRequest(model, body, stream, credentials = null) {
functionResponse: {
...(stripFunctionCallId ? {} : { id: toolUseId }),
name,
response: { result: content },
response: { result: parsedContent },
},
});
break;

View File

@@ -511,12 +511,18 @@ function openaiToGeminiBase(
name = sanitizeToolName(name);
const resp = toolResponses[fid];
let parsedResp = tryParseJSON(resp);
if (parsedResp === null) {
parsedResp = { result: resp };
} else if (typeof parsedResp !== "object") {
parsedResp = { result: parsedResp };
}
toolParts.push({
functionResponse: {
...(toolNameOptions.stripFunctionCallId ? {} : { id: fid }),
name: name,
response: { result: resp },
response: { result: parsedResp },
},
});
}

121
package-lock.json generated
View File

@@ -86,6 +86,7 @@
"undici": "^8.10.0",
"update-notifier": "^7.3.1",
"uuid": "^14.0.0",
"wreq-js": "3.1.0",
"ws": "^8.21.3",
"xxhash-wasm": "^1.1.0",
"yazl": "^3.3.1",
@@ -109,7 +110,7 @@
"@testing-library/react": "^16.3.2",
"@testing-library/user-event": "^14.6.6",
"@types/better-sqlite3": "^9.6.0",
"@types/bun": "latest",
"@types/bun": "*",
"@types/node": "^26.2.0",
"@types/react": "^19.2.18",
"@types/react-dom": "^19.2.4",
@@ -160,7 +161,7 @@
"better-sqlite3": "^13.0.2",
"js-tiktoken": "^1.0.20",
"keytar": "^7.9.0",
"onnxruntime-node": "1.24.3",
"onnxruntime-node": "1.27.0",
"sqlite-vec": "^0.1.9",
"tls-client-node": "^0.2.0",
"wreq-js": "^3.1.0"
@@ -15120,14 +15121,6 @@
"url": "https://opencollective.com/express"
}
},
"node_modules/boolean": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz",
"integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==",
"deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.",
"license": "MIT",
"optional": true
},
"node_modules/bottleneck": {
"version": "2.19.5",
"resolved": "https://registry.npmjs.org/bottleneck/-/bottleneck-2.19.5.tgz",
@@ -18213,13 +18206,6 @@
"node": ">=8"
}
},
"node_modules/detect-node": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz",
"integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==",
"license": "MIT",
"optional": true
},
"node_modules/detect-node-es": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz",
@@ -19065,13 +19051,6 @@
"benchmarks"
]
},
"node_modules/es6-error": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz",
"integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==",
"license": "MIT",
"optional": true
},
"node_modules/es6-promisify": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-7.0.0.tgz",
@@ -21878,18 +21857,16 @@
}
},
"node_modules/global-agent": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/global-agent/-/global-agent-3.0.0.tgz",
"integrity": "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==",
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/global-agent/-/global-agent-4.1.3.tgz",
"integrity": "sha512-KUJEViiuFT3I97t+GYMikLPJS2Lfo/S2F+DQuBWzuzaMPnvt5yyZePzArx36fBzpGTxZjIpDbXLeySLgh+k76g==",
"license": "BSD-3-Clause",
"optional": true,
"dependencies": {
"boolean": "^3.0.1",
"es6-error": "^4.1.1",
"matcher": "^3.0.0",
"roarr": "^2.15.3",
"semver": "^7.3.2",
"serialize-error": "^7.0.1"
"globalthis": "^1.0.2",
"matcher": "^4.0.0",
"semver": "^7.3.5",
"serialize-error": "^8.1.0"
},
"engines": {
"node": ">=10.0"
@@ -25447,13 +25424,6 @@
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/json-stringify-safe": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
"integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==",
"license": "ISC",
"optional": true
},
"node_modules/json5": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
@@ -27322,9 +27292,9 @@
}
},
"node_modules/matcher": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz",
"integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==",
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/matcher/-/matcher-4.0.0.tgz",
"integrity": "sha512-S6x5wmcDmsDRRU/c2dkccDwQPXoFczc5+HpQ2lON8pnvHlnvHAHj5WlLVvw6n6vNyHuVugYrFohYxbS+pvFpKQ==",
"license": "MIT",
"optional": true,
"dependencies": {
@@ -27332,6 +27302,9 @@
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/material-symbols": {
@@ -30304,16 +30277,16 @@
}
},
"node_modules/onnxruntime-common": {
"version": "1.24.3",
"resolved": "https://registry.npmjs.org/onnxruntime-common/-/onnxruntime-common-1.24.3.tgz",
"integrity": "sha512-GeuPZO6U/LBJXvwdaqHbuUmoXiEdeCjWi/EG7Y1HNnDwJYuk6WUbNXpF6luSUY8yASul3cmUlLGrCCL1ZgVXqA==",
"version": "1.27.0",
"resolved": "https://registry.npmjs.org/onnxruntime-common/-/onnxruntime-common-1.27.0.tgz",
"integrity": "sha512-3KxL5wIVqa8Ex08jxSzncm9CMgw8CjOFyOQ7SxvG9o0cVLlhTNKXyIQuTbtX4tGPJEf73OER2xrjt4HJSBL4ow==",
"license": "MIT",
"optional": true
},
"node_modules/onnxruntime-node": {
"version": "1.24.3",
"resolved": "https://registry.npmjs.org/onnxruntime-node/-/onnxruntime-node-1.24.3.tgz",
"integrity": "sha512-JH7+czbc8ALA819vlTgcV+Q214/+VjGeBHDjX81+ZCD0PCVCIFGFNtT0V4sXG/1JXypKPgScQcB3ij/hk3YnTg==",
"version": "1.27.0",
"resolved": "https://registry.npmjs.org/onnxruntime-node/-/onnxruntime-node-1.27.0.tgz",
"integrity": "sha512-QEzGwrvNBgv4uPVdnbHsOGG4G6T96mdlcFI8aAKPjMU8wOPpVocPXb6k3QGkaZagVTv2G9Bnnbo6Z3JdXr1fQw==",
"hasInstallScript": true,
"license": "MIT",
"optional": true,
@@ -30324,8 +30297,8 @@
],
"dependencies": {
"adm-zip": "^0.5.16",
"global-agent": "^3.0.0",
"onnxruntime-common": "1.24.3"
"global-agent": "^4.1.3",
"onnxruntime-common": "1.27.0"
}
},
"node_modules/onnxruntime-web": {
@@ -34002,24 +33975,6 @@
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/roarr": {
"version": "2.15.4",
"resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz",
"integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==",
"license": "BSD-3-Clause",
"optional": true,
"dependencies": {
"boolean": "^3.0.1",
"detect-node": "^2.0.4",
"globalthis": "^1.0.1",
"json-stringify-safe": "^5.0.1",
"semver-compare": "^1.0.0",
"sprintf-js": "^1.1.2"
},
"engines": {
"node": ">=8.0"
}
},
"node_modules/robot3": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/robot3/-/robot3-0.4.1.tgz",
@@ -34379,13 +34334,6 @@
"semver": "bin/semver.js"
}
},
"node_modules/semver-compare": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz",
"integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==",
"license": "MIT",
"optional": true
},
"node_modules/send": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz",
@@ -34413,13 +34361,13 @@
}
},
"node_modules/serialize-error": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz",
"integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==",
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-8.1.0.tgz",
"integrity": "sha512-3NnuWfM6vBYoy5gZFvHiYsVbafvI9vZv/+jlIigFn4oP4zjNPK3LhcY0xSCgeb1a5L8jO71Mit9LlNoi2UfDDQ==",
"license": "MIT",
"optional": true,
"dependencies": {
"type-fest": "^0.13.1"
"type-fest": "^0.20.2"
},
"engines": {
"node": ">=10"
@@ -34429,9 +34377,9 @@
}
},
"node_modules/serialize-error/node_modules/type-fest": {
"version": "0.13.1",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz",
"integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==",
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
"integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
"license": "(MIT OR CC0-1.0)",
"optional": true,
"engines": {
@@ -35201,13 +35149,6 @@
"node": ">= 10.x"
}
},
"node_modules/sprintf-js": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz",
"integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==",
"license": "BSD-3-Clause",
"optional": true
},
"node_modules/sql.js": {
"version": "1.14.2",
"resolved": "https://registry.npmjs.org/sql.js/-/sql.js-1.14.2.tgz",

View File

@@ -1,7 +1,7 @@
{
"name": "omniroute",
"version": "3.8.51",
"description": "Unified AI router with 354 providers, RTK+Caveman compression, auto fallback, MCP/A2A, desktop, PWA, and OpenAI-compatible APIs.",
"description": "Unified AI router with 353 providers, RTK+Caveman compression, auto fallback, MCP/A2A, desktop, PWA, and OpenAI-compatible APIs.",
"type": "module",
"bin": {
"omniroute": "bin/omniroute.mjs",
@@ -252,7 +252,7 @@
"postinstall": "node scripts/build/postinstall.mjs",
"uninstall": "node scripts/build/uninstall.mjs",
"uninstall:full": "node scripts/build/uninstall.mjs --full",
"prepare": "node -e \"try{require.resolve('husky')}catch(e){process.exit(0)};require('child_process').execSync('husky',{stdio:'inherit'})\"",
"prepare": "husky",
"system-info": "node scripts/dev/system-info.mjs",
"build:cli-api": "node --import tsx/esm scripts/cli/generate-api-commands.mjs",
"postbuild": "node scripts/build/colocate-standalone.mjs",
@@ -347,7 +347,7 @@
"better-sqlite3": "^13.0.2",
"js-tiktoken": "^1.0.20",
"keytar": "^7.9.0",
"onnxruntime-node": "1.24.3",
"onnxruntime-node": "1.27.0",
"sqlite-vec": "^0.1.9",
"tls-client-node": "^0.2.0",
"wreq-js": "^3.1.0"
@@ -436,7 +436,7 @@
"unrs-resolver": true
},
"overrides": {
"onnxruntime-node": "1.24.3",
"onnxruntime-node": "1.27.0",
"fast-xml-parser": "^5.10.1",
"sharp": "^0.35.3",
"postcss": "^8.5.18",

View File

@@ -1,7 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" role="img" aria-label="Nimble">
<rect width="24" height="24" rx="5" fill="#6D28D9" />
<path
d="M7 17V7h2.1l5.8 6.6V7H17v10h-2.1L9.1 10.4V17H7z"
fill="#fff"
/>
</svg>

Before

Width:  |  Height:  |  Size: 240 B

View File

@@ -19,20 +19,12 @@
*/
const KEY = process.env.NVIDIA_API_KEY ?? "";
const BASE_URL =
process.env.NVIDIA_BASE_URL || "https://integrate.api.nvidia.com/v1/chat/completions";
const BASE_URL = process.env.NVIDIA_BASE_URL || "https://integrate.api.nvidia.com/v1/chat/completions";
const MODEL = process.env.NVIDIA_MODEL || "openai/gpt-oss-120b";
// Neutralize CR/LF before logging so env-derived values (NVIDIA_MODEL, etc.)
// cannot forge extra log lines (S5145 log injection). Also strip any raw
// occurrence of the API key so an upstream error/response that echoes it
// back (e.g. inside err.stack or a validation result) never reaches the
// terminal in clear text (js/clear-text-logging, CWE-312/532).
const line = (s = "") => {
let out = String(s).replace(/[\r\n]+/g, " ");
if (KEY) out = out.split(KEY).join("[REDACTED]");
console.log(out);
};
// cannot forge extra log lines (S5145 log injection).
const line = (s = "") => console.log(String(s).replace(/[\r\n]+/g, " "));
const hr = () => line("─".repeat(72));
function show(label: string, value: unknown) {
@@ -60,13 +52,8 @@ async function partA() {
});
line(" ✅ validateProviderApiKey retornou (sem crash):");
show("resultado", result);
if (
typeof (result as any)?.error === "string" &&
(result as any).error.includes("startsWith")
) {
line(
" ⚠️ A mensagem de erro contém 'startsWith' → crash CAPTURADO dentro do try/catch da validação."
);
if (typeof (result as any)?.error === "string" && (result as any).error.includes("startsWith")) {
line(" ⚠️ A mensagem de erro contém 'startsWith' → crash CAPTURADO dentro do try/catch da validação.");
}
} catch (err: any) {
line(" ❌ validateProviderApiKey LANÇOU (crash não tratado):");

View File

@@ -22,52 +22,6 @@ interface SearchStats {
avgDurationMs: number;
}
function isFiniteNumber(value: unknown): value is number {
return typeof value === "number" && Number.isFinite(value);
}
function isSearchStats(value: unknown): value is SearchStats {
if (!value || typeof value !== "object") return false;
const candidate = value as Partial<SearchStats>;
return (
isFiniteNumber(candidate.total) &&
isFiniteNumber(candidate.today) &&
isFiniteNumber(candidate.cached) &&
isFiniteNumber(candidate.errors) &&
isFiniteNumber(candidate.totalCostUsd) &&
isFiniteNumber(candidate.cacheHitRate) &&
isFiniteNumber(candidate.avgDurationMs) &&
!!candidate.byProvider &&
typeof candidate.byProvider === "object" &&
!Array.isArray(candidate.byProvider) &&
Object.values(candidate.byProvider).every(
(provider) =>
!!provider &&
typeof provider === "object" &&
isFiniteNumber(provider.count) &&
isFiniteNumber(provider.costUsd)
) &&
Array.isArray(candidate.last24h) &&
candidate.last24h.every(
(point) => typeof point.hour === "string" && isFiniteNumber(point.count)
)
);
}
async function readSearchStats(response: Response): Promise<SearchStats> {
const body: unknown = await response.json().catch(() => null);
if (!response.ok) {
const message =
body && typeof body === "object" && "error" in body && typeof body.error === "string"
? body.error
: null;
throw new Error(message ?? "searchAnalyticsNoData");
}
if (!isSearchStats(body)) throw new Error("searchAnalyticsNoData");
return body;
}
function StatCard({
icon,
label,
@@ -131,25 +85,16 @@ export default function SearchAnalyticsTab() {
const [error, setError] = useState<string | null>(null);
useEffect(() => {
let cancelled = false;
const controller = new AbortController();
async function loadStats() {
try {
const response = await fetch("/api/v1/search/analytics", { signal: controller.signal });
const nextStats = await readSearchStats(response);
if (!cancelled) setStats(nextStats);
} catch (cause) {
const message = cause instanceof Error ? cause.message : "searchAnalyticsNoData";
if (!cancelled) setError(message);
} finally {
if (!cancelled) setLoading(false);
}
}
void loadStats();
return () => {
cancelled = true;
controller.abort();
};
fetch("/api/v1/search/analytics")
.then((r) => r.json())
.then((d) => {
setStats(d);
setLoading(false);
})
.catch((e) => {
setError(e.message);
setLoading(false);
});
}, []);
if (loading) {

View File

@@ -249,23 +249,21 @@ export default function ProfilePage() {
<div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 gap-4">
{allBadges.map((badge) => {
const isEarned = earnedIds.has(badge.id);
const isHiddenAndLocked = Boolean(badge.hidden) && !isEarned;
const earnedInfo = earnedBadges.find((b) => b.badgeId === badge.id);
const rarityColor = RARITY_COLORS[badge.rarity] || RARITY_COLORS.common;
return (
<button
key={badge.id}
onClick={() => !isHiddenAndLocked && setSelectedBadge(badge)}
disabled={isHiddenAndLocked}
onClick={() => setSelectedBadge(badge)}
className={`relative p-4 rounded-xl border transition-all text-left ${
isEarned
? `${rarityColor} bg-surface hover:shadow-md`
: "border-border/50 bg-surface/50 opacity-50 grayscale enabled:hover:opacity-70 disabled:cursor-default"
: "border-border/50 bg-surface/50 opacity-50 grayscale hover:opacity-70"
}`}
>
<div className="text-3xl mb-2">
<BadgeIcon icon={isHiddenAndLocked ? null : badge.icon} earned={isEarned} />
<BadgeIcon icon={badge.icon} earned={isEarned} />
</div>
<p className="font-semibold text-sm truncate">
{badge.hidden && !isEarned ? "???" : translateBadge(badge, "name")}

View File

@@ -1,247 +0,0 @@
"use client";
import { Button, Input, Select, Toggle } from "@/shared/components";
import {
PEAK_HOUR_PROTECTION_DAYS,
type PeakHourProtectionConfig,
type PeakHourProtectionDay,
type PeakHourProtectionMode,
type PeakHourWindow,
} from "@/lib/providers/peakHourProtection";
import { providerText, type ProviderMessageTranslator } from "../providerPageHelpers";
export const EMPTY_PEAK_HOUR_PROTECTION: PeakHourProtectionConfig = {
enabled: false,
mode: "block",
windows: [],
};
const DAY_LABELS: Record<PeakHourProtectionDay, string> = {
mon: "Mon",
tue: "Tue",
wed: "Wed",
thu: "Thu",
fri: "Fri",
sat: "Sat",
sun: "Sun",
};
function cloneConfig(value: unknown): PeakHourProtectionConfig {
if (!value || typeof value !== "object" || Array.isArray(value)) {
return { ...EMPTY_PEAK_HOUR_PROTECTION, windows: [] };
}
const record = value as Record<string, unknown>;
const mode: PeakHourProtectionMode = record.mode === "avoid" ? "avoid" : "block";
const windows = Array.isArray(record.windows)
? record.windows
.filter((entry): entry is PeakHourWindow => !!entry && typeof entry === "object")
.map((entry) => ({
id: typeof entry.id === "string" ? entry.id : crypto.randomUUID(),
name: typeof entry.name === "string" ? entry.name : "",
days: Array.isArray(entry.days)
? entry.days.filter((day): day is PeakHourProtectionDay =>
(PEAK_HOUR_PROTECTION_DAYS as readonly string[]).includes(day)
)
: [],
startUtc: typeof entry.startUtc === "string" ? entry.startUtc : "06:00",
endUtc: typeof entry.endUtc === "string" ? entry.endUtc : "10:00",
}))
: [];
return { enabled: record.enabled === true, mode, windows };
}
function newWindow(): PeakHourWindow {
return { id: crypto.randomUUID(), name: "", days: [], startUtc: "06:00", endUtc: "10:00" };
}
function weekdayWindow(startUtc: string, endUtc: string): PeakHourWindow {
return {
id: crypto.randomUUID(),
name: "Weekday peak",
days: ["mon", "tue", "wed", "thu", "fri"],
startUtc,
endUtc,
};
}
export function normalizePeakHourProtectionForSave(
value: PeakHourProtectionConfig
): PeakHourProtectionConfig | null {
const windows = value.windows
.map((window) => ({
...(window.name?.trim() ? { name: window.name.trim() } : {}),
...(window.days && window.days.length > 0 ? { days: window.days } : {}),
startUtc: window.startUtc,
endUtc: window.endUtc,
}))
.filter(
(window) => /^\d{2}:\d{2}$/.test(window.startUtc) && /^\d{2}:\d{2}$/.test(window.endUtc)
);
if (!value.enabled && windows.length === 0) return null;
return { enabled: value.enabled, mode: value.mode, windows };
}
export function formatPeakHourSummary(value: unknown): string | null {
const config = cloneConfig(value);
if (!config.enabled || config.windows.length === 0) return null;
const mode = config.mode === "avoid" ? "Avoid" : "Block";
return `${mode} during ${config.windows.length} peak window${config.windows.length === 1 ? "" : "s"}`;
}
export default function PeakHourProtectionEditor({
value,
onChange,
t,
}: {
value: PeakHourProtectionConfig;
onChange: (next: PeakHourProtectionConfig) => void;
t: ProviderMessageTranslator;
}) {
const updateWindow = (id: string | undefined, patch: Partial<PeakHourWindow>) => {
onChange({
...value,
windows: value.windows.map((window) => (window.id === id ? { ...window, ...patch } : window)),
});
};
const toggleDay = (window: PeakHourWindow, day: PeakHourProtectionDay) => {
const days = new Set(window.days || []);
if (days.has(day)) days.delete(day);
else days.add(day);
updateWindow(window.id, { days: Array.from(days) });
};
const applyPreset = (provider: "deepseek" | "zai") => {
const windows =
provider === "deepseek"
? [weekdayWindow("01:00", "04:00"), weekdayWindow("06:00", "10:00")]
: [{ ...newWindow(), name: "Daily peak", startUtc: "06:00", endUtc: "10:00" }];
onChange({ enabled: true, mode: value.mode, windows });
};
return (
<div className="flex flex-col gap-4 rounded-lg border border-amber-500/30 bg-amber-500/5 p-4">
<Toggle
checked={value.enabled}
onChange={(enabled) => onChange({ ...value, enabled })}
label={providerText(t, "peakHourProtectionLabel", "Peak-hour protection")}
description={providerText(
t,
"peakHourProtectionDescription",
"Block this connection during configured UTC peak-hour windows. This avoids uncertain peak multipliers instead of trying to price them."
)}
/>
<Select
label={providerText(t, "peakHourProtectionModeLabel", "Protection mode")}
value={value.mode}
options={[
{
value: "block",
label: providerText(t, "peakHourProtectionModeBlock", "Block requests"),
},
{
value: "avoid",
label: providerText(t, "peakHourProtectionModeAvoid", "Avoid in routing"),
},
]}
onChange={(event) =>
onChange({ ...value, mode: event.target.value === "avoid" ? "avoid" : "block" })
}
hint={providerText(
t,
"peakHourProtectionModeHint",
"Direct requests fail while active; combo/auto routing skips protected connections when alternatives exist."
)}
/>
<div className="flex flex-wrap gap-2">
<Button size="sm" variant="secondary" onClick={() => applyPreset("deepseek")}>
{providerText(t, "peakHourDeepSeekPreset", "Use DeepSeek preset")}
</Button>
<Button size="sm" variant="secondary" onClick={() => applyPreset("zai")}>
{providerText(t, "peakHourZaiPreset", "Use Z.ai preset")}
</Button>
<Button
size="sm"
variant="secondary"
icon="add"
onClick={() => onChange({ ...value, windows: [...value.windows, newWindow()] })}
>
{providerText(t, "peakHourAddWindow", "Add window")}
</Button>
</div>
<div className="flex flex-col gap-3">
{value.windows.length === 0 ? (
<p className="text-xs text-text-muted">
{providerText(t, "peakHourNoWindows", "No peak-hour windows configured.")}
</p>
) : (
value.windows.map((window) => (
<div key={window.id} className="rounded-lg border border-border/70 bg-surface/50 p-3">
<div className="mb-3 flex items-center justify-between gap-2">
<Input
label={providerText(t, "peakHourWindowName", "Window name")}
value={window.name || ""}
onChange={(event) => updateWindow(window.id, { name: event.target.value })}
placeholder={providerText(
t,
"peakHourWindowNamePlaceholder",
"e.g. weekday peak"
)}
/>
<Button
size="sm"
variant="ghost"
icon="delete"
onClick={() =>
onChange({
...value,
windows: value.windows.filter((entry) => entry.id !== window.id),
})
}
/>
</div>
<div className="grid grid-cols-1 gap-3 sm:grid-cols-2">
<Input
label={providerText(t, "peakHourStartUtc", "Start UTC")}
type="time"
value={window.startUtc}
onChange={(event) => updateWindow(window.id, { startUtc: event.target.value })}
/>
<Input
label={providerText(t, "peakHourEndUtc", "End UTC")}
type="time"
value={window.endUtc}
onChange={(event) => updateWindow(window.id, { endUtc: event.target.value })}
/>
</div>
<div className="mt-3">
<p className="mb-2 text-xs font-medium text-text-muted">
{providerText(t, "peakHourDays", "Days (empty = every day)")}
</p>
<div className="flex flex-wrap gap-1.5">
{PEAK_HOUR_PROTECTION_DAYS.map((day) => {
const active = (window.days || []).includes(day);
return (
<button
type="button"
key={day}
onClick={() => toggleDay(window, day)}
className={`rounded-full px-2 py-1 text-xs font-medium transition-colors ${
active
? "bg-amber-500 text-white"
: "bg-muted/60 text-text-muted hover:bg-muted"
}`}
>
{DAY_LABELS[day]}
</button>
);
})}
</div>
</div>
</div>
))
)}
</div>
</div>
);
}

View File

@@ -59,12 +59,6 @@ import AgentrouterConsoleFields from "./AgentrouterConsoleFields";
import QuotaScrapingFields, { EMPTY_QUOTA_SCRAPING_FIELDS } from "./QuotaScrapingFields";
import GlmTeamQuotaFields, { EMPTY_GLM_TEAM_QUOTA_FIELDS } from "./GlmTeamQuotaFields";
import ProviderRegionField, { getProviderRegionConfig } from "./AlibabaProviderRegionField";
import PeakHourProtectionEditor, {
EMPTY_PEAK_HOUR_PROTECTION,
formatPeakHourSummary,
normalizePeakHourProtectionForSave,
} from "../PeakHourProtectionEditor";
import type { PeakHourProtectionConfig } from "@/lib/providers/peakHourProtection";
export interface EditConnectionModalConnection {
id?: string;
name?: string;
@@ -160,7 +154,6 @@ export default function EditConnectionModal({
runtimeKey: "",
connectorName: stringField(connectionProviderSpecificData?.connectorName) || "OmniRoute Codex",
m365Tier: normalizeM365TierValue(connectionProviderSpecificData?.tier) as M365TierValue,
peakHourProtection: { ...EMPTY_PEAK_HOUR_PROTECTION, windows: [] } as PeakHourProtectionConfig,
});
const [testing, setTesting] = useState(false);
const [testResult, setTestResult] = useState(null);
@@ -398,22 +391,6 @@ export default function EditConnectionModal({
connectorName:
stringField(connection.providerSpecificData?.connectorName) || "OmniRoute Codex",
m365Tier: normalizeM365TierValue(connection.providerSpecificData?.tier) as M365TierValue,
peakHourProtection: {
...EMPTY_PEAK_HOUR_PROTECTION,
...((connection.providerSpecificData?.peakHourProtection as PeakHourProtectionConfig) ||
{}),
windows: Array.isArray(
(
connection.providerSpecificData?.peakHourProtection as
PeakHourProtectionConfig | undefined
)?.windows
)
? [
...(connection.providerSpecificData?.peakHourProtection as PeakHourProtectionConfig)
.windows,
]
: [],
},
});
const existing = connection.providerSpecificData?.extraApiKeys;
setExtraApiKeys(Array.isArray(existing) ? existing : []);
@@ -722,9 +699,6 @@ export default function EditConnectionModal({
}
if (updates.providerSpecificData) {
updates.providerSpecificData.disableCooling = formData.disableCooling ? true : undefined;
updates.providerSpecificData.peakHourProtection = normalizePeakHourProtectionForSave(
formData.peakHourProtection
);
// Explicit `null`, not `undefined`: the PUT route merges
// { ...existing, ...incoming }, so omitting the key would keep the previous
// choice and switching back to the default would never take effect.
@@ -870,16 +844,6 @@ export default function EditConnectionModal({
label={t("disableCoolingLabel")}
description={t("disableCoolingDescription")}
/>
<PeakHourProtectionEditor
value={formData.peakHourProtection}
onChange={(peakHourProtection) => setFormData({ ...formData, peakHourProtection })}
t={t}
/>
{formatPeakHourSummary(formData.peakHourProtection) && (
<p className="text-xs text-text-muted">
{formatPeakHourSummary(formData.peakHourProtection)}
</p>
)}
</div>
<QuotaScrapingFields
provider={provider}

View File

@@ -29,7 +29,7 @@ import {
import { providerAllowsOptionalApiKey } from "@/shared/constants/providers";
import { shouldUseApiKeyConnectionTest } from "./webSessionTestDispatch";
import { testCodexAppServerConnection, makeDiagnosis } from "./codexAppServerHealth";
import { recoverKeyHealth } from "@omniroute/open-sse/services/apiKeyRotator.ts";
import { removeConnectionHealth } from "@omniroute/open-sse/services/apiKeyRotator.ts";
import { shouldClearErrorStateOnValidProbe } from "@/lib/usage/providerLimits";
import { isConnectionUnavailableToAuxiliaryActivity } from "@/lib/exclusiveLeaseIsolation";
import { classifyAmbiguousOrAuthError, type ClassifyFailureArgs } from "./mistralAmbiguousAuth";
@@ -1125,11 +1125,7 @@ export async function testSingleConnection(connectionId: string, validationModel
lastError: clearErrorState ? null : result.valid ? connection.lastError : result.error,
lastErrorAt: clearErrorState ? null : result.valid ? connection.lastErrorAt : now,
lastTested: now,
lastErrorType: clearErrorState
? null
: result.valid
? connection.lastErrorType
: diagnosis.type,
lastErrorType: clearErrorState ? null : result.valid ? connection.lastErrorType : diagnosis.type,
lastErrorSource: clearErrorState
? null
: result.valid
@@ -1151,11 +1147,16 @@ export async function testSingleConnection(connectionId: string, validationModel
if (clearErrorState) {
updateData.backoffLevel = 0;
}
if (result.valid && (connection.apiKey || connection.accessToken)) {
const recovered = recoverKeyHealth(connectionId, "primary", connection.providerSpecificData);
if (recovered) updateData.providerSpecificData = recovered;
const psd = connection?.providerSpecificData as Record<string, unknown> | undefined;
updateData.providerSpecificData = {
...(psd || {}),
apiKeyHealth: {},
};
try {
removeConnectionHealth(connectionId);
} catch {}
}
// If token was refreshed, update tokens in DB

View File

@@ -54,13 +54,6 @@ const FETCH_PROVIDERS: FetchProviderDef[] = [
freeMonthlyQuota: 0,
fetchFormats: ["markdown", "html"],
},
{
id: "nimble-search",
name: "Nimble Extract",
costPerQuery: 0.005,
freeMonthlyQuota: 0,
fetchFormats: ["markdown", "html", "links", "screenshot"],
},
];
// ---------------------------------------------------------------------------

View File

@@ -11314,9 +11314,7 @@
"noPayload": "لا توجد بيانات حمولة متاحة لهذه السجل.",
"correlationId": "معرف الترابط",
"correlationIdValue": "معرّف الارتباط: {id}",
"detailedPayloadInfo": "قم بتمكين تسجيل الدخول التفصيلي أولاً إذا كنت ترغب في عرض الحمولة من أربعة مراحل للعميل/المزود للطلبات الجديدة.",
"copyAll": "Copy all",
"copiedAll": "Copied all"
"detailedPayloadInfo": "قم بتمكين تسجيل الدخول التفصيلي أولاً إذا كنت ترغب في عرض الحمولة من أربعة مراحل للعميل/المزود للطلبات الجديدة."
}
},
"proxyLogger": {

View File

@@ -11314,9 +11314,7 @@
"noPayload": "Bu log girişi üçün heç bir yük məlumatı mövcud deyil.",
"correlationId": "Korrelyasiya ID-si",
"correlationIdValue": "Korrelyasiya ID: {id}",
"detailedPayloadInfo": "Əvvəlcə ətraflı qeydiyyatı aktiv edin, əgər yeni tələblər üçün dörd mərhələli müştəri/təchizatçı yük görünüşünü istəyirsinizsə.",
"copyAll": "Copy all",
"copiedAll": "Copied all"
"detailedPayloadInfo": "Əvvəlcə ətraflı qeydiyyatı aktiv edin, əgər yeni tələblər üçün dörd mərhələli müştəri/təchizatçı yük görünüşünü istəyirsinizsə."
}
},
"proxyLogger": {

View File

@@ -11314,9 +11314,7 @@
"noPayload": "Няма налични данни за полезен товар за този запис в логовете.",
"correlationId": "Идентификатор на корелацията",
"correlationIdValue": "Идентификатор на корелация: {id}",
"detailedPayloadInfo": "Първо активирайте подробното регистриране, ако искате изгледа на четирите етапа на клиент/доставчик за нови заявки.",
"copyAll": "Copy all",
"copiedAll": "Copied all"
"detailedPayloadInfo": "Първо активирайте подробното регистриране, ако искате изгледа на четирите етапа на клиент/доставчик за нови заявки."
}
},
"proxyLogger": {

View File

@@ -11314,9 +11314,7 @@
"noPayload": "এই লগ এন্ট্রির জন্য কোনো পেলোড ডেটা উপলব্ধ নেই।",
"correlationId": "সংযোগ আইডি",
"correlationIdValue": "সংযোগ আইডি: {id}",
"detailedPayloadInfo": "নতুন অনুরোধের জন্য চার-পর্যায়ের ক্লায়েন্ট/প্রদানকারী পে-লোড ভিউ পেতে হলে প্রথমে বিস্তারিত লগিং সক্ষম করুন।",
"copyAll": "Copy all",
"copiedAll": "Copied all"
"detailedPayloadInfo": "নতুন অনুরোধের জন্য চার-পর্যায়ের ক্লায়েন্ট/প্রদানকারী পে-লোড ভিউ পেতে হলে প্রথমে বিস্তারিত লগিং সক্ষম করুন।"
}
},
"proxyLogger": {

View File

@@ -11314,9 +11314,7 @@
"noPayload": "Žádná data o payloadu nejsou k dispozici pro tento záznam.",
"correlationId": "ID korelace",
"correlationIdValue": "ID korelace: {id}",
"detailedPayloadInfo": "Nejprve povolte podrobné protokolování, pokud chcete čtyřstupňový pohled na payload klienta/poskytovatele pro nové požadavky.",
"copyAll": "Copy all",
"copiedAll": "Copied all"
"detailedPayloadInfo": "Nejprve povolte podrobné protokolování, pokud chcete čtyřstupňový pohled na payload klienta/poskytovatele pro nové požadavky."
}
},
"proxyLogger": {

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