RuleStack

Configs

Stacks

Compare

Diff

RuleStack

Configs

Stacks

Compare

Diff

Read API

RuleStack

Configs

Stacks

Compare

Diff

Read API

Diff/diegosouzapw-omniroute-agents ↔ diegosouzapw-omniroute-claude

Comparison

A · AGENTS.md · diegosouzapw/OmniRouteB · CLAUDE.md · diegosouzapw/OmniRoute
What each file covers, counted
DimensionSharedOnly in AOnly in BOverlap
Sections449364%
Commands14152625%
Section tags124171%

What each file covers

Sections

4 shared · 49 only in A · 36 only in B
  • − omniroute — Agent Guidelines
  • − Project
  • − Doc Accuracy Discipline (read before writing any doc)
  • − Stack
  • − Build, Lint, and Test Commands
  • − All tests (unit + vitest + ecosystem + e2e)
  • − Single test file (Node.js native test runner — most tests use this)
  • − Integration tests
  • − Vitest (MCP server, autoCombo)
  • − E2E with Playwright
  • − Protocol clients E2E (MCP transports, A2A)
  • − Ecosystem compatibility tests
  • − Coverage (see CONTRIBUTING.md)
  • − Code Style Guidelines
  • − Formatting (Prettier — enforced via lint-staged)
  • − TypeScript
  • − ESLint Rules
  • − Naming
  • − Imports
  • − Architecture
  • − Data Layer (`src/lib/db/`)
  • − API Route Layer (`src/app/api/v1/`)
  • − Request Pipeline (`open-sse/`)
  • − Provider Categories
  • − Executors (`open-sse/executors/`)
  • − Translator (`open-sse/translator/`)
  • − Transformer (`open-sse/transformer/`)
  • − Services (`open-sse/services/`)
  • − Domain Layer (`src/domain/`)
  • − MCP Server (`open-sse/mcp-server/`)
  • − A2A Server (`src/lib/a2a/`)
  • − ACP Module (`src/lib/acp/`)
  • − Memory System (`src/lib/memory/`)
  • − Skills System (`src/lib/skills/`)
  • − Compliance (`src/lib/compliance/`)
  • − MITM Proxy (`src/mitm/`)
  • − Middleware (`src/middleware/`)
  • − Guardrails (`src/lib/guardrails/`)
  • − Cloud Agents (`src/lib/cloudAgent/`)
  • − Evals (`src/lib/evals/`)
  • − Webhooks (`src/lib/webhookDispatcher.ts`)
  • − Authorization Pipeline (`src/server/authz/`)
  • − Reasoning Replay (`src/lib/db/reasoningCache.ts` + `open-sse/services/reasoningCache.ts`)
  • − Tunnels (`src/lib/{cloudflaredTunnel,ngrokTunnel}.ts` + `src/app/api/tunnels/`)
  • − Subdirectory AGENTS.md Files
  • − Reference Documentation (docs/)
  • − Fork / Upstream Workflow
  • − the default branch is the active release line, e.g. release/v3.8.49
  • − Review Focus
  • + CLAUDE.md
  • + Quick Start
  • + Single test file (Node.js native test runner — most tests)
  • + Vitest (MCP server, autoCombo, cache)
  • + All suites
  • + Project at a Glance
  • + Request Pipeline
  • + Resilience Runtime State
  • + Provider Circuit Breaker
  • + Connection Cooldown
  • + Model Lockout
  • + Debugging Guidance
  • + Key Conventions
  • + Code Style
  • + Database
  • + Common Modification Scenarios
  • + Adding a New API Route
  • + Adding a New DB Module
  • + Adding a New MCP Tool
  • + Adding a New A2A Skill
  • + Adding a New Cloud Agent
  • + Adding a New Embedded Service
  • + Adding a New Guardrail / Eval / Skill / Webhook event
  • + Reference Documentation
  • + Testing
  • + Planning & Research Artifacts (superpowers, deep-research)
  • + Git Workflow
  • + Never commit directly to main
  • + Worktree isolation (MANDATORY for every development task)
  • + Environment
  • + Quality Gates & Ratchets
  • + Hard Rules
  • + PII & Stream Sanitization Learnings
  • + 1. Regex Security (ReDoS)
  • + 2. SSE Snapshot Handling
  • + 3. Database Handles in Tests
  •   Running Tests
  •   Error Handling
  •   Security
  •   Adding a New Provider

Commands

14 shared · 15 only in A · 26 only in B
  • − node --import tsx/esm --test tests/unit/plan3-p0.test.ts
  • − node --import tsx/esm --test tests/unit/fixes-p1.test.ts
  • − node --import tsx/esm --test tests/unit/security-fase01.test.ts
  • − node --import tsx/esm --test tests/integration/*.test.ts
  • − git fetch upstream
  • − git switch -c <branch-name> upstream/release/vX.Y.Z
  • − npm run check:docs-all
  • − npm run check:fabricated-docs
  • − npm run build:release
  • − npm run start
  • − npm run build:cli
  • − npm run electron:dev
  • − npm run electron:build
  • − prettier --write
  • − npm run check:docs-counts
  • + npm install
  • + git checkout -b feat/your-feature
  • + git commit -m "feat: describe your change"
  • + git push -u origin feat/your-feature
  • + git fetch origin "$BASE_BRANCH"
  • + git worktree add ".claude/worktrees/${TASK##*/}" -b "$TASK" "origin/$BASE_BRANCH"
  • + npm run test:unit
  • + node --import tsx/esm --test tests/unit/file.test.ts
  • + npm run coverage:report
  • + git -C _tasks …
  • + docker
  • + git checkout
  • + git worktree remove .claude/worktrees/<dir>
  • + git branch -D <task>
  • + node:test
  • + bun:sqlite
  • + bun install -g omniroute
  • + npm ci
  • + node --import tsx
  • + bun
  • + bun: not found
  • + npm run quality:ratchet -- --update
  • + node
  • + gh issue list --repo diegosouzapw/OmniRoute --label release-freeze --state open
  • + gh pr edit <N> --base release/vX+1
  • + gh pr view <N> --json baseRefName
  •   npm run test:all
  •   node --import tsx/esm --test tests/unit/your-file.test.ts
  •   npm run test:vitest
  •   npm run test:e2e
  •   npm run test:protocols:e2e
  •   npm run test:ecosystem
  •   npm run test:coverage
  •   npm run dev
  •   npm run build
  •   npm run lint
  •   npm run typecheck:core
  •   npm run typecheck:noimplicit:core
  •   npm run check
  •   npm run check:cycles

Section tags

12 shared · 4 only in A · 1 only in B
  • − types
  • − testing-strategy
  • − performance
  • − monorepo
  • + setup
  •   build
  •   test
  •   lint-format
  •   code-style
  •   git-pr
  •   security
  •   database
  •   api
  •   deployment
  •   do-not
  •   agent-behaviour
  •   docs

Line diff

+430 added−458 removed143 unchanged23.8% identical
diegosouzapw/OmniRoute · AGENTS.md
@@ −1 @@
1# omniroute — Agent Guidelines
2 
3## Project
4 
5Unified AI proxy/router — route any LLM through one endpoint. Multi-provider support
6with **290 provider entries** (OpenAI, Anthropic, Gemini, DeepSeek, Groq, xAI, Mistral, Fireworks,
7Cohere, NVIDIA, Cerebras, Pollinations, Puter, Cloudflare AI, HuggingFace, DeepInfra,
8SambaNova, Meta Llama API, Moonshot AI, AI21 Labs, Databricks, Snowflake, and many more)
9with **MCP Server** (104 tools), **A2A v0.3 Protocol**, and **Electron desktop app**.
10 
11> **Live counts (v3.8.49)**: providers 290 · MCP tools 104 · MCP scopes 30 · A2A skills 6 ·
12> open-sse services 134 · routing strategies 17 · auto-combo scoring factors 12 ·
13> DB modules 95 · DB migrations 110 · base tables 17 · search providers 11 ·
14> i18n locales 42. **Refresh with `npm run check:docs-all`.**
 
 
 
 
 
 
 
15 
16## Doc Accuracy Discipline (read before writing any doc)
17 
18> **If `grep -rn "name" src/ open-sse/ bin/` returns nothing, the name does not exist. Do not document it.**
 
 
19 
20The recurring failure mode in AI-generated docs is _plausible-but-unverified specifics_.
21Every claim in a `.md` file under `docs/` should be verifiable against the source.
22 
23**Rules (enforced by `npm run check:fabricated-docs`):**
 
 
24 
251. **Never state an API name, endpoint, path, CLI command, or env var without grepping for it first.**
26 ```bash
27 grep -rn "theName" src/ open-sse/ bin/
28 # 0 hits → do not document
29 ```
302. **Never write a line count, file size, migration count, provider count, or strategy count from memory.**
31 ```bash
32 wc -l <file> # exact line count
33 ls <dir>/*.ts | wc -l # file count
34 ```
353. **Every code example should be copy-pasted from real usage or actually run** — not synthesized.
36 Link to a real call site (`path:line`) instead of inventing a signature.
374. **Prefer citing real source (`file.ts:line`) over paraphrasing behavior** — verifiable and self-correcting.
385. **A shorter doc that is 100% accurate beats a comprehensive one with fabrications.**
39 Wrong docs cost more than missing docs, because people trust and act on them.
40 
41The script `scripts/check/check-fabricated-docs.mjs` extracts every route path, env var, hook
42name, function name, and file reference from `docs/**/*.md` and verifies each one against the
43codebase. Run it locally before pushing docs; it runs in CI via `npm run check:docs-all`.
44 
45## Stack
46 
47- **Runtime**: Next.js 16 (App Router), Node.js `>=22.0.0 <23 || >=24.0.0 <27`, ES Modules (`"type": "module"`)
48- **Language**: TypeScript 6.0 (`src/`) + JavaScript (`open-sse/`, `electron/`)
49- **Database**: better-sqlite3 (SQLite) — `DATA_DIR` configurable, default `~/.omniroute/`
50- **Streaming**: SSE via `open-sse` internal workspace package
51- **Styling**: Tailwind CSS v4
52- **i18n**: next-intl with 42 locales (`src/i18n/messages/`) — refresh with `ls src/i18n/messages/*.json | wc -l`
53- **Desktop**: Electron (cross-platform: Windows, macOS, Linux)
54- **Schemas**: Zod v4 for all API / MCP input validation
55 
56---
57 
58## Build, Lint, and Test Commands
59 
60| Command | Description |
61| ----------------------------------- | ------------------------------------------------------------------ |
62| `npm run dev` | Start Next.js dev server |
63| `npm run build` | Production build: `next build` → `.build/next/` + assemble `dist/` |
64| `npm run build:release` | Clean rebuild + HEAD sentinel (`dist/BUILD_SHA`) — use for deploy |
65| `npm run start` | Run production build |
66| `npm run build:cli` | Build CLI package |
67| `npm run lint` | ESLint on all source files |
68| `npm run typecheck:core` | TypeScript core type checking |
69| `npm run typecheck:noimplicit:core` | Strict checking (no implicit any) |
70| `npm run check` | Run lint + test |
71| `npm run check:cycles` | Check for circular dependencies |
72| `npm run electron:dev` | Run Electron app in dev mode |
73| `npm run electron:build` | Build Electron app for current OS |
74 
75**Build output layout:**
 
 
 
 
 
 
 
 
 
 
 
 
 
76 
77| Directory | Purpose | Gitignored |
78| --------- | -------------------------------------------------- | ---------- |
79| `src/` | Application source (TypeScript / TSX) | No |
80| `.build/` | Build intermediates (`distDir = .build/next`) | Yes |
81| `dist/` | Shippable bundle assembled by `assembleStandalone` | Yes |
82 
83The pipeline is a single `next build` pass — intermediates land in `.build/next/`, the
84assembled bundle in `dist/`. VPS deploys rsync `dist/` into the remote
85`/usr/lib/node_modules/omniroute/app/` directory (VPS image path is unchanged).
86 
87### Running Tests
88 
89```bash
90# All tests (unit + vitest + ecosystem + e2e)
91npm run test:all
92 
93# Single test file (Node.js native test runner — most tests use this)
94node --import tsx/esm --test tests/unit/your-file.test.ts
95node --import tsx/esm --test tests/unit/plan3-p0.test.ts
96node --import tsx/esm --test tests/unit/fixes-p1.test.ts
97node --import tsx/esm --test tests/unit/security-fase01.test.ts
98 
99# Integration tests
100node --import tsx/esm --test tests/integration/*.test.ts
101 
102# Vitest (MCP server, autoCombo)
103npm run test:vitest
104 
105# E2E with Playwright
106npm run test:e2e
107 
108# Protocol clients E2E (MCP transports, A2A)
109npm run test:protocols:e2e
110 
111# Ecosystem compatibility tests
112npm run test:ecosystem
113 
114# Coverage (see CONTRIBUTING.md)
115npm run test:coverage
116```
 
 
 
 
 
 
 
 
 
 
117 
118**For authoritative coverage requirements, test execution, and PR gates, see [`CONTRIBUTING.md`](CONTRIBUTING.md#running-tests).**
119 
 
 
120---
121 
122## Code Style Guidelines
123 
124### Formatting (Prettier — enforced via lint-staged)
 
 
 
 
125 
1262 spaces · semicolons required · double quotes (`"`) · 100 char width · es5 trailing commas.
127Always run `prettier --write` on changed files.
128 
129### TypeScript
130 
131- **Target**: ES2022 · **Module**: `esnext` · **Resolution**: `bundler`
132- `strict: false` — prefer explicit types, don't rely on inference
133- Path aliases: `@/*` → `src/`, `@omniroute/open-sse` → `open-sse/`, `@omniroute/open-sse/*` → `open-sse/*`
134 
135### ESLint Rules
136 
137- **Security (error, everywhere)**: `no-eval`, `no-implied-eval`, `no-new-func`
138- **Relaxed in `open-sse/` and `tests/`**: `@typescript-eslint/no-explicit-any` = warn
139- React hooks rules and `@next/next/no-assign-module-variable` disabled in `open-sse/` and `tests/`
 
 
140 
141### Naming
142 
143| Element | Convention | Example |
144| ------------------- | -------------------------------- | ------------------------------------ |
145| Files | camelCase / kebab-case | `chatCore.ts`, `tokenHealthCheck.ts` |
146| React components | PascalCase | `Dashboard.tsx`, `ProviderCard.tsx` |
147| Functions/variables | camelCase | `getHealth()`, `switchCombo()` |
148| Constants | UPPER_SNAKE | `MAX_RETRIES`, `DEFAULT_TIMEOUT` |
149| Interfaces | PascalCase (`I` prefix optional) | `ProviderConfig` |
150| Enums | PascalCase (members too) | `LogLevel.Error` |
151 
152### Imports
153 
154- **Order**: external → internal (`@/`, `@omniroute/open-sse`) → relative (`./`, `../`)
155- **No barrel imports** from `localDb.ts` — import from the specific `db/` module instead
 
156 
157### Error Handling
158 
159- try/catch with specific error types; always log with context (pino logger)
160- Never silently swallow errors in SSE streams — use abort signals for cleanup
161- Return proper HTTP status codes (4xx client, 5xx server)
162 
163### Security
 
 
 
164 
165- **NEVER** commit API keys, secrets, or credentials
166- Validate all user inputs with Zod schemas
167- Auth middleware required on all API routes
168- Never log SQLite encryption keys
169- Sanitize user content (dompurify for HTML)
170- **Public upstream OAuth identifiers** (Gemini / Antigravity / Windsurf-style client_id/secret + Firebase Web keys extracted from public CLIs): use `resolvePublicCred()` from `open-sse/utils/publicCreds.ts`, **never** as string literals. Full pattern in `docs/security/PUBLIC_CREDS.md`.
171- **Error responses** (HTTP / SSE / executor / MCP): use `buildErrorBody()` or `sanitizeErrorMessage()` from `open-sse/utils/error.ts`, **never** put raw `err.stack` / `err.message` in a Response body. Full pattern in `docs/security/ERROR_SANITIZATION.md`.
172- **`exec()` / `spawn()` with runtime values**: pass via the `env` option, **never** string-interpolate paths/values into the script body. Reference: `src/mitm/cert/install.ts::updateNssDatabases`.
173- Prefer secure-by-default libraries when available — see [tldrsec/awesome-secure-defaults](https://github.com/tldrsec/awesome-secure-defaults) for the curated list (Helmet.js, DOMPurify, ssrf-req-filter, safe-regex, Google Tink, etc.).
174 
175---
176 
177## Architecture
178 
179### Data Layer (`src/lib/db/`)
 
180 
181All persistence uses SQLite through **95 domain-specific modules** in `src/lib/db/`. Top modules:
182 
183- Core: `core.ts`, `migrationRunner.ts`, `encryption.ts`, `stateReset.ts`
184- Providers / catalog: `providers.ts`, `models.ts`, `providerLimits.ts`, `compressionAnalytics.ts`
185- Routing: `combos.ts`, `modelComboMappings.ts`, `domainState.ts`, `commandCodeAuth.ts`
186- Auth: `apiKeys.ts`, `secrets.ts`, `registeredKeys.ts`, `sessionAccountAffinity.ts`
187- Usage / billing: `quotaSnapshots.ts`, `creditBalance.ts`, `usage*.ts`, `compressionCacheStats.ts`
188- Storage: `backup.ts`, `cleanup.ts`, `jsonMigration.ts`, `healthCheck.ts`, `databaseSettings.ts`
189- Extension modules: `evals.ts`, `webhooks.ts`, `reasoningCache.ts`, `readCache.ts`, `tierConfig.ts`, `compressionCombos.ts`, `compressionScheduler.ts`, `batches.ts`, `files.ts`, `syncTokens.ts`, `proxies.ts`, `oneproxy.ts`, `upstreamProxy.ts`, `versionManager.ts`, `cliToolState.ts`, `prompts.ts`, `detailedLogs.ts`, `contextHandoffs.ts`, `compression.ts`, `stats.ts`
190 
191Live count: `ls src/lib/db/*.ts | wc -l` (currently 95). Drift detection: `npm run check:docs-counts`.
192Schema migrations live in `db/migrations/` (**110 files** as of v3.8.43) and run via `migrationRunner.ts`.
193`src/lib/localDb.ts` is a **re-export layer only** — never add logic there.
194 
195#### DB Internals
 
 
 
 
 
 
 
196 
197- **`core.ts`**: `getDbInstance()` returns a singleton `better-sqlite3` instance with WAL
198 journaling. `SCHEMA_SQL` defines **17 base tables** (verify with `grep -c "CREATE TABLE" src/lib/db/core.ts` minus 1 for the bookkeeping `_omniroute_migrations` table). Helpers: `rowToCamel`, `encryptConnectionFields`.
199- **`migrationRunner.ts`**: Applies versioned SQL files from `db/migrations/` inside transactions.
200 Tracks applied migrations in `_omniroute_migrations` table.
201- **Migrations**: 110 files (`001_initial_schema.sql` → `110_*.sql`).
202 Each migration is idempotent and runs in a transaction. Live count: `ls src/lib/db/migrations/*.sql | wc -l`.
203- **Domain modules** import `getDbInstance()` from `core.ts` for all CRUD operations.
204 Each module owns a specific table/set of tables (e.g., `providers.ts` → `provider_connections`,
205 `combos.ts` → `combos`). Encryption helpers protect sensitive fields at rest.
206- **`localDb.ts`** re-exports all domain modules — consumers import from here for convenience.
207 
208### API Route Layer (`src/app/api/v1/`)
209 
210Next.js App Router routes — each follows a consistent pattern:
211 
212```
213Route → CORS preflight → Body validation (Zod) → Optional auth (extractApiKey/isValidApiKey)
214 → API key policy enforcement (enforceApiKeyPolicy) → Handler delegation (open-sse)
215```
216 
217| Route | Handler | Notes |
218| ------------------------------- | ------------------------- | ------------------------------------------------------------- |
219| `chat/completions/route.ts` | `handleChat()` | + prompt injection guard (clones request) |
220| `responses/route.ts` | `handleChat()` (unified) | Responses API format |
221| `embeddings/route.ts` | `handleEmbedding()` | Model listing + creation |
222| `images/generations/route.ts` | `handleImageGeneration()` | Model listing + creation |
223| `audio/transcriptions/route.ts` | audio handler | Multipart form data |
224| `audio/speech/route.ts` | TTS handler | Binary audio response |
225| `videos/generations/route.ts` | video handler | ComfyUI/SD WebUI |
226| `music/generations/route.ts` | music handler | ComfyUI workflows |
227| `moderations/route.ts` | moderation handler | Content safety |
228| `rerank/route.ts` | rerank handler | Document relevance |
229| `search/route.ts` | search handler | Web search (12 providers per `open-sse/handlers/search.ts:6`) |
230 
231**No global Next.js middleware file** — interception is route-specific. Auth is optional
232(controlled by `REQUIRE_API_KEY` env). Prompt injection guard is unique to chat completions.
233 
234### Request Pipeline (`open-sse/`)
 
 
 
 
235 
236The `open-sse/` workspace is the core streaming engine. Full request flow:
237 
238```
239Client Request
240 → src/app/api/v1/.../route.ts (Next.js route)
241 → open-sse/handlers/chatCore.ts::handleChatCore()
242 → Semantic/signature cache check
243 → Rate limit check (rateLimitManager)
244 → Combo routing? → open-sse/services/combo.ts::handleComboChat()
245 → resolveComboTargets() → ordered ResolvedComboTarget[]
246 → For each target: handleSingleModel() (wraps chatCore)
247 → translateRequest() (open-sse/translator/)
248 → Convert source format (e.g., OpenAI) → target format (e.g., Claude)
249 → getExecutor() → provider-specific executor instance
250 → executor.execute() (BaseExecutor → DefaultExecutor or provider-specific)
251 → buildUrl() + buildHeaders() + transformRequest()
252 → fetch() to upstream provider
253 → Retry logic with exponential backoff
254 → Response translation back to client format
255 → If Responses API: responsesTransformer.ts TransformStream
256 → SSE stream or JSON response to client
257```
258 
259**Handlers** (`open-sse/handlers/`): `chatCore.ts`, `responsesHandler.ts`, `embeddings.ts`,
260`imageGeneration.ts`, `videoGeneration.ts`, `musicGeneration.ts`, `audioSpeech.ts`,
261`audioTranscription.ts`, `moderations.ts`, `rerank.ts`, `search.ts`.
262 
263**Upstream headers**: merged after default auth; same header name replaces executor value.
264**T5 intra-family fallback** recomputes headers using only the fallback model id.
265Forbidden header names: `src/shared/constants/upstreamHeaders.ts` — keep sanitize,
266Zod schemas, and unit tests aligned when editing.
267 
268### Provider Categories
269 
270- **Free** (2): Qoder AI, Kiro AI
271- **OAuth** (13): Claude Code, Antigravity, Codex, GitHub Copilot, Cursor, Kimi Coding, Kilo Code, Cline, Kiro, Qoder, Gemini, Windsurf (v3.8), GitLab Duo (v3.8)
272- **API Key** (120+): OpenAI, Anthropic, Gemini, DeepSeek, Groq, xAI, Mistral, Perplexity,
273 Together, Fireworks, Cerebras, Cohere, NVIDIA, Nebius, SiliconFlow, Hyperbolic,
274 HuggingFace, OpenRouter, Vertex AI, Cloudflare AI, Scaleway, AI/ML API, Pollinations,
275 Puter, Longcat, Alibaba, Kimi, Minimax, Blackbox, Synthetic, Kilo Gateway,
276 Z.AI, GLM, Deepgram, AssemblyAI, ElevenLabs, Cartesia, PlayHT, Inworld,
277 NanoBanana, SD WebUI, ComfyUI, Ollama Cloud, Perplexity Search, Serper, Brave, Exa,
278 Tavily, OpenCode Zen/Go, Bailian Coding Plan, DeepInfra, Vercel AI Gateway,
279 Lambda AI, SambaNova, nScale, OVHcloud AI, Baseten, PublicAI, Moonshot AI,
280 Meta Llama API, v0 (Vercel), Morph, Featherless AI, FriendliAI, LlamaGate,
281 Galadriel, Weights & Biases Inference, Volcengine, AI21 Labs, Venice.ai,
282 Codestral, Upstage, Maritalk, Xiaomi MiMo, Inference.net, NanoGPT, Predibase,
283 Bytez, Heroku AI, Databricks, Snowflake Cortex, GigaChat (Sber), CrofAI,
284 AgentRouter, ChatGPT Web, Baidu Qianfan, AWS Polly, RunwayML, GitLab Duo,
285 Amazon Q, Empower, Poe, and many more.
286- **Self-Hosted** (8+): LM Studio, vLLM, Lemonade, Llamafile, Triton, Docker Model Runner, Xinference, Oobabooga
287- **Custom**: OpenAI-compatible (`openai-compatible-*`) and Anthropic-compatible (`anthropic-compatible-*`) prefixes
288 
289Providers are registered in `src/shared/constants/providers.ts` with Zod validation at module load.
 
290 
291### Executors (`open-sse/executors/`)
292 
293Provider-specific request executors: `base.ts`, `default.ts`, `cursor.ts`, `codex.ts`,
294`antigravity.ts`, `github.ts`, `kiro.ts`, `qoder.ts`, `vertex.ts`,
295`cloudflare-ai.ts`, `opencode.ts`, `pollinations.ts`, `puter.ts`.
296 
297#### Executor Internals
 
298 
299- **`base.ts`** (`BaseExecutor`): Abstract base with `buildUrl()`, `buildHeaders()`,
300 `transformRequest()`, retry logic (exponential backoff), and `execute()`. Subclasses
301 override URL/header/transform methods for provider-specific behavior.
302- **`default.ts`** (`DefaultExecutor extends BaseExecutor`): Handles most OpenAI-compatible
303 providers. Reads provider config from `providerRegistry.ts` to resolve base URL, auth
304 header format, and request transformations.
305- **`getExecutor()`** (`executors/index.ts`): Factory that returns the correct executor
306 instance based on provider ID. Provider-specific executors (Cursor, Codex, Vertex, etc.)
307 override only what differs from the default.
308 
309### Translator (`open-sse/translator/`)
 
 
 
 
 
 
 
 
 
310 
311Translates between API formats (OpenAI-format ↔ Anthropic, Gemini, etc.).
312Includes request/response translators with helpers for image handling.
313 
314#### Translator Internals
315 
316- **`translator/index.ts`**: Exports `translateRequest()` and format constants. Called by
317 `chatCore.ts` before executor dispatch.
318- **Flow**: `translateRequest(body, sourceFormat, targetFormat)` → detects source format
319 (OpenAI, Anthropic, Gemini) → applies the matching translator module → returns
320 transformed body ready for the target provider.
321- **Response translation** runs in reverse after upstream response, converting back to
322 the client's expected format.
323 
324### Transformer (`open-sse/transformer/`)
 
 
 
 
325 
326`responsesTransformer.ts` — transforms Responses API format to/from Chat Completions format.
327 
328#### Transformer Internals
 
 
 
 
329 
330- **`createResponsesApiTransformStream()`**: Returns a `TransformStream` that converts
331 Chat Completions SSE chunks (`data: {"choices":[...]}`) into Responses API SSE events
332 (`response.output_item.added`, `response.output_text.delta`, etc.).
333- Used when the client sends a Responses API request: the request is internally converted
334 to Chat Completions format, dispatched normally, and the response is piped through this
335 transform stream before reaching the client.
336 
337### Services (`open-sse/services/`)
 
 
338 
339134 service modules in `open-sse/services/` (top-level only; more including sub-dirs like `autoCombo/` and `compression/`). Refresh: `ls open-sse/services/*.ts | wc -l`. Key modules:
340`combo.ts` (routing engine), `usage.ts`, `tokenRefresh.ts`,
341`rateLimitManager.ts`, `accountFallback.ts`, `sessionManager.ts`, `wildcardRouter.ts`,
342`autoCombo/`, `intentClassifier.ts`, `taskAwareRouter.ts`, `thinkingBudget.ts`,
343`contextManager.ts`, `modelDeprecation.ts`, `modelFamilyFallback.ts`,
344`emergencyFallback.ts`, `workflowFSM.ts`, `backgroundTaskDetector.ts`, `ipFilter.ts`,
345`signatureCache.ts`, `volumeDetector.ts`, `contextHandoff.ts`, `compression/` (prompt
346compression pipeline), and more.
347 
348#### Prompt Compression Pipeline (`compression/`)
 
 
 
 
 
 
 
349 
350Modular prompt compression that runs proactively before the existing reactive context manager.
351 
352- **`strategySelector.ts`**: Selects compression mode based on config, compression combo assignments,
353 combo overrides, auto-trigger thresholds, and defaults. Priority: assigned compression combo >
354 combo override > auto-trigger > default mode > off.
355- **`lite.ts`**: 5 lite-mode techniques: `collapseWhitespace`, `dedupSystemPrompt`,
356 `compressToolResults`, `removeRedundantContent`, `replaceImageUrls`. Target: 10-15% savings at
357 <1ms latency.
358- **`caveman.ts` / `cavemanRules.ts`**: Caveman-style semantic condensation backed by built-in
359 rules plus file-loaded language packs under `compression/rules/`.
360- **`engines/rtk/`**: Rule-based terminal/tool-output compression inspired by RTK patterns. Detects
361 command output classes, applies JSON filter packs, deduplicates repeated lines, strips ANSI/code
362 noise, and preserves errors/actionable context. The RTK JSON DSL supports replace,
363 match-output short-circuit, strip/keep, per-line truncation, head/tail/max-line truncation,
364 inline tests, trust-gated project/global custom filters, and optional redacted raw-output
365 retention for authenticated recovery.
366- **`engines/registry.ts`**: Registers engines (`caveman`, `rtk`) and powers stacked pipelines.
367- **`stats.ts`**: Per-request compression stats tracking (original tokens, compressed tokens,
368 savings %, techniques used, engine breakdown, compression combo id).
369- **`types.ts`**: `CompressionMode` (off/lite/standard/aggressive/ultra/rtk/stacked),
370 `CompressionConfig`, `CompressionStats`, `CompressionResult`.
371- DB settings in `src/lib/db/compression.ts`, compression combos in
372 `src/lib/db/compressionCombos.ts`, API routes under `src/app/api/settings/compression/`,
373 `src/app/api/context/*`, and preview/language-pack routes under `src/app/api/compression/*`.
374 
375#### Combo Routing Engine (`combo.ts`)
376 
377- **`handleComboChat()`**: Entry point for combo-routed requests. Receives the combo config
378 and iterates through targets in order until one succeeds or all fail.
379- **`resolveComboTargets()`**: Expands a combo configuration into an ordered array of
380 `ResolvedComboTarget[]`, each specifying provider + model + account + credentials.
381- **Strategies** (17): priority, weighted, fill-first, round-robin, P2C, random, least-used, reset-aware (v3.8),
382 reset-window, cost-optimized, strict-random, auto, lkgp, context-optimized, context-relay, headroom, fusion. Source: `ROUTING_STRATEGY_VALUES` in `src/shared/constants/routingStrategies.ts`.
383- Each target calls **`handleSingleModel()`** which wraps `handleChatCore()` with
384 per-target error handling and circuit breaker checks.
385 
386### Domain Layer (`src/domain/`)
387 
388Policy engine modules: `policyEngine.ts`, `comboResolver.ts`, `costRules.ts`,
389`degradation.ts`, `fallbackPolicy.ts`, `lockoutPolicy.ts`, `modelAvailability.ts`,
390`providerExpiration.ts`, `quotaCache.ts`, `responses.ts`, `configAudit.ts`.
 
 
 
391 
392### MCP Server (`open-sse/mcp-server/`)
393 
394**104 tools** total (`TOTAL_MCP_TOOL_COUNT`, `open-sse/mcp-server/server.ts`): a 42-entry base registry (`MCP_TOOLS` in `schemas/tools.ts`, bundling the core / cache / compression / 1proxy / advanced tools) **plus** standalone module sets — memory (3), skill (4), agentSkill (3), pool (6), gamification (8), plugin (8), notion (6), obsidian (22). 3 transports (stdio / SSE / Streamable HTTP). Scoped auth (31 scopes — see `OMNIROUTE_MCP_SCOPES`), Zod schemas. See [`docs/frameworks/MCP-SERVER.md`](docs/frameworks/MCP-SERVER.md).
 
 
 
 
395 
396**Core tools** (20): get_health, list_combos, get_combo_metrics, switch_combo, check_quota,
397route_request, cost_report, list_models_catalog, web_search, simulate_route, set_budget_guard,
398set_routing_strategy, set_resilience_profile, test_combo, get_provider_metrics,
399best_combo_for_task, explain_route, get_session_snapshot, db_health_check, sync_pricing.
400 
401**Cache tools** (2): cache_stats, cache_flush.
 
 
 
402 
403**Compression tools** (5): compression_status, compression_configure, set_compression_engine,
404list_compression_combos, compression_combo_stats.
405 
406**1proxy tools** (3): oneproxy_fetch, oneproxy_rotate, oneproxy_stats.
 
 
 
 
 
407 
408**Memory tools** (3): memory_search, memory_add, memory_clear.
409 
410**Skill tools** (4): skills_list, skills_enable, skills_execute, skills_executions.
 
 
 
 
411 
412**Agent-skill tools** (3): A2A skill discovery / invocation bridges.
413 
414**Gamification tools** (8): levels, badges, leaderboard, and community-federation queries.
 
 
 
 
 
 
 
415 
416**Plugin tools** (8): plugin marketplace listing, install/enable/disable, and runtime inspection.
417 
418**Notion tools** (6) + **Obsidian tools** (22): knowledge-base read/write integrations (the largest tool family — vault search, note CRUD, WebDAV-backed file ops).
 
 
 
419 
420#### MCP Internals
421 
422- **Tool registration**: Each tool is an object with `{ name, description, inputSchema: ZodSchema,
423handler: async (args) => {...} }`. Zod validates inputs before the handler fires.
424- **`createMcpServer()`** and **`startMcpStdio()`** exported from `mcp-server/index.ts`.
425 `createMcpServer()` wires all tool sets; `startMcpStdio()` launches the stdio transport.
426- **Transports**: stdio (CLI `omniroute --mcp`), SSE (`/api/mcp/sse`), Streamable HTTP
427 (`/api/mcp/stream`). All share the same tool/scope engine.
428- **Scopes** (30): Control which tool categories an API key can access. Enforcement happens
429 before handler dispatch.
430- **Audit**: Every tool invocation is logged to SQLite (`mcp_audit` table) with tool name,
431 args, success/failure, API key attribution, and timestamp.
432 
433### A2A Server (`src/lib/a2a/`)
434 
435JSON-RPC 2.0, SSE streaming, Task Manager with TTL cleanup.
436Agent Card at `/.well-known/agent.json`.
437Skills (6): `smartRouting.ts`, `quotaManagement.ts`, `providerDiscovery.ts`, `costAnalysis.ts`, `healthReport.ts`, `listCapabilities.ts`.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
438 
439#### A2A Internals
440 
441- **`taskManager.ts`**: State machine lifecycle for tasks: `submitted → working →
442completed | failed | canceled`. Tasks have TTL and are cleaned up automatically.
443- **JSON-RPC methods**: `message/send` (sync), `message/stream` (SSE), `tasks/get`,
444 `tasks/cancel`. Dispatched via `POST /a2a`.
445- **Skills**: Registered in a DB-backed registry. Each skill receives task context
446 (messages, metadata) and returns structured results. `quotaManagement.ts` summarizes
447 quota; `smartRouting.ts` recommends routing decisions.
448- **Agent Card**: `/.well-known/agent.json` exposes capabilities, skills, and metadata
449 for client auto-discovery.
450 
451### ACP Module (`src/lib/acp/`)
 
 
 
 
 
 
 
 
 
452 
453Agent Communication Protocol registry and manager.
454 
455### Memory System (`src/lib/memory/`)
456 
457Extraction, injection, retrieval, summarization, and store modules for persistent
458conversational memory across sessions.
459 
460### Skills System (`src/lib/skills/`)
461 
462Extensible skill framework: registry, executor, sandbox, built-in skills,
463custom skill support, interception, and injection.
 
464 
465#### Skills Internals
466 
467- **`registry.ts`**: DB-backed skill registration and discovery. Skills have metadata
468 (name, description, version, enabled status) stored in SQLite.
469- **`executor.ts`**: Execution engine with configurable timeout and retry logic.
470 Receives skill name + input, looks up the skill, runs it in the sandbox.
471- **`sandbox.ts`**: Isolation layer for custom (user-provided) skills. Limits resource
472 access and execution time.
473- **Built-in skills**: Ship with OmniRoute (e.g., quota management, routing). Located
474 alongside the registry.
475- **Interception/Injection**: Skills can intercept requests in the pipeline (pre/post
476 processing) or inject context into prompts.
477 
478### Compliance (`src/lib/compliance/`)
479 
480Policy index for compliance enforcement.
481 
482### MITM Proxy (`src/mitm/`)
 
 
 
483 
484MITM proxy capability with certificate management, DNS handling, and target routing.
 
 
 
 
 
485 
486### Middleware (`src/middleware/`)
 
 
 
 
 
487 
488Request middleware including `promptInjectionGuard.ts`.
 
 
489 
490### Guardrails (`src/lib/guardrails/`)
491 
492Hot-reloadable guardrails framework (3 built-in: pii-masker, prompt-injection, vision-bridge). Fail-open. The `pii-masker` guardrail is registered and runs on every request, but its data-mutating logic is **opt-in** and OFF by default — it only redacts when `PII_REDACTION_ENABLED` (request) / `PII_RESPONSE_SANITIZATION` (response + streaming) are enabled (both `defaultValue: "false"`); with them off, payloads pass through untouched. A request can additionally opt OUT of any guardrail via header (`x-omniroute-disabled-guardrails`). Never make PII default-on (Hard Rule #20). See [`docs/security/GUARDRAILS.md`](docs/security/GUARDRAILS.md).
 
 
 
 
 
493 
494### Cloud Agents (`src/lib/cloudAgent/`)
495 
496`CloudAgentBase` abstract class + 3 agents (codex-cloud, devin, jules). Tasks persisted in `cloud_agent_tasks`; management auth required. See [`docs/frameworks/CLOUD_AGENT.md`](docs/frameworks/CLOUD_AGENT.md).
497 
498### Evals (`src/lib/evals/`)
499 
500Generic eval framework: `evalRunner.ts`, `runtime.ts`. Targets: combo / model / suite-default. See [`docs/frameworks/EVALS.md`](docs/frameworks/EVALS.md).
 
 
 
501 
502### Webhooks (`src/lib/webhookDispatcher.ts`)
503 
504HMAC-signed delivery, exponential backoff, auto-disable after 10 failures. 7 event types. See [`docs/frameworks/WEBHOOKS.md`](docs/frameworks/WEBHOOKS.md).
 
 
505 
506### Authorization Pipeline (`src/server/authz/`)
 
507 
508`classify → policies → enforce`. 3 route classes (PUBLIC / CLIENT_API / MANAGEMENT). See [`docs/architecture/AUTHZ_GUIDE.md`](docs/architecture/AUTHZ_GUIDE.md).
 
 
 
 
 
 
 
 
 
 
 
509 
510### Reasoning Replay (`src/lib/db/reasoningCache.ts` + `open-sse/services/reasoningCache.ts`)
 
 
 
 
 
 
 
 
 
 
511 
512Hybrid in-memory + SQLite cache for `reasoning_content`. Re-injects on multi-turn for strict providers (DeepSeek V4, Kimi K2, Qwen-Thinking, GLM, xiaomi-mimo). See [`docs/routing/REASONING_REPLAY.md`](docs/routing/REASONING_REPLAY.md).
 
 
 
 
513 
514### Tunnels (`src/lib/{cloudflaredTunnel,ngrokTunnel}.ts` + `src/app/api/tunnels/`)
 
 
515 
516Cloudflare Quick/Named, ngrok, Tailscale Funnel. See [`docs/ops/TUNNELS_GUIDE.md`](docs/ops/TUNNELS_GUIDE.md).
 
 
 
 
 
 
 
517 
518### Adding a New Provider
519 
5201. Register in `src/shared/constants/providers.ts`
5212. Add executor in `open-sse/executors/` (if custom logic needed)
5223. Add translator in `open-sse/translator/` (if non-OpenAI format)
5234. Add OAuth config in `src/lib/oauth/constants/oauth.ts` (if OAuth-based)
5245. Add models in `open-sse/config/providerRegistry.ts`
525 
 
 
 
 
 
 
 
 
 
526---
527 
528## Subdirectory AGENTS.md Files
529 
530- **[`src/lib/db/AGENTS.md`](src/lib/db/AGENTS.md)** — SQLite persistence, domain modules, migrations
531- **[`open-sse/services/AGENTS.md`](open-sse/services/AGENTS.md)** — Routing engine, combo resolution, strategy selection
 
 
 
 
 
532 
533## Reference Documentation (docs/)
534 
535For any non-trivial change, read the matching deep-dive first:
 
 
 
 
 
 
536 
537| Area | Doc |
538| ------------------------------------------ | --------------------------------------------------------------------------------------------------------------- |
539| Repo navigation | [`docs/architecture/REPOSITORY_MAP.md`](docs/architecture/REPOSITORY_MAP.md) |
540| Architecture | [`docs/architecture/ARCHITECTURE.md`](docs/architecture/ARCHITECTURE.md) |
541| Engineering reference | [`docs/architecture/CODEBASE_DOCUMENTATION.md`](docs/architecture/CODEBASE_DOCUMENTATION.md) |
542| Auto-Combo (12-factor, 18 strategies) | [`docs/routing/AUTO-COMBO.md`](docs/routing/AUTO-COMBO.md) |
543| Resilience (3 layers) | [`docs/architecture/RESILIENCE_GUIDE.md`](docs/architecture/RESILIENCE_GUIDE.md) |
544| Skills | [`docs/frameworks/SKILLS.md`](docs/frameworks/SKILLS.md) |
545| Memory | [`docs/frameworks/MEMORY.md`](docs/frameworks/MEMORY.md) |
546| Cloud agents | [`docs/frameworks/CLOUD_AGENT.md`](docs/frameworks/CLOUD_AGENT.md) |
547| Guardrails | [`docs/security/GUARDRAILS.md`](docs/security/GUARDRAILS.md) |
548| Evals | [`docs/frameworks/EVALS.md`](docs/frameworks/EVALS.md) |
549| Compliance | [`docs/security/COMPLIANCE.md`](docs/security/COMPLIANCE.md) |
550| Webhooks | [`docs/frameworks/WEBHOOKS.md`](docs/frameworks/WEBHOOKS.md) |
551| Authz | [`docs/architecture/AUTHZ_GUIDE.md`](docs/architecture/AUTHZ_GUIDE.md) |
552| Stealth | [`docs/security/STEALTH_GUIDE.md`](docs/security/STEALTH_GUIDE.md) |
553| Reasoning replay | [`docs/routing/REASONING_REPLAY.md`](docs/routing/REASONING_REPLAY.md) |
554| Agent protocols (A2A / ACP / Cloud) | [`docs/frameworks/AGENT_PROTOCOLS_GUIDE.md`](docs/frameworks/AGENT_PROTOCOLS_GUIDE.md) |
555| MCP server | [`docs/frameworks/MCP-SERVER.md`](docs/frameworks/MCP-SERVER.md) |
556| A2A server | [`docs/frameworks/A2A-SERVER.md`](docs/frameworks/A2A-SERVER.md) |
557| API reference | [`docs/reference/API_REFERENCE.md`](docs/reference/API_REFERENCE.md) + [`docs/openapi.yaml`](docs/openapi.yaml) |
558| Provider catalog (auto-generated) | [`docs/reference/PROVIDER_REFERENCE.md`](docs/reference/PROVIDER_REFERENCE.md) |
559| Tunnels | [`docs/ops/TUNNELS_GUIDE.md`](docs/ops/TUNNELS_GUIDE.md) |
560| Electron desktop | [`docs/guides/ELECTRON_GUIDE.md`](docs/guides/ELECTRON_GUIDE.md) |
561| Release flow | [`docs/ops/RELEASE_CHECKLIST.md`](docs/ops/RELEASE_CHECKLIST.md) |
562| Quality gates (35 gates, allowlist policy) | [`docs/architecture/QUALITY_GATES.md`](docs/architecture/QUALITY_GATES.md) |
563| Cluster opt-in profiles (memory, bifrost) | [`docs/architecture/cluster-decisions.md`](docs/architecture/cluster-decisions.md) |
564 
565---
566 
567## Fork / Upstream Workflow
568 
569This repository is a fork of `diegosouzapw/OmniRoute`. Keep fork-only operational
570changes (for example GHCR image publishing, personal deployment workflows, or local
571automation) out of upstream contribution PRs.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
572 
573When preparing a PR for upstream, always start the work branch from the upstream
574**default branch** — the active `release/vX.Y.Z` line (today `release/v3.8.49`).
575Never branch from `main`: `main` only receives release squash-merges, so a branch
576cut there is weeks behind and produces conflict-heavy PRs
577(see `CONTRIBUTING.md` and `docs/ops/BRANCHING_MODEL.md`):
578 
579```bash
580git fetch upstream
581# the default branch is the active release line, e.g. release/v3.8.49
582git switch -c <branch-name> upstream/release/vX.Y.Z
583```
584 
585Only cherry-pick or reapply the changes intended for the upstream PR.
586 
587---
588 
589## Review Focus
590 
591- **DB ops** go through `src/lib/db/` modules, never raw SQL in routes
592- **Provider requests** flow through `open-sse/handlers/`
593- **MCP/A2A pages** are tabs inside `/dashboard/endpoint`, not standalone routes
594- **No memory leaks** in SSE streams (abort signals, cleanup)
595- **Rate limit headers** must be parsed correctly
596- All API inputs validated with **Zod schemas**
597- **Provider constants** validated at module load via Zod (`src/shared/validation/providerSchema.ts`)
598- **Pricing data** syncs from LiteLLM via `src/lib/pricingSync.ts`
599- **Memory/Skills** are cross-cutting: affect MCP tools, request pipeline, and A2A skills
600- **⛔ NEVER close a contributor's PR** after using their code — always merge via GitHub so they get credit. See `.agents/workflows/review-prs.md` for full policy.
601 
diegosouzapw/OmniRoute · CLAUDE.md
@@ +1 @@
1# CLAUDE.md
2 
3This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4 
5## Quick Start
 
 
 
 
6 
7```bash
8npm install # Install deps (auto-generates .env from .env.example)
9npm run dev # Dev server at http://localhost:20128
10npm run build # Production build (Next.js 16 standalone)
11npm run lint # ESLint (0 errors expected; warnings are pre-existing)
12npm run typecheck:core # TypeScript check (should be clean)
13npm run typecheck:noimplicit:core # Strict check (no implicit any)
14npm run test:coverage # Unit tests + coverage gate (60/60/60/60 — statements/lines/functions/branches)
15npm run check # lint + test combined
16npm run check:cycles # Detect circular dependencies
17```
18 
19### Running Tests
20 
21```bash
22# Single test file (Node.js native test runner — most tests)
23node --import tsx/esm --test tests/unit/your-file.test.ts
24 
25# Vitest (MCP server, autoCombo, cache)
26npm run test:vitest
27 
28# All suites
29npm run test:all
30```
31 
32For full test matrix, see `CONTRIBUTING.md` → "Running Tests". For deep architecture, see `AGENTS.md`.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34---
35 
36## Project at a Glance
37 
38**OmniRoute** — unified AI proxy/router. One endpoint, 290 LLM providers, auto-fallback.
 
 
 
 
 
 
 
 
 
 
 
 
 
39 
40| Layer | Location | Purpose |
41| ------------- | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
42| API Routes | `src/app/api/v1/` | Next.js App Router — entry points |
43| Handlers | `open-sse/handlers/` | Request processing (chat, embeddings, etc) |
44| Executors | `open-sse/executors/` | Provider-specific HTTP dispatch |
45| Translators | `open-sse/translator/` | Format conversion (OpenAI↔Claude↔Gemini) |
46| Transformer | `open-sse/transformer/` | Responses API ↔ Chat Completions |
47| Services | `open-sse/services/` | Combo routing, rate limits, caching, etc |
48| Database | `src/lib/db/` | SQLite domain modules (130 migrations) |
49| Domain/Policy | `src/domain/` | Policy engine, cost rules, fallback logic |
50| MCP Server | `open-sse/mcp-server/` | 104 tools (42 base + memory/skill/agentSkill/pool/notion/obsidian/gamification/plugin modules), 3 transports (stdio / SSE / Streamable HTTP), 31 scopes |
51| A2A Server | `src/lib/a2a/` | JSON-RPC 2.0 agent protocol |
52| Skills | `src/lib/skills/` | Extensible skill framework |
53| Memory | `src/lib/memory/` | Persistent conversational memory |
54 
55Monorepo: `src/` (Next.js 16 app), `open-sse/` (streaming engine workspace), `electron/` (desktop app), `tests/`, `bin/` (CLI entry point).
 
 
 
 
56 
57---
 
 
58 
59## Request Pipeline
60 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61```
62Client → /v1/chat/completions (Next.js route)
63 → CORS → Zod validation → auth? → policy check → prompt injection guard
64 → handleChatCore() [open-sse/handlers/chatCore.ts]
65 → cache check → rate limit → combo routing?
66 → resolveComboTargets() → handleSingleModel() per target
67 → translateRequest() → getExecutor() → executor.execute()
68 → fetch() upstream → retry w/ backoff
69 → response translation → SSE stream or JSON
70 → If Responses API: responsesTransformer.ts TransformStream
71```
72 
73API routes follow a consistent pattern: `Route → CORS preflight → Zod body validation → Optional auth (extractApiKey/isValidApiKey) → API key policy enforcement → Handler delegation (open-sse)`. No global Next.js middleware — interception is route-specific.
74 
75**Combo routing** (`open-sse/services/combo.ts`): 19 public strategies (priority, weighted, fill-first, round-robin, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, cache-optimized, context-relay, fusion, pipeline). Each target calls `handleSingleModel()` which wraps `handleChatCore()` with per-target error handling and circuit breaker checks. The `fusion` strategy is the exception: it fans out to a panel of models in parallel, then a judge model synthesizes one final answer (`open-sse/services/fusion.ts`). See `docs/routing/AUTO-COMBO.md` for the 13-factor Auto-Combo scoring + the full strategy table and `docs/architecture/RESILIENCE_GUIDE.md` for the 3 resilience layers.
76 
77---
78 
79## Resilience Runtime State
80 
81OmniRoute has three related but distinct temporary-failure mechanisms. Keep their
82scope separate when debugging routing behavior. See the
83[3-layer resilience diagram](./docs/diagrams/exported/resilience-3layers.svg)
84(source: [docs/diagrams/resilience-3layers.mmd](./docs/diagrams/resilience-3layers.mmd))
85for an at-a-glance map.
86 
87### Provider Circuit Breaker
 
88 
89**Scope**: whole provider, e.g. `glm`, `openai`, `anthropic`.
90 
91**Purpose**: stop sending traffic to a provider that is repeatedly failing at the
92upstream/service level, so one unhealthy provider does not slow down every request.
 
93 
94**Implementation**:
95 
96- Core class: `src/shared/utils/circuitBreaker.ts`
97- Chat gate/execution wiring: `src/sse/handlers/chatHelpers.ts`, `src/sse/handlers/chat.ts`
98- Runtime status API: `src/app/api/monitoring/health/route.ts`
99- Shared wrappers: `open-sse/services/accountFallback.ts`
100- Persisted state table: `domain_circuit_breakers`
101 
102**States**:
103 
104- `CLOSED`: normal traffic is allowed.
105- `OPEN`: provider is temporarily blocked; callers get a provider-circuit-open response
106 or combo routing skips to another target.
107- `HALF_OPEN`: reset timeout has elapsed; allow a probe request. Success closes the
108 breaker, failure opens it again.
 
 
 
109 
110**Defaults** (`open-sse/config/constants.ts`):
111 
112- OAuth providers: threshold `3`, reset timeout `60s`.
113- API-key providers: threshold `5`, reset timeout `30s`.
114- Local providers: threshold `2`, reset timeout `15s`.
115 
116Only provider-level failure statuses should trip the provider breaker:
117 
118```ts
119(408, 500, 502, 503, 504);
120```
121 
122Do not trip the whole-provider breaker for normal account/key/model errors like most
123`401`, `403`, or `429` cases. Those usually belong to connection cooldown or model
124lockout. A generic API-key provider `403` should be recoverable unless it is classified
125as a terminal provider/account error.
126 
127The breaker uses lazy recovery, not a background timer. When `OPEN` expires, reads such
128as `getStatus()`, `canExecute()`, and `getRetryAfterMs()` refresh the state to
129`HALF_OPEN`, so dashboards and combo candidate builders do not keep excluding an
130expired provider forever.
 
 
 
 
 
131 
132### Connection Cooldown
133 
134**Scope**: one provider connection/account/key.
135 
136**Purpose**: temporarily skip one bad key/account while allowing other connections for
137the same provider to continue serving requests.
138 
139**Implementation**:
140 
141- Write/update path: `src/sse/services/auth.ts::markAccountUnavailable()`
142- Account selection/filtering: `src/sse/services/auth.ts::getProviderCredentials...`
143- Cooldown calculation: `open-sse/services/accountFallback.ts::checkFallbackError()`
144- Settings: `src/lib/resilience/settings.ts`
 
 
 
145 
146Important fields on provider connections:
 
 
147 
148```ts
149rateLimitedUntil;
150testStatus: "unavailable";
151lastError;
152lastErrorType;
153errorCode;
154backoffLevel;
155```
156 
157During account selection, a connection is skipped while:
 
 
 
 
 
 
 
 
 
158 
159```ts
160new Date(rateLimitedUntil).getTime() > Date.now();
 
 
161```
 
 
 
162 
163Cooldowns are also lazy: when `rateLimitedUntil` is in the past, the connection becomes
164eligible again. On successful use, `clearAccountError()` clears `testStatus`,
165`rateLimitedUntil`, error fields, and `backoffLevel`.
 
 
 
 
 
 
 
 
 
 
166 
167Default connection cooldown behavior:
 
168 
169- OAuth base cooldown: `5s`.
170- API-key base cooldown: `3s`.
171- API-key `429` should prefer upstream retry hints (`Retry-After`, reset headers, or
172 parseable reset text) when available.
173- Repeated recoverable failures use exponential backoff:
174 
175```ts
176baseCooldownMs * 2 ** failureIndex;
177```
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
178 
179The anti-thundering-herd guard prevents concurrent failures on the same connection from
180repeatedly extending the cooldown or double-incrementing `backoffLevel`.
 
181 
182Terminal states are not cooldowns. `banned`, `expired`, and `credits_exhausted` are
183intended to stay unavailable until credentials/settings change or an operator resets
184them. Do not overwrite terminal states with transient cooldown state.
 
185 
186### Model Lockout
187 
188**Scope**: provider + connection + model.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
189 
190**Purpose**: avoid disabling a whole connection when only one model is unavailable or
191quota-limited for that connection.
192 
193Examples:
194 
195- Per-model quota providers returning `429`.
196- Local providers returning `404` for one missing model.
197- Provider-specific mode/model permission failures such as selected Grok modes.
198 
199Model lockout lives in `open-sse/services/accountFallback.ts` and lets the same
200connection continue serving other models.
201 
202### Debugging Guidance
 
 
 
 
 
 
 
 
203 
204- If all keys for a provider are skipped, inspect both provider breaker state and each
205 connection's `rateLimitedUntil`/`testStatus`.
206- If a provider appears permanently excluded after the reset window, check whether code
207 is reading raw `state` instead of using `getStatus()`/`canExecute()`.
208- If one provider key fails but others should work, prefer connection cooldown over
209 provider breaker.
210- If only one model fails, prefer model lockout over connection cooldown.
211- If a state should self-recover, it should have a future timestamp/reset timeout and a
212 read path that refreshes expired state. Permanent statuses require manual credential
213 or config changes.
214 
215---
 
216 
217## Key Conventions
218 
219### Code Style
 
 
 
 
 
 
220 
221- **2 spaces**, semicolons, double quotes, 100 char width, es5 trailing commas (enforced by lint-staged via Prettier)
222- **Imports**: external → internal (`@/`, `@omniroute/open-sse`) → relative
223- **Naming**: files=camelCase/kebab, components=PascalCase, constants=UPPER_SNAKE
224- **ESLint**: `no-eval`, `no-implied-eval`, `no-new-func` = error everywhere; `no-explicit-any` = **error** in `open-sse/` and `tests/` (since #6218 — pre-existing violations are frozen in `config/quality/eslint-suppressions.json`, new ones must be fixed; `npm run lint` applies the suppressions and is what CI runs)
225- **TypeScript**: `strict: false`, target ES2022, module esnext, resolution bundler. Prefer explicit types.
226 
227### Database
228 
229- **Always** go through `src/lib/db/` domain modules — **never** write raw SQL in routes or handlers
230- **Never** add logic to `src/lib/localDb.ts` (re-export layer only)
231- **Never** barrel-import from `localDb.ts` — import specific `db/` modules instead
232- DB singleton: `getDbInstance()` from `src/lib/db/core.ts` (WAL journaling)
233- Migrations: `src/lib/db/migrations/` — versioned SQL files, idempotent, run in transactions
234 
235### Error Handling
 
 
 
 
 
236 
237- try/catch with specific error types, log with pino context
238- Never swallow errors in SSE streams — use abort signals for cleanup
239- Return proper HTTP status codes (4xx/5xx)
240 
241### Security
 
 
 
 
 
 
 
242 
243- **Never** use `eval()`, `new Function()`, or implied eval
244- Validate all inputs with Zod schemas
245- Encrypt credentials at rest (AES-256-GCM)
246- Upstream header denylist: `src/shared/constants/upstreamHeaders.ts` — keep sanitize, Zod schemas, and unit tests aligned when editing
247- **Public upstream credentials** (Gemini/Antigravity/Windsurf-style OAuth client_id/secret + Firebase Web keys extracted from public CLIs): **MUST** be embedded via `resolvePublicCred()` from `open-sse/utils/publicCreds.ts` — **never** as string literals. See `docs/security/PUBLIC_CREDS.md` for the mandatory pattern.
248- **Error responses** (HTTP / SSE / executor / MCP handler): **MUST** route through `buildErrorBody()` or `sanitizeErrorMessage()` from `open-sse/utils/error.ts` — **never** put raw `err.stack` or `err.message` in a response body. See `docs/security/ERROR_SANITIZATION.md`.
249- **Shell commands built from variables**: when calling `exec()`/`spawn()` with a script that needs runtime values, pass them via the `env` option (shell-escaped automatically) — **never** string-interpolate untrusted/external paths into the script body. Reference: `src/mitm/cert/install.ts::updateNssDatabases`.
250- **Secure-by-default libraries** ([tldrsec/awesome-secure-defaults](https://github.com/tldrsec/awesome-secure-defaults)): prefer Helmet.js, DOMPurify, ssrf-req-filter, safe-regex, Google Tink over custom implementations whenever adding new security-sensitive surfaces.
251 
252---
253 
254## Common Modification Scenarios
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
255 
256### Adding a New Provider
257 
2581. Register in `src/shared/constants/providers.ts` (Zod-validated at load)
2592. Add executor in `open-sse/executors/` if custom logic needed (extend `BaseExecutor`)
2603. Add translator in `open-sse/translator/` if non-OpenAI format
2614. Add OAuth config in `src/lib/oauth/constants/oauth.ts` if OAuth-based — if the upstream CLI ships a public client_id/secret, embed via `resolvePublicCred()` (see `docs/security/PUBLIC_CREDS.md`), **never** as a literal
2625. Register models in `open-sse/config/providerRegistry.ts`
2636. Write tests in `tests/unit/` (include the publicCreds shape assertion if you added a new embedded default)
 
 
264 
265### Adding a New API Route
266 
2671. Create directory under `src/app/api/v1/your-route/`
2682. Create `route.ts` with `GET`/`POST` handlers
2693. Follow pattern: CORS → Zod body validation → optional auth → handler delegation
2704. Handler goes in `open-sse/handlers/` (import from there, not inline)
2715. Error responses use `buildErrorBody()` / `errorResponse()` from `open-sse/utils/error.ts` (auto-sanitized — never put `err.stack` or `err.message` raw in the body). See `docs/security/ERROR_SANITIZATION.md`.
2726. Add tests — including at least one assertion that error responses do not leak stack traces (`!body.error.message.includes("at /")`)
273 
274### Adding a New DB Module
275 
2761. Create `src/lib/db/yourModule.ts` — import `getDbInstance` from `./core.ts`
2772. Export CRUD functions for your domain table(s)
2783. Add migration in `src/lib/db/migrations/` if new tables needed
2794. Re-export from `src/lib/localDb.ts` (add to the re-export list only)
2805. Write tests
281 
282### Adding a New MCP Tool
 
 
 
283 
2841. Add tool definition in `open-sse/mcp-server/tools/` with Zod input schema + async handler
2852. Register in tool set (wired by `createMcpServer()`)
2863. Assign to appropriate scope(s)
2874. Write tests (tool invocation logged to `mcp_audit` table)
288 
289### Adding a New A2A Skill
 
290 
2911. Create skill in `src/lib/a2a/skills/` (5 already exist: smart-routing, quota-management, provider-discovery, cost-analysis, health-report)
2922. Skill receives task context (messages, metadata) → returns structured result
2933. Register in `A2A_SKILL_HANDLERS` in `src/lib/a2a/taskExecution.ts`
2944. Expose in `src/app/.well-known/agent.json/route.ts` (Agent Card)
2955. Write tests in `tests/unit/`
2966. Document in `docs/frameworks/A2A-SERVER.md` skill table
297 
298### Adding a New Cloud Agent
299 
3001. Create agent class in `src/lib/cloudAgent/agents/` extending `CloudAgentBase` (3 already exist: codex-cloud, devin, jules)
3012. Implement `createTask`, `getStatus`, `approvePlan`, `sendMessage`, `listSources`
3023. Register in `src/lib/cloudAgent/registry.ts`
3034. Add OAuth/credentials handling if needed (`src/lib/oauth/providers/`)
3045. Tests + document in `docs/frameworks/CLOUD_AGENT.md`
305 
306### Adding a New Embedded Service
307 
3081. Create installer in `src/lib/services/installers/{name}.ts` modeled on `ninerouter.ts` (use `runNpm` from `installers/utils.ts` — no shell interpolation, hard rule #13).
3092. Register the service in `src/lib/services/bootstrap.ts` (add to `SERVICES[]` array and extend `buildSpawnArgsFactory()`).
3103. Add a DB seed row for the new service in `src/lib/db/migrations/` (`version_manager` table, `status='not_installed'`, `auto_start=0`).
3114. Create 7 API endpoints under `src/app/api/services/{name}/` (`_lib.ts`, `install`, `start`, `stop`, `restart`, `update`, `status`, `auto-start`). All delegate errors through `createErrorResponse()`. The shared `logs` endpoint is already wired via `[name]/logs/route.ts`.
3125. Verify `/api/services/` is in `LOCAL_ONLY_API_PREFIXES` in `src/server/authz/routeGuard.ts`; add a test asserting `isLocalOnlyPath()` returns `true` for the new prefix if you add one (hard rule #17).
3136. Add a UI tab in `src/app/(dashboard)/dashboard/providers/services/tabs/` reusing `ServiceStatusCard`, `ServiceLifecycleButtons`, `ServiceLogsPanel`.
3147. Document in `docs/frameworks/EMBEDDED-SERVICES.md` (update §1 service table + §4 API reference) and `docs/openapi.yaml`.
3158. Write tests: unit (`tests/unit/services/`), integration (`tests/integration/services/`, gated by `RUN_SERVICES_INT=1`), and update `docs/ops/RELEASE_CHECKLIST.md` smoke section.
316 
317### Adding a New Guardrail / Eval / Skill / Webhook event
318 
319- Guardrail: `src/lib/guardrails/` → docs: `docs/security/GUARDRAILS.md`
320- Eval suite: `src/lib/evals/` → docs: `docs/frameworks/EVALS.md`
321- Skill (sandbox): `src/lib/skills/` → docs: `docs/frameworks/SKILLS.md`
322- Webhook event: `src/lib/webhookDispatcher.ts` → docs: `docs/frameworks/WEBHOOKS.md`
323 
324---
325 
326## Reference Documentation
 
 
 
 
 
 
 
 
 
327 
328For any non-trivial change, read the matching deep-dive first:
329 
330| Area | Doc |
331| --------------------------------------------- | ------------------------------------------------------- |
332| Repo navigation | `docs/architecture/REPOSITORY_MAP.md` |
333| Architecture | `docs/architecture/ARCHITECTURE.md` |
334| Engineering reference | `docs/architecture/CODEBASE_DOCUMENTATION.md` |
335| Auto-Combo (13-factor scoring, 19 strategies) | `docs/routing/AUTO-COMBO.md` |
336| Resilience (3 mechanisms) | `docs/architecture/RESILIENCE_GUIDE.md` |
337| Reasoning replay | `docs/routing/REASONING_REPLAY.md` |
338| Skills framework | `docs/frameworks/SKILLS.md` |
339| Memory system (FTS5 + Qdrant) | `docs/frameworks/MEMORY.md` |
340| Cloud agents | `docs/frameworks/CLOUD_AGENT.md` |
341| Guardrails (PII / injection / vision) | `docs/security/GUARDRAILS.md` |
342| Public upstream credentials (Gemini/etc.) | `docs/security/PUBLIC_CREDS.md` |
343| Error message sanitization | `docs/security/ERROR_SANITIZATION.md` |
344| Evals | `docs/frameworks/EVALS.md` |
345| Compliance / audit | `docs/security/COMPLIANCE.md` |
346| Webhooks | `docs/frameworks/WEBHOOKS.md` |
347| Authorization pipeline | `docs/architecture/AUTHZ_GUIDE.md` |
348| Stealth (TLS / fingerprint) | `docs/security/STEALTH_GUIDE.md` |
349| Agent protocols (A2A / ACP / Cloud) | `docs/frameworks/AGENT_PROTOCOLS_GUIDE.md` |
350| MCP server | `docs/frameworks/MCP-SERVER.md` |
351| A2A server | `docs/frameworks/A2A-SERVER.md` |
352| API reference + OpenAPI | `docs/reference/API_REFERENCE.md` + `docs/openapi.yaml` |
353| Provider catalog (auto-generated) | `docs/reference/PROVIDER_REFERENCE.md` |
354| Release flow | `docs/ops/RELEASE_CHECKLIST.md` |
355| Embedded services | `docs/frameworks/EMBEDDED-SERVICES.md` |
356| Quality gates (~48 scripts, allowlist policy) | `docs/architecture/QUALITY_GATES.md` |
357 
358---
359 
360## Testing
 
 
 
 
 
 
 
 
361 
362| What | Command |
363| ----------------------- | --------------------------------------------------------------------------- |
364| Unit tests | `npm run test:unit` |
365| Single file | `node --import tsx/esm --test tests/unit/file.test.ts` |
366| Vitest (MCP, autoCombo) | `npm run test:vitest` |
367| E2E (Playwright) | `npm run test:e2e` |
368| Protocol E2E (MCP+A2A) | `npm run test:protocols:e2e` |
369| Ecosystem | `npm run test:ecosystem` |
370| Coverage gate | `npm run test:coverage` (60/60/60/60 — statements/lines/functions/branches) |
371| Coverage report | `npm run coverage:report` |
372 
373**PR rule**: If you change production code in `src/`, `open-sse/`, `electron/`, or `bin/`, you must include or update tests in the same PR.
374 
375**Test layer preference**: unit first → integration (multi-module or DB state) → e2e (UI/workflow only). Encode bug reproductions as automated tests before or alongside the fix.
376 
377**Both test runners must pass**: `npm run test:unit` (Node native — most tests) AND `npm run test:vitest` (MCP server, autoCombo, cache) cover **non-overlapping files**. Both are wired in CI (jobs `test-unit` and `test-vitest`) and must be green before merging. A PR where only one suite passes may silently ship broken MCP tools or routing regressions.
 
378 
379**Bug fix / issue triage protocol (Hard Rule #18)**: Every fix for a reported issue must be validated by one of the following — no exceptions:
380 
3811. **TDD (preferred)** — write a failing test reproducing the bug → fix it → confirm the test passes. The test becomes the permanent regression guard. Touch only the files the test proves need changing; nothing more.
3822. **Real-environment test (when TDD is not possible)** — deploy to the production VPS (`root@192.168.0.15`) and run a documented live test. Record the exact command + result in the PR description. Applies to: OAuth upstream flows, Cloudflare/WS upstream behavior, UI-only regressions, hardware-dependent behavior.
3833. "It worked locally without a test" does not count. A fix without a test or a VPS validation record is not a fix — it is a guess.
384 
385Why this matters: fixing bug A while opening bug B is worse than not fixing at all. The TDD/VPS gate enforces surgical scope — you touch only what the failing test proves is broken. Examples where this paid off: #3090 (claude-web 403), #3113 (WS HTTP fallback), #3052 (heap-guard auto-calibration).
386 
387**Copilot coverage policy**: When a PR changes production code and coverage is below 60% (statements/lines/functions/branches), do not just report — add or update tests, rerun the coverage gate, then ask for confirmation. Include commands run, changed test files, and final coverage result in the PR report.
 
 
 
 
 
 
 
 
 
388 
389---
390 
391## Planning & Research Artifacts (superpowers, deep-research)
392 
393`_tasks/` is a **separate, isolated git repository** that is gitignored by the main
394repo (`.gitignore` → `_tasks/`). It is the canonical home for working artifacts —
395plans, specs/designs, research, hand-offs — so they stay **versioned in their own
396repo** instead of polluting the main OmniRoute tree.
397 
398**Hard rule — never write superpowers / planning / research output under `docs/` or
399the repo root.** The superpowers skills ship with defaults that point at `docs/…`
400(`writing-plans` → `docs/superpowers/plans/`, `brainstorming` → `docs/superpowers/specs/`).
401Those defaults are **overridden here**. Whenever you invoke superpowers (or any
402plan/spec/research generator) in this project, save to `_tasks/` instead, using the
403same filename convention:
404 
405| Artifact (skill) | Default (do NOT use) | Save here instead |
406| ---------------------------------- | ------------------------- | ------------------------------------------------------------- |
407| Plans (`writing-plans`) | `docs/superpowers/plans/` | `_tasks/superpowers/plans/YYYY-MM-DD-<feature>.md` |
408| Specs / design (`brainstorming`) | `docs/superpowers/specs/` | `_tasks/superpowers/specs/YYYY-MM-DD-<topic>-design.md` |
409| Research (`deep-research`, ad-hoc) | `docs/research/` | `_tasks/research/…` |
410| Hand-offs (`/handoff`) | — | `_tasks/hands-off/<YYYY-MM-DD>_<branch>_v<versão>_sess-<id>/` |
411 
412When a superpowers skill announces a path like "saved to `docs/superpowers/plans/…`",
413rewrite it to the `_tasks/…` equivalent before writing. Commit those artifacts inside
414the `_tasks/` repo (`git -C _tasks …`), never in the main repo.
415 
416## Git Workflow
417 
418```bash
419# Never commit directly to main
420git checkout -b feat/your-feature
421git commit -m "feat: describe your change"
422git push -u origin feat/your-feature
423```
424 
425**Branch prefixes**: `feat/`, `fix/`, `refactor/`, `docs/`, `test/`, `chore/`
426 
427**Commit format** (Conventional Commits): `feat(db): add circuit breaker` — scopes: `db`, `sse`, `oauth`, `dashboard`, `api`, `cli`, `docker`, `ci`, `mcp`, `a2a`, `memory`, `skills`
428 
429**Husky hooks**:
430 
431- **pre-commit**: lint-staged + `check-docs-sync` + `check:any-budget:t11` + `check:tracked-artifacts`
432- **pre-push**: intentionally light (PATH/npm sanity only). `any-budget` + `tracked-artifacts`
433 already run on pre-commit; re-running them on every push was pure double-pay. CI still
434 enforces both. (Was Fase 6A.12 full pre-push gate; folded into pre-commit in #6716.)
435 
436### Worktree isolation (MANDATORY for every development task)
437 
438Multiple sessions/agents work this repo in parallel. The main checkout is **shared**, so a
439`git checkout`/branch switch in it silently discards another session's uncommitted work and
440yanks the branch out from under whatever else is running (incidents: 2026-06-05, 2026-06-13).
441 
442**Rule: never develop on the shared main checkout. Every task gets its own git worktree on its
443own dedicated branch, and you MUST confirm the base branch with the operator before creating it.**
444 
4451. **Ask first — which base branch?** Before creating anything, ask the operator (via
446 `AskUserQuestion`, unless they already told you) from which branch the new worktree/branch
447 should be cut. Do NOT assume `main` or "whatever I'm on" — the answer is usually the active
448 `release/vX.Y.Z`, but it can be another feature/release branch. Get the base explicitly.
4492. **Create an isolated worktree + branch off that base** (never reuse the main checkout).
450 **🔴 MANDATORY PATH: every worktree lives under `.claude/worktrees/` — and nowhere else.**
451 This is the single canonical location (the same dir the native `EnterWorktree` tool uses). It
452 is gitignored AND in the `tsconfig.json` / `.dockerignore` excludes, so worktrees never leak
453 into the build scope. **Never** use `.worktrees/`, repo-root, or any other path — a worktree
454 outside `.claude/worktrees/` (a) escapes the build-scope excludes and poisons `next build` (the
455 `tsconfig` `include: **/*` globs ~70× the codebase → OOM; incident 2026-06-25) and (b) scatters
456 worktrees across two dirs.
457 
458 ```bash
459 BASE_BRANCH="release/vX.Y.Z" # ← the branch the operator confirmed in step 1
460 TASK="feat/your-feature" # feat/ fix/ refactor/ docs/ test/ chore/
461 git fetch origin "$BASE_BRANCH"
462 git worktree add ".claude/worktrees/${TASK##*/}" -b "$TASK" "origin/$BASE_BRANCH"
463 cd ".claude/worktrees/${TASK##*/}"
464 # Reuse the main checkout's node_modules to skip a per-worktree npm install.
465 # HARD LINKS (`cp -al`), never a symlink: ~5s for the whole tree and near-zero extra
466 # disk (the inodes are shared), and unlike a symlink it does not break the dev server.
467 cp -al "$(git -C <main_checkout> rev-parse --show-toplevel)/node_modules" node_modules
468 ```
469 
470 **Never `ln -s` node_modules.** Turbopack rejects a symlink that resolves outside the
471 project root, so `npm run dev` dies with a FATAL panic (`Symlink [project]/node_modules
472 is invalid, it points out of the filesystem root`) while typecheck, lint and the test
473 runners all keep passing — the error names "filesystem root", not the worktree, so it
474 reads like a Next/build bug and costs real time to trace (incident 2026-07-31, #9043).
475 
476 In Claude Code prefer the native `EnterWorktree` tool (it already creates worktrees under
477 `.claude/worktrees/`): create the worktree with the command above, then call `EnterWorktree`
478 with its `path`.
479 
4803. **Work, commit, push, open the PR — all from inside the worktree.** Never `git checkout` a
481 different branch inside a worktree another session might share.
4824. **Tear down only your own** worktree + branch when done, from the main checkout:
483 `git worktree remove .claude/worktrees/<dir>` then `git branch -D <task>`. Never blanket-delete
484 `fix/*`/`feat/*` — other sessions keep their own; delete only the branches you created, by name.
4855. **Never touch another session's worktree, branch, or uncommitted changes.** If `git worktree
486list` shows worktrees you didn't create, leave them alone. End every session with the main
487 checkout back on the branch it started on (the active `release/vX.Y.Z`, never `main`).
488 
489---
490 
491## Environment
 
 
 
 
492 
493- **Runtime**: Node.js ≥22.0.0 <23 || ≥24.0.0 <27, ES Modules. This is the **only supported** runtime for the published `omniroute` CLI, the server, and the test suites (`node:test` + vitest) — `engines.node` is authoritative and end users never need Bun. A **best-effort `bun:sqlite` compatibility path** exists so a global Bun install (`bun install -g omniroute`) can start without `better-sqlite3` (driver adapter + Bun-aware process spawning); it is **not** a supported runtime — no support guarantees — and every Bun-specific runtime change MUST preserve the Node driver/fallback chain and ship a Bun test (`test:bun:db`) or an explicit reason why the path is Node-only.
494- **Bun (build/dev script runner + compatibility smoke only)**: Bun `1.3.14` is pinned as an **exact devDependency** (provisioned through the existing `npm ci` via the lockfile's `@oven/bun-*` platform binaries — no `setup-bun`/ad-hoc install). It is used **only** to execute a small, allow-listed set of TypeScript **gate/generator scripts** (replacing `node --import tsx` for startup speed): the CI checks `check:provider-consistency`, `check:compression-budget`, `check:known-symbols`, and the non-CI `gen:provider-reference`, `bench:compression` — plus the focused `test:bun:db` compatibility smoke suite for the best-effort `bun:sqlite` path. **Do NOT** widen Bun to `npm install`, the build (`build:cli*`), `check:pack-artifact`, the supported published runtime, or the main test runners — those stay on Node. Any new Bun-invoking gate/generator script must be validated byte-identical against its `node --import tsx` output first. After pulling the lockfile change, run `npm install` so `bun` resolves locally (a stale `node_modules` will fail those scripts with `bun: not found`).
495- **TypeScript**: 6.0+, target ES2022, module esnext, resolution bundler
496- **Path aliases**: `@/*` → `src/`, `@omniroute/open-sse` → `open-sse/`, `@omniroute/open-sse/*` → `open-sse/*`
497- **Default port**: 20128 (API + dashboard on same port)
498- **Data directory**: `DATA_DIR` env var, defaults to `~/.omniroute/`
499- **Key env vars**: `PORT`, `JWT_SECRET`, `API_KEY_SECRET`, `INITIAL_PASSWORD`, `REQUIRE_API_KEY`, `APP_LOG_LEVEL`
500- Setup: `cp .env.example .env` then generate `JWT_SECRET` (`openssl rand -base64 48`) and `API_KEY_SECRET` (`openssl rand -hex 32`)
501 
502---
503 
504## Quality Gates & Ratchets
505 
506OmniRoute has **~48 quality-gate scripts** (`scripts/check/` + `scripts/quality/`) wired
507across **9 gate-running jobs** in `.github/workflows/ci.yml` (`lint`, `quality-gate`,
508`quality-extended`, `docs-sync-strict`, `i18n-ui-coverage`, `i18n`, `pr-test-policy`,
509`test-vitest`, `sonarqube`), plus the `quality.yml` fast-gates job (PR→`release/**`) and
5103 nightly workflows (`nightly-property`, `nightly-resilience`, `nightly-llm-security`;
511`nightly-mutation` once merged). Full inventory, per-job breakdown, and operational
512procedures are in [`docs/architecture/QUALITY_GATES.md`](docs/architecture/QUALITY_GATES.md).
513 
514**Quick reference:**
515 
516- Gates in jobs `lint` + `docs-sync-strict`: pass/fail policy gates —
517 fix the violation or add an allowlist entry with a justification comment + tracking issue.
518- Gates in job `quality-gate`: ratchet — metrics (ESLint warnings, code coverage, duplication,
519 complexity) must not regress vs `quality-baseline.json`. Update via
520 `npm run quality:ratchet -- --update` when a metric genuinely improves.
521- Job `test-vitest` runs `npm run test:vitest` (MCP tools, autoCombo, cache) — blocking.
522 `test:vitest:ui` is advisory until UI component tests are triaged.
523 
524**Allowlist policy (short form):** Fix the cause; use the allowlist only for pre-existing
525violations you cannot fix in the same PR. Add a comment with justification + issue number.
526Stale allowlist entries (suppressing a violation that no longer exists) will be caught by
527the stale-enforcement added in Fase 6A.3.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
528 
529---
530 
531## Hard Rules
532 
5331. Never commit secrets or credentials
5342. Never add logic to `localDb.ts`
5353. Never use `eval()` / `new Function()` / implied eval
5364. Never commit directly to `main`
5375. Never write raw SQL in routes — use `src/lib/db/` modules
5386. Never silently swallow errors in SSE streams
5397. Always validate inputs with Zod schemas
5408. Always include tests when changing production code
5419. Coverage must not regress below the baseline frozen in `quality-baseline.json` (ratchet); absolute floor is 60% (statements/lines/functions/branches). Update the baseline via `npm run quality:ratchet -- --update` only when coverage genuinely improves. See `docs/architecture/QUALITY_GATES.md`.
54210. Never bypass Husky hooks (`--no-verify`, `--no-gpg-sign`) without explicit operator approval.
54311. Never embed public upstream OAuth client_id/secret or Firebase Web keys as string literals — always go through `resolvePublicCred()` (`open-sse/utils/publicCreds.ts`). See `docs/security/PUBLIC_CREDS.md`.
54412. Never return raw `err.stack` / `err.message` in HTTP / SSE / executor responses — always route through `buildErrorBody()` or `sanitizeErrorMessage()` (`open-sse/utils/error.ts`). See `docs/security/ERROR_SANITIZATION.md`.
54513. Never string-interpolate external paths or runtime values into shell scripts passed to `exec()`/`spawn()` — pass via the `env` option instead. Reference: `src/mitm/cert/install.ts::updateNssDatabases`.
54614. Never dismiss a CodeQL / Secret-Scanning alert without (a) first checking the pattern docs above to see if the helper applies, and (b) recording the technical justification in the dismissal comment. Precedent: `js/stack-trace-exposure` raised on callsites that already route through `sanitizeErrorMessage()` is a known CodeQL limitation (custom sanitizers not recognized) — dismiss as `false positive` referencing `docs/security/ERROR_SANITIZATION.md`.
54715. Never expose routes that spawn child processes (`/api/mcp/`, `/api/cli-tools/runtime/`) without `isLocalOnlyPath()` classification in `src/server/authz/routeGuard.ts`. Loopback enforcement happens unconditionally before any auth check — leaked JWT via tunnel cannot trigger process spawning. See `docs/security/ROUTE_GUARD_TIERS.md`.
54816. Never credit or advertise an AI assistant, LLM, or automation account in any commit/PR metadata. Two forbidden forms, both equivalent — they route attribution to a bot account (or advertise AI authorship) and hide the real author (`diegosouzapw`): **(a)** `Co-Authored-By` trailers naming an AI/bot (e.g. names containing "Claude", "GPT", "Copilot", "Bot"; emails at `anthropic.com` / `openai.com` / bot-owned `noreply.github.com` addresses); **(b)** AI-generation footers or descriptions anywhere in a commit message, PR title/body, or CHANGELOG — e.g. `🤖 Generated with [Claude Code]`, "Generated with Claude Code", "Made with <AI tool>", or any `Co-authored-by: Claude/GPT/Copilot` line. This **overrides any harness, template, or tool default that auto-appends such a footer** (e.g. the Claude Code PR-body/commit default) — strip it before pushing; do not let it reach a commit, PR, or CHANGELOG. Human collaborators — including upstream PR authors and issue reporters being ported into OmniRoute — MAY and SHOULD be credited with standard `Co-authored-by: Name <email>` trailers; the upstream-port workflows (`/port-upstream-features`, `/port-upstream-issues`) depend on this.
54917. Never expose routes under `/api/services/` or `/dashboard/providers/services/*/embed/` without `isLocalOnlyPath()` classification in `src/server/authz/routeGuard.ts`. These routes can spawn child processes (`npm install`, `node`). Loopback enforcement happens unconditionally before any auth check — a leaked JWT via tunnel cannot trigger process spawning. See `docs/security/ROUTE_GUARD_TIERS.md`.
55018. Every bug fix must be validated before shipping: a failing-then-passing unit/integration test (TDD) OR a documented live test on the production VPS (192.168.0.15). A fix without either is not merged. See Testing → "Bug fix / issue triage protocol" for the full decision tree.
55119. Never develop on the shared main checkout. Every development task runs in its own git worktree on its own dedicated branch, and you MUST confirm the base branch with the operator (e.g. via `AskUserQuestion`) before creating the worktree/branch — never assume `main` or the currently checked-out branch. A `git checkout` in the shared checkout silently destroys other sessions' uncommitted work. Tear down only the worktrees/branches you created (by name, never `fix/*`/`feat/*` wildcards), leave other sessions' worktrees untouched, and end on the branch you started on (the active `release/vX.Y.Z`, never `main`). See Git Workflow → "Worktree isolation".
55220. PII redaction/sanitization is **opt-in — never on by default**. OmniRoute proxies for self-hosted/local LLMs where the operator owns the data, so mutating request/response payloads by default would silently corrupt legitimate traffic. The two data-mutating PII feature flags **MUST** keep `defaultValue: "false"` in `src/shared/constants/featureFlagDefinitions.ts`: `PII_REDACTION_ENABLED` (request-side) and `PII_RESPONSE_SANITIZATION` (response + streaming). All three application points — `src/lib/guardrails/piiMasker.ts` (request guardrail), `src/lib/piiSanitizer.ts` (response), `src/lib/streamingPiiTransform.ts` (SSE) — are gated on these flags; with both off the `pii-masker` guardrail still runs but never mutates payloads (data passes through untouched). Flipping either default to `"true"` requires explicit operator approval. The regression guard is `tests/unit/pii-opt-in-default.test.ts` (asserts both definition defaults + behavioral pass-through). Opt-in is per-operator via env or the settings/DB override (`src/lib/db/featureFlags.ts`), never a silent default. See `docs/security/GUARDRAILS.md`.
55321. **Release-freeze — the FROZEN release branch belongs to the release captain; development does NOT stop (parallel-cycle model, 2026-07-04).** `/generate-release` opens a marker issue labeled `release-freeze` at the start of reconciliation (Phase 0a), **immediately cuts the next cycle's branch `release/vX+1` from the frozen tip (Phase 0a.0b — bump + living release PR + re-home of open PRs)**, and closes the freeze once the release PR squash-merges to `main`. Before merging **any** PR, every campaign workflow (`/review-prs`, `/review-group-prs`, `/merge-prs`, `/triage-fix-bugs`, `/implement-fix-bugs`, `/triage-features`, `/implement-features`, `/green-prs`, `/port-upstream-*`) **MUST** check `gh issue list --repo diegosouzapw/OmniRoute --label release-freeze --state open` — if a freeze is active: **NEVER merge into the frozen `release/vX.Y.Z` named in the freeze title**; instead resolve the ACTIVE development branch (the **highest** `release/v*` by semver — normally `release/vX+1`, announced in a freeze-issue comment) and **retarget the PR there** (`gh pr edit <N> --base release/vX+1`, then VERIFY with `gh pr view <N> --json baseRefName` — the edit fails silently) and merge normally. **HOLD only when the highest release/v\* branch IS the frozen one** (the short window before 0a.0b completes, or a pre-parallel-cycle release) — in that case leave the PR ready and open, tell the operator, and resume when the next branch appears or the freeze lifts. The just-shipped fixes reach `release/vX+1` via the Phase 5 sync-back (`scripts/release/sync-next-cycle.mjs`); do not try to sync mid-release. This is a **coordination signal, not a permission lock**: the release captain and the campaign sessions share the `diegosouzapw` identity, so a GitHub branch-protection lock cannot distinguish them — only this honored marker prevents the mid-release commit races that forced full CHANGELOG re-reconciliation in v3.8.40/v3.8.41 (a parallel campaign advanced `release/vX.Y.Z` by 34 commits mid-run). The release captain's own reconciliation/cycle-open pushes are exempt — they _are_ the release. Fixes that must land during a freeze (a homologation finding) follow the post-merge read-only rule: land on `main` first via `fix/release-vX.Y.Z-*`. **⛔ ONLY `/generate-release` may raise a release-freeze, and ONLY at its Phase 0a (start of generating a new version) — lifted at Phase 12c after the squash-merge to `main`.** No campaign, session, or agent may open a `release-freeze` marker at any other time — a freeze is **never** a mid-development coordination tool. If a session ever believes a freeze is genuinely, unavoidably necessary outside the `/generate-release` flow, it **MUST first ask the operator (`diegosouzapw`) in chat, explicitly alert "estou criando um freeze" and get an explicit yes** — never open, extend, or re-open a `release-freeze` autonomously. Conversely, do **not** close/lift an active `/generate-release` freeze to unblock campaign merges: it protects the captain's single clean CI run and auto-lifts at Phase 12c — closing it early re-triggers the exact commit race it prevents. Verify a freeze is legitimate before acting on it: an open `release-freeze` whose title/body references an **OPEN** release PR (`gh pr view <N> --json state`) is the authorized captain freeze — hold, don't touch.
55422. **Cross-session safety — this repo is worked by MANY parallel sessions/agents at once; never step on another's in-flight work.** Two absolute bans, both recurring incidents (this rule exists because they keep happening):
555 - **(a) Never `git stash` / `git stash pop` — ANYWHERE in this repo, including inside an isolated worktree, and including inside any subagent you dispatch.** `git stash` operates on the **shared repository object store**, not the per-worktree working tree — so a stash pushed or popped in one session can silently clobber or resurrect another parallel session's uncommitted changes. This is not hypothetical: 2026-07-02 a `#5923` quotaCache change leaked into the unrelated `#2296` worktree via a global `stash pop`, and the same class reincided through a **subagent**. To compare working changes against a base ref **without** stashing, use `git show <ref>:<path>` or `git diff <ref> -- <path>`; to confirm a typecheck/lint error is pre-existing on the base, inspect the base ref directly (`git show origin/release/vX.Y.Z:<path>`) — never stash your tree away to "get it clean". **Put this ban verbatim in the prompt of every subagent that touches git** (agents don't inherit this file's context — the recurrence was a subagent).
556 - **(b) Never merge, push, rebase, or force-push a PR / branch / worktree that another session is actively working.** An open PR whose head is a live fix worktree in `.claude/worktrees/` you did **not** create (e.g. `fix-5852`/`fix-5923` carrying fresh commits, even when they share your `diegosouzapw` identity), or any branch another session owns, is **off-limits — HOLD**, and let the owning session merge it. **Before** merging or pushing to any PR you did not create _this_ session, run `git worktree list` to check for a matching in-flight worktree and re-check `gh pr view <N> --json state,headRefOid`. Only the owning session merges its own in-flight PR; mid-flight merges race the owner and re-trigger the exact commit/CHANGELOG races Rule #19 and Rule #21 guard against. (Reinforces Rule #19.)
557 
558---
 
 
 
 
559 
560## PII & Stream Sanitization Learnings
 
 
 
 
561 
562### 1. Regex Security (ReDoS)
563 
564All regex patterns matching variable-length strings (e.g. IPv6 address, credit cards) must use strictly bounded, non-overlapping sequences (e.g., limit occurrences with bounded ranges `{1,7}`) to prevent catastrophic backtracking when processing untrusted inputs.
565 
566### 2. SSE Snapshot Handling
567 
568When parsing streaming LLM responses (e.g. Responses API), check if a chunk represents a final snapshot (`done` or `completed` events). Snapshot text must be sanitized directly as a standalone string (bypassing rolling delta buffers) to prevent text duplication at the end of the stream.
569 
570### 3. Database Handles in Tests
571 
572Ensure that any unit tests that trigger database migrations or establish SQLite connections call `resetDbInstance()` and properly clean up/close all DB handles in a `test.after(...)` hook. Failure to release database connection handles will cause Node's native test runner to hang indefinitely.
 
 
 
 
 
573 
@@ −1 +1 @@
1−# omniroute — Agent Guidelines
1+# CLAUDE.md
22  
3−## Project
3+This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
44  
5−Unified AI proxy/router — route any LLM through one endpoint. Multi-provider support
6−with **290 provider entries** (OpenAI, Anthropic, Gemini, DeepSeek, Groq, xAI, Mistral, Fireworks,
7−Cohere, NVIDIA, Cerebras, Pollinations, Puter, Cloudflare AI, HuggingFace, DeepInfra,
8−SambaNova, Meta Llama API, Moonshot AI, AI21 Labs, Databricks, Snowflake, and many more)
9−with **MCP Server** (104 tools), **A2A v0.3 Protocol**, and **Electron desktop app**.
5+## Quick Start
106  
11−> **Live counts (v3.8.49)**: providers 290 · MCP tools 104 · MCP scopes 30 · A2A skills 6 ·
12−> open-sse services 134 · routing strategies 17 · auto-combo scoring factors 12 ·
13−> DB modules 95 · DB migrations 110 · base tables 17 · search providers 11 ·
14−> i18n locales 42. **Refresh with `npm run check:docs-all`.**
7+```bash
8+npm install # Install deps (auto-generates .env from .env.example)
9+npm run dev # Dev server at http://localhost:20128
10+npm run build # Production build (Next.js 16 standalone)
11+npm run lint # ESLint (0 errors expected; warnings are pre-existing)
12+npm run typecheck:core # TypeScript check (should be clean)
13+npm run typecheck:noimplicit:core # Strict check (no implicit any)
14+npm run test:coverage # Unit tests + coverage gate (60/60/60/60 — statements/lines/functions/branches)
15+npm run check # lint + test combined
16+npm run check:cycles # Detect circular dependencies
17+```
1518  
16−## Doc Accuracy Discipline (read before writing any doc)
19+### Running Tests
1720  
18−> **If `grep -rn "name" src/ open-sse/ bin/` returns nothing, the name does not exist. Do not document it.**
21+```bash
22+# Single test file (Node.js native test runner — most tests)
23+node --import tsx/esm --test tests/unit/your-file.test.ts
1924  
20−The recurring failure mode in AI-generated docs is _plausible-but-unverified specifics_.
21−Every claim in a `.md` file under `docs/` should be verifiable against the source.
25+# Vitest (MCP server, autoCombo, cache)
26+npm run test:vitest
2227  
23−**Rules (enforced by `npm run check:fabricated-docs`):**
28+# All suites
29+npm run test:all
30+```
2431  
25−1. **Never state an API name, endpoint, path, CLI command, or env var without grepping for it first.**
26− ```bash
27− grep -rn "theName" src/ open-sse/ bin/
28− # 0 hits → do not document
29− ```
30−2. **Never write a line count, file size, migration count, provider count, or strategy count from memory.**
31− ```bash
32− wc -l <file> # exact line count
33− ls <dir>/*.ts | wc -l # file count
34− ```
35−3. **Every code example should be copy-pasted from real usage or actually run** — not synthesized.
36− Link to a real call site (`path:line`) instead of inventing a signature.
37−4. **Prefer citing real source (`file.ts:line`) over paraphrasing behavior** — verifiable and self-correcting.
38−5. **A shorter doc that is 100% accurate beats a comprehensive one with fabrications.**
39− Wrong docs cost more than missing docs, because people trust and act on them.
32+For full test matrix, see `CONTRIBUTING.md` → "Running Tests". For deep architecture, see `AGENTS.md`.
4033  
41−The script `scripts/check/check-fabricated-docs.mjs` extracts every route path, env var, hook
42−name, function name, and file reference from `docs/**/*.md` and verifies each one against the
43−codebase. Run it locally before pushing docs; it runs in CI via `npm run check:docs-all`.
44− 
45−## Stack
46− 
47−- **Runtime**: Next.js 16 (App Router), Node.js `>=22.0.0 <23 || >=24.0.0 <27`, ES Modules (`"type": "module"`)
48−- **Language**: TypeScript 6.0 (`src/`) + JavaScript (`open-sse/`, `electron/`)
49−- **Database**: better-sqlite3 (SQLite) — `DATA_DIR` configurable, default `~/.omniroute/`
50−- **Streaming**: SSE via `open-sse` internal workspace package
51−- **Styling**: Tailwind CSS v4
52−- **i18n**: next-intl with 42 locales (`src/i18n/messages/`) — refresh with `ls src/i18n/messages/*.json | wc -l`
53−- **Desktop**: Electron (cross-platform: Windows, macOS, Linux)
54−- **Schemas**: Zod v4 for all API / MCP input validation
55− 
5634 ---
5735  
58−## Build, Lint, and Test Commands
36+## Project at a Glance
5937  
60−| Command | Description |
61−| ----------------------------------- | ------------------------------------------------------------------ |
62−| `npm run dev` | Start Next.js dev server |
63−| `npm run build` | Production build: `next build` → `.build/next/` + assemble `dist/` |
64−| `npm run build:release` | Clean rebuild + HEAD sentinel (`dist/BUILD_SHA`) — use for deploy |
65−| `npm run start` | Run production build |
66−| `npm run build:cli` | Build CLI package |
67−| `npm run lint` | ESLint on all source files |
68−| `npm run typecheck:core` | TypeScript core type checking |
69−| `npm run typecheck:noimplicit:core` | Strict checking (no implicit any) |
70−| `npm run check` | Run lint + test |
71−| `npm run check:cycles` | Check for circular dependencies |
72−| `npm run electron:dev` | Run Electron app in dev mode |
73−| `npm run electron:build` | Build Electron app for current OS |
38+**OmniRoute** — unified AI proxy/router. One endpoint, 290 LLM providers, auto-fallback.
7439  
75−**Build output layout:**
40+| Layer | Location | Purpose |
41+| ------------- | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
42+| API Routes | `src/app/api/v1/` | Next.js App Router — entry points |
43+| Handlers | `open-sse/handlers/` | Request processing (chat, embeddings, etc) |
44+| Executors | `open-sse/executors/` | Provider-specific HTTP dispatch |
45+| Translators | `open-sse/translator/` | Format conversion (OpenAI↔Claude↔Gemini) |
46+| Transformer | `open-sse/transformer/` | Responses API ↔ Chat Completions |
47+| Services | `open-sse/services/` | Combo routing, rate limits, caching, etc |
48+| Database | `src/lib/db/` | SQLite domain modules (130 migrations) |
49+| Domain/Policy | `src/domain/` | Policy engine, cost rules, fallback logic |
50+| MCP Server | `open-sse/mcp-server/` | 104 tools (42 base + memory/skill/agentSkill/pool/notion/obsidian/gamification/plugin modules), 3 transports (stdio / SSE / Streamable HTTP), 31 scopes |
51+| A2A Server | `src/lib/a2a/` | JSON-RPC 2.0 agent protocol |
52+| Skills | `src/lib/skills/` | Extensible skill framework |
53+| Memory | `src/lib/memory/` | Persistent conversational memory |
7654  
77−| Directory | Purpose | Gitignored |
78−| --------- | -------------------------------------------------- | ---------- |
79−| `src/` | Application source (TypeScript / TSX) | No |
80−| `.build/` | Build intermediates (`distDir = .build/next`) | Yes |
81−| `dist/` | Shippable bundle assembled by `assembleStandalone` | Yes |
55+Monorepo: `src/` (Next.js 16 app), `open-sse/` (streaming engine workspace), `electron/` (desktop app), `tests/`, `bin/` (CLI entry point).
8256  
83−The pipeline is a single `next build` pass — intermediates land in `.build/next/`, the
84−assembled bundle in `dist/`. VPS deploys rsync `dist/` into the remote
85−`/usr/lib/node_modules/omniroute/app/` directory (VPS image path is unchanged).
57+---
8658  
87−### Running Tests
59+## Request Pipeline
8860  
89−```bash
90−# All tests (unit + vitest + ecosystem + e2e)
91−npm run test:all
92− 
93−# Single test file (Node.js native test runner — most tests use this)
94−node --import tsx/esm --test tests/unit/your-file.test.ts
95−node --import tsx/esm --test tests/unit/plan3-p0.test.ts
96−node --import tsx/esm --test tests/unit/fixes-p1.test.ts
97−node --import tsx/esm --test tests/unit/security-fase01.test.ts
98− 
99−# Integration tests
100−node --import tsx/esm --test tests/integration/*.test.ts
101− 
102−# Vitest (MCP server, autoCombo)
103−npm run test:vitest
104− 
105−# E2E with Playwright
106−npm run test:e2e
107− 
108−# Protocol clients E2E (MCP transports, A2A)
109−npm run test:protocols:e2e
110− 
111−# Ecosystem compatibility tests
112−npm run test:ecosystem
113− 
114−# Coverage (see CONTRIBUTING.md)
115−npm run test:coverage
11661 ```
62+Client → /v1/chat/completions (Next.js route)
63+ → CORS → Zod validation → auth? → policy check → prompt injection guard
64+ → handleChatCore() [open-sse/handlers/chatCore.ts]
65+ → cache check → rate limit → combo routing?
66+ → resolveComboTargets() → handleSingleModel() per target
67+ → translateRequest() → getExecutor() → executor.execute()
68+ → fetch() upstream → retry w/ backoff
69+ → response translation → SSE stream or JSON
70+ → If Responses API: responsesTransformer.ts TransformStream
71+```
11772  
118−**For authoritative coverage requirements, test execution, and PR gates, see [`CONTRIBUTING.md`](CONTRIBUTING.md#running-tests).**
73+API routes follow a consistent pattern: `Route → CORS preflight → Zod body validation → Optional auth (extractApiKey/isValidApiKey) → API key policy enforcement → Handler delegation (open-sse)`. No global Next.js middleware — interception is route-specific.
11974  
75+**Combo routing** (`open-sse/services/combo.ts`): 19 public strategies (priority, weighted, fill-first, round-robin, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, cache-optimized, context-relay, fusion, pipeline). Each target calls `handleSingleModel()` which wraps `handleChatCore()` with per-target error handling and circuit breaker checks. The `fusion` strategy is the exception: it fans out to a panel of models in parallel, then a judge model synthesizes one final answer (`open-sse/services/fusion.ts`). See `docs/routing/AUTO-COMBO.md` for the 13-factor Auto-Combo scoring + the full strategy table and `docs/architecture/RESILIENCE_GUIDE.md` for the 3 resilience layers.
76+ 
12077 ---
12178  
122−## Code Style Guidelines
79+## Resilience Runtime State
12380  
124−### Formatting (Prettier — enforced via lint-staged)
81+OmniRoute has three related but distinct temporary-failure mechanisms. Keep their
82+scope separate when debugging routing behavior. See the
83+[3-layer resilience diagram](./docs/diagrams/exported/resilience-3layers.svg)
84+(source: [docs/diagrams/resilience-3layers.mmd](./docs/diagrams/resilience-3layers.mmd))
85+for an at-a-glance map.
12586  
126−2 spaces · semicolons required · double quotes (`"`) · 100 char width · es5 trailing commas.
127−Always run `prettier --write` on changed files.
87+### Provider Circuit Breaker
12888  
129−### TypeScript
89+**Scope**: whole provider, e.g. `glm`, `openai`, `anthropic`.
13090  
131−- **Target**: ES2022 · **Module**: `esnext` · **Resolution**: `bundler`
132−- `strict: false` — prefer explicit types, don't rely on inference
133−- Path aliases: `@/*` → `src/`, `@omniroute/open-sse` → `open-sse/`, `@omniroute/open-sse/*` → `open-sse/*`
91+**Purpose**: stop sending traffic to a provider that is repeatedly failing at the
92+upstream/service level, so one unhealthy provider does not slow down every request.
13493  
135−### ESLint Rules
94+**Implementation**:
13695  
137−- **Security (error, everywhere)**: `no-eval`, `no-implied-eval`, `no-new-func`
138−- **Relaxed in `open-sse/` and `tests/`**: `@typescript-eslint/no-explicit-any` = warn
139−- React hooks rules and `@next/next/no-assign-module-variable` disabled in `open-sse/` and `tests/`
96+- Core class: `src/shared/utils/circuitBreaker.ts`
97+- Chat gate/execution wiring: `src/sse/handlers/chatHelpers.ts`, `src/sse/handlers/chat.ts`
98+- Runtime status API: `src/app/api/monitoring/health/route.ts`
99+- Shared wrappers: `open-sse/services/accountFallback.ts`
100+- Persisted state table: `domain_circuit_breakers`
140101  
141−### Naming
102+**States**:
142103  
143−| Element | Convention | Example |
144−| ------------------- | -------------------------------- | ------------------------------------ |
145−| Files | camelCase / kebab-case | `chatCore.ts`, `tokenHealthCheck.ts` |
146−| React components | PascalCase | `Dashboard.tsx`, `ProviderCard.tsx` |
147−| Functions/variables | camelCase | `getHealth()`, `switchCombo()` |
148−| Constants | UPPER_SNAKE | `MAX_RETRIES`, `DEFAULT_TIMEOUT` |
149−| Interfaces | PascalCase (`I` prefix optional) | `ProviderConfig` |
150−| Enums | PascalCase (members too) | `LogLevel.Error` |
104+- `CLOSED`: normal traffic is allowed.
105+- `OPEN`: provider is temporarily blocked; callers get a provider-circuit-open response
106+ or combo routing skips to another target.
107+- `HALF_OPEN`: reset timeout has elapsed; allow a probe request. Success closes the
108+ breaker, failure opens it again.
151109  
152−### Imports
110+**Defaults** (`open-sse/config/constants.ts`):
153111  
154−- **Order**: external → internal (`@/`, `@omniroute/open-sse`) → relative (`./`, `../`)
155−- **No barrel imports** from `localDb.ts` — import from the specific `db/` module instead
112+- OAuth providers: threshold `3`, reset timeout `60s`.
113+- API-key providers: threshold `5`, reset timeout `30s`.
114+- Local providers: threshold `2`, reset timeout `15s`.
156115  
157−### Error Handling
116+Only provider-level failure statuses should trip the provider breaker:
158117  
159−- try/catch with specific error types; always log with context (pino logger)
160−- Never silently swallow errors in SSE streams — use abort signals for cleanup
161−- Return proper HTTP status codes (4xx client, 5xx server)
118+```ts
119+(408, 500, 502, 503, 504);
120+```
162121  
163−### Security
122+Do not trip the whole-provider breaker for normal account/key/model errors like most
123+`401`, `403`, or `429` cases. Those usually belong to connection cooldown or model
124+lockout. A generic API-key provider `403` should be recoverable unless it is classified
125+as a terminal provider/account error.
164126  
165−- **NEVER** commit API keys, secrets, or credentials
166−- Validate all user inputs with Zod schemas
167−- Auth middleware required on all API routes
168−- Never log SQLite encryption keys
169−- Sanitize user content (dompurify for HTML)
170−- **Public upstream OAuth identifiers** (Gemini / Antigravity / Windsurf-style client_id/secret + Firebase Web keys extracted from public CLIs): use `resolvePublicCred()` from `open-sse/utils/publicCreds.ts`, **never** as string literals. Full pattern in `docs/security/PUBLIC_CREDS.md`.
171−- **Error responses** (HTTP / SSE / executor / MCP): use `buildErrorBody()` or `sanitizeErrorMessage()` from `open-sse/utils/error.ts`, **never** put raw `err.stack` / `err.message` in a Response body. Full pattern in `docs/security/ERROR_SANITIZATION.md`.
172−- **`exec()` / `spawn()` with runtime values**: pass via the `env` option, **never** string-interpolate paths/values into the script body. Reference: `src/mitm/cert/install.ts::updateNssDatabases`.
173−- Prefer secure-by-default libraries when available — see [tldrsec/awesome-secure-defaults](https://github.com/tldrsec/awesome-secure-defaults) for the curated list (Helmet.js, DOMPurify, ssrf-req-filter, safe-regex, Google Tink, etc.).
127+The breaker uses lazy recovery, not a background timer. When `OPEN` expires, reads such
128+as `getStatus()`, `canExecute()`, and `getRetryAfterMs()` refresh the state to
129+`HALF_OPEN`, so dashboards and combo candidate builders do not keep excluding an
130+expired provider forever.
174131  
175−---
132+### Connection Cooldown
176133  
177−## Architecture
134+**Scope**: one provider connection/account/key.
178135  
179−### Data Layer (`src/lib/db/`)
136+**Purpose**: temporarily skip one bad key/account while allowing other connections for
137+the same provider to continue serving requests.
180138  
181−All persistence uses SQLite through **95 domain-specific modules** in `src/lib/db/`. Top modules:
139+**Implementation**:
182140  
183−- Core: `core.ts`, `migrationRunner.ts`, `encryption.ts`, `stateReset.ts`
184−- Providers / catalog: `providers.ts`, `models.ts`, `providerLimits.ts`, `compressionAnalytics.ts`
185−- Routing: `combos.ts`, `modelComboMappings.ts`, `domainState.ts`, `commandCodeAuth.ts`
186−- Auth: `apiKeys.ts`, `secrets.ts`, `registeredKeys.ts`, `sessionAccountAffinity.ts`
187−- Usage / billing: `quotaSnapshots.ts`, `creditBalance.ts`, `usage*.ts`, `compressionCacheStats.ts`
188−- Storage: `backup.ts`, `cleanup.ts`, `jsonMigration.ts`, `healthCheck.ts`, `databaseSettings.ts`
189−- Extension modules: `evals.ts`, `webhooks.ts`, `reasoningCache.ts`, `readCache.ts`, `tierConfig.ts`, `compressionCombos.ts`, `compressionScheduler.ts`, `batches.ts`, `files.ts`, `syncTokens.ts`, `proxies.ts`, `oneproxy.ts`, `upstreamProxy.ts`, `versionManager.ts`, `cliToolState.ts`, `prompts.ts`, `detailedLogs.ts`, `contextHandoffs.ts`, `compression.ts`, `stats.ts`
141+- Write/update path: `src/sse/services/auth.ts::markAccountUnavailable()`
142+- Account selection/filtering: `src/sse/services/auth.ts::getProviderCredentials...`
143+- Cooldown calculation: `open-sse/services/accountFallback.ts::checkFallbackError()`
144+- Settings: `src/lib/resilience/settings.ts`
190145  
191−Live count: `ls src/lib/db/*.ts | wc -l` (currently 95). Drift detection: `npm run check:docs-counts`.
192−Schema migrations live in `db/migrations/` (**110 files** as of v3.8.43) and run via `migrationRunner.ts`.
193−`src/lib/localDb.ts` is a **re-export layer only** — never add logic there.
146+Important fields on provider connections:
194147  
195−#### DB Internals
148+```ts
149+rateLimitedUntil;
150+testStatus: "unavailable";
151+lastError;
152+lastErrorType;
153+errorCode;
154+backoffLevel;
155+```
196156  
197−- **`core.ts`**: `getDbInstance()` returns a singleton `better-sqlite3` instance with WAL
198− journaling. `SCHEMA_SQL` defines **17 base tables** (verify with `grep -c "CREATE TABLE" src/lib/db/core.ts` minus 1 for the bookkeeping `_omniroute_migrations` table). Helpers: `rowToCamel`, `encryptConnectionFields`.
199−- **`migrationRunner.ts`**: Applies versioned SQL files from `db/migrations/` inside transactions.
200− Tracks applied migrations in `_omniroute_migrations` table.
201−- **Migrations**: 110 files (`001_initial_schema.sql` → `110_*.sql`).
202− Each migration is idempotent and runs in a transaction. Live count: `ls src/lib/db/migrations/*.sql | wc -l`.
203−- **Domain modules** import `getDbInstance()` from `core.ts` for all CRUD operations.
204− Each module owns a specific table/set of tables (e.g., `providers.ts` → `provider_connections`,
205− `combos.ts` → `combos`). Encryption helpers protect sensitive fields at rest.
206−- **`localDb.ts`** re-exports all domain modules — consumers import from here for convenience.
157+During account selection, a connection is skipped while:
207158  
208−### API Route Layer (`src/app/api/v1/`)
209− 
210−Next.js App Router routes — each follows a consistent pattern:
211− 
159+```ts
160+new Date(rateLimitedUntil).getTime() > Date.now();
212161 ```
213−Route → CORS preflight → Body validation (Zod) → Optional auth (extractApiKey/isValidApiKey)
214− → API key policy enforcement (enforceApiKeyPolicy) → Handler delegation (open-sse)
215−```
216162  
217−| Route | Handler | Notes |
218−| ------------------------------- | ------------------------- | ------------------------------------------------------------- |
219−| `chat/completions/route.ts` | `handleChat()` | + prompt injection guard (clones request) |
220−| `responses/route.ts` | `handleChat()` (unified) | Responses API format |
221−| `embeddings/route.ts` | `handleEmbedding()` | Model listing + creation |
222−| `images/generations/route.ts` | `handleImageGeneration()` | Model listing + creation |
223−| `audio/transcriptions/route.ts` | audio handler | Multipart form data |
224−| `audio/speech/route.ts` | TTS handler | Binary audio response |
225−| `videos/generations/route.ts` | video handler | ComfyUI/SD WebUI |
226−| `music/generations/route.ts` | music handler | ComfyUI workflows |
227−| `moderations/route.ts` | moderation handler | Content safety |
228−| `rerank/route.ts` | rerank handler | Document relevance |
229−| `search/route.ts` | search handler | Web search (12 providers per `open-sse/handlers/search.ts:6`) |
163+Cooldowns are also lazy: when `rateLimitedUntil` is in the past, the connection becomes
164+eligible again. On successful use, `clearAccountError()` clears `testStatus`,
165+`rateLimitedUntil`, error fields, and `backoffLevel`.
230166  
231−**No global Next.js middleware file** — interception is route-specific. Auth is optional
232−(controlled by `REQUIRE_API_KEY` env). Prompt injection guard is unique to chat completions.
167+Default connection cooldown behavior:
233168  
234−### Request Pipeline (`open-sse/`)
169+- OAuth base cooldown: `5s`.
170+- API-key base cooldown: `3s`.
171+- API-key `429` should prefer upstream retry hints (`Retry-After`, reset headers, or
172+ parseable reset text) when available.
173+- Repeated recoverable failures use exponential backoff:
235174  
236−The `open-sse/` workspace is the core streaming engine. Full request flow:
237− 
175+```ts
176+baseCooldownMs * 2 ** failureIndex;
238177 ```
239−Client Request
240− → src/app/api/v1/.../route.ts (Next.js route)
241− → open-sse/handlers/chatCore.ts::handleChatCore()
242− → Semantic/signature cache check
243− → Rate limit check (rateLimitManager)
244− → Combo routing? → open-sse/services/combo.ts::handleComboChat()
245− → resolveComboTargets() → ordered ResolvedComboTarget[]
246− → For each target: handleSingleModel() (wraps chatCore)
247− → translateRequest() (open-sse/translator/)
248− → Convert source format (e.g., OpenAI) → target format (e.g., Claude)
249− → getExecutor() → provider-specific executor instance
250− → executor.execute() (BaseExecutor → DefaultExecutor or provider-specific)
251− → buildUrl() + buildHeaders() + transformRequest()
252− → fetch() to upstream provider
253− → Retry logic with exponential backoff
254− → Response translation back to client format
255− → If Responses API: responsesTransformer.ts TransformStream
256− → SSE stream or JSON response to client
257−```
258178  
259−**Handlers** (`open-sse/handlers/`): `chatCore.ts`, `responsesHandler.ts`, `embeddings.ts`,
260−`imageGeneration.ts`, `videoGeneration.ts`, `musicGeneration.ts`, `audioSpeech.ts`,
261−`audioTranscription.ts`, `moderations.ts`, `rerank.ts`, `search.ts`.
179+The anti-thundering-herd guard prevents concurrent failures on the same connection from
180+repeatedly extending the cooldown or double-incrementing `backoffLevel`.
262181  
263−**Upstream headers**: merged after default auth; same header name replaces executor value.
264−**T5 intra-family fallback** recomputes headers using only the fallback model id.
265−Forbidden header names: `src/shared/constants/upstreamHeaders.ts` — keep sanitize,
266−Zod schemas, and unit tests aligned when editing.
182+Terminal states are not cooldowns. `banned`, `expired`, and `credits_exhausted` are
183+intended to stay unavailable until credentials/settings change or an operator resets
184+them. Do not overwrite terminal states with transient cooldown state.
267185  
268−### Provider Categories
186+### Model Lockout
269187  
270−- **Free** (2): Qoder AI, Kiro AI
271−- **OAuth** (13): Claude Code, Antigravity, Codex, GitHub Copilot, Cursor, Kimi Coding, Kilo Code, Cline, Kiro, Qoder, Gemini, Windsurf (v3.8), GitLab Duo (v3.8)
272−- **API Key** (120+): OpenAI, Anthropic, Gemini, DeepSeek, Groq, xAI, Mistral, Perplexity,
273− Together, Fireworks, Cerebras, Cohere, NVIDIA, Nebius, SiliconFlow, Hyperbolic,
274− HuggingFace, OpenRouter, Vertex AI, Cloudflare AI, Scaleway, AI/ML API, Pollinations,
275− Puter, Longcat, Alibaba, Kimi, Minimax, Blackbox, Synthetic, Kilo Gateway,
276− Z.AI, GLM, Deepgram, AssemblyAI, ElevenLabs, Cartesia, PlayHT, Inworld,
277− NanoBanana, SD WebUI, ComfyUI, Ollama Cloud, Perplexity Search, Serper, Brave, Exa,
278− Tavily, OpenCode Zen/Go, Bailian Coding Plan, DeepInfra, Vercel AI Gateway,
279− Lambda AI, SambaNova, nScale, OVHcloud AI, Baseten, PublicAI, Moonshot AI,
280− Meta Llama API, v0 (Vercel), Morph, Featherless AI, FriendliAI, LlamaGate,
281− Galadriel, Weights & Biases Inference, Volcengine, AI21 Labs, Venice.ai,
282− Codestral, Upstage, Maritalk, Xiaomi MiMo, Inference.net, NanoGPT, Predibase,
283− Bytez, Heroku AI, Databricks, Snowflake Cortex, GigaChat (Sber), CrofAI,
284− AgentRouter, ChatGPT Web, Baidu Qianfan, AWS Polly, RunwayML, GitLab Duo,
285− Amazon Q, Empower, Poe, and many more.
286−- **Self-Hosted** (8+): LM Studio, vLLM, Lemonade, Llamafile, Triton, Docker Model Runner, Xinference, Oobabooga
287−- **Custom**: OpenAI-compatible (`openai-compatible-*`) and Anthropic-compatible (`anthropic-compatible-*`) prefixes
188+**Scope**: provider + connection + model.
288189  
289−Providers are registered in `src/shared/constants/providers.ts` with Zod validation at module load.
190+**Purpose**: avoid disabling a whole connection when only one model is unavailable or
191+quota-limited for that connection.
290192  
291−### Executors (`open-sse/executors/`)
193+Examples:
292194  
293−Provider-specific request executors: `base.ts`, `default.ts`, `cursor.ts`, `codex.ts`,
294−`antigravity.ts`, `github.ts`, `kiro.ts`, `qoder.ts`, `vertex.ts`,
295−`cloudflare-ai.ts`, `opencode.ts`, `pollinations.ts`, `puter.ts`.
195+- Per-model quota providers returning `429`.
196+- Local providers returning `404` for one missing model.
197+- Provider-specific mode/model permission failures such as selected Grok modes.
296198  
297−#### Executor Internals
199+Model lockout lives in `open-sse/services/accountFallback.ts` and lets the same
200+connection continue serving other models.
298201  
299−- **`base.ts`** (`BaseExecutor`): Abstract base with `buildUrl()`, `buildHeaders()`,
300− `transformRequest()`, retry logic (exponential backoff), and `execute()`. Subclasses
301− override URL/header/transform methods for provider-specific behavior.
302−- **`default.ts`** (`DefaultExecutor extends BaseExecutor`): Handles most OpenAI-compatible
303− providers. Reads provider config from `providerRegistry.ts` to resolve base URL, auth
304− header format, and request transformations.
305−- **`getExecutor()`** (`executors/index.ts`): Factory that returns the correct executor
306− instance based on provider ID. Provider-specific executors (Cursor, Codex, Vertex, etc.)
307− override only what differs from the default.
202+### Debugging Guidance
308203  
309−### Translator (`open-sse/translator/`)
204+- If all keys for a provider are skipped, inspect both provider breaker state and each
205+ connection's `rateLimitedUntil`/`testStatus`.
206+- If a provider appears permanently excluded after the reset window, check whether code
207+ is reading raw `state` instead of using `getStatus()`/`canExecute()`.
208+- If one provider key fails but others should work, prefer connection cooldown over
209+ provider breaker.
210+- If only one model fails, prefer model lockout over connection cooldown.
211+- If a state should self-recover, it should have a future timestamp/reset timeout and a
212+ read path that refreshes expired state. Permanent statuses require manual credential
213+ or config changes.
310214  
311−Translates between API formats (OpenAI-format ↔ Anthropic, Gemini, etc.).
312−Includes request/response translators with helpers for image handling.
215+---
313216  
314−#### Translator Internals
217+## Key Conventions
315218  
316−- **`translator/index.ts`**: Exports `translateRequest()` and format constants. Called by
317− `chatCore.ts` before executor dispatch.
318−- **Flow**: `translateRequest(body, sourceFormat, targetFormat)` → detects source format
319− (OpenAI, Anthropic, Gemini) → applies the matching translator module → returns
320− transformed body ready for the target provider.
321−- **Response translation** runs in reverse after upstream response, converting back to
322− the client's expected format.
219+### Code Style
323220  
324−### Transformer (`open-sse/transformer/`)
221+- **2 spaces**, semicolons, double quotes, 100 char width, es5 trailing commas (enforced by lint-staged via Prettier)
222+- **Imports**: external → internal (`@/`, `@omniroute/open-sse`) → relative
223+- **Naming**: files=camelCase/kebab, components=PascalCase, constants=UPPER_SNAKE
224+- **ESLint**: `no-eval`, `no-implied-eval`, `no-new-func` = error everywhere; `no-explicit-any` = **error** in `open-sse/` and `tests/` (since #6218 — pre-existing violations are frozen in `config/quality/eslint-suppressions.json`, new ones must be fixed; `npm run lint` applies the suppressions and is what CI runs)
225+- **TypeScript**: `strict: false`, target ES2022, module esnext, resolution bundler. Prefer explicit types.
325226  
326−`responsesTransformer.ts` — transforms Responses API format to/from Chat Completions format.
227+### Database
327228  
328−#### Transformer Internals
229+- **Always** go through `src/lib/db/` domain modules — **never** write raw SQL in routes or handlers
230+- **Never** add logic to `src/lib/localDb.ts` (re-export layer only)
231+- **Never** barrel-import from `localDb.ts` — import specific `db/` modules instead
232+- DB singleton: `getDbInstance()` from `src/lib/db/core.ts` (WAL journaling)
233+- Migrations: `src/lib/db/migrations/` — versioned SQL files, idempotent, run in transactions
329234  
330−- **`createResponsesApiTransformStream()`**: Returns a `TransformStream` that converts
331− Chat Completions SSE chunks (`data: {"choices":[...]}`) into Responses API SSE events
332− (`response.output_item.added`, `response.output_text.delta`, etc.).
333−- Used when the client sends a Responses API request: the request is internally converted
334− to Chat Completions format, dispatched normally, and the response is piped through this
335− transform stream before reaching the client.
235+### Error Handling
336236  
337−### Services (`open-sse/services/`)
237+- try/catch with specific error types, log with pino context
238+- Never swallow errors in SSE streams — use abort signals for cleanup
239+- Return proper HTTP status codes (4xx/5xx)
338240  
339−134 service modules in `open-sse/services/` (top-level only; more including sub-dirs like `autoCombo/` and `compression/`). Refresh: `ls open-sse/services/*.ts | wc -l`. Key modules:
340−`combo.ts` (routing engine), `usage.ts`, `tokenRefresh.ts`,
341−`rateLimitManager.ts`, `accountFallback.ts`, `sessionManager.ts`, `wildcardRouter.ts`,
342−`autoCombo/`, `intentClassifier.ts`, `taskAwareRouter.ts`, `thinkingBudget.ts`,
343−`contextManager.ts`, `modelDeprecation.ts`, `modelFamilyFallback.ts`,
344−`emergencyFallback.ts`, `workflowFSM.ts`, `backgroundTaskDetector.ts`, `ipFilter.ts`,
345−`signatureCache.ts`, `volumeDetector.ts`, `contextHandoff.ts`, `compression/` (prompt
346−compression pipeline), and more.
241+### Security
347242  
348−#### Prompt Compression Pipeline (`compression/`)
243+- **Never** use `eval()`, `new Function()`, or implied eval
244+- Validate all inputs with Zod schemas
245+- Encrypt credentials at rest (AES-256-GCM)
246+- Upstream header denylist: `src/shared/constants/upstreamHeaders.ts` — keep sanitize, Zod schemas, and unit tests aligned when editing
247+- **Public upstream credentials** (Gemini/Antigravity/Windsurf-style OAuth client_id/secret + Firebase Web keys extracted from public CLIs): **MUST** be embedded via `resolvePublicCred()` from `open-sse/utils/publicCreds.ts` — **never** as string literals. See `docs/security/PUBLIC_CREDS.md` for the mandatory pattern.
248+- **Error responses** (HTTP / SSE / executor / MCP handler): **MUST** route through `buildErrorBody()` or `sanitizeErrorMessage()` from `open-sse/utils/error.ts` — **never** put raw `err.stack` or `err.message` in a response body. See `docs/security/ERROR_SANITIZATION.md`.
249+- **Shell commands built from variables**: when calling `exec()`/`spawn()` with a script that needs runtime values, pass them via the `env` option (shell-escaped automatically) — **never** string-interpolate untrusted/external paths into the script body. Reference: `src/mitm/cert/install.ts::updateNssDatabases`.
250+- **Secure-by-default libraries** ([tldrsec/awesome-secure-defaults](https://github.com/tldrsec/awesome-secure-defaults)): prefer Helmet.js, DOMPurify, ssrf-req-filter, safe-regex, Google Tink over custom implementations whenever adding new security-sensitive surfaces.
349251  
350−Modular prompt compression that runs proactively before the existing reactive context manager.
252+---
351253  
352−- **`strategySelector.ts`**: Selects compression mode based on config, compression combo assignments,
353− combo overrides, auto-trigger thresholds, and defaults. Priority: assigned compression combo >
354− combo override > auto-trigger > default mode > off.
355−- **`lite.ts`**: 5 lite-mode techniques: `collapseWhitespace`, `dedupSystemPrompt`,
356− `compressToolResults`, `removeRedundantContent`, `replaceImageUrls`. Target: 10-15% savings at
357− <1ms latency.
358−- **`caveman.ts` / `cavemanRules.ts`**: Caveman-style semantic condensation backed by built-in
359− rules plus file-loaded language packs under `compression/rules/`.
360−- **`engines/rtk/`**: Rule-based terminal/tool-output compression inspired by RTK patterns. Detects
361− command output classes, applies JSON filter packs, deduplicates repeated lines, strips ANSI/code
362− noise, and preserves errors/actionable context. The RTK JSON DSL supports replace,
363− match-output short-circuit, strip/keep, per-line truncation, head/tail/max-line truncation,
364− inline tests, trust-gated project/global custom filters, and optional redacted raw-output
365− retention for authenticated recovery.
366−- **`engines/registry.ts`**: Registers engines (`caveman`, `rtk`) and powers stacked pipelines.
367−- **`stats.ts`**: Per-request compression stats tracking (original tokens, compressed tokens,
368− savings %, techniques used, engine breakdown, compression combo id).
369−- **`types.ts`**: `CompressionMode` (off/lite/standard/aggressive/ultra/rtk/stacked),
370− `CompressionConfig`, `CompressionStats`, `CompressionResult`.
371−- DB settings in `src/lib/db/compression.ts`, compression combos in
372− `src/lib/db/compressionCombos.ts`, API routes under `src/app/api/settings/compression/`,
373− `src/app/api/context/*`, and preview/language-pack routes under `src/app/api/compression/*`.
254+## Common Modification Scenarios
374255  
375−#### Combo Routing Engine (`combo.ts`)
256+### Adding a New Provider
376257  
377−- **`handleComboChat()`**: Entry point for combo-routed requests. Receives the combo config
378− and iterates through targets in order until one succeeds or all fail.
379−- **`resolveComboTargets()`**: Expands a combo configuration into an ordered array of
380− `ResolvedComboTarget[]`, each specifying provider + model + account + credentials.
381−- **Strategies** (17): priority, weighted, fill-first, round-robin, P2C, random, least-used, reset-aware (v3.8),
382− reset-window, cost-optimized, strict-random, auto, lkgp, context-optimized, context-relay, headroom, fusion. Source: `ROUTING_STRATEGY_VALUES` in `src/shared/constants/routingStrategies.ts`.
383−- Each target calls **`handleSingleModel()`** which wraps `handleChatCore()` with
384− per-target error handling and circuit breaker checks.
258+1. Register in `src/shared/constants/providers.ts` (Zod-validated at load)
259+2. Add executor in `open-sse/executors/` if custom logic needed (extend `BaseExecutor`)
260+3. Add translator in `open-sse/translator/` if non-OpenAI format
261+4. Add OAuth config in `src/lib/oauth/constants/oauth.ts` if OAuth-based — if the upstream CLI ships a public client_id/secret, embed via `resolvePublicCred()` (see `docs/security/PUBLIC_CREDS.md`), **never** as a literal
262+5. Register models in `open-sse/config/providerRegistry.ts`
263+6. Write tests in `tests/unit/` (include the publicCreds shape assertion if you added a new embedded default)
385264  
386−### Domain Layer (`src/domain/`)
265+### Adding a New API Route
387266  
388−Policy engine modules: `policyEngine.ts`, `comboResolver.ts`, `costRules.ts`,
389−`degradation.ts`, `fallbackPolicy.ts`, `lockoutPolicy.ts`, `modelAvailability.ts`,
390−`providerExpiration.ts`, `quotaCache.ts`, `responses.ts`, `configAudit.ts`.
267+1. Create directory under `src/app/api/v1/your-route/`
268+2. Create `route.ts` with `GET`/`POST` handlers
269+3. Follow pattern: CORS → Zod body validation → optional auth → handler delegation
270+4. Handler goes in `open-sse/handlers/` (import from there, not inline)
271+5. Error responses use `buildErrorBody()` / `errorResponse()` from `open-sse/utils/error.ts` (auto-sanitized — never put `err.stack` or `err.message` raw in the body). See `docs/security/ERROR_SANITIZATION.md`.
272+6. Add tests — including at least one assertion that error responses do not leak stack traces (`!body.error.message.includes("at /")`)
391273  
392−### MCP Server (`open-sse/mcp-server/`)
274+### Adding a New DB Module
393275  
394−**104 tools** total (`TOTAL_MCP_TOOL_COUNT`, `open-sse/mcp-server/server.ts`): a 42-entry base registry (`MCP_TOOLS` in `schemas/tools.ts`, bundling the core / cache / compression / 1proxy / advanced tools) **plus** standalone module sets — memory (3), skill (4), agentSkill (3), pool (6), gamification (8), plugin (8), notion (6), obsidian (22). 3 transports (stdio / SSE / Streamable HTTP). Scoped auth (31 scopes — see `OMNIROUTE_MCP_SCOPES`), Zod schemas. See [`docs/frameworks/MCP-SERVER.md`](docs/frameworks/MCP-SERVER.md).
276+1. Create `src/lib/db/yourModule.ts` — import `getDbInstance` from `./core.ts`
277+2. Export CRUD functions for your domain table(s)
278+3. Add migration in `src/lib/db/migrations/` if new tables needed
279+4. Re-export from `src/lib/localDb.ts` (add to the re-export list only)
280+5. Write tests
395281  
396−**Core tools** (20): get_health, list_combos, get_combo_metrics, switch_combo, check_quota,
397−route_request, cost_report, list_models_catalog, web_search, simulate_route, set_budget_guard,
398−set_routing_strategy, set_resilience_profile, test_combo, get_provider_metrics,
399−best_combo_for_task, explain_route, get_session_snapshot, db_health_check, sync_pricing.
282+### Adding a New MCP Tool
400283  
401−**Cache tools** (2): cache_stats, cache_flush.
284+1. Add tool definition in `open-sse/mcp-server/tools/` with Zod input schema + async handler
285+2. Register in tool set (wired by `createMcpServer()`)
286+3. Assign to appropriate scope(s)
287+4. Write tests (tool invocation logged to `mcp_audit` table)
402288  
403−**Compression tools** (5): compression_status, compression_configure, set_compression_engine,
404−list_compression_combos, compression_combo_stats.
289+### Adding a New A2A Skill
405290  
406−**1proxy tools** (3): oneproxy_fetch, oneproxy_rotate, oneproxy_stats.
291+1. Create skill in `src/lib/a2a/skills/` (5 already exist: smart-routing, quota-management, provider-discovery, cost-analysis, health-report)
292+2. Skill receives task context (messages, metadata) → returns structured result
293+3. Register in `A2A_SKILL_HANDLERS` in `src/lib/a2a/taskExecution.ts`
294+4. Expose in `src/app/.well-known/agent.json/route.ts` (Agent Card)
295+5. Write tests in `tests/unit/`
296+6. Document in `docs/frameworks/A2A-SERVER.md` skill table
407297  
408−**Memory tools** (3): memory_search, memory_add, memory_clear.
298+### Adding a New Cloud Agent
409299  
410−**Skill tools** (4): skills_list, skills_enable, skills_execute, skills_executions.
300+1. Create agent class in `src/lib/cloudAgent/agents/` extending `CloudAgentBase` (3 already exist: codex-cloud, devin, jules)
301+2. Implement `createTask`, `getStatus`, `approvePlan`, `sendMessage`, `listSources`
302+3. Register in `src/lib/cloudAgent/registry.ts`
303+4. Add OAuth/credentials handling if needed (`src/lib/oauth/providers/`)
304+5. Tests + document in `docs/frameworks/CLOUD_AGENT.md`
411305  
412−**Agent-skill tools** (3): A2A skill discovery / invocation bridges.
306+### Adding a New Embedded Service
413307  
414−**Gamification tools** (8): levels, badges, leaderboard, and community-federation queries.
308+1. Create installer in `src/lib/services/installers/{name}.ts` modeled on `ninerouter.ts` (use `runNpm` from `installers/utils.ts` — no shell interpolation, hard rule #13).
309+2. Register the service in `src/lib/services/bootstrap.ts` (add to `SERVICES[]` array and extend `buildSpawnArgsFactory()`).
310+3. Add a DB seed row for the new service in `src/lib/db/migrations/` (`version_manager` table, `status='not_installed'`, `auto_start=0`).
311+4. Create 7 API endpoints under `src/app/api/services/{name}/` (`_lib.ts`, `install`, `start`, `stop`, `restart`, `update`, `status`, `auto-start`). All delegate errors through `createErrorResponse()`. The shared `logs` endpoint is already wired via `[name]/logs/route.ts`.
312+5. Verify `/api/services/` is in `LOCAL_ONLY_API_PREFIXES` in `src/server/authz/routeGuard.ts`; add a test asserting `isLocalOnlyPath()` returns `true` for the new prefix if you add one (hard rule #17).
313+6. Add a UI tab in `src/app/(dashboard)/dashboard/providers/services/tabs/` reusing `ServiceStatusCard`, `ServiceLifecycleButtons`, `ServiceLogsPanel`.
314+7. Document in `docs/frameworks/EMBEDDED-SERVICES.md` (update §1 service table + §4 API reference) and `docs/openapi.yaml`.
315+8. Write tests: unit (`tests/unit/services/`), integration (`tests/integration/services/`, gated by `RUN_SERVICES_INT=1`), and update `docs/ops/RELEASE_CHECKLIST.md` smoke section.
415316  
416−**Plugin tools** (8): plugin marketplace listing, install/enable/disable, and runtime inspection.
317+### Adding a New Guardrail / Eval / Skill / Webhook event
417318  
418−**Notion tools** (6) + **Obsidian tools** (22): knowledge-base read/write integrations (the largest tool family — vault search, note CRUD, WebDAV-backed file ops).
319+- Guardrail: `src/lib/guardrails/` → docs: `docs/security/GUARDRAILS.md`
320+- Eval suite: `src/lib/evals/` → docs: `docs/frameworks/EVALS.md`
321+- Skill (sandbox): `src/lib/skills/` → docs: `docs/frameworks/SKILLS.md`
322+- Webhook event: `src/lib/webhookDispatcher.ts` → docs: `docs/frameworks/WEBHOOKS.md`
419323  
420−#### MCP Internals
324+---
421325  
422−- **Tool registration**: Each tool is an object with `{ name, description, inputSchema: ZodSchema,
423−handler: async (args) => {...} }`. Zod validates inputs before the handler fires.
424−- **`createMcpServer()`** and **`startMcpStdio()`** exported from `mcp-server/index.ts`.
425− `createMcpServer()` wires all tool sets; `startMcpStdio()` launches the stdio transport.
426−- **Transports**: stdio (CLI `omniroute --mcp`), SSE (`/api/mcp/sse`), Streamable HTTP
427− (`/api/mcp/stream`). All share the same tool/scope engine.
428−- **Scopes** (30): Control which tool categories an API key can access. Enforcement happens
429− before handler dispatch.
430−- **Audit**: Every tool invocation is logged to SQLite (`mcp_audit` table) with tool name,
431− args, success/failure, API key attribution, and timestamp.
326+## Reference Documentation
432327  
433−### A2A Server (`src/lib/a2a/`)
328+For any non-trivial change, read the matching deep-dive first:
434329  
435−JSON-RPC 2.0, SSE streaming, Task Manager with TTL cleanup.
436−Agent Card at `/.well-known/agent.json`.
437−Skills (6): `smartRouting.ts`, `quotaManagement.ts`, `providerDiscovery.ts`, `costAnalysis.ts`, `healthReport.ts`, `listCapabilities.ts`.
330+| Area | Doc |
331+| --------------------------------------------- | ------------------------------------------------------- |
332+| Repo navigation | `docs/architecture/REPOSITORY_MAP.md` |
333+| Architecture | `docs/architecture/ARCHITECTURE.md` |
334+| Engineering reference | `docs/architecture/CODEBASE_DOCUMENTATION.md` |
335+| Auto-Combo (13-factor scoring, 19 strategies) | `docs/routing/AUTO-COMBO.md` |
336+| Resilience (3 mechanisms) | `docs/architecture/RESILIENCE_GUIDE.md` |
337+| Reasoning replay | `docs/routing/REASONING_REPLAY.md` |
338+| Skills framework | `docs/frameworks/SKILLS.md` |
339+| Memory system (FTS5 + Qdrant) | `docs/frameworks/MEMORY.md` |
340+| Cloud agents | `docs/frameworks/CLOUD_AGENT.md` |
341+| Guardrails (PII / injection / vision) | `docs/security/GUARDRAILS.md` |
342+| Public upstream credentials (Gemini/etc.) | `docs/security/PUBLIC_CREDS.md` |
343+| Error message sanitization | `docs/security/ERROR_SANITIZATION.md` |
344+| Evals | `docs/frameworks/EVALS.md` |
345+| Compliance / audit | `docs/security/COMPLIANCE.md` |
346+| Webhooks | `docs/frameworks/WEBHOOKS.md` |
347+| Authorization pipeline | `docs/architecture/AUTHZ_GUIDE.md` |
348+| Stealth (TLS / fingerprint) | `docs/security/STEALTH_GUIDE.md` |
349+| Agent protocols (A2A / ACP / Cloud) | `docs/frameworks/AGENT_PROTOCOLS_GUIDE.md` |
350+| MCP server | `docs/frameworks/MCP-SERVER.md` |
351+| A2A server | `docs/frameworks/A2A-SERVER.md` |
352+| API reference + OpenAPI | `docs/reference/API_REFERENCE.md` + `docs/openapi.yaml` |
353+| Provider catalog (auto-generated) | `docs/reference/PROVIDER_REFERENCE.md` |
354+| Release flow | `docs/ops/RELEASE_CHECKLIST.md` |
355+| Embedded services | `docs/frameworks/EMBEDDED-SERVICES.md` |
356+| Quality gates (~48 scripts, allowlist policy) | `docs/architecture/QUALITY_GATES.md` |
438357  
439−#### A2A Internals
358+---
440359  
441−- **`taskManager.ts`**: State machine lifecycle for tasks: `submitted → working →
442−completed | failed | canceled`. Tasks have TTL and are cleaned up automatically.
443−- **JSON-RPC methods**: `message/send` (sync), `message/stream` (SSE), `tasks/get`,
444− `tasks/cancel`. Dispatched via `POST /a2a`.
445−- **Skills**: Registered in a DB-backed registry. Each skill receives task context
446− (messages, metadata) and returns structured results. `quotaManagement.ts` summarizes
447− quota; `smartRouting.ts` recommends routing decisions.
448−- **Agent Card**: `/.well-known/agent.json` exposes capabilities, skills, and metadata
449− for client auto-discovery.
360+## Testing
450361  
451−### ACP Module (`src/lib/acp/`)
362+| What | Command |
363+| ----------------------- | --------------------------------------------------------------------------- |
364+| Unit tests | `npm run test:unit` |
365+| Single file | `node --import tsx/esm --test tests/unit/file.test.ts` |
366+| Vitest (MCP, autoCombo) | `npm run test:vitest` |
367+| E2E (Playwright) | `npm run test:e2e` |
368+| Protocol E2E (MCP+A2A) | `npm run test:protocols:e2e` |
369+| Ecosystem | `npm run test:ecosystem` |
370+| Coverage gate | `npm run test:coverage` (60/60/60/60 — statements/lines/functions/branches) |
371+| Coverage report | `npm run coverage:report` |
452372  
453−Agent Communication Protocol registry and manager.
373+**PR rule**: If you change production code in `src/`, `open-sse/`, `electron/`, or `bin/`, you must include or update tests in the same PR.
454374  
455−### Memory System (`src/lib/memory/`)
375+**Test layer preference**: unit first → integration (multi-module or DB state) → e2e (UI/workflow only). Encode bug reproductions as automated tests before or alongside the fix.
456376  
457−Extraction, injection, retrieval, summarization, and store modules for persistent
458−conversational memory across sessions.
377+**Both test runners must pass**: `npm run test:unit` (Node native — most tests) AND `npm run test:vitest` (MCP server, autoCombo, cache) cover **non-overlapping files**. Both are wired in CI (jobs `test-unit` and `test-vitest`) and must be green before merging. A PR where only one suite passes may silently ship broken MCP tools or routing regressions.
459378  
460−### Skills System (`src/lib/skills/`)
379+**Bug fix / issue triage protocol (Hard Rule #18)**: Every fix for a reported issue must be validated by one of the following — no exceptions:
461380  
462−Extensible skill framework: registry, executor, sandbox, built-in skills,
463−custom skill support, interception, and injection.
381+1. **TDD (preferred)** — write a failing test reproducing the bug → fix it → confirm the test passes. The test becomes the permanent regression guard. Touch only the files the test proves need changing; nothing more.
382+2. **Real-environment test (when TDD is not possible)** — deploy to the production VPS (`root@192.168.0.15`) and run a documented live test. Record the exact command + result in the PR description. Applies to: OAuth upstream flows, Cloudflare/WS upstream behavior, UI-only regressions, hardware-dependent behavior.
383+3. "It worked locally without a test" does not count. A fix without a test or a VPS validation record is not a fix — it is a guess.
464384  
465−#### Skills Internals
385+Why this matters: fixing bug A while opening bug B is worse than not fixing at all. The TDD/VPS gate enforces surgical scope — you touch only what the failing test proves is broken. Examples where this paid off: #3090 (claude-web 403), #3113 (WS HTTP fallback), #3052 (heap-guard auto-calibration).
466386  
467−- **`registry.ts`**: DB-backed skill registration and discovery. Skills have metadata
468− (name, description, version, enabled status) stored in SQLite.
469−- **`executor.ts`**: Execution engine with configurable timeout and retry logic.
470− Receives skill name + input, looks up the skill, runs it in the sandbox.
471−- **`sandbox.ts`**: Isolation layer for custom (user-provided) skills. Limits resource
472− access and execution time.
473−- **Built-in skills**: Ship with OmniRoute (e.g., quota management, routing). Located
474− alongside the registry.
475−- **Interception/Injection**: Skills can intercept requests in the pipeline (pre/post
476− processing) or inject context into prompts.
387+**Copilot coverage policy**: When a PR changes production code and coverage is below 60% (statements/lines/functions/branches), do not just report — add or update tests, rerun the coverage gate, then ask for confirmation. Include commands run, changed test files, and final coverage result in the PR report.
477388  
478−### Compliance (`src/lib/compliance/`)
389+---
479390  
480−Policy index for compliance enforcement.
391+## Planning & Research Artifacts (superpowers, deep-research)
481392  
482−### MITM Proxy (`src/mitm/`)
393+`_tasks/` is a **separate, isolated git repository** that is gitignored by the main
394+repo (`.gitignore` → `_tasks/`). It is the canonical home for working artifacts —
395+plans, specs/designs, research, hand-offs — so they stay **versioned in their own
396+repo** instead of polluting the main OmniRoute tree.
483397  
484−MITM proxy capability with certificate management, DNS handling, and target routing.
398+**Hard rule — never write superpowers / planning / research output under `docs/` or
399+the repo root.** The superpowers skills ship with defaults that point at `docs/…`
400+(`writing-plans` → `docs/superpowers/plans/`, `brainstorming` → `docs/superpowers/specs/`).
401+Those defaults are **overridden here**. Whenever you invoke superpowers (or any
402+plan/spec/research generator) in this project, save to `_tasks/` instead, using the
403+same filename convention:
485404  
486−### Middleware (`src/middleware/`)
405+| Artifact (skill) | Default (do NOT use) | Save here instead |
406+| ---------------------------------- | ------------------------- | ------------------------------------------------------------- |
407+| Plans (`writing-plans`) | `docs/superpowers/plans/` | `_tasks/superpowers/plans/YYYY-MM-DD-<feature>.md` |
408+| Specs / design (`brainstorming`) | `docs/superpowers/specs/` | `_tasks/superpowers/specs/YYYY-MM-DD-<topic>-design.md` |
409+| Research (`deep-research`, ad-hoc) | `docs/research/` | `_tasks/research/…` |
410+| Hand-offs (`/handoff`) | — | `_tasks/hands-off/<YYYY-MM-DD>_<branch>_v<versão>_sess-<id>/` |
487411  
488−Request middleware including `promptInjectionGuard.ts`.
412+When a superpowers skill announces a path like "saved to `docs/superpowers/plans/…`",
413+rewrite it to the `_tasks/…` equivalent before writing. Commit those artifacts inside
414+the `_tasks/` repo (`git -C _tasks …`), never in the main repo.
489415  
490−### Guardrails (`src/lib/guardrails/`)
416+## Git Workflow
491417  
492−Hot-reloadable guardrails framework (3 built-in: pii-masker, prompt-injection, vision-bridge). Fail-open. The `pii-masker` guardrail is registered and runs on every request, but its data-mutating logic is **opt-in** and OFF by default — it only redacts when `PII_REDACTION_ENABLED` (request) / `PII_RESPONSE_SANITIZATION` (response + streaming) are enabled (both `defaultValue: "false"`); with them off, payloads pass through untouched. A request can additionally opt OUT of any guardrail via header (`x-omniroute-disabled-guardrails`). Never make PII default-on (Hard Rule #20). See [`docs/security/GUARDRAILS.md`](docs/security/GUARDRAILS.md).
418+```bash
419+# Never commit directly to main
420+git checkout -b feat/your-feature
421+git commit -m "feat: describe your change"
422+git push -u origin feat/your-feature
423+```
493424  
494−### Cloud Agents (`src/lib/cloudAgent/`)
425+**Branch prefixes**: `feat/`, `fix/`, `refactor/`, `docs/`, `test/`, `chore/`
495426  
496−`CloudAgentBase` abstract class + 3 agents (codex-cloud, devin, jules). Tasks persisted in `cloud_agent_tasks`; management auth required. See [`docs/frameworks/CLOUD_AGENT.md`](docs/frameworks/CLOUD_AGENT.md).
427+**Commit format** (Conventional Commits): `feat(db): add circuit breaker` — scopes: `db`, `sse`, `oauth`, `dashboard`, `api`, `cli`, `docker`, `ci`, `mcp`, `a2a`, `memory`, `skills`
497428  
498−### Evals (`src/lib/evals/`)
429+**Husky hooks**:
499430  
500−Generic eval framework: `evalRunner.ts`, `runtime.ts`. Targets: combo / model / suite-default. See [`docs/frameworks/EVALS.md`](docs/frameworks/EVALS.md).
431+- **pre-commit**: lint-staged + `check-docs-sync` + `check:any-budget:t11` + `check:tracked-artifacts`
432+- **pre-push**: intentionally light (PATH/npm sanity only). `any-budget` + `tracked-artifacts`
433+ already run on pre-commit; re-running them on every push was pure double-pay. CI still
434+ enforces both. (Was Fase 6A.12 full pre-push gate; folded into pre-commit in #6716.)
501435  
502−### Webhooks (`src/lib/webhookDispatcher.ts`)
436+### Worktree isolation (MANDATORY for every development task)
503437  
504−HMAC-signed delivery, exponential backoff, auto-disable after 10 failures. 7 event types. See [`docs/frameworks/WEBHOOKS.md`](docs/frameworks/WEBHOOKS.md).
438+Multiple sessions/agents work this repo in parallel. The main checkout is **shared**, so a
439+`git checkout`/branch switch in it silently discards another session's uncommitted work and
440+yanks the branch out from under whatever else is running (incidents: 2026-06-05, 2026-06-13).
505441  
506−### Authorization Pipeline (`src/server/authz/`)
442+**Rule: never develop on the shared main checkout. Every task gets its own git worktree on its
443+own dedicated branch, and you MUST confirm the base branch with the operator before creating it.**
507444  
508−`classify → policies → enforce`. 3 route classes (PUBLIC / CLIENT_API / MANAGEMENT). See [`docs/architecture/AUTHZ_GUIDE.md`](docs/architecture/AUTHZ_GUIDE.md).
445+1. **Ask first — which base branch?** Before creating anything, ask the operator (via
446+ `AskUserQuestion`, unless they already told you) from which branch the new worktree/branch
447+ should be cut. Do NOT assume `main` or "whatever I'm on" — the answer is usually the active
448+ `release/vX.Y.Z`, but it can be another feature/release branch. Get the base explicitly.
449+2. **Create an isolated worktree + branch off that base** (never reuse the main checkout).
450+ **🔴 MANDATORY PATH: every worktree lives under `.claude/worktrees/` — and nowhere else.**
451+ This is the single canonical location (the same dir the native `EnterWorktree` tool uses). It
452+ is gitignored AND in the `tsconfig.json` / `.dockerignore` excludes, so worktrees never leak
453+ into the build scope. **Never** use `.worktrees/`, repo-root, or any other path — a worktree
454+ outside `.claude/worktrees/` (a) escapes the build-scope excludes and poisons `next build` (the
455+ `tsconfig` `include: **/*` globs ~70× the codebase → OOM; incident 2026-06-25) and (b) scatters
456+ worktrees across two dirs.
509457  
510−### Reasoning Replay (`src/lib/db/reasoningCache.ts` + `open-sse/services/reasoningCache.ts`)
458+ ```bash
459+ BASE_BRANCH="release/vX.Y.Z" # ← the branch the operator confirmed in step 1
460+ TASK="feat/your-feature" # feat/ fix/ refactor/ docs/ test/ chore/
461+ git fetch origin "$BASE_BRANCH"
462+ git worktree add ".claude/worktrees/${TASK##*/}" -b "$TASK" "origin/$BASE_BRANCH"
463+ cd ".claude/worktrees/${TASK##*/}"
464+ # Reuse the main checkout's node_modules to skip a per-worktree npm install.
465+ # HARD LINKS (`cp -al`), never a symlink: ~5s for the whole tree and near-zero extra
466+ # disk (the inodes are shared), and unlike a symlink it does not break the dev server.
467+ cp -al "$(git -C <main_checkout> rev-parse --show-toplevel)/node_modules" node_modules
468+ ```
511469  
512−Hybrid in-memory + SQLite cache for `reasoning_content`. Re-injects on multi-turn for strict providers (DeepSeek V4, Kimi K2, Qwen-Thinking, GLM, xiaomi-mimo). See [`docs/routing/REASONING_REPLAY.md`](docs/routing/REASONING_REPLAY.md).
470+ **Never `ln -s` node_modules.** Turbopack rejects a symlink that resolves outside the
471+ project root, so `npm run dev` dies with a FATAL panic (`Symlink [project]/node_modules
472+ is invalid, it points out of the filesystem root`) while typecheck, lint and the test
473+ runners all keep passing — the error names "filesystem root", not the worktree, so it
474+ reads like a Next/build bug and costs real time to trace (incident 2026-07-31, #9043).
513475  
514−### Tunnels (`src/lib/{cloudflaredTunnel,ngrokTunnel}.ts` + `src/app/api/tunnels/`)
476+ In Claude Code prefer the native `EnterWorktree` tool (it already creates worktrees under
477+ `.claude/worktrees/`): create the worktree with the command above, then call `EnterWorktree`
478+ with its `path`.
515479  
516−Cloudflare Quick/Named, ngrok, Tailscale Funnel. See [`docs/ops/TUNNELS_GUIDE.md`](docs/ops/TUNNELS_GUIDE.md).
480+3. **Work, commit, push, open the PR — all from inside the worktree.** Never `git checkout` a
481+ different branch inside a worktree another session might share.
482+4. **Tear down only your own** worktree + branch when done, from the main checkout:
483+ `git worktree remove .claude/worktrees/<dir>` then `git branch -D <task>`. Never blanket-delete
484+ `fix/*`/`feat/*` — other sessions keep their own; delete only the branches you created, by name.
485+5. **Never touch another session's worktree, branch, or uncommitted changes.** If `git worktree
486+list` shows worktrees you didn't create, leave them alone. End every session with the main
487+ checkout back on the branch it started on (the active `release/vX.Y.Z`, never `main`).
517488  
518−### Adding a New Provider
489+---
519490  
520−1. Register in `src/shared/constants/providers.ts`
521−2. Add executor in `open-sse/executors/` (if custom logic needed)
522−3. Add translator in `open-sse/translator/` (if non-OpenAI format)
523−4. Add OAuth config in `src/lib/oauth/constants/oauth.ts` (if OAuth-based)
524−5. Add models in `open-sse/config/providerRegistry.ts`
491+## Environment
525492  
493+- **Runtime**: Node.js ≥22.0.0 <23 || ≥24.0.0 <27, ES Modules. This is the **only supported** runtime for the published `omniroute` CLI, the server, and the test suites (`node:test` + vitest) — `engines.node` is authoritative and end users never need Bun. A **best-effort `bun:sqlite` compatibility path** exists so a global Bun install (`bun install -g omniroute`) can start without `better-sqlite3` (driver adapter + Bun-aware process spawning); it is **not** a supported runtime — no support guarantees — and every Bun-specific runtime change MUST preserve the Node driver/fallback chain and ship a Bun test (`test:bun:db`) or an explicit reason why the path is Node-only.
494+- **Bun (build/dev script runner + compatibility smoke only)**: Bun `1.3.14` is pinned as an **exact devDependency** (provisioned through the existing `npm ci` via the lockfile's `@oven/bun-*` platform binaries — no `setup-bun`/ad-hoc install). It is used **only** to execute a small, allow-listed set of TypeScript **gate/generator scripts** (replacing `node --import tsx` for startup speed): the CI checks `check:provider-consistency`, `check:compression-budget`, `check:known-symbols`, and the non-CI `gen:provider-reference`, `bench:compression` — plus the focused `test:bun:db` compatibility smoke suite for the best-effort `bun:sqlite` path. **Do NOT** widen Bun to `npm install`, the build (`build:cli*`), `check:pack-artifact`, the supported published runtime, or the main test runners — those stay on Node. Any new Bun-invoking gate/generator script must be validated byte-identical against its `node --import tsx` output first. After pulling the lockfile change, run `npm install` so `bun` resolves locally (a stale `node_modules` will fail those scripts with `bun: not found`).
495+- **TypeScript**: 6.0+, target ES2022, module esnext, resolution bundler
496+- **Path aliases**: `@/*` → `src/`, `@omniroute/open-sse` → `open-sse/`, `@omniroute/open-sse/*` → `open-sse/*`
497+- **Default port**: 20128 (API + dashboard on same port)
498+- **Data directory**: `DATA_DIR` env var, defaults to `~/.omniroute/`
499+- **Key env vars**: `PORT`, `JWT_SECRET`, `API_KEY_SECRET`, `INITIAL_PASSWORD`, `REQUIRE_API_KEY`, `APP_LOG_LEVEL`
500+- Setup: `cp .env.example .env` then generate `JWT_SECRET` (`openssl rand -base64 48`) and `API_KEY_SECRET` (`openssl rand -hex 32`)
501+ 
526502 ---
527503  
528−## Subdirectory AGENTS.md Files
504+## Quality Gates & Ratchets
529505  
530−- **[`src/lib/db/AGENTS.md`](src/lib/db/AGENTS.md)** — SQLite persistence, domain modules, migrations
531−- **[`open-sse/services/AGENTS.md`](open-sse/services/AGENTS.md)** — Routing engine, combo resolution, strategy selection
506+OmniRoute has **~48 quality-gate scripts** (`scripts/check/` + `scripts/quality/`) wired
507+across **9 gate-running jobs** in `.github/workflows/ci.yml` (`lint`, `quality-gate`,
508+`quality-extended`, `docs-sync-strict`, `i18n-ui-coverage`, `i18n`, `pr-test-policy`,
509+`test-vitest`, `sonarqube`), plus the `quality.yml` fast-gates job (PR→`release/**`) and
510+3 nightly workflows (`nightly-property`, `nightly-resilience`, `nightly-llm-security`;
511+`nightly-mutation` once merged). Full inventory, per-job breakdown, and operational
512+procedures are in [`docs/architecture/QUALITY_GATES.md`](docs/architecture/QUALITY_GATES.md).
532513  
533−## Reference Documentation (docs/)
514+**Quick reference:**
534515  
535−For any non-trivial change, read the matching deep-dive first:
516+- Gates in jobs `lint` + `docs-sync-strict`: pass/fail policy gates —
517+ fix the violation or add an allowlist entry with a justification comment + tracking issue.
518+- Gates in job `quality-gate`: ratchet — metrics (ESLint warnings, code coverage, duplication,
519+ complexity) must not regress vs `quality-baseline.json`. Update via
520+ `npm run quality:ratchet -- --update` when a metric genuinely improves.
521+- Job `test-vitest` runs `npm run test:vitest` (MCP tools, autoCombo, cache) — blocking.
522+ `test:vitest:ui` is advisory until UI component tests are triaged.
536523  
537−| Area | Doc |
538−| ------------------------------------------ | --------------------------------------------------------------------------------------------------------------- |
539−| Repo navigation | [`docs/architecture/REPOSITORY_MAP.md`](docs/architecture/REPOSITORY_MAP.md) |
540−| Architecture | [`docs/architecture/ARCHITECTURE.md`](docs/architecture/ARCHITECTURE.md) |
541−| Engineering reference | [`docs/architecture/CODEBASE_DOCUMENTATION.md`](docs/architecture/CODEBASE_DOCUMENTATION.md) |
542−| Auto-Combo (12-factor, 18 strategies) | [`docs/routing/AUTO-COMBO.md`](docs/routing/AUTO-COMBO.md) |
543−| Resilience (3 layers) | [`docs/architecture/RESILIENCE_GUIDE.md`](docs/architecture/RESILIENCE_GUIDE.md) |
544−| Skills | [`docs/frameworks/SKILLS.md`](docs/frameworks/SKILLS.md) |
545−| Memory | [`docs/frameworks/MEMORY.md`](docs/frameworks/MEMORY.md) |
546−| Cloud agents | [`docs/frameworks/CLOUD_AGENT.md`](docs/frameworks/CLOUD_AGENT.md) |
547−| Guardrails | [`docs/security/GUARDRAILS.md`](docs/security/GUARDRAILS.md) |
548−| Evals | [`docs/frameworks/EVALS.md`](docs/frameworks/EVALS.md) |
549−| Compliance | [`docs/security/COMPLIANCE.md`](docs/security/COMPLIANCE.md) |
550−| Webhooks | [`docs/frameworks/WEBHOOKS.md`](docs/frameworks/WEBHOOKS.md) |
551−| Authz | [`docs/architecture/AUTHZ_GUIDE.md`](docs/architecture/AUTHZ_GUIDE.md) |
552−| Stealth | [`docs/security/STEALTH_GUIDE.md`](docs/security/STEALTH_GUIDE.md) |
553−| Reasoning replay | [`docs/routing/REASONING_REPLAY.md`](docs/routing/REASONING_REPLAY.md) |
554−| Agent protocols (A2A / ACP / Cloud) | [`docs/frameworks/AGENT_PROTOCOLS_GUIDE.md`](docs/frameworks/AGENT_PROTOCOLS_GUIDE.md) |
555−| MCP server | [`docs/frameworks/MCP-SERVER.md`](docs/frameworks/MCP-SERVER.md) |
556−| A2A server | [`docs/frameworks/A2A-SERVER.md`](docs/frameworks/A2A-SERVER.md) |
557−| API reference | [`docs/reference/API_REFERENCE.md`](docs/reference/API_REFERENCE.md) + [`docs/openapi.yaml`](docs/openapi.yaml) |
558−| Provider catalog (auto-generated) | [`docs/reference/PROVIDER_REFERENCE.md`](docs/reference/PROVIDER_REFERENCE.md) |
559−| Tunnels | [`docs/ops/TUNNELS_GUIDE.md`](docs/ops/TUNNELS_GUIDE.md) |
560−| Electron desktop | [`docs/guides/ELECTRON_GUIDE.md`](docs/guides/ELECTRON_GUIDE.md) |
561−| Release flow | [`docs/ops/RELEASE_CHECKLIST.md`](docs/ops/RELEASE_CHECKLIST.md) |
562−| Quality gates (35 gates, allowlist policy) | [`docs/architecture/QUALITY_GATES.md`](docs/architecture/QUALITY_GATES.md) |
563−| Cluster opt-in profiles (memory, bifrost) | [`docs/architecture/cluster-decisions.md`](docs/architecture/cluster-decisions.md) |
524+**Allowlist policy (short form):** Fix the cause; use the allowlist only for pre-existing
525+violations you cannot fix in the same PR. Add a comment with justification + issue number.
526+Stale allowlist entries (suppressing a violation that no longer exists) will be caught by
527+the stale-enforcement added in Fase 6A.3.
564528  
565529 ---
566530  
567−## Fork / Upstream Workflow
531+## Hard Rules
568532  
569−This repository is a fork of `diegosouzapw/OmniRoute`. Keep fork-only operational
570−changes (for example GHCR image publishing, personal deployment workflows, or local
571−automation) out of upstream contribution PRs.
533+1. Never commit secrets or credentials
534+2. Never add logic to `localDb.ts`
535+3. Never use `eval()` / `new Function()` / implied eval
536+4. Never commit directly to `main`
537+5. Never write raw SQL in routes — use `src/lib/db/` modules
538+6. Never silently swallow errors in SSE streams
539+7. Always validate inputs with Zod schemas
540+8. Always include tests when changing production code
541+9. Coverage must not regress below the baseline frozen in `quality-baseline.json` (ratchet); absolute floor is 60% (statements/lines/functions/branches). Update the baseline via `npm run quality:ratchet -- --update` only when coverage genuinely improves. See `docs/architecture/QUALITY_GATES.md`.
542+10. Never bypass Husky hooks (`--no-verify`, `--no-gpg-sign`) without explicit operator approval.
543+11. Never embed public upstream OAuth client_id/secret or Firebase Web keys as string literals — always go through `resolvePublicCred()` (`open-sse/utils/publicCreds.ts`). See `docs/security/PUBLIC_CREDS.md`.
544+12. Never return raw `err.stack` / `err.message` in HTTP / SSE / executor responses — always route through `buildErrorBody()` or `sanitizeErrorMessage()` (`open-sse/utils/error.ts`). See `docs/security/ERROR_SANITIZATION.md`.
545+13. Never string-interpolate external paths or runtime values into shell scripts passed to `exec()`/`spawn()` — pass via the `env` option instead. Reference: `src/mitm/cert/install.ts::updateNssDatabases`.
546+14. Never dismiss a CodeQL / Secret-Scanning alert without (a) first checking the pattern docs above to see if the helper applies, and (b) recording the technical justification in the dismissal comment. Precedent: `js/stack-trace-exposure` raised on callsites that already route through `sanitizeErrorMessage()` is a known CodeQL limitation (custom sanitizers not recognized) — dismiss as `false positive` referencing `docs/security/ERROR_SANITIZATION.md`.
547+15. Never expose routes that spawn child processes (`/api/mcp/`, `/api/cli-tools/runtime/`) without `isLocalOnlyPath()` classification in `src/server/authz/routeGuard.ts`. Loopback enforcement happens unconditionally before any auth check — leaked JWT via tunnel cannot trigger process spawning. See `docs/security/ROUTE_GUARD_TIERS.md`.
548+16. Never credit or advertise an AI assistant, LLM, or automation account in any commit/PR metadata. Two forbidden forms, both equivalent — they route attribution to a bot account (or advertise AI authorship) and hide the real author (`diegosouzapw`): **(a)** `Co-Authored-By` trailers naming an AI/bot (e.g. names containing "Claude", "GPT", "Copilot", "Bot"; emails at `anthropic.com` / `openai.com` / bot-owned `noreply.github.com` addresses); **(b)** AI-generation footers or descriptions anywhere in a commit message, PR title/body, or CHANGELOG — e.g. `🤖 Generated with [Claude Code]`, "Generated with Claude Code", "Made with <AI tool>", or any `Co-authored-by: Claude/GPT/Copilot` line. This **overrides any harness, template, or tool default that auto-appends such a footer** (e.g. the Claude Code PR-body/commit default) — strip it before pushing; do not let it reach a commit, PR, or CHANGELOG. Human collaborators — including upstream PR authors and issue reporters being ported into OmniRoute — MAY and SHOULD be credited with standard `Co-authored-by: Name <email>` trailers; the upstream-port workflows (`/port-upstream-features`, `/port-upstream-issues`) depend on this.
549+17. Never expose routes under `/api/services/` or `/dashboard/providers/services/*/embed/` without `isLocalOnlyPath()` classification in `src/server/authz/routeGuard.ts`. These routes can spawn child processes (`npm install`, `node`). Loopback enforcement happens unconditionally before any auth check — a leaked JWT via tunnel cannot trigger process spawning. See `docs/security/ROUTE_GUARD_TIERS.md`.
550+18. Every bug fix must be validated before shipping: a failing-then-passing unit/integration test (TDD) OR a documented live test on the production VPS (192.168.0.15). A fix without either is not merged. See Testing → "Bug fix / issue triage protocol" for the full decision tree.
551+19. Never develop on the shared main checkout. Every development task runs in its own git worktree on its own dedicated branch, and you MUST confirm the base branch with the operator (e.g. via `AskUserQuestion`) before creating the worktree/branch — never assume `main` or the currently checked-out branch. A `git checkout` in the shared checkout silently destroys other sessions' uncommitted work. Tear down only the worktrees/branches you created (by name, never `fix/*`/`feat/*` wildcards), leave other sessions' worktrees untouched, and end on the branch you started on (the active `release/vX.Y.Z`, never `main`). See Git Workflow → "Worktree isolation".
552+20. PII redaction/sanitization is **opt-in — never on by default**. OmniRoute proxies for self-hosted/local LLMs where the operator owns the data, so mutating request/response payloads by default would silently corrupt legitimate traffic. The two data-mutating PII feature flags **MUST** keep `defaultValue: "false"` in `src/shared/constants/featureFlagDefinitions.ts`: `PII_REDACTION_ENABLED` (request-side) and `PII_RESPONSE_SANITIZATION` (response + streaming). All three application points — `src/lib/guardrails/piiMasker.ts` (request guardrail), `src/lib/piiSanitizer.ts` (response), `src/lib/streamingPiiTransform.ts` (SSE) — are gated on these flags; with both off the `pii-masker` guardrail still runs but never mutates payloads (data passes through untouched). Flipping either default to `"true"` requires explicit operator approval. The regression guard is `tests/unit/pii-opt-in-default.test.ts` (asserts both definition defaults + behavioral pass-through). Opt-in is per-operator via env or the settings/DB override (`src/lib/db/featureFlags.ts`), never a silent default. See `docs/security/GUARDRAILS.md`.
553+21. **Release-freeze — the FROZEN release branch belongs to the release captain; development does NOT stop (parallel-cycle model, 2026-07-04).** `/generate-release` opens a marker issue labeled `release-freeze` at the start of reconciliation (Phase 0a), **immediately cuts the next cycle's branch `release/vX+1` from the frozen tip (Phase 0a.0b — bump + living release PR + re-home of open PRs)**, and closes the freeze once the release PR squash-merges to `main`. Before merging **any** PR, every campaign workflow (`/review-prs`, `/review-group-prs`, `/merge-prs`, `/triage-fix-bugs`, `/implement-fix-bugs`, `/triage-features`, `/implement-features`, `/green-prs`, `/port-upstream-*`) **MUST** check `gh issue list --repo diegosouzapw/OmniRoute --label release-freeze --state open` — if a freeze is active: **NEVER merge into the frozen `release/vX.Y.Z` named in the freeze title**; instead resolve the ACTIVE development branch (the **highest** `release/v*` by semver — normally `release/vX+1`, announced in a freeze-issue comment) and **retarget the PR there** (`gh pr edit <N> --base release/vX+1`, then VERIFY with `gh pr view <N> --json baseRefName` — the edit fails silently) and merge normally. **HOLD only when the highest release/v\* branch IS the frozen one** (the short window before 0a.0b completes, or a pre-parallel-cycle release) — in that case leave the PR ready and open, tell the operator, and resume when the next branch appears or the freeze lifts. The just-shipped fixes reach `release/vX+1` via the Phase 5 sync-back (`scripts/release/sync-next-cycle.mjs`); do not try to sync mid-release. This is a **coordination signal, not a permission lock**: the release captain and the campaign sessions share the `diegosouzapw` identity, so a GitHub branch-protection lock cannot distinguish them — only this honored marker prevents the mid-release commit races that forced full CHANGELOG re-reconciliation in v3.8.40/v3.8.41 (a parallel campaign advanced `release/vX.Y.Z` by 34 commits mid-run). The release captain's own reconciliation/cycle-open pushes are exempt — they _are_ the release. Fixes that must land during a freeze (a homologation finding) follow the post-merge read-only rule: land on `main` first via `fix/release-vX.Y.Z-*`. **⛔ ONLY `/generate-release` may raise a release-freeze, and ONLY at its Phase 0a (start of generating a new version) — lifted at Phase 12c after the squash-merge to `main`.** No campaign, session, or agent may open a `release-freeze` marker at any other time — a freeze is **never** a mid-development coordination tool. If a session ever believes a freeze is genuinely, unavoidably necessary outside the `/generate-release` flow, it **MUST first ask the operator (`diegosouzapw`) in chat, explicitly alert "estou criando um freeze" and get an explicit yes** — never open, extend, or re-open a `release-freeze` autonomously. Conversely, do **not** close/lift an active `/generate-release` freeze to unblock campaign merges: it protects the captain's single clean CI run and auto-lifts at Phase 12c — closing it early re-triggers the exact commit race it prevents. Verify a freeze is legitimate before acting on it: an open `release-freeze` whose title/body references an **OPEN** release PR (`gh pr view <N> --json state`) is the authorized captain freeze — hold, don't touch.
554+22. **Cross-session safety — this repo is worked by MANY parallel sessions/agents at once; never step on another's in-flight work.** Two absolute bans, both recurring incidents (this rule exists because they keep happening):
555+ - **(a) Never `git stash` / `git stash pop` — ANYWHERE in this repo, including inside an isolated worktree, and including inside any subagent you dispatch.** `git stash` operates on the **shared repository object store**, not the per-worktree working tree — so a stash pushed or popped in one session can silently clobber or resurrect another parallel session's uncommitted changes. This is not hypothetical: 2026-07-02 a `#5923` quotaCache change leaked into the unrelated `#2296` worktree via a global `stash pop`, and the same class reincided through a **subagent**. To compare working changes against a base ref **without** stashing, use `git show <ref>:<path>` or `git diff <ref> -- <path>`; to confirm a typecheck/lint error is pre-existing on the base, inspect the base ref directly (`git show origin/release/vX.Y.Z:<path>`) — never stash your tree away to "get it clean". **Put this ban verbatim in the prompt of every subagent that touches git** (agents don't inherit this file's context — the recurrence was a subagent).
556+ - **(b) Never merge, push, rebase, or force-push a PR / branch / worktree that another session is actively working.** An open PR whose head is a live fix worktree in `.claude/worktrees/` you did **not** create (e.g. `fix-5852`/`fix-5923` carrying fresh commits, even when they share your `diegosouzapw` identity), or any branch another session owns, is **off-limits — HOLD**, and let the owning session merge it. **Before** merging or pushing to any PR you did not create _this_ session, run `git worktree list` to check for a matching in-flight worktree and re-check `gh pr view <N> --json state,headRefOid`. Only the owning session merges its own in-flight PR; mid-flight merges race the owner and re-trigger the exact commit/CHANGELOG races Rule #19 and Rule #21 guard against. (Reinforces Rule #19.)
572557  
573−When preparing a PR for upstream, always start the work branch from the upstream
574−**default branch** — the active `release/vX.Y.Z` line (today `release/v3.8.49`).
575−Never branch from `main`: `main` only receives release squash-merges, so a branch
576−cut there is weeks behind and produces conflict-heavy PRs
577−(see `CONTRIBUTING.md` and `docs/ops/BRANCHING_MODEL.md`):
558+---
578559  
579−```bash
580−git fetch upstream
581−# the default branch is the active release line, e.g. release/v3.8.49
582−git switch -c <branch-name> upstream/release/vX.Y.Z
583−```
560+## PII & Stream Sanitization Learnings
584561  
585−Only cherry-pick or reapply the changes intended for the upstream PR.
562+### 1. Regex Security (ReDoS)
586563  
587−---
564+All regex patterns matching variable-length strings (e.g. IPv6 address, credit cards) must use strictly bounded, non-overlapping sequences (e.g., limit occurrences with bounded ranges `{1,7}`) to prevent catastrophic backtracking when processing untrusted inputs.
588565  
589−## Review Focus
566+### 2. SSE Snapshot Handling
590567  
591−- **DB ops** go through `src/lib/db/` modules, never raw SQL in routes
592−- **Provider requests** flow through `open-sse/handlers/`
593−- **MCP/A2A pages** are tabs inside `/dashboard/endpoint`, not standalone routes
594−- **No memory leaks** in SSE streams (abort signals, cleanup)
595−- **Rate limit headers** must be parsed correctly
596−- All API inputs validated with **Zod schemas**
597−- **Provider constants** validated at module load via Zod (`src/shared/validation/providerSchema.ts`)
598−- **Pricing data** syncs from LiteLLM via `src/lib/pricingSync.ts`
599−- **Memory/Skills** are cross-cutting: affect MCP tools, request pipeline, and A2A skills
600−- **⛔ NEVER close a contributor's PR** after using their code — always merge via GitHub so they get credit. See `.agents/workflows/review-prs.md` for full policy.
568+When parsing streaming LLM responses (e.g. Responses API), check if a chunk represents a final snapshot (`done` or `completed` events). Snapshot text must be sanitized directly as a standalone string (bypassing rolling delta buffers) to prevent text duplication at the end of the stream.
569+ 
570+### 3. Database Handles in Tests
571+ 
572+Ensure that any unit tests that trigger database migrations or establish SQLite connections call `resetDbInstance()` and properly clean up/close all DB handles in a `test.after(...)` hook. Failure to release database connection handles will cause Node's native test runner to hang indefinitely.
601573  
RuleStack

Built by

Kynth Studio

Directory

Configs
Stacks
Compare formats
Diff two configs
Best AGENTS.md examples

Formats

AGENTS.md
CLAUDE.md
Cursor rules
Copilot instructions

Reference

Read API
Corpus health
Privacy Policy
Terms

RuleStack

RuleStack

Built by

Kynth Studio

Directory

Configs
Stacks
Compare formats
Diff two configs
Best AGENTS.md examples

Formats

AGENTS.md
CLAUDE.md
Cursor rules
Copilot instructions

Reference

Read API
Corpus health
Privacy Policy
Terms

RuleStack

RuleStack

Built by

Kynth Studio

Directory

Configs
Stacks
Compare formats
Diff two configs
Best AGENTS.md examples

Formats

AGENTS.md
CLAUDE.md
Cursor rules
Copilot instructions

Reference

Read API
Corpus health
Privacy Policy
Terms

RuleStack