Compare commits

...

23 Commits

Author SHA1 Message Date
Markus Hartung
c84e980402 fix(sse): synthesize the native Codex turn envelope for chatgpt-session
The executor passed the OpenAI chat-completions body through as `_rawBody`, but the
vendored browser adapter reads `_rawBody` as a native Codex Responses body and demands
turn identity from it. Every request therefore failed with "ChatGPT web requires native
Codex turn_id metadata for browser-session replay" before any browser work started; no
unit test caught it because they all mock the adapter call.

`buildParsedRequest` now builds the envelope itself: a fresh thread/turn id pair per
request (this provider serves stateless chat completions, so each request genuinely is
its own turn), the turn metadata as the JSON string the real client sends, an `input`
array mirroring the parsed messages in Responses item shape, and the current-turn
passthrough marker on the last user item only. The `rawBody` parameter is gone so no
caller can reintroduce the passthrough.

Verified against the real adapter through the production code path: the turn now reaches
stage=browser_page and fails only on the missing login state.
2026-09-02 18:32:31 -03:00
Markus Hartung
6850e93002 feat(executors): make chatgpt-session stream-open timeout tunable
Add OMNIROUTE_CHATGPT_SESSION_STREAM_OPEN_TIMEOUT_MS to override the
30s CHATGPT_SESSION_STREAM_OPEN_TIMEOUT_MS default, mirroring
resolveDirectHeadersTimeoutMs's validation. Resolved per request in
the executor so a changed env var takes effect without a restart.
2026-09-02 15:28:38 -03:00
Markus Hartung
d9041b7070 fix(sse): bound the chatgpt-session stream-open gate with a deadline
The gate withholds the HTTP response until the first event that also counts
as committed output on the buffered path, so both paths agree on failover
status. While it is closed nothing reaches the client — not even a keepalive,
since the first byte commits the 200 — and a turn here runs in a real browser
that can think for a long time, so a client with an idle timeout could hang up
on a perfectly healthy turn.

Race the gate against CHATGPT_SESSION_STREAM_OPEN_TIMEOUT_MS (30s, overridable
per call through the new options argument). A committing event or an error
still wins exactly as before; only when the deadline elapses with neither does
the stream open anyway and keep consuming the same iterator, replaying the
buffered reasoning after the role chunk so nothing is lost or reordered. The
in-flight next() the deadline outran is carried into the stream body instead of
being abandoned, otherwise its event would vanish behind a second next(). The
timer is unref'd and cleared on every exit path.

Every failure that needs a real HTTP status — no browser, missing or expired
credentials, rate limiting, an incompatible route — surfaces within seconds,
far inside the window.
2026-09-02 15:16:05 -03:00
Markus Hartung
7183252e4b fix(sse): accept assistant refusal content parts in chatgpt-session
A `{ type: "refusal", refusal: "…" }` part is legal inside an assistant
message in the chat-completions spec, so a client replaying its own
conversation history sends it back. The unsupported-part guard rejected it
with a 400 and failed the whole request.

Fold a refusal part into the flattened content the way a text part is folded,
reading its `refusal` field. A refusal part whose payload is missing or is not
a string still falls through to the existing rejection — it is never dropped
in silence — and every other unrecognised part keeps throwing as before.
2026-09-02 15:10:53 -03:00
Markus Hartung
07c5e01b59 docs(providers): record the adapter task-framing limitation for chatgpt-session
Turns are relayed through the vendored adapter's task-framing prompt, which API clients
never see; note that a tool turn may be answered with a local-tool refusal instead of a
tool block, explicitly flagged as unconfirmed pending live validation.

The markdown tables are reflowed by the lint-staged Prettier hook.
2026-09-02 08:30:57 -03:00
Markus Hartung
8483b98205 fix(sse): reject unsupported chatgpt-session content parts instead of dropping them
textFromContent threw for image parts but silently skipped everything else, so a `file`
or `input_audio` part vanished and the model answered about content it never received.
Any part that is neither a text part nor an already-handled image part now throws
ChatGptSessionInputError with code "unsupported_content_part" (a terminal 400).
2026-09-02 08:30:57 -03:00
Markus Hartung
dc8e458dfe fix(dashboard): encode chatgpt-session credentials with the shared predicate
Both connection modals wrapped the pasted Cookie header in the {version, cookie,
runtimeKey} envelope only when provider === "chatgpt-web-codex", but the create and
update routes send every provider accepted by usesChatGptBrowserSessionCredentials()
through finalizeValidatedChatGptWebCodexSecrets, whose first statement is JSON.parse. A
chatgpt-session save therefore always failed with 400 "Unexpected token '_',
"__Secure-n"... is not valid JSON" — the dashboard could never create or update a
connection.

Both modals now key the envelope off the same predicate the routes use, so client and
server cannot drift again, and omit runtimeKey when empty (this provider never has one).

The two failure paths returned error.message raw, echoing the first characters of the
pasted credential; they now go through sanitizeErrorMessage, and their untranslated
German fallback is replaced with provider-neutral English.
2026-09-02 08:30:50 -03:00
Markus Hartung
9ebd91cdb9 fix(sse): trust the adapter's explicit status over message patterns
CONTRACT CHANGE. classifyChatGptSessionError put message-pattern matching ahead of an
explicit numeric status, so an adapter error carrying status 503 whose prose mentioned
signing in was classified 401 session_expired — marking a healthy account's credentials
expired and pulling it out of rotation. The vendor documents the field as "Authoritative
upstream/proxy status when known; avoids message-based classification"
(vendor/codex-chatgpt-web/types.ts).

Explicit status now runs third (after ChatGptSessionInputError and TimeoutError, both of
which are stronger signals), uses the event's own code when present, and attaches
fallbackHint: "connection_cooldown" for 503/429 so a genuine outage cools one connection
instead of tripping the whole-provider breaker. Message matching is unchanged and still
essential — errors thrown by the executor itself carry no status.

ChatGptSessionStreamOpen's error arm now carries fallbackHint, so the executor uses the
bridge's classification directly instead of re-classifying the sanitized message.

The test "a matching message wins over an explicit upstream status" is deliberately
inverted and renamed; its comment records why.
2026-09-02 08:30:42 -03:00
Markus Hartung
30eabbef3b fix(sse): drop the Codex read-only banner and align chatgpt-session failover
The vendored adapter emits its "local Codex computer is unavailable" warning as a
commentary-phase text_delta on every fresh turn, because this provider pins
localToolsEnabled: false. The bridge treated every text_delta alike and let
assistant_boundary fall through to `default`, so 100% of answers were prefixed with the
banner. Commentary-phase deltas are now dropped on both the streaming and the buffered
path (not rerouted to reasoning_content — it is transport chatter, not model reasoning),
and assistant_boundary gets an explicit ignoring case.

Same commit aligns the streaming gate with the buffered failover: the stream no longer
commits a 200 on a heartbeat, an assistant boundary, a commentary delta, an empty text
delta or a thinking delta, so `thinking_delta` followed by `error{429}` now returns HTTP
429 on both paths instead of a 200 stream carrying an in-band error. Reasoning that
arrives while the gate is closed is buffered and replayed once the gate opens.
2026-09-02 08:30:33 -03:00
Markus Hartung
6bcc31bdf2 test: bump RESERVED_PREFIX_COUNT ratchet to 402 for chatgpt-session provider
The chatgpt-session provider adds two distinct reserved prefixes (id
chatgpt-session, alias cgpt-session), moving the shared reserved-prefix
set from 400 to 402 members. Adds explicit membership assertions for
both new prefixes alongside the count.
2026-09-02 07:57:07 -03:00
Markus Hartung
4457f89c3f docs(changelog): add chatgpt-session provider entry 2026-09-02 05:14:56 -03:00
Markus Hartung
177d64da97 docs(providers): document the chatgpt-session provider 2026-09-02 04:01:17 -03:00
Markus Hartung
4ae94a9329 fix(providers): route chatgpt-session through the shared browser-session credential lifecycle
Both /api/providers routes gated the raw-cookie -> verified-storage-state
finalize step on a hardcoded chatgpt-web-codex check, so chatgpt-session
connections never had their pasted cookie replaced, never released the
temporary validation directory, and leaked validationId into persisted
providerSpecificData. Introduce usesChatGptBrowserSessionCredentials() as
the single source of truth for both provider ids and use it in place of
the hardcoded equality checks. Also replace a brittle source-regex test
with one that proves the validation dispatch actually resolves.
2026-09-02 03:43:11 -03:00
Markus Hartung
16288c9928 feat(providers): wire chatgpt-session into the dashboard and validation 2026-09-02 03:20:31 -03:00
Markus Hartung
a853ce146e feat(providers): register the chatgpt-session provider and executor 2026-09-02 02:40:42 -03:00
Markus Hartung
71651d8c0a fix(providers): preserve chatgpt-session stream error classification and guarantee queue close
Keep the bridge's status/code authoritative on the streaming error branch instead of
re-deriving them from the message alone, which downgraded a name-classified 400 into a
breaker-tripping 502. Also guarantee events.close() runs even when the storage-state
persist path or a caller-supplied logger throws, so neither consumer can hang.
2026-09-02 02:26:16 -03:00
Markus Hartung
1f061d7251 feat(providers): add the chatgpt-session executor host 2026-09-02 02:12:26 -03:00
Markus Hartung
58d5d52c72 fix(providers): surface mid-stream chatgpt-session errors as structured SSE error chunks
- Route mid-stream error events through formatTranslatedStreamError instead of
  silently closing with finish_reason:stop, so a truncated turn is distinguishable
  from a completed one (AGENTS.md Hard Rule #6).
- Sanitize the message on the pre-stream error verdict as defense in depth.
- Cover the previously-untested gating paths: terminal event as the first
  meaningful event, source ending without a terminal event, empty source,
  heartbeat-only source, and incomplete with endTurn:true.
2026-09-02 02:04:07 -03:00
Markus Hartung
ed00c8c489 feat(providers): bridge chatgpt-session adapter events to OpenAI payloads 2026-09-02 01:56:21 -03:00
Markus Hartung
f16e0ed682 fix(providers): give TimeoutError name precedence over message matching in chatgpt-session error classification 2026-09-02 01:51:18 -03:00
Markus Hartung
5851d6e287 feat(providers): classify chatgpt-session failures onto the router contract 2026-09-02 01:41:59 -03:00
Markus Hartung
cc85011dd4 feat(providers): translate OpenAI messages for chatgpt-session 2026-09-02 01:35:36 -03:00
Markus Hartung
baa45e9a67 feat(providers): add chatgpt-session model route table 2026-09-02 01:25:34 -03:00
89 changed files with 3196 additions and 219 deletions

View File

@@ -2922,6 +2922,15 @@ QUOTA_STORE_DRIVER=sqlite
# CODEX_CHATGPT_WEB_BUN=/absolute/path/to/bun
# CODEX_WEB_GPT_BUN=/absolute/path/to/bun
# ─────────────────────────────────────────────────────────────────────────────
# ChatGPT Session provider — stream-open gate
# Used by: open-sse/executors/chatgpt-session/bridge.ts
# How long the stream-open gate waits for the first committing event before
# opening the stream anyway. Falls back to the 30s default when unset, empty,
# unparseable, non-finite, or not positive.
# ─────────────────────────────────────────────────────────────────────────────
# OMNIROUTE_CHATGPT_SESSION_STREAM_OPEN_TIMEOUT_MS=30000
# ─────────────────────────────────────────────────────────────────────────────
# Browser-login VNC sessions (optional — src/lib/vncSession/manifest.ts)
# Containerized Chromium+VNC used for interactive browser-login credential

View File

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

View File

@@ -7,7 +7,7 @@
# 🚀 OmniRoute — The Free AI Gateway
<img src="./docs/diagrams/readme-hero.svg" width="100%" alt="OmniRoute — Never stop coding. Every AI tool → 352 providers — 150+ free — through one endpoint. Claude Code, Codex, Cursor, Cline, Copilot & Antigravity into FREE Claude / GPT / Gemini with auto-fallback. RTK + Caveman stacked compression saves 1595% tokens (~89% avg) — never hit limits. 352 AI providers · 150+ free tiers · ~1.51B free tokens/mo · 19 routing strategies · $0 to start."/>
<img src="./docs/diagrams/readme-hero.svg" width="100%" alt="OmniRoute — Never stop coding. Every AI tool → 353 providers — 150+ free — through one endpoint. Claude Code, Codex, Cursor, Cline, Copilot & Antigravity into FREE Claude / GPT / Gemini with auto-fallback. RTK + Caveman stacked compression saves 1595% tokens (~89% avg) — never hit limits. 353 AI providers · 150+ free tiers · ~1.51B free tokens/mo · 19 routing strategies · $0 to start."/>
</div>
@@ -101,7 +101,7 @@
<tr>
<td align="right"><b>⚙️ Features</b></td>
<td align="center"><a href="#-combos--the-flagship">🎯 Combos</a></td>
<td align="center"><a href="#-352-ai-providers--154-catalog-marked-free">🌐 Providers</a></td>
<td align="center"><a href="#-353-ai-providers--154-catalog-marked-free">🌐 Providers</a></td>
<td align="center"><a href="#-full-cli--a2a--mcp">🔌 CLI &amp; MCP</a></td>
</tr>
<tr>
@@ -210,7 +210,7 @@ curl http://localhost:20128/v1/chat/completions \
</div>
<img src="./docs/diagrams/promise-pillars.svg" width="100%" alt="The Promise — One endpoint and 352 providers. Automatic fallback keeps routing while another healthy target is available. Six pillars: resilient fallback across 352 providers · up to 95% token savings on eligible workloads · $0 to start with 150+ free tiers and 53 recurring/keyless free-forever providers · 36 CLI/agent integrations through one config · OpenAI, Claude, Gemini and Responses API compatibility at /v1 · production controls including circuit breakers, TLS stealth, MCP 110 tools, A2A, memory, guardrails, evals and 39,000+ static test declarations across 5,100+ tracked test files."/>
<img src="./docs/diagrams/promise-pillars.svg" width="100%" alt="The Promise — One endpoint and 353 providers. Automatic fallback keeps routing while another healthy target is available. Six pillars: resilient fallback across 353 providers · up to 95% token savings on eligible workloads · $0 to start with 150+ free tiers and 53 recurring/keyless free-forever providers · 36 CLI/agent integrations through one config · OpenAI, Claude, Gemini and Responses API compatibility at /v1 · production controls including circuit breakers, TLS stealth, MCP 110 tools, A2A, memory, guardrails, evals and 39,000+ static test declarations across 5,100+ tracked test files."/>
<br/>
<br/>
@@ -463,7 +463,7 @@ All **19** strategies — mix & match per combo step:
</div>
<img src="./docs/diagrams/comparison-table.svg" width="100%" alt="What sets OmniRoute apart — a dated feature snapshot vs 9router, OpenRouter, CLIProxyAPI and LiteLLM across 13 capabilities. OmniRoute: 352 providers, 150+ free tiers built in, 19 routing strategies, 12-engine token compression, built-in MCP server with 110 tools, A2A agent protocol, persistent memory, guardrails, cloud agents, TLS fingerprint stealth, Desktop/Termux/PWA and 43 i18n UI locales. OmniRoute is MIT-licensed and self-hostable. Competitor capabilities and counts may change; see the linked methodology."/>
<img src="./docs/diagrams/comparison-table.svg" width="100%" alt="What sets OmniRoute apart — a dated feature snapshot vs 9router, OpenRouter, CLIProxyAPI and LiteLLM across 13 capabilities. OmniRoute: 353 providers, 150+ free tiers built in, 19 routing strategies, 12-engine token compression, built-in MCP server with 110 tools, A2A agent protocol, persistent memory, guardrails, cloud agents, TLS fingerprint stealth, Desktop/Termux/PWA and 43 i18n UI locales. OmniRoute is MIT-licensed and self-hostable. Competitor capabilities and counts may change; see the linked methodology."/>
<sub>📊 Full methodology &amp; per-feature detail vs 9router, OpenRouter, CLIProxyAPI &amp; LiteLLM → [`docs/comparison/OMNIROUTE_VS_ALTERNATIVES.md`](docs/comparison/OMNIROUTE_VS_ALTERNATIVES.md)</sub>
@@ -561,7 +561,7 @@ the current catalog at **[radar.omniroute.online/planos](https://radar.omniroute
- **🖼️ New endpoints** — `/v1/ocr` (Mistral OCR) and `/v1/audio/translations` (Whisper-style) round out the media surface. → [API Reference](docs/reference/API_REFERENCE.md)
- **🎨 Image / video / audio generation** — one API for media: xAI Grok Imagine & Novita AI video, ComfyUI, Magnific, Adobe Firefly, Segmind, and speech providers such as ElevenLabs. → [API Reference](docs/reference/API_REFERENCE.md)
- **🌍 Deployment & ops** — reverse-proxy `basePath`, browser-language auto-detect, per-key device tracking, root-less MITM trust, zh-TW localization. → [Environment](docs/reference/ENVIRONMENT.md)
- **🤝 More providers & agents** — cloud agents (Codex Cloud, Cursor, Devin, Jules), Grok Build (xAI) with browser + OAuth login, Ollama first-class card, Claude Opus 5 & Sonnet 5, Kimi official partnership (Code/Web/Moonshot), Zed, Requesty, SenseNova, Yuanbao, Agnes AI… and a refreshed **352-provider catalog**. → [Providers](docs/reference/PROVIDER_REFERENCE.md)
- **🤝 More providers & agents** — cloud agents (Codex Cloud, Cursor, Devin, Jules), Grok Build (xAI) with browser + OAuth login, Ollama first-class card, Claude Opus 5 & Sonnet 5, Kimi official partnership (Code/Web/Moonshot), Zed, Requesty, SenseNova, Yuanbao, Agnes AI… and a refreshed **353-provider catalog**. → [Providers](docs/reference/PROVIDER_REFERENCE.md)
- **📡 Routing transparency** — every response carries an `X-OmniRoute-Decision` header naming the strategy/provider/latency that served it, a new `cache-optimized` combo strategy + Auto-Combo `cacheAffinity` factor route repeat requests back to the connection holding the cached prefix, and a read-only `/v1/auto-combo/{channel}/candidates` endpoint exposes an `auto/*` channel's live candidate pool. → [Auto-Combo](docs/routing/AUTO-COMBO.md)
- **⚡ Local performance & infra** — one-click local Redis, Cloudflare Workers / Deno Deploy relay deployers, Bifrost & Mux as supervised embedded services. → [Embedded Services](docs/frameworks/EMBEDDED-SERVICES.md)
- **🧩 Also in the box** — plugin framework + marketplace, Omni/Agent/GitHub skills frameworks, Obsidian vault integration (22 MCP tools), OpenAI-compatible Batch & Files APIs, semantic response cache, gamification with leaderboards, ACP agent discovery (15 built-in agents), scheduled log export to BigQuery, `auto/chaos` fault injection, a Telegram bot bridge, an in-app version manager and LMArena-ELO free-provider rankings. → [Docs](docs/README.md)
@@ -645,11 +645,11 @@ of your shell history. → [CLI Integrations](docs/guides/CLI-INTEGRATIONS.md)
<div align="center">
## 🌐 352 AI Providers — 152 Catalog-Marked Free
## 🌐 353 AI Providers — 152 Catalog-Marked Free
</div>
> **352 registered providers** across the canonical chat, media, search, local, cloud-agent and system collections, including **152 carrying `hasFree: true` discovery metadata**. The chat model registry covers **229 providers / 2,554 distinct provider-model pairs / 1,283 raw model IDs**; the separate free-budget catalog has **446 per-model rows**, **38 recurring pools** and **53 recurring/keyless free-forever providers**. These are different denominators by design; definitions and pool-deduped calculations live in the [Provider Reference](docs/reference/PROVIDER_REFERENCE.md) and [Free Tiers](docs/reference/FREE_TIERS.md).
> **353 registered providers** across the canonical chat, media, search, local, cloud-agent and system collections, including **152 carrying `hasFree: true` discovery metadata**. The chat model registry covers **229 providers / 2,554 distinct provider-model pairs / 1,283 raw model IDs**; the separate free-budget catalog has **446 per-model rows**, **38 recurring pools** and **53 recurring/keyless free-forever providers**. These are different denominators by design; definitions and pool-deduped calculations live in the [Provider Reference](docs/reference/PROVIDER_REFERENCE.md) and [Free Tiers](docs/reference/FREE_TIERS.md).
<div align="center">

View File

@@ -0,0 +1,3 @@
- Added `chatgpt-session` (alias `cgpt-session`), a clean-room ChatGPT Web provider serving
`/v1/chat/completions` from an authenticated browser session. The retired common
`chatgpt-web` provider stays retired.

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 (352 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 86 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 86 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"/>
@@ -6,7 +6,7 @@
<path d="M 0 34 L 1200 34" stroke="#ffffff" stroke-opacity="0.08" stroke-width="1"/>
<circle cx="24" cy="17" r="6" fill="#ff5f56"/><circle cx="46" cy="17" r="6" fill="#ffbd2e"/><circle cx="68" cy="17" r="6" fill="#27c93f"/>
<text x="600" y="22" text-anchor="middle" font-family="Consolas, 'Courier New', monospace" font-size="13" fill="#71717a">omniroute &#8212; 86 top-level commands</text>
<g font-family="Consolas, 'Courier New', monospace" font-size="17"><animate attributeName="opacity" values="1;0;0" keyTimes="0;0.006;1" dur="18s" repeatCount="indefinite"/><text x="64" y="66" fill="#F7F6FC">omniroute providers list</text><text x="40" y="100" font-weight="700" fill="#38bdf8">OmniRoute Providers</text><text x="40" y="128" fill="#a1a1aa">1f3a9c2e&#160;&#160;anthropic&#160;&#160;&#160;Claude Max 20x&#160;&#160;&#160;&#160;<tspan fill='#22c55e'>active</tspan></text><text x="40" y="154" fill="#a1a1aa">8c2d5b1a&#160;&#160;codex&#160;&#160;&#160;&#160;&#160;&#160;&#160;Codex Pro (team)&#160;&#160;<tspan fill='#22c55e'>active</tspan></text><text x="40" y="180" fill="#a1a1aa">f4e0a97b&#160;&#160;glm&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;GLM Coding Plan&#160;&#160;&#160;<tspan fill='#22c55e'>active</tspan></text><text x="40" y="206" fill="#a1a1aa">03bd6e5f&#160;&#160;kimi&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Kimi K2 free&#160;&#160;&#160;&#160;&#160;&#160;<tspan fill='#22c55e'>active</tspan></text><text x="40" y="232" fill="#71717a">&#8230; 348 more providers</text></g><g opacity="1" font-family="Consolas, 'Courier New', monospace" font-size="17">
<g font-family="Consolas, 'Courier New', monospace" font-size="17"><animate attributeName="opacity" values="1;0;0" keyTimes="0;0.006;1" dur="18s" repeatCount="indefinite"/><text x="64" y="66" fill="#F7F6FC">omniroute providers list</text><text x="40" y="100" font-weight="700" fill="#38bdf8">OmniRoute Providers</text><text x="40" y="128" fill="#a1a1aa">1f3a9c2e&#160;&#160;anthropic&#160;&#160;&#160;Claude Max 20x&#160;&#160;&#160;&#160;<tspan fill='#22c55e'>active</tspan></text><text x="40" y="154" fill="#a1a1aa">8c2d5b1a&#160;&#160;codex&#160;&#160;&#160;&#160;&#160;&#160;&#160;Codex Pro (team)&#160;&#160;<tspan fill='#22c55e'>active</tspan></text><text x="40" y="180" fill="#a1a1aa">f4e0a97b&#160;&#160;glm&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;GLM Coding Plan&#160;&#160;&#160;<tspan fill='#22c55e'>active</tspan></text><text x="40" y="206" fill="#a1a1aa">03bd6e5f&#160;&#160;kimi&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Kimi K2 free&#160;&#160;&#160;&#160;&#160;&#160;<tspan fill='#22c55e'>active</tspan></text><text x="40" y="232" fill="#71717a">&#8230; 349 more providers</text></g><g opacity="1" font-family="Consolas, 'Courier New', monospace" font-size="17">
<animate attributeName="opacity" values="1;1;0;0" keyTimes="0;0.315;0.33;1" dur="18s" repeatCount="indefinite"/>
<text x="40" y="66" fill="#22c55e">$</text>
<g clip-path="url(#tw0)"><text x="64" y="66" fill="#F7F6FC">omniroute providers list</text></g>
@@ -14,7 +14,7 @@
<animate attributeName="x" calcMode="discrete" values="64;95;125;156;186;217;248;278;309;309" keyTimes="0.000;0.012;0.018;0.024;0.030;0.036;0.042;0.048;0.054;1" dur="18s" repeatCount="indefinite"/>
<animate attributeName="opacity" values="0;0;1;0.2;1;0.2;1;0;0" keyTimes="0;0.011;0.012;0.022;0.032;0.042;0.052;0.074;1" dur="18s" repeatCount="indefinite"/>
</rect>
<text x="40" y="100" font-weight="700" fill="#38bdf8" opacity="0"><animate attributeName="opacity" calcMode="discrete" values="0;0;1" keyTimes="0;0.045;0.047" dur="18s" repeatCount="indefinite"/>OmniRoute Providers</text><text x="40" y="128" fill="#a1a1aa" opacity="0"><animate attributeName="opacity" calcMode="discrete" values="0;0;1" keyTimes="0;0.053;0.055" dur="18s" repeatCount="indefinite"/>1f3a9c2e&#160;&#160;anthropic&#160;&#160;&#160;Claude Max 20x&#160;&#160;&#160;&#160;<tspan fill='#22c55e'>active</tspan></text><text x="40" y="154" fill="#a1a1aa" opacity="0"><animate attributeName="opacity" calcMode="discrete" values="0;0;1" keyTimes="0;0.061;0.063" dur="18s" repeatCount="indefinite"/>8c2d5b1a&#160;&#160;codex&#160;&#160;&#160;&#160;&#160;&#160;&#160;Codex Pro (team)&#160;&#160;<tspan fill='#22c55e'>active</tspan></text><text x="40" y="180" fill="#a1a1aa" opacity="0"><animate attributeName="opacity" calcMode="discrete" values="0;0;1" keyTimes="0;0.069;0.07100000000000001" dur="18s" repeatCount="indefinite"/>f4e0a97b&#160;&#160;glm&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;GLM Coding Plan&#160;&#160;&#160;<tspan fill='#22c55e'>active</tspan></text><text x="40" y="206" fill="#a1a1aa" opacity="0"><animate attributeName="opacity" calcMode="discrete" values="0;0;1" keyTimes="0;0.077;0.079" dur="18s" repeatCount="indefinite"/>03bd6e5f&#160;&#160;kimi&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Kimi K2 free&#160;&#160;&#160;&#160;&#160;&#160;<tspan fill='#22c55e'>active</tspan></text><text x="40" y="232" fill="#71717a" opacity="0"><animate attributeName="opacity" calcMode="discrete" values="0;0;1" keyTimes="0;0.085;0.08700000000000001" dur="18s" repeatCount="indefinite"/>&#8230; 348 more providers</text>
<text x="40" y="100" font-weight="700" fill="#38bdf8" opacity="0"><animate attributeName="opacity" calcMode="discrete" values="0;0;1" keyTimes="0;0.045;0.047" dur="18s" repeatCount="indefinite"/>OmniRoute Providers</text><text x="40" y="128" fill="#a1a1aa" opacity="0"><animate attributeName="opacity" calcMode="discrete" values="0;0;1" keyTimes="0;0.053;0.055" dur="18s" repeatCount="indefinite"/>1f3a9c2e&#160;&#160;anthropic&#160;&#160;&#160;Claude Max 20x&#160;&#160;&#160;&#160;<tspan fill='#22c55e'>active</tspan></text><text x="40" y="154" fill="#a1a1aa" opacity="0"><animate attributeName="opacity" calcMode="discrete" values="0;0;1" keyTimes="0;0.061;0.063" dur="18s" repeatCount="indefinite"/>8c2d5b1a&#160;&#160;codex&#160;&#160;&#160;&#160;&#160;&#160;&#160;Codex Pro (team)&#160;&#160;<tspan fill='#22c55e'>active</tspan></text><text x="40" y="180" fill="#a1a1aa" opacity="0"><animate attributeName="opacity" calcMode="discrete" values="0;0;1" keyTimes="0;0.069;0.07100000000000001" dur="18s" repeatCount="indefinite"/>f4e0a97b&#160;&#160;glm&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;GLM Coding Plan&#160;&#160;&#160;<tspan fill='#22c55e'>active</tspan></text><text x="40" y="206" fill="#a1a1aa" opacity="0"><animate attributeName="opacity" calcMode="discrete" values="0;0;1" keyTimes="0;0.077;0.079" dur="18s" repeatCount="indefinite"/>03bd6e5f&#160;&#160;kimi&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Kimi K2 free&#160;&#160;&#160;&#160;&#160;&#160;<tspan fill='#22c55e'>active</tspan></text><text x="40" y="232" fill="#71717a" opacity="0"><animate attributeName="opacity" calcMode="discrete" values="0;0;1" keyTimes="0;0.085;0.08700000000000001" dur="18s" repeatCount="indefinite"/>&#8230; 349 more providers</text>
</g><g opacity="0" font-family="Consolas, 'Courier New', monospace" font-size="17">
<animate attributeName="opacity" values="0;0;1;1;0;0" keyTimes="0;0.333;0.34800000000000003;0.648;0.663;1" dur="18s" repeatCount="indefinite"/>
<text x="40" y="66" fill="#22c55e">$</text>

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: 352 providers, 150+ 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, 150+ 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>
@@ -23,7 +23,7 @@
<g font-family="Inter, 'Segoe UI', Arial, Helvetica, system-ui, sans-serif">
<g opacity="0"><animate attributeName="opacity" values="0;1" dur="0.4s" begin="0.15s" fill="freeze"/>
<text x="44" y="196" font-size="14.5" fill="#c9d1d9">Providers</text>
<text x="440" y="196" text-anchor="middle" font-family="Inter, 'Segoe UI', Arial, Helvetica, system-ui, sans-serif" font-size="15" font-weight="800" fill="#7ee787">352</text>
<text x="440" y="196" text-anchor="middle" font-family="Inter, 'Segoe UI', Arial, Helvetica, system-ui, sans-serif" font-size="15" font-weight="800" fill="#7ee787">353</text>
<text x="604" y="196" text-anchor="middle" font-family="Inter, 'Segoe UI', Arial, Helvetica, system-ui, sans-serif" font-size="13.5" font-weight="600" fill="#8b949e">40+</text>
<text x="760" y="196" text-anchor="middle" font-family="Inter, 'Segoe UI', Arial, Helvetica, system-ui, sans-serif" font-size="13.5" font-weight="600" fill="#8b949e">400+*</text>
<text x="916" y="196" text-anchor="middle" font-family="Inter, 'Segoe UI', Arial, Helvetica, system-ui, sans-serif" font-size="13.5" font-weight="600" fill="#8b949e">~5</text>

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 352 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: 150+ providers with a free tier and 53 recurring or keyless free-forever providers. Every tool works: 36 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: 150+ providers with a free tier and 53 recurring or keyless free-forever providers. Every tool works: 36 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">352 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 352 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 352 providers — 150+ 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: 352 AI providers, 150+ 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 — 150+ 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, 150+ 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">352 providers</tspan><tspan fill="#7ee787" font-weight="800">150+ 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">150+ 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>
@@ -66,7 +66,7 @@
<!-- stat chips -->
<g font-family="Inter, 'Segoe UI', Arial, Helvetica, system-ui, sans-serif" text-anchor="middle">
<rect x="48" y="448" width="172" height="52" rx="12" fill="#161b22" stroke="#6c5ce7" stroke-opacity="0.55" stroke-width="1.5"/>
<text x="134" y="471" font-size="17" font-weight="800" fill="#a78bfa">352</text>
<text x="134" y="471" font-size="17" font-weight="800" fill="#a78bfa">353</text>
<text x="134" y="490" font-size="11" fill="#a1a1aa">AI PROVIDERS</text>
<rect x="234" y="448" width="172" height="52" rx="12" fill="#161b22" stroke="#22c55e" stroke-opacity="0.55" stroke-width="1.5"/>
<text x="320" y="471" font-size="17" font-weight="800" fill="#7ee787">90+</text>

Before

Width:  |  Height:  |  Size: 7.3 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

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 352 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in 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
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
│ │ └── manager.ts # MITM proxy manager
│ ├── shared/ # Shared utilities, components, and constants
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
│ │ ├── constants/ # Provider definitions (352), model lists, pricing, routing strategies, MCP scopes
│ │ ├── constants/ # Provider definitions (353), model lists, pricing, routing strategies, MCP scopes
│ │ ├── contracts/ # Shared API contracts
│ │ ├── hooks/ # React hooks
│ │ ├── middleware/ # Shared middleware utilities
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **352 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)
- **19 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, cache-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -478,7 +478,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **352-provider catalog** with 150+ free tiers, one-click account imports, and bulk key add
- **353-provider catalog** with 150+ 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`
- **16-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 352 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in 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
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
│ │ └── manager.ts # MITM proxy manager
│ ├── shared/ # Shared utilities, components, and constants
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
│ │ ├── constants/ # Provider definitions (352), model lists, pricing, routing strategies, MCP scopes
│ │ ├── constants/ # Provider definitions (353), model lists, pricing, routing strategies, MCP scopes
│ │ ├── contracts/ # Shared API contracts
│ │ ├── hooks/ # React hooks
│ │ ├── middleware/ # Shared middleware utilities
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **352 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)
- **19 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, cache-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -478,7 +478,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **352-provider catalog** with 150+ free tiers, one-click account imports, and bulk key add
- **353-provider catalog** with 150+ 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`
- **16-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 352 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in 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
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
│ │ └── manager.ts # MITM proxy manager
│ ├── shared/ # Shared utilities, components, and constants
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
│ │ ├── constants/ # Provider definitions (352), model lists, pricing, routing strategies, MCP scopes
│ │ ├── constants/ # Provider definitions (353), model lists, pricing, routing strategies, MCP scopes
│ │ ├── contracts/ # Shared API contracts
│ │ ├── hooks/ # React hooks
│ │ ├── middleware/ # Shared middleware utilities
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **352 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)
- **19 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, cache-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -478,7 +478,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **352-provider catalog** with 150+ free tiers, one-click account imports, and bulk key add
- **353-provider catalog** with 150+ 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`
- **16-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 352 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in 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
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
│ │ └── manager.ts # MITM proxy manager
│ ├── shared/ # Shared utilities, components, and constants
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
│ │ ├── constants/ # Provider definitions (352), model lists, pricing, routing strategies, MCP scopes
│ │ ├── constants/ # Provider definitions (353), model lists, pricing, routing strategies, MCP scopes
│ │ ├── contracts/ # Shared API contracts
│ │ ├── hooks/ # React hooks
│ │ ├── middleware/ # Shared middleware utilities
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **352 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)
- **19 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, cache-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -478,7 +478,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **352-provider catalog** with 150+ free tiers, one-click account imports, and bulk key add
- **353-provider catalog** with 150+ 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`
- **16-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 352 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in 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
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
│ │ └── manager.ts # MITM proxy manager
│ ├── shared/ # Shared utilities, components, and constants
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
│ │ ├── constants/ # Provider definitions (352), model lists, pricing, routing strategies, MCP scopes
│ │ ├── constants/ # Provider definitions (353), model lists, pricing, routing strategies, MCP scopes
│ │ ├── contracts/ # Shared API contracts
│ │ ├── hooks/ # React hooks
│ │ ├── middleware/ # Shared middleware utilities
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **352 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)
- **19 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, cache-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -478,7 +478,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **352-provider catalog** with 150+ free tiers, one-click account imports, and bulk key add
- **353-provider catalog** with 150+ 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`
- **16-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 352 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in 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
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
│ │ └── manager.ts # MITM proxy manager
│ ├── shared/ # Shared utilities, components, and constants
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
│ │ ├── constants/ # Provider definitions (352), model lists, pricing, routing strategies, MCP scopes
│ │ ├── constants/ # Provider definitions (353), model lists, pricing, routing strategies, MCP scopes
│ │ ├── contracts/ # Shared API contracts
│ │ ├── hooks/ # React hooks
│ │ ├── middleware/ # Shared middleware utilities
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **352 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)
- **19 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, cache-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -478,7 +478,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **352-provider catalog** with 150+ free tiers, one-click account imports, and bulk key add
- **353-provider catalog** with 150+ 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`
- **16-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 352 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in 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
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
│ │ └── manager.ts # MITM proxy manager
│ ├── shared/ # Shared utilities, components, and constants
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
│ │ ├── constants/ # Provider definitions (352), model lists, pricing, routing strategies, MCP scopes
│ │ ├── constants/ # Provider definitions (353), model lists, pricing, routing strategies, MCP scopes
│ │ ├── contracts/ # Shared API contracts
│ │ ├── hooks/ # React hooks
│ │ ├── middleware/ # Shared middleware utilities
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **352 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)
- **19 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, cache-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -478,7 +478,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **352-provider catalog** with 150+ free tiers, one-click account imports, and bulk key add
- **353-provider catalog** with 150+ 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`
- **16-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 352 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in 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
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
│ │ └── manager.ts # MITM proxy manager
│ ├── shared/ # Shared utilities, components, and constants
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
│ │ ├── constants/ # Provider definitions (352), model lists, pricing, routing strategies, MCP scopes
│ │ ├── constants/ # Provider definitions (353), model lists, pricing, routing strategies, MCP scopes
│ │ ├── contracts/ # Shared API contracts
│ │ ├── hooks/ # React hooks
│ │ ├── middleware/ # Shared middleware utilities
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **352 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)
- **19 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, cache-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -478,7 +478,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **352-provider catalog** with 150+ free tiers, one-click account imports, and bulk key add
- **353-provider catalog** with 150+ 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`
- **16-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 352 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in 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
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
│ │ └── manager.ts # MITM proxy manager
│ ├── shared/ # Shared utilities, components, and constants
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
│ │ ├── constants/ # Provider definitions (352), model lists, pricing, routing strategies, MCP scopes
│ │ ├── constants/ # Provider definitions (353), model lists, pricing, routing strategies, MCP scopes
│ │ ├── contracts/ # Shared API contracts
│ │ ├── hooks/ # React hooks
│ │ ├── middleware/ # Shared middleware utilities
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **352 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)
- **19 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, cache-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -478,7 +478,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **352-provider catalog** with 150+ free tiers, one-click account imports, and bulk key add
- **353-provider catalog** with 150+ 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`
- **16-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 352 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in 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
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
│ │ └── manager.ts # MITM proxy manager
│ ├── shared/ # Shared utilities, components, and constants
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
│ │ ├── constants/ # Provider definitions (352), model lists, pricing, routing strategies, MCP scopes
│ │ ├── constants/ # Provider definitions (353), model lists, pricing, routing strategies, MCP scopes
│ │ ├── contracts/ # Shared API contracts
│ │ ├── hooks/ # React hooks
│ │ ├── middleware/ # Shared middleware utilities
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **352 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)
- **19 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, cache-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -478,7 +478,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **352-provider catalog** with 150+ free tiers, one-click account imports, and bulk key add
- **353-provider catalog** with 150+ 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`
- **16-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 352 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in 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
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
│ │ └── manager.ts # MITM proxy manager
│ ├── shared/ # Shared utilities, components, and constants
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
│ │ ├── constants/ # Provider definitions (352), model lists, pricing, routing strategies, MCP scopes
│ │ ├── constants/ # Provider definitions (353), model lists, pricing, routing strategies, MCP scopes
│ │ ├── contracts/ # Shared API contracts
│ │ ├── hooks/ # React hooks
│ │ ├── middleware/ # Shared middleware utilities
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **352 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)
- **19 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, cache-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -478,7 +478,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **352-provider catalog** with 150+ free tiers, one-click account imports, and bulk key add
- **353-provider catalog** with 150+ 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`
- **16-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 352 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in 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
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
│ │ └── manager.ts # MITM proxy manager
│ ├── shared/ # Shared utilities, components, and constants
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
│ │ ├── constants/ # Provider definitions (352), model lists, pricing, routing strategies, MCP scopes
│ │ ├── constants/ # Provider definitions (353), model lists, pricing, routing strategies, MCP scopes
│ │ ├── contracts/ # Shared API contracts
│ │ ├── hooks/ # React hooks
│ │ ├── middleware/ # Shared middleware utilities
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **352 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)
- **19 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, cache-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -478,7 +478,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **352-provider catalog** with 150+ free tiers, one-click account imports, and bulk key add
- **353-provider catalog** with 150+ 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`
- **16-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 352 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in 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
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
│ │ └── manager.ts # MITM proxy manager
│ ├── shared/ # Shared utilities, components, and constants
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
│ │ ├── constants/ # Provider definitions (352), model lists, pricing, routing strategies, MCP scopes
│ │ ├── constants/ # Provider definitions (353), model lists, pricing, routing strategies, MCP scopes
│ │ ├── contracts/ # Shared API contracts
│ │ ├── hooks/ # React hooks
│ │ ├── middleware/ # Shared middleware utilities
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **352 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)
- **19 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, cache-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -478,7 +478,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **352-provider catalog** with 150+ free tiers, one-click account imports, and bulk key add
- **353-provider catalog** with 150+ 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`
- **16-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 352 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in 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
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
│ │ └── manager.ts # MITM proxy manager
│ ├── shared/ # Shared utilities, components, and constants
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
│ │ ├── constants/ # Provider definitions (352), model lists, pricing, routing strategies, MCP scopes
│ │ ├── constants/ # Provider definitions (353), model lists, pricing, routing strategies, MCP scopes
│ │ ├── contracts/ # Shared API contracts
│ │ ├── hooks/ # React hooks
│ │ ├── middleware/ # Shared middleware utilities
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **352 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)
- **19 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, cache-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -478,7 +478,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **352-provider catalog** with 150+ free tiers, one-click account imports, and bulk key add
- **353-provider catalog** with 150+ 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`
- **16-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 352 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in 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
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
│ │ └── manager.ts # MITM proxy manager
│ ├── shared/ # Shared utilities, components, and constants
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
│ │ ├── constants/ # Provider definitions (352), model lists, pricing, routing strategies, MCP scopes
│ │ ├── constants/ # Provider definitions (353), model lists, pricing, routing strategies, MCP scopes
│ │ ├── contracts/ # Shared API contracts
│ │ ├── hooks/ # React hooks
│ │ ├── middleware/ # Shared middleware utilities
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **352 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)
- **19 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, cache-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -478,7 +478,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **352-provider catalog** with 150+ free tiers, one-click account imports, and bulk key add
- **353-provider catalog** with 150+ 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`
- **16-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 352 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in 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
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
│ │ └── manager.ts # MITM proxy manager
│ ├── shared/ # Shared utilities, components, and constants
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
│ │ ├── constants/ # Provider definitions (352), model lists, pricing, routing strategies, MCP scopes
│ │ ├── constants/ # Provider definitions (353), model lists, pricing, routing strategies, MCP scopes
│ │ ├── contracts/ # Shared API contracts
│ │ ├── hooks/ # React hooks
│ │ ├── middleware/ # Shared middleware utilities
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **352 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)
- **19 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, cache-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -478,7 +478,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **352-provider catalog** with 150+ free tiers, one-click account imports, and bulk key add
- **353-provider catalog** with 150+ 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`
- **16-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 352 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in 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
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
│ │ └── manager.ts # MITM proxy manager
│ ├── shared/ # Shared utilities, components, and constants
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
│ │ ├── constants/ # Provider definitions (352), model lists, pricing, routing strategies, MCP scopes
│ │ ├── constants/ # Provider definitions (353), model lists, pricing, routing strategies, MCP scopes
│ │ ├── contracts/ # Shared API contracts
│ │ ├── hooks/ # React hooks
│ │ ├── middleware/ # Shared middleware utilities
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **352 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)
- **19 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, cache-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -478,7 +478,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **352-provider catalog** with 150+ free tiers, one-click account imports, and bulk key add
- **353-provider catalog** with 150+ 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`
- **16-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 352 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in 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
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
│ │ └── manager.ts # MITM proxy manager
│ ├── shared/ # Shared utilities, components, and constants
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
│ │ ├── constants/ # Provider definitions (352), model lists, pricing, routing strategies, MCP scopes
│ │ ├── constants/ # Provider definitions (353), model lists, pricing, routing strategies, MCP scopes
│ │ ├── contracts/ # Shared API contracts
│ │ ├── hooks/ # React hooks
│ │ ├── middleware/ # Shared middleware utilities
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **352 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)
- **19 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, cache-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -478,7 +478,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **352-provider catalog** with 150+ free tiers, one-click account imports, and bulk key add
- **353-provider catalog** with 150+ 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`
- **16-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 352 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in 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
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
│ │ └── manager.ts # MITM proxy manager
│ ├── shared/ # Shared utilities, components, and constants
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
│ │ ├── constants/ # Provider definitions (352), model lists, pricing, routing strategies, MCP scopes
│ │ ├── constants/ # Provider definitions (353), model lists, pricing, routing strategies, MCP scopes
│ │ ├── contracts/ # Shared API contracts
│ │ ├── hooks/ # React hooks
│ │ ├── middleware/ # Shared middleware utilities
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **352 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)
- **19 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, cache-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -478,7 +478,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **352-provider catalog** with 150+ free tiers, one-click account imports, and bulk key add
- **353-provider catalog** with 150+ 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`
- **16-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 352 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in 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
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
│ │ └── manager.ts # MITM proxy manager
│ ├── shared/ # Shared utilities, components, and constants
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
│ │ ├── constants/ # Provider definitions (352), model lists, pricing, routing strategies, MCP scopes
│ │ ├── constants/ # Provider definitions (353), model lists, pricing, routing strategies, MCP scopes
│ │ ├── contracts/ # Shared API contracts
│ │ ├── hooks/ # React hooks
│ │ ├── middleware/ # Shared middleware utilities
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **352 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)
- **19 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, cache-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -478,7 +478,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **352-provider catalog** with 150+ free tiers, one-click account imports, and bulk key add
- **353-provider catalog** with 150+ 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`
- **16-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 352 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in 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
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
│ │ └── manager.ts # MITM proxy manager
│ ├── shared/ # Shared utilities, components, and constants
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
│ │ ├── constants/ # Provider definitions (352), model lists, pricing, routing strategies, MCP scopes
│ │ ├── constants/ # Provider definitions (353), model lists, pricing, routing strategies, MCP scopes
│ │ ├── contracts/ # Shared API contracts
│ │ ├── hooks/ # React hooks
│ │ ├── middleware/ # Shared middleware utilities
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **352 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)
- **19 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, cache-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -478,7 +478,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **352-provider catalog** with 150+ free tiers, one-click account imports, and bulk key add
- **353-provider catalog** with 150+ 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`
- **16-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 352 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in 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
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
│ │ └── manager.ts # MITM proxy manager
│ ├── shared/ # Shared utilities, components, and constants
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
│ │ ├── constants/ # Provider definitions (352), model lists, pricing, routing strategies, MCP scopes
│ │ ├── constants/ # Provider definitions (353), model lists, pricing, routing strategies, MCP scopes
│ │ ├── contracts/ # Shared API contracts
│ │ ├── hooks/ # React hooks
│ │ ├── middleware/ # Shared middleware utilities
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **352 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)
- **19 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, cache-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -478,7 +478,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **352-provider catalog** with 150+ free tiers, one-click account imports, and bulk key add
- **353-provider catalog** with 150+ 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`
- **16-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 352 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in 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
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
│ │ └── manager.ts # MITM proxy manager
│ ├── shared/ # Shared utilities, components, and constants
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
│ │ ├── constants/ # Provider definitions (352), model lists, pricing, routing strategies, MCP scopes
│ │ ├── constants/ # Provider definitions (353), model lists, pricing, routing strategies, MCP scopes
│ │ ├── contracts/ # Shared API contracts
│ │ ├── hooks/ # React hooks
│ │ ├── middleware/ # Shared middleware utilities
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **352 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)
- **19 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, cache-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -478,7 +478,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **352-provider catalog** with 150+ free tiers, one-click account imports, and bulk key add
- **353-provider catalog** with 150+ 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`
- **16-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 352 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in 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
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
│ │ └── manager.ts # MITM proxy manager
│ ├── shared/ # Shared utilities, components, and constants
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
│ │ ├── constants/ # Provider definitions (352), model lists, pricing, routing strategies, MCP scopes
│ │ ├── constants/ # Provider definitions (353), model lists, pricing, routing strategies, MCP scopes
│ │ ├── contracts/ # Shared API contracts
│ │ ├── hooks/ # React hooks
│ │ ├── middleware/ # Shared middleware utilities
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **352 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)
- **19 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, cache-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -478,7 +478,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **352-provider catalog** with 150+ free tiers, one-click account imports, and bulk key add
- **353-provider catalog** with 150+ 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`
- **16-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 352 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in 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
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
│ │ └── manager.ts # MITM proxy manager
│ ├── shared/ # Shared utilities, components, and constants
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
│ │ ├── constants/ # Provider definitions (352), model lists, pricing, routing strategies, MCP scopes
│ │ ├── constants/ # Provider definitions (353), model lists, pricing, routing strategies, MCP scopes
│ │ ├── contracts/ # Shared API contracts
│ │ ├── hooks/ # React hooks
│ │ ├── middleware/ # Shared middleware utilities
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **352 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)
- **19 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, cache-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -478,7 +478,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **352-provider catalog** with 150+ free tiers, one-click account imports, and bulk key add
- **353-provider catalog** with 150+ 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`
- **16-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 352 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in 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
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
│ │ └── manager.ts # MITM proxy manager
│ ├── shared/ # Shared utilities, components, and constants
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
│ │ ├── constants/ # Provider definitions (352), model lists, pricing, routing strategies, MCP scopes
│ │ ├── constants/ # Provider definitions (353), model lists, pricing, routing strategies, MCP scopes
│ │ ├── contracts/ # Shared API contracts
│ │ ├── hooks/ # React hooks
│ │ ├── middleware/ # Shared middleware utilities
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **352 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)
- **19 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, cache-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -478,7 +478,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **352-provider catalog** with 150+ free tiers, one-click account imports, and bulk key add
- **353-provider catalog** with 150+ 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`
- **16-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 352 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in 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
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
│ │ └── manager.ts # MITM proxy manager
│ ├── shared/ # Shared utilities, components, and constants
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
│ │ ├── constants/ # Provider definitions (352), model lists, pricing, routing strategies, MCP scopes
│ │ ├── constants/ # Provider definitions (353), model lists, pricing, routing strategies, MCP scopes
│ │ ├── contracts/ # Shared API contracts
│ │ ├── hooks/ # React hooks
│ │ ├── middleware/ # Shared middleware utilities
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **352 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)
- **19 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, cache-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -478,7 +478,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **352-provider catalog** with 150+ free tiers, one-click account imports, and bulk key add
- **353-provider catalog** with 150+ 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`
- **16-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 352 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in 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
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
│ │ └── manager.ts # MITM proxy manager
│ ├── shared/ # Shared utilities, components, and constants
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
│ │ ├── constants/ # Provider definitions (352), model lists, pricing, routing strategies, MCP scopes
│ │ ├── constants/ # Provider definitions (353), model lists, pricing, routing strategies, MCP scopes
│ │ ├── contracts/ # Shared API contracts
│ │ ├── hooks/ # React hooks
│ │ ├── middleware/ # Shared middleware utilities
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **352 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)
- **19 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, cache-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -478,7 +478,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **352-provider catalog** with 150+ free tiers, one-click account imports, and bulk key add
- **353-provider catalog** with 150+ 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`
- **16-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 352 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in 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
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
│ │ └── manager.ts # MITM proxy manager
│ ├── shared/ # Shared utilities, components, and constants
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
│ │ ├── constants/ # Provider definitions (352), model lists, pricing, routing strategies, MCP scopes
│ │ ├── constants/ # Provider definitions (353), model lists, pricing, routing strategies, MCP scopes
│ │ ├── contracts/ # Shared API contracts
│ │ ├── hooks/ # React hooks
│ │ ├── middleware/ # Shared middleware utilities
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **352 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)
- **19 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, cache-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -478,7 +478,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **352-provider catalog** with 150+ free tiers, one-click account imports, and bulk key add
- **353-provider catalog** with 150+ 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`
- **16-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 352 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in 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
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
│ │ └── manager.ts # MITM proxy manager
│ ├── shared/ # Shared utilities, components, and constants
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
│ │ ├── constants/ # Provider definitions (352), model lists, pricing, routing strategies, MCP scopes
│ │ ├── constants/ # Provider definitions (353), model lists, pricing, routing strategies, MCP scopes
│ │ ├── contracts/ # Shared API contracts
│ │ ├── hooks/ # React hooks
│ │ ├── middleware/ # Shared middleware utilities
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **352 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)
- **19 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, cache-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -478,7 +478,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **352-provider catalog** with 150+ free tiers, one-click account imports, and bulk key add
- **353-provider catalog** with 150+ 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`
- **16-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 352 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in 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
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
│ │ └── manager.ts # MITM proxy manager
│ ├── shared/ # Shared utilities, components, and constants
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
│ │ ├── constants/ # Provider definitions (352), model lists, pricing, routing strategies, MCP scopes
│ │ ├── constants/ # Provider definitions (353), model lists, pricing, routing strategies, MCP scopes
│ │ ├── contracts/ # Shared API contracts
│ │ ├── hooks/ # React hooks
│ │ ├── middleware/ # Shared middleware utilities
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **352 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)
- **19 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, cache-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -478,7 +478,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **352-provider catalog** with 150+ free tiers, one-click account imports, and bulk key add
- **353-provider catalog** with 150+ 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`
- **16-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 352 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in 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
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
│ │ └── manager.ts # MITM proxy manager
│ ├── shared/ # Shared utilities, components, and constants
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
│ │ ├── constants/ # Provider definitions (352), model lists, pricing, routing strategies, MCP scopes
│ │ ├── constants/ # Provider definitions (353), model lists, pricing, routing strategies, MCP scopes
│ │ ├── contracts/ # Shared API contracts
│ │ ├── hooks/ # React hooks
│ │ ├── middleware/ # Shared middleware utilities
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **352 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)
- **19 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, cache-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -478,7 +478,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **352-provider catalog** with 150+ free tiers, one-click account imports, and bulk key add
- **353-provider catalog** with 150+ 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`
- **16-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 352 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in 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
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
│ │ └── manager.ts # MITM proxy manager
│ ├── shared/ # Shared utilities, components, and constants
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
│ │ ├── constants/ # Provider definitions (352), model lists, pricing, routing strategies, MCP scopes
│ │ ├── constants/ # Provider definitions (353), model lists, pricing, routing strategies, MCP scopes
│ │ ├── contracts/ # Shared API contracts
│ │ ├── hooks/ # React hooks
│ │ ├── middleware/ # Shared middleware utilities
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **352 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)
- **19 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, cache-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -478,7 +478,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **352-provider catalog** with 150+ free tiers, one-click account imports, and bulk key add
- **353-provider catalog** with 150+ 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`
- **16-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 352 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in 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
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
│ │ └── manager.ts # MITM proxy manager
│ ├── shared/ # Shared utilities, components, and constants
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
│ │ ├── constants/ # Provider definitions (352), model lists, pricing, routing strategies, MCP scopes
│ │ ├── constants/ # Provider definitions (353), model lists, pricing, routing strategies, MCP scopes
│ │ ├── contracts/ # Shared API contracts
│ │ ├── hooks/ # React hooks
│ │ ├── middleware/ # Shared middleware utilities
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **352 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)
- **19 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, cache-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -478,7 +478,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **352-provider catalog** with 150+ free tiers, one-click account imports, and bulk key add
- **353-provider catalog** with 150+ 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`
- **16-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 352 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in 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
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
│ │ └── manager.ts # MITM proxy manager
│ ├── shared/ # Shared utilities, components, and constants
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
│ │ ├── constants/ # Provider definitions (352), model lists, pricing, routing strategies, MCP scopes
│ │ ├── constants/ # Provider definitions (353), model lists, pricing, routing strategies, MCP scopes
│ │ ├── contracts/ # Shared API contracts
│ │ ├── hooks/ # React hooks
│ │ ├── middleware/ # Shared middleware utilities
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **352 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)
- **19 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, cache-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -478,7 +478,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **352-provider catalog** with 150+ free tiers, one-click account imports, and bulk key add
- **353-provider catalog** with 150+ 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`
- **16-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 352 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in 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
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
│ │ └── manager.ts # MITM proxy manager
│ ├── shared/ # Shared utilities, components, and constants
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
│ │ ├── constants/ # Provider definitions (352), model lists, pricing, routing strategies, MCP scopes
│ │ ├── constants/ # Provider definitions (353), model lists, pricing, routing strategies, MCP scopes
│ │ ├── contracts/ # Shared API contracts
│ │ ├── hooks/ # React hooks
│ │ ├── middleware/ # Shared middleware utilities
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **352 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)
- **19 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, cache-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -478,7 +478,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **352-provider catalog** with 150+ free tiers, one-click account imports, and bulk key add
- **353-provider catalog** with 150+ 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`
- **16-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 352 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in 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
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
│ │ └── manager.ts # MITM proxy manager
│ ├── shared/ # Shared utilities, components, and constants
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
│ │ ├── constants/ # Provider definitions (352), model lists, pricing, routing strategies, MCP scopes
│ │ ├── constants/ # Provider definitions (353), model lists, pricing, routing strategies, MCP scopes
│ │ ├── contracts/ # Shared API contracts
│ │ ├── hooks/ # React hooks
│ │ ├── middleware/ # Shared middleware utilities
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **352 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)
- **19 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, cache-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -478,7 +478,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **352-provider catalog** with 150+ free tiers, one-click account imports, and bulk key add
- **353-provider catalog** with 150+ 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`
- **16-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 352 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in 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
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
│ │ └── manager.ts # MITM proxy manager
│ ├── shared/ # Shared utilities, components, and constants
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
│ │ ├── constants/ # Provider definitions (352), model lists, pricing, routing strategies, MCP scopes
│ │ ├── constants/ # Provider definitions (353), model lists, pricing, routing strategies, MCP scopes
│ │ ├── contracts/ # Shared API contracts
│ │ ├── hooks/ # React hooks
│ │ ├── middleware/ # Shared middleware utilities
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **352 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)
- **19 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, cache-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -478,7 +478,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **352-provider catalog** with 150+ free tiers, one-click account imports, and bulk key add
- **353-provider catalog** with 150+ 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`
- **16-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 352 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in 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
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
│ │ └── manager.ts # MITM proxy manager
│ ├── shared/ # Shared utilities, components, and constants
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
│ │ ├── constants/ # Provider definitions (352), model lists, pricing, routing strategies, MCP scopes
│ │ ├── constants/ # Provider definitions (353), model lists, pricing, routing strategies, MCP scopes
│ │ ├── contracts/ # Shared API contracts
│ │ ├── hooks/ # React hooks
│ │ ├── middleware/ # Shared middleware utilities
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **352 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)
- **19 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, cache-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -478,7 +478,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **352-provider catalog** with 150+ free tiers, one-click account imports, and bulk key add
- **353-provider catalog** with 150+ 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`
- **16-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 352 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in 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
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
│ │ └── manager.ts # MITM proxy manager
│ ├── shared/ # Shared utilities, components, and constants
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
│ │ ├── constants/ # Provider definitions (352), model lists, pricing, routing strategies, MCP scopes
│ │ ├── constants/ # Provider definitions (353), model lists, pricing, routing strategies, MCP scopes
│ │ ├── contracts/ # Shared API contracts
│ │ ├── hooks/ # React hooks
│ │ ├── middleware/ # Shared middleware utilities
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **352 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)
- **19 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, cache-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -478,7 +478,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **352-provider catalog** with 150+ free tiers, one-click account imports, and bulk key add
- **353-provider catalog** with 150+ 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`
- **16-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 352 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in 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
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
│ │ └── manager.ts # MITM proxy manager
│ ├── shared/ # Shared utilities, components, and constants
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
│ │ ├── constants/ # Provider definitions (352), model lists, pricing, routing strategies, MCP scopes
│ │ ├── constants/ # Provider definitions (353), model lists, pricing, routing strategies, MCP scopes
│ │ ├── contracts/ # Shared API contracts
│ │ ├── hooks/ # React hooks
│ │ ├── middleware/ # Shared middleware utilities
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **352 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)
- **19 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, cache-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -478,7 +478,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **352-provider catalog** with 150+ free tiers, one-click account imports, and bulk key add
- **353-provider catalog** with 150+ 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`
- **16-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 352 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in 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
@@ -169,7 +169,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
│ │ └── manager.ts # MITM proxy manager
│ ├── shared/ # Shared utilities, components, and constants
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
│ │ ├── constants/ # Provider definitions (352), model lists, pricing, routing strategies, MCP scopes
│ │ ├── constants/ # Provider definitions (353), model lists, pricing, routing strategies, MCP scopes
│ │ ├── contracts/ # Shared API contracts
│ │ ├── hooks/ # React hooks
│ │ ├── middleware/ # Shared middleware utilities
@@ -281,7 +281,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **352 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)
- **19 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, cache-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -478,7 +478,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **352-provider catalog** with 150+ free tiers, one-click account imports, and bulk key add
- **353-provider catalog** with 150+ 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`
- **16-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

@@ -0,0 +1,85 @@
---
title: "Providers — ChatGPT Web (Session)"
version: 3.8.51
lastUpdated: 2026-09-02
---
# Providers — ChatGPT Web (Session)
`chatgpt-session` (alias `cgpt-session`) serves ordinary `/v1/chat/completions` requests from
an authenticated ChatGPT browser session. It is a clean-room implementation and shares no code
with the retired common `chatgpt-web` provider; the browser interaction reuses the MIT-noticed
implementation under `open-sse/vendor/codex-chatgpt-web/`.
## Relationship to the other ChatGPT providers
| Provider | Endpoint | Client |
| ----------------------- | ---------------------- | ---------------------------- |
| `chatgpt-session` | `/v1/chat/completions` | any OpenAI-compatible client |
| `chatgpt-web-codex` | `/v1/responses` | the native Codex CLI only |
| `chatgpt-web` (retired) | — | fails closed with HTTP 410 |
## Prerequisites
- a full Cookie header from a signed-in ChatGPT session;
- Chrome or Chromium, plus a graphical session or Xvfb on headless hosts;
- with the Docker `web` profile, the internal Chromium service from `docker-compose.yml`.
## Setup
1. Open the **ChatGPT Web (Session)** provider in the dashboard and add a connection.
2. Paste the full ChatGPT Cookie header.
3. Run the connection check. OmniRoute opens an isolated browser profile, verifies the session
and detects whether Sol and Pro are available for the account.
4. Save. The pasted cookie is replaced by the verified browser session state; the raw cookie is
not retained.
When the session expires, paste a fresh Cookie header and rerun the check.
## Models
| Model | Account requirement |
| ---------------------------- | -------------------------------- |
| `chatgpt-session/luna` | Free/Go accounts (Luna selector) |
| `chatgpt-session/think` | Free/Go accounts |
| `chatgpt-session/instant` | Sol-capable accounts |
| `chatgpt-session/medium` | Sol-capable accounts |
| `chatgpt-session/high` | Sol-capable accounts |
| `chatgpt-session/extra-high` | Pro-capable accounts |
| `chatgpt-session/pro` | Pro-capable accounts |
Each model pins one backend model and one reasoning effort. Requesting a route the account does
not expose fails closed with HTTP 400 instead of silently switching modes.
## Tool calling
Tool calling is prompt-emulated, the same contract `perplexity-web` and `gemini-web` use. Tool
turns are answered non-streaming and then replayed as a terminal SSE stream when the client
asked for one.
## Errors
| Condition | Status |
| -------------------------------------------- | ------------------------------------ |
| No Chrome or Chromium available | 503, with a connection-cooldown hint |
| Missing or unreadable credentials | 401 |
| Expired session | 401 |
| ChatGPT usage limit reached | 429 |
| Model not available for the account | 400 |
| ChatGPT interface changed (selector timeout) | 400 |
| Any other turn failure | 502 |
Cookies and session state never appear in responses or logs.
## Limitations
Phase 1 covers text chat only. Image generation and editing, citation links and conversation
resume are not implemented.
Every turn is relayed to ChatGPT through the vendored adapter's own task-framing prompt
(`open-sse/vendor/codex-chatgpt-web/adapters/chatgpt-web/prompt.ts`), which wraps the request
before it is typed into the web UI. That framing is not visible to API clients: what a client
sends is not literally what the ChatGPT session receives. One consequence to watch for is that a
tool turn may occasionally be answered with a refusal about local-tool access instead of a tool
block. This has not been observed on a live turn yet — it is a property of the framing that live
validation still has to confirm or rule out.

View File

@@ -24,6 +24,9 @@ invalidates their active session leases. It preserves connection history and API
allowlists; it does not add replacement access to an allowlist. The Codex provider and
its connections are not matched by this retirement.
For ordinary `/v1/chat/completions` traffic over a ChatGPT session, use the clean-room
`chatgpt-session` provider instead — see [CHATGPT_SESSION.md](./CHATGPT_SESSION.md).
## Prerequisites
- a full Cookie header from a signed-in ChatGPT session;

View File

@@ -5,6 +5,7 @@
"ALIBABA-QWEN-PROVIDER-FAMILIES",
"CLAUDE_WEB",
"CHATGPT_WEB",
"CHATGPT_SESSION",
"AGENTROUTER",
"ZED-DOCKER",
"CURSOR-DOCKER",

View File

@@ -749,6 +749,7 @@ REQUEST_TIMEOUT_MS (global override)
| `OMNIROUTE_CODEX_APPSERVER_AUTO_APPROVE` | `false` | Auto-approve the app-server's own approval prompts (command/file/permission execution on the host). Off by default — prompts are auto-denied; harness tool calls are unaffected (they travel the separate `item/tool/call` passthrough). Accepts `true`/`1`/`yes`. Per-connection override: `providerSpecificData.codexAppServerAutoApprove`. |
| `FETCH_HEADERS_TIMEOUT_MS` | = `FETCH_TIMEOUT_MS` | Time to receive response headers. |
| `OMNIROUTE_DIRECT_HEADERS_TIMEOUT_MS` | `30000` (30s) | Maximum response-start wait (ms) for each direct no-proxy attempt. A timeout retries once on a fresh socket; set `0` to disable the bound and retain the previous behavior. |
| `OMNIROUTE_CHATGPT_SESSION_STREAM_OPEN_TIMEOUT_MS` | `30000` (30s) | How long the `chatgpt-session` executor's stream-open gate waits for the first committing event before opening the stream anyway (`open-sse/executors/chatgpt-session/bridge.ts`). Falls back to the 30s default when unset, empty, unparseable, non-finite, or not positive. |
| `FETCH_BODY_TIMEOUT_MS` | = `FETCH_TIMEOUT_MS` | Time to receive the full response body. |
| `FETCH_CONNECT_TIMEOUT_MS` | `30000` | TCP connection establishment timeout. |
| `FETCH_KEEPALIVE_TIMEOUT_MS` | `4000` | Keep-alive socket idle timeout. |

View File

@@ -1,16 +1,16 @@
---
title: "Provider Reference"
version: 3.8.51
lastUpdated: 2026-08-30
lastUpdated: 2026-09-02
---
# Provider Reference
> **Auto-generated** from `src/shared/constants/providers.ts` — do not edit by hand.
> Regenerate with: `npm run gen:provider-reference`
> **Last generated:** 2026-08-30
> **Last generated:** 2026-09-02
Total providers: **352**. See category breakdown below.
Total providers: **353**. See category breakdown below.
## Categories
@@ -80,13 +80,14 @@ Use the dashboard at `/dashboard/providers` to enable, configure, and test each
| `zed` | `zd` | Zed IDE | OAuth | [link](https://zed.dev) | Zed stores LLM provider credentials (OpenAI, Anthropic, Google, Mistral, xAI) in the OS keychain. Use the Import button below to discover and import them automatically. |
| `zed-hosted` | — | Zed Hosted Models | OAuth | [link](https://zed.dev) | Sign in with your Zed account (native-app sign-in). OmniRoute generates a one-time RSA keypair and opens zed.dev to authorize it — on a remote/headless install, copy the resulting 127.0.0.1 callback URL from your browser's address bar and paste it back here. Distinct from the 'Zed IDE' credential-import entry above: this proxies chat completions through Zed's own hosted model aggregator (cloud.zed.dev), fronting Anthropic/OpenAI/Google/xAI models under your Zed plan. |
## Web Cookie Providers (31)
## Web Cookie Providers (32)
| ID | Alias | Name | Tags | Website | Notes | Tool calling |
|----|-------|------|------|---------|-------|--------------|
| `adapta-web` | `adp-web` | Adapta.org (Adapta One Web) | Web cookie | [link](https://agent.adapta.one) | Paste your __client cookie value from .clerk.agent.adapta.one (DevTools → Application → Cookies) | emulated |
| `adobe-firefly` | `firefly` | Adobe Firefly (Image/Video) | Web cookie | [link](https://firefly.adobe.com) | RECOMMENDED: firefly.adobe.com signed-in → F12 → Network → click firefly-3p.ff.adobe.io (generate-async or models/discovery) → Request Headers → Authorization → copy the token AFTER 'Bearer ' (starts with eyJ…). Cookie-only from firefly.adobe.com mints a GUEST token → 401/403; only multi-domain IMS cookies (adobelogin.com) or that Bearer JWT work. Unofficial/experimental media + Limits. | — |
| `blackbox-web` | `bb-web` | Blackbox Web (Subscription) | Web cookie | [link](https://app.blackbox.ai) | Paste your __Secure-authjs.session-token value or full cookie header from app.blackbox.ai | emulated |
| `chatgpt-session` | `cgpt-session` | ChatGPT Web (Session) | Web cookie | [link](https://chatgpt.com) | Paste the full ChatGPT Cookie header. OmniRoute verifies it in an isolated browser profile and then stores only the verified session state. | emulated |
| `chatgpt-web-codex` | `cgpt-codex` | ChatGPT Web (Codex) | Web cookie | [link](https://chatgpt.com) | Paste the full ChatGPT Cookie header. OmniRoute verifies it in an isolated headless browser profile. | native |
| `claude-web` | `cw` | Claude Web | Web cookie | [link](https://claude.ai) | Paste your session cookie from claude.ai | none |
| `conol-web` | `cnl` | Conol (Unofficial/Experimental) | Web cookie | [link](https://conol.ai) | Use browser sign-in, or paste the full Cookie header from conol.ai. The __Secure-better-auth.session_token cookie is required. | — |
@@ -440,7 +441,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/) (104 implementations)
- Executors: [`open-sse/executors/`](../../open-sse/executors/) (107 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 352 AI providers — all through a single OpenAI-compatible endpoint. Includes a built-in 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
@@ -165,7 +165,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
│ │ └── manager.ts # MITM proxy manager
│ ├── shared/ # Shared utilities, components, and constants
│ │ ├── components/ # Reusable UI components (Card, Badge, Button, Modal, Sidebar, ProviderIcon, etc.)
│ │ ├── constants/ # Provider definitions (352), model lists, pricing, routing strategies, MCP scopes
│ │ ├── constants/ # Provider definitions (353), model lists, pricing, routing strategies, MCP scopes
│ │ ├── contracts/ # Shared API contracts
│ │ ├── hooks/ # React hooks
│ │ ├── middleware/ # Shared middleware utilities
@@ -277,7 +277,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
## Key Features (v3.8.50)
### Core Proxy
- **352 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)
- **19 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, cache-optimized, context-relay, fusion, pipeline
- **4-tier fallback**: Subscription → API Key → Cheap → Free
@@ -474,7 +474,7 @@ diagnostics) plus **memory**, **skill**, **agentSkill**, **githubSkill**, **pool
## v3.8.x Highlights
- **352-provider catalog** with 150+ free tiers, one-click account imports, and bulk key add
- **353-provider catalog** with 150+ 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`
- **16-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

@@ -122,6 +122,7 @@ import { blackbox_webProvider } from "./registry/blackbox/web/index.ts";
import { uncloseaiProvider } from "./registry/uncloseai/index.ts";
import { nscaleProvider } from "./registry/nscale/index.ts";
import { chatgpt_web_codexProvider } from "./registry/chatgpt-web-codex/index.ts";
import { chatgpt_sessionProvider } from "./registry/chatgpt-session/index.ts";
import { openrouterProvider } from "./registry/openrouter/index.ts";
import { cheaperinferenceProvider } from "./registry/cheaperinference/index.ts";
import { openvectaProvider } from "./registry/openvecta/index.ts";
@@ -388,6 +389,7 @@ export const REGISTRY: Record<string, RegistryEntry> = {
uncloseai: uncloseaiProvider,
nscale: nscaleProvider,
"chatgpt-web-codex": chatgpt_web_codexProvider,
"chatgpt-session": chatgpt_sessionProvider,
openrouter: openrouterProvider,
cheaperinference: cheaperinferenceProvider,
openvecta: openvectaProvider,

View File

@@ -0,0 +1,27 @@
import type { RegistryEntry } from "../../shared.ts";
const SESSION_CAPABILITIES = {
toolCalling: true,
supportsReasoning: true,
supportsVision: false,
} as const;
export const chatgpt_sessionProvider: RegistryEntry = {
id: "chatgpt-session",
alias: "cgpt-session",
format: "openai",
executor: "chatgpt-session",
baseUrl: "https://chatgpt.com",
reasoningTransport: "opaque",
authType: "apikey",
authHeader: "cookie",
models: [
{ id: "luna", name: "ChatGPT Session — Luna", ...SESSION_CAPABILITIES },
{ id: "think", name: "ChatGPT Session — Think", ...SESSION_CAPABILITIES },
{ id: "instant", name: "ChatGPT Session — Instant", ...SESSION_CAPABILITIES },
{ id: "medium", name: "ChatGPT Session — Medium", ...SESSION_CAPABILITIES },
{ id: "high", name: "ChatGPT Session — High", ...SESSION_CAPABILITIES },
{ id: "extra-high", name: "ChatGPT Session — Extra High", ...SESSION_CAPABILITIES },
{ id: "pro", name: "ChatGPT Session — Pro", ...SESSION_CAPABILITIES },
],
};

View File

@@ -0,0 +1,347 @@
/**
* ChatGptSessionExecutor — OpenAI chat completions over an authenticated ChatGPT browser
* session.
*
* The vendored MIT browser adapter owns every anti-bot interaction (sentinel, turnstile,
* proof-of-work) because a real signed-in browser performs them; this executor only translates
* request and response shapes around it.
*/
import { CHATGPT_WEB_CODEX_CONNECTOR_NAME } from "@/shared/constants/chatgptWebCodex";
import { FORMATS } from "../translator/formats.ts";
import { prepareToolMessages } from "../translator/webTools.ts";
import { buildErrorBody, sanitizeErrorMessage } from "../utils/error.ts";
import { AsyncEventQueue } from "../vendor/codex-chatgpt-web/event-queue.ts";
import type { AdapterEvent, CodexProviderConfig } from "../vendor/codex-chatgpt-web/types.ts";
import { BaseExecutor, type ExecuteInput, type ExecutorExecuteResult } from "./base.ts";
import { buildToolModeResponse } from "./chatgptWebTools.ts";
import {
decodeChatGptWebCodexSecrets,
encodeChatGptWebCodexSecrets,
} from "./chatgpt-web-codex/credentials.ts";
import { connectionRuntimePaths } from "./chatgpt-web-codex/storageState.ts";
import {
buildChatGptSessionCompletion,
openChatGptSessionStream,
resolveChatGptSessionStreamOpenTimeoutMs,
type ChatGptSessionResponseMeta,
} from "./chatgpt-session/bridge.ts";
import { classifyChatGptSessionError } from "./chatgpt-session/errors.ts";
import { buildParsedRequest } from "./chatgpt-session/messages.ts";
import { requireChatGptSessionRoute, type ChatGptSessionRoute } from "./chatgpt-session/models.ts";
import {
chatGptSessionRuntime,
type ChatGptSessionLoginConfig,
} from "./chatgpt-session/runtime.ts";
const BASE_URL = "https://chatgpt.com";
const JSON_HEADERS = { "Content-Type": "application/json" };
const SSE_HEADERS = {
"Cache-Control": "no-cache, no-transform",
Connection: "keep-alive",
"Content-Type": "text/event-stream; charset=utf-8",
};
function record(value: unknown): Record<string, unknown> {
return value && typeof value === "object" && !Array.isArray(value)
? (value as Record<string, unknown>)
: {};
}
function configuredString(data: Record<string, unknown>, ...keys: string[]): string | undefined {
for (const key of keys) {
const value = data[key];
if (typeof value === "string" && value.trim()) return value.trim();
}
return undefined;
}
/**
* A caller-supplied logger is foreign code and may throw. It never gets to decide whether the
* request is answered, so every warn goes through here.
*/
function warn(log: ExecuteInput["log"], message: unknown): void {
try {
log?.warn?.(
"CHATGPT_SESSION",
sanitizeErrorMessage(message instanceof Error ? message.message : message)
);
} catch {
// A broken logger must not fail the turn.
}
}
function wrapped(response: Response, body: unknown): ExecutorExecuteResult {
return {
response,
url: BASE_URL,
headers: {},
transformedBody: body,
transport: "chatgpt-session-browser",
};
}
function errorResponse(
status: number,
message: unknown,
code: string,
fallbackHint?: "connection_cooldown"
): Response {
return new Response(
JSON.stringify(
buildErrorBody(status, sanitizeErrorMessage(message), undefined, {
type: status >= 500 ? "provider_error" : "invalid_request_error",
code,
})
),
{
status,
headers: fallbackHint
? { ...JSON_HEADERS, "X-Omni-Fallback-Hint": fallbackHint }
: JSON_HEADERS,
}
);
}
export function buildChatGptSessionProviderConfig(args: {
route: ChatGptSessionRoute;
connectionId: string;
storageStatePath: string;
connectorName: string;
chromeExecutablePath?: string;
cdpEndpoint?: string;
solAvailable: boolean;
proAvailable: boolean;
}): CodexProviderConfig {
const paths = connectionRuntimePaths(args.connectionId);
return {
adapter: "chatgpt-web",
baseUrl: BASE_URL,
defaultModel: args.route.backendModel,
models: [args.route.backendModel],
chatgptWeb: {
appName: args.connectorName,
storageStatePath: args.storageStatePath,
...(args.chromeExecutablePath ? { chromeExecutablePath: args.chromeExecutablePath } : {}),
...(args.cdpEndpoint ? { cdpEndpoint: args.cdpEndpoint } : {}),
brokerSocketPath: paths.brokerSocketPath,
threadEnvironmentStatePath: paths.threadEnvironmentStatePath,
lunaCheckpointStatePath: paths.lunaCheckpointStatePath,
headed: true,
// Prompt-emulated tools only: never attach the turn-bound Codex connector capability.
localToolsEnabled: false,
solAvailable: args.solAvailable,
proAvailable: args.proAvailable,
autoApproveToolCalls: false,
},
};
}
export class ChatGptSessionExecutor extends BaseExecutor {
constructor() {
super("chatgpt-session", {
id: "chatgpt-session",
baseUrl: BASE_URL,
format: FORMATS.OPENAI,
});
}
override async execute(input: ExecuteInput): Promise<ExecutorExecuteResult> {
const runtime = chatGptSessionRuntime();
const requestBody = record(input.body);
try {
const route = requireChatGptSessionRoute(input.model);
const connectionId = input.credentials.connectionId?.trim();
const encodedCredentials = input.credentials.apiKey?.trim();
if (!connectionId || !encodedCredentials) {
throw new Error("ChatGPT browser credentials are missing");
}
const secrets = decodeChatGptWebCodexSecrets(encodedCredentials);
const providerData = record(input.credentials.providerSpecificData);
const cdpEndpoint =
configuredString(providerData, "browserCdpEndpoint") ??
process.env.CHATGPT_WEB_CODEX_CDP_URL?.trim();
const chromeExecutablePath = runtime.detectChrome(
configuredString(providerData, "chromeExecutablePath")
);
if (!chromeExecutablePath && !cdpEndpoint) {
throw new Error("No supported Chrome or Chromium executable was found");
}
const storageStatePath = runtime.ensureStorageState(connectionId, secrets);
const connectorName =
configuredString(providerData, "connectorName", "appName") ??
CHATGPT_WEB_CODEX_CONNECTOR_NAME;
const loginConfig: ChatGptSessionLoginConfig = {
appName: connectorName,
storageStatePath,
headed: true,
proAvailable: providerData.proAvailable === true,
autoApproveToolCalls: false,
...(chromeExecutablePath ? { chromeExecutablePath } : {}),
...(cdpEndpoint ? { cdpEndpoint } : {}),
};
let solAvailable = providerData.solAvailable !== false;
let proAvailable = providerData.proAvailable === true;
if (!runtime.loginStateExists(loginConfig)) {
const capabilities = await runtime.inspectLogin(loginConfig);
solAvailable = capabilities.solAvailable;
proAvailable = capabilities.proAvailable;
await input.onCredentialsRefreshed?.({
providerSpecificData: {
...providerData,
solAvailable,
proAvailable,
browserVerified: true,
...(chromeExecutablePath ? { chromeExecutablePath } : {}),
...(cdpEndpoint ? { browserCdpEndpoint: cdpEndpoint } : {}),
},
});
}
if (route.sol !== solAvailable) {
throw new Error(
route.sol
? `${route.id} is not available for this Luna-only connection`
: `${route.id} is not available while the account exposes the Sol model selector`
);
}
if (route.pro && !proAvailable) {
throw new Error(`${route.id} is not available for this non-Pro connection`);
}
const messages = Array.isArray(requestBody.messages)
? (requestBody.messages as Array<{ role: string; content: unknown }>)
: [];
const { hasTools, requestedTools, effectiveMessages } = prepareToolMessages(
requestBody,
messages
);
// No `rawBody` here on purpose: the adapter reads `_rawBody` as a native Codex Responses
// body, so handing it the OpenAI chat-completions body failed every turn before any browser
// work. `buildParsedRequest` synthesizes that envelope itself.
const parsed = buildParsedRequest({
route,
messages: effectiveMessages,
stream: Boolean(input.stream) && !hasTools,
});
const provider = buildChatGptSessionProviderConfig({
route,
connectionId,
storageStatePath,
connectorName,
solAvailable,
proAvailable,
...(chromeExecutablePath ? { chromeExecutablePath } : {}),
...(cdpEndpoint ? { cdpEndpoint } : {}),
});
const events = new AsyncEventQueue<AdapterEvent>();
const incoming = {
headers: new Headers(),
...(input.signal ? { abortSignal: input.signal } : {}),
};
const persistRotatedState = async () => {
try {
const storageState = runtime.readStorageState(storageStatePath);
await input.onCredentialsRefreshed?.({
apiKey: encodeChatGptWebCodexSecrets({
storageState,
...(secrets.runtimeKey ? { runtimeKey: secrets.runtimeKey } : {}),
}),
});
} catch (refreshError) {
warn(input.log, refreshError);
}
};
const run = async () => {
try {
await runtime.runTurn(parsed, incoming, (event) => events.push(event), provider);
} catch (error) {
const classified = classifyChatGptSessionError(error);
events.push({
type: "error",
message: sanitizeErrorMessage(error instanceof Error ? error.message : error),
status: classified.status,
code: classified.code,
});
} finally {
// The close MUST happen even if persisting (or a caller-supplied logger inside its
// catch) throws: without it `events.collect()` and the streaming consumer both wait
// on a queue that is never closed, and the streaming path — started as `void run()` —
// turns the throw into an unhandled rejection on top of the hang.
try {
await persistRotatedState();
} finally {
events.close();
}
}
};
const meta: ChatGptSessionResponseMeta = {
cid: `chatcmpl-cgpts-${crypto.randomUUID().slice(0, 12)}`,
created: Math.floor(Date.now() / 1000),
model: input.model,
};
if (!parsed.stream) {
const running = run();
const collected = await events.collect();
await running;
const built = buildChatGptSessionCompletion(collected, meta);
const jsonResponse = new Response(JSON.stringify(built.body), {
status: built.status,
headers: JSON_HEADERS,
});
if (!hasTools || built.status !== 200) return wrapped(jsonResponse, input.body);
const toolResponse = await buildToolModeResponse(
jsonResponse,
requestedTools,
Boolean(input.stream),
{ cid: meta.cid, created: meta.created, model: meta.model, idSeed: "cgpts" }
);
return wrapped(toolResponse, input.body);
}
void run();
const opened = await openChatGptSessionStream(events, meta, {
streamOpenTimeoutMs: resolveChatGptSessionStreamOpenTimeoutMs(),
});
if (opened.kind === "error") {
// Every field of the verdict comes from the bridge's classification of the real adapter
// event — status, code and fallbackHint alike. Re-classifying the sanitized message here
// would discard the event's own `status`/`code`/`name`.
return wrapped(
errorResponse(opened.status, opened.message, opened.code, opened.fallbackHint),
input.body
);
}
return wrapped(
new Response(opened.stream, { status: 200, headers: SSE_HEADERS }),
input.body
);
} catch (error) {
const classified = classifyChatGptSessionError(error);
warn(input.log, error);
return wrapped(
errorResponse(
classified.status,
error instanceof Error ? error.message : error,
classified.code,
classified.fallbackHint
),
input.body
);
}
}
}

View File

@@ -0,0 +1,336 @@
/**
* Bridges the vendored adapter's event stream into OpenAI chat-completions payloads.
*
* Stream opening is gated on the first event that would ALSO count as committed output on the
* buffered path, so a turn that fails before producing any assistant content can still be
* answered with a real HTTP status instead of a 200 stream carrying an error chunk. The gate and
* `buildChatGptSessionCompletion` must agree on what "output" means — transport framing
* (heartbeats, assistant boundaries), commentary-phase text, empty text deltas and reasoning are
* all non-committing on both paths. Once real content has been emitted the status line is
* already committed, so a later failure just closes the stream cleanly.
*
* The gate is bounded: nothing at all can reach the client while it is closed (not even a
* keepalive, since the first byte commits the 200), and a turn here runs in a real browser that
* may think for a long time. Past `CHATGPT_SESSION_STREAM_OPEN_TIMEOUT_MS` the stream opens
* anyway and keeps consuming the same iterator, so a slow-but-healthy turn stays connected.
*/
import { buildErrorBody, sanitizeErrorMessage } from "../../utils/error.ts";
import { formatTranslatedStreamError } from "../../utils/streamErrorFormat.ts";
import type {
AdapterEvent,
CodexMessagePhase,
CodexUsage,
} from "../../vendor/codex-chatgpt-web/types.ts";
import { classifyChatGptSessionError } from "./errors.ts";
export interface ChatGptSessionResponseMeta {
cid: string;
created: number;
model: string;
}
export type ChatGptSessionStreamOpen =
| {
kind: "error";
status: number;
code: string;
message: string;
fallbackHint?: "connection_cooldown";
}
| { kind: "stream"; stream: ReadableStream<Uint8Array> };
/**
* The adapter tags its own transport chatter with the commentary phase — most visibly the
* "local Codex computer is unavailable" banner it emits on every fresh turn while
* `localToolsEnabled` is false, which is this provider's permanent configuration. It is not
* model output and it is not model reasoning, so it never reaches the client on either path.
*/
const COMMENTARY_PHASE: CodexMessagePhase = "commentary";
/**
* How long the stream-open gate waits for the first committing event before opening the stream
* anyway.
*
* The trade-off: while the gate is closed the client sees nothing, so an idle-timeout client can
* hang up on a healthy turn that is simply thinking for a long time in the browser. Opening the
* stream commits the 200, which costs the ability to answer with a real HTTP status if the turn
* dies later — but every failure that needs a real status (no browser, missing or expired
* credentials, rate limiting, an incompatible route) surfaces within seconds, far inside this
* window. Only a genuinely long-running healthy turn reaches the deadline.
*
* Callers can override it per request through `options.streamOpenTimeoutMs`; a value that is not
* a finite number greater than zero disables the deadline and the gate waits indefinitely.
*/
export const CHATGPT_SESSION_STREAM_OPEN_TIMEOUT_MS = 30_000;
/**
* Reads `OMNIROUTE_CHATGPT_SESSION_STREAM_OPEN_TIMEOUT_MS` and falls back to
* {@link CHATGPT_SESSION_STREAM_OPEN_TIMEOUT_MS} whenever the variable is unset, empty,
* unparseable, non-finite, or not positive — mirrors `resolveDirectHeadersTimeoutMs`
* (`open-sse/utils/directResponseStartTimeout.ts`). Callers resolve this per request so a
* changed environment variable takes effect without a restart.
*/
export function resolveChatGptSessionStreamOpenTimeoutMs(
env: Record<string, string | undefined> = process.env
): number {
const raw = env.OMNIROUTE_CHATGPT_SESSION_STREAM_OPEN_TIMEOUT_MS;
if (raw == null || raw.trim() === "") return CHATGPT_SESSION_STREAM_OPEN_TIMEOUT_MS;
const parsed = Number(raw);
return Number.isFinite(parsed) && parsed > 0
? Math.floor(parsed)
: CHATGPT_SESSION_STREAM_OPEN_TIMEOUT_MS;
}
/** Race marker for the gate deadline — distinguishable from any `IteratorResult`. */
const GATE_TIMED_OUT = Symbol("chatgpt-session-gate-timeout");
export interface ChatGptSessionStreamOptions {
/** Overrides {@link CHATGPT_SESSION_STREAM_OPEN_TIMEOUT_MS} for this call (tests, tuning). */
streamOpenTimeoutMs?: number;
}
interface OpenAiUsage {
prompt_tokens: number;
completion_tokens: number;
total_tokens: number;
completion_tokens_details?: { reasoning_tokens: number };
}
function mapUsage(usage: CodexUsage | undefined): OpenAiUsage | undefined {
if (!usage) return undefined;
const prompt = usage.inputTokens ?? 0;
const completion = usage.outputTokens ?? 0;
return {
prompt_tokens: prompt,
completion_tokens: completion,
total_tokens: usage.totalTokens ?? prompt + completion,
...(typeof usage.reasoningOutputTokens === "number"
? { completion_tokens_details: { reasoning_tokens: usage.reasoningOutputTokens } }
: {}),
};
}
function chunk(
meta: ChatGptSessionResponseMeta,
delta: Record<string, unknown>,
finishReason: string | null,
usage?: OpenAiUsage
): string {
const payload: Record<string, unknown> = {
id: meta.cid,
object: "chat.completion.chunk",
created: meta.created,
model: meta.model,
choices: [{ index: 0, delta, finish_reason: finishReason }],
};
if (usage) payload.usage = usage;
return `data: ${JSON.stringify(payload)}\n\n`;
}
function finishReasonFor(event: AdapterEvent): string {
if (event.type === "incomplete") return event.endTurn ? "stop" : "length";
return "stop";
}
export async function openChatGptSessionStream(
events: AsyncIterable<AdapterEvent>,
meta: ChatGptSessionResponseMeta,
options?: ChatGptSessionStreamOptions
): Promise<ChatGptSessionStreamOpen> {
const iterator = events[Symbol.asyncIterator]();
// Reasoning that arrives while the gate is still closed is real output the client must
// receive; it just may not commit the status line, because the buffered path fails over on
// absent CONTENT regardless of how much reasoning preceded it.
const bufferedReasoning: AdapterEvent[] = [];
let first: AdapterEvent | null = null;
// The `iterator.next()` the deadline outran. It is still in flight and will settle with the
// event the gate never saw, so the stream body must await THIS promise instead of asking the
// iterator for another one — a second `next()` would queue behind it and the first event would
// be lost with the abandoned promise.
let pendingNext: Promise<IteratorResult<AdapterEvent>> | null = null;
const timeoutMs = options?.streamOpenTimeoutMs ?? CHATGPT_SESSION_STREAM_OPEN_TIMEOUT_MS;
const bounded = Number.isFinite(timeoutMs) && timeoutMs > 0;
let timer: ReturnType<typeof setTimeout> | undefined;
const deadline = bounded
? new Promise<typeof GATE_TIMED_OUT>((resolve) => {
timer = setTimeout(() => resolve(GATE_TIMED_OUT), timeoutMs);
// A gate deadline must never be the reason the process stays alive.
(timer as unknown as { unref?: () => void }).unref?.();
})
: null;
try {
for (;;) {
const step = iterator.next();
const settled: IteratorResult<AdapterEvent> | typeof GATE_TIMED_OUT = deadline
? await Promise.race([step, deadline])
: await step;
if (settled === GATE_TIMED_OUT) {
pendingNext = step;
break;
}
if (settled.done) break;
const event = settled.value;
if (event.type === "heartbeat" || event.type === "assistant_boundary") continue;
if (event.type === "text_delta" && (!event.text || event.phase === COMMENTARY_PHASE)) {
continue;
}
if (event.type === "thinking_delta") {
bufferedReasoning.push(event);
continue;
}
first = event;
break;
}
} finally {
// Every exit path clears the timer, so a pending deadline can neither fire after the gate
// resolved nor hold a handle open.
if (timer !== undefined) clearTimeout(timer);
}
if (first && first.type === "error") {
const classified = classifyChatGptSessionError(first);
return {
kind: "error",
status: classified.status,
code: classified.code,
message: sanitizeErrorMessage(first.message),
...(classified.fallbackHint ? { fallbackHint: classified.fallbackHint } : {}),
};
}
const pending: AdapterEvent[] = first ? [...bufferedReasoning, first] : bufferedReasoning;
const encoder = new TextEncoder();
const stream = new ReadableStream<Uint8Array>(
{
async start(controller) {
const emit = (text: string) => controller.enqueue(encoder.encode(text));
emit(chunk(meta, { role: "assistant" }, null));
let terminatedWithError = false;
const handle = (event: AdapterEvent): boolean => {
switch (event.type) {
case "heartbeat":
emit(": keepalive\n\n");
return true;
case "text_delta":
if (event.phase === COMMENTARY_PHASE) return true;
if (event.text) emit(chunk(meta, { content: event.text }, null));
return true;
case "thinking_delta":
if (event.thinking) emit(chunk(meta, { reasoning_content: event.thinking }, null));
return true;
case "assistant_boundary":
// Internal framing between the adapter's guarded first pass and its one-shot
// continuation (the vendor's Responses bridge only closes the open item here).
// There is no chat-completions delta for it, and letting it reach `default` would
// be indistinguishable from a real event we forgot to handle.
return true;
case "done":
emit(chunk(meta, {}, "stop", mapUsage(event.usage)));
return false;
case "incomplete":
emit(chunk(meta, {}, finishReasonFor(event), mapUsage(event.usage)));
return false;
case "error": {
const classified = classifyChatGptSessionError(event);
emit(
formatTranslatedStreamError({
status: classified.status,
message: event.message,
code: classified.code,
type: classified.status >= 500 ? "provider_error" : "invalid_request_error",
})
);
terminatedWithError = true;
return false;
}
default:
return true;
}
};
try {
let open = true;
for (const event of pending) {
open = handle(event);
if (!open) break;
}
while (open) {
const step = pendingNext ?? iterator.next();
pendingNext = null;
const next = await step;
if (next.done) {
emit(chunk(meta, {}, "stop"));
break;
}
open = handle(next.value);
}
} finally {
if (!terminatedWithError) emit("data: [DONE]\n\n");
controller.close();
}
},
cancel() {
void iterator.return?.();
},
},
{ highWaterMark: 16384 }
);
return { kind: "stream", stream };
}
export function buildChatGptSessionCompletion(
events: readonly AdapterEvent[],
meta: ChatGptSessionResponseMeta
): { status: number; body: Record<string, unknown> } {
let content = "";
let reasoning = "";
let finishReason = "stop";
let usage: CodexUsage | undefined;
let failure: AdapterEvent | null = null;
for (const event of events) {
if (event.type === "text_delta") {
if (event.phase !== COMMENTARY_PHASE) content += event.text;
} else if (event.type === "thinking_delta") reasoning += event.thinking;
else if (event.type === "done") usage = event.usage;
else if (event.type === "incomplete") {
usage = event.usage;
finishReason = finishReasonFor(event);
} else if (event.type === "error") {
usage = event.usage ?? usage;
failure = event;
}
}
if (failure && failure.type === "error" && !content) {
const classified = classifyChatGptSessionError(failure);
return {
status: classified.status,
body: buildErrorBody(classified.status, sanitizeErrorMessage(failure.message), undefined, {
type: classified.status >= 500 ? "provider_error" : "invalid_request_error",
code: classified.code,
}) as unknown as Record<string, unknown>,
};
}
const message: Record<string, unknown> = { role: "assistant", content };
if (reasoning) message.reasoning_content = reasoning;
return {
status: 200,
body: {
id: meta.cid,
object: "chat.completion",
created: meta.created,
model: meta.model,
choices: [{ index: 0, message, finish_reason: finishReason, logprobs: null }],
...(mapUsage(usage) ? { usage: mapUsage(usage) } : {}),
},
};
}

View File

@@ -0,0 +1,106 @@
/**
* Maps every failure this provider can produce onto the HTTP contract the router expects.
*
* The distinction that matters: a 503 with `connection_cooldown` lets combo routing skip this
* connection without opening the provider circuit breaker, while a 400 is terminal and must not
* be retried (a changed ChatGPT DOM will not fix itself on a retry).
*/
import { ChatGptSessionInputError } from "./messages.ts";
export interface ChatGptSessionErrorClass {
status: number;
code: string;
fallbackHint?: "connection_cooldown";
}
interface ErrorLike {
message: string;
name?: string;
status?: number;
code?: string;
}
function asErrorLike(error: unknown): ErrorLike {
if (error instanceof Error) {
const typed = error as Error & { status?: unknown; code?: unknown };
return {
message: error.message,
name: error.name,
...(typeof typed.status === "number" ? { status: typed.status } : {}),
...(typeof typed.code === "string" ? { code: typed.code } : {}),
};
}
if (error && typeof error === "object") {
const typed = error as Record<string, unknown>;
return {
message: typeof typed.message === "string" ? typed.message : String(error),
...(typeof typed.name === "string" ? { name: typed.name } : {}),
...(typeof typed.status === "number" ? { status: typed.status } : {}),
...(typeof typed.code === "string" ? { code: typed.code } : {}),
};
}
return { message: String(error ?? "") };
}
const BROWSER_UNAVAILABLE =
/No supported Chrome|browserType\.launch|Executable doesn't exist|chromium.*not installed/i;
const MISSING_CREDENTIALS =
/credentials are missing|Cookie or verified browser storage state is required|Cookie header is missing/i;
const SESSION_EXPIRED = /not authenticated|storage state is invalid|sign ?in|log ?in|logged out/i;
const RATE_LIMITED = /rate limit|usage limit|too many requests|message limit/i;
const ROUTE_UNAVAILABLE =
/not available for this|not available while the account|is not supported/i;
const UI_TIMEOUT = /waitForSelector|Timeout \d+ms exceeded|actionability|interception/i;
export function classifyChatGptSessionError(error: unknown): ChatGptSessionErrorClass {
if (error instanceof ChatGptSessionInputError) {
return { status: 400, code: error.code };
}
const like = asErrorLike(error);
// The error's own constructor name is a stronger signal than substring matching on its
// message: a Playwright TimeoutError is always a terminal DOM/selector timeout, even when
// its message happens to name a login-related selector (which would otherwise look like an
// expired session to the message-pattern checks below).
if (like.name === "TimeoutError") {
return { status: 400, code: "browser_ui_timeout" };
}
// An explicit numeric status is the adapter's own authoritative verdict. The vendor documents
// it that way on `AdapterEvent.error.status` ("Authoritative upstream/proxy status when known;
// avoids message-based classification"), and trusting it first is what stops a 503 whose prose
// happens to mention signing in from being reported as an expired session — which would mark a
// healthy account's credentials dead and pull it out of rotation. Message matching still runs
// below, because failures thrown by this executor itself carry no status at all.
if (typeof like.status === "number" && like.status >= 400) {
const status = like.status;
return {
status,
code: like.code ?? "turn_failed",
// 503/429 must cool this one connection down rather than trip the whole-provider breaker.
...(status === 503 || status === 429 ? { fallbackHint: "connection_cooldown" as const } : {}),
};
}
if (BROWSER_UNAVAILABLE.test(like.message)) {
return { status: 503, code: "browser_unavailable", fallbackHint: "connection_cooldown" };
}
if (MISSING_CREDENTIALS.test(like.message)) {
return { status: 401, code: "missing_credentials" };
}
if (SESSION_EXPIRED.test(like.message)) {
return { status: 401, code: "session_expired" };
}
if (RATE_LIMITED.test(like.message)) {
return { status: 429, code: "rate_limited" };
}
if (ROUTE_UNAVAILABLE.test(like.message)) {
return { status: 400, code: "route_unavailable" };
}
if (UI_TIMEOUT.test(like.message)) {
return { status: 400, code: "browser_ui_timeout" };
}
return { status: 502, code: "turn_failed" };
}

View File

@@ -0,0 +1,185 @@
/**
* Pure translation from an OpenAI chat-completions message list into the synthetic
* CodexParsedRequest the vendored browser adapter consumes.
*
* Tools are deliberately never placed in `context.tools`: this provider uses the shared
* prompt-emulated tool contract (translator/webTools.ts), so the adapter must not try to
* attach the turn-bound Codex connector capability.
*
* `_rawBody` is SYNTHESIZED here, never passed through from the OpenAI request. The vendored
* adapter reads `_rawBody` as a native Codex *Responses* body and refuses to run a turn without
* turn identity in it — an OpenAI chat-completions body makes every request fail with
* "ChatGPT web requires native Codex turn_id metadata for browser-session replay" before any
* browser work starts. Three things are load-bearing (see
* vendor/codex-chatgpt-web/adapters/chatgpt-web/environment.ts):
* 1. `client_metadata["x-codex-turn-metadata"]` carrying `thread_id` / `turn_id`
* (`clientTurnMetadata`),
* 2. `input` as an array of Responses items (`latestChatGptTurnUserRevision`,
* `chatGptTurnRoundKey`, the Luna rolling checkpoint),
* 3. `internal_chat_message_metadata_passthrough.turn_id` on the CURRENT user item
* (`itemTurnId`).
* This provider serves stateless chat completions, so a fresh thread/turn pair per request is
* the truthful identity: every request genuinely is its own turn.
*/
import { randomUUID } from "node:crypto";
import type {
CodexMessage,
CodexParsedRequest,
CodexTextContent,
} from "../../vendor/codex-chatgpt-web/types.ts";
import type { ChatGptSessionRoute } from "./models.ts";
export class ChatGptSessionInputError extends Error {
readonly code: string;
constructor(code: string, message: string) {
super(message);
this.name = "ChatGptSessionInputError";
this.code = code;
}
}
interface OpenAiMessage {
role: string;
content: unknown;
}
/**
* One Responses-shaped `input` item. Only the fields the vendored adapter actually reads are
* emitted: `type`/`role`/`content[].text` (`rawMessageText`, `inputContentParts`) and the
* current-turn marker (`itemTurnId`).
*/
interface ResponsesInputItem {
type: "message";
role: "user" | "assistant";
content: Array<{ type: "input_text" | "output_text"; text: string }>;
internal_chat_message_metadata_passthrough?: { turn_id: string };
}
function textFromContent(content: unknown): string {
if (typeof content === "string") return content;
if (!Array.isArray(content)) return "";
const chunks: string[] = [];
for (const part of content) {
const typed =
part && typeof part === "object" && !Array.isArray(part)
? (part as Record<string, unknown>)
: null;
if (typed && typed.type === "text" && typeof typed.text === "string") {
chunks.push(typed.text);
continue;
}
// `{ type: "refusal", refusal: "…" }` is a legal ASSISTANT content part in the
// chat-completions spec, and a client replaying its own conversation history sends it back
// verbatim. It is text as far as the adapter is concerned. A `refusal` part whose payload is
// missing or not a string falls through to the rejection below rather than being dropped.
if (typed && typed.type === "refusal" && typeof typed.refusal === "string") {
chunks.push(typed.refusal);
continue;
}
if (typed && (typed.type === "image_url" || typed.type === "image")) {
throw new ChatGptSessionInputError(
"vision_unsupported",
"ChatGPT Session does not accept image input yet"
);
}
// Every other part — file, input_audio, a future part type, or anything malformed — would
// otherwise be dropped in silence, and the model would answer about content it never
// received. Reject loudly instead.
throw new ChatGptSessionInputError(
"unsupported_content_part",
"ChatGPT Session does not accept this message content part type"
);
}
return chunks.join("\n");
}
function assistantParts(content: unknown): CodexTextContent[] {
const text = textFromContent(content);
return text ? [{ type: "text", text }] : [];
}
export function buildParsedRequest(input: {
route: ChatGptSessionRoute;
messages: ReadonlyArray<OpenAiMessage>;
stream: boolean;
}): CodexParsedRequest {
const systemPrompt: string[] = [];
const messages: CodexMessage[] = [];
const inputItems: ResponsesInputItem[] = [];
let lastUserItem = -1;
for (const message of input.messages) {
const role = typeof message.role === "string" ? message.role : "";
if (role === "system" || role === "developer") {
const text = textFromContent(message.content);
if (text) systemPrompt.push(text);
continue;
}
if (role === "assistant") {
const content = assistantParts(message.content);
if (content.length > 0) {
messages.push({ role: "assistant", content, timestamp: Date.now() });
inputItems.push({
type: "message",
role: "assistant",
content: content.map((part) => ({ type: "output_text", text: part.text })),
});
}
continue;
}
if (role === "user" || role === "tool" || role === "function") {
const text = textFromContent(message.content);
if (!text) continue;
messages.push({ role: "user", content: text, timestamp: Date.now() });
lastUserItem = inputItems.length;
inputItems.push({
type: "message",
role: "user",
content: [{ type: "input_text", text }],
});
}
}
if (lastUserItem < 0) {
throw new ChatGptSessionInputError(
"no_user_message",
"ChatGPT Session requires at least one user message"
);
}
const threadId = `thread_omniroute_${randomUUID()}`;
const turnId = `turn_omniroute_${randomUUID()}`;
// Only the LAST user item is the current turn. Marking an earlier one would make the adapter
// replay stale history as the live instruction.
inputItems[lastUserItem] = {
...inputItems[lastUserItem]!,
internal_chat_message_metadata_passthrough: { turn_id: turnId },
};
return {
modelId: input.route.backendModel,
context: {
...(systemPrompt.length > 0 ? { systemPrompt } : {}),
messages,
},
stream: input.stream,
options: { reasoning: input.route.effort },
_rawBody: {
model: input.route.backendModel,
// The adapter's Luna checkpoint path re-parses `_rawBody` through the vendored Responses
// parser, which reads system prompts from `instructions` — keep the two representations
// equivalent so a re-parse reproduces this same context.
...(systemPrompt.length > 0 ? { instructions: systemPrompt.join("\n") } : {}),
input: inputItems,
// The real Codex client sends this metadata as a JSON string; the vendor accepts a plain
// object too, but matching the client's wire shape keeps us on the path the vendor's own
// tests and future tightening cover.
client_metadata: {
"x-codex-turn-metadata": JSON.stringify({ thread_id: threadId, turn_id: turnId }),
},
},
};
}

View File

@@ -0,0 +1,47 @@
/**
* Public model routes for the ChatGPT Session provider.
*
* Each public slug pins one backend model plus one reasoning effort. The vendored browser
* adapter accepts only the two backend ids and reads the effort from
* `CodexParsedRequest.options.reasoning`, so the slug is the only knob a client turns.
*/
export type ChatGptSessionEffort = "low" | "medium" | "high" | "xhigh" | "max";
export interface ChatGptSessionRoute {
id: string;
backendModel: "gpt-5.6-sol" | "gpt-5.6-luna";
effort: ChatGptSessionEffort;
/** Requires an account whose browser probe reported Pro. */
pro: boolean;
/** Requires the Sol model selector; Luna-only accounts get the luna/think routes. */
sol: boolean;
}
const ROUTES: ReadonlyMap<string, ChatGptSessionRoute> = new Map([
["luna", { id: "luna", backendModel: "gpt-5.6-luna", effort: "low", pro: false, sol: false }],
[
"think",
{ id: "think", backendModel: "gpt-5.6-luna", effort: "medium", pro: false, sol: false },
],
["instant", { id: "instant", backendModel: "gpt-5.6-sol", effort: "low", pro: false, sol: true }],
[
"medium",
{ id: "medium", backendModel: "gpt-5.6-sol", effort: "medium", pro: false, sol: true },
],
["high", { id: "high", backendModel: "gpt-5.6-sol", effort: "high", pro: false, sol: true }],
[
"extra-high",
{ id: "extra-high", backendModel: "gpt-5.6-sol", effort: "xhigh", pro: true, sol: true },
],
["pro", { id: "pro", backendModel: "gpt-5.6-sol", effort: "max", pro: true, sol: true }],
] as const);
export const CHATGPT_SESSION_ROUTE_IDS: readonly string[] = [...ROUTES.keys()];
export function requireChatGptSessionRoute(model: string): ChatGptSessionRoute {
const normalized = model.trim().replace(/^(?:chatgpt-session|cgpt-session)\//, "");
const route = ROUTES.get(normalized);
if (!route) throw new Error(`Unsupported ChatGPT Session model: ${model}`);
return route;
}

View File

@@ -0,0 +1,73 @@
/**
* Indirection layer over every side-effecting dependency of the executor (browser detection,
* storage-state IO, login probe, adapter turn). Tests swap the whole record so no unit test
* ever launches Chrome; production resolves to the vendored implementations.
*/
import { createChatGptWebAdapter } from "../../vendor/codex-chatgpt-web/adapters/chatgpt-web/index.ts";
import {
browserLoginStateExists,
inspectBrowserLoginCapabilities,
} from "../../vendor/codex-chatgpt-web/browser-login.ts";
import type {
AdapterEvent,
CodexParsedRequest,
CodexProviderConfig,
} from "../../vendor/codex-chatgpt-web/types.ts";
import { detectChromeExecutable } from "../chatgpt-web-codex.ts";
import {
ensureConnectionStorageStateFromCredential,
readConnectionStorageState,
} from "../chatgpt-web-codex/storageState.ts";
export interface ChatGptSessionLoginConfig {
appName: string;
storageStatePath: string;
headed: boolean;
proAvailable: boolean;
autoApproveToolCalls: boolean;
chromeExecutablePath?: string;
cdpEndpoint?: string;
}
export interface ChatGptSessionRuntime {
detectChrome(explicit?: string): string | undefined;
ensureStorageState(
connectionId: string,
credential: { cookie?: string; storageState?: Record<string, unknown> }
): string;
readStorageState(path: string): Record<string, unknown>;
loginStateExists(config: ChatGptSessionLoginConfig): boolean;
inspectLogin(
config: ChatGptSessionLoginConfig
): Promise<{ solAvailable: boolean; proAvailable: boolean }>;
runTurn(
parsed: CodexParsedRequest,
incoming: { headers: Headers; abortSignal?: AbortSignal },
emit: (event: AdapterEvent) => void,
provider: CodexProviderConfig
): Promise<void>;
}
const productionRuntime: ChatGptSessionRuntime = {
detectChrome: (explicit) => detectChromeExecutable(explicit),
ensureStorageState: (connectionId, credential) =>
ensureConnectionStorageStateFromCredential(connectionId, credential),
readStorageState: (path) => readConnectionStorageState(path),
loginStateExists: (config) => browserLoginStateExists(config),
inspectLogin: (config) => inspectBrowserLoginCapabilities(config),
runTurn: (parsed, incoming, emit, provider) =>
createChatGptWebAdapter(provider).runTurn(parsed, incoming, emit),
};
let override: Partial<ChatGptSessionRuntime> | null = null;
export function __setChatGptSessionRuntimeForTesting(
next: Partial<ChatGptSessionRuntime> | null
): void {
override = next;
}
export function chatGptSessionRuntime(): ChatGptSessionRuntime {
return override ? { ...productionRuntime, ...override } : productionRuntime;
}

View File

@@ -48,6 +48,10 @@ const lazyExecutors: Record<string, () => Promise<BaseExecutor>> = {
import("./chatgpt-web-codex.ts").then((m) => new m.ChatGptWebCodexExecutor()),
"cgpt-codex": () =>
import("./chatgpt-web-codex.ts").then((m) => new m.ChatGptWebCodexExecutor()),
"chatgpt-session": () =>
import("./chatgpt-session.ts").then((m) => new m.ChatGptSessionExecutor()),
"cgpt-session": () =>
import("./chatgpt-session.ts").then((m) => new m.ChatGptSessionExecutor()),
cursor: () => import("./cursor.ts").then((m) => new m.CursorExecutor()),
trae: () => import("./trae.ts").then((m) => new m.TraeExecutor()),
glm: () => import("./glm.ts").then((m) => new m.GlmExecutor("glm")),

View File

@@ -1,7 +1,7 @@
{
"name": "omniroute",
"version": "3.8.51",
"description": "Unified AI router with 352 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",

View File

@@ -1,6 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 420" font-family="system-ui, -apple-system, Segoe UI, Arial, Helvetica, sans-serif" role="img" aria-label="OmniRoute 4-tier fallback: your IDE or CLI calls one local endpoint and the OmniRoute Smart Router fails over across 352 providers in 4 tiers — Tier 1 Subscription, Tier 2 API, Tier 3 Cheap, Tier 4 Free.">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 420" font-family="system-ui, -apple-system, Segoe UI, Arial, Helvetica, sans-serif" role="img" aria-label="OmniRoute 4-tier fallback: your IDE or CLI calls one local endpoint and the OmniRoute Smart Router fails over across 353 providers in 4 tiers — Tier 1 Subscription, Tier 2 API, Tier 3 Cheap, Tier 4 Free.">
<title>OmniRoute 4-tier fallback</title>
<desc>OmniRoute 4-tier fallback: your IDE or CLI calls one local endpoint and the OmniRoute Smart Router fails over across 352 providers in 4 tiers — Tier 1 Subscription, Tier 2 API, Tier 3 Cheap, Tier 4 Free.</desc>
<desc>OmniRoute 4-tier fallback: your IDE or CLI calls one local endpoint and the OmniRoute Smart Router fails over across 353 providers in 4 tiers — Tier 1 Subscription, Tier 2 API, Tier 3 Cheap, Tier 4 Free.</desc>
<defs>
<marker id="arrow-dark" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto">
<path d="M0,0 L0,6 L9,3 z" fill="#9ca3af"/>
@@ -15,7 +15,7 @@
<!-- Title -->
<text x="400" y="32" text-anchor="middle" font-size="18" font-weight="700" fill="#f9fafb">OmniRoute 4-tier fallback</text>
<text x="400" y="52" text-anchor="middle" font-size="12" fill="#6b7280">Never stop building — automatic zero-config failover across 352 providers</text>
<text x="400" y="52" text-anchor="middle" font-size="12" fill="#6b7280">Never stop building — automatic zero-config failover across 353 providers</text>
<!-- Client box -->
<rect x="275" y="70" width="250" height="52" rx="8" fill="#1f2937" stroke="#374151" stroke-width="1.5"/>

Before

Width:  |  Height:  |  Size: 6.2 KiB

After

Width:  |  Height:  |  Size: 6.2 KiB

View File

@@ -1,6 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 420" font-family="system-ui, -apple-system, Segoe UI, Arial, Helvetica, sans-serif" role="img" aria-label="OmniRoute 4-tier fallback: your IDE or CLI calls one local endpoint and the OmniRoute Smart Router fails over across 352 providers in 4 tiers — Tier 1 Subscription, Tier 2 API, Tier 3 Cheap, Tier 4 Free.">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 420" font-family="system-ui, -apple-system, Segoe UI, Arial, Helvetica, sans-serif" role="img" aria-label="OmniRoute 4-tier fallback: your IDE or CLI calls one local endpoint and the OmniRoute Smart Router fails over across 353 providers in 4 tiers — Tier 1 Subscription, Tier 2 API, Tier 3 Cheap, Tier 4 Free.">
<title>OmniRoute 4-tier fallback</title>
<desc>OmniRoute 4-tier fallback: your IDE or CLI calls one local endpoint and the OmniRoute Smart Router fails over across 352 providers in 4 tiers — Tier 1 Subscription, Tier 2 API, Tier 3 Cheap, Tier 4 Free.</desc>
<desc>OmniRoute 4-tier fallback: your IDE or CLI calls one local endpoint and the OmniRoute Smart Router fails over across 353 providers in 4 tiers — Tier 1 Subscription, Tier 2 API, Tier 3 Cheap, Tier 4 Free.</desc>
<defs>
<marker id="arrow-light" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto">
<path d="M0,0 L0,6 L9,3 z" fill="#6b7280"/>
@@ -15,7 +15,7 @@
<!-- Title -->
<text x="400" y="32" text-anchor="middle" font-size="18" font-weight="700" fill="#111827">OmniRoute 4-tier fallback</text>
<text x="400" y="52" text-anchor="middle" font-size="12" fill="#6b7280">Never stop building — automatic zero-config failover across 352 providers</text>
<text x="400" y="52" text-anchor="middle" font-size="12" fill="#6b7280">Never stop building — automatic zero-config failover across 353 providers</text>
<!-- Client box -->
<rect x="275" y="70" width="250" height="52" rx="8" fill="#ffffff" stroke="#d1d5db" stroke-width="1.5"/>

Before

Width:  |  Height:  |  Size: 6.2 KiB

After

Width:  |  Height:  |  Size: 6.2 KiB

View File

@@ -2,7 +2,10 @@
import { useState, useEffect, useRef } from "react";
import { useTranslations } from "next-intl";
import { Button, Badge, Input, Modal, Toggle, TALL_MODAL_PROPS } from "@/shared/components";
import { CHATGPT_WEB_CODEX_CONNECTOR_NAME } from "@/shared/constants/chatgptWebCodex";
import {
CHATGPT_WEB_CODEX_CONNECTOR_NAME,
usesChatGptBrowserSessionCredentials,
} from "@/shared/constants/chatgptWebCodex";
import {
providerAllowsOptionalApiKey,
supportsBulkApiKey,
@@ -97,6 +100,12 @@ export default function AddApiKeyModal({
const isLocalSelfHostedProvider = !!localProviderMetadata;
const isGooglePse = provider === "google-pse-search";
const isChatGptWebCodex = provider === "chatgpt-web-codex";
// The credential ENVELOPE is decided by the shared browser-session lifecycle predicate,
// not by the codex id: `/api/providers` routes every provider this predicate accepts into
// `finalizeValidatedChatGptWebCodexSecrets`, which starts with `JSON.parse`. Posting a raw
// cookie for one of them fails the save with a JSON parse error, so client and server must
// read the same predicate.
const usesBrowserSessionCredential = usesChatGptBrowserSessionCredentials(provider);
const isAwsPolly = provider === "aws-polly";
const webSessionCredential = getWebSessionCredentialRequirement(provider);
const isNoAuthWebSessionCredential = webSessionCredential?.kind === "none";
@@ -402,11 +411,12 @@ export default function AddApiKeyModal({
...(validatedProviderSpecificData || {}),
};
const encodedCredential = isChatGptWebCodex
const encodedCredential = usesBrowserSessionCredential
? JSON.stringify({
version: 1,
cookie: credentialInput.trim().replace(/^cookie\s*:\s*/i, ""),
runtimeKey: formData.runtimeKey.trim(),
// Only chatgpt-web-codex ever has a runtime key; omit the field entirely otherwise.
...(formData.runtimeKey.trim() ? { runtimeKey: formData.runtimeKey.trim() } : {}),
})
: credentialInput.trim();
const payload = {

View File

@@ -3,7 +3,10 @@
import { useState } from "react";
import { useTranslations } from "next-intl";
import { Button, Badge, Input, Modal, Toggle, Select } from "@/shared/components";
import { CHATGPT_WEB_CODEX_CONNECTOR_NAME } from "@/shared/constants/chatgptWebCodex";
import {
CHATGPT_WEB_CODEX_CONNECTOR_NAME,
usesChatGptBrowserSessionCredentials,
} from "@/shared/constants/chatgptWebCodex";
import {
isOpenAICompatibleProvider,
isAnthropicCompatibleProvider,
@@ -230,6 +233,11 @@ export default function EditConnectionModal({
const isLocalSelfHostedProvider = !!localProviderMetadata;
const isGooglePse = provider === "google-pse-search";
const isChatGptWebCodex = provider === "chatgpt-web-codex";
// The credential ENVELOPE is decided by the shared browser-session lifecycle predicate,
// not by the codex id: `/api/providers/[id]` routes every provider this predicate accepts
// into `decodeChatGptWebCodexSecrets` + `finalizeValidatedChatGptWebCodexSecrets`, both of
// which expect the JSON envelope. Client and server must read the same predicate.
const usesBrowserSessionCredential = usesChatGptBrowserSessionCredentials(provider);
const isAwsPolly = provider === "aws-polly";
const isM365TierCapable = isM365TierCapableProvider(provider);
const webSessionCredential = getWebSessionCredentialRequirement(provider);
@@ -648,7 +656,7 @@ export default function EditConnectionModal({
}
}
if (isValid) {
updates.apiKey = isChatGptWebCodex
updates.apiKey = usesBrowserSessionCredential
? JSON.stringify({
version: 1,
cookie: formData.apiKey.trim().replace(/^cookie\s*:\s*/i, ""),

View File

@@ -34,7 +34,9 @@ import {
decodeChatGptWebCodexSecrets,
encodeChatGptWebCodexSecrets,
} from "@omniroute/open-sse/services/chatgptWebCodexAdmin.ts";
import { sanitizeErrorMessage } from "@omniroute/open-sse/utils/error.ts";
import { rejectRetiredCommonChatGptWebProvider } from "@/lib/providers/chatgptWebRetirementResponse";
import { usesChatGptBrowserSessionCredentials } from "@/shared/constants/chatgptWebCodex";
function normalizeCodexLimitPolicy(
incoming: unknown,
@@ -172,7 +174,7 @@ export async function PUT(request: Request, { params }: { params: Promise<{ id:
if (defaultModel !== undefined) updateData.defaultModel = defaultModel;
if (isActive !== undefined) updateData.isActive = isActive;
if (apiKey && canUpdateProviderApiKey(existing.authType, existing.provider)) {
if (existing.provider === "chatgpt-web-codex") {
if (usesChatGptBrowserSessionCredentials(existing.provider)) {
const validationId =
incomingPsd && typeof incomingPsd.validationId === "string"
? incomingPsd.validationId
@@ -191,10 +193,11 @@ export async function PUT(request: Request, { params }: { params: Promise<{ id:
} catch (error) {
return NextResponse.json(
{
error:
error: sanitizeErrorMessage(
error instanceof Error
? error.message
: "Die ChatGPT-Browserprüfung konnte nicht abgeschlossen werden.",
: "The browser session verification could not be completed."
),
},
{ status: 400 }
);
@@ -215,7 +218,8 @@ export async function PUT(request: Request, { params }: { params: Promise<{ id:
// the override (connection follows the global default); 0-1440 = explicit
// per-connection minutes (0 opts this connection out of the sweep).
if (healthCheckInterval === null) updateData.healthCheckInterval = null;
else if (healthCheckInterval !== undefined) updateData.healthCheckInterval = healthCheckInterval;
else if (healthCheckInterval !== undefined)
updateData.healthCheckInterval = healthCheckInterval;
if (group !== undefined) updateData.group = group;
if (maxConcurrent !== undefined) updateData.maxConcurrent = maxConcurrent;
if (incomingWindowThresholds !== undefined) {

View File

@@ -48,6 +48,8 @@ import {
getModelSyncInternalBaseUrl,
} from "@/shared/services/modelSyncScheduler";
import { finalizeValidatedChatGptWebCodexSecrets } from "@omniroute/open-sse/services/chatgptWebCodexAdmin.ts";
import { sanitizeErrorMessage } from "@omniroute/open-sse/utils/error.ts";
import { usesChatGptBrowserSessionCredentials } from "@/shared/constants/chatgptWebCodex";
import { isAutoFetchModelsEnabled } from "@/lib/providerModels/modelDiscovery";
import { testSingleConnection } from "./[id]/test/route";
import { rejectRetiredCommonChatGptWebProvider } from "@/lib/providers/chatgptWebRetirementResponse";
@@ -198,7 +200,7 @@ export async function POST(request: Request) {
providerSpecificData = normalizeQoderPatProviderData(providerSpecificData || {});
}
if (provider === "chatgpt-web-codex") {
if (usesChatGptBrowserSessionCredentials(provider)) {
const validationId =
providerSpecificData && typeof providerSpecificData.validationId === "string"
? providerSpecificData.validationId
@@ -211,10 +213,11 @@ export async function POST(request: Request) {
} catch (error) {
return NextResponse.json(
{
error:
error: sanitizeErrorMessage(
error instanceof Error
? error.message
: "Die ChatGPT-Browserprüfung konnte nicht abgeschlossen werden.",
: "The browser session verification could not be completed."
),
},
{ status: 400 }
);
@@ -324,11 +327,16 @@ export async function POST(request: Request) {
})
.then((syncRes) => {
if (!syncRes.ok) {
console.log(`[providers] Auto-sync failed for ${newConnection.id}: ${syncRes.status}`);
console.log(
`[providers] Auto-sync failed for ${newConnection.id}: ${syncRes.status}`
);
}
})
.catch((err) => {
console.log(`[providers] Auto-sync error for ${newConnection.id}:`, err?.message || err);
console.log(
`[providers] Auto-sync error for ${newConnection.id}:`,
err?.message || err
);
});
} catch (syncSetupError) {
// Defensive: if URL parsing or header construction itself throws, do

View File

@@ -80,6 +80,7 @@ import {
validatePoeProvider,
} from "./validation/audioMiscProviders";
import { validateChatGptWebCodexProvider } from "./validation/chatgptWebCodex";
import { validateChatGptSessionProvider } from "./validation/chatgptSession";
import { validateZaiWebProvider } from "./validation/zaiWeb";
import { validateSearchProvider, SEARCH_VALIDATOR_CONFIGS } from "./validation/searchProviders";
import {
@@ -309,6 +310,7 @@ export async function validateProviderApiKey({ provider, apiKey, providerSpecifi
"grok-web": validateGrokWebProvider,
"kimi-web": validateKimiWebProvider,
"chatgpt-web-codex": validateChatGptWebCodexProvider,
"chatgpt-session": validateChatGptSessionProvider,
"perplexity-web": validatePerplexityWebProvider,
"blackbox-web": validateBlackboxWebProvider,
"muse-spark-web": validateMuseSparkWebProvider,

View File

@@ -0,0 +1,93 @@
import { randomBytes } from "node:crypto";
import { rmSync } from "node:fs";
import { CHATGPT_WEB_CODEX_CONNECTOR_NAME } from "@/shared/constants/chatgptWebCodex";
import { inspectBrowserLoginCapabilities } from "@omniroute/open-sse/vendor/codex-chatgpt-web/browser-login.ts";
import { decodeChatGptWebCodexSecrets } from "@omniroute/open-sse/executors/chatgpt-web-codex/credentials.ts";
import { detectChromeExecutable } from "@omniroute/open-sse/executors/chatgpt-web-codex.ts";
import {
connectionRuntimePaths,
ensureConnectionStorageState,
ensureConnectionStorageStateFromCredential,
} from "@omniroute/open-sse/executors/chatgpt-web-codex/storageState.ts";
import { sanitizeErrorMessage } from "@omniroute/open-sse/utils/error.ts";
export async function validateChatGptSessionProvider({
apiKey,
providerSpecificData = {},
}: {
apiKey?: string;
providerSpecificData?: Record<string, unknown>;
}) {
try {
const secrets = decodeChatGptWebCodexSecrets(String(apiKey || ""));
if (!secrets.cookie && !secrets.storageState) {
return {
valid: false,
error: "A ChatGPT cookie header or a stored browser session is required.",
};
}
const cdpEndpoint = process.env.CHATGPT_WEB_CODEX_CDP_URL?.trim();
const chromeExecutablePath = detectChromeExecutable(
typeof providerSpecificData.chromeExecutablePath === "string"
? providerSpecificData.chromeExecutablePath
: undefined
);
if (!chromeExecutablePath && !cdpEndpoint) {
return {
valid: false,
error:
"No supported Chrome or Chromium was found. Install Chromium or configure the browser path.",
};
}
const validationId = `validation-${randomBytes(12).toString("hex")}`;
const paths = connectionRuntimePaths(validationId);
const freshCookie = Boolean(secrets.cookie);
if (secrets.cookie) ensureConnectionStorageState(validationId, secrets.cookie);
else ensureConnectionStorageStateFromCredential(validationId, secrets);
let capabilities;
try {
capabilities = await inspectBrowserLoginCapabilities({
appName: CHATGPT_WEB_CODEX_CONNECTOR_NAME,
...(chromeExecutablePath ? { chromeExecutablePath } : {}),
...(cdpEndpoint ? { cdpEndpoint } : {}),
storageStatePath: paths.storageStatePath,
headed: false,
proAvailable: false,
autoApproveToolCalls: false,
});
} catch (error) {
rmSync(paths.root, { recursive: true, force: true });
throw error;
}
if (!freshCookie) rmSync(paths.root, { recursive: true, force: true });
return {
valid: true,
error: null,
method: "headless-browser",
capabilities: {
browser: "ready",
storageState: "verified",
login: "authenticated",
solAvailable: capabilities.solAvailable,
proAvailable: capabilities.proAvailable,
},
providerSpecificData: {
solAvailable: capabilities.solAvailable,
proAvailable: capabilities.proAvailable,
browserVerified: true,
...(chromeExecutablePath ? { chromeExecutablePath } : {}),
...(freshCookie ? { validationId } : {}),
},
};
} catch (error) {
return {
valid: false,
error: sanitizeErrorMessage(error instanceof Error ? error.message : error),
};
}
}

View File

@@ -329,6 +329,7 @@ const LOBE_PROVIDER_ALIASES = {
"black-forest-labs": "Bfl",
cerebras: "Cerebras",
"chatgpt-web-codex": "OpenAI",
"chatgpt-session": "OpenAI",
claude: "ClaudeCode",
"claude-web": "Claude",
cline: "Cline",

View File

@@ -10,3 +10,21 @@ export function isChatGptWebCodexModel(model: unknown): boolean {
// persisted account session is valid. Keep runtime turns aligned with the headed browser
// used to verify that same storage state.
export const CHATGPT_WEB_CODEX_RUNTIME_HEADED = true;
// Both "chatgpt-web-codex" and "chatgpt-session" store a verified Playwright storage
// state produced from a pasted ChatGPT cookie header — the browser-session credential
// lifecycle (decode -> ensure storage state -> inspect -> finalize) is shared between
// them. The finalize step is what discards the raw pasted cookie in favor of the
// verified storage state, so any dashboard route that gates on that lifecycle must
// recognize both provider ids, not just the codex one.
export const CHATGPT_BROWSER_SESSION_PROVIDER_IDS = [
CHATGPT_WEB_CODEX_PROVIDER_ID,
"chatgpt-session",
] as const;
export function usesChatGptBrowserSessionCredentials(provider: unknown): boolean {
return (
typeof provider === "string" &&
(CHATGPT_BROWSER_SESSION_PROVIDER_IDS as readonly string[]).includes(provider)
);
}

View File

@@ -18,6 +18,21 @@ export const WEB_COOKIE_PROVIDERS = {
riskNoticeVariant: "webCookie",
toolCalling: "native",
},
"chatgpt-session": {
id: "chatgpt-session",
serviceKinds: ["llm"],
alias: "cgpt-session",
name: "ChatGPT Web (Session)",
icon: "chat",
color: "#10A37F",
textIcon: "CS",
website: "https://chatgpt.com",
authHint:
"Paste the full ChatGPT Cookie header. OmniRoute verifies it in an isolated browser profile and then stores only the verified session state.",
subscriptionRisk: true,
riskNoticeVariant: "webCookie",
toolCalling: "emulated",
},
"grok-web": {
id: "grok-web",
serviceKinds: ["llm"],

View File

@@ -35,6 +35,13 @@ export const WEB_SESSION_CREDENTIAL_REQUIREMENTS = {
acceptsFullCookieHeader: true,
storageKeys: ["cookie", "sessionToken", "session-token", "__Secure-next-auth.session-token"],
},
"chatgpt-session": {
kind: "cookie",
credentialName: "ChatGPT Cookie header (full)",
placeholder: "__Secure-next-auth.session-token=...; cf_clearance=...",
acceptsFullCookieHeader: true,
storageKeys: ["cookie", "sessionToken", "session-token", "__Secure-next-auth.session-token"],
},
"zenmux-free": {
kind: "cookie",
credentialName: "Cookie header (full)",

View File

@@ -90,6 +90,16 @@
"configSource": "<custom-config>",
"provider": "chatgpt-web-codex"
},
"cgpt-session": {
"className": "ChatGptSessionExecutor",
"configSource": "<custom-config>",
"provider": "chatgpt-session"
},
"chatgpt-session": {
"className": "ChatGptSessionExecutor",
"configSource": "<custom-config>",
"provider": "chatgpt-session"
},
"chatgpt-web-codex": {
"className": "ChatGptWebCodexExecutor",
"configSource": "<custom-config>",
@@ -666,6 +676,6 @@
"provider": "zai-web"
}
},
"keyCount": 133,
"keyCount": 135,
"sharedInstances": []
}

View File

@@ -842,6 +842,29 @@
"stream": "https://api.chatanywhere.org/v1/chat/completions"
}
},
"chatgpt-session": {
"format": "openai",
"headers": {
"apiKey": {
"Accept": "text/event-stream",
"Authorization": "Bearer <TOK>",
"Content-Type": "application/json"
},
"nonStream": {
"Authorization": "Bearer <TOK>",
"Content-Type": "application/json"
},
"oauth": {
"Accept": "text/event-stream",
"Authorization": "Bearer <TOK>",
"Content-Type": "application/json"
}
},
"url": {
"nonStream": "https://chatgpt.com",
"stream": "https://chatgpt.com"
}
},
"chatgpt-web-codex": {
"format": "openai-responses",
"headers": {

View File

@@ -0,0 +1,489 @@
import { test } from "node:test";
import assert from "node:assert/strict";
import {
CHATGPT_SESSION_STREAM_OPEN_TIMEOUT_MS,
buildChatGptSessionCompletion,
openChatGptSessionStream,
resolveChatGptSessionStreamOpenTimeoutMs,
} from "../../open-sse/executors/chatgpt-session/bridge.ts";
import type { AdapterEvent } from "../../open-sse/vendor/codex-chatgpt-web/types.ts";
const META = { cid: "chatcmpl-test", created: 1_700_000_000, model: "chatgpt-session/high" };
async function* iterate(events: AdapterEvent[]): AsyncGenerator<AdapterEvent> {
for (const event of events) yield event;
}
async function readAll(stream: ReadableStream<Uint8Array>): Promise<string> {
const decoder = new TextDecoder();
let out = "";
for await (const chunk of stream as unknown as AsyncIterable<Uint8Array>) {
out += decoder.decode(chunk, { stream: true });
}
return out + decoder.decode();
}
test("streams role, content and a terminal stop chunk", async () => {
const opened = await openChatGptSessionStream(
iterate([
{ type: "text_delta", text: "Hel" },
{ type: "text_delta", text: "lo" },
{ type: "done", usage: { inputTokens: 10, outputTokens: 2 } },
]),
META
);
assert.equal(opened.kind, "stream");
const text = await readAll((opened as { stream: ReadableStream<Uint8Array> }).stream);
assert.match(text, /"delta":\{"role":"assistant"\}/);
assert.match(text, /"content":"Hel"/);
assert.match(text, /"content":"lo"/);
assert.match(text, /"finish_reason":"stop"/);
assert.match(text, /"prompt_tokens":10/);
assert.ok(text.trimEnd().endsWith("data: [DONE]"));
});
test("thinking deltas surface as reasoning_content", async () => {
const opened = await openChatGptSessionStream(
iterate([
{ type: "thinking_delta", thinking: "hmm" },
{ type: "text_delta", text: "ok" },
{ type: "done" },
]),
META
);
const text = await readAll((opened as { stream: ReadableStream<Uint8Array> }).stream);
assert.match(text, /"reasoning_content":"hmm"/);
});
test("heartbeats become SSE comments, never data chunks", async () => {
const opened = await openChatGptSessionStream(
iterate([{ type: "text_delta", text: "x" }, { type: "heartbeat" }, { type: "done" }]),
META
);
const text = await readAll((opened as { stream: ReadableStream<Uint8Array> }).stream);
assert.match(text, /^: keepalive$/m);
assert.doesNotMatch(text, /"heartbeat"/);
});
test("an error before any output returns an error verdict instead of a stream", async () => {
const opened = await openChatGptSessionStream(
iterate([{ type: "error", message: "ChatGPT page is not authenticated" }]),
META
);
assert.equal(opened.kind, "error");
assert.equal((opened as { status: number }).status, 401);
assert.equal((opened as { code: string }).code, "session_expired");
});
test("an error verdict message is sanitized before it leaves the bridge", async () => {
const opened = await openChatGptSessionStream(
iterate([
{
type: "error",
message: "not authenticated\n at /app/open-sse/x.ts:1:1",
},
]),
META
);
assert.equal(opened.kind, "error");
assert.doesNotMatch((opened as { message: string }).message, /at \//);
});
test("an error mid-stream terminates the stream after the emitted text", async () => {
const opened = await openChatGptSessionStream(
iterate([
{ type: "text_delta", text: "partial" },
{ type: "error", message: "boom", status: 502 },
]),
META
);
assert.equal(opened.kind, "stream");
const text = await readAll((opened as { stream: ReadableStream<Uint8Array> }).stream);
assert.match(text, /"content":"partial"/);
assert.match(text, /"error"/);
assert.match(text, /"message":"boom"/);
assert.match(text, /"code":"turn_failed"/);
assert.ok(text.trimEnd().endsWith("data: [DONE]"));
const doneCount = text.split("data: [DONE]").length - 1;
assert.equal(doneCount, 1);
});
test("incomplete maps to a length finish reason", async () => {
const opened = await openChatGptSessionStream(
iterate([
{ type: "text_delta", text: "x" },
{ type: "incomplete", reason: "max_output" },
]),
META
);
const text = await readAll((opened as { stream: ReadableStream<Uint8Array> }).stream);
assert.match(text, /"finish_reason":"length"/);
});
test("a terminal event as the first meaningful event is replayed, not dropped", async () => {
const opened = await openChatGptSessionStream(
iterate([{ type: "done", usage: { inputTokens: 1, outputTokens: 1 } }]),
META
);
assert.equal(opened.kind, "stream");
const text = await readAll((opened as { stream: ReadableStream<Uint8Array> }).stream);
assert.match(text, /"delta":\{"role":"assistant"\}/);
assert.match(text, /"finish_reason":"stop"/);
assert.ok(text.trimEnd().endsWith("data: [DONE]"));
});
test("a source that ends with no terminal event still emits a synthetic stop", async () => {
const opened = await openChatGptSessionStream(iterate([{ type: "text_delta", text: "x" }]), META);
assert.equal(opened.kind, "stream");
const text = await readAll((opened as { stream: ReadableStream<Uint8Array> }).stream);
assert.match(text, /"content":"x"/);
assert.match(text, /"finish_reason":"stop"/);
assert.ok(text.trimEnd().endsWith("data: [DONE]"));
});
test("an empty source still opens a stream with role and a synthetic stop", async () => {
const opened = await openChatGptSessionStream(iterate([]), META);
assert.equal(opened.kind, "stream");
const text = await readAll((opened as { stream: ReadableStream<Uint8Array> }).stream);
assert.match(text, /"delta":\{"role":"assistant"\}/);
assert.match(text, /"finish_reason":"stop"/);
assert.ok(text.trimEnd().endsWith("data: [DONE]"));
});
test("a heartbeat-only source still opens a stream and never leaks the heartbeat type", async () => {
const opened = await openChatGptSessionStream(
iterate([{ type: "heartbeat" }, { type: "heartbeat" }]),
META
);
assert.equal(opened.kind, "stream");
const text = await readAll((opened as { stream: ReadableStream<Uint8Array> }).stream);
assert.match(text, /"delta":\{"role":"assistant"\}/);
assert.match(text, /"finish_reason":"stop"/);
assert.ok(text.trimEnd().endsWith("data: [DONE]"));
assert.doesNotMatch(text, /"heartbeat"/);
});
test("incomplete with endTurn true maps to a stop finish reason", async () => {
const opened = await openChatGptSessionStream(
iterate([
{ type: "text_delta", text: "x" },
{ type: "incomplete", reason: "x", endTurn: true },
]),
META
);
const text = await readAll((opened as { stream: ReadableStream<Uint8Array> }).stream);
assert.match(text, /"finish_reason":"stop"/);
assert.doesNotMatch(text, /"finish_reason":"length"/);
});
test("buffered completion collects content, reasoning and usage", () => {
const result = buildChatGptSessionCompletion(
[
{ type: "thinking_delta", thinking: "think" },
{ type: "text_delta", text: "Hello" },
{ type: "text_delta", text: " world" },
{ type: "done", usage: { inputTokens: 7, outputTokens: 3, totalTokens: 10 } },
],
META
);
assert.equal(result.status, 200);
const choice = (result.body.choices as Array<Record<string, unknown>>)[0];
const message = choice.message as Record<string, unknown>;
assert.equal(message.content, "Hello world");
assert.equal(message.reasoning_content, "think");
assert.equal(choice.finish_reason, "stop");
assert.deepEqual(result.body.usage, {
prompt_tokens: 7,
completion_tokens: 3,
total_tokens: 10,
});
});
test("buffered completion surfaces an error event as a classified status", () => {
const result = buildChatGptSessionCompletion(
[{ type: "error", message: "ChatGPT reported a usage limit" }],
META
);
assert.equal(result.status, 429);
});
test("buffered error bodies never leak a stack trace", () => {
const result = buildChatGptSessionCompletion(
[{ type: "error", message: "failure\n at /app/open-sse/x.ts:1:1" }],
META
);
const error = result.body.error as Record<string, unknown>;
assert.doesNotMatch(String(error.message), /at \//);
assert.match(String(error.message), /failure/);
});
test("the commentary read-only banner never reaches streamed content", async () => {
const opened = await openChatGptSessionStream(
iterate([
{ type: "assistant_boundary" },
{
type: "text_delta",
text: "⚠️ The local Codex computer is unavailable, so this turn is read-only.",
phase: "commentary",
},
{ type: "assistant_boundary" },
{ type: "text_delta", text: "real answer" },
{ type: "done" },
]),
META
);
assert.equal(opened.kind, "stream");
const text = await readAll((opened as { stream: ReadableStream<Uint8Array> }).stream);
const content = [...text.matchAll(/"content":"((?:[^"\\]|\\.)*)"/g)]
.map((match) => JSON.parse(`"${match[1]}"`) as string)
.join("");
assert.equal(content, "real answer");
assert.doesNotMatch(text, /read-only/);
assert.doesNotMatch(text, /Codex computer/);
// Commentary must not be laundered into reasoning either — it is transport chatter.
assert.doesNotMatch(text, /"reasoning_content"/);
});
test("the commentary read-only banner never reaches buffered content", () => {
const result = buildChatGptSessionCompletion(
[
{ type: "assistant_boundary" },
{
type: "text_delta",
text: "⚠️ The local Codex computer is unavailable, so this turn is read-only.",
phase: "commentary",
},
{ type: "assistant_boundary" },
{ type: "text_delta", text: "real answer" },
{ type: "done" },
],
META
);
assert.equal(result.status, 200);
const message = (result.body.choices as Array<Record<string, unknown>>)[0].message as Record<
string,
unknown
>;
assert.equal(message.content, "real answer");
assert.equal("reasoning_content" in message, false);
assert.doesNotMatch(JSON.stringify(result.body), /read-only/);
});
// I1 — the streaming gate and the buffered failover must agree on what counts as committed
// output. Reasoning alone commits neither, so a failure right after a thinking delta is a real
// HTTP status on BOTH paths instead of a 200 stream carrying an in-band error chunk.
test("reasoning before an error does not commit a 200 on the streaming path", async () => {
const opened = await openChatGptSessionStream(
iterate([
{ type: "thinking_delta", thinking: "weighing options" },
{ type: "error", message: "ChatGPT reported a usage limit", status: 429 },
]),
META
);
assert.equal(opened.kind, "error");
assert.equal((opened as { status: number }).status, 429);
});
test("the buffered path returns the same status for reasoning followed by an error", () => {
const result = buildChatGptSessionCompletion(
[
{ type: "thinking_delta", thinking: "weighing options" },
{ type: "error", message: "ChatGPT reported a usage limit", status: 429 },
],
META
);
assert.equal(result.status, 429);
});
test("reasoning buffered behind the gate is still streamed once the gate opens", async () => {
const opened = await openChatGptSessionStream(
iterate([
{ type: "thinking_delta", thinking: "first" },
{ type: "thinking_delta", thinking: "second" },
{ type: "text_delta", text: "answer" },
{ type: "done" },
]),
META
);
assert.equal(opened.kind, "stream");
const text = await readAll((opened as { stream: ReadableStream<Uint8Array> }).stream);
assert.match(text, /"reasoning_content":"first"/);
assert.match(text, /"reasoning_content":"second"/);
assert.match(text, /"content":"answer"/);
});
test("an empty text delta does not commit a 200 ahead of an error", async () => {
const opened = await openChatGptSessionStream(
iterate([
{ type: "text_delta", text: "" },
{ type: "error", message: "ChatGPT reported a usage limit" },
]),
META
);
assert.equal(opened.kind, "error");
assert.equal((opened as { status: number }).status, 429);
});
test("a cooldown-hinted classification reaches the stream-open error verdict", async () => {
const opened = await openChatGptSessionStream(
iterate([{ type: "error", message: "upstream unavailable", status: 503 }]),
META
);
assert.equal(opened.kind, "error");
assert.equal((opened as { fallbackHint?: string }).fallbackHint, "connection_cooldown");
});
// I2 — the gate must be bounded: while it is closed the client receives nothing at all (any byte
// commits the 200), so a healthy turn that thinks for a long time in the browser would otherwise
// look dead to a client with an idle timeout.
interface ControllableSource {
events: AsyncIterable<AdapterEvent>;
push(event: AdapterEvent): void;
end(): void;
}
function controllable(): ControllableSource {
const queued: AdapterEvent[] = [];
const waiting: Array<(result: IteratorResult<AdapterEvent>) => void> = [];
let ended = false;
const iterator: AsyncIterator<AdapterEvent> = {
next() {
const event = queued.shift();
if (event) return Promise.resolve({ value: event, done: false });
if (ended) return Promise.resolve({ value: undefined, done: true });
return new Promise((resolve) => waiting.push(resolve));
},
};
return {
events: { [Symbol.asyncIterator]: () => iterator },
push(event) {
const waiter = waiting.shift();
if (waiter) waiter({ value: event, done: false });
else queued.push(event);
},
end() {
ended = true;
for (const waiter of waiting.splice(0)) waiter({ value: undefined, done: true });
},
};
}
test("the default stream-open deadline stays at 30s", () => {
assert.equal(CHATGPT_SESSION_STREAM_OPEN_TIMEOUT_MS, 30_000);
});
test("a silent turn opens the stream once the gate deadline elapses", async () => {
const source = controllable();
const opened = await openChatGptSessionStream(source.events, META, {
streamOpenTimeoutMs: 5,
});
assert.equal(opened.kind, "stream");
const reading = readAll((opened as { stream: ReadableStream<Uint8Array> }).stream);
source.push({ type: "heartbeat" });
source.push({ type: "text_delta", text: "late answer" });
source.push({ type: "done" });
source.end();
const text = await reading;
assert.match(text, /"delta":\{"role":"assistant"\}/);
assert.match(text, /"content":"late answer"/);
assert.match(text, /"finish_reason":"stop"/);
// The role chunk still leads, and heartbeats keep flowing as SSE comments once the gate opened.
assert.ok(text.indexOf('"role":"assistant"') < text.indexOf('"content":"late answer"'));
assert.match(text, /^: keepalive$/m);
assert.ok(text.trimEnd().endsWith("data: [DONE]"));
});
test("reasoning buffered before the deadline is replayed in order after the gate opens", async () => {
const source = controllable();
source.push({ type: "thinking_delta", thinking: "first" });
source.push({ type: "thinking_delta", thinking: "second" });
const opened = await openChatGptSessionStream(source.events, META, {
streamOpenTimeoutMs: 5,
});
assert.equal(opened.kind, "stream");
const reading = readAll((opened as { stream: ReadableStream<Uint8Array> }).stream);
source.push({ type: "text_delta", text: "answer" });
source.push({ type: "done" });
source.end();
const text = await reading;
const roleAt = text.indexOf('"role":"assistant"');
const firstAt = text.indexOf('"reasoning_content":"first"');
const secondAt = text.indexOf('"reasoning_content":"second"');
const answerAt = text.indexOf('"content":"answer"');
assert.ok(roleAt >= 0 && firstAt > roleAt && secondAt > firstAt && answerAt > secondAt);
});
test("an error before the deadline still returns an error verdict, never a timed-out stream", async () => {
const source = controllable();
const opening = openChatGptSessionStream(source.events, META, {
streamOpenTimeoutMs: 1_000,
});
source.push({ type: "error", message: "ChatGPT reported a usage limit" });
const opened = await opening;
assert.equal(opened.kind, "error");
assert.equal((opened as { status: number }).status, 429);
});
test("no event is lost to the deadline race", async () => {
const source = controllable();
const opened = await openChatGptSessionStream(source.events, META, {
streamOpenTimeoutMs: 5,
});
const reading = readAll((opened as { stream: ReadableStream<Uint8Array> }).stream);
source.push({ type: "text_delta", text: "one" });
source.push({ type: "text_delta", text: "two" });
source.push({ type: "done" });
source.end();
const text = await reading;
assert.match(text, /"content":"one"/);
assert.match(text, /"content":"two"/);
});
const STREAM_OPEN_TIMEOUT_ENV_VAR = "OMNIROUTE_CHATGPT_SESSION_STREAM_OPEN_TIMEOUT_MS";
function withStreamOpenTimeoutEnv(raw: string | undefined, run: () => void): void {
const saved = process.env[STREAM_OPEN_TIMEOUT_ENV_VAR];
if (raw === undefined) delete process.env[STREAM_OPEN_TIMEOUT_ENV_VAR];
else process.env[STREAM_OPEN_TIMEOUT_ENV_VAR] = raw;
try {
run();
} finally {
if (saved === undefined) delete process.env[STREAM_OPEN_TIMEOUT_ENV_VAR];
else process.env[STREAM_OPEN_TIMEOUT_ENV_VAR] = saved;
}
}
test("resolveChatGptSessionStreamOpenTimeoutMs falls back to the default when the env var is unset", () => {
withStreamOpenTimeoutEnv(undefined, () => {
assert.equal(
resolveChatGptSessionStreamOpenTimeoutMs(),
CHATGPT_SESSION_STREAM_OPEN_TIMEOUT_MS
);
});
});
test("resolveChatGptSessionStreamOpenTimeoutMs honors a valid positive integer", () => {
withStreamOpenTimeoutEnv("45000", () => {
assert.equal(resolveChatGptSessionStreamOpenTimeoutMs(), 45_000);
});
});
const INVALID_STREAM_OPEN_TIMEOUT_INPUTS: Array<[label: string, raw: string]> = [
["an empty string", ""],
["non-numeric text", "not-a-number"],
["zero", "0"],
["a negative number", "-10"],
["a non-finite value", "Infinity"],
];
for (const [label, raw] of INVALID_STREAM_OPEN_TIMEOUT_INPUTS) {
test(`resolveChatGptSessionStreamOpenTimeoutMs falls back to the default for ${label}`, () => {
withStreamOpenTimeoutEnv(raw, () => {
assert.equal(
resolveChatGptSessionStreamOpenTimeoutMs(),
CHATGPT_SESSION_STREAM_OPEN_TIMEOUT_MS
);
});
});
}

View File

@@ -0,0 +1,157 @@
import { test } from "node:test";
import assert from "node:assert/strict";
import { classifyChatGptSessionError } from "../../open-sse/executors/chatgpt-session/errors.ts";
import { ChatGptSessionInputError } from "../../open-sse/executors/chatgpt-session/messages.ts";
test("a missing browser is a cooldown-hinted 503", () => {
const result = classifyChatGptSessionError(
new Error("No supported Chrome or Chromium executable was found")
);
assert.equal(result.status, 503);
assert.equal(result.code, "browser_unavailable");
assert.equal(result.fallbackHint, "connection_cooldown");
});
test("a playwright launch failure is also a cooldown-hinted 503", () => {
const result = classifyChatGptSessionError(
new Error("browserType.launch: Executable doesn't exist at /root/.cache/ms-playwright/x")
);
assert.equal(result.status, 503);
assert.equal(result.fallbackHint, "connection_cooldown");
});
test("missing credentials are a 401", () => {
assert.equal(
classifyChatGptSessionError(new Error("ChatGPT browser credentials are missing")).status,
401
);
});
test("an expired session is a 401 session_expired", () => {
const result = classifyChatGptSessionError(new Error("ChatGPT page is not authenticated"));
assert.equal(result.status, 401);
assert.equal(result.code, "session_expired");
});
test("a rate-limit dialog is a 429", () => {
const result = classifyChatGptSessionError(new Error("ChatGPT reported a usage limit"));
assert.equal(result.status, 429);
assert.equal(result.code, "rate_limited");
});
test("an account-capability mismatch is a terminal 400", () => {
const result = classifyChatGptSessionError(
new Error("pro is not available for this non-Pro connection")
);
assert.equal(result.status, 400);
assert.equal(result.code, "route_unavailable");
assert.equal(result.fallbackHint, undefined);
});
test("a DOM timeout is a terminal 400, not a retryable 5xx", () => {
const timeout = new Error("locator.waitForSelector: Timeout 30000ms exceeded");
timeout.name = "TimeoutError";
const result = classifyChatGptSessionError(timeout);
assert.equal(result.status, 400);
assert.equal(result.code, "browser_ui_timeout");
});
test("a TimeoutError naming a login selector is a UI timeout, not an expired session", () => {
const timeout = new Error(
'page.waitForSelector: Timeout 15000ms exceeded ... selector "button:has-text(\\"Log in\\")"'
);
timeout.name = "TimeoutError";
const result = classifyChatGptSessionError(timeout);
assert.equal(result.status, 400);
assert.equal(result.code, "browser_ui_timeout");
});
test("input errors map to their own 400 codes", () => {
const result = classifyChatGptSessionError(
new ChatGptSessionInputError("vision_unsupported", "no images")
);
assert.equal(result.status, 400);
assert.equal(result.code, "vision_unsupported");
});
test("an explicit upstream status is used when no message pattern matches", () => {
const result = classifyChatGptSessionError({ message: "anything", status: 502, code: "x" });
assert.equal(result.status, 502);
});
// CONTRACT CHANGE (deliberate inversion of the previous expectation). This test used to assert
// that a recognised message beat an explicit upstream status; the precedence is now the other way
// round. The vendor documents `AdapterEvent.error.status` as "Authoritative upstream/proxy status
// when known; avoids message-based classification", and honouring that is what stops a 503 whose
// prose happens to mention signing in from being reported as an expired session — which would
// mark a HEALTHY account's credentials dead and pull it out of rotation. Message matching still
// runs, just as the fallback for failures that carry no status at all (everything this executor
// throws itself).
test("an explicit upstream status wins over a matching message", () => {
const result = classifyChatGptSessionError({
message: "ChatGPT reported a usage limit",
status: 500,
});
assert.equal(result.status, 500);
assert.equal(result.code, "turn_failed");
});
test("a session-expired message cannot downgrade an explicit 503 to a 401", () => {
const result = classifyChatGptSessionError({
message: "Please sign in to continue",
status: 503,
});
assert.equal(result.status, 503);
assert.notEqual(result.code, "session_expired");
});
test("an explicit 503 carries the connection cooldown hint", () => {
const result = classifyChatGptSessionError({ message: "upstream unavailable", status: 503 });
assert.equal(result.status, 503);
assert.equal(result.fallbackHint, "connection_cooldown");
});
test("an explicit 429 carries the connection cooldown hint", () => {
const result = classifyChatGptSessionError({ message: "slow down", status: 429 });
assert.equal(result.status, 429);
assert.equal(result.fallbackHint, "connection_cooldown");
});
test("an explicit 400 carries no cooldown hint", () => {
const result = classifyChatGptSessionError({ message: "bad request", status: 400 });
assert.equal(result.status, 400);
assert.equal(result.fallbackHint, undefined);
});
test("an explicit status keeps the event's own code when it carries one", () => {
const result = classifyChatGptSessionError({
message: "anything",
status: 503,
code: "upstream_unavailable",
});
assert.equal(result.code, "upstream_unavailable");
});
test("a TimeoutError still outranks an explicit upstream status", () => {
const timeout = new Error("locator.waitForSelector: Timeout 30000ms exceeded") as Error & {
status?: number;
};
timeout.name = "TimeoutError";
timeout.status = 503;
const result = classifyChatGptSessionError(timeout);
assert.equal(result.status, 400);
assert.equal(result.code, "browser_ui_timeout");
});
test("message patterns still classify failures that carry no status", () => {
const result = classifyChatGptSessionError(new Error("ChatGPT reported a usage limit"));
assert.equal(result.status, 429);
assert.equal(result.code, "rate_limited");
});
test("an unrecognised failure is a retryable 502", () => {
const result = classifyChatGptSessionError(new Error("something odd happened"));
assert.equal(result.status, 502);
assert.equal(result.code, "turn_failed");
});

View File

@@ -0,0 +1,292 @@
import { test } from "node:test";
import assert from "node:assert/strict";
import { ChatGptSessionExecutor } from "../../open-sse/executors/chatgpt-session.ts";
import { __setChatGptSessionRuntimeForTesting } from "../../open-sse/executors/chatgpt-session/runtime.ts";
import type { AdapterEvent } from "../../open-sse/vendor/codex-chatgpt-web/types.ts";
const CREDENTIALS = {
connectionId: "conn-1",
apiKey: JSON.stringify({ version: 2, storageState: { cookies: [], origins: [] } }),
providerSpecificData: { solAvailable: true, proAvailable: false, browserVerified: true },
};
function stubRuntime(events: AdapterEvent[], overrides: Record<string, unknown> = {}) {
__setChatGptSessionRuntimeForTesting({
detectChrome: () => "/usr/bin/chromium",
ensureStorageState: () => "/tmp/state.json",
readStorageState: () => ({ cookies: [], origins: [] }),
loginStateExists: () => true,
inspectLogin: async () => ({ solAvailable: true, proAvailable: false }),
runTurn: async (_parsed, _incoming, emit) => {
for (const event of events) emit(event);
},
...overrides,
});
}
function body(extra: Record<string, unknown> = {}) {
return { messages: [{ role: "user", content: "Hi" }], ...extra };
}
test.afterEach(() => {
__setChatGptSessionRuntimeForTesting(null);
});
test("returns a streaming completion for a normal turn", async () => {
stubRuntime([{ type: "text_delta", text: "Hello" }, { type: "done" }]);
const result = await new ChatGptSessionExecutor().execute({
model: "high",
body: body(),
stream: true,
credentials: CREDENTIALS,
});
const response = "response" in result ? result.response : result;
assert.equal(response.status, 200);
assert.equal(response.headers.get("Content-Type"), "text/event-stream; charset=utf-8");
const text = await response.text();
assert.match(text, /"content":"Hello"/);
});
test("returns a buffered completion when stream is false", async () => {
stubRuntime([{ type: "text_delta", text: "Hello" }, { type: "done" }]);
const result = await new ChatGptSessionExecutor().execute({
model: "high",
body: body(),
stream: false,
credentials: CREDENTIALS,
});
const response = "response" in result ? result.response : result;
const json = (await response.json()) as Record<string, unknown>;
assert.equal(json.object, "chat.completion");
assert.equal(
((json.choices as Array<Record<string, unknown>>)[0].message as Record<string, unknown>)
.content,
"Hello"
);
});
test("answers 503 with a cooldown hint when no browser is installed", async () => {
stubRuntime([], { detectChrome: () => undefined });
const result = await new ChatGptSessionExecutor().execute({
model: "high",
body: body(),
stream: true,
credentials: CREDENTIALS,
});
const response = "response" in result ? result.response : result;
assert.equal(response.status, 503);
assert.equal(response.headers.get("X-Omni-Fallback-Hint"), "connection_cooldown");
});
test("answers 401 when the connection has no credentials", async () => {
stubRuntime([]);
const result = await new ChatGptSessionExecutor().execute({
model: "high",
body: body(),
stream: true,
credentials: { connectionId: "conn-1" },
});
const response = "response" in result ? result.response : result;
assert.equal(response.status, 401);
});
test("rejects a Pro route on a non-Pro account without touching the browser", async () => {
let ran = false;
stubRuntime([], {
runTurn: async () => {
ran = true;
},
});
const result = await new ChatGptSessionExecutor().execute({
model: "pro",
body: body(),
stream: true,
credentials: CREDENTIALS,
});
const response = "response" in result ? result.response : result;
assert.equal(response.status, 400);
assert.equal(ran, false);
});
test("an expired session before output becomes a 401, not a 200 stream", async () => {
stubRuntime([{ type: "error", message: "ChatGPT page is not authenticated" }]);
const result = await new ChatGptSessionExecutor().execute({
model: "high",
body: body(),
stream: true,
credentials: CREDENTIALS,
});
const response = "response" in result ? result.response : result;
assert.equal(response.status, 401);
const json = (await response.json()) as { error: { message: string } };
assert.doesNotMatch(json.error.message, /at \//);
});
test("persists rotated storage state through onCredentialsRefreshed", async () => {
stubRuntime([{ type: "text_delta", text: "x" }, { type: "done" }], {
readStorageState: () => ({ cookies: [{ name: "rotated" }], origins: [] }),
});
const patches: Array<Record<string, unknown>> = [];
const result = await new ChatGptSessionExecutor().execute({
model: "high",
body: body(),
stream: false,
credentials: CREDENTIALS,
onCredentialsRefreshed: async (patch) => {
patches.push(patch);
},
});
const response = "response" in result ? result.response : result;
await response.text();
const persisted = patches.find((patch) => typeof patch.apiKey === "string");
assert.ok(persisted, "expected an apiKey patch");
assert.match(String(persisted.apiKey), /rotated/);
});
test("emulated tool calls go through the shared web-tools contract", async () => {
stubRuntime([
{ type: "text_delta", text: '<tool>{"name": "get_time", "arguments": {}}</tool>' },
{ type: "done" },
]);
const result = await new ChatGptSessionExecutor().execute({
model: "high",
body: body({
tools: [
{
type: "function",
function: { name: "get_time", description: "time", parameters: { type: "object" } },
},
],
}),
stream: false,
credentials: CREDENTIALS,
});
const response = "response" in result ? result.response : result;
const json = (await response.json()) as Record<string, unknown>;
const choice = (json.choices as Array<Record<string, unknown>>)[0];
assert.equal(choice.finish_reason, "tool_calls");
});
test("forwards the abort signal to the adapter", async () => {
let seenSignal: AbortSignal | undefined;
stubRuntime([{ type: "done" }], {
runTurn: async (
_parsed: unknown,
incoming: { abortSignal?: AbortSignal },
emit: (e: AdapterEvent) => void
) => {
seenSignal = incoming.abortSignal;
emit({ type: "done" });
},
});
const controller = new AbortController();
const result = await new ChatGptSessionExecutor().execute({
model: "high",
body: body(),
stream: false,
credentials: CREDENTIALS,
signal: controller.signal,
});
const response = "response" in result ? result.response : result;
await response.text();
assert.equal(seenSignal, controller.signal);
});
test("keeps the bridge's own status when a first-event error is not message-classifiable", async () => {
// A Playwright TimeoutError is classified by `name`/`status`, not by its message. Re-deriving
// the class from the message alone would fall through to 502 and trip the provider breaker.
stubRuntime([
{
type: "error",
message: "locator.click: Target closed",
status: 400,
code: "browser_ui_timeout",
} as AdapterEvent,
]);
const result = await new ChatGptSessionExecutor().execute({
model: "high",
body: body(),
stream: true,
credentials: CREDENTIALS,
});
const response = "response" in result ? result.response : result;
assert.equal(response.status, 400);
const json = (await response.json()) as { error: { code: string } };
assert.equal(json.error.code, "browser_ui_timeout");
});
test(
"closes the event queue even when persisting state and its logger both throw",
{ timeout: 5000 },
async () => {
stubRuntime([{ type: "text_delta", text: "Hello" }, { type: "done" }], {
readStorageState: () => {
throw new Error("storage state is unreadable");
},
});
const result = await new ChatGptSessionExecutor().execute({
model: "high",
body: body(),
stream: false,
credentials: CREDENTIALS,
log: {
warn: () => {
throw new Error("logger exploded");
},
},
});
const response = "response" in result ? result.response : result;
assert.ok(response instanceof Response);
assert.equal(typeof response.status, "number");
}
);
test("replays emulated tool calls as an SSE stream when the client asked to stream", async () => {
stubRuntime([
{ type: "text_delta", text: '<tool>{"name": "get_time", "arguments": {}}</tool>' },
{ type: "done" },
]);
const result = await new ChatGptSessionExecutor().execute({
model: "high",
body: body({
tools: [
{
type: "function",
function: { name: "get_time", description: "time", parameters: { type: "object" } },
},
],
}),
stream: true,
credentials: CREDENTIALS,
});
const response = "response" in result ? result.response : result;
assert.equal(response.status, 200);
assert.match(response.headers.get("Content-Type") ?? "", /text\/event-stream/);
const text = await response.text();
assert.match(text, /"tool_calls"/);
assert.match(text, /get_time/);
assert.match(text, /"finish_reason":"tool_calls"/);
});
test("skips the tool-mode replay when the buffered turn did not return 200", async () => {
stubRuntime([{ type: "error", message: "ChatGPT page is not authenticated" }]);
const result = await new ChatGptSessionExecutor().execute({
model: "high",
body: body({
tools: [
{
type: "function",
function: { name: "get_time", description: "time", parameters: { type: "object" } },
},
],
}),
stream: false,
credentials: CREDENTIALS,
});
const response = "response" in result ? result.response : result;
assert.equal(response.status, 401);
const json = (await response.json()) as Record<string, unknown>;
assert.ok(json.error, "expected the error body to survive the tool-mode path");
assert.equal(json.choices, undefined);
});

View File

@@ -0,0 +1,312 @@
import { test } from "node:test";
import assert from "node:assert/strict";
import {
ChatGptSessionInputError,
buildParsedRequest,
} from "../../open-sse/executors/chatgpt-session/messages.ts";
import { requireChatGptSessionRoute } from "../../open-sse/executors/chatgpt-session/models.ts";
const route = requireChatGptSessionRoute("high");
test("system messages become the system prompt, not conversation turns", () => {
const parsed = buildParsedRequest({
route,
messages: [
{ role: "system", content: "Be terse." },
{ role: "user", content: "Hi" },
],
stream: true,
});
assert.deepEqual(parsed.context.systemPrompt, ["Be terse."]);
assert.equal(parsed.context.messages.length, 1);
assert.equal(parsed.context.messages[0].role, "user");
});
test("developer messages join the system prompt in order", () => {
const parsed = buildParsedRequest({
route,
messages: [
{ role: "system", content: "A" },
{ role: "developer", content: "B" },
{ role: "user", content: "Hi" },
],
stream: false,
});
assert.deepEqual(parsed.context.systemPrompt, ["A", "B"]);
});
test("pins the backend model and the route effort", () => {
const parsed = buildParsedRequest({
route: requireChatGptSessionRoute("luna"),
messages: [{ role: "user", content: "Hi" }],
stream: true,
});
assert.equal(parsed.modelId, "gpt-5.6-luna");
assert.equal(parsed.options.reasoning, "low");
assert.equal(parsed.stream, true);
});
test("preserves multi-turn history with assistant text parts", () => {
const parsed = buildParsedRequest({
route,
messages: [
{ role: "user", content: "one" },
{ role: "assistant", content: "two" },
{ role: "user", content: "three" },
],
stream: true,
});
assert.equal(parsed.context.messages.length, 3);
assert.deepEqual(parsed.context.messages[1], {
role: "assistant",
content: [{ type: "text", text: "two" }],
timestamp: parsed.context.messages[1].timestamp,
});
});
test("flattens OpenAI text content parts into one string", () => {
const parsed = buildParsedRequest({
route,
messages: [
{
role: "user",
content: [
{ type: "text", text: "a" },
{ type: "text", text: "b" },
],
},
],
stream: true,
});
assert.equal(parsed.context.messages[0].content, "a\nb");
});
test("rejects image parts in phase 1", () => {
assert.throws(
() =>
buildParsedRequest({
route,
messages: [
{
role: "user",
content: [{ type: "image_url", image_url: { url: "data:image/png;base64,AA" } }],
},
],
stream: true,
}),
(error: unknown) =>
error instanceof ChatGptSessionInputError && error.code === "vision_unsupported"
);
});
test("rejects a request with no user turn", () => {
assert.throws(
() => buildParsedRequest({ route, messages: [{ role: "system", content: "x" }], stream: true }),
(error: unknown) =>
error instanceof ChatGptSessionInputError && error.code === "no_user_message"
);
});
test("never carries tools into the parsed context", () => {
const parsed = buildParsedRequest({
route,
messages: [{ role: "user", content: "Hi" }],
stream: true,
});
assert.equal(parsed.context.tools, undefined);
});
test("rejects any content part that is neither text nor an image", () => {
for (const part of [
{ type: "file", file: { file_id: "f-1" } },
{ type: "input_audio", input_audio: { data: "AA", format: "wav" } },
]) {
assert.throws(
() =>
buildParsedRequest({
route,
messages: [{ role: "user", content: [part] }],
stream: false,
}),
(error: unknown) =>
error instanceof ChatGptSessionInputError && error.code === "unsupported_content_part"
);
}
});
test("an assistant refusal part is folded into the mapped content as text", () => {
const parsed = buildParsedRequest({
route,
messages: [
{ role: "user", content: "one" },
{
role: "assistant",
content: [
{ type: "refusal", refusal: "I can't help with that." },
{ type: "text", text: "Here is something else." },
],
},
{ role: "user", content: "three" },
],
stream: true,
});
assert.equal(parsed.context.messages.length, 3);
assert.deepEqual(parsed.context.messages[1], {
role: "assistant",
content: [{ type: "text", text: "I can't help with that.\nHere is something else." }],
timestamp: parsed.context.messages[1].timestamp,
});
});
test("a refusal part whose refusal field is not a string is still rejected", () => {
for (const part of [
{ type: "refusal" },
{ type: "refusal", refusal: null },
{ type: "refusal", refusal: { text: "nope" } },
{ type: "refusal", text: "wrong field" },
]) {
assert.throws(
() =>
buildParsedRequest({
route,
messages: [
{ role: "user", content: "one" },
{ role: "assistant", content: [part] },
],
stream: false,
}),
(error: unknown) =>
error instanceof ChatGptSessionInputError && error.code === "unsupported_content_part"
);
}
});
/**
* The synthesized `_rawBody` envelope. Without it the vendored adapter refuses every turn
* ("ChatGPT web requires native Codex turn_id metadata for browser-session replay") before any
* browser work, so these assertions guard a live-fatal defect that adapter mocking cannot see.
*/
function rawBody(parsed: { _rawBody?: unknown }): Record<string, unknown> {
const body = parsed._rawBody;
assert.ok(body && typeof body === "object" && !Array.isArray(body), "_rawBody must be an object");
return body as Record<string, unknown>;
}
function turnMetadata(parsed: { _rawBody?: unknown }): Record<string, unknown> {
const clientMetadata = rawBody(parsed).client_metadata;
assert.ok(
clientMetadata && typeof clientMetadata === "object" && !Array.isArray(clientMetadata),
"client_metadata must be an object"
);
const raw = (clientMetadata as Record<string, unknown>)["x-codex-turn-metadata"];
assert.equal(
typeof raw,
"string",
"x-codex-turn-metadata must be the JSON string the client sends"
);
const decoded: unknown = JSON.parse(raw as string);
assert.ok(decoded && typeof decoded === "object" && !Array.isArray(decoded));
return decoded as Record<string, unknown>;
}
function inputItems(parsed: { _rawBody?: unknown }): Array<Record<string, unknown>> {
const input = rawBody(parsed).input;
assert.ok(Array.isArray(input), "_rawBody.input must be an array");
return input.map((item) => {
assert.ok(item && typeof item === "object" && !Array.isArray(item));
return item as Record<string, unknown>;
});
}
function passthroughTurnId(item: Record<string, unknown>): unknown {
const passthrough = item.internal_chat_message_metadata_passthrough;
if (passthrough === undefined) return undefined;
assert.ok(passthrough && typeof passthrough === "object" && !Array.isArray(passthrough));
return (passthrough as Record<string, unknown>).turn_id;
}
test("the turn metadata is a JSON string carrying a thread id and a turn id", () => {
const parsed = buildParsedRequest({
route,
messages: [{ role: "user", content: "Hi" }],
stream: true,
});
const metadata = turnMetadata(parsed);
assert.equal(typeof metadata.thread_id, "string");
assert.equal(typeof metadata.turn_id, "string");
assert.match(String(metadata.thread_id), /^thread_omniroute_/);
assert.match(String(metadata.turn_id), /^turn_omniroute_/);
});
test("_rawBody.input mirrors the parsed messages, in order and in Responses item shape", () => {
const parsed = buildParsedRequest({
route,
messages: [
{ role: "system", content: "Be terse." },
{ role: "user", content: "one" },
{ role: "assistant", content: "two" },
{ role: "user", content: "three" },
],
stream: true,
});
const items = inputItems(parsed);
assert.equal(items.length, parsed.context.messages.length);
assert.deepEqual(
items.map((item) => item.role),
["user", "assistant", "user"]
);
assert.deepEqual(
items.map((item) => item.type),
["message", "message", "message"]
);
assert.deepEqual(items[0].content, [{ type: "input_text", text: "one" }]);
assert.deepEqual(items[1].content, [{ type: "output_text", text: "two" }]);
assert.deepEqual(items[2].content, [{ type: "input_text", text: "three" }]);
// System prompts stay out of `input`; the vendored parser reads them from `instructions`.
assert.equal(rawBody(parsed).instructions, "Be terse.");
assert.equal(rawBody(parsed).model, parsed.modelId);
});
test("only the last user item carries the current-turn passthrough id", () => {
const parsed = buildParsedRequest({
route,
messages: [
{ role: "user", content: "one" },
{ role: "assistant", content: "two" },
{ role: "user", content: "three" },
],
stream: true,
});
const items = inputItems(parsed);
const turnId = turnMetadata(parsed).turn_id;
assert.equal(passthroughTurnId(items[0]), undefined);
assert.equal(passthroughTurnId(items[1]), undefined);
assert.equal(passthroughTurnId(items[2]), turnId);
});
test("a trailing assistant turn leaves the marker on the last USER item", () => {
const parsed = buildParsedRequest({
route,
messages: [
{ role: "user", content: "one" },
{ role: "assistant", content: "two" },
],
stream: true,
});
const items = inputItems(parsed);
const turnId = turnMetadata(parsed).turn_id;
assert.equal(passthroughTurnId(items[0]), turnId);
assert.equal(passthroughTurnId(items[1]), undefined);
});
test("every request gets its own thread id and turn id", () => {
const first = turnMetadata(
buildParsedRequest({ route, messages: [{ role: "user", content: "Hi" }], stream: true })
);
const second = turnMetadata(
buildParsedRequest({ route, messages: [{ role: "user", content: "Hi" }], stream: true })
);
assert.notEqual(first.thread_id, second.thread_id);
assert.notEqual(first.turn_id, second.turn_id);
});

View File

@@ -0,0 +1,48 @@
import { test } from "node:test";
import assert from "node:assert/strict";
import {
CHATGPT_SESSION_ROUTE_IDS,
requireChatGptSessionRoute,
} from "../../open-sse/executors/chatgpt-session/models.ts";
test("exposes the seven public routes", () => {
assert.deepEqual(
[...CHATGPT_SESSION_ROUTE_IDS],
["luna", "think", "instant", "medium", "high", "extra-high", "pro"]
);
});
test("maps a slug to its backend model and effort", () => {
assert.deepEqual(requireChatGptSessionRoute("high"), {
id: "high",
backendModel: "gpt-5.6-sol",
effort: "high",
pro: false,
sol: true,
});
});
test("strips the provider prefix from a qualified model id", () => {
assert.equal(requireChatGptSessionRoute("chatgpt-session/pro").id, "pro");
assert.equal(requireChatGptSessionRoute("cgpt-session/pro").id, "pro");
});
test("luna routes target the luna backend and never require sol", () => {
const luna = requireChatGptSessionRoute("luna");
assert.equal(luna.backendModel, "gpt-5.6-luna");
assert.equal(luna.sol, false);
assert.equal(requireChatGptSessionRoute("think").effort, "medium");
});
test("pro routes are flagged pro", () => {
assert.equal(requireChatGptSessionRoute("pro").pro, true);
assert.equal(requireChatGptSessionRoute("extra-high").pro, true);
});
test("rejects an unknown slug", () => {
assert.throws(
() => requireChatGptSessionRoute("gpt-4"),
/Unsupported ChatGPT Session model: gpt-4/
);
});

View File

@@ -0,0 +1,153 @@
/**
* C2 regression guard — creating a `chatgpt-session` connection from the dashboard.
*
* `POST /api/providers` routes every provider accepted by
* `usesChatGptBrowserSessionCredentials()` into `finalizeValidatedChatGptWebCodexSecrets`,
* whose first statement is `JSON.parse`. The dashboard modals used to build the
* `{version, cookie}` envelope only for `chatgpt-web-codex` and post the pasted Cookie header
* verbatim for anything else, so every `chatgpt-session` save died with
* `Unexpected token '_', "__Secure-n"... is not valid JSON`. These tests pin both halves of the
* contract: the envelope the fixed modals send is accepted and stored as the VERIFIED storage
* state (never the raw cookie), and a raw header is rejected — which is exactly why the client
* must key its encoding off the same shared predicate the route uses.
*
* The browser probe is not run: the verification artifacts the real Playwright inspector leaves
* behind are seeded on disk, and a CDP endpoint is configured so nothing in this file can ever
* reach `chromium.launch()`.
*/
import test from "node:test";
import assert from "node:assert/strict";
import { randomBytes } from "node:crypto";
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { makeManagementSessionRequest } from "../helpers/managementSession.ts";
const TEST_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-chatgpt-session-route-"));
process.env.DATA_DIR = TEST_DATA_DIR;
// Hard "never launch Chrome" guarantee for the fire-and-forget auto-test the POST kicks off:
// with a CDP endpoint set, the vendored login inspector connects over CDP (to a dead loopback
// port here) instead of spawning a browser.
process.env.CHATGPT_WEB_CODEX_CDP_URL = "http://127.0.0.1:1";
const core = await import("../../src/lib/db/core.ts");
const providersDb = await import("../../src/lib/db/providers.ts");
const storageState = await import("../../open-sse/executors/chatgpt-web-codex/storageState.ts");
const browserLogin = await import("../../open-sse/vendor/codex-chatgpt-web/browser-login.ts");
const providersRoute = await import("../../src/app/api/providers/route.ts");
const RAW_COOKIE = "__Secure-next-auth.session-token=session-value-abc; _cfuvid=cf-value";
test.after(() => {
core.resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
});
/**
* Reproduce, without Playwright, exactly what a successful browser validation leaves on disk:
* the cookie-derived storage state plus the verification marker the inspector writes on success.
*/
function seedVerifiedValidation(cookie: string): string {
const validationId = `validation-${randomBytes(12).toString("hex")}`;
const paths = storageState.connectionRuntimePaths(validationId);
storageState.ensureConnectionStorageState(validationId, cookie);
browserLogin.writeVerificationMarker(paths.storageStatePath, {
solAvailable: false,
proAvailable: false,
});
return validationId;
}
async function createConnection(apiKey: string, validationId: string): Promise<Response> {
return providersRoute.POST(
await makeManagementSessionRequest("http://localhost/api/providers", {
method: "POST",
headers: { "x-skip-model-sync": "true" },
body: {
provider: "chatgpt-session",
name: `ChatGPT session ${randomBytes(4).toString("hex")}`,
apiKey,
providerSpecificData: { validationId },
},
})
);
}
test("POST creates a chatgpt-session connection from a pasted cookie header", async () => {
const validationId = seedVerifiedValidation(RAW_COOKIE);
// What the fixed modals send for ANY provider `usesChatGptBrowserSessionCredentials()`
// accepts — the raw pasted header wrapped in the shared credential envelope, with no
// `runtimeKey` (this provider never has one).
const envelope = JSON.stringify({ version: 1, cookie: RAW_COOKIE });
const response = await createConnection(envelope, validationId);
const body = (await response.json()) as { connection?: { id: string }; error?: string };
assert.equal(response.status, 201, `expected 201, got ${response.status}: ${body.error ?? ""}`);
assert.ok(body.connection?.id);
const stored = (await providersDb.getProviderConnectionById(body.connection.id)) as Record<
string,
unknown
> | null;
assert.ok(stored);
const persisted = JSON.parse(String(stored.apiKey)) as Record<string, unknown>;
// The raw cookie is discarded in favour of the verified Playwright storage state.
assert.equal(persisted.version, 2);
assert.equal("cookie" in persisted, false);
assert.equal("runtimeKey" in persisted, false);
const state = persisted.storageState as Record<string, unknown>;
assert.ok(Array.isArray(state.cookies));
assert.equal(String(stored.apiKey).includes("session-value-abc"), true);
assert.equal(
String(stored.apiKey).includes(RAW_COOKIE),
false,
"the pasted Cookie header itself must never be persisted"
);
// The one-shot validation scratch directory is consumed by the finalize step.
assert.equal(
fs.existsSync(storageState.connectionRuntimePaths(validationId).storageStatePath),
false
);
});
test("POST rejects a raw cookie header and never echoes a stack or a path", async () => {
const validationId = seedVerifiedValidation(RAW_COOKIE);
const response = await createConnection(RAW_COOKIE, validationId);
const body = (await response.json()) as { error?: string };
assert.equal(response.status, 400);
assert.ok(body.error);
// Routed through sanitizeErrorMessage: no stack tail, no absolute source path.
assert.doesNotMatch(String(body.error), /\n\s+at /);
assert.doesNotMatch(String(body.error), /at \//);
// The German fallback is gone — this provider is labelled in English.
assert.doesNotMatch(String(body.error), /Browserprüfung/);
});
test("both dashboard modals derive the credential envelope from the shared predicate", () => {
// The client/server drift that caused C2 is only detectable at the source level: the modals
// are React components with no unit-testable seam. Pin that neither of them gates the
// ENVELOPE on the codex-only id any more.
const modals = [
"src/app/(dashboard)/dashboard/providers/[id]/components/modals/AddApiKeyModal.tsx",
"src/app/(dashboard)/dashboard/providers/[id]/components/modals/EditConnectionModal.tsx",
];
for (const modal of modals) {
const source = fs.readFileSync(modal, "utf8");
assert.match(
source,
/usesChatGptBrowserSessionCredentials\(provider\)/,
`${modal} must derive the envelope from the shared browser-session predicate`
);
assert.doesNotMatch(
source,
/=\s*isChatGptWebCodex\s*\n?\s*\?\s*JSON\.stringify\(\{/,
`${modal} must not gate the credential envelope on the codex-only provider id`
);
}
});

View File

@@ -0,0 +1,35 @@
import { test } from "node:test";
import assert from "node:assert/strict";
import { REGISTRY } from "../../open-sse/config/providerRegistry.ts";
import { CHATGPT_SESSION_ROUTE_IDS } from "../../open-sse/executors/chatgpt-session/models.ts";
import {
RETIRED_COMMON_CHATGPT_WEB_PROVIDER_IDS,
isCommonChatGptWebRetiredProviderId,
} from "../../src/shared/constants/chatgptWebRetirement.ts";
test("the registry exposes chatgpt-session with its seven routes", () => {
const entry = REGISTRY["chatgpt-session"];
assert.ok(entry, "chatgpt-session must be registered");
assert.equal(entry.alias, "cgpt-session");
assert.equal(entry.format, "openai");
assert.equal(entry.executor, "chatgpt-session");
assert.deepEqual(
entry.models.map((model) => model.id),
[...CHATGPT_SESSION_ROUTE_IDS]
);
});
test("the new ids are not the retired ones", () => {
assert.equal(isCommonChatGptWebRetiredProviderId("chatgpt-session"), false);
assert.equal(isCommonChatGptWebRetiredProviderId("cgpt-session"), false);
assert.deepEqual([...RETIRED_COMMON_CHATGPT_WEB_PROVIDER_IDS], ["chatgpt-web", "cgpt-web"]);
});
test("both executor aliases resolve to the session executor", async () => {
const { getExecutor } = await import("../../open-sse/executors/index.ts");
for (const id of ["chatgpt-session", "cgpt-session"]) {
const executor = await getExecutor(id);
assert.equal(executor.getProvider(), "chatgpt-session");
}
});

View File

@@ -0,0 +1,57 @@
import { test } from "node:test";
import assert from "node:assert/strict";
import { WEB_COOKIE_PROVIDERS } from "../../src/shared/constants/providers/web-cookie.ts";
import { WEB_SESSION_CREDENTIAL_REQUIREMENTS } from "../../src/shared/providers/webSessionCredentials.ts";
import { usesChatGptBrowserSessionCredentials } from "../../src/shared/constants/chatgptWebCodex.ts";
test("the dashboard card describes the session provider", () => {
const card = (WEB_COOKIE_PROVIDERS as Record<string, Record<string, unknown>>)["chatgpt-session"];
assert.ok(card, "chatgpt-session must have a web-cookie card");
assert.equal(card.alias, "cgpt-session");
assert.equal(card.website, "https://chatgpt.com");
assert.equal(card.subscriptionRisk, true);
assert.equal(card.riskNoticeVariant, "webCookie");
assert.equal(card.toolCalling, "emulated");
});
test("the credential requirement accepts a full cookie header", () => {
const requirement = (
WEB_SESSION_CREDENTIAL_REQUIREMENTS as Record<string, Record<string, unknown>>
)["chatgpt-session"];
assert.ok(requirement);
assert.equal(requirement.kind, "cookie");
assert.equal(requirement.acceptsFullCookieHeader, true);
});
test("validateProviderApiKey dispatch resolves chatgpt-session to its own validator", async () => {
const { validateProviderApiKey } = await import("../../src/lib/providers/validation.ts");
// "cookie:" decodes to an empty cookie (decodeChatGptWebCodexSecrets strips the
// "cookie:" prefix), so validateChatGptSessionProvider returns its own
// cookie-required rejection immediately — before any Chrome/CDP detection or
// browser launch. A non-empty credential is required here: an empty apiKey is
// intercepted by validateProviderApiKey's own "Provider and API key required"
// gate before dispatch ever reaches the SPECIALTY_VALIDATORS map, which would
// prove nothing about chatgpt-session's own registration.
const result = await validateProviderApiKey({
provider: "chatgpt-session",
apiKey: "cookie:",
});
assert.equal(result.valid, false);
assert.equal(result.error, "A ChatGPT cookie header or a stored browser session is required.");
});
test("validation rejects an empty credential without launching a browser", async () => {
const { validateChatGptSessionProvider } =
await import("../../src/lib/providers/validation/chatgptSession.ts");
const result = await validateChatGptSessionProvider({ apiKey: "" });
assert.equal(result.valid, false);
assert.match(String(result.error), /cookie|credential/i);
});
test("usesChatGptBrowserSessionCredentials recognizes both browser-session providers", () => {
assert.equal(usesChatGptBrowserSessionCredentials("chatgpt-web-codex"), true);
assert.equal(usesChatGptBrowserSessionCredentials("chatgpt-session"), true);
assert.equal(usesChatGptBrowserSessionCredentials("openai"), false);
assert.equal(usesChatGptBrowserSessionCredentials(undefined), false);
});

View File

@@ -171,7 +171,12 @@ test("shared set size includes live REGISTRY and retired Designer + Felo + Qwen
// 1 and adds 2 distinct tombstones "qwen-web"/"qw", a net +1) on top of the
// live REGISTRY walk, minus the 3 GPL-derived Raycast/Hailuo Web
// ids/aliases removed from REGISTRY by #11691's migration 166.
assert.equal(RESERVED_PREFIX_COUNT, 400);
// 2026-09-02: 400 → 402 with the ChatGPT Session provider
// (feat/chatgpt-session-provider) — its REGISTRY entry contributes 2
// distinct new members: id "chatgpt-session" and alias "cgpt-session".
assert.equal(RESERVED_PREFIX_COUNT, 402);
assert.equal(RESERVED_PROVIDER_PREFIXES.has("chatgpt-session"), true);
assert.equal(RESERVED_PROVIDER_PREFIXES.has("cgpt-session"), true);
});
test("isReservedProviderPrefix rejects non-string input", () => {