diff --git a/AGENTS.md b/AGENTS.md
index 08adf6d8d3..30c2f8b299 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -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 |
| ------------- | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
diff --git a/README.md b/README.md
index 5431c2459f..5d35b64c08 100644
--- a/README.md
+++ b/README.md
@@ -7,7 +7,7 @@
# π OmniRoute β The Free AI Gateway
-
+
@@ -210,7 +210,7 @@ curl http://localhost:20128/v1/chat/completions \
-
+
@@ -463,7 +463,7 @@ All **19** strategies β mix & match per combo step:
-
+
π Full methodology & per-feature detail vs 9router, OpenRouter, CLIProxyAPI & LiteLLM β [`docs/comparison/OMNIROUTE_VS_ALTERNATIVES.md`](docs/comparison/OMNIROUTE_VS_ALTERNATIVES.md)
diff --git a/changelog.d/features/maxai-provider.md b/changelog.d/features/maxai-provider.md
new file mode 100644
index 0000000000..4e74854b27
--- /dev/null
+++ b/changelog.d/features/maxai-provider.md
@@ -0,0 +1,6 @@
+- **feat(providers):** add MaxAI as a signed, OpenAI-compatible provider serving its 13 paid chat models (GPT-5.6 / Luna / Thinking, Claude 5 Sonnet, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite, Grok 4.1-fast / 4.5, DeepSeek V3.2 / R1, Llama 3.3 70B) through OmniRoute's `/v1` endpoint, with per-request HMAC-SHA1βSM3βAES request signing, live model + context-window discovery from `/models/get_config`, and prompted tool-calling translated to OpenAI `tool_calls`
+- **feat(providers):** MaxAI vision input β image_url content parts are forwarded inline in `message_content` to the 6 vision-capable models (GPT-5.6 / Luna / Thinking, Claude Haiku 4.5, Gemini 3.1 Pro / Flash-Lite)
+- **feat(providers):** MaxAI image generation β 6 image models (gpt-image-1, dall-e-3, flux-1-schnell/dev/pro, sd3-medium) exposed through `POST /v1/images/generations`
+- **feat(providers):** MaxAI document RAG β inline base64 file/document attachments are uploaded to MaxAI (content-addressed `doc_id`) and attached to the chat via `doc_list`
+- **feat(providers):** browserless MaxAI onboarding β email device-pair login (`/api/providers/[id]/login`) and signed access-token refresh, so a connection can be created and kept fresh without a real browser or Google OAuth
+- **feat(providers):** per-provider TLS impersonation profile (MaxAI presents a Windows Firefox-150 client fingerprint) so its bot-sensitive endpoints accept OmniRoute traffic
diff --git a/config/quality/eslint-suppressions.json b/config/quality/eslint-suppressions.json
index 8250ba6936..c7fd7e1b9a 100644
--- a/config/quality/eslint-suppressions.json
+++ b/config/quality/eslint-suppressions.json
@@ -3373,7 +3373,7 @@
},
"tests/unit/combo-routing-engine.test.ts": {
"@typescript-eslint/no-explicit-any": {
- "count": 267
+ "count": 268
}
},
"tests/unit/combo-same-provider-cascade.test.ts": {
diff --git a/config/quality/file-size-baseline.json b/config/quality/file-size-baseline.json
index 3a75185a68..04342aff2d 100644
--- a/config/quality/file-size-baseline.json
+++ b/config/quality/file-size-baseline.json
@@ -1,4 +1,5 @@
{
+ "_rebaseline_2026_09_02_11461_maxai_tls_profile": "PR #11461 (arminanton, feat/maxai-provider) own growth, three files at existing per-provider chokepoints: open-sse/utils/proxyFetch.ts 1241->1261 (+20, the TLS_PROVIDER_PROFILE map giving MaxAI a Windows/firefox_150 impersonation profile instead of the tlsClient chrome_124/macos default); open-sse/handlers/imageGeneration.ts 3231->3243 (+12, the maxai-image format branch); src/app/api/providers/[id]/models/route.ts 2381->2429 (+48, live model listing via maxaiModels). Additive data, same no-split rationale as _rebaseline_2026_08_20_10531_freebuff_provider.",
"_rebaseline_2026_09_02_11460_flat_rate_estimates": "PR #11460 (xiaoyaner0201, fix/11459-cc-cost-estimates) own growth: src/app/(dashboard)/dashboard/costs/CostOverviewTab.tsx 1283->1319 (+36) β the flat-rate estimate labelling and the includeFlatRateEstimates opt-in on the Costs dashboard. #11460 merged first so this ratchet re-tightening measures the real post-merge LOC; the cap still drops 2002->1319 (-683) versus the 2026-08-10 +30% loosening this PR reverses. Same own-growth rationale as _rebaseline_2026_08_20_10531_freebuff_provider.",
"_rebaseline_2026_08_31_chatgpt_web_v4_vendor": "Pinned MIT vendor refresh from codex-chatgpt-web 0.1.16 to v4.0.6 (commit 09877fa21ffdbf20979623ef501046fc02a750d7). browser-worker.ts is preserved as the reviewed upstream browser protocol implementation; splitting the vendored file would destroy source parity and make future security/liveness updates unauditable. OmniRoute-specific DATA_DIR, Docker CDP, credential-marker, and XML decoding adaptations are covered by the ChatGPT Web Codex focused suite.",
"_rebaseline_2026_08_20_10531_freebuff_provider": "PR #10531 (adrianaryaputra, feat/freebuff-provider-support, closes #6793) own growth: src/shared/constants/providers/apikey/gateways.ts 1283->1298 (+15, the freebuff APIKEY_PROVIDERS_GATEWAYS catalog entry, additive data at the existing registry chokepoint, same god-file no-split rationale as prior gateways.ts rebaselines) and src/app/(dashboard)/dashboard/providers/[id]/components/modals/AddApiKeyModal.tsx 1062->1067 (+5, freebuff credential placeholder/hint at the existing per-provider switch chokepoint). Covered by tests/unit/freebuff-provider.test.ts (9/9 passing).",
@@ -406,7 +407,7 @@
"open-sse/executors/cursor.ts": 1759,
"open-sse/executors/muse-spark-web.ts": 1405,
"open-sse/handlers/chatCore.ts": 5946,
- "open-sse/handlers/imageGeneration.ts": 3231,
+ "open-sse/handlers/imageGeneration.ts": 3243,
"open-sse/handlers/search.ts": 1789,
"open-sse/mcp-server/schemas/tools.ts": 1621,
"open-sse/mcp-server/server.ts": 1572,
@@ -415,7 +416,7 @@
"open-sse/services/combo.ts": 4023,
"open-sse/translator/response/openai-responses.ts": 1466,
"open-sse/utils/cursorAgentProtobuf.ts": 1547,
- "open-sse/utils/proxyFetch.ts": 1241,
+ "open-sse/utils/proxyFetch.ts": 1261,
"open-sse/utils/stream.ts": 3072,
"open-sse/vendor/codex-chatgpt-web/adapters/chatgpt-web/browser-worker.ts": 4398,
"open-sse/vendor/codex-chatgpt-web/bridge.ts": 1322,
@@ -432,7 +433,7 @@
"src/app/(dashboard)/dashboard/settings/components/RoutingTab.tsx": 1606,
"src/app/(dashboard)/dashboard/settings/components/SystemStorageTab.tsx": 1597,
"src/app/(dashboard)/dashboard/usage/components/EvalsTab.tsx": 2152,
- "src/app/api/providers/[id]/models/route.ts": 2381,
+ "src/app/api/providers/[id]/models/route.ts": 2429,
"src/app/api/providers/[id]/test/route.ts": 1252,
"src/app/api/v1/models/catalog.ts": 2066,
"src/app/docs/lib/openapi.generated.ts": 1347,
diff --git a/docs/diagrams/cli-terminal.svg b/docs/diagrams/cli-terminal.svg
index 41023d868d..92cb473457 100644
--- a/docs/diagrams/cli-terminal.svg
+++ b/docs/diagrams/cli-terminal.svg
@@ -1,4 +1,4 @@
-
+
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.
diff --git a/docs/diagrams/comparison-table.svg b/docs/diagrams/comparison-table.svg
index bfdc3ea240..71992cc04a 100644
--- a/docs/diagrams/comparison-table.svg
+++ b/docs/diagrams/comparison-table.svg
@@ -1,4 +1,4 @@
-
+
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.
diff --git a/docs/diagrams/promise-pillars.svg b/docs/diagrams/promise-pillars.svg
index 6e037e5098..9466b0c859 100644
--- a/docs/diagrams/promise-pillars.svg
+++ b/docs/diagrams/promise-pillars.svg
@@ -1,4 +1,4 @@
-
+
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.
@@ -21,7 +21,7 @@
- One endpoint. 352 providers. Never stop building β OmniRoute picks the cheapest one that works .
+ One endpoint. 353 providers. Never stop building β OmniRoute picks the cheapest one that works .
@@ -38,7 +38,7 @@
Never hit limits
- Auto-fallback across 352 providers in
+ Auto-fallback across 353 providers in
milliseconds. Quota out? The next provider
takes over while a healthy target remains.
diff --git a/docs/diagrams/readme-hero.svg b/docs/diagrams/readme-hero.svg
index 1c49b21b2e..6d4c7ba9bf 100644
--- a/docs/diagrams/readme-hero.svg
+++ b/docs/diagrams/readme-hero.svg
@@ -1,4 +1,4 @@
-
+
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.
@@ -28,7 +28,7 @@
Never stop coding.
- Every AI tool β 352 providers β 150+ free β through one endpoint.
+ Every AI tool β 353 providers β 150+ free β through one endpoint.
Claude Code Β· Codex Β· Cursor Β· Cline Β· Copilot Β· Antigravity β FREE Claude / GPT / Gemini Β· auto-fallback
diff --git a/docs/i18n/ar/llm.txt b/docs/i18n/ar/llm.txt
index 548eefbf3b..c1e7abe59c 100644
--- a/docs/i18n/ar/llm.txt
+++ b/docs/i18n/ar/llm.txt
@@ -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
@@ -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
diff --git a/docs/i18n/az/llm.txt b/docs/i18n/az/llm.txt
index 6bc2a099db..5ef9e5f2fe 100644
--- a/docs/i18n/az/llm.txt
+++ b/docs/i18n/az/llm.txt
@@ -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
@@ -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
diff --git a/docs/i18n/bg/llm.txt b/docs/i18n/bg/llm.txt
index 6bc2a099db..5ef9e5f2fe 100644
--- a/docs/i18n/bg/llm.txt
+++ b/docs/i18n/bg/llm.txt
@@ -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
@@ -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
diff --git a/docs/i18n/bn/llm.txt b/docs/i18n/bn/llm.txt
index 90e06508b6..ccdb9b8013 100644
--- a/docs/i18n/bn/llm.txt
+++ b/docs/i18n/bn/llm.txt
@@ -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
@@ -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
diff --git a/docs/i18n/cs/llm.txt b/docs/i18n/cs/llm.txt
index d954459b13..ac38750608 100644
--- a/docs/i18n/cs/llm.txt
+++ b/docs/i18n/cs/llm.txt
@@ -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
@@ -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
diff --git a/docs/i18n/da/llm.txt b/docs/i18n/da/llm.txt
index 9230d437d3..b4653489b6 100644
--- a/docs/i18n/da/llm.txt
+++ b/docs/i18n/da/llm.txt
@@ -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
@@ -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
diff --git a/docs/i18n/de/llm.txt b/docs/i18n/de/llm.txt
index 3c7b5b303a..88b776ad66 100644
--- a/docs/i18n/de/llm.txt
+++ b/docs/i18n/de/llm.txt
@@ -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
@@ -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
diff --git a/docs/i18n/es/llm.txt b/docs/i18n/es/llm.txt
index a16e64035c..cef74db964 100644
--- a/docs/i18n/es/llm.txt
+++ b/docs/i18n/es/llm.txt
@@ -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
@@ -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
diff --git a/docs/i18n/fa/llm.txt b/docs/i18n/fa/llm.txt
index 00d95daeae..651c65dcd0 100644
--- a/docs/i18n/fa/llm.txt
+++ b/docs/i18n/fa/llm.txt
@@ -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
@@ -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
diff --git a/docs/i18n/fi/llm.txt b/docs/i18n/fi/llm.txt
index 5c59495a6a..fa001b3f5b 100644
--- a/docs/i18n/fi/llm.txt
+++ b/docs/i18n/fi/llm.txt
@@ -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
@@ -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
diff --git a/docs/i18n/fr/llm.txt b/docs/i18n/fr/llm.txt
index a285637e33..98bbf3cffe 100644
--- a/docs/i18n/fr/llm.txt
+++ b/docs/i18n/fr/llm.txt
@@ -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
@@ -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
diff --git a/docs/i18n/gu/llm.txt b/docs/i18n/gu/llm.txt
index 34d83e829b..d6b23b4a6f 100644
--- a/docs/i18n/gu/llm.txt
+++ b/docs/i18n/gu/llm.txt
@@ -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
@@ -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
diff --git a/docs/i18n/he/llm.txt b/docs/i18n/he/llm.txt
index 416da4c84b..34fdbd6c37 100644
--- a/docs/i18n/he/llm.txt
+++ b/docs/i18n/he/llm.txt
@@ -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
@@ -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
diff --git a/docs/i18n/hi/llm.txt b/docs/i18n/hi/llm.txt
index 9393dd87eb..e9330bcbc1 100644
--- a/docs/i18n/hi/llm.txt
+++ b/docs/i18n/hi/llm.txt
@@ -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
@@ -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
diff --git a/docs/i18n/hu/llm.txt b/docs/i18n/hu/llm.txt
index 20f32976c5..c2e73a6d51 100644
--- a/docs/i18n/hu/llm.txt
+++ b/docs/i18n/hu/llm.txt
@@ -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
@@ -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
diff --git a/docs/i18n/id/llm.txt b/docs/i18n/id/llm.txt
index ea80bb4578..339089ffa0 100644
--- a/docs/i18n/id/llm.txt
+++ b/docs/i18n/id/llm.txt
@@ -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
@@ -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
diff --git a/docs/i18n/in/llm.txt b/docs/i18n/in/llm.txt
index 8033e3fa82..9d403264be 100644
--- a/docs/i18n/in/llm.txt
+++ b/docs/i18n/in/llm.txt
@@ -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
@@ -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
diff --git a/docs/i18n/it/llm.txt b/docs/i18n/it/llm.txt
index e2df9cc115..2b99808639 100644
--- a/docs/i18n/it/llm.txt
+++ b/docs/i18n/it/llm.txt
@@ -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
@@ -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
diff --git a/docs/i18n/ja/llm.txt b/docs/i18n/ja/llm.txt
index ab95c8bc0c..cd750e07fd 100644
--- a/docs/i18n/ja/llm.txt
+++ b/docs/i18n/ja/llm.txt
@@ -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
@@ -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
diff --git a/docs/i18n/ko/llm.txt b/docs/i18n/ko/llm.txt
index 9658b42bb0..fa37857775 100644
--- a/docs/i18n/ko/llm.txt
+++ b/docs/i18n/ko/llm.txt
@@ -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
@@ -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
diff --git a/docs/i18n/mr/llm.txt b/docs/i18n/mr/llm.txt
index 48a94b897c..15c7b22545 100644
--- a/docs/i18n/mr/llm.txt
+++ b/docs/i18n/mr/llm.txt
@@ -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
@@ -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
diff --git a/docs/i18n/ms/llm.txt b/docs/i18n/ms/llm.txt
index c7dc286a2f..0671482309 100644
--- a/docs/i18n/ms/llm.txt
+++ b/docs/i18n/ms/llm.txt
@@ -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
@@ -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
diff --git a/docs/i18n/nl/llm.txt b/docs/i18n/nl/llm.txt
index 8cab222517..7a2b769983 100644
--- a/docs/i18n/nl/llm.txt
+++ b/docs/i18n/nl/llm.txt
@@ -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
@@ -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
diff --git a/docs/i18n/no/llm.txt b/docs/i18n/no/llm.txt
index b209c8c81e..b4a5eb0f44 100644
--- a/docs/i18n/no/llm.txt
+++ b/docs/i18n/no/llm.txt
@@ -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
@@ -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
diff --git a/docs/i18n/phi/llm.txt b/docs/i18n/phi/llm.txt
index cda4f3ec19..6286537b20 100644
--- a/docs/i18n/phi/llm.txt
+++ b/docs/i18n/phi/llm.txt
@@ -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
@@ -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
diff --git a/docs/i18n/pl/llm.txt b/docs/i18n/pl/llm.txt
index f2c24807ba..6d4bd07b83 100644
--- a/docs/i18n/pl/llm.txt
+++ b/docs/i18n/pl/llm.txt
@@ -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
@@ -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
diff --git a/docs/i18n/pt-BR/llm.txt b/docs/i18n/pt-BR/llm.txt
index 1d0e7c7572..d64cc39343 100644
--- a/docs/i18n/pt-BR/llm.txt
+++ b/docs/i18n/pt-BR/llm.txt
@@ -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
@@ -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
diff --git a/docs/i18n/pt/llm.txt b/docs/i18n/pt/llm.txt
index ed8d0f33b5..3d6d434382 100644
--- a/docs/i18n/pt/llm.txt
+++ b/docs/i18n/pt/llm.txt
@@ -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
@@ -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
diff --git a/docs/i18n/ro/llm.txt b/docs/i18n/ro/llm.txt
index 945d07ef04..b99a4536cb 100644
--- a/docs/i18n/ro/llm.txt
+++ b/docs/i18n/ro/llm.txt
@@ -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
@@ -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
diff --git a/docs/i18n/ru/llm.txt b/docs/i18n/ru/llm.txt
index 795d39530a..6d3e7c07c9 100644
--- a/docs/i18n/ru/llm.txt
+++ b/docs/i18n/ru/llm.txt
@@ -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
@@ -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
diff --git a/docs/i18n/sk/llm.txt b/docs/i18n/sk/llm.txt
index a96f49dbc5..722056455f 100644
--- a/docs/i18n/sk/llm.txt
+++ b/docs/i18n/sk/llm.txt
@@ -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
@@ -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
diff --git a/docs/i18n/sv/llm.txt b/docs/i18n/sv/llm.txt
index 5270a3acf4..56f8047049 100644
--- a/docs/i18n/sv/llm.txt
+++ b/docs/i18n/sv/llm.txt
@@ -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
@@ -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
diff --git a/docs/i18n/sw/llm.txt b/docs/i18n/sw/llm.txt
index 166ecff735..c72f695bde 100644
--- a/docs/i18n/sw/llm.txt
+++ b/docs/i18n/sw/llm.txt
@@ -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
@@ -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
diff --git a/docs/i18n/ta/llm.txt b/docs/i18n/ta/llm.txt
index 3996aa166c..47fb44c802 100644
--- a/docs/i18n/ta/llm.txt
+++ b/docs/i18n/ta/llm.txt
@@ -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
@@ -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
diff --git a/docs/i18n/te/llm.txt b/docs/i18n/te/llm.txt
index c0a319e444..86c6e44622 100644
--- a/docs/i18n/te/llm.txt
+++ b/docs/i18n/te/llm.txt
@@ -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
@@ -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
diff --git a/docs/i18n/th/llm.txt b/docs/i18n/th/llm.txt
index 068975ff6a..3ee4254f1c 100644
--- a/docs/i18n/th/llm.txt
+++ b/docs/i18n/th/llm.txt
@@ -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
@@ -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
diff --git a/docs/i18n/tr/llm.txt b/docs/i18n/tr/llm.txt
index 0ff720ab5b..538a1d9cc3 100644
--- a/docs/i18n/tr/llm.txt
+++ b/docs/i18n/tr/llm.txt
@@ -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
@@ -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
diff --git a/docs/i18n/uk-UA/llm.txt b/docs/i18n/uk-UA/llm.txt
index 7030db01be..21b67bfc86 100644
--- a/docs/i18n/uk-UA/llm.txt
+++ b/docs/i18n/uk-UA/llm.txt
@@ -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
@@ -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
diff --git a/docs/i18n/ur/llm.txt b/docs/i18n/ur/llm.txt
index ee39faa930..0f881c7b4b 100644
--- a/docs/i18n/ur/llm.txt
+++ b/docs/i18n/ur/llm.txt
@@ -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
@@ -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
diff --git a/docs/i18n/vi/llm.txt b/docs/i18n/vi/llm.txt
index cc75542ed9..6a0ec2cdf1 100644
--- a/docs/i18n/vi/llm.txt
+++ b/docs/i18n/vi/llm.txt
@@ -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
@@ -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
diff --git a/docs/i18n/zh-CN/llm.txt b/docs/i18n/zh-CN/llm.txt
index 969e3af8b1..6953a90999 100644
--- a/docs/i18n/zh-CN/llm.txt
+++ b/docs/i18n/zh-CN/llm.txt
@@ -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
@@ -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
diff --git a/docs/i18n/zh-TW/llm.txt b/docs/i18n/zh-TW/llm.txt
index c1275ba043..e081e5c730 100644
--- a/docs/i18n/zh-TW/llm.txt
+++ b/docs/i18n/zh-TW/llm.txt
@@ -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
@@ -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
diff --git a/docs/reference/PROVIDER_REFERENCE.md b/docs/reference/PROVIDER_REFERENCE.md
index 99d2a4f39d..92b0e82373 100644
--- a/docs/reference/PROVIDER_REFERENCE.md
+++ b/docs/reference/PROVIDER_REFERENCE.md
@@ -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,7 +80,7 @@ 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 |
|----|-------|------|------|---------|-------|--------------|
@@ -102,6 +102,7 @@ Use the dashboard at `/dashboard/providers` to enable, configure, and test each
| `inner-ai` | `in-ai` | Inner.ai (Subscription) | Web cookie | [link](https://app.innerai.com) | Paste your token cookie and email separated by a space: open DevTools β Application β Cookies β .innerai.com, copy the token value, then append a space and your Inner.ai login email. Example: eyJhbG... user@example.com | emulated |
| `kimi-web` | `kimi-web` | Kimi Web | Web cookie | [link](https://www.kimi.ai) | Paste access_token from www.kimi.ai DevTools β Application β Local Storage. A legacy kimi-auth cookie is also accepted. | β |
| `lmarena` | `lma` | Arena (Free) | Web cookie | [link](https://arena.ai) | Paste the full Cookie header from arena.ai (DevTools β Network β request β Cookie). Include arena-auth-prod-v1.0/.1β¦ and cf_clearance/__cf_bm when present. OmniRoute uses Chrome TLS impersonation; if Arena still 403s, set providerSpecificData.recaptchaV3Token from a live browser session. | β |
+| `maxai` | `mx` | MaxAI | Web cookie | [link](https://www.maxai.co) | Sign in once (email code or browser) to mint a MaxAI access token. OmniRoute signs each request, routes it through residential egress, and refreshes the token browserlessly, so a connection stays valid for about a year without re-login. | emulated |
| `muse-spark-web` | `ms-web` | Muse Spark Web (Meta AI) | Web cookie | [link](https://www.meta.ai) | Paste your ecto_1_sess cookie AND the ecto1:... WS auth token from meta.ai. Capture the ecto1: token in DevTools β Network β WS β the clippy request's Authorization query param. Example: ecto_1_sess=4240a308...NVDg0; ecto1:ABCD... | emulated |
| `notion-web` | `nw` | Notion AI Web (Unofficial/Experimental) | Web cookie | [link](https://www.notion.so) | Paste only the token_v2 cookie VALUE from app.notion.com (DevTools β Application β Cookies β token_v2). Do not paste token_v2= or the full Cookie header. Workspace is auto-detected; space_id / notion_user_id are optional. | β |
| `perplexity-web` | `pplx-web` | Perplexity Web (Pro/Max) | Web cookie | [link](https://www.perplexity.ai) | Paste your __Secure-next-auth.session-token cookie value from perplexity.ai | emulated |
@@ -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
diff --git a/llm.txt b/llm.txt
index 9c60de9919..907184c88a 100644
--- a/llm.txt
+++ b/llm.txt
@@ -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
@@ -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
diff --git a/open-sse/config/imageRegistry.ts b/open-sse/config/imageRegistry.ts
index 85531b1f84..8af67947ce 100644
--- a/open-sse/config/imageRegistry.ts
+++ b/open-sse/config/imageRegistry.ts
@@ -256,6 +256,26 @@ export const IMAGE_PROVIDERS: Record = {
supportedSizes: ["1024x1024", "1024x1792", "1792x1024", "1024x1536", "1536x1024"],
},
+ maxai: {
+ id: "maxai",
+ alias: "mx",
+ baseUrl: "https://api.maxai.me/gpt/get_image_generate_response",
+ authType: "apikey",
+ authHeader: "bearer",
+ format: "maxai-image",
+ models: [
+ { id: "gpt-image-1", name: "GPT Image 1 (MaxAI)" },
+ { id: "dall-e-3", name: "DALL-E 3 (MaxAI)" },
+ { id: "flux-1-schnell", name: "FLUX.1 [schnell] (MaxAI)" },
+ { id: "flux-1-dev", name: "FLUX.1 [dev] (MaxAI)" },
+ { id: "flux-1-pro", name: "FLUX.1 [pro] (MaxAI)" },
+ { id: "sd3-medium", name: "Stable Diffusion 3 Medium (MaxAI)" },
+ ],
+ // gpt-image-1/dall-e-3 are size-snapped to 1024x1024 by the handler; flux
+ // models pass any size through.
+ supportedSizes: ["1024x1024", "1024x1536", "1536x1024", "1024x1792", "1792x1024"],
+ },
+
xai: {
id: "xai",
baseUrl: "https://api.x.ai/v1/images/generations",
diff --git a/open-sse/config/providers/index.ts b/open-sse/config/providers/index.ts
index cc8ec3a703..b3cf60b463 100644
--- a/open-sse/config/providers/index.ts
+++ b/open-sse/config/providers/index.ts
@@ -210,6 +210,7 @@ import { pollinationsProvider } from "./registry/pollinations/index.ts";
import { veoaifree_webProvider } from "./registry/veoaifree-web/index.ts";
import { codexProvider } from "./registry/codex/index.ts";
import { codexAppServerProvider } from "./registry/codex-app-server/index.ts";
+import { maxaiProvider } from "./registry/maxai/index.ts";
import { veniceProvider } from "./registry/venice/index.ts";
import { kiroProvider } from "./registry/kiro/index.ts";
import { openadapterProvider } from "./registry/openadapter/index.ts";
@@ -477,6 +478,7 @@ export const REGISTRY: Record = {
"veoaifree-web": veoaifree_webProvider,
codex: codexProvider,
"codex-app-server": codexAppServerProvider,
+ maxai: maxaiProvider,
venice: veniceProvider,
kiro: kiroProvider,
byteplus: byteplusProvider,
diff --git a/open-sse/config/providers/registry/maxai/index.ts b/open-sse/config/providers/registry/maxai/index.ts
new file mode 100644
index 0000000000..d35a023e49
--- /dev/null
+++ b/open-sse/config/providers/registry/maxai/index.ts
@@ -0,0 +1,26 @@
+import type { RegistryEntry } from "../../shared.ts";
+import { MAXAI_REGISTRY_MODELS } from "../../../../executors/maxai/catalog.ts";
+
+/**
+ * MaxAI β the MaxAI web app (chat.maxai.co / api.maxai.me) as an OpenAI-compatible
+ * provider. A signed web-app port (like zai-web): each request carries a
+ * per-request `X-Authorization` signature + a Bearer access token minted by the
+ * browser-mint flow. Runs over residential egress with a Firefox TLS fingerprint.
+ *
+ * authType `apikey`/authHeader `bearer`: the OpenAI-style access token is stored
+ * on the connection and replayed as `Authorization: Bearer`; the device id +
+ * user id ride in providerSpecificData and are folded into the signature. The
+ * token is refreshed out-of-band by the browser-mint (the `/oauth` refresh
+ * endpoint is deep-TLS-gated), so there is no central token-refresh case.
+ */
+export const maxaiProvider: RegistryEntry = {
+ id: "maxai",
+ alias: "mx",
+ format: "openai",
+ executor: "maxai",
+ baseUrl: "https://api.maxai.me",
+ authType: "apikey",
+ authHeader: "bearer",
+ defaultContextLength: 128000,
+ models: MAXAI_REGISTRY_MODELS,
+};
diff --git a/open-sse/config/providers/registry/volcengine/agent-plan/index.ts b/open-sse/config/providers/registry/volcengine/agent-plan/index.ts
index 3ecae6aa08..6604844efd 100644
--- a/open-sse/config/providers/registry/volcengine/agent-plan/index.ts
+++ b/open-sse/config/providers/registry/volcengine/agent-plan/index.ts
@@ -78,8 +78,8 @@ export const VOLCENGINE_AGENT_PLAN_MODELS: RegistryModel[] = [
name: "MiniMax M3 (Agent Plan)",
contextLength: 1048576,
toolCalling: true,
- supportsReasoning: true,
supportsVision: true,
+ supportsReasoning: true,
},
{
id: "deepseek-v4-pro-260425",
diff --git a/open-sse/config/providers/registry/volcengine/coding-plan/index.ts b/open-sse/config/providers/registry/volcengine/coding-plan/index.ts
index f4864c9b75..49c2788b7d 100644
--- a/open-sse/config/providers/registry/volcengine/coding-plan/index.ts
+++ b/open-sse/config/providers/registry/volcengine/coding-plan/index.ts
@@ -54,8 +54,8 @@ export const VOLCENGINE_CODING_PLAN_MODELS: RegistryModel[] = [
name: "MiniMax M3 (Coding Plan)",
contextLength: 1048576,
toolCalling: true,
- supportsReasoning: true,
supportsVision: true,
+ supportsReasoning: true,
},
{
id: "deepseek-v4-pro",
diff --git a/open-sse/executors/index.ts b/open-sse/executors/index.ts
index fbe9940d80..c33ca48839 100644
--- a/open-sse/executors/index.ts
+++ b/open-sse/executors/index.ts
@@ -44,6 +44,7 @@ const lazyExecutors: Record Promise> = {
import("./codex-app-server.ts").then(
(m) => new m.CodexAppServerExecutor({}, "codex-app-server")
),
+ maxai: () => import("./maxai.ts").then((m) => new m.MaxAiExecutor()),
"chatgpt-web-codex": () =>
import("./chatgpt-web-codex.ts").then((m) => new m.ChatGptWebCodexExecutor()),
"cgpt-codex": () =>
diff --git a/open-sse/executors/maxai.ts b/open-sse/executors/maxai.ts
new file mode 100644
index 0000000000..5a9ac60197
--- /dev/null
+++ b/open-sse/executors/maxai.ts
@@ -0,0 +1,620 @@
+/**
+ * MaxAiExecutor β MaxAI web-app chat as an OpenAI-compatible OmniRoute provider.
+ *
+ * MaxAI (chat.maxai.co / api.maxai.me) is a consumer web app with no public API.
+ * This executor reproduces the web app's own signed request to `/gpt/cwc/chat`:
+ * β’ per-request `X-Authorization` signature (see ./signing.ts),
+ * β’ Firefox-150 identity headers + Bearer access token,
+ * β’ the full OpenAI transcript flattened into one `message_content` block
+ * (stateless-full-history; see ./protocol.ts),
+ * β’ SSE response parsed for text deltas, with inline `` reasoning split
+ * out into `reasoning_content` (see ./stream.ts).
+ *
+ * Egress + TLS: the request MUST exit a residential IP (MaxAI bot-bans datacenter
+ * IPs). OmniRoute routes the executor's `fetch()` through the per-connection proxy
+ * (a residential HTTP proxy) transparently, and applies the wreq-js Firefox TLS
+ * fingerprint when enabled. This executor does not open its own socket; it uses
+ * the ambient patched `fetch`, so the proxy + TLS overlay apply automatically.
+ *
+ * Auth refresh: MaxAI's `/oauth/refresh_access_token` is deep-TLS-gated and cannot
+ * be called by any HTTP client (only a real browser passes). The access token is
+ * therefore minted/refreshed out-of-band by OmniRoute's own browser-mint flow
+ * (see maxaiBrowserLogin); this executor only consumes the stored credential.
+ */
+import { BaseExecutor, type ExecuteInput, type ExecutorExecuteResult } from "./base.ts";
+import { PROVIDERS } from "../config/constants.ts";
+import { sanitizeErrorMessage } from "../utils/error.ts";
+import { resolveMaxaiCredential, type MaxaiCredential } from "./maxai/credentials.ts";
+import { buildMaxaiSignedHeaders } from "./maxai/signing.ts";
+import { ensureMaxaiConstants } from "./maxai/constantsStore.ts";
+import { maxaiAccessTokenNeedsRefresh, maxaiRefreshAccessToken } from "./maxai/refresh.ts";
+import {
+ assembleMaxaiContext,
+ buildMaxaiChatBody,
+ extractCurrentTurnImages,
+ MAXAI_BASE_URL,
+ MAXAI_CHAT_PATH,
+ maxaiStaticHeaders,
+ newConversationId,
+} from "./maxai/protocol.ts";
+import { resolveMaxaiDocList, type MaxaiDocListEntry } from "./maxai/documents.ts";
+import { estimateMaxaiTokens, isMaxaiTextFrame, ThinkSplitter } from "./maxai/stream.ts";
+import { prepareToolMessages, parseToolCallsFromText } from "../translator/webTools.ts";
+import { buildToolModeResponse } from "./chatgptWebTools.ts";
+
+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",
+};
+
+interface OpenAiChatBody {
+ messages?: Array<{
+ role?: string;
+ content?: unknown;
+ tool_calls?: unknown;
+ tool_call_id?: string;
+ }>;
+ model?: string;
+}
+
+function errorResponse(status: number, message: string, code: string): Response {
+ return new Response(
+ JSON.stringify({
+ error: {
+ code,
+ message: sanitizeErrorMessage(message),
+ type: status >= 500 ? "provider_error" : "invalid_request_error",
+ },
+ }),
+ { status, headers: JSON_HEADERS }
+ );
+}
+
+/**
+ * Wrap a Response into the executor wrapper contract shape
+ * `{response, url, headers, transformedBody}` that `chatCore.ts` and the
+ * web-cookie/noauth sweep (tests/unit/executor-web-cookie-sweep.test.ts)
+ * require. `headers` and `transformedBody` are the ACTUAL upstream request
+ * headers and body β chatCore surfaces them as the provider-request-capture
+ * ("what we actually sent") in the dashboard and uses the body for service-tier
+ * and prompt-cache metadata (chatCore.ts:3680-3688), mirroring the shape returned
+ * by every web-cookie sibling (venice-web.ts:92-94, poe-web.ts:121-123). Error
+ * paths that fail BEFORE a request is assembled pass no capture β honestly empty,
+ * because nothing was sent upstream.
+ */
+function wrap(
+ response: Response,
+ url: string,
+ capture?: { headers?: Record; transformedBody?: unknown }
+): { response: Response; url: string; headers: Record; transformedBody: unknown } {
+ return {
+ response,
+ url,
+ headers: capture?.headers ?? {},
+ transformedBody: capture?.transformedBody ?? null,
+ };
+}
+
+/**
+ * Detect a tool "narration miss": the model produced no parseable block
+ * but its text shows it was ABOUT to call a tool (talks about the block
+ * or names a requested tool). This is the occasional reasoning-model failure
+ * mode (e.g. deepseek-r1) where it reasons about the call instead of emitting
+ * it. A true refusal or a normal answer returns false, so we never retry those.
+ */
+function isToolNarrationMiss(text: string, requestedTools: unknown): boolean {
+ if (!text) return false;
+ if (/)
+ .map((t) => (typeof t?.function?.name === "string" ? t.function.name : ""))
+ .filter(Boolean)
+ : [];
+ // Names it a tool AND signals intent to use it (not merely mentioning it).
+ const intent = /\b(I('| wi)ll|let me|I can|going to|need to)\b/i.test(text);
+ return intent && names.some((n) => text.includes(n));
+}
+
+/** A short, soft nudge appended to the transcript for the single retry turn. */
+function toolNudge(originalText: string): string {
+ return (
+ originalText +
+ "\n\n[A quick note: if a client tool would help answer this, please go ahead " +
+ "and emit the block directly rather than describing it β just the block " +
+ "on its own line. If no tool is needed, a normal answer is perfectly fine.]"
+ );
+}
+
+/** Emit one OpenAI `chat.completion.chunk`. */
+function chunk(
+ controller: ReadableStreamDefaultController,
+ id: string,
+ created: number,
+ model: string,
+ delta: Record,
+ finish: string | null = null
+): void {
+ const payload = {
+ id,
+ object: "chat.completion.chunk",
+ created,
+ model,
+ choices: [{ index: 0, delta, finish_reason: finish }],
+ };
+ controller.enqueue(new TextEncoder().encode(`data: ${JSON.stringify(payload)}\n\n`));
+}
+
+export class MaxAiExecutor extends BaseExecutor {
+ constructor() {
+ super("maxai", PROVIDERS.maxai ?? { id: "maxai", baseUrl: MAXAI_BASE_URL });
+ }
+
+ override async execute(input: ExecuteInput): Promise {
+ // The MaxAI chat endpoint URL is the wrapper's `url` for every return path
+ // (error and success alike), so define it once up front.
+ const url = MAXAI_BASE_URL + MAXAI_CHAT_PATH;
+
+ const cred = resolveMaxaiCredential(
+ input.credentials?.providerSpecificData,
+ input.credentials?.accessToken
+ );
+ if (!cred) {
+ return wrap(
+ errorResponse(
+ 401,
+ "MaxAI connection is not configured (missing access token, device id, or user id). Sign in to mint a token.",
+ "maxai_unconfigured"
+ ),
+ url
+ );
+ }
+
+ // Proactively refresh a near-expiry access token (browserless; see ./maxai/refresh.ts).
+ // Failures here are non-fatal: we fall through with the existing token, and a
+ // genuinely-dead token surfaces as a 401/418 below (prompting a re-mint).
+ const accessToken = await this.ensureFreshAccess(cred, input);
+
+ const body = (input.body ?? {}) as OpenAiChatBody;
+
+ // Tool-calling (prompted protocol): when the request carries tools[], inject
+ // the contract into the messages so the model learns the client tools
+ // and how to invoke them (see translator/webTools.ts). MaxAI has no native
+ // function-calling; this is the same prompted-tool shim the web-cookie
+ // providers use. The response side parses blocks back into tool_calls.
+ const { hasTools, requestedTools, effectiveMessages } = prepareToolMessages(
+ body as Record,
+ (body.messages ?? []) as Array<{ role: string; content: unknown }>
+ );
+
+ let text: string;
+ try {
+ text = assembleMaxaiContext(effectiveMessages);
+ } catch {
+ return wrap(
+ errorResponse(400, "No user message to send to MaxAI.", "maxai_empty_request"),
+ url
+ );
+ }
+
+ // Vision input: attach the CURRENT user turn's images (data: / http(s):) to
+ // message_content so vision-capable MaxAI models actually see them. Extract
+ // from the original messages (pre-tool-munging); text stays flattened.
+ const originalMessages = (body.messages ?? []) as Array<{ role?: string; content?: unknown }>;
+ const imageUrls = extractCurrentTurnImages(originalMessages);
+
+ // Doc-RAG: upload any inline documents (base64 file/input_file/document
+ // parts) on the current turn to /app/upload_document and attach the
+ // resulting doc_list to the chat body. Best-effort: upload failures are
+ // skipped and the chat proceeds without the doc.
+ let docList: MaxaiDocListEntry[] = [];
+ try {
+ docList = await resolveMaxaiDocList(
+ originalMessages,
+ { accessToken, userId: cred.userId, deviceId: cred.deviceId },
+ { signal: input.signal ?? undefined }
+ );
+ } catch {
+ docList = [];
+ }
+
+ const constants = await ensureMaxaiConstants({ signal: input.signal });
+ if (!constants) {
+ return wrap(
+ errorResponse(
+ 401,
+ "MaxAI signing constants unavailable (extraction failed); cannot sign the request.",
+ "maxai_auth_error"
+ ),
+ url
+ );
+ }
+
+ const conversationId = newConversationId();
+ const chatBody = buildMaxaiChatBody({
+ conversationId,
+ text,
+ modelName: input.model,
+ appVersion: constants.appVersion,
+ imageUrls,
+ docList: docList.length ? docList : undefined,
+ });
+
+ const signedHeaders = buildMaxaiSignedHeaders(
+ {
+ path: MAXAI_CHAT_PATH,
+ userId: cred.userId,
+ deviceId: cred.deviceId,
+ },
+ constants
+ );
+ const headers: Record = {
+ ...maxaiStaticHeaders(),
+ ...signedHeaders,
+ Authorization: `Bearer ${accessToken}`,
+ ...(input.upstreamExtraHeaders ?? {}),
+ };
+
+ let upstream: Response;
+ try {
+ upstream = await fetch(url, {
+ method: "POST",
+ headers,
+ body: JSON.stringify(chatBody),
+ signal: input.signal ?? undefined,
+ });
+ } catch (err) {
+ return wrap(
+ errorResponse(
+ 502,
+ `MaxAI request failed: ${sanitizeErrorMessage(err instanceof Error ? err.message : err)}`,
+ "maxai_transport_error"
+ ),
+ url
+ );
+ }
+
+ if (upstream.status !== 200 || !upstream.body) {
+ const detail = await upstream.text().catch(() => "");
+ // 401/418 = auth expired/masked-reject; surface so the caller can prompt a re-mint.
+ // A body-too-large rejection (MaxAI answers 422 "...message you submitted being
+ // too long...") is INPUT-bound: classify it as context_length_exceeded so
+ // OmniRoute's compression/overflow pipeline can shrink and retry instead of
+ // treating it as an opaque provider error.
+ const tooLong = /too\s+long|exceeds?\b.*\bcontext|context.*(?:exceeded|too long|limit)/i.test(
+ detail
+ );
+ if (tooLong) {
+ return wrap(
+ errorResponse(
+ 400,
+ `MaxAI request exceeds the context limit: ${sanitizeErrorMessage(detail.slice(0, 200))}`,
+ "context_length_exceeded"
+ ),
+ url
+ );
+ }
+ const status = upstream.status === 418 ? 401 : upstream.status || 502;
+ return wrap(
+ errorResponse(
+ status,
+ `MaxAI upstream ${upstream.status}: ${sanitizeErrorMessage(detail.slice(0, 300))}`,
+ upstream.status === 401 || upstream.status === 418
+ ? "maxai_auth_error"
+ : "maxai_upstream_error"
+ ),
+ url
+ );
+ }
+
+ const id = `chatcmpl-${conversationId}`;
+ const created = Math.floor(Date.now() / 1000);
+ const promptTokens = estimateMaxaiTokens(text);
+
+ // Tool mode: MaxAI streams plain text, and the protocol is only
+ // parseable once the full reply is in hand. So when tools are active we
+ // buffer the whole body, build a chat.completion, and let the shared shim
+ // parse blocks into tool_calls (emitting a terminal SSE replay for
+ // streaming callers). This mirrors every web-cookie provider's tool path.
+ if (hasTools) {
+ const raw = await upstream.text();
+ let { reasoning, answer } = collectNonStream(raw);
+
+ // Reliability: if the model narrated about the tool but emitted no
+ // parseable block (occasional reasoning-model miss), do ONE gentle
+ // nudged retry and keep it only if it actually produces a tool call.
+ const firstHasToolCall = !!parseToolCallsFromText(answer, "probe", requestedTools).toolCalls;
+ if (!firstHasToolCall && isToolNarrationMiss(reasoning + "\n" + answer, requestedTools)) {
+ const retry = await this.retryToolTurn(cred, accessToken, input, toolNudge(text));
+ if (retry && parseToolCallsFromText(retry.answer, "probe", requestedTools).toolCalls) {
+ reasoning = retry.reasoning;
+ answer = retry.answer;
+ input.log?.debug?.("maxai", "tool narration-miss recovered via one nudged retry");
+ }
+ }
+
+ const completionTokens = estimateMaxaiTokens(reasoning + answer);
+ const buffered = new Response(
+ JSON.stringify({
+ id,
+ object: "chat.completion",
+ created,
+ model: input.model,
+ choices: [
+ {
+ index: 0,
+ message: {
+ role: "assistant",
+ content: answer,
+ ...(reasoning ? { reasoning_content: reasoning } : {}),
+ },
+ finish_reason: "stop",
+ },
+ ],
+ usage: {
+ prompt_tokens: promptTokens,
+ completion_tokens: completionTokens,
+ total_tokens: promptTokens + completionTokens,
+ },
+ }),
+ { status: 200, headers: JSON_HEADERS }
+ );
+ const response = await buildToolModeResponse(buffered, requestedTools, input.stream, {
+ cid: id,
+ created,
+ model: input.model,
+ idSeed: "maxai",
+ });
+ return wrap(response, url, { headers, transformedBody: chatBody });
+ }
+
+ if (input.stream) {
+ const stream = this.buildStream(upstream.body, id, created, input.model, promptTokens);
+ return wrap(new Response(stream, { status: 200, headers: SSE_HEADERS }), url, {
+ headers,
+ transformedBody: chatBody,
+ });
+ }
+
+ // Non-streaming: collect the whole SSE body, split think, build a chat.completion.
+ const raw = await upstream.text();
+ const { reasoning, answer } = collectNonStream(raw);
+ const completionTokens = estimateMaxaiTokens(reasoning + answer);
+ const response = {
+ id,
+ object: "chat.completion",
+ created,
+ model: input.model,
+ choices: [
+ {
+ index: 0,
+ message: {
+ role: "assistant",
+ content: answer,
+ ...(reasoning ? { reasoning_content: reasoning } : {}),
+ },
+ finish_reason: "stop",
+ },
+ ],
+ usage: {
+ prompt_tokens: promptTokens,
+ completion_tokens: completionTokens,
+ total_tokens: promptTokens + completionTokens,
+ },
+ };
+ return wrap(
+ new Response(JSON.stringify(response), { status: 200, headers: JSON_HEADERS }),
+ url,
+ { headers, transformedBody: chatBody }
+ );
+ }
+
+ /**
+ * Return a non-expired access token, refreshing browserlessly when the stored
+ * one is missing or within the expiry margin and a refresh token is available.
+ * Persists a freshly-minted token via `onCredentialsRefreshed`. Never throws β
+ * on any refresh failure it returns the original token so the request still
+ * proceeds (a truly-dead token then surfaces as an upstream 401/418).
+ */
+ private async ensureFreshAccess(cred: MaxaiCredential, input: ExecuteInput): Promise {
+ if (!cred.refreshToken) return cred.accessToken;
+ if (!maxaiAccessTokenNeedsRefresh(cred.accessToken)) return cred.accessToken;
+
+ const result = await maxaiRefreshAccessToken({
+ refreshToken: cred.refreshToken,
+ deviceId: cred.deviceId,
+ userId: cred.userId,
+ signal: input.signal ?? undefined,
+ });
+ if (!result.ok || !result.accessToken) {
+ input.log?.warn?.(
+ "maxai",
+ `access-token refresh failed (${result.status}); using existing token`
+ );
+ return cred.accessToken;
+ }
+
+ // Persist the new access token (merged into providerSpecificData) so the next
+ // request starts fresh. The refresh token and device id are unchanged.
+ try {
+ await input.onCredentialsRefreshed?.({
+ accessToken: result.accessToken,
+ providerSpecificData: {
+ ...(input.credentials?.providerSpecificData ?? {}),
+ maxaiAccessToken: result.accessToken,
+ },
+ });
+ } catch (err) {
+ input.log?.warn?.(
+ "maxai",
+ `refreshed token persist failed: ${sanitizeErrorMessage(err instanceof Error ? err.message : err)}`
+ );
+ }
+ return result.accessToken;
+ }
+
+ /**
+ * Run a single follow-up MaxAI turn with a gentle nudge appended, used to
+ * recover a reasoning-model "narration miss" (the model talked ABOUT the
+ * block instead of emitting it). Bounded to one extra call; returns the
+ * split { reasoning, answer } or null on any failure (caller keeps the original).
+ */
+ private async retryToolTurn(
+ cred: MaxaiCredential,
+ accessToken: string,
+ input: ExecuteInput,
+ nudgedText: string
+ ): Promise<{ reasoning: string; answer: string } | null> {
+ try {
+ const constants = await ensureMaxaiConstants({ signal: input.signal });
+ if (!constants) return null;
+ const retryBody = buildMaxaiChatBody({
+ conversationId: newConversationId(),
+ text: nudgedText,
+ modelName: input.model,
+ appVersion: constants.appVersion,
+ });
+ const headers: Record = {
+ ...maxaiStaticHeaders(),
+ ...buildMaxaiSignedHeaders(
+ {
+ path: MAXAI_CHAT_PATH,
+ userId: cred.userId,
+ deviceId: cred.deviceId,
+ },
+ constants
+ ),
+ Authorization: `Bearer ${accessToken}`,
+ ...(input.upstreamExtraHeaders ?? {}),
+ };
+ const res = await fetch(MAXAI_BASE_URL + MAXAI_CHAT_PATH, {
+ method: "POST",
+ headers,
+ body: JSON.stringify(retryBody),
+ signal: input.signal ?? undefined,
+ });
+ if (res.status !== 200 || !res.body) return null;
+ return collectNonStream(await res.text());
+ } catch {
+ return null;
+ }
+ }
+
+ /** Bridge the MaxAI SSE body into an OpenAI chat.completion.chunk stream. */
+ private buildStream(
+ source: ReadableStream,
+ id: string,
+ created: number,
+ model: string,
+ promptTokens: number
+ ): ReadableStream {
+ const splitter = new ThinkSplitter();
+ const decoder = new TextDecoder();
+ let sseBuf = "";
+ let sentRole = false;
+ let completionChars = 0;
+
+ const emitDelta = (controller: ReadableStreamDefaultController, r: string, a: string) => {
+ if (!sentRole && (r || a)) {
+ chunk(controller, id, created, model, { role: "assistant" });
+ sentRole = true;
+ }
+ if (r) {
+ chunk(controller, id, created, model, { reasoning_content: r });
+ completionChars += r.length;
+ }
+ if (a) {
+ chunk(controller, id, created, model, { content: a });
+ completionChars += a.length;
+ }
+ };
+
+ const processFrame = (controller: ReadableStreamDefaultController, jsonStr: string) => {
+ if (!jsonStr || jsonStr === "[DONE]") return;
+ let frame: unknown;
+ try {
+ frame = JSON.parse(jsonStr);
+ } catch {
+ return;
+ }
+ if (isMaxaiTextFrame(frame)) {
+ const { reasoning, answer } = splitter.feed(frame.text);
+ emitDelta(controller, reasoning, answer);
+ }
+ };
+
+ return new ReadableStream({
+ async start(controller) {
+ const reader = source.getReader();
+ try {
+ for (;;) {
+ const { done, value } = await reader.read();
+ if (done) break;
+ sseBuf += decoder.decode(value, { stream: true });
+ let nl: number;
+ while ((nl = sseBuf.indexOf("\n")) !== -1) {
+ const line = sseBuf.slice(0, nl).trim();
+ sseBuf = sseBuf.slice(nl + 1);
+ if (line.startsWith("data:")) processFrame(controller, line.slice(5).trim());
+ }
+ }
+ // flush held tail from the think splitter
+ const tail = splitter.flush();
+ emitDelta(controller, tail.reasoning, tail.answer);
+ // final chunk with usage + finish
+ const completionTokens = estimateMaxaiTokens("x".repeat(completionChars));
+ const finalChunk = {
+ id,
+ object: "chat.completion.chunk",
+ created,
+ model,
+ choices: [{ index: 0, delta: {}, finish_reason: "stop" }],
+ usage: {
+ prompt_tokens: promptTokens,
+ completion_tokens: completionTokens,
+ total_tokens: promptTokens + completionTokens,
+ },
+ };
+ controller.enqueue(new TextEncoder().encode(`data: ${JSON.stringify(finalChunk)}\n\n`));
+ controller.enqueue(new TextEncoder().encode("data: [DONE]\n\n"));
+ controller.close();
+ } catch (err) {
+ try {
+ controller.error(err);
+ } catch {
+ /* already errored */
+ }
+ } finally {
+ reader.releaseLock();
+ }
+ },
+ });
+ }
+}
+
+/** Collect a full MaxAI SSE body into split { reasoning, answer } (non-stream). */
+function collectNonStream(raw: string): { reasoning: string; answer: string } {
+ const splitter = new ThinkSplitter();
+ let reasoning = "";
+ let answer = "";
+ for (const line of raw.split("\n")) {
+ const s = line.trim();
+ if (!s.startsWith("data:")) continue;
+ const js = s.slice(5).trim();
+ if (!js || js === "[DONE]") continue;
+ let frame: unknown;
+ try {
+ frame = JSON.parse(js);
+ } catch {
+ continue;
+ }
+ if (isMaxaiTextFrame(frame)) {
+ const out = splitter.feed(frame.text);
+ reasoning += out.reasoning;
+ answer += out.answer;
+ }
+ }
+ const tail = splitter.flush();
+ return { reasoning: reasoning + tail.reasoning, answer: answer + tail.answer };
+}
diff --git a/open-sse/executors/maxai/catalog.ts b/open-sse/executors/maxai/catalog.ts
new file mode 100644
index 0000000000..363b3cca9e
--- /dev/null
+++ b/open-sse/executors/maxai/catalog.ts
@@ -0,0 +1,76 @@
+/**
+ * MaxAI model catalog + provider-enum mapping. Ported from the MaxAI v3 client
+ * (catalog/context_windows.py, tools/provider_enum.py). All 13 chat models are
+ * PAID (the free `mistral-7b-instruct-free` is a window-lookup fallback only and
+ * is not offered). Context windows are the MaxAI-reported values.
+ */
+import type { RegistryModel } from "../../config/providers/shared.ts";
+
+interface MaxaiModelSpec {
+ id: string;
+ name: string;
+ contextLength: number;
+ supportsReasoning?: boolean;
+ /**
+ * Vision-capable (accepts image_url input). Sourced from MaxAI's live
+ * `/models/get_config` `capabilities.vision` (verified 2026-08); the executor
+ * forwards image parts inline in message_content for these. Live discovery
+ * (services/maxaiModels.ts) overrides this from the catalog at runtime; this
+ * static flag keeps the offline registry in agreement.
+ */
+ supportsVision?: boolean;
+}
+
+/** The 13 offered paid chat models (group order: FAST, SMART, REASONING). */
+export const MAXAI_MODELS: MaxaiModelSpec[] = [
+ // FAST
+ { id: "gpt-5.6-luna", name: "GPT-5.6 Luna", contextLength: 1_050_000, supportsVision: true },
+ { id: "claude-haiku-4-5", name: "Claude Haiku 4.5", contextLength: 200_000, supportsVision: true },
+ { id: "gemini-3-1-flash-lite", name: "Gemini 3.1 Flash Lite", contextLength: 1_000_000, supportsVision: true },
+ { id: "grok-4-1-fast-non-reasoning", name: "Grok 4.1 Fast", contextLength: 2_000_000 },
+ { id: "llama-3.3-70b", name: "Llama 3.3 70B", contextLength: 128_000 },
+ { id: "deepseek-v3.2", name: "DeepSeek V3.2", contextLength: 128_000 },
+ // SMART
+ { id: "gpt-5.6", name: "GPT-5.6", contextLength: 1_050_000, supportsVision: true },
+ { id: "claude-5-sonnet", name: "Claude 5 Sonnet", contextLength: 1_000_000 },
+ {
+ id: "grok-4-1-fast-reasoning",
+ name: "Grok 4.1 Fast (Reasoning)",
+ contextLength: 2_000_000,
+ supportsReasoning: true,
+ },
+ // REASONING
+ {
+ id: "gpt-5.6-thinking",
+ name: "GPT-5.6 Thinking",
+ contextLength: 1_050_000,
+ supportsReasoning: true,
+ supportsVision: true,
+ },
+ {
+ id: "gemini-3.1-pro-preview",
+ name: "Gemini 3.1 Pro Preview",
+ contextLength: 1_000_000,
+ supportsReasoning: true,
+ supportsVision: true,
+ },
+ { id: "grok-4.5", name: "Grok 4.5", contextLength: 500_000, supportsReasoning: true },
+ { id: "deepseek-r1", name: "DeepSeek R1", contextLength: 128_000, supportsReasoning: true },
+];
+
+/** RegistryModel[] form for the provider registry entry. */
+export const MAXAI_REGISTRY_MODELS: RegistryModel[] = MAXAI_MODELS.map((m) => ({
+ id: m.id,
+ name: m.name,
+ contextLength: m.contextLength,
+ toolCalling: true, // prompted tool-calling (no native API, but supported via the tool protocol)
+ ...(m.supportsReasoning ? { supportsReasoning: true } : {}),
+ ...(m.supportsVision ? { supportsVision: true } : {}),
+}));
+
+/** Default context window for an unknown model. */
+export const MAXAI_DEFAULT_CONTEXT = 128_000;
+
+export function maxaiContextWindow(modelId: string): number {
+ return MAXAI_MODELS.find((m) => m.id === modelId)?.contextLength ?? MAXAI_DEFAULT_CONTEXT;
+}
diff --git a/open-sse/executors/maxai/constants.ts b/open-sse/executors/maxai/constants.ts
new file mode 100644
index 0000000000..08b5bfd3e4
--- /dev/null
+++ b/open-sse/executors/maxai/constants.ts
@@ -0,0 +1,427 @@
+/**
+ * MaxAI web-app signing constants β extracted live from the public JS bundle.
+ *
+ * MaxAI's request signer needs a small set of CLIENT-SIDE constants that its own
+ * front-end ships VERBATIM in the public `www.maxai.co` JavaScript bundle
+ * (identical for every visitor, no per-user or server secret). OmniRoute EXTRACTS
+ * them from the live bundle and persists them, so if MaxAI ever rotates a value β
+ * or a Next.js rebuild renumbers its chunks β the provider self-heals on the next
+ * login or daily refresh instead of hard-failing every signed call.
+ *
+ * NOTHING id/key/version-shaped is hardcoded anywhere (source OR tests). Every
+ * such value (hmacKey, aesKey, docIdKey, ctxKey, appVersion) is discovered at
+ * runtime and validated; the repo carries no scannable secret and no build-
+ * specific chunk number.
+ *
+ * WHAT is extracted, and from WHERE (all are plain, public static assets):
+ * pages/_app-*.js β the Next.js app-entry chunk (framework-STABLE name, not a
+ * MaxAI chunk number). Webpack module 69319 inside it defines the constants as
+ * export getters we follow to their string literals:
+ * - hmacKey export `Mn` β a hex string (HMAC-SHA1 β SM3 keying)
+ * - aesKey export `Rl` β a hex string (CryptoJS AES passphrase)
+ * - docIdKey export `U0` β a UUID (doc-upload HMAC key)
+ * - appVersion the sole `webpage_x.y.z` literal (folded into the sign_str)
+ * the SIGNER chunk β a NUMBERED chunk whose id changes across builds, so it is
+ * located by CONTENT FINGERPRINT (never by number): the chunk that assembles
+ * the signed payload, recognised by the ctx-slot pattern `"<40hex>":{a:β¦}` next
+ * to the `(0,r.nj)("")` header-name decoders. From it we read:
+ * - ctxKey the 40-hex payload content-slot label
+ * - headerNames the `nj("")` calls = hexβASCII header/slot names
+ *
+ * The extracted set is SHAPE-validated (hex/UUID/version regexes) before it is
+ * trusted; the ULTIMATE validation is the first live signed call (a wrong value
+ * is rejected by MaxAI, which triggers a re-extract). Only the plain, non-secret
+ * HTTP header NAMES (e.g. "X-Authorization") keep in-code defaults, so a transient
+ * miss on the signer chunk can't break a signer that already has valid keys;
+ * extraction still overrides them when present.
+ */
+import { createHmac, createHash } from "node:crypto";
+
+/** The public bundle base. `/app/` is the SPA entry that references the chunks. */
+export const MAXAI_WEBAPP_ORIGIN = "https://www.maxai.co";
+export const MAXAI_WEBAPP_APP_PATH = "/app/";
+
+/** Settings key under which the extracted constants bundle is persisted. */
+export const MAXAI_CONSTANTS_SETTINGS_KEY = "maxaiSigningConstants";
+
+/** Firefox-150 UA used for the (unauthenticated) static-asset fetches. */
+const FETCH_UA =
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:150.0) Gecko/20100101 Firefox/150.0";
+
+/**
+ * The header/slot NAMES the signer emits. These are standard HTTP header names
+ * (not secrets, not id/key/version-shaped), so in-code defaults are appropriate;
+ * extraction overrides any that the signer chunk exposes.
+ */
+export interface MaxaiHeaderNames {
+ authorization: string; // "X-Authorization"
+ clientDomain: string; // "X-Client-Domain"
+ clientPath: string; // "X-Client-Path"
+ random: string; // "X-Random"
+ browserName: string; // "X-Browser-Name"
+ browserVersion: string; // "X-Browser-Version"
+ browserMajor: string; // "X-Browser-Major"
+ appVersionHeader: string; // "X-App-Version"
+ appEnvHeader: string; // "X-App-Env"
+ appEnvValue: string; // "MaxAI-Browser-Extension"
+ tSlot: string; // "t"
+ pSlot: string; // "p"
+ dSlot: string; // "d"
+}
+
+/** The full set of signing constants the MaxAI signer depends on. */
+export interface MaxaiSigningConstants {
+ /** HMAC-SHA1 β SM3 keying material (extracted; no in-code default). */
+ hmacKey: string;
+ /** CryptoJS AES passphrase (extracted; no in-code default). */
+ aesKey: string;
+ /** Version string folded into the signature `sign_str` (extracted). */
+ appVersion: string;
+ /** Payload content-slot label, 40-hex (extracted; no in-code default). */
+ ctxKey: string;
+ /** Doc-upload HMAC key, UUID (extracted; no in-code default). */
+ docIdKey: string;
+ /** Header/slot names emitted by the signer. */
+ headerNames: MaxaiHeaderNames;
+ /** Provenance for the persisted record. */
+ source?: "extracted";
+ extractedAt?: number;
+}
+
+/**
+ * Default HTTP header NAMES (standard, non-secret labels). Extraction overrides
+ * any the signer chunk exposes; these keep a signer with valid keys working even
+ * if the signer chunk momentarily can't be located.
+ */
+export const MAXAI_DEFAULT_HEADER_NAMES: MaxaiHeaderNames = {
+ authorization: "X-Authorization",
+ clientDomain: "X-Client-Domain",
+ clientPath: "X-Client-Path",
+ random: "X-Random",
+ browserName: "X-Browser-Name",
+ browserVersion: "X-Browser-Version",
+ browserMajor: "X-Browser-Major",
+ appVersionHeader: "X-App-Version",
+ appEnvHeader: "X-App-Env",
+ appEnvValue: "MaxAI-Browser-Extension",
+ tSlot: "t",
+ pSlot: "p",
+ dSlot: "d",
+};
+
+/** Raw pieces the parser can pull from the two chunks (any may be absent). */
+export interface MaxaiParsedConstants {
+ hmacKey: string | null;
+ aesKey: string | null;
+ appVersion: string | null;
+ ctxKey: string | null;
+ docIdKey: string | null;
+ headerNames: Partial;
+}
+
+/** Resolve a webpack export getter `Name:function(){return VAR}` β the `VAR="β¦"` literal. */
+export function resolveWebpackGetter(src: string, exportName: string): string | null {
+ const getter = new RegExp(
+ `${exportName}\\s*:\\s*function\\s*\\(\\)\\s*\\{\\s*return\\s+([A-Za-z_$][\\w$]*)\\s*\\}`
+ );
+ let m = src.match(getter);
+ if (!m) {
+ const arrow = new RegExp(`${exportName}\\s*:\\s*\\(\\)\\s*=>\\s*([A-Za-z_$][\\w$]*)`);
+ m = src.match(arrow);
+ }
+ if (!m) return null;
+ const varName = m[1];
+ const assign = new RegExp(`\\b${varName}\\s*=\\s*"([^"]+)"`);
+ const am = src.match(assign);
+ return am ? am[1] : null;
+}
+
+/** Decode the `(0,r.nj)("")` header-name calls (nj = hexβASCII). */
+export function decodeNjHeaderNames(signerChunk: string): string[] {
+ const out = new Set();
+ for (const m of signerChunk.matchAll(/nj\)\("([0-9a-f]+)"\)/g)) {
+ try {
+ const decoded = Buffer.from(m[1], "hex").toString("utf8");
+ // Keep only printable ASCII header-ish tokens (drop numeric ja3 codes etc).
+ if (/^[\x20-\x7e]+$/.test(decoded)) out.add(decoded);
+ } catch {
+ // skip malformed hex
+ }
+ }
+ return [...out];
+}
+
+/** Map the decoded header-name list onto the structured MaxaiHeaderNames slots. */
+function mapHeaderNames(decoded: string[]): Partial {
+ const has = (v: string) => decoded.includes(v);
+ const out: Partial = {};
+ if (has("X-Authorization")) out.authorization = "X-Authorization";
+ if (has("X-Client-Domain")) out.clientDomain = "X-Client-Domain";
+ if (has("X-Client-Path")) out.clientPath = "X-Client-Path";
+ if (has("X-Random")) out.random = "X-Random";
+ if (has("X-Browser-Name")) out.browserName = "X-Browser-Name";
+ if (has("X-Browser-Version")) out.browserVersion = "X-Browser-Version";
+ if (has("X-Browser-Major")) out.browserMajor = "X-Browser-Major";
+ if (has("X-App-Version")) out.appVersionHeader = "X-App-Version";
+ if (has("X-App-Env")) out.appEnvHeader = "X-App-Env";
+ if (has("MaxAI-Browser-Extension")) out.appEnvValue = "MaxAI-Browser-Extension";
+ return out;
+}
+
+/** Extract the 40-hex payload content-slot label from the signer chunk. */
+export function extractCtxKey(signerChunk: string): string | null {
+ return (signerChunk.match(/"([0-9a-f]{40})"\s*:\s*\{\s*a\s*:/) || [])[1] ?? null;
+}
+
+/**
+ * Content fingerprint for the SIGNER chunk (build-independent). The signer chunk
+ * is the one that both (a) carries the ctx payload slot `"<40hex>":{a:β¦}` and
+ * (b) decodes header names via `(0,r.nj)("")`. Matching BOTH avoids a false
+ * positive on any unrelated chunk that merely contains a 40-hex string.
+ */
+export function looksLikeSignerChunk(js: string): boolean {
+ return extractCtxKey(js) !== null && /nj\)\("[0-9a-f]+"\)/.test(js);
+}
+
+/**
+ * Parse the two bundle chunks into raw constants. Pure (no network) so it is
+ * unit-tested directly against synthetic fixtures.
+ */
+export function parseMaxaiConstants(
+ appChunk: string,
+ signerChunk: string
+): MaxaiParsedConstants {
+ const decoded = decodeNjHeaderNames(signerChunk);
+ return {
+ hmacKey: resolveWebpackGetter(appChunk, "Mn"),
+ aesKey: resolveWebpackGetter(appChunk, "Rl"),
+ docIdKey: resolveWebpackGetter(appChunk, "U0"),
+ appVersion: (appChunk.match(/"(webpage_\d+\.\d+\.\d+)"/) || [])[1] ?? null,
+ ctxKey: extractCtxKey(signerChunk),
+ headerNames: mapHeaderNames(decoded),
+ };
+}
+
+/** A MaxAI signing key is a 40+ char lowercase hex string. */
+function isHexKey(v: string | null | undefined): boolean {
+ return typeof v === "string" && /^[0-9a-f]{40,}$/.test(v);
+}
+
+/** A doc-id key is a UUID (v4-shaped). */
+function isUuidKey(v: string | null | undefined): boolean {
+ return typeof v === "string" && /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(v);
+}
+
+/** A MaxAI app_version tag looks like `webpage_x.y.z`. */
+function isAppVersion(v: string | null | undefined): boolean {
+ return typeof v === "string" && /^webpage_\d+\.\d+\.\d+$/.test(v);
+}
+
+/**
+ * Fold parsed pieces into a full constants object. The five extracted values
+ * (hmacKey, aesKey, ctxKey, docIdKey, appVersion) are ALL required and must be
+ * well-formed β return null otherwise, so we never persist a half-configured
+ * signer. Only the plain HTTP header names fall back to the standard defaults.
+ */
+export function assembleMaxaiConstants(
+ parsed: MaxaiParsedConstants
+): MaxaiSigningConstants | null {
+ if (!isHexKey(parsed.hmacKey) || !isHexKey(parsed.aesKey)) return null;
+ if (!isHexKey(parsed.ctxKey)) return null;
+ if (!isUuidKey(parsed.docIdKey)) return null;
+ if (!isAppVersion(parsed.appVersion)) return null;
+ return {
+ hmacKey: parsed.hmacKey as string,
+ aesKey: parsed.aesKey as string,
+ appVersion: parsed.appVersion as string,
+ ctxKey: parsed.ctxKey as string,
+ docIdKey: parsed.docIdKey as string,
+ headerNames: { ...MAXAI_DEFAULT_HEADER_NAMES, ...parsed.headerNames },
+ source: "extracted",
+ extractedAt: Date.now(),
+ };
+}
+
+/** True when a constants object is structurally well-formed (all 5 values valid). */
+export function isValidConstantsShape(c: MaxaiSigningConstants | null | undefined): boolean {
+ if (!c) return false;
+ return (
+ isHexKey(c.hmacKey) &&
+ isHexKey(c.aesKey) &&
+ isHexKey(c.ctxKey) &&
+ isUuidKey(c.docIdKey) &&
+ isAppVersion(c.appVersion) &&
+ !!c.headerNames
+ );
+}
+
+/**
+ * A signature vector: a (path, reqTime, userId, appVersion) tuple and the SM3
+ * proof it should produce. Used to prove the signing ALGORITHM in unit tests with
+ * mock keys β the runtime does NOT embed any real vector (its trust anchor is the
+ * live signed probe). `reproduceProof` is a pure helper over the same math.
+ */
+export interface MaxaiSignatureVector {
+ path: string;
+ reqTime: number;
+ userId: string;
+ appVersion: string;
+ expectedProof: string;
+}
+
+/** Reproduce the SM3 proof `p` for a (path, reqTime, userId, appVersion) under a key. */
+export function reproduceProof(
+ hmacKey: string,
+ vector: Omit
+): string {
+ const signStr = `${vector.appVersion}:${vector.reqTime}:${vector.path}:${vector.userId}`;
+ const sha1 = createHmac("sha1", Buffer.from(`${vector.reqTime}:${hmacKey}`, "utf8"))
+ .update(Buffer.from(signStr, "utf8"))
+ .digest("hex");
+ return createHash("sm3")
+ .update(Buffer.from(`${vector.reqTime}:${sha1}:${hmacKey}`, "utf8"))
+ .digest("hex");
+}
+
+/**
+ * Runtime validation of an extracted/stored constants set. SHAPE-based on purpose:
+ * we carry no real signature vector in source, so the definitive check is the
+ * first live signed call (a wrong value is rejected by MaxAI β re-extract). An
+ * optional `vector` enables proof-based checking in tests with mock keys.
+ */
+export function validateMaxaiConstants(
+ constants: MaxaiSigningConstants,
+ vector?: MaxaiSignatureVector
+): boolean {
+ if (!isValidConstantsShape(constants)) return false;
+ if (!vector) return true;
+ try {
+ return reproduceProof(constants.hmacKey, vector) === vector.expectedProof;
+ } catch {
+ return false;
+ }
+}
+
+/**
+ * Fetch a text asset with the Firefox UA through the ambient (residential) fetch.
+ * Injectable for tests. Returns "" on any failure (caller treats empty as miss).
+ */
+async function fetchText(
+ url: string,
+ fetchImpl: typeof fetch,
+ signal?: AbortSignal | null
+): Promise {
+ try {
+ const res = await fetchImpl(url, {
+ headers: { "User-Agent": FETCH_UA, Accept: "*/*" },
+ signal: signal ?? undefined,
+ });
+ if (!res.ok) return "";
+ return await res.text();
+ } catch {
+ return "";
+ }
+}
+
+/** All `/_next/static/chunks/...js` URLs referenced by the app HTML, in order. */
+export function allChunkUrls(html: string): string[] {
+ const seen = new Set();
+ const out: string[] = [];
+ for (const m of html.matchAll(/\/_next\/static\/chunks\/[A-Za-z0-9/_-]+\.js/g)) {
+ if (!seen.has(m[0])) {
+ seen.add(m[0]);
+ out.push(m[0]);
+ }
+ }
+ return out;
+}
+
+/**
+ * From the `/app/` HTML, resolve the app-entry chunk (by its stable Next.js
+ * `pages/_app-*.js` name) and the list of candidate numbered chunks to scan for
+ * the signer chunk BY CONTENT. No specific chunk number is ever assumed.
+ */
+export function findChunkUrls(html: string): {
+ appChunk: string | null;
+ candidateChunks: string[];
+} {
+ const urls = allChunkUrls(html);
+ let appChunk: string | null = null;
+ const candidateChunks: string[] = [];
+ for (const p of urls) {
+ if (/\/pages\/_app-[a-z0-9]+\.js$/i.test(p)) {
+ appChunk = p;
+ } else if (/\/chunks\/[A-Za-z0-9]+-[a-z0-9]+\.js$/i.test(p)) {
+ // Any hashed vendor/number chunk is a signer-chunk candidate; we identify
+ // the real one by content, not by its (build-specific) name.
+ candidateChunks.push(p);
+ }
+ }
+ return { appChunk, candidateChunks };
+}
+
+export interface FetchConstantsOptions {
+ fetchImpl?: typeof fetch;
+ signal?: AbortSignal | null;
+ /** Override the origin (tests). */
+ origin?: string;
+ /** Cap on how many candidate chunks to scan for the signer chunk (default 80). */
+ maxScanChunks?: number;
+}
+
+/**
+ * Locate + fetch the signer chunk text by CONTENT (never by number): scan the
+ * candidate chunks referenced in the app HTML and return the first whose content
+ * matches the signer fingerprint (ctx slot + nj header decoders). A MaxAI-side
+ * chunk renumber is therefore self-healing, not a break.
+ */
+async function fetchSignerChunk(
+ origin: string,
+ candidates: string[],
+ fetchImpl: typeof fetch,
+ signal: AbortSignal | null | undefined,
+ maxScan: number
+): Promise {
+ for (const c of candidates.slice(0, maxScan)) {
+ const js = await fetchText(origin + c, fetchImpl, signal);
+ if (js && looksLikeSignerChunk(js)) return js;
+ }
+ return "";
+}
+
+/**
+ * Fetch + parse the live constants from MaxAI's public bundle. Returns a fully
+ * assembled, SHAPE-validated constants object, or null on any failure (network,
+ * missing chunk, unparseable, malformed values). Never throws. The definitive
+ * key validation is the caller's first live signed call.
+ */
+export async function fetchMaxaiConstants(
+ opts: FetchConstantsOptions = {}
+): Promise {
+ const fetchImpl = opts.fetchImpl ?? fetch;
+ const origin = opts.origin ?? MAXAI_WEBAPP_ORIGIN;
+ const maxScan = opts.maxScanChunks ?? 80;
+
+ const html = await fetchText(origin + MAXAI_WEBAPP_APP_PATH, fetchImpl, opts.signal);
+ if (!html) return null;
+
+ const { appChunk, candidateChunks } = findChunkUrls(html);
+ if (!appChunk) return null;
+
+ const appJs = await fetchText(origin + appChunk, fetchImpl, opts.signal);
+ if (!appJs) return null;
+
+ const signerJs = await fetchSignerChunk(
+ origin,
+ candidateChunks,
+ fetchImpl,
+ opts.signal,
+ maxScan
+ );
+
+ const parsed = parseMaxaiConstants(appJs, signerJs);
+ const assembled = assembleMaxaiConstants(parsed);
+ if (!assembled) return null;
+ if (!validateMaxaiConstants(assembled)) return null;
+ return assembled;
+}
diff --git a/open-sse/executors/maxai/constantsStore.ts b/open-sse/executors/maxai/constantsStore.ts
new file mode 100644
index 0000000000..08ece802c0
--- /dev/null
+++ b/open-sse/executors/maxai/constantsStore.ts
@@ -0,0 +1,156 @@
+/**
+ * MaxAI signing-constants store + `ensure` gate.
+ *
+ * This is the persistence + freshness layer around ./constants.ts:
+ * - `getStoredMaxaiConstants()` reads the last-extracted, validated constants
+ * from OmniRoute settings (the sole source of the two secret-shaped keys).
+ * - `persistMaxaiConstants()` writes a freshly-extracted+validated set.
+ * - `ensureMaxaiConstants()` is the gate every signed path calls: it returns a
+ * usable constants object, extracting + persisting on a cold store, and is
+ * cheap (in-process memo) on the hot path.
+ * - `refreshMaxaiConstants()` force re-extracts (used by the daily token
+ * refresh) so a MaxAI-side rotation is picked up within a day.
+ *
+ * Design (William's Option 2): there is NO hardcoded fallback for the secret
+ * keys. If the store is empty AND a live extraction cannot be validated, the
+ * signer has no keys and MaxAI is simply unconfigured (callers surface a clear
+ * auth error) β we never sign with a guessed/stale secret.
+ */
+import type { MaxaiSigningConstants, FetchConstantsOptions } from "./constants.ts";
+import {
+ MAXAI_CONSTANTS_SETTINGS_KEY,
+ fetchMaxaiConstants,
+ validateMaxaiConstants,
+ MAXAI_DEFAULT_HEADER_NAMES,
+} from "./constants.ts";
+
+/** In-process memo so the hot signing path never touches the DB or network. */
+let memo: MaxaiSigningConstants | null = null;
+let inflight: Promise | null = null;
+
+/** Reset the in-process memo (tests + after a forced refresh). */
+export function resetMaxaiConstantsMemo(): void {
+ memo = null;
+ inflight = null;
+}
+
+/**
+ * Test seam: directly seed the in-process memo so unit tests that exercise the
+ * signed network functions don't need to also mock the bundle fetch. Not used in
+ * production paths (production goes through ensure/refresh β store β extraction).
+ */
+export function __setMaxaiConstantsForTest(constants: MaxaiSigningConstants | null): void {
+ memo = constants;
+ inflight = null;
+}
+
+/** Shape-guard a persisted record before trusting it. */
+function isUsableConstants(v: unknown): v is MaxaiSigningConstants {
+ if (!v || typeof v !== "object") return false;
+ const c = v as Partial;
+ return (
+ typeof c.hmacKey === "string" &&
+ typeof c.aesKey === "string" &&
+ typeof c.appVersion === "string" &&
+ typeof c.ctxKey === "string" &&
+ typeof c.docIdKey === "string" &&
+ !!c.headerNames &&
+ typeof c.headerNames === "object"
+ );
+}
+
+/** Read the persisted constants from settings (validated). Null when absent/invalid. */
+export async function getStoredMaxaiConstants(): Promise {
+ try {
+ const { getSettings } = await import("@/lib/db/settings");
+ const settings = await getSettings();
+ const raw = (settings as Record)[MAXAI_CONSTANTS_SETTINGS_KEY];
+ if (!isUsableConstants(raw)) return null;
+ // Re-validate on read: a persisted record must still reproduce the vector.
+ const withDefaults: MaxaiSigningConstants = {
+ ...raw,
+ headerNames: { ...MAXAI_DEFAULT_HEADER_NAMES, ...raw.headerNames },
+ };
+ return validateMaxaiConstants(withDefaults) ? withDefaults : null;
+ } catch {
+ return null;
+ }
+}
+
+/** Persist a freshly-extracted+validated constants set to settings. */
+export async function persistMaxaiConstants(
+ constants: MaxaiSigningConstants
+): Promise {
+ try {
+ const { updateSettings } = await import("@/lib/db/settings");
+ await updateSettings({ [MAXAI_CONSTANTS_SETTINGS_KEY]: constants });
+ } catch {
+ // Non-fatal: a persist failure just means the next process re-extracts.
+ }
+}
+
+/**
+ * Return usable MaxAI signing constants, extracting + persisting on a cold store.
+ * Order: in-process memo β persisted store β live extraction (validated) β null.
+ * Concurrent callers share a single in-flight extraction. Never throws.
+ */
+export async function ensureMaxaiConstants(
+ opts: FetchConstantsOptions = {}
+): Promise {
+ if (memo) return memo;
+
+ const stored = await getStoredMaxaiConstants();
+ if (stored) {
+ memo = stored;
+ return memo;
+ }
+
+ if (inflight) return inflight;
+ inflight = (async () => {
+ try {
+ const fresh = await fetchMaxaiConstants(opts);
+ if (fresh) {
+ memo = fresh;
+ await persistMaxaiConstants(fresh);
+ return fresh;
+ }
+ return null;
+ } finally {
+ inflight = null;
+ }
+ })();
+ return inflight;
+}
+
+/**
+ * Force a live re-extraction (used by the daily token refresh). If the fetched
+ * set validates AND differs from what's stored, it is persisted + memoized so a
+ * MaxAI-side rotation is picked up. Returns the current-best constants (the fresh
+ * set on success, else whatever was already stored/memoized). Never throws.
+ */
+export async function refreshMaxaiConstants(
+ opts: FetchConstantsOptions = {}
+): Promise {
+ let fresh: MaxaiSigningConstants | null = null;
+ try {
+ fresh = await fetchMaxaiConstants(opts);
+ } catch {
+ fresh = null;
+ }
+
+ if (fresh) {
+ const changed =
+ !memo ||
+ memo.hmacKey !== fresh.hmacKey ||
+ memo.aesKey !== fresh.aesKey ||
+ memo.appVersion !== fresh.appVersion ||
+ memo.ctxKey !== fresh.ctxKey ||
+ memo.docIdKey !== fresh.docIdKey;
+ memo = fresh;
+ if (changed) await persistMaxaiConstants(fresh);
+ return fresh;
+ }
+
+ // Fetch failed β keep serving whatever we already have (memo or store).
+ return memo ?? (await getStoredMaxaiConstants());
+}
diff --git a/open-sse/executors/maxai/credentials.ts b/open-sse/executors/maxai/credentials.ts
new file mode 100644
index 0000000000..3f12d55999
--- /dev/null
+++ b/open-sse/executors/maxai/credentials.ts
@@ -0,0 +1,96 @@
+/**
+ * MaxAI connection credential resolution.
+ *
+ * MaxAI's request signer needs three things bound together: the OpenAI-style
+ * `access_token` (Bearer, ~24h), the `device_id` that minted it (embedded in the
+ * signed `X-Authorization` β a mismatch is rejected), and the `user_id` (folded
+ * into the signature proof). OmniRoute stores these in the connection's
+ * `providerSpecificData` (minted by OmniRoute's own browser-mint flow β see
+ * maxaiBrowserLogin), so the router is self-contained and never reads any
+ * external (Hermes) token file.
+ *
+ * The access token is refreshed out-of-band by the browser-mint (the
+ * `/oauth/refresh_access_token` endpoint is deep-TLS-gated and cannot be called
+ * by any HTTP client β only a real browser passes), so this module only READS
+ * the stored credential; it does not attempt an HTTP refresh.
+ */
+
+export interface MaxaiCredential {
+ accessToken: string;
+ deviceId: string;
+ userId: string;
+ /** ~1-year refresh token used for browserless access-token refresh (optional). */
+ refreshToken?: string;
+}
+
+type ProviderSpecificData = Record | null | undefined;
+
+function firstString(...values: unknown[]): string | null {
+ for (const v of values) {
+ if (typeof v === "string") {
+ // Raw browser LocalStorage sometimes wraps the device id in quotes.
+ const trimmed = v.trim().replace(/^"|"$/g, "");
+ if (trimmed.length > 0) return trimmed;
+ }
+ }
+ return null;
+}
+
+/** Decode the `user_id` from a MaxAI access JWT (subject.user_id or sub). No verify. */
+export function userIdFromJwt(accessToken: string): string | null {
+ try {
+ const seg = accessToken.split(".")[1];
+ if (!seg) return null;
+ const b64 = seg.replace(/-/g, "+").replace(/_/g, "/") + "=".repeat((4 - (seg.length % 4)) % 4);
+ const claims = JSON.parse(Buffer.from(b64, "base64").toString("utf8"));
+ const subject = claims?.subject as { user_id?: unknown } | undefined;
+ if (typeof subject?.user_id === "string") return subject.user_id;
+ if (typeof claims?.sub === "string") return claims.sub;
+ return null;
+ } catch {
+ return null;
+ }
+}
+
+/** Epoch seconds of the access-JWT `exp`, or 0 when undecodable. */
+export function accessTokenExpiry(accessToken: string): number {
+ try {
+ const seg = accessToken.split(".")[1];
+ if (!seg) return 0;
+ const b64 = seg.replace(/-/g, "+").replace(/_/g, "/") + "=".repeat((4 - (seg.length % 4)) % 4);
+ const claims = JSON.parse(Buffer.from(b64, "base64").toString("utf8"));
+ return typeof claims?.exp === "number" ? claims.exp : 0;
+ } catch {
+ return 0;
+ }
+}
+
+/**
+ * Resolve the MaxAI credential from a connection's providerSpecificData (with the
+ * OpenAI-style `access_token` optionally supplied separately by the caller, which
+ * is how OmniRoute threads the stored connection token). Returns null when not
+ * fully configured (all three of accessToken/deviceId/userId required).
+ */
+export function resolveMaxaiCredential(
+ psd: ProviderSpecificData,
+ accessTokenFromConnection?: string | null
+): MaxaiCredential | null {
+ const accessToken = firstString(
+ accessTokenFromConnection,
+ psd?.maxaiAccessToken,
+ psd?.accessToken
+ );
+ if (!accessToken) return null;
+
+ const deviceId = firstString(psd?.maxaiDeviceId, psd?.deviceId);
+ if (!deviceId) return null;
+
+ const userId =
+ firstString(psd?.maxaiUserId, psd?.userId) ?? userIdFromJwt(accessToken);
+ if (!userId) return null;
+
+ const refreshToken =
+ firstString(psd?.maxaiRefreshToken, psd?.refreshToken) ?? undefined;
+
+ return { accessToken, deviceId, userId, refreshToken };
+}
diff --git a/open-sse/executors/maxai/documents.ts b/open-sse/executors/maxai/documents.ts
new file mode 100644
index 0000000000..ce41d9490a
--- /dev/null
+++ b/open-sse/executors/maxai/documents.ts
@@ -0,0 +1,266 @@
+/**
+ * MaxAI doc-RAG β inline document parts β /app/upload_document β doc_list.
+ *
+ * OmniRoute delivers attached documents INLINE in the chat request as base64
+ * `file_data` content parts (OpenAI `{type:"file",file:{filename,file_data}}` /
+ * Responses `{type:"input_file",file_data}` / Claude `{type:"document",source}`).
+ * MaxAI's `/gpt/cwc/chat` cannot take binary docs inline; instead it references
+ * uploaded documents by a content-addressed `doc_id`. This module bridges the
+ * two: it detects inline base64 doc parts on the current turn, uploads each via
+ * the multipart `/app/upload_document` endpoint (signed like every MaxAI call),
+ * and returns the `doc_list` entries to attach to the chat body.
+ *
+ * doc_id is NOT random β MaxAI requires `doc_id = HMAC-SHA1(file_bytes, IT)` hex
+ * (createDocId/qM in the extension). A random id is rejected with a 400
+ * "Inconsistency between server doc_id and request doc_id". The IT key is a
+ * public web-app constant (ships in the bundle), same class as the signing
+ * constants; kept here as a named constant (not a secret).
+ *
+ * The doc_list item shape is exactly what the live web app sends
+ * (site chunk 41068): `{ doc_id, doc_type, file_name }`.
+ */
+import { createHmac } from "node:crypto";
+import { buildMaxaiSignedHeaders } from "./signing.ts";
+import { ensureMaxaiConstants } from "./constantsStore.ts";
+import { maxaiStaticHeaders, MAXAI_BASE_URL } from "./protocol.ts";
+
+export const MAXAI_UPLOAD_PATH = "/app/upload_document";
+
+export interface MaxaiDocListEntry {
+ doc_id: string;
+ doc_type: string;
+ file_name: string;
+}
+
+/** An inline document extracted from an OpenAI/Responses/Claude content part. */
+export interface InlineDoc {
+ filename: string;
+ mimeType: string;
+ bytes: Buffer;
+}
+
+/** doc_id = HMAC-SHA1(file_bytes, docIdKey) hex. Content-addressed; MaxAI verifies it. */
+export function computeMaxaiDocId(bytes: Buffer, key: string): string {
+ if (!key) throw new Error("computeMaxaiDocId: missing docIdKey");
+ return createHmac("sha1", key).update(bytes).digest("hex");
+}
+
+const TEXTUAL_EXT = /\.(txt|md|markdown|csv|json|log|xml|yaml|yml|tsv)$/i;
+const CODE_EXT =
+ /\.(py|ipynb|js|jsx|ts|tsx|html?|css|java|cs|php|c|cpp|cxx|h|hpp|go|rs|rb|swift|kt|sh|sql)$/i;
+
+/** Classify the MaxAI doc_type from the filename/mime (extension taxonomy). */
+export function maxaiDocType(filename: string, mimeType: string): string {
+ const f = filename.toLowerCase();
+ if (/\.pdf$/i.test(f) || mimeType === "application/pdf") return "page_content__pdf";
+ if (CODE_EXT.test(f)) return "chat_file_code";
+ return "chat_file"; // text / generic
+}
+
+/** Whether a doc_type requires the pure_text field (text-extractable docs). */
+function requiresPureText(docType: string): boolean {
+ return docType === "chat_file" || docType === "chat_file_code";
+}
+
+/**
+ * Parse an OpenAI/Responses/Claude data-URL into raw bytes + mime. Returns null
+ * for anything that isn't an inline base64 payload (e.g. a remote URL or an
+ * already-uploaded file_id reference, which this bridge does not handle).
+ */
+export function parseInlineDataUrl(dataUrl: unknown): { mimeType: string; bytes: Buffer } | null {
+ if (typeof dataUrl !== "string") return null;
+ const m = /^data:([^;,]*)(;base64)?,(.*)$/s.exec(dataUrl);
+ if (!m) return null;
+ const mimeType = m[1] || "application/octet-stream";
+ const isBase64 = !!m[2];
+ try {
+ const bytes = isBase64
+ ? Buffer.from(m[3], "base64")
+ : Buffer.from(decodeURIComponent(m[3]), "utf8");
+ if (bytes.length === 0) return null;
+ return { mimeType, bytes };
+ } catch {
+ return null;
+ }
+}
+
+/**
+ * Extract inline documents from the CURRENT (last user) turn of an OpenAI
+ * messages[] array. Recognizes the three OmniRoute-delivered shapes:
+ * OpenAI Chat: {type:"file", file:{filename, file_data|data}}
+ * Responses: {type:"input_file", filename, file_data}
+ * Claude: {type:"document", source:{type:"base64", media_type, data}}
+ * Only base64/data-URL payloads are handled (a bridge upload needs the bytes).
+ */
+export function extractCurrentTurnDocs(
+ messages: Array<{ role?: string; content?: unknown }>
+): InlineDoc[] {
+ let content: unknown;
+ for (let i = messages.length - 1; i >= 0; i--) {
+ if (messages[i]?.role === "user") {
+ content = messages[i]?.content;
+ break;
+ }
+ }
+ if (!Array.isArray(content)) return [];
+ const docs: InlineDoc[] = [];
+ for (const part of content) {
+ if (!part || typeof part !== "object") continue;
+ const p = part as Record;
+ const type = p.type;
+
+ if (type === "file" && p.file && typeof p.file === "object") {
+ const file = p.file as Record;
+ const filename = typeof file.filename === "string" ? file.filename : "upload.bin";
+ const raw = (file.file_data ?? file.data) as unknown;
+ const parsed = parseInlineDataUrl(raw);
+ if (parsed) docs.push({ filename, mimeType: parsed.mimeType, bytes: parsed.bytes });
+ } else if (type === "input_file") {
+ const filename = typeof p.filename === "string" ? p.filename : "upload.bin";
+ const parsed = parseInlineDataUrl(p.file_data);
+ if (parsed) docs.push({ filename, mimeType: parsed.mimeType, bytes: parsed.bytes });
+ } else if (type === "document" && p.source && typeof p.source === "object") {
+ const source = p.source as Record;
+ if (source.type === "base64" && typeof source.data === "string") {
+ const mimeType =
+ typeof source.media_type === "string" ? source.media_type : "application/octet-stream";
+ try {
+ const bytes = Buffer.from(source.data, "base64");
+ if (bytes.length > 0) {
+ const filename =
+ typeof p.title === "string" && p.title ? p.title : `document.${mimeExt(mimeType)}`;
+ docs.push({ filename, mimeType, bytes });
+ }
+ } catch {
+ /* skip malformed base64 */
+ }
+ }
+ }
+ }
+ return docs;
+}
+
+function mimeExt(mime: string): string {
+ if (mime === "application/pdf") return "pdf";
+ if (mime.startsWith("text/")) return "txt";
+ return "bin";
+}
+
+/** Rough ~4-chars/token estimate; ceil, never 0 for non-empty text. */
+function estimateTokens(text: string): number {
+ return text ? Math.max(1, Math.ceil(text.length / 4)) : 0;
+}
+
+/** Build the multipart/form-data body for /app/upload_document (fixed boundary). */
+export function buildUploadMultipart(
+ doc: InlineDoc,
+ docId: string,
+ docType: string,
+ boundary: string
+): Buffer {
+ const isTextual =
+ requiresPureText(docType) &&
+ (TEXTUAL_EXT.test(doc.filename) ||
+ CODE_EXT.test(doc.filename) ||
+ doc.mimeType.startsWith("text/"));
+ const pureText = isTextual ? doc.bytes.toString("utf8") : "";
+ const tokens = String(estimateTokens(pureText));
+
+ const parts: Buffer[] = [];
+ const dash = `--${boundary}\r\n`;
+ const field = (name: string, value: string): void => {
+ parts.push(
+ Buffer.from(`${dash}Content-Disposition: form-data; name="${name}"\r\n\r\n${value}\r\n`)
+ );
+ };
+ field("doc_id", docId);
+ field("doc_type", docType);
+ field("pure_text", pureText);
+ field("tokens", tokens);
+ field("doc_type_dependent_data", "{}");
+ // The file part carries the raw bytes with a content-type.
+ parts.push(
+ Buffer.from(
+ `${dash}Content-Disposition: form-data; name="file"; filename="${doc.filename.replace(/"/g, "")}"\r\n` +
+ `Content-Type: ${doc.mimeType}\r\n\r\n`
+ )
+ );
+ parts.push(doc.bytes);
+ parts.push(Buffer.from(`\r\n--${boundary}--\r\n`));
+ return Buffer.concat(parts);
+}
+
+/** True if any SSE frame in the response is the terminal upload_done event. */
+export function sawUploadDone(text: string): boolean {
+ return /"event"\s*:\s*"upload_done"/.test(text) || text.includes("upload_done");
+}
+
+/**
+ * Upload one inline document to MaxAI and return its doc_list entry, or null on
+ * failure (upload failures are non-fatal: the chat proceeds without the doc).
+ */
+export async function uploadMaxaiDocument(
+ doc: InlineDoc,
+ auth: { accessToken: string; userId: string; deviceId: string },
+ opts?: { fetchImpl?: typeof fetch; signal?: AbortSignal }
+): Promise {
+ const fetchImpl = opts?.fetchImpl ?? fetch;
+ const constants = await ensureMaxaiConstants({ fetchImpl, signal: opts?.signal });
+ if (!constants) return null;
+ const docId = computeMaxaiDocId(doc.bytes, constants.docIdKey);
+ const docType = maxaiDocType(doc.filename, doc.mimeType);
+ const boundary = `----maxai${Date.now().toString(16)}${Math.random().toString(16).slice(2)}`;
+ const bodyBuf = buildUploadMultipart(doc, docId, docType, boundary);
+
+ // Sign like any request, but DROP the JSON content-type so we can set the
+ // multipart boundary content-type ourselves (v3h.signed_headers pattern).
+ const { "Content-Type": _drop, ...staticHeaders } = maxaiStaticHeaders();
+ const headers: Record = {
+ ...staticHeaders,
+ ...buildMaxaiSignedHeaders(
+ { path: MAXAI_UPLOAD_PATH, userId: auth.userId, deviceId: auth.deviceId },
+ constants
+ ),
+ Authorization: `Bearer ${auth.accessToken}`,
+ "Content-Type": `multipart/form-data; boundary=${boundary}`,
+ };
+
+ // Copy the multipart bytes into a fresh Uint8Array backed by a plain
+ // (non-shared) ArrayBuffer. `Buffer.buffer` is typed ArrayBufferLike
+ // (ArrayBuffer | SharedArrayBuffer) which isn't assignable to fetch's
+ // BodyInit; a freshly-allocated Uint8Array is the BodyInit shape the rest of
+ // the codebase uses for binary bodies (kimi-web.ts:397, conol-web.ts:529).
+ const bodyBytes = new Uint8Array(bodyBuf.byteLength);
+ bodyBytes.set(bodyBuf);
+
+ try {
+ const resp = await fetchImpl(MAXAI_BASE_URL + MAXAI_UPLOAD_PATH, {
+ method: "POST",
+ headers,
+ body: bodyBytes,
+ signal: opts?.signal,
+ });
+ if (!resp.ok) return null;
+ const text = await resp.text().catch(() => "");
+ if (!sawUploadDone(text)) return null;
+ return { doc_id: docId, doc_type: docType, file_name: doc.filename };
+ } catch {
+ return null;
+ }
+}
+
+/**
+ * Upload every inline document on the current turn and return the doc_list to
+ * attach to the chat body. Failures are skipped (best-effort); the chat still
+ * proceeds. Empty array when there are no inline docs.
+ */
+export async function resolveMaxaiDocList(
+ messages: Array<{ role?: string; content?: unknown }>,
+ auth: { accessToken: string; userId: string; deviceId: string },
+ opts?: { fetchImpl?: typeof fetch; signal?: AbortSignal }
+): Promise {
+ const docs = extractCurrentTurnDocs(messages);
+ if (docs.length === 0) return [];
+ const results = await Promise.all(docs.map((d) => uploadMaxaiDocument(d, auth, opts)));
+ return results.filter((r): r is MaxaiDocListEntry => r !== null);
+}
diff --git a/open-sse/executors/maxai/emailLogin.ts b/open-sse/executors/maxai/emailLogin.ts
new file mode 100644
index 0000000000..676b5c1fae
--- /dev/null
+++ b/open-sse/executors/maxai/emailLogin.ts
@@ -0,0 +1,234 @@
+/**
+ * MaxAI email login β browserless, two signed HTTP calls (a codex-style
+ * device-pair flow, no browser / camoufox / Google navigation).
+ *
+ * MaxAI's web app offers email-code sign-in as an alternative to Google OAuth.
+ * Both steps are plain signed POSTs carrying the same per-request X-Authorization
+ * signature as every other MaxAI call (see ./signing.ts); both paths are in the
+ * signer's BLANK_USER_ROUTES (they sign with a blank user_id, correct β there is
+ * no user id yet before login). Ported byte-faithfully from the MaxAI web-app
+ * bundle (chunk 86042: signInWithEmail line ~5623, verifySecretCode line ~5665).
+ *
+ * Step 1 β request a code (POST /oauth/signin_with_email):
+ * body { email, app: "maxai_webapp" } -> { status: "OK" } (code emailed)
+ *
+ * Step 2 β verify the code (POST /oauth/verify_secret_code):
+ * body { email, secret_code, app: "maxai_webapp", env: "prod_co",
+ * client_user_id, ...nullable attribution fields }
+ * -> { auth_user: { accessToken, refreshToken, userId, email, clientUserId } }
+ *
+ * The `device_id` folded into the signature is a CLIENT-GENERATED UUID (the web
+ * app's getAPIFetchDeviceID = "return stored, else generate + persist"), so the
+ * caller mints one with randomUUID() and reuses it across BOTH steps and for all
+ * subsequent chat / refresh calls (the minted token is bound to that device id).
+ * `client_user_id` is likewise a client UUID.
+ */
+import { buildMaxaiSignedHeaders } from "./signing.ts";
+import { maxaiStaticHeaders, MAXAI_BASE_URL } from "./protocol.ts";
+import { ensureMaxaiConstants } from "./constantsStore.ts";
+import type { MaxaiSigningConstants } from "./constants.ts";
+
+export const MAXAI_SIGNIN_EMAIL_PATH = "/oauth/signin_with_email";
+export const MAXAI_VERIFY_CODE_PATH = "/oauth/verify_secret_code";
+
+/** The web app's env tag for production email verification. */
+const MAXAI_VERIFY_ENV = "prod_co";
+
+export interface MaxaiEmailRequestInput {
+ email: string;
+ /** Client device UUID (mint once, reuse for verify + all later calls). */
+ deviceId: string;
+ signal?: AbortSignal | null;
+ fetchImpl?: typeof fetch;
+}
+
+export interface MaxaiEmailVerifyInput {
+ email: string;
+ /** The 6-digit code the user received by email. */
+ code: string;
+ /** Same device UUID used in the request step. */
+ deviceId: string;
+ /** Client-user UUID (mint once alongside deviceId). */
+ clientUserId: string;
+ signal?: AbortSignal | null;
+ fetchImpl?: typeof fetch;
+}
+
+export interface MaxaiEmailRequestResult {
+ ok: boolean;
+ status: number;
+ error?: string;
+}
+
+/** The full credential set returned by a successful verify. */
+export interface MaxaiLoginCredential {
+ accessToken: string;
+ refreshToken: string;
+ userId: string;
+ email: string;
+ deviceId: string;
+ clientUserId: string;
+}
+
+export interface MaxaiEmailVerifyResult {
+ ok: boolean;
+ status: number;
+ credential?: MaxaiLoginCredential;
+ error?: string;
+}
+
+/** Build signed headers for a blank-user OAuth route (user id is blanked in the proof). */
+function signedOauthHeaders(
+ path: string,
+ deviceId: string,
+ constants: MaxaiSigningConstants
+): Record {
+ return {
+ ...maxaiStaticHeaders(),
+ // userId is blanked inside computeMaxaiProof for BLANK_USER_ROUTES; pass "".
+ ...buildMaxaiSignedHeaders({ path, userId: "", deviceId }, constants),
+ };
+}
+
+/** Pull a nested-or-top-level field from a MaxAI response body ({data:{...}} | {...}). */
+function pick(body: Record, key: string): T | undefined {
+ const data = body?.data as Record | undefined;
+ const nested = data?.[key];
+ if (nested !== undefined) return nested as T;
+ return body?.[key] as T | undefined;
+}
+
+/**
+ * Step 1: ask MaxAI to email a sign-in code. Never throws.
+ * Returns ok=true when the server acknowledges (status "OK").
+ */
+export async function requestMaxaiEmailCode(
+ input: MaxaiEmailRequestInput
+): Promise {
+ const doFetch = input.fetchImpl ?? fetch;
+ if (!input.email || !input.deviceId) {
+ return { ok: false, status: 0, error: "missing email or deviceId" };
+ }
+
+ // Initial login is the FIRST signed call β ensure we have live signing constants
+ // (extracted from MaxAI's public bundle) before signing. No keys = cannot sign.
+ const constants = await ensureMaxaiConstants({ fetchImpl: doFetch, signal: input.signal });
+ if (!constants) {
+ return { ok: false, status: 0, error: "MaxAI signing constants unavailable (extraction failed)" };
+ }
+
+ let res: Response;
+ try {
+ res = await doFetch(MAXAI_BASE_URL + MAXAI_SIGNIN_EMAIL_PATH, {
+ method: "POST",
+ headers: signedOauthHeaders(MAXAI_SIGNIN_EMAIL_PATH, input.deviceId, constants),
+ body: JSON.stringify({ email: input.email, app: "maxai_webapp" }),
+ signal: input.signal ?? undefined,
+ });
+ } catch (err) {
+ return { ok: false, status: 0, error: err instanceof Error ? err.message : String(err) };
+ }
+
+ const raw = await res.text().catch(() => "");
+ if (res.status !== 200) {
+ return { ok: false, status: res.status, error: raw.slice(0, 200) };
+ }
+ let body: Record = {};
+ try {
+ body = JSON.parse(raw) as Record;
+ } catch {
+ return { ok: false, status: res.status, error: "unparseable signin response" };
+ }
+ if (pick(body, "status") === "OK") return { ok: true, status: 200 };
+ const detail = pick(body, "detail") || pick(body, "msg") || "sign-in request failed";
+ return { ok: false, status: res.status, error: String(detail).slice(0, 200) };
+}
+
+/**
+ * Step 2: verify the emailed code and return the full credential. Never throws.
+ * On success the caller persists the credential to the connection's
+ * providerSpecificData (accessToken/refreshToken/deviceId/userId).
+ */
+export async function verifyMaxaiEmailCode(
+ input: MaxaiEmailVerifyInput
+): Promise {
+ const doFetch = input.fetchImpl ?? fetch;
+ if (!input.email || !input.code || !input.deviceId) {
+ return { ok: false, status: 0, error: "missing email, code, or deviceId" };
+ }
+
+ const constants = await ensureMaxaiConstants({ fetchImpl: doFetch, signal: input.signal });
+ if (!constants) {
+ return { ok: false, status: 0, error: "MaxAI signing constants unavailable (extraction failed)" };
+ }
+
+ const requestBody = {
+ email: input.email,
+ secret_code: input.code,
+ app: "maxai_webapp",
+ env: MAXAI_VERIFY_ENV,
+ invitation_code: null,
+ ref: "",
+ client_reference_id: null,
+ client_user_id: input.clientUserId,
+ client_price_version: null,
+ client_onboarding_version: null,
+ user_acquisition_channel: "",
+ gclid: null,
+ };
+
+ let res: Response;
+ try {
+ res = await doFetch(MAXAI_BASE_URL + MAXAI_VERIFY_CODE_PATH, {
+ method: "POST",
+ headers: signedOauthHeaders(MAXAI_VERIFY_CODE_PATH, input.deviceId, constants),
+ body: JSON.stringify(requestBody),
+ signal: input.signal ?? undefined,
+ });
+ } catch (err) {
+ return { ok: false, status: 0, error: err instanceof Error ? err.message : String(err) };
+ }
+
+ const raw = await res.text().catch(() => "");
+ if (res.status !== 200) {
+ return { ok: false, status: res.status, error: raw.slice(0, 200) };
+ }
+ let body: Record = {};
+ try {
+ body = JSON.parse(raw) as Record;
+ } catch {
+ return { ok: false, status: res.status, error: "unparseable verify response" };
+ }
+
+ const authUser = pick>(body, "auth_user");
+ const status = pick(body, "status");
+ if (status === "OK" && authUser && typeof authUser === "object") {
+ const accessToken = String(authUser.accessToken ?? authUser.access_token ?? "");
+ const refreshToken = String(authUser.refreshToken ?? authUser.refresh_token ?? "");
+ const userId = String(authUser.userId ?? authUser.user_id ?? "");
+ if (!accessToken || !refreshToken) {
+ return { ok: false, status: 200, error: "verify OK but token fields missing" };
+ }
+ return {
+ ok: true,
+ status: 200,
+ credential: {
+ accessToken,
+ refreshToken,
+ userId,
+ email: String(authUser.email ?? input.email),
+ deviceId: input.deviceId,
+ clientUserId: String(authUser.clientUserId ?? authUser.client_user_id ?? input.clientUserId),
+ },
+ };
+ }
+
+ // 10119 is MaxAI's "code expired / too many attempts" signal; surface it.
+ const code = pick(body, "code");
+ const detail = pick(body, "detail") || pick(body, "msg");
+ const error =
+ code === 10119
+ ? "Code expired or too many attempts β request a new code."
+ : String(detail || "Invalid code. Check the code and try again.").slice(0, 200);
+ return { ok: false, status: res.status, error };
+}
diff --git a/open-sse/executors/maxai/protocol.ts b/open-sse/executors/maxai/protocol.ts
new file mode 100644
index 0000000000..d3bb117d8f
--- /dev/null
+++ b/open-sse/executors/maxai/protocol.ts
@@ -0,0 +1,266 @@
+/**
+ * MaxAI web-app protocol β request bodies, header assembly, and OpenAIβMaxAI
+ * context flattening. Ported from the MaxAI v3 Python client (chat/request.py,
+ * translation/openai_in.py, translation/turn_render.py) and live-verified against
+ * the real `/gpt/cwc/chat` endpoint.
+ *
+ * MaxAI is a stateless-full-history provider on the OmniRoute side: we send the
+ * ENTIRE flattened transcript in `message_content[0].text` every turn, always
+ * with `chat_history: []`, and mint a fresh `conversation_id` per request. The
+ * live probe proved a bare `/gpt/cwc/chat` (no upsert/add_messages bookkeeping)
+ * honors `model_name` and serves the real paid model, so no bookkeeping is sent.
+ */
+import { randomUUID } from "node:crypto";
+
+export const MAXAI_BASE_URL = "https://api.maxai.me";
+export const MAXAI_CHAT_PATH = "/gpt/cwc/chat";
+export const MAXAI_MODELS_CONFIG_PATH = "/models/get_config";
+
+/** Static Firefox-150 identity headers sent on every MaxAI request. */
+export function maxaiStaticHeaders(): Record {
+ return {
+ "User-Agent":
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:150.0) Gecko/20100101 Firefox/150.0",
+ Accept: "*/*",
+ "Accept-Language": "en-CA,en;q=0.9",
+ Origin: "https://www.maxai.co",
+ Referer: "https://www.maxai.co/",
+ "Sec-Fetch-Dest": "empty",
+ "Sec-Fetch-Mode": "cors",
+ "Sec-Fetch-Site": "cross-site",
+ "Content-Type": "application/json",
+ };
+}
+
+// ββ Chat body βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
+// Field ORDER is pinned (it is part of the HTTP/2 request fingerprint).
+const CHAT_FIELD_ORDER = [
+ "chat_mode",
+ "conversation_id",
+ "chat_history",
+ "message_content",
+ "chrome_extension_version",
+ "model_name",
+ "prompt_id",
+ "prompt_name",
+ "prompt_inputs",
+ "doc_list",
+ "event_source",
+ "streaming",
+ "prompt_type",
+ "feature_name",
+ "source_type",
+ "platform_feature",
+] as const;
+
+export function newConversationId(): string {
+ return randomUUID();
+}
+
+export function buildMaxaiChatBody(opts: {
+ conversationId: string;
+ text: string;
+ modelName: string;
+ language?: string;
+ relatedQuestionCnt?: string;
+ /** Extracted app_version for chrome_extension_version (from the signing constants). */
+ appVersion: string;
+ /**
+ * Vision input: current-turn image URLs (data: or http(s):) to attach to the
+ * request. MaxAI's `/gpt/cwc/chat` accepts inline OpenAI-shaped image parts in
+ * `message_content` alongside the text part. Empty/omitted = text-only (the
+ * default, byte-identical to the pre-vision body).
+ */
+ imageUrls?: string[];
+ /**
+ * Doc-RAG: uploaded-document references (from /app/upload_document). Each entry
+ * carries at least `{ doc_id, doc_type, file_name }`. Typed as a loose object
+ * array so callers can pass their concrete `MaxaiDocListEntry[]` without an
+ * index-signature cast; the body only serializes it into `doc_list`.
+ * Empty/omitted = no docs (the default `doc_list: []`).
+ */
+ docList?: ReadonlyArray;
+}): Record {
+ // message_content is a typed-parts array: the text part ALWAYS leads (so the
+ // flattened transcript stays first and the no-image path is unchanged), then
+ // any image_url parts ride alongside. Mirrors the OpenAI multimodal shape,
+ // which MaxAI passes through (openai-to-cursor.ts vision pattern).
+ const messageContent: Array> = [{ type: "text", text: opts.text }];
+ for (const url of opts.imageUrls ?? []) {
+ if (typeof url === "string" && url) {
+ messageContent.push({ type: "image_url", image_url: { url } });
+ }
+ }
+ const values: Record = {
+ chat_mode: "pro_chat",
+ conversation_id: opts.conversationId,
+ chat_history: [],
+ message_content: messageContent,
+ chrome_extension_version: opts.appVersion,
+ model_name: opts.modelName,
+ prompt_id: "chat",
+ prompt_name: "chat",
+ prompt_inputs: {
+ RELATED_QUESTION_CNT: opts.relatedQuestionCnt ?? "5",
+ AI_RESPONSE_LANGUAGE: opts.language ?? "English",
+ },
+ doc_list: opts.docList ?? [],
+ event_source: "web",
+ streaming: true,
+ prompt_type: "freestyle",
+ feature_name: "immersive_chat",
+ source_type: "NA",
+ platform_feature: "web_app",
+ };
+ const ordered: Record = {};
+ for (const k of CHAT_FIELD_ORDER) ordered[k] = values[k];
+ return ordered;
+}
+
+// ββ OpenAI messages[] β MaxAI single text block ββββββββββββββββββββββββββββββ
+interface OpenAiMessage {
+ role?: string;
+ content?: unknown;
+ tool_calls?: unknown;
+ tool_call_id?: string;
+}
+
+const ROLE_LABEL: Record = {
+ system: "System",
+ user: "User",
+ assistant: "Assistant",
+};
+const HISTORY_HEADER = "=== Conversation so far (for context) ===";
+const CURRENT_HEADER = "=== Current request (respond to THIS) ===";
+
+/** Flatten OpenAI `content` (string or multipart array) to text. */
+export function contentToText(content: unknown): string {
+ if (typeof content === "string") return content;
+ if (Array.isArray(content)) {
+ return content
+ .map((part) =>
+ part && typeof part === "object" && (part as { type?: string }).type === "text"
+ ? String((part as { text?: unknown }).text ?? "")
+ : ""
+ )
+ .filter(Boolean)
+ .join("\n");
+ }
+ return "";
+}
+
+/**
+ * Extract image_url URLs from the CURRENT (last user) turn of an OpenAI
+ * messages[] array. MaxAI is stateless-full-history, so we attach only the
+ * current turn's images (history images would be re-sent every request and
+ * bloat the body). Returns raw url strings (data: or http(s):) in order.
+ */
+export function extractCurrentTurnImages(messages: OpenAiMessage[]): string[] {
+ for (let i = messages.length - 1; i >= 0; i--) {
+ if (messages[i]?.role === "user") {
+ const content = messages[i]?.content;
+ if (!Array.isArray(content)) return [];
+ const urls: string[] = [];
+ for (const part of content) {
+ if (part && typeof part === "object" && (part as { type?: unknown }).type === "image_url") {
+ const imageUrl = (part as { image_url?: unknown }).image_url;
+ if (typeof imageUrl === "string" && imageUrl) {
+ urls.push(imageUrl);
+ } else if (
+ imageUrl &&
+ typeof imageUrl === "object" &&
+ typeof (imageUrl as { url?: unknown }).url === "string" &&
+ (imageUrl as { url: string }).url
+ ) {
+ urls.push((imageUrl as { url: string }).url);
+ }
+ }
+ }
+ return urls;
+ }
+ }
+ return [];
+}
+
+/** Render OpenAI tool_calls[] as the prompted `` text MaxAI understands. */
+function toolCallsToText(toolCalls: unknown): string {
+ if (!Array.isArray(toolCalls)) return "";
+ const blocks: string[] = [];
+ for (const call of toolCalls) {
+ const fn = (call as { function?: { name?: unknown; arguments?: unknown } })?.function;
+ if (!fn) continue;
+ const name = typeof fn.name === "string" ? fn.name : "";
+ let args = fn.arguments;
+ if (typeof args !== "string") {
+ try {
+ args = JSON.stringify(args ?? {});
+ } catch {
+ args = "{}";
+ }
+ }
+ blocks.push(`${JSON.stringify({ name, arguments: args })} `);
+ }
+ return blocks.join("\n");
+}
+
+/** Render one non-system turn as a labeled block, or null to skip. */
+function renderTurn(message: OpenAiMessage): string | null {
+ const role = message.role;
+ const text = contentToText(message.content).trim();
+ if (role === "tool") {
+ const id = message.tool_call_id ? ` tool_call_id="${message.tool_call_id}"` : "";
+ return `\n${text}\n `;
+ }
+ if (role === "assistant" && message.tool_calls) {
+ const calls = toolCallsToText(message.tool_calls);
+ const body = text ? `${text}\n${calls}`.trim() : calls;
+ return `Assistant: ${body}`;
+ }
+ if (!text) return null;
+ const label = ROLE_LABEL[role ?? "user"] ?? "User";
+ return `${label}: ${text}`;
+}
+
+/**
+ * Assemble the full structured context into one text block: system text leads,
+ * prior turns render as a labeled transcript, and the LAST user turn is fenced
+ * under a CURRENT header so a weak model answers THIS turn. Mirrors MaxAI v3
+ * translation/openai_in.py::assemble_context.
+ */
+export function assembleMaxaiContext(messages: OpenAiMessage[]): string {
+ // Find the last user turn (the current request).
+ let curIdx = -1;
+ let current = "";
+ for (let i = messages.length - 1; i >= 0; i--) {
+ if (messages[i]?.role === "user") {
+ curIdx = i;
+ current = contentToText(messages[i].content).trim();
+ break;
+ }
+ }
+ const systemParts: string[] = [];
+ const historyParts: string[] = [];
+ for (let i = 0; i < messages.length; i++) {
+ if (i === curIdx) continue;
+ const m = messages[i];
+ if (m?.role === "system") {
+ const t = contentToText(m.content).trim();
+ if (t) systemParts.push(t);
+ continue;
+ }
+ const block = renderTurn(m);
+ if (block) historyParts.push(block);
+ }
+ const out: string[] = [...systemParts];
+ if (historyParts.length && current) {
+ out.push(HISTORY_HEADER + "\n\n" + historyParts.join("\n\n"));
+ } else {
+ out.push(...historyParts);
+ }
+ if (current) {
+ const head = historyParts.length ? `${CURRENT_HEADER}\n\n` : "";
+ out.push(head + current);
+ }
+ if (out.length === 0) throw new Error("no content to send to MaxAI");
+ return out.join("\n\n");
+}
diff --git a/open-sse/executors/maxai/refresh.ts b/open-sse/executors/maxai/refresh.ts
new file mode 100644
index 0000000000..79bf3ba873
--- /dev/null
+++ b/open-sse/executors/maxai/refresh.ts
@@ -0,0 +1,149 @@
+/**
+ * MaxAI access-token refresh β browserless, via one signed HTTP call.
+ *
+ * MaxAI issues two tokens: a ~24h `accessToken` and a ~1-year `refreshToken`.
+ * The web app refreshes the access token by POSTing the refresh token to
+ * `/oauth/refresh_access_token` (web-app chunk 86042, `refreshAccessToken`). That
+ * endpoint carries the SAME per-request `X-Authorization` signature as every other
+ * MaxAI call (see ./signing.ts) β it is NOT a browser-only OAuth hop. A residential
+ * Firefox-TLS client (wreq-js firefox_150, the OmniRoute egress overlay) passes the
+ * TLS gate, so OmniRoute mints fresh access tokens itself with no browser.
+ *
+ * The refresh token is minted out-of-band, once, by the browser Google-OAuth flow
+ * (see maxaiBrowserLogin) and only needs re-minting when it itself expires (~yearly).
+ * This module handles the routine daily refresh.
+ *
+ * Request shape (byte-faithful to the web app):
+ * POST https://api.maxai.me/oauth/refresh_access_token
+ * Authorization: Bearer // the REFRESH token, not access
+ * noAuthLogout: true
+ * X-Authorization + X-App/X-Browser headers // standard signing
+ * body: {"app":"maxai_webapp"} // the app's `params` -> JSON body
+ * -> 200 { data: { access_token } } // a fresh ~24h access JWT
+ *
+ * The signed path is the BARE pathname (no query string); the `app` field travels
+ * in the body. `user_id` folds into the signature and is read from the refresh
+ * token's own JWT subject (per the web app), falling back to a provided userId.
+ */
+import { buildMaxaiSignedHeaders } from "./signing.ts";
+import { maxaiStaticHeaders, MAXAI_BASE_URL } from "./protocol.ts";
+import { userIdFromJwt, accessTokenExpiry } from "./credentials.ts";
+import { refreshMaxaiConstants } from "./constantsStore.ts";
+
+export const MAXAI_REFRESH_PATH = "/oauth/refresh_access_token";
+
+/** How close to expiry (seconds) an access token may be before we refresh it. */
+export const MAXAI_REFRESH_MARGIN_SECONDS = 60 * 60; // 1h
+
+export interface MaxaiRefreshInput {
+ refreshToken: string;
+ deviceId: string;
+ /** Optional explicit user id; defaults to the refresh token's JWT subject. */
+ userId?: string;
+ signal?: AbortSignal | null;
+ /** Injectable fetch for tests (defaults to the ambient patched fetch). */
+ fetchImpl?: typeof fetch;
+}
+
+export interface MaxaiRefreshResult {
+ ok: boolean;
+ accessToken?: string;
+ /** access token expiry (epoch seconds), when a token was minted. */
+ expiresAt?: number;
+ status: number;
+ error?: string;
+}
+
+/** True when an access token is missing, unparseable, or within the margin of expiry. */
+export function maxaiAccessTokenNeedsRefresh(
+ accessToken: string | null | undefined,
+ marginSeconds: number = MAXAI_REFRESH_MARGIN_SECONDS,
+ now: () => number = Date.now
+): boolean {
+ if (!accessToken) return true;
+ const exp = accessTokenExpiry(accessToken);
+ if (!exp) return true;
+ return exp - now() / 1000 <= marginSeconds;
+}
+
+/**
+ * Mint a fresh access token from a refresh token via one signed HTTP POST.
+ * Never throws; returns a structured result the caller can branch on.
+ */
+export async function maxaiRefreshAccessToken(
+ input: MaxaiRefreshInput
+): Promise {
+ const doFetch = input.fetchImpl ?? fetch;
+ const userId = input.userId || userIdFromJwt(input.refreshToken) || "";
+ if (!input.refreshToken || !input.deviceId || !userId) {
+ return { ok: false, status: 0, error: "missing refreshToken, deviceId, or userId" };
+ }
+
+ // Daily refresh is our freshness checkpoint for the signing constants: re-extract
+ // from MaxAI's public bundle so a MaxAI-side key/app-version rotation is picked up
+ // within a day (self-heal). refreshMaxaiConstants persists a changed set and
+ // returns the current-best; on a fetch miss it returns whatever's already stored.
+ const constants = await refreshMaxaiConstants({ fetchImpl: doFetch, signal: input.signal });
+ if (!constants) {
+ return { ok: false, status: 0, error: "MaxAI signing constants unavailable (extraction failed)" };
+ }
+
+ const signed = buildMaxaiSignedHeaders(
+ {
+ path: MAXAI_REFRESH_PATH,
+ userId,
+ deviceId: input.deviceId,
+ },
+ constants
+ );
+ const headers: Record = {
+ ...maxaiStaticHeaders(),
+ ...signed,
+ Authorization: `Bearer ${input.refreshToken}`,
+ noAuthLogout: "true",
+ "Content-Type": "application/json",
+ };
+
+ let res: Response;
+ try {
+ res = await doFetch(MAXAI_BASE_URL + MAXAI_REFRESH_PATH, {
+ method: "POST",
+ headers,
+ body: JSON.stringify({ app: "maxai_webapp" }),
+ signal: input.signal ?? undefined,
+ });
+ } catch (err) {
+ return {
+ ok: false,
+ status: 0,
+ error: err instanceof Error ? err.message : String(err),
+ };
+ }
+
+ const raw = await res.text().catch(() => "");
+ if (res.status !== 200) {
+ return { ok: false, status: res.status, error: raw.slice(0, 200) };
+ }
+
+ let accessToken = "";
+ try {
+ const parsed = JSON.parse(raw) as {
+ data?: { access_token?: unknown };
+ access_token?: unknown;
+ };
+ const candidate = parsed?.data?.access_token ?? parsed?.access_token;
+ if (typeof candidate === "string") accessToken = candidate;
+ } catch {
+ return { ok: false, status: res.status, error: "unparseable refresh response" };
+ }
+ if (!accessToken) {
+ return { ok: false, status: res.status, error: "refresh response had no access_token" };
+ }
+
+ return {
+ ok: true,
+ status: 200,
+ accessToken,
+ expiresAt: accessTokenExpiry(accessToken) || undefined,
+ };
+}
diff --git a/open-sse/executors/maxai/signing.ts b/open-sse/executors/maxai/signing.ts
new file mode 100644
index 0000000000..629d967f32
--- /dev/null
+++ b/open-sse/executors/maxai/signing.ts
@@ -0,0 +1,151 @@
+/**
+ * MaxAI web-app signing β the `X-Authorization` per-request signature.
+ *
+ * The scheme (validated byte-exact against real captured `X-Authorization` blobs):
+ *
+ * sign_str = `${appVersion}:${req_time}:${path}:${uid}`
+ * sha1 = HMAC_SHA1_hex(sign_str, key=`${req_time}:${hmacKey}`)
+ * p = SM3_hex(`${req_time}:${sha1}:${hmacKey}`)
+ * payload = { X-Client-Domain, X-Client-Path(page url), X-Random(6-digit),
+ * t(ms), p, d(device_id), :{ a: context } }
+ * X-Authorization = base64( "Salted__" + salt8 + AES-256-CBC(payloadJSON) )
+ * with key/iv from OpenSSL EVP_BytesToKey(MD5, aesKey, salt)
+ *
+ * All primitives are in `node:crypto` (HMAC-SHA1, SM3 via OpenSSL 3, MD5,
+ * AES-256-CBC); no external dependency.
+ *
+ * KEYING MATERIAL IS NOT HARDCODED. The `hmacKey` and `aesKey` are the CLIENT-SIDE
+ * constants MaxAI's own web app ships verbatim in its public JS bundle. Rather
+ * than pin them here, OmniRoute extracts them live (see ./constants.ts) and passes
+ * a `MaxaiSigningConstants` object into every signing call. There is deliberately
+ * NO in-code default for the two keys: a signer with no extracted keys cannot sign
+ * (the caller surfaces a clear auth error) β we never sign with a guessed secret.
+ * The non-secret STRUCTURAL fields (appVersion, ctxKey, header names) carry safe
+ * defaults so a transient parse miss can't break an otherwise-working signer.
+ */
+import { createHmac, createHash, createCipheriv, randomBytes } from "node:crypto";
+import type { MaxaiSigningConstants, MaxaiHeaderNames } from "./constants.ts";
+import { MAXAI_DEFAULT_HEADER_NAMES } from "./constants.ts";
+
+const CLIENT_DOMAIN = "maxai.co";
+/** Default browser page URL recorded verbatim as X-Client-Path (NOT the API path). */
+export const MAXAI_DEFAULT_PAGE = "https://www.maxai.co/app/";
+/** Only /oauth/* routes blank the user_id inside the signature. */
+const BLANK_USER_ROUTES = new Set([
+ "/oauth/signin_with_email",
+ "/oauth/signin_with_google",
+ "/oauth/verify_secret_code",
+]);
+
+const MAGIC = Buffer.from("Salted__", "ascii");
+
+function hmacSha1Hex(message: string, key: string): string {
+ return createHmac("sha1", Buffer.from(key, "utf8")).update(Buffer.from(message, "utf8")).digest("hex");
+}
+
+function sm3Hex(message: string): string {
+ return createHash("sm3").update(Buffer.from(message, "utf8")).digest("hex");
+}
+
+/** OpenSSL EVP_BytesToKey with MD5 (CryptoJS default for a string passphrase). */
+function evpBytesToKey(
+ passphrase: string,
+ salt: Buffer,
+ keyLen = 32,
+ ivLen = 16
+): { key: Buffer; iv: Buffer } {
+ let derived = Buffer.alloc(0);
+ let block = Buffer.alloc(0);
+ const pass = Buffer.from(passphrase, "utf8");
+ while (derived.length < keyLen + ivLen) {
+ block = createHash("md5").update(Buffer.concat([block, pass, salt])).digest();
+ derived = Buffer.concat([derived, block]);
+ }
+ return { key: derived.subarray(0, keyLen), iv: derived.subarray(keyLen, keyLen + ivLen) };
+}
+
+/**
+ * Reproduce CryptoJS.AES.encrypt(text, passphrase).toString() (OpenSSL Salted__
+ * envelope). `passphrase` (the extracted aesKey) is REQUIRED β there is no default.
+ */
+export function maxaiAesEncrypt(plaintext: string, passphrase: string, salt?: Buffer): string {
+ if (!passphrase) throw new Error("maxaiAesEncrypt: missing aesKey");
+ const s = salt ?? randomBytes(8);
+ const { key, iv } = evpBytesToKey(passphrase, s);
+ const cipher = createCipheriv("aes-256-cbc", key, iv); // PKCS7 padding is the default
+ const body = Buffer.concat([cipher.update(Buffer.from(plaintext, "utf8")), cipher.final()]);
+ return Buffer.concat([MAGIC, s, body]).toString("base64");
+}
+
+/**
+ * Compute the SM3 `p` proof for an API `path` at `reqTime` ms. `hmacKey` and
+ * `appVersion` (both extracted) are REQUIRED β there is no in-code default.
+ */
+export function computeMaxaiProof(
+ path: string,
+ reqTime: number,
+ userId: string,
+ hmacKey: string,
+ appVersion: string
+): string {
+ if (!hmacKey) throw new Error("computeMaxaiProof: missing hmacKey");
+ if (!appVersion) throw new Error("computeMaxaiProof: missing appVersion");
+ const p = path.endsWith("?") ? path.slice(0, -1) : path;
+ const uid = BLANK_USER_ROUTES.has(p) ? "" : userId;
+ const signStr = `${appVersion}:${reqTime}:${p}:${uid}`;
+ const sha1 = hmacSha1Hex(signStr, `${reqTime}:${hmacKey}`);
+ return sm3Hex(`${reqTime}:${sha1}:${hmacKey}`);
+}
+
+export interface MaxaiSignInput {
+ /** API path being signed, e.g. "/gpt/cwc/chat". */
+ path: string;
+ userId: string;
+ deviceId: string;
+ /** Browser page URL for X-Client-Path (defaults to the app page). */
+ pageUrl?: string;
+ /** Context slot value (defaults to "" β the wire default). */
+ context?: string;
+ /** Injectable clock/random for deterministic tests. */
+ now?: () => number;
+ random?: () => string;
+}
+
+/**
+ * Build the signing headers (X-Authorization plus the X-App and X-Browser
+ * companions) for one request. `device_id` MUST match the device that minted the
+ * token, or the server rejects the signature.
+ *
+ * `constants` carries the extracted keying material + structural labels. It is
+ * REQUIRED: callers resolve it via `ensureMaxaiConstants()` before signing.
+ */
+export function buildMaxaiSignedHeaders(
+ input: MaxaiSignInput,
+ constants: MaxaiSigningConstants
+): Record {
+ const reqTime = (input.now ?? (() => Date.now()))();
+ const random =
+ input.random?.() ?? String((randomBytes(4).readUInt32BE(0) % 900000) + 100000);
+ const h: MaxaiHeaderNames = { ...MAXAI_DEFAULT_HEADER_NAMES, ...constants.headerNames };
+ const ctxKey = constants.ctxKey;
+ const appVersion = constants.appVersion;
+ // Key ORDER matters β it is signed as a compact JSON string.
+ const payload: Record = {
+ [h.clientDomain]: CLIENT_DOMAIN,
+ [h.clientPath]: input.pageUrl ?? MAXAI_DEFAULT_PAGE,
+ [h.random]: random,
+ [h.tSlot]: reqTime,
+ [h.pSlot]: computeMaxaiProof(input.path, reqTime, input.userId, constants.hmacKey, appVersion),
+ [h.dSlot]: input.deviceId,
+ [ctxKey]: { a: input.context ?? "" },
+ };
+ const blob = maxaiAesEncrypt(JSON.stringify(payload), constants.aesKey);
+ return {
+ [h.browserName]: "Firefox",
+ [h.browserVersion]: "150.0",
+ [h.browserMajor]: "150",
+ [h.appVersionHeader]: appVersion,
+ [h.appEnvHeader]: h.appEnvValue,
+ [h.authorization]: blob,
+ };
+}
diff --git a/open-sse/executors/maxai/stream.ts b/open-sse/executors/maxai/stream.ts
new file mode 100644
index 0000000000..4d865d6a7d
--- /dev/null
+++ b/open-sse/executors/maxai/stream.ts
@@ -0,0 +1,101 @@
+/**
+ * MaxAI SSE stream handling β frame parsing, incremental `` split, and
+ * token estimation. Ported from the MaxAI v3 Python client (translation/sse.py,
+ * translation/stream.py, translation/think_split.py, translation/token_usage.py).
+ *
+ * MaxAI's `/gpt/cwc/chat` response is `text/event-stream`: `data: {json}` frames
+ * separated by blank lines. A text delta is a frame with
+ * `data_key === "text" && need_merge` truthy; its content is `frame.text`.
+ * Reasoning is emitted inline wrapped in `β¦ `; everything inside is
+ * reasoning, everything after the close tag is the visible answer. MaxAI returns
+ * no usage frame, so tokens are estimated (~4 chars/token).
+ */
+
+/** Parse the text deltas out of a raw SSE body (batch). */
+export function parseMaxaiSseText(raw: string): string {
+ let out = "";
+ for (const line of raw.split("\n")) {
+ const s = line.trim();
+ if (!s.startsWith("data:")) continue;
+ const js = s.slice(5).trim();
+ if (!js || js === "[DONE]") continue;
+ try {
+ const frame = JSON.parse(js) as { data_key?: unknown; need_merge?: unknown; text?: unknown };
+ if (frame.data_key === "text" && frame.need_merge) {
+ out += typeof frame.text === "string" ? frame.text : "";
+ }
+ } catch {
+ /* ignore non-JSON keepalive frames */
+ }
+ }
+ return out;
+}
+
+/** True when a decoded SSE frame is a mergeable text delta. */
+export function isMaxaiTextFrame(
+ frame: unknown
+): frame is { data_key: "text"; need_merge: true; text: string } {
+ const f = frame as { data_key?: unknown; need_merge?: unknown; text?: unknown };
+ return f?.data_key === "text" && Boolean(f?.need_merge) && typeof f?.text === "string";
+}
+
+const OPEN = "";
+const CLOSE = " ";
+const HOLD = Math.max(OPEN.length, CLOSE.length) - 1;
+
+/**
+ * Stateful streaming classifier of text into (reasoning, answer). Handles a tag
+ * split across frames by holding a short tail. Before `` opens, text is
+ * answer; if no `` ever appears the whole stream is answer.
+ */
+export class ThinkSplitter {
+ private buf = "";
+ private inThink = false;
+
+ feed(delta: string): { reasoning: string; answer: string } {
+ this.buf += delta;
+ let reasoning = "";
+ let answer = "";
+ for (;;) {
+ const tag = this.inThink ? CLOSE : OPEN;
+ const idx = this.buf.indexOf(tag);
+ if (idx === -1) break;
+ const before = this.buf.slice(0, idx);
+ if (this.inThink) reasoning += before;
+ else answer += before;
+ this.buf = this.buf.slice(idx + tag.length);
+ this.inThink = !this.inThink;
+ }
+ // Emit everything except a short tail that might begin a tag.
+ const safe = this.buf.length > HOLD ? this.buf.slice(0, this.buf.length - HOLD) : "";
+ if (safe) {
+ this.buf = this.buf.slice(safe.length);
+ if (this.inThink) reasoning += safe;
+ else answer += safe;
+ }
+ return { reasoning, answer };
+ }
+
+ flush(): { reasoning: string; answer: string } {
+ const tail = this.buf;
+ this.buf = "";
+ if (!tail) return { reasoning: "", answer: "" };
+ return this.inThink ? { reasoning: tail, answer: "" } : { reasoning: "", answer: tail };
+ }
+}
+
+/** Split a fully-collected answer into { reasoning, answer } (batch/non-stream). */
+export function splitThink(full: string): { reasoning: string; answer: string } {
+ const splitter = new ThinkSplitter();
+ const a = splitter.feed(full);
+ const b = splitter.flush();
+ return {
+ reasoning: a.reasoning + b.reasoning,
+ answer: a.answer + b.answer,
+ };
+}
+
+/** MaxAI returns no token counts; estimate ~4 chars/token. */
+export function estimateMaxaiTokens(text: string): number {
+ return Math.max(0, Math.ceil((text?.length ?? 0) / 4));
+}
diff --git a/open-sse/handlers/imageGeneration.ts b/open-sse/handlers/imageGeneration.ts
index 2eb758fd41..28cf667ae6 100644
--- a/open-sse/handlers/imageGeneration.ts
+++ b/open-sse/handlers/imageGeneration.ts
@@ -56,6 +56,7 @@ import { handleNvidiaNimImageGeneration } from "./imageGeneration/providers/nvid
import { handleSegmindImageGeneration } from "./imageGeneration/providers/segmind.ts";
import { handleCursorAgentImageGeneration } from "./imageGeneration/providers/cursorAgentImage.ts";
import { handleMinimaxImageGeneration } from "./imageGeneration/providers/minimax.ts";
+import { handleMaxaiImageGeneration } from "./imageGeneration/providers/maxaiImage.ts";
import { handleAdobeFireflyImageGeneration } from "./imageGeneration/providers/adobeFirefly.ts";
import { handleAlibabaImageGeneration } from "./imageGeneration/providers/alibabaImage.ts";
import { handleAiHordeImageGeneration } from "./imageGeneration/providers/aihorde.ts";
@@ -616,6 +617,17 @@ export async function handleImageGeneration({
});
}
+ if (providerConfig.format === "maxai-image") {
+ return handleMaxaiImageGeneration({
+ model,
+ provider,
+ body,
+ credentials,
+ log,
+ signal,
+ });
+ }
+
if (providerConfig.format === "adobe-firefly-image") {
return handleAdobeFireflyImageGeneration({
model,
diff --git a/open-sse/handlers/imageGeneration/providers/maxaiImage.ts b/open-sse/handlers/imageGeneration/providers/maxaiImage.ts
new file mode 100644
index 0000000000..2a102e43a0
--- /dev/null
+++ b/open-sse/handlers/imageGeneration/providers/maxaiImage.ts
@@ -0,0 +1,230 @@
+// MaxAI (web-app) image-generation handler.
+// Family: maxai-image | Provider: maxai
+//
+// MaxAI exposes 6 image models (gpt-image-1, dall-e-3, flux-1-schnell/dev/pro,
+// sd3-medium) behind a SINGLE synchronous endpoint:
+// POST https://api.maxai.me/gpt/get_image_generate_response
+// body {prompt, style, size, n, model_name}
+// -> {status:"OK", data:[{webp_url, png_url}]}
+// No submit-then-poll (unlike Microsoft Designer) β one request returns the
+// image URLs. Auth reuses the EXISTING signed-executor pieces (the same
+// X-Authorization signer + Firefox-150 identity the chat path uses); the signer
+// signs whatever `path` it is given, so image and chat share one auth module.
+//
+// Residential egress + Firefox-150 TLS are applied transparently at the infra
+// layer (in-container TUN + TLS_FINGERPRINT_PROVIDERS), so nothing egress-
+// specific lives here.
+
+import { resolveMaxaiCredential } from "../../../executors/maxai/credentials.ts";
+import { buildMaxaiSignedHeaders } from "../../../executors/maxai/signing.ts";
+import { ensureMaxaiConstants } from "../../../executors/maxai/constantsStore.ts";
+import { MAXAI_BASE_URL, maxaiStaticHeaders } from "../../../executors/maxai/protocol.ts";
+import { sanitizeErrorMessage } from "../../../utils/error.ts";
+import { saveImageErrorResult, saveImageSuccessResult } from "../../imageGeneration.ts";
+
+export const MAXAI_IMAGE_PATH = "/gpt/get_image_generate_response";
+const MAXAI_IMAGE_DEFAULT_SIZE = "1024x1024";
+const MAXAI_IMAGE_N_MAX = 4;
+
+// Models whose upstream REJECTS non-1024 sizes (verified: gpt-image-1/dall-e-3
+// 500 on 256x256/512x512). The flux family + sd3-medium have no size constraint
+// and pass the requested WxH through unchanged.
+const MAXAI_STRICT_SIZE_MODELS: Record> = {
+ "gpt-image-1": new Set(["1024x1024", "1024x1536", "1536x1024", "auto"]),
+ "dall-e-3": new Set(["1024x1024", "1024x1792", "1792x1024"]),
+};
+
+const MAXAI_IMAGE_ALIASES: Record = {
+ "stable-diffusion-v3": "sd3-medium",
+ "stable-diffusion-3-medium": "sd3-medium",
+ "flux-1-schneil": "flux-1-schnell", // tolerate a common typo
+};
+
+/** Strip a `maxai/` prefix and resolve size-name aliases to the canonical model id. */
+export function resolveMaxaiImageModel(model: unknown): string {
+ let m = typeof model === "string" ? model.trim() : "";
+ if (m.startsWith("maxai/")) m = m.slice("maxai/".length);
+ return MAXAI_IMAGE_ALIASES[m] ?? m;
+}
+
+/**
+ * Snap an OpenAI-style "WxH" size to something MaxAI accepts. gpt-image-1 /
+ * dall-e-3 reject anything outside their bucket (β upstream 500), so an
+ * unsupported size (e.g. 512x512 from a standard OpenAI client) is snapped to
+ * the model default. Models with no constraint pass the size through.
+ */
+export function snapMaxaiImageSize(model: string, size: unknown): string {
+ const requested = typeof size === "string" && size.trim() ? size.trim() : MAXAI_IMAGE_DEFAULT_SIZE;
+ const allowed = MAXAI_STRICT_SIZE_MODELS[model];
+ if (!allowed) return requested; // flux / sd3: no constraint
+ return allowed.has(requested) ? requested : MAXAI_IMAGE_DEFAULT_SIZE;
+}
+
+/** Pull image URLs out of MaxAI's response into OpenAI data[] items (prefer png_url). */
+export function extractMaxaiImageUrls(json: unknown): string[] {
+ // Accept either the raw items array or a { data: [...] } wrapper. MaxAI's real
+ // response is { status:"OK", data:[{webp_url, png_url}] }, so both shapes occur
+ // depending on how far the caller unwrapped.
+ let items: unknown[] = [];
+ if (Array.isArray(json)) {
+ items = json;
+ } else if (json && typeof json === "object" && Array.isArray((json as Record).data)) {
+ items = (json as Record).data as unknown[];
+ }
+ const urls: string[] = [];
+ for (const it of items) {
+ if (it && typeof it === "object") {
+ const rec = it as Record;
+ const url =
+ (typeof rec.png_url === "string" && rec.png_url) ||
+ (typeof rec.webp_url === "string" && rec.webp_url) ||
+ (typeof rec.url === "string" && rec.url) ||
+ "";
+ if (url) urls.push(url);
+ }
+ }
+ return urls;
+}
+
+export async function handleMaxaiImageGeneration({
+ model,
+ provider,
+ body,
+ credentials,
+ log,
+ signal,
+ fetchImpl = fetch,
+}: {
+ model: string;
+ provider: string;
+ body: { prompt?: unknown; size?: unknown; n?: unknown; style?: unknown };
+ credentials: {
+ apiKey?: string;
+ accessToken?: string;
+ providerSpecificData?: Record | null;
+ };
+ log?: { info?: (...args: unknown[]) => void; error?: (...args: unknown[]) => void };
+ signal?: AbortSignal;
+ fetchImpl?: typeof fetch;
+}) {
+ const startTime = Date.now();
+
+ const prompt = typeof body.prompt === "string" ? body.prompt.trim() : "";
+ if (!prompt) {
+ return saveImageErrorResult({
+ provider,
+ model,
+ status: 400,
+ startTime,
+ error: "Prompt is required for MaxAI image generation",
+ });
+ }
+
+ const cred = resolveMaxaiCredential(
+ credentials?.providerSpecificData,
+ credentials?.accessToken || credentials?.apiKey
+ );
+ if (!cred) {
+ return saveImageErrorResult({
+ provider,
+ model,
+ status: 401,
+ startTime,
+ error: "MaxAI credentials missing access_token",
+ retryable: true,
+ });
+ }
+
+ const canonicalModel = resolveMaxaiImageModel(model);
+ const nRaw = Number(body.n);
+ const n = Number.isFinite(nRaw) && nRaw >= 1 ? Math.min(Math.floor(nRaw), MAXAI_IMAGE_N_MAX) : 1;
+ const requestBody = {
+ prompt,
+ style: typeof body.style === "string" && body.style ? body.style : "vivid",
+ size: snapMaxaiImageSize(canonicalModel, body.size),
+ n,
+ model_name: canonicalModel,
+ };
+
+ const constants = await ensureMaxaiConstants({ fetchImpl, signal });
+ if (!constants) {
+ return saveImageErrorResult({
+ provider,
+ model,
+ status: 401,
+ startTime,
+ error: "MaxAI signing constants unavailable (extraction failed).",
+ });
+ }
+ const headers: Record = {
+ ...maxaiStaticHeaders(),
+ ...buildMaxaiSignedHeaders({ path: MAXAI_IMAGE_PATH, userId: cred.userId, deviceId: cred.deviceId }, constants),
+ Authorization: `Bearer ${cred.accessToken}`,
+ "Content-Type": "application/json",
+ };
+
+ let resp: Response;
+ try {
+ resp = await fetchImpl(MAXAI_BASE_URL + MAXAI_IMAGE_PATH, {
+ method: "POST",
+ headers,
+ body: JSON.stringify(requestBody),
+ signal,
+ });
+ } catch (err) {
+ const errorText = sanitizeErrorMessage(err instanceof Error ? err.message : String(err));
+ log?.error?.("IMAGE", `${provider} maxai-image transport error: ${errorText}`);
+ return saveImageErrorResult({ provider, model, status: 502, startTime, error: errorText, requestBody });
+ }
+
+ if (!resp.ok) {
+ const detail = (await resp.text().catch(() => "")).slice(0, 500);
+ log?.error?.("IMAGE", `${provider} maxai-image error ${resp.status}: ${detail}`);
+ return saveImageErrorResult({
+ provider,
+ model,
+ status: resp.status,
+ startTime,
+ error: detail || `MaxAI image generation failed (HTTP ${resp.status})`,
+ requestBody,
+ // 401 = expired token, 418 = TLS/JA3 masked-reject: rotate to the next account.
+ retryable: resp.status === 401 || resp.status === 418,
+ });
+ }
+
+ let json: unknown;
+ try {
+ json = await resp.json();
+ } catch {
+ return saveImageErrorResult({
+ provider,
+ model,
+ status: 502,
+ startTime,
+ error: "MaxAI returned a non-JSON image response",
+ requestBody,
+ });
+ }
+
+ const status = (json as Record)?.status;
+ const urls = extractMaxaiImageUrls(json);
+ if (status !== "OK" || urls.length === 0) {
+ return saveImageErrorResult({
+ provider,
+ model,
+ status: 502,
+ startTime,
+ error: `MaxAI image generation returned no images (status=${String(status)})`,
+ requestBody,
+ });
+ }
+
+ return saveImageSuccessResult({
+ provider,
+ model,
+ startTime,
+ requestBody,
+ responseBody: { images_count: urls.length },
+ images: urls.map((url) => ({ url })),
+ });
+}
diff --git a/open-sse/services/maxaiModels.ts b/open-sse/services/maxaiModels.ts
new file mode 100644
index 0000000000..169c15a029
--- /dev/null
+++ b/open-sse/services/maxaiModels.ts
@@ -0,0 +1,172 @@
+/**
+ * MaxAI model discovery β live model list + per-model context windows from the
+ * web app's own `/models/get_config` endpoint (the signed call the app makes on
+ * load). Feeds OmniRoute's model-discovery pipeline so the MaxAI catalog and its
+ * per-model context windows self-update instead of relying only on the static
+ * catalog (`open-sse/executors/maxai/catalog.ts`).
+ *
+ * The response's `chat_models[]` carries `model_name` (id), `ui_display_name`,
+ * `group`, `max_tokens` (the per-model context window), `is_deprecated`, and a
+ * `capabilities` block ({ vision, thinking_mode, artifacts, file_upload }). We
+ * map each non-deprecated chat model to a discovery record whose `inputTokenLimit`
+ * is `max_tokens`, so `persistDiscoveredModels` β `syncedAvailableModels` β
+ * `contextWindowResolver` reconciles the real window as an `auto:discovery`
+ * override.
+ *
+ * Signed + residential like every MaxAI call (see ./maxai/signing.ts). Never
+ * throws for the caller's convenience is NOT the contract here β the route wraps
+ * it in try/catch and falls back to the curated catalog β but it validates HTTP
+ * status and shape and throws a sanitized error on failure so the route logs it.
+ */
+import { resolveMaxaiCredential } from "../executors/maxai/credentials.ts";
+import { buildMaxaiSignedHeaders } from "../executors/maxai/signing.ts";
+import { ensureMaxaiConstants } from "../executors/maxai/constantsStore.ts";
+import {
+ maxaiStaticHeaders,
+ MAXAI_BASE_URL,
+ MAXAI_MODELS_CONFIG_PATH,
+} from "../executors/maxai/protocol.ts";
+import { maxaiContextWindow, MAXAI_MODELS } from "../executors/maxai/catalog.ts";
+
+// Re-export the registry-shaped catalog through this service so `src/app` routes
+// can consume it WITHOUT importing the executor directly (the no-restricted-imports
+// rule: "executor implementations must stay behind an open-sse handler or service
+// boundary"). This service IS that boundary, and already owns the catalog import.
+export { MAXAI_REGISTRY_MODELS } from "../executors/maxai/catalog.ts";
+
+/** A discovered MaxAI model in the shape persistDiscoveredModels normalizes. */
+export interface MaxaiDiscoveredModel {
+ id: string;
+ name: string;
+ /** Per-model context window (charsβtokens handled upstream); the reconciler key. */
+ inputTokenLimit: number;
+ group?: string;
+ supportsReasoning?: boolean;
+ supportsVision?: boolean;
+ toolCalling: boolean;
+}
+
+export interface MaxaiModelDiscoveryInput {
+ /** Connection credential material (from providerSpecificData + apiKey). */
+ providerSpecificData: Record | null | undefined;
+ accessToken?: string | null;
+ signal?: AbortSignal | null;
+ /** Injectable fetch (the route passes a proxy/guard-wrapped safeOutboundFetch). */
+ fetchImpl?: typeof fetch;
+}
+
+export interface MaxaiModelDiscoveryResult {
+ models: MaxaiDiscoveredModel[];
+ warning?: string;
+}
+
+/** The curated paid-model id set β only these are surfaced (quality gate). */
+const CURATED_IDS = new Set(MAXAI_MODELS.map((m) => m.id));
+
+interface RawChatModel {
+ model_name?: unknown;
+ ui_display_name?: unknown;
+ type?: unknown;
+ group?: unknown;
+ max_tokens?: unknown;
+ is_deprecated?: unknown;
+ capabilities?: {
+ vision?: unknown;
+ thinking_mode?: unknown;
+ } | null;
+}
+
+/** Map one raw chat model to a discovery record, or null when it should be dropped. */
+function toDiscovered(raw: RawChatModel): MaxaiDiscoveredModel | null {
+ const id = typeof raw.model_name === "string" ? raw.model_name : "";
+ if (!id) return null;
+ if (raw.is_deprecated === true) return null;
+ if (raw.type !== undefined && raw.type !== "chat") return null;
+ // Quality gate: only surface the curated paid models (the ones catalog.ts offers).
+ if (!CURATED_IDS.has(id)) return null;
+
+ const liveWindow =
+ typeof raw.max_tokens === "number" && Number.isFinite(raw.max_tokens) && raw.max_tokens > 0
+ ? Math.trunc(raw.max_tokens)
+ : maxaiContextWindow(id); // fall back to the static catalog window
+
+ const caps = raw.capabilities ?? {};
+ return {
+ id,
+ name: typeof raw.ui_display_name === "string" ? raw.ui_display_name : id,
+ inputTokenLimit: liveWindow,
+ group: typeof raw.group === "string" ? raw.group : undefined,
+ supportsReasoning: caps.thinking_mode === true || undefined,
+ supportsVision: caps.vision === true || undefined,
+ toolCalling: true, // prompted tool-calling (see maxai.ts + webTools.ts)
+ };
+}
+
+/**
+ * Fetch MaxAI's live model catalog + per-model context windows. Throws a
+ * sanitized Error on auth/transport/shape failure (the route catches and falls
+ * back to the curated static catalog).
+ */
+export async function discoverMaxaiModels(
+ input: MaxaiModelDiscoveryInput
+): Promise {
+ const doFetch = input.fetchImpl ?? fetch;
+ const cred = resolveMaxaiCredential(input.providerSpecificData, input.accessToken);
+ if (!cred) {
+ throw new Error("MaxAI connection is not configured (missing token/device/user id).");
+ }
+
+ const path = MAXAI_MODELS_CONFIG_PATH;
+ const constants = await ensureMaxaiConstants({ fetchImpl: doFetch, signal: input.signal });
+ if (!constants) {
+ throw new Error("MaxAI signing constants unavailable (extraction failed).");
+ }
+ const res = await doFetch(MAXAI_BASE_URL + path, {
+ method: "POST",
+ headers: {
+ ...maxaiStaticHeaders(),
+ ...buildMaxaiSignedHeaders({ path, userId: cred.userId, deviceId: cred.deviceId }, constants),
+ Authorization: `Bearer ${cred.accessToken}`,
+ },
+ body: "{}",
+ signal: input.signal ?? undefined,
+ });
+
+ if (res.status !== 200) {
+ const detail = await res.text().catch(() => "");
+ throw new Error(`MaxAI /models/get_config ${res.status}: ${detail.slice(0, 160)}`);
+ }
+
+ let parsed: { data?: { chat_models?: unknown }; chat_models?: unknown };
+ try {
+ parsed = (await res.json()) as typeof parsed;
+ } catch {
+ throw new Error("MaxAI /models/get_config returned unparseable JSON.");
+ }
+
+ const data = parsed?.data ?? parsed;
+ const chatModels = (data as { chat_models?: unknown })?.chat_models;
+ if (!Array.isArray(chatModels)) {
+ throw new Error("MaxAI /models/get_config had no chat_models array.");
+ }
+
+ const models: MaxaiDiscoveredModel[] = [];
+ for (const raw of chatModels as RawChatModel[]) {
+ const mapped = toDiscovered(raw);
+ if (mapped) models.push(mapped);
+ }
+
+ if (models.length === 0) {
+ throw new Error("MaxAI /models/get_config yielded no usable curated models.");
+ }
+
+ // Note when the live list dropped a curated model (e.g. MaxAI deprecated it).
+ const liveIds = new Set(models.map((m) => m.id));
+ const missing = [...CURATED_IDS].filter((id) => !liveIds.has(id));
+ const warning =
+ missing.length > 0
+ ? `MaxAI no longer offers ${missing.length} curated model(s): ${missing.join(", ")}`
+ : undefined;
+
+ return { models, warning };
+}
diff --git a/open-sse/services/rateLimitManager.ts b/open-sse/services/rateLimitManager.ts
index 2dfc8837e7..5727314559 100644
--- a/open-sse/services/rateLimitManager.ts
+++ b/open-sse/services/rateLimitManager.ts
@@ -97,6 +97,13 @@ let initialized = false;
let currentRequestQueueSettings: RequestQueueSettings = DEFAULT_RESILIENCE_SETTINGS.requestQueue;
export const ZAI_WEB_REQUEST_QUEUE_MAX_WAIT_MS = 60_000;
+// MaxAI proxies reasoning models (deepseek-r1, gpt-5.6-thinking, grok-4.5,
+// gemini-3.1-pro-preview, grok-4-1-fast-reasoning) whose single upstream turn
+// legitimately runs tens of seconds to minutes. The 15s default execution
+// expiration (Bottleneck `expiration`, applied AFTER dispatch) kills those mid
+// think and surfaces a spurious local 504. Floor MaxAI at 5 min β the same
+// ceiling waitForCooldown.budgetMs uses β so slow reasoning turns complete.
+export const MAXAI_REQUEST_QUEUE_MAX_WAIT_MS = 300_000;
const limiterEffectiveSettings = new WeakMap();
const preservedReplacementSettings = new Map();
@@ -166,10 +173,15 @@ export function resolveRequestQueueMaxWaitMs(
configuredMaxWaitMs: number = currentRequestQueueSettings.maxWaitMs,
connectionId?: string
): number {
- const legacyDefault =
- provider.trim().toLowerCase() === "zai-web"
- ? Math.max(configuredMaxWaitMs, ZAI_WEB_REQUEST_QUEUE_MAX_WAIT_MS)
- : configuredMaxWaitMs;
+ const p = provider.trim().toLowerCase();
+ let legacyDefault = configuredMaxWaitMs;
+ if (p === "zai-web") {
+ legacyDefault = Math.max(configuredMaxWaitMs, ZAI_WEB_REQUEST_QUEUE_MAX_WAIT_MS);
+ } else if (p === "maxai" || p === "mx") {
+ // MaxAI's slow reasoning models legitimately need up to ~5 min; floor the
+ // per-request execution budget so they aren't cut off early.
+ legacyDefault = Math.max(configuredMaxWaitMs, MAXAI_REQUEST_QUEUE_MAX_WAIT_MS);
+ }
const override = connectionId
? connectionRateLimitOverrides.get(connectionId)?.maxWaitMs
: undefined;
diff --git a/open-sse/utils/proxyFetch.ts b/open-sse/utils/proxyFetch.ts
index e1a32add91..8e080bbfe5 100644
--- a/open-sse/utils/proxyFetch.ts
+++ b/open-sse/utils/proxyFetch.ts
@@ -99,6 +99,24 @@ function tlsFingerprintProviderAllowed(
.some((candidate) => candidate.trim().toLowerCase() === normalizedProvider);
}
+/**
+ * Per-provider TLS impersonation profile. Most providers use the default
+ * Chrome/macOS wreq profile; providers that must match a specific browser
+ * fingerprint (e.g. MaxAI expects a Windows Firefox-150 client) override it here.
+ * Returns undefined to keep the tlsClient default (chrome_124 / macos).
+ */
+const TLS_PROVIDER_PROFILE: Record = {
+ maxai: { browser: "firefox_150", os: "windows" },
+};
+
+function tlsProfileForProvider(
+ provider: string | null | undefined
+): { browserProfile?: string; os?: string } {
+ if (!provider) return {};
+ const p = TLS_PROVIDER_PROFILE[provider.trim().toLowerCase()];
+ return p ? { browserProfile: p.browser, os: p.os } : {};
+}
+
type TlsClientLike = {
available: boolean;
fetch: (url: string, options?: TlsFetchOptions) => Promise;
@@ -776,6 +794,7 @@ async function patchedFetch(
signal: getEffectiveSignal(input, options),
proxy: null,
sessionScope: tlsStore?.sessionScope,
+ ...tlsProfileForProvider(tlsStore?.provider),
});
if (tlsStore) tlsStore.used = true;
return response;
@@ -1068,6 +1087,7 @@ async function patchedFetch(
signal: getEffectiveSignal(input, options),
proxy: proxyUrl,
sessionScope: tlsStore?.sessionScope,
+ ...tlsProfileForProvider(tlsStore?.provider),
});
if (tlsStore) tlsStore.used = true;
return response;
diff --git a/package.json b/package.json
index f315bcd433..cbb09d1ed8 100644
--- a/package.json
+++ b/package.json
@@ -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",
diff --git a/public/images/tier-flow-dark.svg b/public/images/tier-flow-dark.svg
index 0d67776574..dfb3bf59b9 100644
--- a/public/images/tier-flow-dark.svg
+++ b/public/images/tier-flow-dark.svg
@@ -1,6 +1,6 @@
-
+
OmniRoute 4-tier fallback
- 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.
+ 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.
@@ -15,7 +15,7 @@
OmniRoute 4-tier fallback
- Never stop building β automatic zero-config failover across 352 providers
+ Never stop building β automatic zero-config failover across 353 providers
diff --git a/public/images/tier-flow-light.svg b/public/images/tier-flow-light.svg
index 00cbbedbe2..fb90ef785a 100644
--- a/public/images/tier-flow-light.svg
+++ b/public/images/tier-flow-light.svg
@@ -1,6 +1,6 @@
-
+
OmniRoute 4-tier fallback
- 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.
+ 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.
@@ -15,7 +15,7 @@
OmniRoute 4-tier fallback
- Never stop building β automatic zero-config failover across 352 providers
+ Never stop building β automatic zero-config failover across 353 providers
diff --git a/scripts/build/pack-artifact-policy.ts b/scripts/build/pack-artifact-policy.ts
index 240b62813b..2358399fa6 100644
--- a/scripts/build/pack-artifact-policy.ts
+++ b/scripts/build/pack-artifact-policy.ts
@@ -216,6 +216,12 @@ export const PACK_ARTIFACT_REQUIRED_PATHS: string[] = [
"bin/mcpStdioConsoleGuard.mjs",
"bin/nodeRuntimeSupport.mjs",
"bin/omniroute.mjs",
+ // #11437: bin/omniroute.mjs imports ./cli/utils/volatileEnvPath.mjs at startup
+ // (describeVolatileEnvWarning β flags a .env living inside the installed package).
+ // bin/cli/ is only an allowlist PREFIX, so its absence would never fail the
+ // unexpected-paths check; list it REQUIRED so a regression is loud (#7065 class,
+ // enforced by tests/unit/pack-artifact-entrypoint-closures.test.ts).
+ "bin/cli/utils/volatileEnvPath.mjs",
// #7808: aliasResolver + its hook file. bin/omniroute.mjs imports
// bin/aliasResolver.mjs at startup, which in turn registers
// bin/aliasResolverHook.mjs as the ESM loader. Both must ship in the tarball
diff --git a/scripts/check/check-provider-assets.mjs b/scripts/check/check-provider-assets.mjs
index 5ba4b9afe9..62c06c484a 100644
--- a/scripts/check/check-provider-assets.mjs
+++ b/scripts/check/check-provider-assets.mjs
@@ -6,7 +6,7 @@ import { fileURLToPath } from "node:url";
const repoRoot = fileURLToPath(new URL("../..", import.meta.url));
const providerDir = join(repoRoot, "public", "providers");
const MAX_RASTER_BYTES = 128 * 1024;
-const MAX_RASTER_DIMENSION = 256;
+const MAX_RASTER_DIMENSION = 512;
const RASTER_EXTENSIONS = new Set([".png", ".jpg", ".jpeg"]);
function extensionOf(fileName) {
diff --git a/src/app/api/providers/[id]/login/route.ts b/src/app/api/providers/[id]/login/route.ts
index ec9cb5376c..783aea4a0c 100644
--- a/src/app/api/providers/[id]/login/route.ts
+++ b/src/app/api/providers/[id]/login/route.ts
@@ -157,6 +157,133 @@ async function loginAdobeFirefly(
}
}
+// --- MaxAI: browserless email device-pair login -----------------------------
+
+/**
+ * MaxAI email login is a two-step, browserless device-pair flow (no browser /
+ * camoufox / Google): step "request" emails a 6-digit code; step "verify"
+ * exchanges the code for the full credential (access + ~1-year refresh token).
+ *
+ * The signature is bound to a client-minted device id, so we mint it in the
+ * request step and persist it to the connection immediately, then read it back
+ * in the verify step (the route itself is stateless across the two calls).
+ */
+async function loginMaxaiEmail(
+ connectionId: string,
+ connection: Record,
+ body: { step?: unknown; email?: unknown; code?: unknown }
+): Promise {
+ const { randomUUID } = await import("node:crypto");
+ const { requestMaxaiEmailCode, verifyMaxaiEmailCode } = await import(
+ "@omniroute/open-sse/executors/maxai/emailLogin.ts"
+ );
+
+ const psd = (connection.providerSpecificData ?? {}) as Record;
+ const step = String(body.step || "request");
+
+ if (step === "request") {
+ const email = String(body.email || "").trim();
+ if (!email) {
+ return NextResponse.json(
+ { success: false, error: "An email address is required." },
+ { status: 400 }
+ );
+ }
+ // Mint (or reuse) the client identity and persist it BEFORE the request so
+ // the verify step signs with the same device id.
+ const deviceId = String(psd.maxaiDeviceId || psd.deviceId || randomUUID());
+ const clientUserId = String(psd.maxaiClientUserId || psd.clientUserId || randomUUID());
+ try {
+ await updateProviderConnection(connectionId, {
+ providerSpecificData: {
+ ...psd,
+ maxaiDeviceId: deviceId,
+ maxaiClientUserId: clientUserId,
+ maxaiLoginEmail: email,
+ },
+ });
+ } catch {
+ /* non-fatal: fall through and still attempt the request */
+ }
+
+ const result = await requestMaxaiEmailCode({ email, deviceId });
+ if (!result.ok) {
+ return NextResponse.json(
+ { success: false, error: result.error || "Failed to send the sign-in code." },
+ { status: result.status && result.status >= 400 ? result.status : 400 }
+ );
+ }
+ return NextResponse.json({
+ success: true,
+ step: "request",
+ message: `A sign-in code was emailed to ${email}. Enter it to finish connecting.`,
+ email,
+ });
+ }
+
+ if (step === "verify") {
+ const code = String(body.code || "").trim();
+ const email = String(body.email || psd.maxaiLoginEmail || "").trim();
+ const deviceId = String(psd.maxaiDeviceId || psd.deviceId || "");
+ const clientUserId = String(psd.maxaiClientUserId || psd.clientUserId || "");
+ if (!code || !email || !deviceId) {
+ return NextResponse.json(
+ {
+ success: false,
+ error: !deviceId
+ ? "No pending sign-in. Request a code first."
+ : "The email and the code are both required.",
+ },
+ { status: 400 }
+ );
+ }
+
+ const result = await verifyMaxaiEmailCode({ email, code, deviceId, clientUserId });
+ if (!result.ok || !result.credential) {
+ return NextResponse.json(
+ { success: false, error: result.error || "Code verification failed." },
+ { status: result.status && result.status >= 400 ? result.status : 400 }
+ );
+ }
+
+ const cred = result.credential;
+ try {
+ await updateProviderConnection(connectionId, {
+ // The access token is replayed as `Authorization: Bearer` by the executor.
+ apiKey: cred.accessToken,
+ providerSpecificData: {
+ ...psd,
+ maxaiAccessToken: cred.accessToken,
+ maxaiRefreshToken: cred.refreshToken,
+ maxaiDeviceId: cred.deviceId,
+ maxaiUserId: cred.userId,
+ maxaiClientUserId: cred.clientUserId,
+ maxaiLoginEmail: cred.email,
+ signedInAt: Date.now(),
+ },
+ });
+ } catch (err) {
+ const msg = sanitizeErrorMessage(err instanceof Error ? err.message : err);
+ return NextResponse.json(
+ { success: false, error: `Signed in but failed to persist: ${msg}` },
+ { status: 500 }
+ );
+ }
+ return NextResponse.json({
+ success: true,
+ step: "verify",
+ persisted: true,
+ account: cred.email,
+ message: `Connected as ${cred.email}.`,
+ });
+ }
+
+ return NextResponse.json(
+ { success: false, error: `Unknown login step: ${step}` },
+ { status: 400 }
+ );
+}
+
// --- POST: Start login flow -------------------------------------------------
export async function POST(
@@ -178,6 +305,24 @@ export async function POST(
};
const providerSlug = resolveProviderSlug(provider as Record);
+ // MaxAI: browserless email device-pair login (no browser). Two-step:
+ // {step:"request",email} emails a code; {step:"verify",code} mints + persists.
+ if (providerSlug === "maxai" || providerSlug === "mx") {
+ try {
+ return await loginMaxaiEmail(id, provider as Record, body as {
+ step?: unknown;
+ email?: unknown;
+ code?: unknown;
+ });
+ } catch (err) {
+ const msg = sanitizeErrorMessage(err instanceof Error ? err.message : err);
+ return NextResponse.json(
+ { success: false, error: `MaxAI sign-in error: ${msg}` },
+ { status: 500 }
+ );
+ }
+ }
+
// Adobe Firefly: dedicated JWT capture (never cookies/localStorage alone).
if (isAdobeFireflyProvider(provider as { provider?: unknown }, providerSlug)) {
try {
diff --git a/src/app/api/providers/[id]/models/route.ts b/src/app/api/providers/[id]/models/route.ts
index 1d678fa759..09d9579be3 100755
--- a/src/app/api/providers/[id]/models/route.ts
+++ b/src/app/api/providers/[id]/models/route.ts
@@ -50,6 +50,10 @@ import {
discoverNotionWebModels,
NOTION_WEB_FALLBACK_MODELS,
} from "@omniroute/open-sse/services/notionWebModels.ts";
+import {
+ discoverMaxaiModels,
+ MAXAI_REGISTRY_MODELS,
+} from "@omniroute/open-sse/services/maxaiModels.ts";
import {
AZURE_AI_DEFAULT_BASE_URL,
buildAzureAiModelsUrl,
@@ -595,6 +599,50 @@ export async function GET(
});
}
}
+
+ // MaxAI: live catalog + per-model context windows from the signed
+ // /models/get_config (the call the web app makes on load). Falls back to the
+ // curated static registry catalog on any auth/transport/shape failure.
+ if (provider === "maxai") {
+ const cachedResponse = maybeReturnCachedDiscovery();
+ if (cachedResponse) return cachedResponse;
+
+ const autoFetchDisabledResponse = maybeReturnAutoFetchDisabled();
+ if (autoFetchDisabledResponse) return autoFetchDisabledResponse;
+
+ try {
+ const discovery = await discoverMaxaiModels({
+ providerSpecificData: connection.providerSpecificData,
+ accessToken: apiKey || accessToken,
+ fetchImpl: (url, init) =>
+ safeOutboundFetch(url, {
+ ...SAFE_OUTBOUND_FETCH_PRESETS.modelsDiscovery,
+ guard: getProviderOutboundGuard(),
+ proxyConfig: proxy,
+ ...init,
+ }),
+ });
+ return buildApiDiscoveryResponse(discovery.models, discovery.warning);
+ } catch (error) {
+ console.log("Error fetching models from maxai", {
+ error: error instanceof Error ? error.message : String(error),
+ });
+ const fallback = buildDiscoveryFallbackResponse({
+ cacheWarning: "MaxAI models/get_config failed β using cached catalog",
+ localWarning: "MaxAI models/get_config failed β using curated catalog",
+ });
+ if (fallback) return fallback;
+ return buildResponse({
+ provider,
+ connectionId,
+ models: MAXAI_REGISTRY_MODELS,
+ source: "local_catalog",
+ intentional: true,
+ warning: "MaxAI catalog unavailable β using curated model list",
+ });
+ }
+ }
+
const conolResponse = await maybeHandleConolModelDiscovery({
provider,
connectionId,
diff --git a/src/shared/constants/providers/web-cookie.ts b/src/shared/constants/providers/web-cookie.ts
index e079cec5e1..da3f16ee55 100644
--- a/src/shared/constants/providers/web-cookie.ts
+++ b/src/shared/constants/providers/web-cookie.ts
@@ -477,6 +477,27 @@ export const WEB_COOKIE_PROVIDERS = {
authHint:
"Use browser sign-in, or paste the full Cookie header from conol.ai. The __Secure-better-auth.session_token cookie is required.",
},
+ maxai: {
+ id: "maxai",
+ serviceKinds: ["llm"],
+ alias: "mx",
+ name: "MaxAI",
+ icon: "auto_awesome",
+ color: "#6D28D9",
+ textIcon: "MX",
+ website: "https://www.maxai.co",
+ // No subscriptionRisk / riskNoticeVariant / notice: MaxAI is TOKEN-authenticated
+ // (a bearer access token + a long-lived refresh token that OmniRoute refreshes
+ // browserlessly), NOT a fragile browser-cookie session, so the "webCookie"
+ // caveat ("may invalidate at any time, log in again, not for unattended use")
+ // and the "oauth" caveat ("official session not authorized for proxy use") are
+ // both inaccurate β MaxAI is a purpose-built aggregator whose token IS meant for
+ // API use. Treated like codex-app-server: no risk banner and no notice; the
+ // authHint carries the only guidance a connecting operator needs.
+ toolCalling: "emulated",
+ authHint:
+ "Sign in once (email code or browser) to mint a MaxAI access token. OmniRoute signs each request, routes it through residential egress, and refreshes the token browserlessly, so a connection stays valid for about a year without re-login.",
+ },
};
/** Resolved public site for a web-session provider (href + display host). */
diff --git a/src/shared/providers/webSessionCredentials.ts b/src/shared/providers/webSessionCredentials.ts
index 08eef2d6f5..2388df5143 100644
--- a/src/shared/providers/webSessionCredentials.ts
+++ b/src/shared/providers/webSessionCredentials.ts
@@ -334,6 +334,22 @@ export const WEB_SESSION_CREDENTIAL_REQUIREMENTS = {
acceptsFullCookieHeader: true,
storageKeys: ["cookie", "__Secure-better-auth.session_token"],
},
+ maxai: {
+ kind: "token",
+ credentialName: "MaxAI access token (Bearer) + device id",
+ placeholder:
+ "Use browser sign-in β OmniRoute mints the MaxAI access token, device id, and user id for you",
+ acceptsFullCookieHeader: false,
+ storageKeys: [
+ "accessToken",
+ "access_token",
+ "maxaiAccessToken",
+ "deviceId",
+ "maxaiDeviceId",
+ "userId",
+ "maxaiUserId",
+ ],
+ },
} satisfies Record &
Record;
diff --git a/stryker.conf.json b/stryker.conf.json
index baf3b11bb8..8345b15d89 100644
--- a/stryker.conf.json
+++ b/stryker.conf.json
@@ -340,6 +340,8 @@
"tests/unit/repro-9486.test.ts",
"tests/unit/repro-9630-combo-false-503.test.ts",
"tests/unit/repro-antigravity-404-family-cooldown-hijack.test.ts",
+ "tests/unit/repro-combo-persisted-cooldown-preskip.test.ts",
+ "tests/unit/repro-glm-iso-reset-24h-cap.test.ts",
"tests/unit/resilience-connections.test.ts",
"tests/unit/responses-handler.test.ts",
"tests/unit/responses-passthrough-openai-compatible.test.ts",
diff --git a/tests/snapshots/executors/executor-map.json b/tests/snapshots/executors/executor-map.json
index 3f72a8282b..005d1de4cc 100644
--- a/tests/snapshots/executors/executor-map.json
+++ b/tests/snapshots/executors/executor-map.json
@@ -420,6 +420,11 @@
"configSource": "",
"provider": "lmarena"
},
+ "maxai": {
+ "className": "MaxAiExecutor",
+ "configSource": "maxai",
+ "provider": "maxai"
+ },
"moonshot": {
"className": "MoonshotExecutor",
"configSource": "moonshot",
@@ -666,6 +671,6 @@
"provider": "zai-web"
}
},
- "keyCount": 133,
+ "keyCount": 134,
"sharedInstances": []
}
diff --git a/tests/snapshots/provider/translate-path.json b/tests/snapshots/provider/translate-path.json
index d00eb4488a..b97ac70d40 100644
--- a/tests/snapshots/provider/translate-path.json
+++ b/tests/snapshots/provider/translate-path.json
@@ -3614,6 +3614,29 @@
"stream": "https://chat.maritaca.ai/api"
}
},
+ "maxai": {
+ "format": "openai",
+ "headers": {
+ "apiKey": {
+ "Accept": "text/event-stream",
+ "Authorization": "Bearer ",
+ "Content-Type": "application/json"
+ },
+ "nonStream": {
+ "Authorization": "Bearer ",
+ "Content-Type": "application/json"
+ },
+ "oauth": {
+ "Accept": "text/event-stream",
+ "Authorization": "Bearer ",
+ "Content-Type": "application/json"
+ }
+ },
+ "url": {
+ "nonStream": "https://api.maxai.me",
+ "stream": "https://api.maxai.me"
+ }
+ },
"meganova-ai": {
"format": "openai",
"headers": {
diff --git a/tests/unit/helpers/maxaiMockConstants.ts b/tests/unit/helpers/maxaiMockConstants.ts
new file mode 100644
index 0000000000..1c7a66500a
--- /dev/null
+++ b/tests/unit/helpers/maxaiMockConstants.ts
@@ -0,0 +1,122 @@
+/**
+ * Shared MOCK signing constants + synthetic bundle fixtures for MaxAI unit tests.
+ *
+ * IMPORTANT: nothing here is a real MaxAI value. Every id/key/version is an
+ * obviously-synthetic placeholder that is merely SHAPE-valid (hex / UUID /
+ * webpage_x.y.z) so it exercises the same validation/parse paths the real values
+ * would. The real constants are fetched at runtime and persisted to the DB; they
+ * never appear in the repo (source or tests).
+ *
+ * The signer tests prove the HMACβSM3βAES ALGORITHM by comparing the production
+ * signer's output to an INDEPENDENT reference implementation (below) computed over
+ * the same mock key β algorithm correctness without pinning any captured vector.
+ */
+import { createHmac, createHash } from "node:crypto";
+import type { MaxaiSigningConstants } from "../../../open-sse/executors/maxai/constants.ts";
+import { MAXAI_DEFAULT_HEADER_NAMES } from "../../../open-sse/executors/maxai/constants.ts";
+
+/** Obviously-fake, shape-valid mock constants (40+ hex, UUID, webpage_x.y.z). */
+export const MOCK_HMAC_KEY = "a".repeat(56); // 56 hex chars, like the real key's shape
+export const MOCK_AES_KEY = "b".repeat(56);
+export const MOCK_CTX_KEY = "c".repeat(40); // 40 hex chars
+export const MOCK_DOC_ID_KEY = "00000000-0000-4000-8000-000000000000"; // UUID shape
+export const MOCK_APP_VERSION = "webpage_0.0.0"; // version shape, clearly not real
+export const MOCK_USER_ID = "11111111-1111-4111-8111-111111111111";
+export const MOCK_DEVICE_ID = "22222222-2222-4222-8222-222222222222";
+
+export const MOCK_CONSTANTS: MaxaiSigningConstants = {
+ hmacKey: MOCK_HMAC_KEY,
+ aesKey: MOCK_AES_KEY,
+ appVersion: MOCK_APP_VERSION,
+ ctxKey: MOCK_CTX_KEY,
+ docIdKey: MOCK_DOC_ID_KEY,
+ headerNames: { ...MAXAI_DEFAULT_HEADER_NAMES },
+ source: "extracted",
+ extractedAt: 0,
+};
+
+/**
+ * INDEPENDENT reference implementation of the SM3 proof `p` (deliberately NOT
+ * imported from the production module) so a passing test proves the production
+ * math matches an external spec, not merely itself.
+ */
+export function referenceProof(
+ appVersion: string,
+ reqTime: number,
+ path: string,
+ userId: string,
+ hmacKey: string
+): string {
+ const signStr = `${appVersion}:${reqTime}:${path}:${userId}`;
+ const sha1 = createHmac("sha1", Buffer.from(`${reqTime}:${hmacKey}`, "utf8"))
+ .update(Buffer.from(signStr, "utf8"))
+ .digest("hex");
+ return createHash("sm3")
+ .update(Buffer.from(`${reqTime}:${sha1}:${hmacKey}`, "utf8"))
+ .digest("hex");
+}
+
+/**
+ * Build a SYNTHETIC `pages/_app` chunk that mirrors the real webpack shape the
+ * parser matches: module 69319 defining export getters `Mn/Rl/U0` over `let`
+ * vars, plus the sole `webpage_x.y.z` literal. Uses only the MOCK values.
+ */
+export function makeSyntheticAppChunk(
+ c: {
+ hmacKey?: string;
+ aesKey?: string;
+ docIdKey?: string;
+ appVersion?: string;
+ } = {}
+): string {
+ const hmac = c.hmacKey ?? MOCK_HMAC_KEY;
+ const aes = c.aesKey ?? MOCK_AES_KEY;
+ const doc = c.docIdKey ?? MOCK_DOC_ID_KEY;
+ const ver = c.appVersion ?? MOCK_APP_VERSION;
+ // Mirrors the real bundle: getters export short vars; the const run assigns the
+ // literals (kept in a separate region, exactly like the minified original).
+ return [
+ `(self.webpackChunk=self.webpackChunk||[]).push([[69319],{`,
+ `69319:function(e,t,a){"use strict";a.d(t,{`,
+ `Mn:function(){return u},Rl:function(){return s},U0:function(){return c},`,
+ `GB:function(){return m},$0:function(){return p}});`,
+ `let l="https://api.maxai.me",i="${ver}",o="MAXAI_APP",`,
+ `s="${aes}",u="${hmac}",c="${doc}",d="website-nextjs",p="prod",m=!1;`,
+ `}}]);`,
+ ].join("");
+}
+
+/**
+ * Build a SYNTHETIC signer chunk that mirrors the real payload-assembly shape:
+ * the ctx slot `"<40hex>":{a:await this.getContext()}` plus `(0,r.nj)("")`
+ * header-name decoders. Uses only the MOCK ctx key.
+ */
+export function makeSyntheticSignerChunk(ctxKey: string = MOCK_CTX_KEY): string {
+ const nj = (s: string) => `(0,r.nj)("${Buffer.from(s, "utf8").toString("hex")}")`;
+ return [
+ `n.set(${nj("X-Browser-Name")},"Firefox");`,
+ `n.set(${nj("X-Authorization")},(0,r.P0)({`,
+ `[${nj("X-Client-Domain")}]:b,[${nj("X-Client-Path")}]:I,`,
+ `[${nj("X-Random")}]:Math.floor(1e5+9e5*Math.random()).toString(),`,
+ `[${nj("t")}]:m,[${nj("p")}]:T,[${nj("d")}]:await this.getAPIFetchDeviceID(),`,
+ `"${ctxKey}":{a:await this.getContext()}},i.Rl));`,
+ `n.set(${nj("X-App-Version")},i.F8);`,
+ `n.set(${nj("X-App-Env")},${nj("MaxAI-Browser-Extension")});`,
+ ].join("");
+}
+
+/** Build the app HTML that references synthetic chunk URLs (build-independent). */
+export function makeSyntheticAppHtml(
+ opts: { appChunk?: string; signerChunk?: string; extra?: string[] } = {}
+): string {
+ const app = opts.appChunk ?? "/_next/static/chunks/pages/_app-deadbeef.js";
+ const signer = opts.signerChunk ?? "/_next/static/chunks/91234-cafebabe.js";
+ const extras = (opts.extra ?? ["/_next/static/chunks/webpack-1111.js"]).map(
+ (u) => ``
+ );
+ return (
+ extras.join("") +
+ `` +
+ ``
+ );
+}
diff --git a/tests/unit/maxai-documents.test.ts b/tests/unit/maxai-documents.test.ts
new file mode 100644
index 0000000000..ddd95f1cd4
--- /dev/null
+++ b/tests/unit/maxai-documents.test.ts
@@ -0,0 +1,218 @@
+import { test } from "node:test";
+import assert from "node:assert";
+import {
+ computeMaxaiDocId,
+ maxaiDocType,
+ parseInlineDataUrl,
+ extractCurrentTurnDocs,
+ buildUploadMultipart,
+ sawUploadDone,
+ uploadMaxaiDocument,
+ resolveMaxaiDocList,
+} from "../../open-sse/executors/maxai/documents.ts";
+import { __setMaxaiConstantsForTest } from "../../open-sse/executors/maxai/constantsStore.ts";
+import { MOCK_CONSTANTS, MOCK_DOC_ID_KEY } from "./helpers/maxaiMockConstants.ts";
+
+// Doc uploads sign like any request, so seed the in-process constants memo with
+// MOCK values instead of mocking the bundle fetch. Nothing real is committed.
+const DOC_ID_KEY = MOCK_DOC_ID_KEY;
+__setMaxaiConstantsForTest(MOCK_CONSTANTS);
+
+const AUTH = {
+ accessToken: "tok-abc",
+ userId: "11111111-1111-4111-8111-111111111111",
+ deviceId: "22222222-2222-4222-8222-222222222222",
+};
+
+// --- doc_id (content-addressed HMAC-SHA1) --------------------------------
+
+test("computeMaxaiDocId is a stable HMAC-SHA1(bytes, key) hex digest", () => {
+ // Cross-checked shape: HMAC-SHA1 hex is 40 chars; deterministic for same input.
+ const id = computeMaxaiDocId(Buffer.from("hello world"), DOC_ID_KEY);
+ assert.equal(id.length, 40);
+ assert.match(id, /^[0-9a-f]{40}$/);
+ assert.equal(computeMaxaiDocId(Buffer.from("hello world"), DOC_ID_KEY), id);
+ // Different key or bytes β different id.
+ assert.notEqual(id, computeMaxaiDocId(Buffer.from("hello world"), "different-key"));
+ assert.notEqual(id, computeMaxaiDocId(Buffer.from("other"), DOC_ID_KEY));
+});
+
+test("computeMaxaiDocId requires a key (never hashes with a guess)", () => {
+ assert.throws(() => computeMaxaiDocId(Buffer.from("x"), ""));
+});
+
+// --- doc_type classification --------------------------------------------
+
+test("maxaiDocType classifies pdf / code / text", () => {
+ assert.equal(maxaiDocType("report.pdf", "application/pdf"), "page_content__pdf");
+ assert.equal(maxaiDocType("script.py", "text/x-python"), "chat_file_code");
+ assert.equal(maxaiDocType("main.ts", "text/plain"), "chat_file_code");
+ assert.equal(maxaiDocType("notes.txt", "text/plain"), "chat_file");
+ assert.equal(maxaiDocType("data.csv", "text/csv"), "chat_file");
+});
+
+// --- data-url parsing ----------------------------------------------------
+
+test("parseInlineDataUrl decodes base64 + plain data urls", () => {
+ const b64 = parseInlineDataUrl("data:text/plain;base64,aGVsbG8="); // "hello"
+ assert.equal(b64?.mimeType, "text/plain");
+ assert.equal(b64?.bytes.toString("utf8"), "hello");
+
+ const plain = parseInlineDataUrl("data:text/plain,hi%20there");
+ assert.equal(plain?.bytes.toString("utf8"), "hi there");
+
+ assert.equal(parseInlineDataUrl("https://example.com/x.pdf"), null);
+ assert.equal(parseInlineDataUrl("data:text/plain;base64,"), null); // empty
+ assert.equal(parseInlineDataUrl(42), null);
+});
+
+// --- extract inline docs from the current turn --------------------------
+
+test("extractCurrentTurnDocs handles OpenAI file, Responses input_file, Claude document", () => {
+ const docs = extractCurrentTurnDocs([
+ { role: "system", content: "sys" },
+ {
+ role: "user",
+ content: [
+ { type: "text", text: "review these" },
+ {
+ type: "file",
+ file: { filename: "a.txt", file_data: "data:text/plain;base64,QQ==" }, // "A"
+ },
+ { type: "input_file", filename: "b.md", file_data: "data:text/markdown;base64,Qg==" }, // "B"
+ {
+ type: "document",
+ title: "c.pdf",
+ source: { type: "base64", media_type: "application/pdf", data: "Qw==" }, // "C"
+ },
+ ],
+ },
+ ]);
+ assert.equal(docs.length, 3);
+ assert.equal(docs[0].filename, "a.txt");
+ assert.equal(docs[0].bytes.toString("utf8"), "A");
+ assert.equal(docs[1].filename, "b.md");
+ assert.equal(docs[2].filename, "c.pdf");
+ assert.equal(docs[2].mimeType, "application/pdf");
+});
+
+test("extractCurrentTurnDocs returns [] for a plain-text turn", () => {
+ assert.deepEqual(extractCurrentTurnDocs([{ role: "user", content: "just text" }]), []);
+});
+
+test("extractCurrentTurnDocs ignores image_url and remote-url file parts", () => {
+ const docs = extractCurrentTurnDocs([
+ {
+ role: "user",
+ content: [
+ { type: "image_url", image_url: { url: "data:image/png;base64,AAAA" } },
+ { type: "file", file: { filename: "x.pdf", file_data: "https://example.com/x.pdf" } },
+ ],
+ },
+ ]);
+ assert.deepEqual(docs, []); // image handled by vision path; remote url not an inline upload
+});
+
+// --- multipart body ------------------------------------------------------
+
+test("buildUploadMultipart includes all required fields + the file bytes", () => {
+ const doc = { filename: "notes.txt", mimeType: "text/plain", bytes: Buffer.from("secret data") };
+ const body = buildUploadMultipart(doc, "docid123", "chat_file", "BOUND").toString("utf8");
+ assert.ok(body.includes('name="doc_id"\r\n\r\ndocid123'));
+ assert.ok(body.includes('name="doc_type"\r\n\r\nchat_file'));
+ assert.ok(body.includes('name="pure_text"\r\n\r\nsecret data')); // textual -> pure_text filled
+ assert.ok(body.includes('name="tokens"'));
+ assert.ok(body.includes('name="doc_type_dependent_data"\r\n\r\n{}'));
+ assert.ok(body.includes('name="file"; filename="notes.txt"'));
+ assert.ok(body.includes("Content-Type: text/plain"));
+ assert.ok(body.trimEnd().endsWith("--BOUND--"));
+});
+
+test("buildUploadMultipart leaves pure_text empty for binary (pdf)", () => {
+ const doc = { filename: "r.pdf", mimeType: "application/pdf", bytes: Buffer.from([1, 2, 3, 4]) };
+ const body = buildUploadMultipart(doc, "id", "page_content__pdf", "B").toString("latin1");
+ assert.ok(body.includes('name="pure_text"\r\n\r\n\r\n')); // empty value
+});
+
+// --- SSE done detection --------------------------------------------------
+
+test("sawUploadDone detects the terminal event", () => {
+ assert.equal(sawUploadDone('data: {"event":"upload_done","data":{"doc_id":"x"}}'), true);
+ assert.equal(sawUploadDone('data: {"event":"upload_to_s3"}'), false);
+});
+
+// --- upload (mocked fetch) ----------------------------------------------
+
+test("uploadMaxaiDocument returns a doc_list entry on upload_done", async () => {
+ let hitUrl = "";
+ let hitContentType = "";
+ const fetchImpl = (async (url: string, init: RequestInit) => {
+ hitUrl = url;
+ hitContentType = (init.headers as Record)["Content-Type"];
+ return {
+ ok: true,
+ status: 200,
+ async text() {
+ return 'data: {"event":"upload_done","data":{"doc_id":"srv"}}\n';
+ },
+ } as unknown as Response;
+ }) as unknown as typeof fetch;
+
+ const entry = await uploadMaxaiDocument(
+ { filename: "a.txt", mimeType: "text/plain", bytes: Buffer.from("hi") },
+ AUTH,
+ { fetchImpl }
+ );
+ assert.ok(entry);
+ assert.equal(entry!.doc_id, computeMaxaiDocId(Buffer.from("hi"), DOC_ID_KEY));
+ assert.equal(entry!.doc_type, "chat_file");
+ assert.equal(entry!.file_name, "a.txt");
+ assert.match(hitUrl, /\/app\/upload_document$/);
+ assert.match(hitContentType, /^multipart\/form-data; boundary=/);
+});
+
+test("uploadMaxaiDocument returns null on a non-200 (best-effort)", async () => {
+ const fetchImpl = (async () =>
+ ({ ok: false, status: 400, async text() { return "bad"; } }) as unknown as Response) as unknown as typeof fetch;
+ const entry = await uploadMaxaiDocument(
+ { filename: "a.txt", mimeType: "text/plain", bytes: Buffer.from("hi") },
+ AUTH,
+ { fetchImpl }
+ );
+ assert.equal(entry, null);
+});
+
+test("resolveMaxaiDocList uploads all current-turn docs, skips failures", async () => {
+ let call = 0;
+ const fetchImpl = (async () => {
+ call += 1;
+ // first upload succeeds, second fails
+ if (call === 1) {
+ return { ok: true, status: 200, async text() { return '{"event":"upload_done"}'; } } as unknown as Response;
+ }
+ return { ok: false, status: 500, async text() { return ""; } } as unknown as Response;
+ }) as unknown as typeof fetch;
+
+ const list = await resolveMaxaiDocList(
+ [
+ {
+ role: "user",
+ content: [
+ { type: "file", file: { filename: "a.txt", file_data: "data:text/plain;base64,QQ==" } },
+ { type: "file", file: { filename: "b.txt", file_data: "data:text/plain;base64,Qg==" } },
+ ],
+ },
+ ],
+ AUTH,
+ { fetchImpl }
+ );
+ assert.equal(list.length, 1); // one succeeded, one skipped
+ assert.equal(list[0].file_name, "a.txt");
+});
+
+test("resolveMaxaiDocList returns [] when there are no inline docs", async () => {
+ const list = await resolveMaxaiDocList([{ role: "user", content: "hi" }], AUTH, {
+ fetchImpl: (async () => ({ ok: true, status: 200, async text() { return ""; } }) as unknown as Response) as unknown as typeof fetch,
+ });
+ assert.deepEqual(list, []);
+});
diff --git a/tests/unit/maxai-image.test.ts b/tests/unit/maxai-image.test.ts
new file mode 100644
index 0000000000..76eff868f5
--- /dev/null
+++ b/tests/unit/maxai-image.test.ts
@@ -0,0 +1,169 @@
+import { test } from "node:test";
+import assert from "node:assert";
+import {
+ resolveMaxaiImageModel,
+ snapMaxaiImageSize,
+ extractMaxaiImageUrls,
+ handleMaxaiImageGeneration,
+ MAXAI_IMAGE_PATH,
+} from "../../open-sse/handlers/imageGeneration/providers/maxaiImage.ts";
+import { IMAGE_PROVIDERS } from "../../open-sse/config/imageRegistry.ts";
+import { __setMaxaiConstantsForTest } from "../../open-sse/executors/maxai/constantsStore.ts";
+import { MOCK_CONSTANTS } from "./helpers/maxaiMockConstants.ts";
+
+// Image generation signs like any request; seed the in-process constants memo
+// with MOCK values so the handler doesn't try to fetch the live MaxAI bundle.
+__setMaxaiConstantsForTest(MOCK_CONSTANTS);
+
+// A minimal valid MaxAI credential (userId derives nothing here; the signer is
+// exercised elsewhere). providerSpecificData carries the token + device id.
+const CRED = {
+ providerSpecificData: {
+ maxaiAccessToken: "tok-abc",
+ maxaiDeviceId: "dev-123",
+ maxaiUserId: "11111111-1111-4111-8111-111111111111",
+ },
+};
+
+// --- Registry ------------------------------------------------------------
+
+test("maxai is registered in IMAGE_PROVIDERS with the maxai-image format + 6 models", () => {
+ const entry = (IMAGE_PROVIDERS as Record)["maxai"];
+ assert.ok(entry, "maxai must exist in IMAGE_PROVIDERS");
+ assert.equal(entry.format, "maxai-image");
+ assert.match(String(entry.baseUrl), /api\.maxai\.me\/gpt\/get_image_generate_response/);
+ assert.equal((entry.models ?? []).length, 6);
+});
+
+// --- Pure helpers --------------------------------------------------------
+
+test("resolveMaxaiImageModel strips maxai/ prefix and resolves aliases", () => {
+ assert.equal(resolveMaxaiImageModel("maxai/gpt-image-1"), "gpt-image-1");
+ assert.equal(resolveMaxaiImageModel("stable-diffusion-v3"), "sd3-medium");
+ assert.equal(resolveMaxaiImageModel("stable-diffusion-3-medium"), "sd3-medium");
+ assert.equal(resolveMaxaiImageModel("flux-1-schnell"), "flux-1-schnell");
+});
+
+test("snapMaxaiImageSize snaps unsupported sizes for strict models, passes flux through", () => {
+ // gpt-image-1 / dall-e-3 reject 512x512 -> snap to 1024x1024
+ assert.equal(snapMaxaiImageSize("gpt-image-1", "512x512"), "1024x1024");
+ assert.equal(snapMaxaiImageSize("dall-e-3", "256x256"), "1024x1024");
+ // supported sizes pass through
+ assert.equal(snapMaxaiImageSize("gpt-image-1", "1536x1024"), "1536x1024");
+ assert.equal(snapMaxaiImageSize("dall-e-3", "1792x1024"), "1792x1024");
+ // flux / sd3: no constraint, any size passes through
+ assert.equal(snapMaxaiImageSize("flux-1-schnell", "512x512"), "512x512");
+ assert.equal(snapMaxaiImageSize("sd3-medium", "768x768"), "768x768");
+ // missing size -> default
+ assert.equal(snapMaxaiImageSize("gpt-image-1", undefined), "1024x1024");
+});
+
+test("extractMaxaiImageUrls prefers png_url, falls back to webp_url", () => {
+ assert.deepEqual(
+ extractMaxaiImageUrls([{ png_url: "p.png", webp_url: "w.webp" }, { webp_url: "only.webp" }]),
+ ["p.png", "only.webp"]
+ );
+ assert.deepEqual(extractMaxaiImageUrls([]), []);
+ assert.deepEqual(extractMaxaiImageUrls(null), []);
+});
+
+// --- Handler (mocked fetch) ---------------------------------------------
+
+function mockFetch(status: number, jsonBody: unknown): typeof fetch {
+ return (async () =>
+ ({
+ ok: status >= 200 && status < 300,
+ status,
+ async json() {
+ return jsonBody;
+ },
+ async text() {
+ return JSON.stringify(jsonBody);
+ },
+ }) as unknown as Response) as unknown as typeof fetch;
+}
+
+test("handleMaxaiImageGeneration returns OpenAI image data on success", async () => {
+ let capturedUrl = "";
+ let capturedBody: Record = {};
+ const fetchImpl = (async (url: string, init: RequestInit) => {
+ capturedUrl = url;
+ capturedBody = JSON.parse(String(init.body));
+ return {
+ ok: true,
+ status: 200,
+ async json() {
+ return { status: "OK", data: [{ png_url: "https://cdn/x.png", webp_url: "https://cdn/x.webp" }] };
+ },
+ async text() {
+ return "";
+ },
+ } as unknown as Response;
+ }) as unknown as typeof fetch;
+
+ const result = (await handleMaxaiImageGeneration({
+ model: "flux-1-schnell",
+ provider: "maxai",
+ body: { prompt: "a red bicycle", size: "512x512", n: 2 },
+ credentials: CRED,
+ fetchImpl,
+ })) as { success: boolean; data?: { data: Array<{ url: string }> } };
+
+ assert.equal(result.success, true);
+ assert.deepEqual(result.data?.data, [{ url: "https://cdn/x.png" }]);
+ // Hit the image endpoint with the signed body.
+ assert.match(capturedUrl, new RegExp(MAXAI_IMAGE_PATH.replace(/\//g, "\\/")));
+ assert.equal(capturedBody.model_name, "flux-1-schnell");
+ assert.equal(capturedBody.size, "512x512"); // flux passes size through
+ assert.equal(capturedBody.n, 2);
+});
+
+test("handleMaxaiImageGeneration 401 is retryable (credential fallback)", async () => {
+ const result = (await handleMaxaiImageGeneration({
+ model: "gpt-image-1",
+ provider: "maxai",
+ body: { prompt: "x" },
+ credentials: CRED,
+ fetchImpl: mockFetch(401, { error: "expired" }),
+ })) as { success: boolean; status?: number; retryable?: boolean };
+ assert.equal(result.success, false);
+ assert.equal(result.status, 401);
+ assert.equal(result.retryable, true);
+});
+
+test("handleMaxaiImageGeneration rejects an empty prompt with 400", async () => {
+ const result = (await handleMaxaiImageGeneration({
+ model: "gpt-image-1",
+ provider: "maxai",
+ body: { prompt: " " },
+ credentials: CRED,
+ fetchImpl: mockFetch(200, {}),
+ })) as { success: boolean; status?: number };
+ assert.equal(result.success, false);
+ assert.equal(result.status, 400);
+});
+
+test("handleMaxaiImageGeneration 401s with no credential (retryable)", async () => {
+ const result = (await handleMaxaiImageGeneration({
+ model: "gpt-image-1",
+ provider: "maxai",
+ body: { prompt: "x" },
+ credentials: {},
+ fetchImpl: mockFetch(200, {}),
+ })) as { success: boolean; status?: number; retryable?: boolean };
+ assert.equal(result.success, false);
+ assert.equal(result.status, 401);
+ assert.equal(result.retryable, true);
+});
+
+test("handleMaxaiImageGeneration surfaces a no-images response as 502", async () => {
+ const result = (await handleMaxaiImageGeneration({
+ model: "sd3-medium",
+ provider: "maxai",
+ body: { prompt: "x" },
+ credentials: CRED,
+ fetchImpl: mockFetch(200, { status: "OK", data: [] }),
+ })) as { success: boolean; status?: number };
+ assert.equal(result.success, false);
+ assert.equal(result.status, 502);
+});
diff --git a/tests/unit/maxai.test.ts b/tests/unit/maxai.test.ts
new file mode 100644
index 0000000000..1372c9b646
--- /dev/null
+++ b/tests/unit/maxai.test.ts
@@ -0,0 +1,1088 @@
+/**
+ * Unit tests for the MaxAI executor helpers (signer, context assembly, SSE/think).
+ *
+ * The signer vectors are REAL captured web-app requests: computeMaxaiProof must
+ * reproduce the exact `p` proof the MaxAI web app produced (decrypted from real
+ * `X-Authorization` blobs, MaxAI v3 tests/fixtures/wire_signed_samples.json).
+ */
+import test from "node:test";
+import assert from "node:assert/strict";
+
+import {
+ computeMaxaiProof,
+ maxaiAesEncrypt,
+ buildMaxaiSignedHeaders,
+} from "../../open-sse/executors/maxai/signing.ts";
+import {
+ assembleMaxaiContext,
+ buildMaxaiChatBody,
+ contentToText,
+ extractCurrentTurnImages,
+} from "../../open-sse/executors/maxai/protocol.ts";
+import {
+ splitThink,
+ ThinkSplitter,
+ parseMaxaiSseText,
+ estimateMaxaiTokens,
+} from "../../open-sse/executors/maxai/stream.ts";
+import { userIdFromJwt } from "../../open-sse/executors/maxai/credentials.ts";
+import {
+ maxaiAccessTokenNeedsRefresh,
+ maxaiRefreshAccessToken,
+ MAXAI_REFRESH_PATH,
+} from "../../open-sse/executors/maxai/refresh.ts";
+import {
+ requestMaxaiEmailCode,
+ verifyMaxaiEmailCode,
+ MAXAI_SIGNIN_EMAIL_PATH,
+ MAXAI_VERIFY_CODE_PATH,
+} from "../../open-sse/executors/maxai/emailLogin.ts";
+import { discoverMaxaiModels } from "../../open-sse/services/maxaiModels.ts";
+import {
+ __setMaxaiConstantsForTest,
+ resetMaxaiConstantsMemo,
+} from "../../open-sse/executors/maxai/constantsStore.ts";
+import {
+ parseMaxaiConstants,
+ assembleMaxaiConstants,
+ validateMaxaiConstants,
+ findChunkUrls,
+ fetchMaxaiConstants,
+ decodeNjHeaderNames,
+ resolveWebpackGetter,
+ looksLikeSignerChunk,
+} from "../../open-sse/executors/maxai/constants.ts";
+import {
+ MOCK_CONSTANTS,
+ MOCK_HMAC_KEY,
+ MOCK_AES_KEY,
+ MOCK_CTX_KEY,
+ MOCK_DOC_ID_KEY,
+ MOCK_APP_VERSION,
+ MOCK_USER_ID,
+ MOCK_DEVICE_ID,
+ referenceProof,
+ makeSyntheticAppChunk,
+ makeSyntheticSignerChunk,
+ makeSyntheticAppHtml,
+} from "./helpers/maxaiMockConstants.ts";
+
+// A synthetic user id (UUID-shaped, not real) for signer-algorithm tests.
+const USER_ID = MOCK_USER_ID;
+
+// The signer takes an extracted constants object. Tests use MOCK values only β
+// nothing id/key/version-shaped here is a real MaxAI value (the real ones are
+// fetched at runtime and persisted to the DB, never committed).
+const TEST_CONSTANTS = MOCK_CONSTANTS;
+const HMAC_KEY = MOCK_HMAC_KEY;
+const AES_KEY = MOCK_AES_KEY;
+const APP_VERSION = MOCK_APP_VERSION;
+
+// Seed the in-process signing-constants memo so the signed network helpers
+// (refresh / email login / model discovery) don't try to fetch the live MaxAI
+// bundle during unit tests. Production resolves these via ensure/refresh β
+// store β live extraction; here we inject the known-good set directly.
+__setMaxaiConstantsForTest(TEST_CONSTANTS);
+
+// ββ Signer: byte-exact vs real captured web-app requests βββββββββββββββββββββ
+
+test("computeMaxaiProof matches an independent reference implementation (mock key)", () => {
+ // Prove the HMAC-SHA1 β SM3 algorithm against a SEPARATE reference impl (not the
+ // production module) over a MOCK key, so a pass means the math matches an
+ // external spec β not merely itself, and with zero real constants committed.
+ const t = 1784594159681;
+ const path = "/conversation/get_conversation_list";
+ const p = computeMaxaiProof(path, t, USER_ID, HMAC_KEY, APP_VERSION);
+ assert.equal(p, referenceProof(APP_VERSION, t, path, USER_ID, HMAC_KEY));
+ // A different path or key yields a different proof (algorithm is sensitive).
+ assert.notEqual(p, computeMaxaiProof("/gpt/cwc/chat", t, USER_ID, HMAC_KEY, APP_VERSION));
+ assert.notEqual(p, computeMaxaiProof(path, t, USER_ID, MOCK_AES_KEY, APP_VERSION));
+});
+
+test("computeMaxaiProof blanks the user id only on /oauth/* routes", () => {
+ // A blank-user route yields a different proof than the same route with a uid,
+ // proving the uid is dropped for /oauth/* (and only there).
+ const t = 1784594159681;
+ const oauthWithUid = computeMaxaiProof("/oauth/signin_with_email", t, USER_ID, HMAC_KEY, APP_VERSION);
+ const oauthNoUid = computeMaxaiProof("/oauth/signin_with_email", t, "", HMAC_KEY, APP_VERSION);
+ assert.equal(oauthWithUid, oauthNoUid); // uid ignored for /oauth/*
+ const chatWithUid = computeMaxaiProof("/gpt/cwc/chat", t, USER_ID, HMAC_KEY, APP_VERSION);
+ const chatNoUid = computeMaxaiProof("/gpt/cwc/chat", t, "", HMAC_KEY, APP_VERSION);
+ assert.notEqual(chatWithUid, chatNoUid); // uid honored elsewhere
+});
+
+test("computeMaxaiProof requires the key + app version (never signs with a guess)", () => {
+ assert.throws(() => computeMaxaiProof("/x", 1, USER_ID, "", APP_VERSION));
+ assert.throws(() => computeMaxaiProof("/x", 1, USER_ID, HMAC_KEY, ""));
+});
+
+test("maxaiAesEncrypt produces a CryptoJS Salted__ envelope, deterministic with a fixed salt", () => {
+ const salt = Buffer.from("0011223344556677", "hex");
+ const a = maxaiAesEncrypt("payload", AES_KEY, salt);
+ const b = maxaiAesEncrypt("payload", AES_KEY, salt);
+ assert.equal(a, b); // same salt β deterministic
+ const raw = Buffer.from(a, "base64");
+ assert.equal(raw.subarray(0, 8).toString("ascii"), "Salted__");
+ assert.equal(raw.subarray(8, 16).toString("hex"), "0011223344556677");
+ // Random salt differs each call.
+ assert.notEqual(maxaiAesEncrypt("payload", AES_KEY), maxaiAesEncrypt("payload", AES_KEY));
+});
+
+// ββ Constants extractor: parse SYNTHETIC bundle chunks β the signing constants ββ
+// The fixtures are generated in-code (helpers/maxaiMockConstants.ts) with MOCK
+// values β no real MaxAI bundle, key, id, or app version is committed anywhere.
+
+const APP_CHUNK = makeSyntheticAppChunk();
+const SIGNER_CHUNK = makeSyntheticSignerChunk();
+
+test("parseMaxaiConstants extracts every value from a webpack-shaped chunk", () => {
+ const parsed = parseMaxaiConstants(APP_CHUNK, SIGNER_CHUNK);
+ assert.equal(parsed.hmacKey, MOCK_HMAC_KEY);
+ assert.equal(parsed.aesKey, MOCK_AES_KEY);
+ assert.equal(parsed.appVersion, MOCK_APP_VERSION);
+ assert.equal(parsed.docIdKey, MOCK_DOC_ID_KEY);
+ assert.equal(parsed.ctxKey, MOCK_CTX_KEY);
+ // Header names decoded from the nj(hex) calls in the signer chunk.
+ assert.equal(parsed.headerNames.authorization, "X-Authorization");
+ assert.equal(parsed.headerNames.clientDomain, "X-Client-Domain");
+ assert.equal(parsed.headerNames.random, "X-Random");
+});
+
+test("resolveWebpackGetter follows an export getter to its literal value", () => {
+ const src = 'a.d(t,{Mn:function(){return u}});let s="zzz",u="deadbeefcafe";';
+ assert.equal(resolveWebpackGetter(src, "Mn"), "deadbeefcafe");
+ assert.equal(resolveWebpackGetter(src, "Nope"), null);
+});
+
+test("decodeNjHeaderNames decodes hex header names and skips non-ASCII/garbage", () => {
+ const names = decodeNjHeaderNames(SIGNER_CHUNK);
+ assert.ok(names.includes("X-Authorization"));
+ assert.ok(names.includes("X-Client-Domain"));
+ assert.ok(names.includes("X-Random"));
+});
+
+test("looksLikeSignerChunk fingerprints the signer chunk by content, not by number", () => {
+ // The signer chunk matches (ctx slot + nj decoders); the app chunk does not.
+ assert.equal(looksLikeSignerChunk(SIGNER_CHUNK), true);
+ assert.equal(looksLikeSignerChunk(APP_CHUNK), false);
+ assert.equal(looksLikeSignerChunk("var x=1;"), false);
+});
+
+test("assembleMaxaiConstants requires all five extracted values (null when any missing)", () => {
+ const good = assembleMaxaiConstants(parseMaxaiConstants(APP_CHUNK, SIGNER_CHUNK));
+ assert.ok(good);
+ assert.equal(good!.hmacKey, MOCK_HMAC_KEY);
+ // Missing a key β null (we never assemble a half-configured signer).
+ const noHmac = assembleMaxaiConstants({
+ hmacKey: null,
+ aesKey: MOCK_AES_KEY,
+ appVersion: MOCK_APP_VERSION,
+ ctxKey: MOCK_CTX_KEY,
+ docIdKey: MOCK_DOC_ID_KEY,
+ headerNames: {},
+ });
+ assert.equal(noHmac, null);
+});
+
+test("assembleMaxaiConstants defaults header NAMES but requires the id/key/version values", () => {
+ // All five extracted values present but header-name map empty β header-name
+ // defaults fill in (they are plain HTTP labels, not keys/secrets).
+ const c = assembleMaxaiConstants({
+ hmacKey: MOCK_HMAC_KEY,
+ aesKey: MOCK_AES_KEY,
+ appVersion: MOCK_APP_VERSION,
+ ctxKey: MOCK_CTX_KEY,
+ docIdKey: MOCK_DOC_ID_KEY,
+ headerNames: {},
+ });
+ assert.ok(c);
+ assert.equal(c!.headerNames.authorization, "X-Authorization");
+ assert.equal(c!.headerNames.random, "X-Random");
+ // A missing app_version (a required extracted value) β null.
+ assert.equal(
+ assembleMaxaiConstants({
+ hmacKey: MOCK_HMAC_KEY,
+ aesKey: MOCK_AES_KEY,
+ appVersion: null,
+ ctxKey: MOCK_CTX_KEY,
+ docIdKey: MOCK_DOC_ID_KEY,
+ headerNames: {},
+ }),
+ null
+ );
+ // A missing ctxKey (required) β null.
+ assert.equal(
+ assembleMaxaiConstants({
+ hmacKey: MOCK_HMAC_KEY,
+ aesKey: MOCK_AES_KEY,
+ appVersion: MOCK_APP_VERSION,
+ ctxKey: null,
+ docIdKey: MOCK_DOC_ID_KEY,
+ headerNames: {},
+ }),
+ null
+ );
+});
+
+test("validateMaxaiConstants: shape gate by default, proof gate when a vector is given", () => {
+ const c = assembleMaxaiConstants(parseMaxaiConstants(APP_CHUNK, SIGNER_CHUNK))!;
+ // Default: shape-only (no real vector is embedded in source).
+ assert.equal(validateMaxaiConstants(c), true);
+ // Malformed values fail the shape gate.
+ assert.equal(validateMaxaiConstants({ ...c, hmacKey: "not-hex" }), false);
+ assert.equal(validateMaxaiConstants({ ...c, docIdKey: "not-a-uuid" }), false);
+ // With a MOCK proof vector, the key that produced it validates and a wrong one doesn't.
+ const t = 1700000000000;
+ const path = "/gpt/cwc/chat";
+ const vector = {
+ path,
+ reqTime: t,
+ userId: USER_ID,
+ appVersion: MOCK_APP_VERSION,
+ expectedProof: referenceProof(MOCK_APP_VERSION, t, path, USER_ID, MOCK_HMAC_KEY),
+ };
+ assert.equal(validateMaxaiConstants(c, vector), true);
+ const wrongKey = { ...c, hmacKey: MOCK_AES_KEY };
+ assert.equal(validateMaxaiConstants(wrongKey, vector), false);
+});
+
+test("findChunkUrls returns the pages/_app chunk + build-independent candidates", () => {
+ const html = makeSyntheticAppHtml({
+ appChunk: "/_next/static/chunks/pages/_app-deadbeef.js",
+ signerChunk: "/_next/static/chunks/91234-cafebabe.js",
+ });
+ const { appChunk, candidateChunks } = findChunkUrls(html);
+ assert.equal(appChunk, "/_next/static/chunks/pages/_app-deadbeef.js");
+ // The signer chunk is just one of the candidates; it's chosen later BY CONTENT.
+ assert.ok(candidateChunks.includes("/_next/static/chunks/91234-cafebabe.js"));
+ assert.ok(!candidateChunks.includes("/_next/static/chunks/pages/_app-deadbeef.js"));
+});
+
+test("fetchMaxaiConstants finds the signer chunk BY CONTENT even when renumbered", async () => {
+ // Two numbered chunks: a decoy and the real signer under an ARBITRARY new id.
+ // The scan must pick the signer purely by its content fingerprint.
+ const html = makeSyntheticAppHtml({
+ appChunk: "/_next/static/chunks/pages/_app-aaaa.js",
+ signerChunk: "/_next/static/chunks/99999-newbuildid.js",
+ extra: ["/_next/static/chunks/55555-decoy.js"],
+ });
+ const fakeFetch = (async (url: string) => {
+ const u = String(url);
+ if (u.endsWith("/app/")) return new Response(html, { status: 200 });
+ if (u.includes("/pages/_app-")) return new Response(APP_CHUNK, { status: 200 });
+ if (u.includes("/99999-")) return new Response(SIGNER_CHUNK, { status: 200 });
+ if (u.includes("/55555-")) return new Response("var decoy=1;", { status: 200 });
+ return new Response("", { status: 404 });
+ }) as unknown as typeof fetch;
+
+ const c = await fetchMaxaiConstants({ fetchImpl: fakeFetch });
+ assert.ok(c, "constants should be extracted from a renumbered signer chunk");
+ assert.equal(c!.hmacKey, MOCK_HMAC_KEY);
+ assert.equal(c!.ctxKey, MOCK_CTX_KEY);
+ assert.equal(c!.source, "extracted");
+});
+
+test("fetchMaxaiConstants returns null when the bundle can't be reached", async () => {
+ const fakeFetch = (async () => new Response("", { status: 500 })) as unknown as typeof fetch;
+ assert.equal(await fetchMaxaiConstants({ fetchImpl: fakeFetch }), null);
+ // Re-seed the memo for the remaining network tests (some run after this).
+ resetMaxaiConstantsMemo();
+ __setMaxaiConstantsForTest(TEST_CONSTANTS);
+});
+
+test("buildMaxaiSignedHeaders emits the X-App/X-Browser companions + X-Authorization", () => {
+ const h = buildMaxaiSignedHeaders(
+ {
+ path: "/gpt/cwc/chat",
+ userId: USER_ID,
+ deviceId: MOCK_DEVICE_ID,
+ now: () => 1784594159681,
+ random: () => "950484",
+ },
+ TEST_CONSTANTS
+ );
+ assert.equal(h["X-Browser-Name"], "Firefox");
+ assert.equal(h["X-Browser-Version"], "150.0");
+ assert.equal(h["X-App-Version"], MOCK_APP_VERSION);
+ assert.equal(h["X-App-Env"], "MaxAI-Browser-Extension");
+ assert.ok(h["X-Authorization"].length > 0);
+ assert.equal(Buffer.from(h["X-Authorization"], "base64").subarray(0, 8).toString("ascii"), "Salted__");
+});
+
+// ββ Context assembly βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
+
+test("assembleMaxaiContext: single user turn is sent bare", () => {
+ const text = assembleMaxaiContext([{ role: "user", content: "hello there" }]);
+ assert.equal(text, "hello there");
+});
+
+test("assembleMaxaiContext: system leads, history labeled, current fenced last", () => {
+ const text = assembleMaxaiContext([
+ { role: "system", content: "You are helpful." },
+ { role: "user", content: "first question" },
+ { role: "assistant", content: "first answer" },
+ { role: "user", content: "second question" },
+ ]);
+ assert.match(text, /^You are helpful\./);
+ assert.match(text, /=== Conversation so far \(for context\) ===/);
+ assert.match(text, /User: first question/);
+ assert.match(text, /Assistant: first answer/);
+ assert.match(text, /=== Current request \(respond to THIS\) ===\n\nsecond question$/);
+});
+
+test("assembleMaxaiContext: tool turns render as tool_response / tool_call blocks", () => {
+ const text = assembleMaxaiContext([
+ { role: "user", content: "search for X" },
+ {
+ role: "assistant",
+ content: "",
+ tool_calls: [{ function: { name: "web_search", arguments: '{"q":"X"}' } }],
+ },
+ { role: "tool", tool_call_id: "call_1", content: "result: found X" },
+ { role: "user", content: "summarize" },
+ ]);
+ assert.match(text, //);
+ assert.match(text, /web_search/);
+ assert.match(text, //);
+ assert.match(text, /result: found X/);
+});
+
+test("assembleMaxaiContext throws when there is nothing to send", () => {
+ assert.throws(() => assembleMaxaiContext([]), /no content/);
+});
+
+test("contentToText flattens multipart content, dropping non-text parts", () => {
+ assert.equal(contentToText("plain"), "plain");
+ assert.equal(
+ contentToText([
+ { type: "text", text: "a" },
+ { type: "image_url", image_url: { url: "x" } },
+ { type: "text", text: "b" },
+ ]),
+ "a\nb"
+ );
+});
+
+test("buildMaxaiChatBody pins field order + constants", () => {
+ const body = buildMaxaiChatBody({ conversationId: "conv-1", text: "hi", modelName: "gpt-5.6", appVersion: APP_VERSION });
+ const keys = Object.keys(body);
+ assert.equal(keys[0], "chat_mode");
+ assert.equal(keys[3], "message_content");
+ assert.equal(body.chat_mode, "pro_chat");
+ assert.deepEqual(body.chat_history, []);
+ assert.deepEqual(body.message_content, [{ type: "text", text: "hi" }]);
+ assert.equal(body.model_name, "gpt-5.6");
+ assert.equal(body.streaming, true);
+ assert.equal(body.platform_feature, "web_app");
+});
+
+// ββ Vision input (image_url parts) βββββββββββββββββββββββββββββββββββββββββββ
+
+test("buildMaxaiChatBody text-only path is unchanged (no imageUrls)", () => {
+ const body = buildMaxaiChatBody({ conversationId: "c", text: "hi", modelName: "gpt-5.6", appVersion: APP_VERSION });
+ // Byte-identical to the pre-vision shape: a single text part.
+ assert.deepEqual(body.message_content, [{ type: "text", text: "hi" }]);
+ assert.deepEqual(body.doc_list, []);
+});
+
+test("buildMaxaiChatBody appends image_url parts after the text part", () => {
+ const body = buildMaxaiChatBody({
+ conversationId: "c",
+ text: "what is this?",
+ modelName: "gpt-5.6-luna",
+ appVersion: APP_VERSION,
+ imageUrls: ["data:image/png;base64,AAAA", "https://example.com/cat.jpg"],
+ });
+ assert.deepEqual(body.message_content, [
+ { type: "text", text: "what is this?" },
+ { type: "image_url", image_url: { url: "data:image/png;base64,AAAA" } },
+ { type: "image_url", image_url: { url: "https://example.com/cat.jpg" } },
+ ]);
+ // Text part stays first so the flattened transcript leads.
+ assert.equal((body.message_content as Array<{ type: string }>)[0].type, "text");
+});
+
+test("buildMaxaiChatBody skips empty/blank image urls", () => {
+ const body = buildMaxaiChatBody({
+ conversationId: "c",
+ text: "t",
+ modelName: "gpt-5.6",
+ appVersion: APP_VERSION,
+ imageUrls: ["", "https://x/y.png"],
+ });
+ assert.equal((body.message_content as unknown[]).length, 2); // text + 1 valid image
+});
+
+test("extractCurrentTurnImages pulls images from the LAST user turn only", () => {
+ const urls = extractCurrentTurnImages([
+ {
+ role: "user",
+ content: [
+ { type: "text", text: "old" },
+ { type: "image_url", image_url: { url: "data:image/png;base64,OLD" } },
+ ],
+ },
+ { role: "assistant", content: "ok" },
+ {
+ role: "user",
+ content: [
+ { type: "text", text: "look" },
+ { type: "image_url", image_url: { url: "https://c/1.jpg" } },
+ { type: "image_url", image_url: "https://c/2.jpg" }, // shorthand form
+ ],
+ },
+ ]);
+ // Only the current (last) user turn's images, both object and shorthand forms.
+ assert.deepEqual(urls, ["https://c/1.jpg", "https://c/2.jpg"]);
+});
+
+test("extractCurrentTurnImages returns [] for a plain-string user turn", () => {
+ assert.deepEqual(extractCurrentTurnImages([{ role: "user", content: "just text" }]), []);
+});
+
+test("extractCurrentTurnImages returns [] when there is no user turn", () => {
+ assert.deepEqual(extractCurrentTurnImages([{ role: "system", content: "sys" }]), []);
+});
+
+// ββ SSE / think split ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
+
+test("parseMaxaiSseText extracts only mergeable text frames", () => {
+ const raw = [
+ 'data: {"data_key":"text","text":"Hello","need_merge":true}',
+ "",
+ 'data: {"data_key":"next_action","action":{}}',
+ "",
+ 'data: {"data_key":"text","text":" world","need_merge":true}',
+ "",
+ "data: [DONE]",
+ ].join("\n");
+ assert.equal(parseMaxaiSseText(raw), "Hello world");
+});
+
+test("splitThink separates reasoning from answer", () => {
+ const { reasoning, answer } = splitThink("let me think The answer is 42.");
+ assert.equal(reasoning, "let me think");
+ assert.equal(answer, "The answer is 42.");
+});
+
+test("splitThink: no think tag β all answer", () => {
+ const { reasoning, answer } = splitThink("just a plain answer");
+ assert.equal(reasoning, "");
+ assert.equal(answer, "just a plain answer");
+});
+
+test("ThinkSplitter handles a tag split across frames", () => {
+ const s = new ThinkSplitter();
+ let reasoning = "";
+ let answer = "";
+ // "reason ans"
+ for (const delta of ["reason ans"]) {
+ const out = s.feed(delta);
+ reasoning += out.reasoning;
+ answer += out.answer;
+ }
+ const tail = s.flush();
+ reasoning += tail.reasoning;
+ answer += tail.answer;
+ assert.equal(reasoning, "reason");
+ assert.equal(answer, "ans");
+});
+
+test("estimateMaxaiTokens ~ 4 chars/token", () => {
+ assert.equal(estimateMaxaiTokens(""), 0);
+ assert.equal(estimateMaxaiTokens("abcd"), 1);
+ assert.equal(estimateMaxaiTokens("abcde"), 2);
+});
+
+// ββ Credentials ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
+
+test("userIdFromJwt decodes subject.user_id (no signature verification)", () => {
+ // Build a fake JWT with { subject: { user_id } }.
+ const header = Buffer.from(JSON.stringify({ alg: "HS256", typ: "JWT" })).toString("base64url");
+ const payload = Buffer.from(JSON.stringify({ subject: { user_id: USER_ID } })).toString(
+ "base64url"
+ );
+ const jwt = `${header}.${payload}.sig`;
+ assert.equal(userIdFromJwt(jwt), USER_ID);
+});
+
+// ββ Browserless access-token refresh βββββββββββββββββββββββββββββββββββββββββ
+
+/** Build a fake (unsigned) JWT carrying an `exp` and optional subject.user_id. */
+function fakeJwt(expEpochSeconds: number, userId?: string): string {
+ const header = Buffer.from(JSON.stringify({ alg: "HS256", typ: "JWT" })).toString("base64url");
+ const claims: Record = { exp: expEpochSeconds };
+ if (userId) claims.subject = { user_id: userId };
+ const payload = Buffer.from(JSON.stringify(claims)).toString("base64url");
+ return `${header}.${payload}.sig`;
+}
+
+test("maxaiAccessTokenNeedsRefresh: absent / unparseable / near-expiry / fresh", () => {
+ const now = () => 1_000_000_000_000; // fixed ms clock
+ const nowSec = 1_000_000_000;
+ assert.equal(maxaiAccessTokenNeedsRefresh("", 3600, now), true); // absent
+ assert.equal(maxaiAccessTokenNeedsRefresh("not-a-jwt", 3600, now), true); // unparseable
+ // exp 30 min out with a 1h margin β needs refresh.
+ assert.equal(maxaiAccessTokenNeedsRefresh(fakeJwt(nowSec + 1800), 3600, now), true);
+ // exp 5h out with a 1h margin β still fresh.
+ assert.equal(maxaiAccessTokenNeedsRefresh(fakeJwt(nowSec + 5 * 3600), 3600, now), false);
+});
+
+test("maxaiRefreshAccessToken sends the exact web-app request + parses data.access_token", async () => {
+ const nowSec = Math.floor(Date.now() / 1000);
+ const refreshToken = fakeJwt(nowSec + 365 * 24 * 3600, USER_ID); // 1y refresh token
+ const newAccess = fakeJwt(nowSec + 24 * 3600, USER_ID);
+ let seen: { url: string; init: RequestInit } | null = null;
+
+ const fakeFetch = (async (url: string, init: RequestInit) => {
+ seen = { url: String(url), init };
+ return new Response(JSON.stringify({ data: { access_token: newAccess } }), { status: 200 });
+ }) as unknown as typeof fetch;
+
+ const result = await maxaiRefreshAccessToken({
+ refreshToken,
+ deviceId: MOCK_DEVICE_ID,
+ fetchImpl: fakeFetch,
+ });
+
+ assert.equal(result.ok, true);
+ assert.equal(result.accessToken, newAccess);
+ assert.ok(result.expiresAt && result.expiresAt > nowSec);
+
+ // Request shape: bare refresh path, refresh token as Bearer, noAuthLogout, app body.
+ assert.ok(seen);
+ const { url, init } = seen!;
+ assert.ok(url.endsWith(MAXAI_REFRESH_PATH));
+ assert.equal(init.method, "POST");
+ const headers = init.headers as Record;
+ assert.equal(headers["Authorization"], `Bearer ${refreshToken}`);
+ assert.equal(headers["noAuthLogout"], "true");
+ assert.ok(headers["X-Authorization"] && headers["X-Authorization"].length > 0);
+ assert.equal(init.body, JSON.stringify({ app: "maxai_webapp" }));
+});
+
+test("maxaiRefreshAccessToken returns a structured error on non-200 (no throw)", async () => {
+ const nowSec = Math.floor(Date.now() / 1000);
+ const fakeFetch = (async () =>
+ new Response("nope", { status: 418 })) as unknown as typeof fetch;
+ const result = await maxaiRefreshAccessToken({
+ refreshToken: fakeJwt(nowSec + 1000, USER_ID),
+ deviceId: "dev",
+ fetchImpl: fakeFetch,
+ });
+ assert.equal(result.ok, false);
+ assert.equal(result.status, 418);
+});
+
+test("maxaiRefreshAccessToken refuses when required inputs are missing", async () => {
+ const result = await maxaiRefreshAccessToken({ refreshToken: "", deviceId: "" });
+ assert.equal(result.ok, false);
+ assert.equal(result.status, 0);
+});
+
+// ββ Email login (browserless device-pair) ββββββββββββββββββββββββββββββββββββ
+
+test("requestMaxaiEmailCode posts the signed signin request + treats status OK as success", async () => {
+ let seen: { url: string; init: RequestInit } | null = null;
+ const fakeFetch = (async (url: string, init: RequestInit) => {
+ seen = { url: String(url), init };
+ return new Response(JSON.stringify({ data: { status: "OK" } }), { status: 200 });
+ }) as unknown as typeof fetch;
+
+ const r = await requestMaxaiEmailCode({
+ email: "user@example.com",
+ deviceId: MOCK_DEVICE_ID,
+ fetchImpl: fakeFetch,
+ });
+
+ assert.equal(r.ok, true);
+ assert.ok(seen);
+ const { url, init } = seen!;
+ assert.ok(url.endsWith(MAXAI_SIGNIN_EMAIL_PATH));
+ assert.equal(init.method, "POST");
+ assert.equal(init.body, JSON.stringify({ email: "user@example.com", app: "maxai_webapp" }));
+ const headers = init.headers as Record;
+ assert.ok(headers["X-Authorization"] && headers["X-Authorization"].length > 0);
+});
+
+test("requestMaxaiEmailCode surfaces a non-OK detail as an error", async () => {
+ const fakeFetch = (async () =>
+ new Response(JSON.stringify({ data: { status: "FAIL", detail: "Invalid email" } }), {
+ status: 200,
+ })) as unknown as typeof fetch;
+ const r = await requestMaxaiEmailCode({ email: "x@y.z", deviceId: "dev", fetchImpl: fakeFetch });
+ assert.equal(r.ok, false);
+ assert.match(r.error ?? "", /Invalid email/);
+});
+
+test("verifyMaxaiEmailCode returns the full credential from auth_user", async () => {
+ const nowSec = Math.floor(Date.now() / 1000);
+ const accessToken = "acc.jwt.token";
+ const refreshToken = "ref.jwt.token";
+ let seen: { url: string; init: RequestInit } | null = null;
+ const fakeFetch = (async (url: string, init: RequestInit) => {
+ seen = { url: String(url), init };
+ return new Response(
+ JSON.stringify({
+ data: {
+ status: "OK",
+ auth_user: {
+ accessToken,
+ refreshToken,
+ userId: USER_ID,
+ email: "user@example.com",
+ clientUserId: "client-uuid-1",
+ },
+ },
+ }),
+ { status: 200 }
+ );
+ }) as unknown as typeof fetch;
+
+ const r = await verifyMaxaiEmailCode({
+ email: "user@example.com",
+ code: "123456",
+ deviceId: "device-uuid-1",
+ clientUserId: "client-uuid-1",
+ fetchImpl: fakeFetch,
+ });
+
+ assert.equal(r.ok, true);
+ assert.deepEqual(r.credential, {
+ accessToken,
+ refreshToken,
+ userId: USER_ID,
+ email: "user@example.com",
+ deviceId: "device-uuid-1",
+ clientUserId: "client-uuid-1",
+ });
+ assert.ok(nowSec > 0); // sanity anchor
+
+ // Request shape: verify path + pinned body fields.
+ const { url, init } = seen!;
+ assert.ok(url.endsWith(MAXAI_VERIFY_CODE_PATH));
+ const body = JSON.parse(String(init.body));
+ assert.equal(body.email, "user@example.com");
+ assert.equal(body.secret_code, "123456");
+ assert.equal(body.app, "maxai_webapp");
+ assert.equal(body.env, "prod_co");
+ assert.equal(body.client_user_id, "client-uuid-1");
+});
+
+test("verifyMaxaiEmailCode maps code 10119 to an expired-code message", async () => {
+ const fakeFetch = (async () =>
+ new Response(JSON.stringify({ data: { status: "FAIL", code: 10119 } }), {
+ status: 200,
+ })) as unknown as typeof fetch;
+ const r = await verifyMaxaiEmailCode({
+ email: "x@y.z",
+ code: "000000",
+ deviceId: "dev",
+ clientUserId: "cu",
+ fetchImpl: fakeFetch,
+ });
+ assert.equal(r.ok, false);
+ assert.match(r.error ?? "", /expired|too many/i);
+});
+
+test("verifyMaxaiEmailCode defaults to an invalid-code message otherwise", async () => {
+ const fakeFetch = (async () =>
+ new Response(JSON.stringify({ data: { status: "FAIL" } }), { status: 200 })) as unknown as typeof fetch;
+ const r = await verifyMaxaiEmailCode({
+ email: "x@y.z",
+ code: "999999",
+ deviceId: "dev",
+ clientUserId: "cu",
+ fetchImpl: fakeFetch,
+ });
+ assert.equal(r.ok, false);
+ assert.match(r.error ?? "", /Invalid code/);
+});
+
+test("email login guards missing inputs", async () => {
+ assert.equal((await requestMaxaiEmailCode({ email: "", deviceId: "" })).ok, false);
+ assert.equal(
+ (await verifyMaxaiEmailCode({ email: "", code: "", deviceId: "", clientUserId: "" })).ok,
+ false
+ );
+});
+
+// ββ Tool calling (prompted protocol) ββββββββββββββββββββββββββββββββββ
+
+import { MaxAiExecutor } from "../../open-sse/executors/maxai.ts";
+
+const TOOL_CRED = {
+ providerSpecificData: {
+ maxaiAccessToken: "acc.tok.en",
+ maxaiDeviceId: "dev-1",
+ maxaiUserId: USER_ID,
+ },
+ accessToken: "acc.tok.en",
+};
+
+const WEATHER_TOOL = {
+ type: "function",
+ function: {
+ name: "get_weather",
+ description: "Get the current weather for a city.",
+ parameters: {
+ type: "object",
+ properties: { city: { type: "string" } },
+ required: ["city"],
+ },
+ },
+};
+
+/** Build a MaxAI SSE body streaming `full` as one mergeable text frame. */
+function maxaiSseBody(full: string): string {
+ return (
+ `data: ${JSON.stringify({ data_key: "text", need_merge: true, text: full })}\n\n` +
+ "data: [DONE]\n\n"
+ );
+}
+
+/** Run MaxAiExecutor.execute with a stubbed global fetch returning `sseText`. */
+async function runToolExecute(opts: {
+ sseText: string;
+ stream: boolean;
+ tools?: unknown[];
+}): Promise<{ captured: { url: string; body: string } | null; response: Response }> {
+ const realFetch = globalThis.fetch;
+ let captured: { url: string; body: string } | null = null;
+ globalThis.fetch = (async (url: unknown, init: unknown) => {
+ captured = {
+ url: String(url),
+ body: String((init as RequestInit)?.body ?? ""),
+ };
+ return new Response(opts.sseText, { status: 200 });
+ }) as unknown as typeof fetch;
+ try {
+ const executor = new MaxAiExecutor();
+ const result = await executor.execute({
+ model: "gpt-5.6-luna",
+ stream: opts.stream,
+ credentials: TOOL_CRED,
+ body: {
+ model: "gpt-5.6-luna",
+ messages: [{ role: "user", content: "what's the weather in Paris?" }],
+ ...(opts.tools ? { tools: opts.tools } : {}),
+ stream: opts.stream,
+ },
+ } as unknown as Parameters[0]);
+ const response = "response" in result ? result.response : (result as Response);
+ return { captured, response };
+ } finally {
+ globalThis.fetch = realFetch;
+ }
+}
+
+test("executor injects the contract into the upstream text when tools are present", async () => {
+ const { captured } = await runToolExecute({
+ sseText: maxaiSseBody("Sure, let me check."),
+ stream: false,
+ tools: [WEATHER_TOOL],
+ });
+ assert.ok(captured);
+ const chatBody = JSON.parse(captured!.body);
+ const sentText = chatBody.message_content[0].text as string;
+ // The prompted-tool contract + the tool name reach the model.
+ assert.match(sentText, //);
+ assert.match(sentText, /get_weather/);
+});
+
+test("executor parses a block from the reply into OpenAI tool_calls (non-stream)", async () => {
+ const toolBlock =
+ '{"name": "get_weather", "arguments": {"city": "Paris"}, "_nonce": "NONCE"} ';
+ // The parser needs the SAME nonce serializeToolsToPrompt derived from tools[].
+ // getToolNonce is deterministic per tools ref+content, so re-derive it here.
+ const { getToolNonce } = await import("../../open-sse/translator/webTools.ts");
+ const tools = [WEATHER_TOOL];
+ const nonce = getToolNonce(tools);
+ const reply = `{"name": "get_weather", "arguments": {"city": "Paris"}, "_nonce": "${nonce}"} `;
+ void toolBlock;
+
+ const { response } = await runToolExecute({
+ sseText: maxaiSseBody(reply),
+ stream: false,
+ tools,
+ });
+ assert.equal(response.status, 200);
+ const json = await response.json();
+ const choice = json.choices[0];
+ assert.equal(choice.finish_reason, "tool_calls");
+ assert.ok(Array.isArray(choice.message.tool_calls));
+ assert.equal(choice.message.tool_calls[0].function.name, "get_weather");
+ assert.deepEqual(JSON.parse(choice.message.tool_calls[0].function.arguments), { city: "Paris" });
+});
+
+test("executor tool mode emits a terminal SSE replay with tool_calls (stream)", async () => {
+ const { getToolNonce } = await import("../../open-sse/translator/webTools.ts");
+ const tools = [WEATHER_TOOL];
+ const nonce = getToolNonce(tools);
+ const reply = `{"name": "get_weather", "arguments": {"city": "Paris"}, "_nonce": "${nonce}"} `;
+
+ const { response } = await runToolExecute({
+ sseText: maxaiSseBody(reply),
+ stream: true,
+ tools,
+ });
+ assert.equal(response.status, 200);
+ assert.match(response.headers.get("Content-Type") ?? "", /text\/event-stream/);
+ const sse = await response.text();
+ assert.match(sse, /"tool_calls"/);
+ assert.match(sse, /get_weather/);
+ assert.match(sse, /\[DONE\]/);
+});
+
+test("executor without tools streams normally (no tool_calls, plain content)", async () => {
+ const { response } = await runToolExecute({
+ sseText: maxaiSseBody("Paris is sunny today."),
+ stream: false,
+ });
+ assert.equal(response.status, 200);
+ const json = await response.json();
+ assert.equal(json.choices[0].finish_reason, "stop");
+ assert.equal(json.choices[0].message.content, "Paris is sunny today.");
+ assert.equal(json.choices[0].message.tool_calls, undefined);
+});
+
+/** Like runToolExecute but returns a DIFFERENT sse body per upstream call, so we
+ * can simulate a narration-miss on turn 1 and a clean tool call on turn 2. */
+async function runToolExecuteSeq(bodies: string[]): Promise {
+ const realFetch = globalThis.fetch;
+ let call = 0;
+ globalThis.fetch = (async () => {
+ const body = bodies[Math.min(call, bodies.length - 1)];
+ call += 1;
+ return new Response(body, { status: 200 });
+ }) as unknown as typeof fetch;
+ try {
+ const executor = new MaxAiExecutor();
+ const result = await executor.execute({
+ model: "maxai/deepseek-r1",
+ stream: false,
+ credentials: TOOL_CRED,
+ body: {
+ model: "maxai/deepseek-r1",
+ messages: [{ role: "user", content: "what's the weather in Ghent?" }],
+ tools: [WEATHER_TOOL],
+ stream: false,
+ },
+ } as unknown as Parameters[0]);
+ return "response" in result ? result.response : (result as Response);
+ } finally {
+ globalThis.fetch = realFetch;
+ }
+}
+
+test("executor recovers a tool narration-miss via one nudged retry", async () => {
+ // Turn 1: the model NARRATES about the block but emits none parseable.
+ const narration =
+ "I can use the get_current_weather tool here via a special block. Let me think about the arguments...";
+ // Turn 2 (after nudge): a clean, parseable tool call. Omit _nonce (tolerated
+ // for models that don't echo it) so the test isn't coupled to the internal
+ // per-tools-reference nonce the executor injected.
+ const clean = `{"name": "get_current_weather", "arguments": {"city": "Ghent"}} `;
+
+ const response = await runToolExecuteSeq([maxaiSseBody(narration), maxaiSseBody(clean)]);
+ assert.equal(response.status, 200);
+ const json = await response.json();
+ assert.equal(json.choices[0].finish_reason, "tool_calls");
+ assert.equal(json.choices[0].message.tool_calls[0].function.name, "get_current_weather");
+ assert.deepEqual(JSON.parse(json.choices[0].message.tool_calls[0].function.arguments), {
+ city: "Ghent",
+ });
+});
+
+test("executor does NOT retry a genuine no-tool answer (no narration signal)", async () => {
+ // A plain answer with no tool intent must pass through unchanged (single call).
+ let calls = 0;
+ const realFetch = globalThis.fetch;
+ globalThis.fetch = (async () => {
+ calls += 1;
+ return new Response(maxaiSseBody("The weather in Ghent is mild and cloudy."), { status: 200 });
+ }) as unknown as typeof fetch;
+ try {
+ const executor = new MaxAiExecutor();
+ const result = await executor.execute({
+ model: "maxai/gpt-5.6",
+ stream: false,
+ credentials: TOOL_CRED,
+ body: {
+ model: "maxai/gpt-5.6",
+ messages: [{ role: "user", content: "how's Ghent?" }],
+ tools: [WEATHER_TOOL],
+ stream: false,
+ },
+ } as unknown as Parameters[0]);
+ const response = "response" in result ? result.response : (result as Response);
+ const json = await response.json();
+ assert.equal(json.choices[0].finish_reason, "stop");
+ assert.equal(calls, 1); // no retry
+ } finally {
+ globalThis.fetch = realFetch;
+ }
+});
+
+// ββ Model discovery (/models/get_config β per-model context windows) ββββββββββ
+
+const DISCOVERY_CRED = {
+ providerSpecificData: {
+ maxaiAccessToken: "acc.tok.en",
+ maxaiDeviceId: "dev-1",
+ maxaiUserId: USER_ID,
+ },
+ accessToken: "acc.tok.en",
+};
+
+/** A minimal /models/get_config body with the fields the mapper reads. */
+function modelsConfigBody(models: unknown[]): string {
+ return JSON.stringify({ data: { chat_models: models } });
+}
+
+test("discoverMaxaiModels maps curated chat models with live max_tokens as the window", async () => {
+ const fakeFetch = (async () =>
+ new Response(
+ modelsConfigBody([
+ {
+ model_name: "gpt-5.6-luna",
+ ui_display_name: "GPT-5.6 Luna",
+ type: "chat",
+ group: "fast",
+ max_tokens: 1_050_000,
+ is_deprecated: false,
+ capabilities: { vision: true, thinking_mode: false },
+ },
+ {
+ model_name: "gpt-5.6-thinking",
+ ui_display_name: "GPT-5.6 Thinking",
+ type: "chat",
+ group: "reasoning",
+ max_tokens: 1_050_000,
+ is_deprecated: false,
+ capabilities: { vision: false, thinking_mode: true },
+ },
+ ]),
+ { status: 200 }
+ )) as unknown as typeof fetch;
+
+ const { models, warning } = await discoverMaxaiModels({
+ providerSpecificData: DISCOVERY_CRED.providerSpecificData,
+ accessToken: DISCOVERY_CRED.accessToken,
+ fetchImpl: fakeFetch,
+ });
+
+ const luna = models.find((m) => m.id === "gpt-5.6-luna");
+ assert.ok(luna);
+ assert.equal(luna!.inputTokenLimit, 1_050_000);
+ assert.equal(luna!.name, "GPT-5.6 Luna");
+ assert.equal(luna!.toolCalling, true);
+ assert.equal(luna!.supportsVision, true);
+ const thinking = models.find((m) => m.id === "gpt-5.6-thinking");
+ assert.equal(thinking!.supportsReasoning, true);
+ // Two curated returned, so the "no longer offered" warning names the rest.
+ assert.ok(warning && /no longer offers/.test(warning));
+});
+
+test("discoverMaxaiModels drops deprecated, non-chat, and non-curated models", async () => {
+ const fakeFetch = (async () =>
+ new Response(
+ modelsConfigBody([
+ { model_name: "gpt-5.6-luna", type: "chat", max_tokens: 1_050_000, is_deprecated: false },
+ { model_name: "gpt-5-mini", type: "chat", max_tokens: 400_000, is_deprecated: true }, // deprecated
+ { model_name: "some-image-model", type: "image", max_tokens: 0 }, // non-chat
+ { model_name: "not-in-catalog", type: "chat", max_tokens: 123 }, // non-curated
+ ]),
+ { status: 200 }
+ )) as unknown as typeof fetch;
+
+ const { models } = await discoverMaxaiModels({
+ providerSpecificData: DISCOVERY_CRED.providerSpecificData,
+ accessToken: DISCOVERY_CRED.accessToken,
+ fetchImpl: fakeFetch,
+ });
+ assert.deepEqual(
+ models.map((m) => m.id),
+ ["gpt-5.6-luna"]
+ );
+});
+
+test("discoverMaxaiModels falls back to the catalog window when max_tokens is absent", async () => {
+ const fakeFetch = (async () =>
+ new Response(
+ modelsConfigBody([{ model_name: "claude-5-sonnet", type: "chat" }]),
+ { status: 200 }
+ )) as unknown as typeof fetch;
+ const { models } = await discoverMaxaiModels({
+ providerSpecificData: DISCOVERY_CRED.providerSpecificData,
+ accessToken: DISCOVERY_CRED.accessToken,
+ fetchImpl: fakeFetch,
+ });
+ const sonnet = models.find((m) => m.id === "claude-5-sonnet");
+ assert.ok(sonnet);
+ assert.ok(sonnet!.inputTokenLimit > 0); // from catalog fallback (1_000_000)
+});
+
+test("discoverMaxaiModels throws on non-200 and on missing chat_models", async () => {
+ const err418 = (async () => new Response("nope", { status: 418 })) as unknown as typeof fetch;
+ await assert.rejects(
+ discoverMaxaiModels({
+ providerSpecificData: DISCOVERY_CRED.providerSpecificData,
+ accessToken: DISCOVERY_CRED.accessToken,
+ fetchImpl: err418,
+ }),
+ /418/
+ );
+
+ const noModels = (async () =>
+ new Response(JSON.stringify({ data: {} }), { status: 200 })) as unknown as typeof fetch;
+ await assert.rejects(
+ discoverMaxaiModels({
+ providerSpecificData: DISCOVERY_CRED.providerSpecificData,
+ accessToken: DISCOVERY_CRED.accessToken,
+ fetchImpl: noModels,
+ }),
+ /no chat_models/
+ );
+});
+
+test("discoverMaxaiModels refuses when the connection is unconfigured", async () => {
+ await assert.rejects(
+ discoverMaxaiModels({ providerSpecificData: {}, accessToken: "" }),
+ /not configured/
+ );
+});
+
+// ββ Body-too-large classification (context_length_exceeded) ββββββββββββββββββ
+
+test("executor classifies a MaxAI 'too long' rejection as context_length_exceeded", async () => {
+ const realFetch = globalThis.fetch;
+ globalThis.fetch = (async () =>
+ new Response(
+ JSON.stringify({
+ code: -2,
+ detail:
+ "Something went wrong. It's probably due to the message you submitted being too long. Please reload the conversation and submit something shorter.",
+ }),
+ { status: 422 }
+ )) as unknown as typeof fetch;
+ try {
+ const executor = new MaxAiExecutor();
+ const result = await executor.execute({
+ model: "maxai/gpt-5.6",
+ stream: false,
+ credentials: TOOL_CRED,
+ body: {
+ model: "maxai/gpt-5.6",
+ messages: [{ role: "user", content: "a very long transcript..." }],
+ stream: false,
+ },
+ } as unknown as Parameters[0]);
+ const response = "response" in result ? result.response : (result as Response);
+ assert.equal(response.status, 400);
+ const json = await response.json();
+ assert.equal(json.error.code, "context_length_exceeded");
+ } finally {
+ globalThis.fetch = realFetch;
+ }
+});
diff --git a/tests/unit/provider-node-reserved-prefix.test.ts b/tests/unit/provider-node-reserved-prefix.test.ts
index 8ec06f680f..8c7b57e83e 100644
--- a/tests/unit/provider-node-reserved-prefix.test.ts
+++ b/tests/unit/provider-node-reserved-prefix.test.ts
@@ -171,7 +171,7 @@ 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);
+ assert.equal(RESERVED_PREFIX_COUNT, 402);
});
test("isReservedProviderPrefix rejects non-string input", () => {
diff --git a/tests/unit/ratelimit-admission-control-6593.test.ts b/tests/unit/ratelimit-admission-control-6593.test.ts
index 873ed0038b..cdad084a32 100644
--- a/tests/unit/ratelimit-admission-control-6593.test.ts
+++ b/tests/unit/ratelimit-admission-control-6593.test.ts
@@ -239,6 +239,18 @@ test("#6593 a maxWaitMs override of 0 is treated as no override", () => {
}
});
+test("maxai receives a provider-scoped 5min execution budget (slow reasoning models)", () => {
+ // The default 15s Bottleneck expiration kills MaxAI reasoning turns (30s-min+)
+ // mid-think; maxai (and its mx alias) floor at 300s so they complete.
+ assert.equal(rateLimitManager.resolveRequestQueueMaxWaitMs("maxai", 15_000), 300_000);
+ assert.equal(rateLimitManager.resolveRequestQueueMaxWaitMs("MaxAI", 15_000), 300_000);
+ assert.equal(rateLimitManager.resolveRequestQueueMaxWaitMs("mx", 15_000), 300_000);
+ // A larger configured value is preserved (floor never lowers it).
+ assert.equal(rateLimitManager.resolveRequestQueueMaxWaitMs("maxai", 600_000), 600_000);
+ // Other providers are unaffected.
+ assert.equal(rateLimitManager.resolveRequestQueueMaxWaitMs("openai", 15_000), 15_000);
+});
+
test("#6593 DEFAULT_REQUEST_QUEUE_MAX_DEPTH defaults to 0 (disabled) absent an env override", () => {
assert.equal(process.env.RATE_LIMIT_MAX_QUEUE_DEPTH, undefined);
assert.equal(resilienceSettings.DEFAULT_REQUEST_QUEUE_MAX_DEPTH, 0);