diff --git a/docs/CODEBASE_DOCUMENTATION.md b/docs/CODEBASE_DOCUMENTATION.md
index 1b16d67844..bf8bdf5d31 100644
--- a/docs/CODEBASE_DOCUMENTATION.md
+++ b/docs/CODEBASE_DOCUMENTATION.md
@@ -1,589 +1,787 @@
-# omniroute โ Codebase Documentation
+# OmniRoute Codebase Documentation
-๐ **Languages:** ๐บ๐ธ [English](CODEBASE_DOCUMENTATION.md) | ๐ง๐ท [Portuguรชs (Brasil)](i18n/pt-BR/CODEBASE_DOCUMENTATION.md) | ๐ช๐ธ [Espaรฑol](i18n/es/CODEBASE_DOCUMENTATION.md) | ๐ซ๐ท [Franรงais](i18n/fr/CODEBASE_DOCUMENTATION.md) | ๐ฎ๐น [Italiano](i18n/it/CODEBASE_DOCUMENTATION.md) | ๐ท๐บ [ะ ัััะบะธะน](i18n/ru/CODEBASE_DOCUMENTATION.md) | ๐จ๐ณ [ไธญๆ (็ฎไฝ)](i18n/zh-CN/CODEBASE_DOCUMENTATION.md) | ๐ฉ๐ช [Deutsch](i18n/de/CODEBASE_DOCUMENTATION.md) | ๐ฎ๐ณ [เคนเคฟเคจเฅเคฆเฅ](i18n/in/CODEBASE_DOCUMENTATION.md) | ๐น๐ญ [เนเธเธข](i18n/th/CODEBASE_DOCUMENTATION.md) | ๐บ๐ฆ [ะฃะบัะฐัะฝััะบะฐ](i18n/uk-UA/CODEBASE_DOCUMENTATION.md) | ๐ธ๐ฆ [ุงูุนุฑุจูุฉ](i18n/ar/CODEBASE_DOCUMENTATION.md) | ๐ฏ๐ต [ๆฅๆฌ่ช](i18n/ja/CODEBASE_DOCUMENTATION.md) | ๐ป๐ณ [Tiแบฟng Viแปt](i18n/vi/CODEBASE_DOCUMENTATION.md) | ๐ง๐ฌ [ะัะปะณะฐััะบะธ](i18n/bg/CODEBASE_DOCUMENTATION.md) | ๐ฉ๐ฐ [Dansk](i18n/da/CODEBASE_DOCUMENTATION.md) | ๐ซ๐ฎ [Suomi](i18n/fi/CODEBASE_DOCUMENTATION.md) | ๐ฎ๐ฑ [ืขืืจืืช](i18n/he/CODEBASE_DOCUMENTATION.md) | ๐ญ๐บ [Magyar](i18n/hu/CODEBASE_DOCUMENTATION.md) | ๐ฎ๐ฉ [Bahasa Indonesia](i18n/id/CODEBASE_DOCUMENTATION.md) | ๐ฐ๐ท [ํ๊ตญ์ด](i18n/ko/CODEBASE_DOCUMENTATION.md) | ๐ฒ๐พ [Bahasa Melayu](i18n/ms/CODEBASE_DOCUMENTATION.md) | ๐ณ๐ฑ [Nederlands](i18n/nl/CODEBASE_DOCUMENTATION.md) | ๐ณ๐ด [Norsk](i18n/no/CODEBASE_DOCUMENTATION.md) | ๐ต๐น [Portuguรชs (Portugal)](i18n/pt/CODEBASE_DOCUMENTATION.md) | ๐ท๐ด [Romรขnฤ](i18n/ro/CODEBASE_DOCUMENTATION.md) | ๐ต๐ฑ [Polski](i18n/pl/CODEBASE_DOCUMENTATION.md) | ๐ธ๐ฐ [Slovenฤina](i18n/sk/CODEBASE_DOCUMENTATION.md) | ๐ธ๐ช [Svenska](i18n/sv/CODEBASE_DOCUMENTATION.md) | ๐ต๐ญ [Filipino](i18n/phi/CODEBASE_DOCUMENTATION.md) | ๐จ๐ฟ [ฤeลกtina](i18n/cs/CODEBASE_DOCUMENTATION.md)
+> **Version:** v3.8.0
+> **Last updated:** 2026-05-13
+> **Audience:** Engineers contributing to OmniRoute or building integrations on top of it.
+>
+> For high-level architecture diagrams and the reasoning behind each subsystem, read
+> [ARCHITECTURE.md](./ARCHITECTURE.md). For deep dives on individual subsystems
+> (Auto Combo, MCP server, A2A server, Skills, Memory, Cloud Agents, Resilience,
+> Compression, etc.) see their dedicated files in this `docs/` directory.
-> A comprehensive, beginner-friendly guide to the **omniroute** multi-provider AI proxy router.
+This file describes **what exists in the repository today** so that a new engineer
+can navigate the tree, understand the runtime layering, and know where to add code
+without inventing new modules.
---
-## 1. What Is omniroute?
+## 1. Tech Stack
-omniroute is a **proxy router** that sits between AI clients (Claude CLI, Codex, Cursor IDE, etc.) and AI providers (Anthropic, Google, OpenAI, AWS, GitHub, etc.). It solves one big problem:
+| Concern | Choice |
+| ------------- | ------------------------------------------------------------------------------------------------------------------------ | --- | ------------- | --- | ------------------------------------ |
+| Web framework | **Next.js 16** (App Router, standalone output, no global middleware) |
+| Language | **TypeScript 5.9+** โ target `ES2022`, `module: esnext`, `moduleResolution: bundler`, `strict: false` |
+| Runtime | **Node.js** `>=20.20.2 <21 | | >=22.22.2 <23 | | >=24.0.0 <27`(enforced via`engines`) |
+| Database | **SQLite** via `better-sqlite3` (singleton, WAL journaling) |
+| Desktop | **Electron 41** + `electron-builder` 26.10 (separate workspace at `electron/`) |
+| Tests | **Node native test runner** (unit/integration), **Vitest** (MCP, autoCombo, cache), **Playwright** (e2e + protocols-e2e) |
+| Build | Next.js standalone via `scripts/build-next-isolated.mjs` |
+| Lint/format | ESLint flat config + Prettier (`lint-staged` via Husky pre-commit) |
+| Module system | ESM everywhere (`"type": "module"`) |
+| Workspaces | npm workspace โ `open-sse` is the only sub-workspace |
-> **Different AI clients speak different "languages" (API formats), and different AI providers expect different "languages" too.** omniroute translates between them automatically.
+Path aliases (`tsconfig.json`):
-Think of it like a universal translator at the United Nations โ any delegate can speak any language, and the translator converts it for any other delegate.
+- `@/*` โ `src/*`
+- `@omniroute/open-sse` โ `open-sse/index.ts`
+- `@omniroute/open-sse/*` โ `open-sse/*`
+
+Default HTTP port: **`20128`** (API and dashboard share the same process). Data
+directory is `DATA_DIR` env var, defaulting to `~/.omniroute/`.
---
-## 2. Architecture Overview
-
-```mermaid
-graph LR
- subgraph Clients
- A[Claude CLI]
- B[Codex]
- C[Cursor IDE]
- D[OpenAI-compatible]
- end
-
- subgraph omniroute
- E[Handler Layer]
- F[Translator Layer]
- G[Executor Layer]
- H[Services Layer]
- end
-
- subgraph Providers
- I[Anthropic Claude]
- J[Google Gemini]
- K[OpenAI / Codex]
- L[GitHub Copilot]
- M[AWS Kiro]
- N[Antigravity]
- O[Cursor API]
- end
-
- A --> E
- B --> E
- C --> E
- D --> E
- E --> F
- F --> G
- G --> I
- G --> J
- G --> K
- G --> L
- G --> M
- G --> N
- G --> O
- H -.-> E
- H -.-> G
-```
-
-### Core Principle: Hub-and-Spoke Translation
-
-All format translation passes through **OpenAI format as the hub**:
+## 2. Repository Layout
```
-Client Format โ [OpenAI Hub] โ Provider Format (request)
-Provider Format โ [OpenAI Hub] โ Client Format (response)
-```
-
-This means you only need **N translators** (one per format) instead of **Nยฒ** (every pair).
-
----
-
-## 3. Project Structure
-
-```
-omniroute/
-โโโ open-sse/ โ Core proxy library (portable, framework-agnostic)
-โ โโโ index.js โ Main entry point, exports everything
-โ โโโ config/ โ Configuration & constants
-โ โโโ executors/ โ Provider-specific request execution
-โ โโโ handlers/ โ Request handling orchestration
-โ โโโ services/ โ Business logic (auth, models, fallback, usage)
-โ โโโ translator/ โ Format translation engine
-โ โ โโโ request/ โ Request translators (8 files)
-โ โ โโโ response/ โ Response translators (7 files)
-โ โ โโโ helpers/ โ Shared translation utilities (6 files)
-โ โโโ utils/ โ Utility functions
-โโโ src/ โ Application layer (Express/Worker runtime)
-โ โโโ app/ โ Web UI, API routes, middleware
-โ โโโ lib/ โ Database, auth, and shared library code
-โ โโโ mitm/ โ Man-in-the-middle proxy utilities
-โ โโโ models/ โ Database models
-โ โโโ shared/ โ Shared utilities (wrappers around open-sse)
-โ โโโ sse/ โ SSE endpoint handlers
-โ โโโ store/ โ State management
-โโโ data/ โ Runtime data (credentials, logs)
-โ โโโ provider-credentials.json (external credentials override, gitignored)
-โโโ tester/ โ Test utilities
+OmniRoute/
+โโโ src/ Next.js application (App Router, libs, domain, server, shared)
+โโโ open-sse/ Streaming engine workspace (@omniroute/open-sse)
+โโโ electron/ Desktop wrapper (Electron 41 main + preload)
+โโโ bin/ CLI entry points (omniroute, reset-password)
+โโโ tests/ Unit, integration, e2e, protocols-e2e, translator, security, fixtures
+โโโ scripts/ Build, sync, check, migration, and runtime helper scripts
+โโโ docs/ Public documentation (this directory)
+โโโ public/ Static assets, PWA manifest, service worker
+โโโ config/ Runtime config samples
+โโโ images/ Marketing/screenshot assets
+โโโ _ideia/, _references/, _mono_repo/, _tasks/ Internal scratch / planning (not shipped)
+โโโ CLAUDE.md Repo rules for Claude Code
+โโโ AGENTS.md Deeper architecture reference for agents
+โโโ package.json v3.8.0, workspace root
+โโโ tsconfig.json Path aliases + core compiler options
```
---
-## 4. Module-by-Module Breakdown
+## 3. `src/` โ Next.js Application
-### 4.1 Config (`open-sse/config/`)
-
-The **single source of truth** for all provider configuration.
-
-| File | Purpose |
-| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| `constants.ts` | `PROVIDERS` object with base URLs, OAuth credentials (defaults), headers, and default system prompts for every provider. Also defines `HTTP_STATUS`, `ERROR_TYPES`, `COOLDOWN_MS`, `BACKOFF_CONFIG`, and `SKIP_PATTERNS`. |
-| `credentialLoader.ts` | Loads external credentials from `data/provider-credentials.json` and merges them over the hardcoded defaults in `PROVIDERS`. Keeps secrets out of source control while maintaining backwards compatibility. |
-| `providerModels.ts` | Central model registry: maps provider aliases โ model IDs. Functions like `getModels()`, `getProviderByAlias()`. |
-| `codexInstructions.ts` | System instructions injected into Codex requests (editing constraints, sandbox rules, approval policies). |
-| `defaultThinkingSignature.ts` | Default "thinking" signatures for Claude and Gemini models. |
-| `ollamaModels.ts` | Schema definition for local Ollama models (name, size, family, quantization). |
-
-#### Credential Loading Flow
-
-```mermaid
-flowchart TD
- A["App starts"] --> B["constants.ts defines PROVIDERS\nwith hardcoded defaults"]
- B --> C{"data/provider-credentials.json\nexists?"}
- C -->|Yes| D["credentialLoader reads JSON"]
- C -->|No| E["Use hardcoded defaults"]
- D --> F{"For each provider in JSON"}
- F --> G{"Provider exists\nin PROVIDERS?"}
- G -->|No| H["Log warning, skip"]
- G -->|Yes| I{"Value is object?"}
- I -->|No| J["Log warning, skip"]
- I -->|Yes| K["Merge clientId, clientSecret,\ntokenUrl, authUrl, refreshUrl"]
- K --> F
- H --> F
- J --> F
- F -->|Done| L["PROVIDERS ready with\nmerged credentials"]
- E --> L
```
+src/
+โโโ app/ App Router pages + API routes
+โโโ lib/ Core libraries (DB, auth, OAuth, skills, memory, โฆ)
+โโโ domain/ Pure domain layer (policy, fallback, cost, lockout, โฆ)
+โโโ server/ Server-only modules (authz, cors, auth)
+โโโ shared/ Types, constants, validation, contracts, utils (cross-boundary safe)
+โโโ mitm/ Man-in-the-middle proxy helpers for CLI integration
+โโโ models/ Local model metadata / aliasing
+โโโ sse/ Legacy SSE handlers that still live under src/ (not open-sse/)
+โโโ store/ Client-side state stores
+โโโ middleware/ Route-level middleware utilities (not Next.js global middleware)
+โโโ scripts/ In-tree scripts importable by app code
+โโโ types/ Ambient and shared TS types
+โโโ i18n/ Locale bundles
+โโโ instrumentation.ts Next.js instrumentation hook
+โโโ instrumentation-node.ts
+โโโ server-init.ts Process-level bootstrap (env, DB, jobs, sync)
+โโโ proxy.ts Top-level proxy bootstrap helper
+```
+
+### 3.1 `src/app/` โ App Router
+
+The App Router exposes both the dashboard UI and the public/management HTTP API.
+There is **no global middleware** โ interception is done per-route.
+
+Top-level segments under `src/app/`:
+
+| Path | Purpose |
+| ----------------------------------------------------------------------------- | ----------------------------------------- |
+| `api/` | All HTTP API routes (see breakdown below) |
+| `a2a/` | A2A JSON-RPC 2.0 endpoint (`POST /a2a`) |
+| `.well-known/agent.json/` | A2A Agent Card discovery document |
+| `(dashboard)/` | Dashboard UI (route group, no URL prefix) |
+| `auth/`, `login/`, `forgot-password/`, `callback/` | Auth flows |
+| `landing/` | Marketing/landing page |
+| `docs/` | Embedded API docs viewer |
+| `status/`, `maintenance/`, `offline/` | Operational pages |
+| `privacy/`, `terms/` | Legal pages |
+| `400/`, `401/`, `403/`, `408/`, `429/`, `500/`, `502/`, `503/` | Static error pages |
+| `error.tsx`, `global-error.tsx`, `not-found.tsx`, `forbidden/`, `loading.tsx` | Framework error/loading boundaries |
+| `layout.tsx`, `page.tsx`, `globals.css`, `manifest.ts` | Root shell |
+
+#### 3.1.1 `src/app/(dashboard)/dashboard/` โ UI pages
+
+`agents`, `analytics`, `api-manager`, `audit`, `auto-combo`, `batch`, `cache`,
+`changelog`, `cli-tools`, `cloud-agents`, `combos`, `compression`, `context`,
+`costs`, `endpoint`, `health`, `limits`, `logs`, `memory`, `onboarding`,
+`playground`, `providers`, `search-tools`, `settings`, `skills`, `system`,
+`translator`, `usage`, `webhooks`, plus root `page.tsx`, `HomePageClient.tsx`,
+`BootstrapBanner.tsx`.
+
+#### 3.1.2 `src/app/api/` โ Top-level API groups
+
+```
+src/app/api/
+โโโ a2a/{status, tasks}
+โโโ acp/
+โโโ admin/
+โโโ analytics/
+โโโ assess/
+โโโ auth/
+โโโ batches/
+โโโ cache/
+โโโ cli-tools/
+โโโ cloud/{codex-responses-ws}
+โโโ combos/
+โโโ compliance/
+โโโ compression/
+โโโ context/
+โโโ db/, db-backups/
+โโโ evals/
+โโโ fallback/
+โโโ files/
+โโโ health/
+โโโ init/
+โโโ internal/{concurrency}
+โโโ keys/
+โโโ logs/
+โโโ mcp/{audit, sse, status, stream, tools}
+โโโ memory/{health, [id]/, route.ts}
+โโโ model-combo-mappings/
+โโโ models/
+โโโ monitoring/
+โโโ oauth/
+โโโ openapi/
+โโโ policies/
+โโโ pricing/
+โโโ provider-metrics/, provider-models/, provider-nodes/
+โโโ providers/
+โโโ rate-limit/, rate-limits/
+โโโ resilience/
+โโโ restart/, shutdown/
+โโโ search/
+โโโ sessions/
+โโโ settings/
+โโโ skills/{executions, [id], install, marketplace, route.ts, skillssh}
+โโโ storage/
+โโโ sync/, synced-available-models/
+โโโ system/
+โโโ tags/
+โโโ telemetry/
+โโโ token-health/
+โโโ translator/
+โโโ tunnels/
+โโโ upstream-proxy/
+โโโ usage/
+โโโ v1/ OpenAI-compatible public API
+โโโ v1beta/ Gemini-style compat
+โโโ version-manager/
+โโโ webhooks/
+```
+
+#### 3.1.3 `src/app/api/v1/` โ OpenAI-compatible public API
+
+```
+v1/
+โโโ accounts/[id]/ account lookup
+โโโ agents/tasks/[id]/, agents/tasks/ A2A-flavored task endpoints
+โโโ api/ internal API helpers exposed under v1/api
+โโโ audio/{speech, transcriptions}/ TTS + STT
+โโโ batches/[id]/{cancel}, batches/ OpenAI Batches API
+โโโ chat/completions/ Chat Completions (the main endpoint)
+โโโ chatgpt-web/ ChatGPT-Web compat
+โโโ completions/ Legacy text completions
+โโโ embeddings/ Embeddings
+โโโ files/[id]/, files/ Files API
+โโโ _helpers/ Shared route helpers (no public URL)
+โโโ images/{edits, generations}/ Image gen + edit
+โโโ issues/ Triage helper endpoints
+โโโ management/{proxies}/ Management-scoped routes inside v1
+โโโ messages/{count_tokens}/ Anthropic-style messages compat
+โโโ models/ Model listing (`route.ts`, `catalog.ts`)
+โโโ moderations/ Moderation
+โโโ music/ Music gen
+โโโ providers/[provider]/ Per-provider operations
+โโโ quotas/{check} Quota probes
+โโโ registered-keys/ Registered key admin
+โโโ rerank/ Reranking
+โโโ responses/[...path]/ OpenAI Responses API (catch-all)
+โโโ search/ Web search
+โโโ videos/ Video gen
+โโโ ws/ WebSocket bridge
+โโโ route.ts Index handler
+```
+
+Every route file follows the same pattern:
+
+```
+Route โ CORS preflight โ Zod body validation โ optional auth
+ โ API key policy enforcement โ handler delegation (open-sse)
+```
+
+`v1beta/` is the Gemini-style compat surface (a thin wrapper that translates into
+the same `open-sse/handlers/` pipeline).
+
+### 3.2 `src/lib/` โ Core libraries
+
+Always import data, sync, OAuth, skill, memory, etc. through these modules. The
+table groups the actual directories and notable top-level files.
+
+| Module | Purpose |
+| ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `a2a/` | A2A protocol server: `taskManager.ts`, `streaming.ts`, `taskExecution.ts`, `routingLogger.ts`, `skills/` (5 skills: cost analysis, health report, provider discovery, quota management, smart routing) |
+| `acp/` | Agent-Control-Protocol: `index.ts`, `manager.ts`, `registry.ts` |
+| `api/` | Internal API helpers: `requireManagementAuth.ts`, `requireCliToolsAuth.ts`, `errorResponse.ts` |
+| `auth/` | `managementPassword.ts` (password reset / hashing) |
+| `batches/` | OpenAI Batches API service (`service.ts`) |
+| `catalog/` | OpenRouter catalog sync (`openrouterCatalog.ts`) |
+| `cloudAgent/` | Cloud agent registry: `api.ts`, `baseAgent.ts`, `db.ts`, `index.ts`, `registry.ts`, `types.ts`, `agents/{codex, devin, jules}.ts` |
+| `combos/` | Combo resolution helpers |
+| `compliance/` | Audit + provider audit: `index.ts`, `providerAudit.ts` |
+| `config/` | Runtime config glue |
+| `db/` | SQLite domain modules (see ยง3.2.1) |
+| `display/` | UI/display helpers used by API responses |
+| `embeddings/` | Embedding service registry |
+| `env/` | Env loading + introspection |
+| `evals/` | Eval runtime |
+| `guardrails/` | `piiMasker.ts`, `promptInjection.ts`, `visionBridge.ts`, `visionBridgeHelpers.ts`, `registry.ts`, `base.ts` |
+| `jobs/` | Background jobs (`autoUpdate.ts`, โฆ) |
+| `memory/` | Persistent memory: `store.ts`, `cache.ts`, `retrieval.ts`, `summarization.ts`, `extraction.ts`, `injection.ts`, `qdrant.ts`, `settings.ts`, `verify.ts`, `schemas.ts`, `types.ts` |
+| `monitoring/` | `observability.ts` |
+| `oauth/` | OAuth providers (14): `antigravity`, `claude`, `cline`, `codex`, `cursor`, `gemini`, `github`, `gitlab-duo`, `kilocode`, `kimi-coding`, `kiro`, `qoder`, `qwen`, `windsurf` plus `services/`, `utils/{pkce, server, banner, codexAuthFile, ui}`, `constants/oauth.ts` |
+| `plugins/` | Plugin loader (`index.ts`) |
+| `promptCache/` | `prefixAnalyzer.ts`, `index.ts` |
+| `providerModels/` | Managed model lifecycle: `modelDiscovery.ts`, `managedModelImport.ts`, `managedAvailableModels.ts`, `cursorAgent.ts` |
+| `providers/` | Provider helpers: `catalog.ts`, `validation.ts`, `imageValidation.ts`, `claudeExtraUsage.ts`, `codexConnectionDefaults.ts`, `codexFastTier.ts`, `webCookieAuth.ts`, `managedAvailableModels.ts`, `requestDefaults.ts` |
+| `resilience/` | `settings.ts` โ settings for circuit breaker, cooldown, lockout |
+| `runtime/` | Runtime feature detection |
+| `search/` | `executeWebSearch.ts` |
+| `skills/` | Skill framework: `registry.ts`, `executor.ts`, `interception.ts`, `injection.ts`, `sandbox.ts`, `custom.ts`, `hybrid.ts`, `builtins.ts`, `a2a.ts`, `providerSettings.ts`, `schemas.ts`, `skillssh.ts`, `types.ts`, plus `builtin/browser.ts` |
+| `spend/` | `batchWriter.ts` (write-behind buffer) |
+| `sync/` | `bundle.ts`, `tokens.ts` (Cloud Sync) |
+| `system/` | System-level helpers |
+| `translator/` | Top-level translator glue (delegates into `open-sse/translator/`) |
+| `usage/` | Usage accounting: `costCalculator.ts`, `tokenAccounting.ts`, `usageHistory.ts`, `aggregateHistory.ts`, `usageStats.ts`, `callLogs.ts`, `callLogArtifacts.ts`, `fetcher.ts`, `providerLimits.ts`, `migrations.ts` |
+| `versionManager/` | Auto-update + version manifest |
+| `ws/` | WebSocket bridge |
+| `zed-oauth/` | Zed editor OAuth flow |
+
+Top-level files in `src/lib/`:
+
+- `localDb.ts` โ re-export layer only. **Never** add logic here.
+- `proxyHealth.ts`, `proxyLogger.ts`, `tokenHealthCheck.ts`, `localHealthCheck.ts`
+- `oneproxyRotator.ts`, `oneproxySync.ts`
+- `apiBridgeServer.ts`, `cacheLayer.ts`, `semanticCache.ts`, `settingsCache.ts`
+- `cloudSync.ts`, `initCloudSync.ts`
+- `cloudflaredTunnel.ts`, `ngrokTunnel.ts`, `tailscaleTunnel.ts`
+- `consoleInterceptor.ts`, `container.ts`, `gracefulShutdown.ts`, `idempotencyLayer.ts`
+- `ipUtils.ts`, `logEnv.ts`, `logPayloads.ts`, `logRotation.ts`
+- `modelAliasSeed.ts`, `modelCapabilities.ts`, `modelMetadataRegistry.ts`, `modelsDevSync.ts`
+- `piiSanitizer.ts`, `pricingSync.ts`
+- `apiKeyExposure.ts`, `cacheControlSettings.ts`, `dataPaths.ts`, `toolPolicy.ts`
+- `translatorEvents.ts`, `usageDb.ts`, `usageAnalytics.ts`, `webhookDispatcher.ts`
+
+#### 3.2.1 `src/lib/db/`
+
+Singleton SQLite database (`getDbInstance()` in `core.ts`, WAL journaling).
+**Never write raw SQL in routes or handlers** โ go through these modules.
+
+Domain modules (each owns one or more tables): `apiKeys.ts`, `backup.ts`,
+`batches.ts`, `cleanup.ts`, `cliToolState.ts`, `combos.ts`,
+`commandCodeAuth.ts`, `compression.ts`, `compressionAnalytics.ts`,
+`compressionCacheStats.ts`, `compressionCombos.ts`, `compressionScheduler.ts`,
+`contextHandoffs.ts`, `core.ts`, `creditBalance.ts`, `databaseSettings.ts`,
+`detailedLogs.ts`, `domainState.ts`, `encryption.ts`, `evals.ts`, `files.ts`,
+`healthCheck.ts`, `jsonMigration.ts`, `migrationRunner.ts`,
+`modelComboMappings.ts`, `models.ts`, `oneproxy.ts`, `prompts.ts`,
+`providers.ts`, `providerLimits.ts`, `proxies.ts`, `quotaSnapshots.ts`,
+`readCache.ts`, `reasoningCache.ts`, `registeredKeys.ts`, `secrets.ts`,
+`sessionAccountAffinity.ts`, `settings.ts`, `stateReset.ts`, `stats.ts`,
+`syncTokens.ts`, `tierConfig.ts`, `upstreamProxy.ts`, `versionManager.ts`,
+`webhooks.ts`.
+
+`migrations/` holds 55 versioned `.sql` files (idempotent, transactional) and is
+executed by `migrationRunner.ts` at boot.
+
+Tables created across the migrations (52 total):
+
+`a`, `account_key_limits`, `api_keys`, `batches`, `call_logs`,
+`combo_adaptation_state`, `combos`, `command_code_auth_sessions`,
+`compression_analytics`, `compression_cache_stats`,
+`compression_combo_assignments`, `compression_combos`, `context_handoffs`,
+`daily_usage_summary`, `db_meta`, `domain_budgets`, `domain_circuit_breakers`,
+`domain_cost_history`, `domain_fallback_chains`, `domain_lockout_state`,
+`eval_cases`, `eval_runs`, `eval_suites`, `files`, `hourly_usage_summary`,
+`key_value`, `mcp_tool_audit`, `memories`, `model_combo_mappings`,
+`provider_connections`, `provider_key_limits`, `provider_nodes`,
+`proxy_assignments`, `proxy_logs`, `proxy_registry`, `quota_snapshots`,
+`reasoning_cache`, `registered_keys`, `request_detail_logs`,
+`routing_decisions`, `semantic_cache`, `session_account_affinity`,
+`skill_executions`, `skills`, `sync_tokens`, `tier_assignments`,
+`tier_config`, `upstream_proxy_config`, `usage_history`, `version_manager`,
+`webhooks` (plus FTS5 virtual tables for memory search).
+
+### 3.3 `src/domain/` โ Domain layer
+
+Pure business logic, no I/O. Imported by routes and handlers.
+
+| File | Purpose |
+| ------------------------------------------ | ------------------------------------------------- |
+| `policyEngine.ts` | Top-level policy resolver |
+| `fallbackPolicy.ts` | Fallback decision tree |
+| `costRules.ts` | Cost calculation rules |
+| `lockoutPolicy.ts` | Model lockout decisions |
+| `tagRouter.ts` | Tag-based routing |
+| `comboResolver.ts` | Combo resolution from request โ target list |
+| `connectionModelRules.ts` | Per-connection model filters |
+| `modelAvailability.ts` | Model availability check |
+| `degradation.ts` | Degraded-mode transitions |
+| `providerExpiration.ts` | Expired account/key detection |
+| `quotaCache.ts` | Cached quota decisions |
+| `responses.ts`, `omnirouteResponseMeta.ts` | Response shape helpers |
+| `configAudit.ts` | Config change audit |
+| `assessment/` | Model assessment (per RFC, partially implemented) |
+| `types.ts` | Shared domain types |
+
+### 3.4 `src/server/` โ Server-only
+
+Cannot be imported from client components.
+
+```
+server/
+โโโ auth/loginGuard.ts
+โโโ authz/
+โ โโโ classify.ts Classifies routes as public vs management
+โ โโโ assertAuth.ts Assertion helper
+โ โโโ context.ts Per-request authz context
+โ โโโ headers.ts
+โ โโโ pipeline.ts Authz pipeline
+โ โโโ policies/ Concrete policies
+โ โโโ types.ts
+โโโ cors/origins.ts CORS origin allowlist
+```
+
+### 3.5 `src/shared/` โ Safe-to-share
+
+Split into focused subdirectories:
+
+- `constants/` โ `providers.ts` (Zod-validated provider catalog), `models.ts`,
+ `modelSpecs.ts`, `modelCompat.ts`, `pricing.ts`, `cliTools.ts`,
+ `cliCompatProviders.ts`, `routingStrategies.ts`, `comboConfigMode.ts`,
+ `headers.ts`, `upstreamHeaders.ts` (denylist), `mcpScopes.ts`,
+ `errorCodes.ts`, `publicApiRoutes.ts`, `batch.ts`, `batchEndpoints.ts`,
+ `bodySize.ts`, `colors.ts`, `appConfig.ts`, `config.ts`,
+ `sidebarVisibility.ts`, `visionBridgeDefaults.ts`.
+- `validation/` โ `schemas.ts` (~80 Zod schemas), `compressionConfigSchemas.ts`,
+ `oneproxySchemas.ts`, `providerSchema.ts`, `settingsSchemas.ts`, `helpers.ts`.
+- `contracts/` โ public API contracts shipped to npm.
+- `types/` โ shared TS types.
+- `utils/` โ `circuitBreaker.ts`, `apiAuth.ts`, `apiKey.ts`, `apiKeyPolicy.ts`,
+ `apiResponse.ts`, `api.ts`, `classify429.ts`, `cliCompat.ts`, `clipboard.ts`,
+ `cloud.ts`, `cn.ts`, `cors.ts`, `costEstimator.ts`, `featureFlags.ts`,
+ `fetchTimeout.ts`, `formatting.ts`, `inputSanitizer.ts`, `logger.ts`,
+ `machine.ts`, `machineId.ts`, `maskEmail.ts`, `modelCatalogSearch.ts`,
+ `nodeRuntimeSupport.ts`, `parseApiKeys.ts`, `providerHints.ts`,
+ `providerModelAliases.ts`, `rateLimiter.ts`, `releaseNotes.ts`,
+ `a11yAudit.ts`, plus dashboard hooks/components under `services/`, `network/`,
+ `middleware/`, `schemas/`, `hooks/`, `components/`.
---
-### 4.2 Executors (`open-sse/executors/`)
+## 4. `open-sse/` โ Streaming engine workspace
-Executors encapsulate **provider-specific logic** using the **Strategy Pattern**. Each executor overrides base methods as needed.
+Separate npm workspace published as `@omniroute/open-sse`. Owns request
+processing, executors, translators, services, transformer, and the MCP server.
-```mermaid
-classDiagram
- class BaseExecutor {
- +buildUrl(model, stream, options)
- +buildHeaders(credentials, stream, body)
- +transformRequest(body, model, stream, credentials)
- +execute(url, options)
- +shouldRetry(status, error)
- +refreshCredentials(credentials, log)
- }
-
- class DefaultExecutor {
- +refreshCredentials()
- }
-
- class AntigravityExecutor {
- +buildUrl()
- +buildHeaders()
- +transformRequest()
- +shouldRetry()
- +refreshCredentials()
- }
-
- class CursorExecutor {
- +buildUrl()
- +buildHeaders()
- +transformRequest()
- +parseResponse()
- +generateChecksum()
- }
-
- class KiroExecutor {
- +buildUrl()
- +buildHeaders()
- +transformRequest()
- +parseEventStream()
- +refreshCredentials()
- }
-
- BaseExecutor <|-- DefaultExecutor
- BaseExecutor <|-- AntigravityExecutor
- BaseExecutor <|-- CursorExecutor
- BaseExecutor <|-- KiroExecutor
- BaseExecutor <|-- CodexExecutor
- BaseExecutor <|-- GeminiCLIExecutor
- BaseExecutor <|-- GithubExecutor
+```
+open-sse/
+โโโ index.ts Public exports
+โโโ package.json Workspace manifest
+โโโ tsconfig.json
+โโโ types.d.ts
+โโโ config/ Provider registries, header profiles, identity, โฆ
+โโโ handlers/ Request handlers (chat, embeddings, audio, image, โฆ)
+โโโ executors/ 31 provider-specific HTTP executors
+โโโ translator/ Format conversion (OpenAI โ Claude โ Gemini โ Cursor โ Kiro)
+โโโ transformer/ Responses API โ Chat Completions stream transformer
+โโโ services/ 80+ service modules (combos, fallback, quotas, identity, โฆ)
+โโโ utils/ Streaming helpers, TLS client, AWS SigV4, proxy fetch, โฆ
+โโโ mcp-server/ MCP server (3 transports, 13 scopes, 42 tools)
```
-| Executor | Provider | Key Specializations |
-| ---------------- | ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------- |
-| `base.ts` | โ | Abstract base: URL building, headers, retry logic, credential refresh |
-| `default.ts` | Claude, Gemini, OpenAI, GLM, Kimi, MiniMax | Generic OAuth token refresh for standard providers |
-| `antigravity.ts` | Google Cloud Code | Project/session ID generation, multi-URL fallback, custom retry parsing from error messages ("reset after 2h7m23s") |
-| `cursor.ts` | Cursor IDE | **Most complex**: SHA-256 checksum auth, Protobuf request encoding, binary EventStream โ SSE response parsing |
-| `codex.ts` | OpenAI Codex | Injects system instructions, manages thinking levels, removes unsupported parameters |
-| `gemini-cli.ts` | Google Gemini CLI | Custom URL building (`streamGenerateContent`), Google OAuth token refresh |
-| `github.ts` | GitHub Copilot | Dual token system (GitHub OAuth + Copilot token), VSCode header mimicking |
-| `kiro.ts` | AWS CodeWhisperer | AWS EventStream binary parsing, AMZN event frames, token estimation |
-| `index.ts` | โ | Factory: maps provider name โ executor class, with default fallback |
+### 4.1 `open-sse/handlers/`
+
+| Handler | Purpose |
+| ----------------------- | ------------------------------------------------------------------------ |
+| `chatCore.ts` | Main chat pipeline (cache, rate limit, combo routing, executor dispatch) |
+| `responsesHandler.ts` | OpenAI Responses API entry point |
+| `embeddings.ts` | Embeddings |
+| `imageGeneration.ts` | Image generation |
+| `audioSpeech.ts` | Text-to-speech |
+| `audioTranscription.ts` | Speech-to-text |
+| `videoGeneration.ts` | Video generation |
+| `musicGeneration.ts` | Music generation |
+| `rerank.ts` | Reranking |
+| `moderations.ts` | Moderation |
+| `search.ts` | Web search |
+| `sseParser.ts` | SSE event parser |
+| `usageExtractor.ts` | Pull token counts out of upstream streams |
+| `responseSanitizer.ts` | Strip provider-specific noise |
+| `responseTranslator.ts` | Glue between provider response and translator layer |
+
+### 4.2 `open-sse/executors/`
+
+31 provider executors, each extending `BaseExecutor` (`base.ts`):
+
+`antigravity`, `azure-openai`, `blackbox-web`, `chatgpt-web`, `cliproxyapi`,
+`cloudflare-ai`, `codex`, `commandCode`, `cursor`, `default`, `devin-cli`,
+`gemini-cli`, `github`, `gitlab`, `glm`, `grok-web`, `kie`, `kiro`,
+`muse-spark-web`, `nlpcloud`, `opencode`, `perplexity-web`, `petals`,
+`pollinations`, `puter`, `qoder`, `vertex`, `windsurf`, plus `claudeIdentity.ts`
+(shared identity helper) and `index.ts` (registry).
+
+> Note: providers not listed here are served by `default.ts` using the generic
+> OpenAI-compatible executor. The full provider catalog (179+ entries) lives in
+> `src/shared/constants/providers.ts`.
+
+### 4.3 `open-sse/translator/`
+
+Hub-and-spoke translation (OpenAI is the hub).
+
+- **9 request translators** (`translator/request/`):
+ `antigravity-to-openai`, `claude-to-gemini`, `claude-to-openai`,
+ `gemini-to-openai`, `openai-responses`, `openai-to-claude`,
+ `openai-to-cursor`, `openai-to-gemini`, `openai-to-kiro`.
+- **8 response translators** (`translator/response/`):
+ `claude-to-openai`, `cursor-to-openai`, `gemini-to-claude`, `gemini-to-openai`,
+ `kiro-to-openai`, `openai-responses`, `openai-to-antigravity`,
+ `openai-to-claude`.
+- **9 helpers** (`translator/helpers/`):
+ `claudeHelper`, `geminiHelper`, `geminiToolsSanitizer`, `maxTokensHelper`,
+ `openaiHelper`, `responsesApiHelper`, `schemaCoercion`, `toolCallHelper`, plus
+ helper tests.
+- **Image helpers** (`translator/image/sizeMapper.ts`).
+- Top-level: `bootstrap.ts`, `formats.ts`, `registry.ts`, `index.ts`.
+
+### 4.4 `open-sse/transformer/`
+
+- `responsesTransformer.ts` โ `TransformStream`-based Responses API โ Chat
+ Completions converter (used by the `responses/` route catch-all).
+
+### 4.5 `open-sse/services/`
+
+Highlights (full list under `open-sse/services/`):
+
+| Concern | Files |
+| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
+| Combo routing | `combo.ts` (14 strategies), `comboConfig.ts`, `comboMetrics.ts`, `comboManifestMetrics.ts`, `comboAgentMiddleware.ts` |
+| Auto Combo engine | `autoCombo/` โ `engine.ts`, `scoring.ts`, `taskFitness.ts`, `virtualFactory.ts`, `modePacks.ts`, `autoPrefix.ts`, `persistence.ts`, `providerDiversity.ts`, `providerRegistryAccessor.ts`, `routerStrategy.ts`, `selfHealing.ts`, `index.ts` |
+| Resilience | `accountFallback.ts` (cooldown + lockout), `errorClassifier.ts`, `emergencyFallback.ts`, `rateLimitManager.ts`, `rateLimitSemaphore.ts`, `accountSemaphore.ts`, `accountSelector.ts` |
+| Quotas | `quotaMonitor.ts`, `quotaPreflight.ts`, `bailianQuotaFetcher.ts`, `codexQuotaFetcher.ts`, `deepseekQuotaFetcher.ts`, `crofUsageFetcher.ts`, `antigravityCredits.ts` |
+| Provider-specific shaping | `claudeCodeCCH.ts`, `claudeCodeCompatible.ts`, `claudeCodeConstraints.ts`, `claudeCodeExtraRemap.ts`, `claudeCodeFingerprint.ts`, `claudeCodeObfuscation.ts`, `claudeCodeToolRemapper.ts`, `cloudCodeHeaders.ts`, `cloudCodeThinking.ts`, `geminiCliHeaders.ts`, `geminiThoughtSignatureStore.ts`, `gigachatAuth.ts`, `antigravityHeaders.ts`, `antigravityHeaderScrub.ts`, `antigravityIdentity.ts`, `antigravityObfuscation.ts`, `antigravityVersion.ts`, `antigravity429Engine.ts`, `chatgptTlsClient.ts`, `chatgptImageCache.ts`, `cursorSessionManager.ts`, `qoderCli.ts`, `qwenThinking.ts`, `modelscopePolicy.ts` |
+| Caching | `reasoningCache.ts`, `searchCache.ts`, `signatureCache.ts`, `requestDedup.ts` |
+| Routing intelligence | `intentClassifier.ts`, `taskAwareRouter.ts`, `backgroundTaskDetector.ts`, `volumeDetector.ts`, `wildcardRouter.ts`, `workflowFSM.ts`, `specificityDetector.ts`, `specificityRules.ts`, `specificityTypes.ts` |
+| Model handling | `modelCapabilities.ts`, `modelDeprecation.ts`, `modelFamilyFallback.ts`, `modelStrip.ts`, `model.ts`, `provider.ts`, `providerRequestDefaults.ts`, `providerCostData.ts`, `payloadRules.ts` |
+| Compression | `compression/` โ full compression engine wiring |
+| Token + session | `tokenRefresh.ts`, `sessionManager.ts`, `apiKeyRotator.ts`, `contextManager.ts`, `contextHandoff.ts`, `systemPrompt.ts`, `roleNormalizer.ts`, `responsesInputSanitizer.ts`, `responsesToolCallState.ts`, `toolSchemaSanitizer.ts`, `toolLimitDetector.ts`, `thinkingBudget.ts` |
+| Tier / manifest | `tierResolver.ts`, `tierConfig.ts`, `tierDefaults.json`, `tierTypes.ts`, `manifestAdapter.ts` |
+| IP / network | `ipFilter.ts`, `webSearchFallback.ts` |
+| Batches | `batchProcessor.ts` |
+| Usage | `usage.ts` |
+
+### 4.6 `open-sse/mcp-server/`
+
+- **31 registered tools** wired in `server.ts` (12 scoped under `schemas/tools.ts`,
+ 5 compression tools, 3 memory tools, 4 skills tools, plus advanced tools added
+ through `advancedTools.ts`).
+- **3 transports**: stdio, HTTP Streamable, SSE.
+- **13 scopes** declared in `src/shared/constants/mcpScopes.ts`.
+- Audit table: `mcp_tool_audit` (populated by `audit.ts`).
+- Files: `server.ts`, `index.ts`, `httpTransport.ts`, `audit.ts`, `scopeEnforcement.ts`,
+ `runtimeHeartbeat.ts`, `descriptionCompressor.ts`, `schemas/{tools, a2a, audit, index}.ts`,
+ `tools/{advancedTools, compressionTools, memoryTools, skillTools}.ts`,
+ plus tests under `__tests__/`.
+- See [MCP-SERVER.md](./MCP-SERVER.md) for the full tool catalog.
+
+### 4.7 `open-sse/config/`
+
+Provider registries (`providerRegistry.ts`, `providerModels.ts`,
+`providerHeaderProfiles.ts`), per-format model registries (`audioRegistry.ts`,
+`embeddingRegistry.ts`, `imageRegistry.ts`, `moderationRegistry.ts`,
+`musicRegistry.ts`, `rerankRegistry.ts`, `searchRegistry.ts`, `videoRegistry.ts`),
+identity helpers (`codexIdentity.ts`, `codexInstructions.ts`,
+`anthropicHeaders.ts`, `antigravityUpstream.ts`, `antigravityModelAliases.ts`,
+`cliFingerprints.ts`, `toolCloaking.ts`, `defaultThinkingSignature.ts`),
+credential helpers (`credentialLoader.ts`, `codexClient.ts`), and cloud
+adapters (`azureAi.ts`, `bedrock.ts`, `datarobot.ts`, `glmProvider.ts`,
+`maritalk.ts`, `oci.ts`, `petals.ts`, `runway.ts`, `sap.ts`, `watsonx.ts`,
+`ollamaModels.ts`, `errorConfig.ts`, `constants.ts`, `registryUtils.ts`).
+
+### 4.8 `open-sse/utils/`
+
+Streaming primitives and provider helpers: `stream.ts`, `streamHandler.ts`,
+`streamHelpers.ts`, `streamPayloadCollector.ts`, `streamReadiness.ts`,
+`sseHeartbeat.ts`, `proxyFetch.ts`, `proxyDispatcher.ts`, `tlsClient.ts`,
+`networkProxy.ts`, `awsSigV4.ts`, `cacheControlPolicy.ts`,
+`cursorChecksum.ts`, `cursorAgentProtobuf.ts`, `cursorVersionDetector.ts`,
+`comfyuiClient.ts`, `kieTask.ts`, `bypassHandler.ts`, `aiSdkCompat.ts`,
+`thinkTagParser.ts`, `urlSanitize.ts`, `usageTracking.ts`, `requestLogger.ts`,
+`progressTracker.ts`, `cors.ts`, `error.ts`, `logger.ts`, `sleep.ts`,
+`ollamaTransform.ts`.
---
-### 4.3 Handlers (`open-sse/handlers/`)
+## 5. `electron/` โ Desktop wrapper
-The **orchestration layer** โ coordinates translation, execution, streaming, and error handling.
-
-| File | Purpose |
-| --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| `chatCore.ts` | **Central orchestrator** (~600 lines). Handles the complete request lifecycle: format detection โ translation โ executor dispatch โ streaming/non-streaming response โ token refresh โ error handling โ usage logging. |
-| `responsesHandler.ts` | Adapter for OpenAI's Responses API: converts Responses format โ Chat Completions โ sends to `chatCore` โ converts SSE back to Responses format. |
-| `embeddings.ts` | Embedding generation handler: resolves embedding model โ provider, dispatches to provider API, returns OpenAI-compatible embedding response. Supports 6+ providers. |
-| `imageGeneration.ts` | Image generation handler: resolves image model โ provider, supports OpenAI-compatible, Gemini-image (Antigravity), and fallback (Nebius) modes. Returns base64 or URL images. |
-
-#### Request Lifecycle (chatCore.ts)
-
-```mermaid
-sequenceDiagram
- participant Client
- participant chatCore
- participant Translator
- participant Executor
- participant Provider
-
- Client->>chatCore: Request (any format)
- chatCore->>chatCore: Detect source format
- chatCore->>chatCore: Check bypass patterns
- chatCore->>chatCore: Resolve model & provider
- chatCore->>Translator: Translate request (source โ OpenAI โ target)
- chatCore->>Executor: Get executor for provider
- Executor->>Executor: Build URL, headers, transform request
- Executor->>Executor: Refresh credentials if needed
- Executor->>Provider: HTTP fetch (streaming or non-streaming)
-
- alt Streaming
- Provider-->>chatCore: SSE stream
- chatCore->>chatCore: Pipe through SSE transform stream
- Note over chatCore: Transform stream translates
each chunk: target โ OpenAI โ source
- chatCore-->>Client: Translated SSE stream
- else Non-streaming
- Provider-->>chatCore: JSON response
- chatCore->>Translator: Translate response
- chatCore-->>Client: Translated JSON
- end
-
- alt Error (401, 429, 500...)
- chatCore->>Executor: Retry with credential refresh
- chatCore->>chatCore: Account fallback logic
- end
```
+electron/
+โโโ main.js Electron main process
+โโโ preload.js Preload bridge (contextIsolation enabled)
+โโโ types.d.ts
+โโโ package.json electron-builder config, version 3.8.0
+โโโ README.md
+โโโ assets/ Build resources (icons, entitlements, โฆ)
+โโโ node_modules/ Dedicated node_modules (better-sqlite3, electron-updater)
+โโโ dist-electron/ Build output (not committed)
+```
+
+Five npm scripts at the workspace root: `electron:dev`, `electron:build`,
+`electron:build:{win,mac,linux}`, `electron:smoke:packaged`. Auto-update is via
+`electron-updater` pointing at the GitHub release feed.
---
-### 4.4 Services (`open-sse/services/`)
+## 6. `bin/` โ CLI
-Business logic that supports the handlers and executors.
-
-| File | Purpose |
-| -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| `provider.ts` | **Format detection** (`detectFormat`): analyzes request body structure to identify Claude/OpenAI/Gemini/Antigravity/Responses formats (includes `max_tokens` heuristic for Claude). Also: URL building, header building, thinking config normalization. Supports `openai-compatible-*` and `anthropic-compatible-*` dynamic providers. |
-| `model.ts` | Model string parsing (`claude/model-name` โ `{provider: "claude", model: "model-name"}`), alias resolution with collision detection, input sanitization (rejects path traversal/control chars), and model info resolution with async alias getter support. |
-| `accountFallback.ts` | Rate-limit handling: exponential backoff (1s โ 2s โ 4s โ max 2min), account cooldown management, error classification (which errors trigger fallback vs. not). |
-| `tokenRefresh.ts` | OAuth token refresh for **every provider**: Google (Gemini, Antigravity), Claude, Codex, Qwen, Qoder, GitHub (OAuth + Copilot dual-token), Kiro (AWS SSO OIDC + Social Auth). Includes in-flight promise deduplication cache and retry with exponential backoff. |
-| `combo.ts` | **Combo models**: chains of fallback models. If model A fails with a fallback-eligible error, try model B, then C, etc. Returns actual upstream status codes. |
-| `usage.ts` | Fetches quota/usage data from provider APIs (GitHub Copilot quotas, Antigravity model quotas, Codex rate limits, Kiro usage breakdowns, Claude settings). |
-| `accountSelector.ts` | Smart account selection with scoring algorithm: considers priority, health status, round-robin position, and cooldown state to pick the optimal account for each request. |
-| `contextManager.ts` | Request context lifecycle management: creates and tracks per-request context objects with metadata (request ID, timestamps, provider info) for debugging and logging. |
-| `ipFilter.ts` | IP-based access control: supports allowlist and blocklist modes. Validates client IP against configured rules before processing API requests. |
-| `sessionManager.ts` | Session tracking with client fingerprinting: tracks active sessions using hashed client identifiers, monitors request counts, and provides session metrics. |
-| `signatureCache.ts` | Request signature-based deduplication cache: prevents duplicate requests by caching recent request signatures and returning cached responses for identical requests within a time window. |
-| `systemPrompt.ts` | Global system prompt injection: prepends or appends a configurable system prompt to all requests, with per-provider compatibility handling. |
-| `thinkingBudget.ts` | Reasoning token budget management: supports passthrough, auto (strip thinking config), custom (fixed budget), and adaptive (complexity-scaled) modes for controlling thinking/reasoning tokens. |
-| `wildcardRouter.ts` | Wildcard model pattern routing: resolves wildcard patterns (e.g., `*/claude-*`) to concrete provider/model pairs based on availability and priority. |
-
-#### Token Refresh Deduplication
-
-```mermaid
-sequenceDiagram
- participant R1 as Request 1
- participant R2 as Request 2
- participant Cache as refreshPromiseCache
- participant OAuth as OAuth Provider
-
- R1->>Cache: getAccessToken("gemini", token)
- Cache->>Cache: No in-flight promise
- Cache->>OAuth: Start refresh
- R2->>Cache: getAccessToken("gemini", token)
- Cache->>Cache: Found in-flight promise
- Cache-->>R2: Return existing promise
- OAuth-->>Cache: New access token
- Cache-->>R1: New access token
- Cache-->>R2: Same access token (shared)
- Cache->>Cache: Delete cache entry
+```
+bin/
+โโโ omniroute.mjs Main CLI entry (Node ESM)
+โโโ reset-password.mjs Reset the management password from CLI
+โโโ mcp-server.mjs MCP server launcher (stdio)
+โโโ cli-commands.mjs Command dispatcher
+โโโ nodeRuntimeSupport.mjs Node version guard
+โโโ cli/
+ โโโ index.mjs
+ โโโ args.mjs
+ โโโ data-dir.mjs
+ โโโ encryption.mjs
+ โโโ io.mjs
+ โโโ provider-catalog.mjs
+ โโโ provider-store.mjs
+ โโโ provider-test.mjs
+ โโโ settings-store.mjs
+ โโโ sqlite.mjs
+ โโโ commands/
+ โโโ setup.mjs
+ โโโ doctor.mjs
+ โโโ providers.mjs
```
-#### Account Fallback State Machine
+Two binaries are exposed in `package.json` โ `bin`:
-```mermaid
-stateDiagram-v2
- [*] --> Active
- Active --> Error: Request fails (401/429/500)
- Error --> Cooldown: Apply backoff
- Cooldown --> Active: Cooldown expires
- Active --> Active: Request succeeds (reset backoff)
-
- state Error {
- [*] --> ClassifyError
- ClassifyError --> ShouldFallback: Rate limit / Auth / Transient
- ClassifyError --> NoFallback: 400 Bad Request
- }
-
- state Cooldown {
- [*] --> ExponentialBackoff
- ExponentialBackoff: Level 0 = 1s
- ExponentialBackoff: Level 1 = 2s
- ExponentialBackoff: Level 2 = 4s
- ExponentialBackoff: Max = 2min
- }
-```
-
-#### Combo Model Chain
-
-```mermaid
-flowchart LR
- A["Request with\ncombo model"] --> B["Model A"]
- B -->|"2xx Success"| C["Return response"]
- B -->|"429/401/500"| D{"Fallback\neligible?"}
- D -->|Yes| E["Model B"]
- D -->|No| F["Return error"]
- E -->|"2xx Success"| C
- E -->|"429/401/500"| G{"Fallback\neligible?"}
- G -->|Yes| H["Model C"]
- G -->|No| F
- H -->|"2xx Success"| C
- H -->|"Fail"| I["All failed โ\nReturn last status"]
-```
+- `omniroute` โ `bin/omniroute.mjs`
+- `omniroute-reset-password` โ `bin/reset-password.mjs`
---
-### 4.5 Translator (`open-sse/translator/`)
+## 7. `tests/`
-The **format translation engine** using a self-registering plugin system.
+| Directory | Type |
+| ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------ |
+| `tests/unit/` | Unit tests via Node native test runner (506 files, plus `api/`, `auth/`, `authz/` subdirs) |
+| `tests/integration/` | Cross-module + DB-state tests |
+| `tests/e2e/` | Playwright UI tests |
+| `tests/protocols-e2e/` | MCP/A2A protocol e2e |
+| `tests/translator/` | Translator-specific tests |
+| `tests/security/` | Security regressions |
+| `tests/load/` | Load / stress tests |
+| `tests/golden-set/` | Reference outputs for translator regressions |
+| `tests/helpers/`, `tests/fixtures/`, `tests/manual/`, `tests/scratch_test.mjs` | Support |
-#### Architecture
+Common commands:
-```mermaid
-graph TD
- subgraph "Request Translation"
- A["Claude โ OpenAI"]
- B["Gemini โ OpenAI"]
- C["Antigravity โ OpenAI"]
- D["OpenAI Responses โ OpenAI"]
- E["OpenAI โ Claude"]
- F["OpenAI โ Gemini"]
- G["OpenAI โ Kiro"]
- H["OpenAI โ Cursor"]
- end
-
- subgraph "Response Translation"
- I["Claude โ OpenAI"]
- J["Gemini โ OpenAI"]
- K["Kiro โ OpenAI"]
- L["Cursor โ OpenAI"]
- M["OpenAI โ Claude"]
- N["OpenAI โ Antigravity"]
- O["OpenAI โ Responses"]
- end
-```
-
-| Directory | Files | Description |
-| ------------ | ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| `request/` | 8 translators | Convert request bodies between formats. Each file self-registers via `register(from, to, fn)` on import. |
-| `response/` | 7 translators | Convert streaming response chunks between formats. Handles SSE event types, thinking blocks, tool calls. |
-| `helpers/` | 6 helpers | Shared utilities: `claudeHelper` (system prompt extraction, thinking config), `geminiHelper` (parts/contents mapping), `openaiHelper` (format filtering), `toolCallHelper` (ID generation, missing response injection), `maxTokensHelper`, `responsesApiHelper`. |
-| `index.ts` | โ | Translation engine: `translateRequest()`, `translateResponse()`, state management, registry. |
-| `formats.ts` | โ | Format constants: `OPENAI`, `CLAUDE`, `GEMINI`, `ANTIGRAVITY`, `KIRO`, `CURSOR`, `OPENAI_RESPONSES`. |
-
-#### Key Design: Self-Registering Plugins
-
-```javascript
-// Each translator file calls register() on import:
-import { register } from "../index.js";
-register("claude", "openai", translateClaudeToOpenAI);
-
-// The index.js imports all translator files, triggering registration:
-import "./request/claude-to-openai.js"; // โ self-registers
-```
+| Command | What it runs |
+| -------------------------------------------------------- | ---------------------------------------------------------------- |
+| `npm run test:unit` | All `tests/unit/*.test.ts` via Node test runner (concurrency 10) |
+| `npm run test:vitest` | Vitest suite (MCP, autoCombo, cache) |
+| `npm run test:e2e` | Playwright UI suite |
+| `npm run test:protocols:e2e` | MCP + A2A protocol e2e |
+| `npm run test:coverage` | Coverage gate (โฅ60% lines/statements/functions/branches) |
+| `node --import tsx/esm --test tests/unit/.test.ts` | Single file run |
---
-### 4.6 Utils (`open-sse/utils/`)
+## 8. `scripts/`
-| File | Purpose |
-| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
-| `error.ts` | Error response building (OpenAI-compatible format), upstream error parsing, Antigravity retry-time extraction from error messages, SSE error streaming. |
-| `stream.ts` | **SSE Transform Stream** โ the core streaming pipeline. Two modes: `TRANSLATE` (full format translation) and `PASSTHROUGH` (normalize + extract usage). Handles chunk buffering, usage estimation, content length tracking. Per-stream encoder/decoder instances avoid shared state. |
-| `streamHelpers.ts` | Low-level SSE utilities: `parseSSELine` (whitespace-tolerant), `hasValuableContent` (filters empty chunks for OpenAI/Claude/Gemini), `fixInvalidId`, `formatSSE` (format-aware SSE serialization with `perf_metrics` cleanup). |
-| `usageTracking.ts` | Token usage extraction from any format (Claude/OpenAI/Gemini/Responses), estimation with separate tool/message char-per-token ratios, buffer addition (2000 tokens safety margin), format-specific field filtering, console logging with ANSI colors. |
-| `requestLogger.ts` | Legacy file-based request logging helper kept for compatibility. Current deployments should prefer `APP_LOG_TO_FILE` for application logs and the call log pipeline for persisted request artifacts. |
-| `bypassHandler.ts` | Intercepts specific patterns from Claude CLI (title extraction, warmup, count) and returns fake responses without calling any provider. Supports both streaming and non-streaming. Intentionally limited to Claude CLI scope. |
-| `networkProxy.ts` | Resolves outbound proxy URL for a given provider with precedence: provider-specific config โ global config โ environment variables (`HTTPS_PROXY`/`HTTP_PROXY`/`ALL_PROXY`). Supports `NO_PROXY` exclusions. Caches config for 30s. |
+42 scripts. Highlights:
-#### SSE Streaming Pipeline
-
-```mermaid
-flowchart TD
- A["Provider SSE stream"] --> B["TextDecoder\n(per-stream instance)"]
- B --> C["Buffer lines\n(split on newline)"]
- C --> D["parseSSELine()\n(trim whitespace, parse JSON)"]
- D --> E{"Mode?"}
- E -->|TRANSLATE| F["translateResponse()\ntarget โ OpenAI โ source"]
- E -->|PASSTHROUGH| G["fixInvalidId()\nnormalize chunk"]
- F --> H["hasValuableContent()\nfilter empty chunks"]
- G --> H
- H -->|"Has content"| I["extractUsage()\ntrack token counts"]
- H -->|"Empty"| J["Skip chunk"]
- I --> K["formatSSE()\nserialize + clean perf_metrics"]
- K --> L["TextEncoder\n(per-stream instance)"]
- L --> M["Enqueue to\nclient stream"]
-
- style A fill:#f9f,stroke:#333
- style M fill:#9f9,stroke:#333
-```
-
-#### Request Logger Session Structure
-
-```
-logs/
-โโโ claude_gemini_claude-sonnet_20260208_143045/
- โโโ 1_req_client.json โ Raw client request
- โโโ 2_req_source.json โ After initial conversion
- โโโ 3_req_openai.json โ OpenAI intermediate format
- โโโ 4_req_target.json โ Final target format
- โโโ 5_res_provider.txt โ Provider SSE chunks (streaming)
- โโโ 5_res_provider.json โ Provider response (non-streaming)
- โโโ 6_res_openai.txt โ OpenAI intermediate chunks
- โโโ 7_res_client.txt โ Client-facing SSE chunks
- โโโ 6_error.json โ Error details (if any)
-```
+- **Build / runtime**: `build-next-isolated.mjs`, `prepare-electron-standalone.mjs`,
+ `run-next.mjs`, `run-next-playwright.mjs`, `run-standalone.mjs`,
+ `standalone-server-ws.mjs`, `responses-ws-proxy.mjs`, `v1-ws-bridge.mjs`,
+ `runtime-env.mjs`, `bootstrap-env.mjs`, `smoke-electron-packaged.mjs`.
+- **Checks**: `check-cycles.mjs`, `check-docs-sync.mjs`,
+ `check-route-validation.mjs`, `check-t11-any-budget.mjs`,
+ `check-pr-test-policy.mjs`, `check-supported-node-runtime.ts`,
+ `native-binary-compat.mjs`.
+- **Generators / sync**: `generate-docs-index.mjs`, `sync-env.mjs`,
+ `sync-cursor-models.mjs`, `migrate-env.mjs`.
+- **Install / publish**: `postinstall.mjs`, `postinstallSupport.mjs`,
+ `prepublish.ts`, `pack-artifact-policy.ts`, `validate-pack-artifact.ts`,
+ `uninstall.mjs`.
+- **Test runners**: `run-playwright-tests.mjs`, `run-ecosystem-tests.mjs`,
+ `run-protocol-clients-tests.mjs`, `test-report-summary.mjs`.
+- **Misc**: `healthcheck.mjs`, `dbsetup.js`, `system-info.mjs`,
+ `cursor-tap.cjs`, `scratch.mjs`, `i18n_autotranslate.py`,
+ `check_translations.py`, `validate_translation.py`.
---
-### 4.7 Application Layer (`src/`)
+## 9. Request Pipeline (Summary)
-| Directory | Purpose |
-| ------------- | ---------------------------------------------------------------------- |
-| `src/app/` | Web UI, API routes, Express middleware, OAuth callback handlers |
-| `src/lib/` | Database access (`localDb.ts`, `usageDb.ts`), authentication, shared |
-| `src/mitm/` | Man-in-the-middle proxy utilities for intercepting provider traffic |
-| `src/models/` | Database model definitions |
-| `src/shared/` | Wrappers around open-sse functions (provider, stream, error, etc.) |
-| `src/sse/` | SSE endpoint handlers that wire the open-sse library to Express routes |
-| `src/store/` | Application state management |
-
-#### Notable API Routes
-
-| Route | Methods | Purpose |
-| --------------------------------------------- | --------------- | ------------------------------------------------------------------------------------- |
-| `/api/provider-models` | GET/POST/DELETE | CRUD for custom models per provider |
-| `/api/models/catalog` | GET | Aggregated catalog of all models (chat, embedding, image, custom) grouped by provider |
-| `/api/settings/proxy` | GET/PUT/DELETE | Hierarchical outbound proxy configuration (`global/providers/combos/keys`) |
-| `/api/settings/proxy/test` | POST | Validates proxy connectivity and returns public IP/latency |
-| `/v1/providers/[provider]/chat/completions` | POST | Dedicated per-provider chat completions with model validation |
-| `/v1/providers/[provider]/embeddings` | POST | Dedicated per-provider embeddings with model validation |
-| `/v1/providers/[provider]/images/generations` | POST | Dedicated per-provider image generation with model validation |
-| `/api/settings/ip-filter` | GET/PUT | IP allowlist/blocklist management |
-| `/api/settings/thinking-budget` | GET/PUT | Reasoning token budget configuration (passthrough/auto/custom/adaptive) |
-| `/api/settings/system-prompt` | GET/PUT | Global system prompt injection for all requests |
-| `/api/sessions` | GET | Active session tracking and metrics |
-| `/api/rate-limits` | GET | Per-account rate limit status |
-
----
-
-## 5. Key Design Patterns
-
-### 5.1 Hub-and-Spoke Translation
-
-All formats translate through **OpenAI format as the hub**. Adding a new provider only requires writing **one pair** of translators (to/from OpenAI), not N pairs.
-
-### 5.2 Executor Strategy Pattern
-
-Each provider has a dedicated executor class inheriting from `BaseExecutor`. The factory in `executors/index.ts` selects the right one at runtime.
-
-### 5.3 Self-Registering Plugin System
-
-Translator modules register themselves on import via `register()`. Adding a new translator is just creating a file and importing it.
-
-### 5.4 Account Fallback with Exponential Backoff
-
-When a provider returns 429/401/500, the system can switch to the next account, applying exponential cooldowns (1s โ 2s โ 4s โ max 2min).
-
-### 5.5 Combo Model Chains
-
-A "combo" groups multiple `provider/model` strings. If the first fails, fallback to the next automatically.
-
-### 5.6 Stateful Streaming Translation
-
-Response translation maintains state across SSE chunks (thinking block tracking, tool call accumulation, content block indexing) via the `initState()` mechanism.
-
-### 5.7 Usage Safety Buffer
-
-A 2000-token buffer is added to reported usage to prevent clients from hitting context window limits due to overhead from system prompts and format translation.
-
----
-
-## 6. Supported Formats
-
-| Format | Direction | Identifier |
-| ----------------------- | --------------- | ------------------ |
-| OpenAI Chat Completions | source + target | `openai` |
-| OpenAI Responses API | source + target | `openai-responses` |
-| Anthropic Claude | source + target | `claude` |
-| Google Gemini | source + target | `gemini` |
-| Google Gemini CLI | target only | `gemini-cli` |
-| Antigravity | source + target | `antigravity` |
-| AWS Kiro | target only | `kiro` |
-| Cursor | target only | `cursor` |
-
----
-
-## 7. Supported Providers
-
-| Provider | Auth Method | Executor | Key Notes |
-| ------------------------ | ---------------------- | ----------- | --------------------------------------------- |
-| Anthropic Claude | API key or OAuth | Default | Uses `x-api-key` header |
-| Google Gemini | API key or OAuth | Default | Uses `x-goog-api-key` header |
-| Google Gemini CLI | OAuth | GeminiCLI | Uses `streamGenerateContent` endpoint |
-| Antigravity | OAuth | Antigravity | Multi-URL fallback, custom retry parsing |
-| OpenAI | API key | Default | Standard Bearer auth |
-| Codex | OAuth | Codex | Injects system instructions, manages thinking |
-| GitHub Copilot | OAuth + Copilot token | Github | Dual token, VSCode header mimicking |
-| Kiro (AWS) | AWS SSO OIDC or Social | Kiro | Binary EventStream parsing |
-| Cursor IDE | Checksum auth | Cursor | Protobuf encoding, SHA-256 checksums |
-| Qwen | OAuth | Default | Standard auth |
-| Qoder | OAuth (Basic + Bearer) | Default | Dual auth header |
-| OpenRouter | API key | Default | Standard Bearer auth |
-| GLM, Kimi, MiniMax | API key | Default | Claude-compatible, use `x-api-key` |
-| `openai-compatible-*` | API key | Default | Dynamic: any OpenAI-compatible endpoint |
-| `anthropic-compatible-*` | API key | Default | Dynamic: any Claude-compatible endpoint |
-
----
-
-## 8. Data Flow Summary
-
-### Streaming Request
-
-```mermaid
-flowchart LR
- A["Client"] --> B["detectFormat()"]
- B --> C["translateRequest()\nsource โ OpenAI โ target"]
- C --> D["Executor\nbuildUrl + buildHeaders"]
- D --> E["fetch(providerURL)"]
- E --> F["createSSEStream()\nTRANSLATE mode"]
- F --> G["parseSSELine()"]
- G --> H["translateResponse()\ntarget โ OpenAI โ source"]
- H --> I["extractUsage()\n+ addBuffer"]
- I --> J["formatSSE()"]
- J --> K["Client receives\ntranslated SSE"]
- K --> L["logUsage()\nsaveRequestUsage()"]
+```
+Client request
+ โ /v1/chat/completions (route.ts)
+ CORS preflight check
+ Zod validation (chatCompletionsSchema in shared/validation/schemas.ts)
+ Auth (extractApiKey + isValidApiKey OR requireManagementAuth)
+ Policy engine (src/server/authz/pipeline.ts)
+ Guardrails (PII masker, prompt injection, vision bridge)
+ โ handleChatCore() (open-sse/handlers/chatCore.ts)
+ Cache check (semantic + read cache)
+ Rate limit (rateLimitManager, accountSemaphore)
+ Combo routing (if model resolves to a combo)
+ comboResolver โ loop per target โ handleSingleModel()
+ translateRequest() (open-sse/translator/request/*)
+ getExecutor(providerId).execute() (open-sse/executors/*)
+ fetch upstream โ retry/backoff via accountFallback
+ translateResponse() (open-sse/translator/response/*)
+ SSE stream OR JSON response
+ If Responses API: TransformStream via open-sse/transformer/responsesTransformer.ts
+ โ Compliance audit (src/lib/compliance/)
+ โ Response to client
```
-### Non-Streaming Request
+### Resilience runtime state (three mechanisms)
-```mermaid
-flowchart LR
- A["Client"] --> B["detectFormat()"]
- B --> C["translateRequest()\nsource โ OpenAI โ target"]
- C --> D["Executor.execute()"]
- D --> E["translateResponse()\ntarget โ OpenAI โ source"]
- E --> F["Return JSON\nresponse"]
-```
+| Mechanism | Scope | Where |
+| ------------------------ | ----------------------------- | ------------------------------------------------------------------------------------------------------------ |
+| Provider circuit breaker | Whole provider | `src/shared/utils/circuitBreaker.ts`, persisted in `domain_circuit_breakers` |
+| Connection cooldown | One account/key | `markAccountUnavailable()` in `src/sse/services/auth.ts`; consumed by `accountFallback.checkFallbackError()` |
+| Model lockout | Provider + connection + model | `open-sse/services/accountFallback.ts`, persisted in `domain_lockout_state` |
-### Bypass Flow (Claude CLI)
+See [RESILIENCE_GUIDE.md](./RESILIENCE_GUIDE.md) and the dedicated section in
+[CLAUDE.md](../CLAUDE.md).
-```mermaid
-flowchart LR
- A["Claude CLI request"] --> B{"Match bypass\npattern?"}
- B -->|"Title/Warmup/Count"| C["Generate fake\nOpenAI response"]
- B -->|"No match"| D["Normal flow"]
- C --> E["Translate to\nsource format"]
- E --> F["Return without\ncalling provider"]
-```
+---
+
+## 10. How to Contribute
+
+### Add a new provider
+
+1. Register in `src/shared/constants/providers.ts` (Zod-validated at load).
+2. Add an executor in `open-sse/executors/` if custom logic is required
+ (extend `BaseExecutor`).
+3. Add a translator in `open-sse/translator/` if it does not speak OpenAI format.
+4. If OAuth-based, add config under `src/lib/oauth/providers/` and
+ `src/lib/oauth/services/`.
+5. Register models in `open-sse/config/providerRegistry.ts` (or the format-specific
+ registry under `open-sse/config/`).
+6. Write tests under `tests/unit/`.
+
+### Add a new API route
+
+1. Create `src/app/api/your-route/route.ts`.
+2. Follow the pattern: CORS โ Zod body validation โ auth โ handler delegation.
+3. If new request shape: add the Zod schema in `src/shared/validation/schemas.ts`.
+4. If management-only: add the path to `src/shared/constants/publicApiRoutes.ts`
+ (denylist for the public API surface).
+5. Add tests under `tests/unit/`.
+6. Update `docs/API_REFERENCE.md` and `docs/openapi.yaml`.
+
+### Add a new DB module
+
+1. Create `src/lib/db/yourModule.ts` and import `getDbInstance()` from `./core.ts`.
+2. Export CRUD functions for your domain.
+3. If new tables: add a migration under `src/lib/db/migrations/`, numbered
+ sequentially, idempotent, transactional.
+4. Re-export from `src/lib/localDb.ts` (re-export only โ **no logic**).
+5. Add tests under `tests/unit/`.
+
+### Add a new MCP tool
+
+1. Add the tool definition under `open-sse/mcp-server/tools/` (or extend
+ `open-sse/mcp-server/schemas/tools.ts`).
+2. Assign the appropriate scope(s) in `src/shared/constants/mcpScopes.ts`.
+3. Register the tool in `open-sse/mcp-server/server.ts`.
+4. Add tests under `open-sse/mcp-server/__tests__/`.
+5. Update [MCP-SERVER.md](./MCP-SERVER.md).
+
+### Add a new A2A skill
+
+See [A2A-SERVER.md ยง Adding a New Skill](./A2A-SERVER.md). Skills live in
+`src/lib/a2a/skills/` and are registered through the A2A task manager.
+
+---
+
+## 11. Conventions
+
+- **Code style**: 2-space indent, double quotes, 100 char width, semicolons,
+ `es5` trailing commas โ enforced by Prettier via `lint-staged`.
+- **Imports**: external โ internal (`@/`, `@omniroute/open-sse`) โ relative.
+- **Naming**: files `camelCase` or `kebab-case`, components `PascalCase`,
+ constants `UPPER_SNAKE`.
+- **ESLint**: `no-eval`, `no-implied-eval`, `no-new-func` = `error` everywhere;
+ `no-explicit-any` = `warn` in `open-sse/` and `tests/`, error elsewhere.
+- **TypeScript**: `strict: false` (legacy posture). Prefer explicit types over
+ inference for cross-module boundaries.
+- **Database**: never write raw SQL in routes or handlers โ always go through
+ `src/lib/db/` modules. Never add logic to `src/lib/localDb.ts`.
+- **Errors**: try/catch with specific error types, log with pino context. Never
+ silently swallow errors in SSE streams; use abort signals for cleanup.
+- **Security**: never use `eval()` / `new Function()` / implied eval. Validate
+ all inputs with Zod. Encrypt credentials at rest (AES-256-GCM). Keep
+ `src/shared/constants/upstreamHeaders.ts` denylist aligned with the
+ sanitize/validation layer.
+- **Commits**: Conventional Commits โ `feat(scope): subject`. Allowed scopes:
+ `db`, `sse`, `oauth`, `dashboard`, `api`, `cli`, `docker`, `ci`, `mcp`,
+ `a2a`, `memory`, `skills`.
+- **Branches**: prefixes `feat/`, `fix/`, `refactor/`, `docs/`, `test/`,
+ `chore/`. Never commit directly to `main`.
+- **Husky**: pre-commit runs `lint-staged` + `check:docs-sync` +
+ `check:any-budget:t11`; pre-push runs `npm run test:unit`.
+
+---
+
+## 12. Hard Rules (from CLAUDE.md)
+
+1. Never commit secrets or credentials.
+2. Never add logic to `src/lib/localDb.ts`.
+3. Never use `eval()` / `new Function()` / implied eval.
+4. Never commit directly to `main`.
+5. Never write raw SQL in routes โ always go through `src/lib/db/` modules.
+6. Never silently swallow errors in SSE streams.
+7. Always validate inputs with Zod schemas.
+8. Always include tests when changing production code.
+9. Coverage must stay โฅ 60% (statements, lines, functions, branches).
+
+---
+
+## 13. See Also
+
+- [ARCHITECTURE.md](./ARCHITECTURE.md) โ high-level architecture and module
+ responsibilities.
+- [API_REFERENCE.md](./API_REFERENCE.md) โ public + management API reference.
+- [FEATURES.md](./FEATURES.md) โ feature matrix and version highlights.
+- [RESILIENCE_GUIDE.md](./RESILIENCE_GUIDE.md) โ circuit breaker, cooldown,
+ lockout deep dive.
+- [AUTO-COMBO.md](./AUTO-COMBO.md) โ Auto Combo scoring and strategies.
+- [MCP-SERVER.md](./MCP-SERVER.md) โ full MCP tool catalog + transports.
+- [A2A-SERVER.md](./A2A-SERVER.md) โ A2A protocol skills and discovery.
+- [COMPRESSION_GUIDE.md](./COMPRESSION_GUIDE.md) โ RTK + Caveman compression.
+- [CLI-TOOLS.md](./CLI-TOOLS.md) โ CLI integrations.
+- [ELECTRON_GUIDE.md](./ELECTRON_GUIDE.md) (if present), [DOCKER_GUIDE.md](./DOCKER_GUIDE.md), [FLY_IO_DEPLOYMENT_GUIDE.md](./FLY_IO_DEPLOYMENT_GUIDE.md), [VM_DEPLOYMENT_GUIDE.md](./VM_DEPLOYMENT_GUIDE.md), [TERMUX_GUIDE.md](./TERMUX_GUIDE.md), [PWA_GUIDE.md](./PWA_GUIDE.md) โ deployment targets.
+- [TROUBLESHOOTING.md](./TROUBLESHOOTING.md) โ common operational issues.
+- [CONTRIBUTING.md](../CONTRIBUTING.md) โ contributor workflow.
+- [CLAUDE.md](../CLAUDE.md) โ repo rules for Claude Code (the source of truth
+ for many of the conventions above).
+- [AGENTS.md](../AGENTS.md) โ deeper architecture reference used by agents.
diff --git a/docs/I18N.md b/docs/I18N.md
index 50525515fc..458e5df371 100644
--- a/docs/I18N.md
+++ b/docs/I18N.md
@@ -6,24 +6,26 @@ OmniRoute supports **30 languages** with full dashboard UI translation, translat
## Quick Reference
-| Task | Command |
-|------|---------|
-| Generate translations | `node scripts/i18n/generate-multilang.mjs messages` |
-| Translate docs (LLM) | `python3 scripts/i18n_autotranslate.py --api-url --api-key --model ` |
-| Validate a locale | `python3 scripts/validate_translation.py quick -l cs` |
-| Check code keys | `python3 scripts/check_translations.py` |
-| Generate QA report | `node scripts/i18n/generate-qa-checklist.mjs` |
-| Visual QA (Playwright) | `node scripts/i18n/run-visual-qa.mjs` |
+| Task | Command |
+| ---------------------- | --------------------------------------------------------------------------------------- |
+| Generate translations | `node scripts/i18n/generate-multilang.mjs messages` |
+| Translate docs (LLM) | `python3 scripts/i18n_autotranslate.py --api-url --api-key --model ` |
+| Validate a locale | `python3 scripts/validate_translation.py quick -l cs` |
+| Check code keys | `python3 scripts/check_translations.py` |
+| Generate QA report | `node scripts/i18n/generate-qa-checklist.mjs` |
+| Visual QA (Playwright) | `node scripts/i18n/run-visual-qa.mjs` |
## Architecture
### Source of Truth
+
- **UI strings**: `src/i18n/messages/en.json` (English source, ~2800 keys)
- **Locale files**: `src/i18n/messages/{locale}.json` (30 translations)
- **Framework**: `next-intl` with cookie-based locale resolution
- **Config**: `src/i18n/config.ts` โ defines all 30 locales, language names, flags
### Runtime Flow
+
1. User selects language โ `NEXT_LOCALE` cookie set
2. `src/i18n/request.ts` resolves locale: cookie โ `Accept-Language` header โ fallback `en`
3. Dynamic import loads `messages/{locale}.json`
@@ -31,44 +33,46 @@ OmniRoute supports **30 languages** with full dashboard UI translation, translat
### Supported Locales
-| Code | Language | RTL | Google Translate Code |
-|------|----------|-----|----------------------|
-| `ar` | ุงูุนุฑุจูุฉ | Yes | `ar` |
-| `bg` | ะัะปะณะฐััะบะธ | No | `bg` |
-| `cs` | ฤeลกtina | No | `cs` |
-| `da` | Dansk | No | `da` |
-| `de` | Deutsch | No | `de` |
-| `es` | Espaรฑol | No | `es` |
-| `fi` | Suomi | No | `fi` |
-| `fr` | Franรงais | No | `fr` |
-| `he` | ืขืืจืืช | Yes | `iw` |
-| `hi` | เคนเคฟเคจเฅเคฆเฅ | No | `hi` |
-| `hu` | Magyar | No | `hu` |
-| `id` | Bahasa Indonesia | No | `id` |
-| `it` | Italiano | No | `it` |
-| `ja` | ๆฅๆฌ่ช | No | `ja` |
-| `ko` | ํ๊ตญ์ด | No | `ko` |
-| `ms` | Bahasa Melayu | No | `ms` |
-| `nl` | Nederlands | No | `nl` |
-| `no` | Norsk | No | `no` |
-| `phi` | Filipino | No | `tl` |
-| `pl` | Polski | No | `pl` |
-| `pt` | Portuguรชs (Portugal) | No | `pt` |
-| `pt-BR` | Portuguรชs (Brasil) | No | `pt` |
-| `ro` | Romรขnฤ | No | `ro` |
-| `ru` | ะ ัััะบะธะน | No | `ru` |
-| `sk` | Slovenฤina | No | `sk` |
-| `sv` | Svenska | No | `sv` |
-| `th` | เนเธเธข | No | `th` |
-| `tr` | Tรผrkรงe | No | `tr` |
-| `uk-UA` | ะฃะบัะฐัะฝััะบะฐ | No | `uk` |
-| `vi` | Tiแบฟng Viแปt | No | `vi` |
-| `zh-CN` | ไธญๆ (็ฎไฝ) | No | `zh-CN` |
+| Code | Language | RTL | Google Translate Code |
+| ------- | -------------------- | --- | --------------------- |
+| `ar` | ุงูุนุฑุจูุฉ | Yes | `ar` |
+| `bg` | ะัะปะณะฐััะบะธ | No | `bg` |
+| `cs` | ฤeลกtina | No | `cs` |
+| `da` | Dansk | No | `da` |
+| `de` | Deutsch | No | `de` |
+| `es` | Espaรฑol | No | `es` |
+| `fi` | Suomi | No | `fi` |
+| `fr` | Franรงais | No | `fr` |
+| `he` | ืขืืจืืช | Yes | `iw` |
+| `hi` | เคนเคฟเคจเฅเคฆเฅ | No | `hi` |
+| `hu` | Magyar | No | `hu` |
+| `id` | Bahasa Indonesia | No | `id` |
+| `it` | Italiano | No | `it` |
+| `ja` | ๆฅๆฌ่ช | No | `ja` |
+| `ko` | ํ๊ตญ์ด | No | `ko` |
+| `ms` | Bahasa Melayu | No | `ms` |
+| `nl` | Nederlands | No | `nl` |
+| `no` | Norsk | No | `no` |
+| `phi` | Filipino | No | `tl` |
+| `pl` | Polski | No | `pl` |
+| `pt` | Portuguรชs (Portugal) | No | `pt` |
+| `pt-BR` | Portuguรชs (Brasil) | No | `pt` |
+| `ro` | Romรขnฤ | No | `ro` |
+| `ru` | ะ ัััะบะธะน | No | `ru` |
+| `sk` | Slovenฤina | No | `sk` |
+| `sv` | Svenska | No | `sv` |
+| `th` | เนเธเธข | No | `th` |
+| `tr` | Tรผrkรงe | No | `tr` |
+| `uk-UA` | ะฃะบัะฐัะฝััะบะฐ | No | `uk` |
+| `vi` | Tiแบฟng Viแปt | No | `vi` |
+| `zh-CN` | ไธญๆ (็ฎไฝ) | No | `zh-CN` |
## Adding a New Language
### 1. Register the Locale
+
Edit `src/i18n/config.ts`:
+
```ts
// Add to LOCALES array
"xx",
@@ -77,7 +81,9 @@ Edit `src/i18n/config.ts`:
```
### 2. Add to Generator
+
Edit `scripts/i18n/generate-multilang.mjs` โ add entry to `LOCALE_SPECS`:
+
```js
{
code: "xx",
@@ -91,24 +97,30 @@ Edit `scripts/i18n/generate-multilang.mjs` โ add entry to `LOCALE_SPECS`:
```
### 3. Generate Initial Translation
+
```bash
node scripts/i18n/generate-multilang.mjs messages
```
+
This creates `src/i18n/messages/xx.json` auto-translated from `en.json` via Google Translate.
### 4. Review & Fix Auto-Translations
+
Auto-translations are a starting point. Review manually for:
+
- Technical accuracy
- Context-appropriate terminology
- Proper handling of placeholders (`{count}`, `{value}`, etc.)
### 5. Validate
+
```bash
python3 scripts/validate_translation.py quick -l xx
python3 scripts/validate_translation.py diff common -l xx
```
### 6. Generate Translated Documentation
+
```bash
node scripts/i18n/generate-multilang.mjs docs
```
@@ -123,15 +135,16 @@ node scripts/i18n/generate-multilang.mjs docs
node scripts/i18n/generate-multilang.mjs [messages|readme|docs|all]
```
-| Mode | What it does |
-|------|-------------|
-| `messages` | Translates missing keys in `src/i18n/messages/{locale}.json` from `en.json` |
-| `readme` | Translates `README.md` into all locales as `README.{code}.md` in project root |
-| `docs` | Translates `DOC_SOURCE_FILES` into `docs/i18n/{locale}/{docName}` |
-| `all` | Runs all three modes |
+| Mode | What it does |
+| ---------- | ----------------------------------------------------------------------------- |
+| `messages` | Translates missing keys in `src/i18n/messages/{locale}.json` from `en.json` |
+| `readme` | Translates `README.md` into all locales as `README.{code}.md` in project root |
+| `docs` | Translates `DOC_SOURCE_FILES` into `docs/i18n/{locale}/{docName}` |
+| `all` | Runs all three modes |
**Features:**
-- **Text protection**: Masks code blocks (```` ``` ````), inline code (`` ` ``), markdown links/images (`[text](url)`), HTML tags, tables, and ICU placeholders (`{count}`, `{value}`, `{total}`, etc.) before translation, then restores them
+
+- **Text protection**: Masks code blocks (` ``` `), inline code (`` ` ``), markdown links/images (`[text](url)`), HTML tags, tables, and ICU placeholders (`{count}`, `{value}`, `{total}`, etc.) before translation, then restores them
- **Chunked batching**: Joins multiple strings with `__OMNIROUTE_I18N_SEPARATOR__` delimiters to minimize API calls (max 1800 chars per request)
- **In-memory cache**: Avoids redundant API calls for repeated strings within a session
- **Retry logic**: Exponential backoff (up to 5 attempts with 300ms ร attempt delay) for 429/5xx errors
@@ -139,6 +152,7 @@ node scripts/i18n/generate-multilang.mjs [messages|readme|docs|all]
- **Skip existing**: If target file already exists, it is NOT overwritten
**Important behaviors:**
+
- `docs/i18n/README.md` is **regenerated** each run โ it's an auto-generated index of all docs
- Root `README.{code}.md` files are only created if they don't exist (skips locales in `EXISTING_README_CODES`)
- Language bars (`๐ **Languages:** ...`) are automatically inserted/updated in all translated docs
@@ -155,6 +169,7 @@ python3 scripts/i18n_autotranslate.py \
```
**Features:**
+
- Scans `docs/i18n/` markdown files for English paragraphs
- Skips code blocks, tables, and already-translated content
- Sends paragraphs to LLM with technical translation system prompt
@@ -189,6 +204,7 @@ python3 scripts/validate_translation.py -l cs
```
**Detects:**
+
- **Missing keys** โ keys in `en.json` but not in locale file
- **Extra keys** โ keys in locale file but not in `en.json`
- **Untranslated keys** โ keys where locale value equals English source (excluding allowlist)
@@ -228,6 +244,7 @@ node scripts/i18n/generate-qa-checklist.mjs
```
**Checks:**
+
- Fixed-width class usage (overflow risk)
- Directional left/right classes (RTL risk)
- Clipping-prone patterns
@@ -252,6 +269,7 @@ QA_ROUTES=/dashboard/settings,/dashboard/providers node scripts/i18n/run-visual-
```
**Detects:**
+
- Text overflow
- Element clipping
- RTL layout mismatches
@@ -279,6 +297,7 @@ Allowlist of keys that should remain identical to English source. Used by `valid
```
**What belongs here:**
+
- Brand/product names: `landing.brandName`, `common.social-github`
- Technical terms/acronyms: `health.cpu`, `mcpDashboard.pid`, `settings.ai`
- ICU/format strings: `apiManager.modelsCount`, `health.millisecondsShort`
@@ -307,6 +326,7 @@ python3 scripts/validate_translation.py quick -l '${{ matrix.lang }}'
```
**Dashboard output:**
+
```
## ๐ Translations
| Metric | Value |
@@ -360,6 +380,7 @@ docs/
## Best Practices
### When Editing Translations
+
1. **Always edit `en.json` first** โ it's the source of truth
2. **Run `generate-multilang.mjs messages`** to propagate new keys to all locales
3. **Review auto-translations** โ Google Translate is a starting point, not final
@@ -367,11 +388,13 @@ docs/
5. **Update `untranslatable-keys.json`** if a key should remain in English
### Placeholder Safety
+
- ICU placeholders (`{count}`, `{value}`, `{total}`, `{seconds}`) must be preserved exactly
- Plural formats (`{count, plural, one {# model} other {# models}}`) must maintain structure
- The validator detects placeholder mismatches automatically
### Adding New Translation Keys in Code
+
```tsx
// Use namespaced keys
const t = useTranslations("settings");
@@ -382,6 +405,7 @@ python3 scripts/check_translations.py --verbose
```
### RTL Considerations
+
- Arabic (`ar`) and Hebrew (`he`) are RTL locales
- Avoid hardcoded `left`/`right` CSS โ use `start`/`end` logical properties
- Visual QA catches RTL layout mismatches via `run-visual-qa.mjs`
@@ -389,21 +413,29 @@ python3 scripts/check_translations.py --verbose
## Known Issues & History
### `in.json` โ `hi.json` Fix
+
The generator originally used `code: "in"` (deprecated Google Translate code) for Hindi instead of the correct ISO 639-1 `hi`. This created an orphaned `in.json` duplicate of `hi.json`. Fixed by changing `code: "in"` to `code: "hi"` in `generate-multilang.mjs` and removing the orphaned file.
+> โ ๏ธ **Audit (2026-05-13):** The `docs/i18n/in/` directory still exists on disk (full duplicate of `hi/`). Translation generator no longer writes to it, but the historical tree was not pruned. Safe to delete with `rm -rf docs/i18n/in/` after confirming no external links reference the old path.
+
### `docs/i18n/README.md` Is Auto-Generated
+
The `docs/i18n/README.md` file is completely regenerated by `generate-multilang.mjs docs`. Any manual edits will be lost. Use `docs/I18N.md` (this file) for hand-written documentation that should persist.
### External Untranslatable Keys List
+
The `untranslatable-keys.json` allowlist was moved from an inline Python set in `validate_translation.py` to an external JSON file for easier maintenance. The validator loads it at runtime.
### `generate-multilang.mjs` Hindi Code Fix
+
The generator originally used `code: "in"` (deprecated Google Translate code) for Hindi instead of the correct ISO 639-1 `hi`. This was introduced in upstream commit `952b0b22c` by `diegosouzapw`. Fixed by changing `code: "in"` to `code: "hi"` in the `LOCALE_SPECS` array and removing the orphaned `in.json` file.
### `validate_translation.py` Ignored Count Output
+
The `quick` check now displays the count of ignored keys from `untranslatable-keys.json`:
+
```
Missing: 0
Untranslated: 0
-Ignored (UNTRANSLATABLE_KEYS): 236
+Ignored (UNTRANSLATABLE_KEYS):
```
diff --git a/docs/TERMUX_GUIDE.md b/docs/TERMUX_GUIDE.md
index 28012906e9..bdc13f15d3 100644
--- a/docs/TERMUX_GUIDE.md
+++ b/docs/TERMUX_GUIDE.md
@@ -12,6 +12,8 @@ pkg upgrade
pkg install nodejs-lts python build-essential git
```
+> **Node.js version:** OmniRoute requires Node `>=20.20.2 <21 || >=22.22.2 <23 || >=24.0.0 <27` (per `engines` in `package.json`). Termux's `nodejs-lts` typically ships Node 20 LTS, which is compatible. If `node --version` reports an older line, install `pkg install nodejs` (current) and verify the major matches a supported range.
+
If native package compilation fails, rerun the `pkg install` command above and then retry the OmniRoute install.
## Install
diff --git a/package.json b/package.json
index 5ceed4c903..03cee3da5f 100644
--- a/package.json
+++ b/package.json
@@ -83,6 +83,10 @@
"check:route-validation:t06": "node scripts/check-route-validation.mjs",
"check:any-budget:t11": "node scripts/check-t11-any-budget.mjs",
"check:docs-sync": "node scripts/check-docs-sync.mjs",
+ "check:env-doc-sync": "node scripts/check-env-doc-sync.mjs",
+ "check:docs-counts": "node scripts/check-docs-counts-sync.mjs",
+ "check:deprecated-versions": "node scripts/check-deprecated-versions.mjs",
+ "check:docs-all": "npm run check:docs-sync && npm run check:docs-counts && npm run check:env-doc-sync && npm run check:deprecated-versions",
"check:node-runtime": "node --import tsx/esm scripts/check-supported-node-runtime.ts",
"check:pack-artifact": "node --import tsx/esm scripts/validate-pack-artifact.ts",
"audit:deps": "npm audit --audit-level=moderate && npm run audit:electron",
diff --git a/scripts/check-deprecated-versions.mjs b/scripts/check-deprecated-versions.mjs
new file mode 100644
index 0000000000..990db76fe0
--- /dev/null
+++ b/scripts/check-deprecated-versions.mjs
@@ -0,0 +1,130 @@
+#!/usr/bin/env node
+// Detects hardcoded old versions / stale dates in docs that should follow the current release.
+// Uses hardcoded regexes to avoid dynamic RegExp() (ReDoS concern flagged by semgrep).
+// Exits 0 if clean, 1 (in --strict mode) if drift detected.
+//
+// Run: node scripts/check-deprecated-versions.mjs
+// Strict: node scripts/check-deprecated-versions.mjs --strict
+
+import fs from "node:fs";
+import path from "node:path";
+import { fileURLToPath } from "node:url";
+
+const __dirname = path.dirname(fileURLToPath(import.meta.url));
+const ROOT = path.resolve(__dirname, "..");
+const DOCS_DIR = path.join(ROOT, "docs");
+const PKG_JSON = path.join(ROOT, "package.json");
+const STRICT = process.argv.includes("--strict");
+
+function currentVersion() {
+ try {
+ return JSON.parse(fs.readFileSync(PKG_JSON, "utf8")).version || "0.0.0";
+ } catch {
+ return "0.0.0";
+ }
+}
+
+// Hardcoded set of obviously-stale version patterns. Update when bumping major/minor.
+// These match any version <= v3.6.x or any pre-3 major.
+const STALE_VERSION_PATTERNS = [
+ /\bv?[12]\.\d+\.\d+\b/, // 1.x.x / 2.x.x
+ /\bv?3\.[0-6]\.\d+\b/, // 3.0.x..3.6.x
+];
+
+const SAFE_CONTEXTS =
+ /(historical|archive|legacy|previously|deprecated|since|introduced|was|originally|fix|fixed)/i;
+
+// Dates older than 60 days are considered stale for "Last updated" / "Last consolidated".
+const STALE_DATE_DAYS = 60;
+const today = new Date();
+
+const LAST_UPDATED_RE = /Last (?:updated|consolidated|generated)[^\d]{0,30}(\d{4}-\d{2}-\d{2})/i;
+
+function isStaleDate(yyyy_mm_dd) {
+ const d = new Date(yyyy_mm_dd);
+ if (Number.isNaN(d.getTime())) return false;
+ const ageDays = (today - d) / (1000 * 60 * 60 * 24);
+ return ageDays > STALE_DATE_DAYS;
+}
+
+const IGNORED_DIRS = new Set(["archive", "i18n", "superpowers"]);
+const IGNORED_BASENAMES = new Set(["CHANGELOG.md", "RFC-AUTO-ASSESSMENT-DRAFT.md"]);
+
+function walkDocs(dir) {
+ const out = [];
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
+ if (IGNORED_DIRS.has(entry.name)) continue;
+ const full = path.join(dir, entry.name);
+ if (entry.isDirectory()) {
+ out.push(...walkDocs(full));
+ } else if (entry.isFile() && entry.name.endsWith(".md")) {
+ if (IGNORED_BASENAMES.has(entry.name)) continue;
+ out.push(full);
+ }
+ }
+ return out;
+}
+
+function main() {
+ const cur = currentVersion();
+ console.log(`Version drift report (current: v${cur})`);
+ console.log("=".repeat(40));
+
+ const files = walkDocs(DOCS_DIR);
+ let drift = 0;
+
+ for (const file of files) {
+ const rel = path.relative(ROOT, file);
+ const txt = fs.readFileSync(file, "utf8");
+ const lines = txt.split("\n");
+ const issues = [];
+
+ for (let i = 0; i < lines.length; i++) {
+ const line = lines[i];
+ if (line.length > 500) continue; // skip very long lines (likely code blocks / data)
+
+ // 1. Stale version refs
+ for (const re of STALE_VERSION_PATTERNS) {
+ const m = re.exec(line);
+ if (m && !SAFE_CONTEXTS.test(line)) {
+ issues.push({
+ line: i + 1,
+ type: "stale-version",
+ match: m[0],
+ text: line.trim().slice(0, 100),
+ });
+ break;
+ }
+ }
+ // 2. Stale "Last updated/consolidated/generated" dates
+ const dateMatch = LAST_UPDATED_RE.exec(line);
+ if (dateMatch && isStaleDate(dateMatch[1])) {
+ issues.push({
+ line: i + 1,
+ type: "stale-date",
+ match: dateMatch[1],
+ text: line.trim().slice(0, 100),
+ });
+ }
+ }
+
+ if (issues.length > 0) {
+ console.log(`\n ${rel}`);
+ for (const iss of issues.slice(0, 5)) {
+ console.log(` L${iss.line} [${iss.type}] ${iss.match}: ${iss.text}`);
+ }
+ if (issues.length > 5) console.log(` ... and ${issues.length - 5} more`);
+ drift += issues.length;
+ }
+ }
+
+ console.log();
+ if (drift > 0) {
+ console.warn(`โ ${drift} potential drift(s) detected across ${files.length} doc files.`);
+ if (STRICT) process.exit(1);
+ } else {
+ console.log(`โ No drift detected across ${files.length} doc files.`);
+ }
+}
+
+main();
diff --git a/scripts/check-docs-counts-sync.mjs b/scripts/check-docs-counts-sync.mjs
new file mode 100644
index 0000000000..833d52c5a5
--- /dev/null
+++ b/scripts/check-docs-counts-sync.mjs
@@ -0,0 +1,115 @@
+#!/usr/bin/env node
+// Validates that count-based assertions in docs match the actual code state.
+// Examples checked:
+// - executors count in open-sse/executors/
+// - routing strategies in src/shared/constants/routingStrategies.ts
+// - OAuth providers in src/lib/oauth/providers/
+// - A2A skills in src/lib/a2a/skills/
+// - Cloud agents in src/lib/cloudAgent/agents/
+//
+// Exits 0 on success, 1 on detected drift.
+// Run: node scripts/check-docs-counts-sync.mjs
+
+import fs from "node:fs";
+import path from "node:path";
+import { fileURLToPath } from "node:url";
+
+const __dirname = path.dirname(fileURLToPath(import.meta.url));
+const ROOT = path.resolve(__dirname, "..");
+
+const COMMON_NON_IMPL_BASENAMES = new Set([
+ "index.ts",
+ "index.mts",
+ "types.ts",
+ "base.ts",
+ "constants.ts",
+]);
+
+function countFiles(dir, suffix = ".ts") {
+ const abs = path.join(ROOT, dir);
+ if (!fs.existsSync(abs)) return 0;
+ return fs
+ .readdirSync(abs)
+ .filter(
+ (f) =>
+ f.endsWith(suffix) &&
+ !f.endsWith(".test.ts") &&
+ !f.startsWith("__") &&
+ !COMMON_NON_IMPL_BASENAMES.has(f)
+ ).length;
+}
+
+function countRoutingStrategies() {
+ const file = path.join(ROOT, "src", "shared", "constants", "routingStrategies.ts");
+ if (!fs.existsSync(file)) return 0;
+ const txt = fs.readFileSync(file, "utf8");
+ const m = txt.match(/ROUTING_STRATEGY_VALUES\s*=\s*\[([^\]]*)\]/);
+ if (!m) return 0;
+ return (m[1].match(/"[^"]+"/g) || []).length;
+}
+
+function docContains(docPath, needle) {
+ const abs = path.join(ROOT, "docs", docPath);
+ if (!fs.existsSync(abs)) return false;
+ return fs.readFileSync(abs, "utf8").includes(needle);
+}
+
+const checks = [
+ {
+ label: "Executors count",
+ actual: countFiles("open-sse/executors"),
+ docKey: "executors",
+ docs: ["ARCHITECTURE.md", "CODEBASE_DOCUMENTATION.md"],
+ },
+ {
+ label: "Routing strategies count",
+ actual: countRoutingStrategies(),
+ docKey: "strategies",
+ docs: ["AUTO-COMBO.md", "RESILIENCE_GUIDE.md"],
+ },
+ {
+ label: "OAuth providers count",
+ actual: countFiles("src/lib/oauth/providers"),
+ docKey: "OAuth providers",
+ docs: ["ARCHITECTURE.md"],
+ },
+ {
+ label: "A2A skills count",
+ actual: countFiles("src/lib/a2a/skills"),
+ docKey: "A2A skills",
+ docs: ["A2A-SERVER.md"],
+ },
+ {
+ label: "Cloud agents count",
+ actual: countFiles("src/lib/cloudAgent/agents"),
+ docKey: "cloud agents",
+ docs: ["CLOUD_AGENT.md", "AGENT_PROTOCOLS_GUIDE.md"],
+ },
+];
+
+let drift = 0;
+console.log("Docs counts sync report");
+console.log("=======================");
+
+for (const c of checks) {
+ console.log(`\nโข ${c.label}: ${c.actual} (real)`);
+ for (const doc of c.docs) {
+ const found = docContains(doc, String(c.actual));
+ if (found) {
+ console.log(` โ docs/${doc} mentions "${c.actual}"`);
+ } else {
+ console.log(` โ docs/${doc} does NOT mention "${c.actual}" for ${c.docKey}`);
+ drift++;
+ }
+ }
+}
+
+console.log();
+if (drift > 0) {
+ console.warn(`โ ${drift} potential drift(s) detected. Review the docs above.`);
+ // Soft-fail by default (count-based heuristic can false-positive).
+ // To enforce, pass --strict.
+ if (process.argv.includes("--strict")) process.exit(1);
+} else {
+ console.log("โ All checks pass.");
+}
diff --git a/scripts/check-env-doc-sync.mjs b/scripts/check-env-doc-sync.mjs
new file mode 100644
index 0000000000..8f03665604
--- /dev/null
+++ b/scripts/check-env-doc-sync.mjs
@@ -0,0 +1,131 @@
+#!/usr/bin/env node
+// Validates that env vars referenced in code appear in .env.example AND in docs/ENVIRONMENT.md.
+// Exits 0 on success, 1 on missing entries. Designed for use in pre-commit / CI.
+//
+// Run: node scripts/check-env-doc-sync.mjs
+// Strict mode: node scripts/check-env-doc-sync.mjs --strict
+// In strict mode, missing entries cause failure. In default mode, only summary is printed.
+
+import fs from "node:fs";
+import path from "node:path";
+import { fileURLToPath } from "node:url";
+import { execSync } from "node:child_process";
+
+const __dirname = path.dirname(fileURLToPath(import.meta.url));
+const ROOT = path.resolve(__dirname, "..");
+const ENV_EXAMPLE = path.join(ROOT, ".env.example");
+const ENV_DOC = path.join(ROOT, "docs", "ENVIRONMENT.md");
+
+const STRICT = process.argv.includes("--strict");
+
+// Vars that are intentionally not documented or detected in code via dynamic patterns.
+const IGNORE = new Set([
+ "NODE_ENV",
+ "PATH",
+ "HOME",
+ "USER",
+ "PWD",
+ "SHELL",
+ "TERM",
+ "TZ",
+ "LANG",
+ "LC_ALL",
+ "CI",
+ "GITHUB_ACTIONS",
+ "RUNNER_OS",
+ // Add false positives here as discovered.
+]);
+
+function readEnvExampleVars() {
+ if (!fs.existsSync(ENV_EXAMPLE)) {
+ console.error(`โ ${ENV_EXAMPLE} not found`);
+ process.exit(2);
+ }
+ const txt = fs.readFileSync(ENV_EXAMPLE, "utf8");
+ const vars = new Set();
+ for (const line of txt.split("\n")) {
+ // Match both "VAR=value" and "# VAR=value" (commented-out examples are still documented)
+ const m = line.match(/^#?\s*([A-Z][A-Z0-9_]+)\s*=/);
+ if (m) vars.add(m[1]);
+ }
+ return vars;
+}
+
+function readEnvDocVars() {
+ if (!fs.existsSync(ENV_DOC)) {
+ console.error(`โ ${ENV_DOC} not found`);
+ process.exit(2);
+ }
+ const txt = fs.readFileSync(ENV_DOC, "utf8");
+ const vars = new Set();
+ // Match `VAR_NAME` in inline code or table cells.
+ for (const m of txt.matchAll(/`([A-Z][A-Z0-9_]{2,})`/g)) {
+ vars.add(m[1]);
+ }
+ return vars;
+}
+
+function readCodeVars() {
+ const vars = new Set();
+ let stdout;
+ try {
+ stdout = execSync(
+ "grep -rhoE 'process\\.env\\.[A-Z][A-Z0-9_]+' src/ open-sse/ bin/ scripts/ 2>/dev/null || true",
+ { cwd: ROOT, encoding: "utf8", maxBuffer: 20 * 1024 * 1024 }
+ );
+ } catch (e) {
+ console.error(`โ grep failed: ${e.message}`);
+ process.exit(2);
+ }
+ for (const line of stdout.split("\n")) {
+ const m = line.match(/^process\.env\.([A-Z][A-Z0-9_]+)$/);
+ if (m && !IGNORE.has(m[1])) vars.add(m[1]);
+ }
+ return vars;
+}
+
+function diff(set, against) {
+ return [...set].filter((v) => !against.has(v)).sort();
+}
+
+function main() {
+ const codeVars = readCodeVars();
+ const exampleVars = readEnvExampleVars();
+ const docVars = readEnvDocVars();
+
+ const inCodeMissingExample = diff(codeVars, exampleVars);
+ const inCodeMissingDoc = diff(codeVars, docVars);
+ const inExampleMissingDoc = diff(exampleVars, docVars);
+ const inExampleMissingCode = diff(exampleVars, codeVars);
+
+ console.log("Env var sync report");
+ console.log("===================");
+ console.log(`Code references: ${codeVars.size} unique vars`);
+ console.log(`In .env.example: ${exampleVars.size} unique vars`);
+ console.log(`In docs/ENVIRONMENT.md: ${docVars.size} unique vars (heuristic)`);
+ console.log();
+
+ function printList(label, list) {
+ if (list.length === 0) {
+ console.log(` โ ${label}: none`);
+ } else {
+ console.log(` โ ${label}: ${list.length}`);
+ for (const v of list.slice(0, 30)) console.log(` - ${v}`);
+ if (list.length > 30) console.log(` ... and ${list.length - 30} more`);
+ }
+ }
+
+ printList("In code but missing from .env.example", inCodeMissingExample);
+ printList("In code but missing from ENVIRONMENT.md", inCodeMissingDoc);
+ printList("In .env.example but missing from ENVIRONMENT.md", inExampleMissingDoc);
+ printList("In .env.example but not referenced in code (dead?)", inExampleMissingCode);
+
+ const errors = inCodeMissingExample.length + inExampleMissingDoc.length;
+ if (STRICT && errors > 0) {
+ console.error(`\nโ ${errors} drift(s) detected (strict mode)`);
+ process.exit(1);
+ }
+ console.log(`\n${errors === 0 ? "โ" : "โ "} Done.`);
+}
+
+main();