mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-02 21:32:10 +03:00
* chore(release): open v3.8.28 development cycle * fix(ws): warm SSE auth import on LiveWS startup; relocate boot test to integration (#4063) The live dashboard WebSocket sidecar lazily import()-ed the SSE auth module inside the connection handler, only on the API-key path. That cold import pulls in hundreds of transitive modules and takes ~7s under tsx, blocking the single-threaded event loop. The first API-key WebSocket connection therefore stalled the loop long enough that any connection arriving in that window — e.g. a same-origin cookie client — could not complete its handshake and timed out. This was deterministic, not an "env flake": the boot test fires an API-key connection immediately followed by a cookie connection, so the cookie connection always raced the cold import and timed out (reproduced 3/3 locally and red on every CI run; proven via instrumented probes — reversing the order or warming the module first makes both connections open in ~20ms). Fix: - Memoize the auth-module import and warm it once at startup (before listen), so connection handling never pays the cold-import cost. Real improvement: the first API-key client no longer stalls the event loop for concurrent clients. - Relocate the boot test from tests/unit/cli to tests/integration. It spawns a real subprocess + WS server + SQLite (~9-11s); under the unit suite's --test-concurrency=20 it contended for CPU and destabilized the shard. The serial integration runner is its correct home; it still guards #4004's cookie-parse fix on every PR via the integration CI job. - Bump the test's startup/overall timeouts to absorb the eager auth warm. Makes `npm run test:unit` deterministically green (the only remaining unit red). Validated: relocated test 3/3 green via the integration runner (was 3/3 red); typecheck:core + eslint clean; confirmed it no longer matches the test:unit glob and does match tests/integration/*.test.ts. * fix(ws): start LiveWS sidecar with cwd at package root (#4055) (#4064) * chore(deps): bump ossf/scorecard-action from 2.4.0 to 2.4.3 (#4045) Integrado em release/v3.8.28. Patch de SHA do ossf/scorecard-action (2.4.0→2.4.3), mantém SHA-pin. Reds de CI são exclusivamente os shards flaky pré-existentes branch-wide (Unit 7/8, Integration, Coverage 7/8, Node 1/2) — não relacionados ao bump (PR deps-only). * deps: bump electron from 42.4.0 to 42.4.1 in /electron (#4049) Integrado em release/v3.8.28. Patch do electron (42.4.0→42.4.1). Reds de CI: shards flaky pré-existentes + PR Test Policy = falso-positivo (mudança deps-only sob electron/ não comporta teste de código) + Node 26(2/2) sem step (flake/infra). Precedente #3913/#3914 (electron dependabot mergeado nessas condições). * fix(auto): resolve built-in auto catalog combos (#4058) Integrado em release/v3.8.28. Resolve os IDs de catálogo `auto/*` built-in (combos virtuais) — corrige o 400 "No auto combos configured" em auto/best-coding etc. Ajuste de review: os mapas AUTO_TEMPLATE_VARIANTS/VALID_AUTO_VARIANTS duplicados em chat.ts e chatHelpers.ts foram extraídos para open-sse/services/autoCombo/builtinCatalog.ts (DRY), devolvendo chatHelpers.ts <800 LOC; baseline de chat.ts rebaselinado 1432→1458 (lógica nova). Fast QG + semgrep + dast verdes; 22/22 testes. * chore(docs): update Discord invite link to a non-expiring one (#4067) * chore(deps): freeze @huggingface/transformers in dependabot (hard-pin) (#4066) Integrado em release/v3.8.28. Congela @huggingface/transformers no dependabot (pin exato 3.5.2, load-bearing p/ LLMLingua + memory embeddings, VPS-validado #4014). Fast QG + semgrep + dast verdes. * ci(quality): flip TIA impacted-unit-tests gate from advisory to blocking (#4069) The pre-existing release unit test-debt that kept the TIA "Impacted unit tests" step advisory has been cleared: - #4030 restored 16 lossless Zod/registry reds (from the oyi77 modularize refactors). - #4063 fixed the last red — the LiveWS boot test — which was a real deterministic event-loop stall in the WS sidecar (cold ~7s lazy auth import racing a second connection), not an env flake; fixed (warm the import at startup) and relocated to the integration suite. A full workflow_dispatch ci.yml run on release/v3.8.28 then showed all 8 Unit Tests shards green. The remaining Integration Tests / Quality Ratchet reds are pre-existing and unrelated (combo/resilience env-flakes; eslint/i18n baseline drift). Removing continue-on-error makes PR->release block on unit-test regressions in the TIA-selected impacted set (fail-safe still runs the full unit suite on hub/unmapped changes). typecheck:core was already blocking. Closes the fast-gates "no tests on PR->release" hole (Quality Gate v2 / Fase 9, P2). * docs(compression): document LLMLingua optional deps + on-demand install (#4061) Integrado em release/v3.8.28. Docs LLMLingua optional deps + on-demand install (F3.1). * feat(dashboard): Combo Studio connection-cooldown badge (U1b Slice 2) (#4068) Integrado em release/v3.8.28. Combo Studio connection-cooldown badge (U1b Slice 2 / F5.1). * feat(compression): record Context Editing telemetry (engine: context-editing) (#4062) Integrado em release/v3.8.28. Context Editing telemetry (F4.1). * feat(sse): Context Editing relay coverage + 400-fallback (#4065) Integrado em release/v3.8.28. Context Editing relay coverage (cc-*) + 400-fallback (F4.2/F4.3). Conflito de file-size-baseline.json (vs #4062) resolvido por união (ambas justificativas + base.ts 1292 + chatCore.ts 5898). Validado local no tree mergeado: typecheck:core ✓, eslint ✓, check:file-size ✓, 4/4 testes ✓; semgrep + semgrep-cloud verdes. Fast QG enfileirado (saturação de runner) — mergeado nos gates de política verificados (precedente #4034/#4020). * feat(providers): add OrcaRouter (OpenAI-compatible routing gateway) (#4070) Integrado em release/v3.8.28. Adiciona o provider OrcaRouter (OpenAI-compatible, API-key, DefaultExecutor). Ajuste de review: rebaseline de file-size de providers.ts 3147→3159 (+12 da entrada OrcaRouter). Validado local no tree sincronizado: provider-consistency ✓, docs-counts STRICT 227 ✓, typecheck:core ✓, teste 3/3 ✓, eslint ✓; semgrep + semgrep-cloud verdes. Fast QG/dast enfileirados (saturação de runner) — merge nos gates de política verificados (precedente #4034/#4065). * test(infra): isolate DATA_DIR per test process; raise Stryker concurrency 1→4 (#4078) * test(infra): isolate DATA_DIR per test process; raise Stryker concurrency 1→4 Every test process resolved DATA_DIR to the same default (~/.omniroute) when the env var was unset (src/lib/dataPaths.ts::resolveDataDir), so concurrent test files opened the SAME on-disk storage.sqlite. node:test spawns a process per file and Stryker spawns one per sandbox, so this shared file caused cross-file state races: - SQLite lock contention that hung `npm run test:unit` under high --test-concurrency (the ~95-min local hang), and - the non-deterministic baseline that forced stryker.conf.json to concurrency: 1, which in turn could not finish the ~15k-mutant run inside the nightly timeout (the cancelled 2026-06-16/17 nightly-mutation runs) — blocking Quality Gate v2 / Fase 9 Onda 2. open-sse/utils/setupPolyfill.ts could NOT host the fix: it is imported by production (bin/omniroute.mjs, proxyFetch.ts, proxyDispatcher.ts), where redirecting DATA_DIR would point the live SQLite DB at a throwaway temp dir. So this adds a TEST-ONLY tests/_setup/isolateDataDir.ts that gives each process its own temp DATA_DIR when none is set (tests that set DATA_DIR explicitly still win), wired via --import into the test, mutation and CI invocations. Verified: - Stryker dry-run A/B at concurrency=4: FAILS without the isolation import (account-fallback-service tap exit 9, a cross-file race) and PASSES with it. - Full `npm run test:unit` green with isolation (0 fail; a one-off chatcore-translation-paths timeout flake did not reproduce and passes 3/3 isolated) and noticeably faster — the DB lock contention is gone. - New tests/unit/isolate-datadir.test.ts guards the contract (unique temp DATA_DIR when unset; explicit DATA_DIR respected). Wired the --import into: package.json (13 test scripts), stryker.conf.json (tap.nodeArgs + concurrency 1→4), .github/workflows/quality.yml (TIA step), ci.yml (the 5 unit/coverage/integration commands), and bumped nightly-mutation.yml timeout 120→180 for the first cold run before the incremental cache is seeded. * ci(quality): run the TIA gate at CI concurrency (4) to stop oversubscription flakes The TIA "Impacted unit tests" step (made blocking in #4069) ran its fail-safe via `npm run test:unit` — concurrency=20, tuned for multi-core dev machines. On a 4-vCPU CI runner that is 5x oversubscribed, so timing-sensitive tests flake under the load (e.g. `db-backup-extended` "The database connection is not open", `chatcore-translation-paths` upstream-timeout). That intermittently fails a blocking gate on legitimate PRs — exactly what surfaced on the DATA_DIR-isolation PR, whose package.json/workflow changes trip the __RUN_ALL__ fail-safe. Run both the impacted set and the fail-safe at --test-concurrency=4, matching the stable ci.yml unit job. Adds a `test:unit:ci` script (test:unit at concurrency=4). The DATA_DIR isolation in this PR keeps the parallel run race-free, so the only change here is matching the runner's core count. Verified locally: db-backup-extended passes 8/8 in isolation (5 with isolation, 3 without). * docs(quality-gates): reconcile gate inventory with ci.yml + add ROI rationalization backlog (#4095) The "authoritative" gate inventory in QUALITY_GATES.md had drifted from ci.yml: it omitted 9 wired gates — `audit:deps`, `check:tracked-artifacts`, `check:lockfile`, `check:licenses` (lint job), `check:dead-code`, `check:cognitive-complexity`, `check:type-coverage`, `check:codeql-ratchet` (quality-gate job), and `check:pr-evidence` (pr-test-policy job). You can't rationalize an inventory you can't trust, so this reconciles it first. Adds those 9 rows to their job tables and a "Rationalization Backlog (ROI review)" section capturing the Fase 9 Onda 3 findings: mechanical merge/dedup candidates (CVE scanners audit:deps↔osv, the two complexity ESLint passes, cycles↔circular-deps, the two /api anti-hallucination gates, the doubly-run check:docs-sync, check:node-runtime ×11) and the operator-only flip/drop decisions (typecheck:noimplicit vs the type-coverage ratchet, test:vitest:ui parked fails, check:secrets frozen FPs, openapi-security-tiers, pr-evidence, the orphaned semgrep baseline). Also flags the undocumented advisory docs-lint job and the standalone scanner workflows. Docs-only — no gate behavior changes. The merges (CI changes) and flips (policy) are deferred to operator-scoped follow-ups; this PR only makes the map accurate. * test(dashboard): smoke e2e for the Combo Live Studio page (#4075) Integrated into release/v3.8.28 * fix(sse): friendly 413 message for ChatGPT web payload-too-large (#4080) Integrated into release/v3.8.28 * feat(sse): port Claude Code quota-probe bypass + command meta-request helpers (#4083) Integrated into release/v3.8.28 * feat(api): exact offline token counting for count_tokens fallback via tiktoken (#4087) Integrated into release/v3.8.28 * feat(compression): RTK learn/discover (sample source + API + UI) (#4088) Integrated into release/v3.8.28 * feat(dashboard): 2026-06-17 free-tier refresh — honest catalog, uncapped + boost tiers, Layout A budget table (#4089) Integrated into release/v3.8.28 * feat(mitm): capture-pipeline self-test route (Gap 12) (#4093) Integrated into release/v3.8.28 * fix(mitm): crash-safe system-state teardown + socket timeouts (ProxyBridge-inspired hardening) (#4084) Integrated into release/v3.8.28 (Fast QG TIA red = 3 pre-existing timing flakes verified passing locally 82/82; PR own tests green) * feat(mitm): attribute intercepted requests to originating process (Gap 1) (#4085) Integrated into release/v3.8.28 (Fast QG TIA red = 3 pre-existing timing flakes verified passing locally 82/82; PR own tests green) * fix(sse): route image requests only to confirmed-vision combo targets (#4071) Integrated into release/v3.8.28 * fix(security): injection guard respects INJECTION_GUARD_MODE DB feature flag (#4077) Integrated into release/v3.8.28 * fix(ws): proxy LAN /live-ws upgrades and add unset JWT_SECRET warning (#4079) Integrated into release/v3.8.28 * fix(dev): force webpack in custom dev server (Turbopack 16.2.x panics) (#4092) Integrated into release/v3.8.28 * ci(quality): dedup the doubly-run check:docs-sync + record validated ROI backlog (#4099) Onda 3 (gate ROI-review) Phase 2. Two parts, both low-risk: 1. Remove the standalone `check:docs-sync` from the `lint` job — it already runs in the `docs-sync-strict` job (via `check:docs-all`) and the husky pre-commit hook, so the `lint`-job copy was a pure duplicate. No coverage lost. 2. Update the Rationalization Backlog in QUALITY_GATES.md with trust-but-verify findings: several "obvious" merges/flips from the ROI review turned out to hide debt and are NOT clean drop-ins — - CVE merge (audit:deps→osv): different semantics (hard high/critical vs regression-ratchet) — keep both. - cycles→circular-deps: dpdm reports 91 cycles (can't promote to blocking) and is broader-scope than the green curated check:cycles — keep both. - openapi-security-tiers flip: blocked by traffic-inspector routes missing the x-loopback-only annotation. - complexity + /api merges: valid but real config/script surgery — deferred. - node-runtime ×11: ~10s savings vs a cheap guard — low ROI, skip. The remaining flips (typecheck:noimplicit, test:vitest:ui, check:secrets, pr-evidence, semgrep) are operator policy decisions, left for the owner. * chore(deps): bump actions/github-script from 7 to 9 (#4046) Integrated into release/v3.8.28 (dependabot GH-Action bump; SHA-pin preserved) * chore(deps): bump actions/setup-node from 4 to 6 (#4048) Integrated into release/v3.8.28 (dependabot GH-Action bump; SHA-pin preserved) * chore(deps): bump actions/upload-artifact from 4 to 7 (#4044) Integrated into release/v3.8.28 (dependabot GH-Action bump; SHA-pin preserved) * chore(deps): bump actions/cache from 4.3.0 to 5.0.5 (#4047) Integrated into release/v3.8.28 (dependabot GH-Action bump; SHA-pin preserved) * deps: bump the development group with 10 updates (#4051) Integrated into release/v3.8.28 (dependabot dev group; cyclonedx 4->5 verified compatible with the SBOM invocation --ignore-npm-errors/--output-format JSON/--output-file) * fix(dashboard): event-driven fail-open auto-refresh for embedded log views (#4054) (#4103) The Request Logger gated each auto-refresh tick on a static document.visibilityState === "visible" read. Hosts that report a permanent non-"visible" state without ever firing a visibilitychange event (Docker dashboard wrappers, embedded/proxied webviews) froze auto-refresh entirely — only the manual Refresh button worked, a regression from 3.8.24's unconditional polling. The pause is now event-driven and fail-open: visibleRef starts true and is only flipped to false on a real visibilitychange → hidden transition, so a host that never signals a genuine background transition keeps polling, while normal browser tabs still pause when actually backgrounded. Regression test reproduces the misreporting-host case (RED) and the perf guard is re-encoded under the event-driven semantics. * fix(docker): raise build-stage Node heap to stop production-build OOM (#4076) (#4104) The Docker builder stage ran `npm run build` with V8's default heap ceiling (~2 GB). After #4052 forced the heavier webpack engine (Turbopack panics on this Next.js version), the production optimization pass exceeded that ceiling and the build died with "FATAL ERROR: ... JavaScript heap out of memory" at [builder] npm run build. The builder stage now sets NODE_OPTIONS=--max-old-space-size (default 4096 MB, overridable via --build-arg OMNIROUTE_BUILD_MEMORY_MB) before the build; the value propagates to the spawned next build (resolveNextBuildEnv spreads process.env). Build-only — the runtime heap on the runner stage is unchanged, and CI/local builds (which invoke npm run build directly) are unaffected. Regression guard: tests/unit/dockerfile-build-heap-4076.test.ts asserts the builder stage sets the heap ceiling, before npm run build, at >= 4096 MB. * feat(agent-bridge): portable JSON import/export of config (Gap 4) (#4094) Integrated into release/v3.8.28 * feat(cli): add 'omniroute launch' zero-config Claude Code launcher (#4097) Integrated into release/v3.8.28 (Fast QG TIA red = pre-existing env-doc-contract drift [MITM_IDLE_TIMEOUT_MS/TURBOPACK from #4084/#4092] + opencode-plugin-dist env flake; #4097 own test 3/3 green) * feat(mitm): loop-guard self-check + verbosity control in server.cjs (Gaps 14+15) (#4101) Integrated into release/v3.8.28 (rebased onto release — dropped the already-squash-merged #4084 commits; only the Gaps 14+15 loop-guard/verbosity delta remains) * feat(sse): generic 400 field-downgrade retry + Groq field stripping (#4096) Integrated into release/v3.8.28 * feat(providers): add Wafer AI (Anthropic-compatible, Bearer auth) (#4098) Integrated into release/v3.8.28 * chore(docs) * fix(responses): clear /v1/responses keepalive timer on cancel/abort (timer + CPU leak) (#4105) Integrated into release/v3.8.28 (r7). * perf(gemini): cache reasoning close-tag regex instead of recompiling per token (#4106) Integrated into release/v3.8.28 (r7). * fix(usage): reap orphaned pending-request details (unbounded memory leak) (#4107) Integrated into release/v3.8.28 (r7). * perf(stream): use structuredClone instead of JSON round-trip for per-chunk reasoning split (#4108) Integrated into release/v3.8.28 (r7). * fix(dashboard): restore Update Available banner with npm-binary-free version fallback (#4100) (#4112) getLatestNpmVersion() derived the latest version only from the npm CLI binary and returned null on any error, so Docker/desktop/locked-down installs without npm on PATH silently hid the home banner even when an update existed. Add resolveLatestVersion() (npm CLI -> registry HTTP fallback -> logged warning) and harden version parsing for v-prefix/pre-release strings. Extracted into testable src/lib/system/versionCheck.ts with TDD coverage. * fix(auth): prune expired entries from login brute-force guard map (unbounded growth) (#4111) Integrated into release/v3.8.28 (r8) * fix(logger): hard-cap the error-dedup map to bound memory under unique-message bursts (#4113) Integrated into release/v3.8.28 (r8) * fix(circuit-breaker): enforce MAX_REGISTRY_SIZE (declared but never applied) (#4114) Integrated into release/v3.8.28 (r8) * perf(obfuscation): cache per-word regexes instead of recompiling every request (#4109) Integrated into release/v3.8.28 (r8) * perf(registry): precompute model->provider index in parseModelFromRegistry (#4110) Integrated into release/v3.8.28 (r8) * fix(timers): unref background interval timers so they don't block clean shutdown (#4117) Integrated into release/v3.8.28 (r8) * fix(webhook): clear abort timer in finally to avoid dangling timers on fetch error (#4115) Integrated into release/v3.8.28 (r8) * fix(combo): detach per-target listener from shared hedge abort signal (#4116) Integrated into release/v3.8.28 (r8) * chore(release): finalize v3.8.28 CHANGELOG + reconcile env-doc contract - Build the complete [3.8.28] CHANGELOG section (55 bullets) covering every commit since v3.8.27, grouped by type with PR back-references and human contributor attribution (artickc's memory-leak/perf cluster, OrcaRouter, Wafer AI, MITM gaps, etc.); move the OrcaRouter bullet out of [Unreleased]. - Inject the EN [3.8.28] section into all 41 i18n CHANGELOG mirrors (parity). - Reconcile the env/docs contract: document MITM_IDLE_TIMEOUT_MS + MITM_VERBOSE in .env.example and ENVIRONMENT.md; allowlist the framework-internal TURBOPACK and the Claude Code ANTHROPIC_AUTH_TOKEN in check-env-doc-sync. - Fix 3 broken relative links in docs/providers/AGENTROUTER.md (regressed when the file was relocated this cycle) so docs-sync-strict passes. * fix(quality): treat test→test renames as relocations, not deletions The anti-test-masking gate's subcheck-1 collected deleted AND renamed test files via `--diff-filter=DR --name-only` and flagged every one as "deleted — human review required", contradicting its own documented contract ("DELETADOS ou renomeados-e-NÃO-substituídos"): a rename test→test IS a substitution (the test moved, coverage preserved). This false-positived on #4063's legitimate relocation of live-ws-startup.test.ts (unit/cli → integration, asserts 2→2) and would block every PR that relocates a test — surfacing only at release-day because the Fast QG (PR→release) doesn't run test-masking. The gate now parses `--name-status -M`: true deletions and test→non-test renames still flag; a test→test rename is run through the assert-reduction check across the move, so a clean relocation passes while gutting-via-rename (dropped asserts / new tautologies / skips) still fires. Adds partitionDeletedRenamed + 6 regression tests. --------- Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Demiurge The Single <megamen932@gmail.com> Co-authored-by: jinhaosong-source <jinhao.song@myflashcloud.com> Co-authored-by: diego-anselmo <contato@diegoanselmo.com.br> Co-authored-by: Felipe Almeman <4226997+zhiru@users.noreply.github.com> Co-authored-by: Rahul sharma <sharmaR0810@gmail.com> Co-authored-by: Chirag Singhal <76880977+chirag127@users.noreply.github.com> Co-authored-by: NOXX - Commiter <artur1992123@mail.ru>
596 lines
36 KiB
Markdown
596 lines
36 KiB
Markdown
# omniroute — Agent Guidelines
|
|
|
|
## Project
|
|
|
|
Unified AI proxy/router — route any LLM through one endpoint. Multi-provider support
|
|
with **227 provider entries** (OpenAI, Anthropic, Gemini, DeepSeek, Groq, xAI, Mistral, Fireworks,
|
|
Cohere, NVIDIA, Cerebras, Pollinations, Puter, Cloudflare AI, HuggingFace, DeepInfra,
|
|
SambaNova, Meta Llama API, Moonshot AI, AI21 Labs, Databricks, Snowflake, and many more)
|
|
with **MCP Server** (87 tools), **A2A v0.3 Protocol**, and **Electron desktop app**.
|
|
|
|
> **Live counts (v3.8.24)**: providers 227 · MCP tools 87 · MCP scopes 30 · A2A skills 6 ·
|
|
> open-sse services 115 · routing strategies 15 · auto-combo scoring factors 9 ·
|
|
> DB modules 83 · DB migrations 97 · base tables 17 · search providers 11 ·
|
|
> i18n locales 42. **Refresh with `npm run check:docs-all`.**
|
|
|
|
## Doc Accuracy Discipline (read before writing any doc)
|
|
|
|
> **If `grep -rn "name" src/ open-sse/ bin/` returns nothing, the name does not exist. Do not document it.**
|
|
|
|
The recurring failure mode in AI-generated docs is _plausible-but-unverified specifics_.
|
|
Every claim in a `.md` file under `docs/` should be verifiable against the source.
|
|
|
|
**Rules (enforced by `npm run check:fabricated-docs`):**
|
|
|
|
1. **Never state an API name, endpoint, path, CLI command, or env var without grepping for it first.**
|
|
```bash
|
|
grep -rn "theName" src/ open-sse/ bin/
|
|
# 0 hits → do not document
|
|
```
|
|
2. **Never write a line count, file size, migration count, provider count, or strategy count from memory.**
|
|
```bash
|
|
wc -l <file> # exact line count
|
|
ls <dir>/*.ts | wc -l # file count
|
|
```
|
|
3. **Every code example should be copy-pasted from real usage or actually run** — not synthesized.
|
|
Link to a real call site (`path:line`) instead of inventing a signature.
|
|
4. **Prefer citing real source (`file.ts:line`) over paraphrasing behavior** — verifiable and self-correcting.
|
|
5. **A shorter doc that is 100% accurate beats a comprehensive one with fabrications.**
|
|
Wrong docs cost more than missing docs, because people trust and act on them.
|
|
|
|
The script `scripts/check/check-fabricated-docs.mjs` extracts every route path, env var, hook
|
|
name, function name, and file reference from `docs/**/*.md` and verifies each one against the
|
|
codebase. Run it locally before pushing docs; it runs in CI via `npm run check:docs-all`.
|
|
|
|
## Stack
|
|
|
|
- **Runtime**: Next.js 16 (App Router), Node.js `>=22.0.0 <23 || >=24.0.0 <27`, ES Modules (`"type": "module"`)
|
|
- **Language**: TypeScript 6.0 (`src/`) + JavaScript (`open-sse/`, `electron/`)
|
|
- **Database**: better-sqlite3 (SQLite) — `DATA_DIR` configurable, default `~/.omniroute/`
|
|
- **Streaming**: SSE via `open-sse` internal workspace package
|
|
- **Styling**: Tailwind CSS v4
|
|
- **i18n**: next-intl with 42 locales (`src/i18n/messages/`) — refresh with `ls src/i18n/messages/*.json | wc -l`
|
|
- **Desktop**: Electron (cross-platform: Windows, macOS, Linux)
|
|
- **Schemas**: Zod v4 for all API / MCP input validation
|
|
|
|
---
|
|
|
|
## Build, Lint, and Test Commands
|
|
|
|
| Command | Description |
|
|
| ----------------------------------- | ------------------------------------------------------------------ |
|
|
| `npm run dev` | Start Next.js dev server |
|
|
| `npm run build` | Production build: `next build` → `.build/next/` + assemble `dist/` |
|
|
| `npm run build:release` | Clean rebuild + HEAD sentinel (`dist/BUILD_SHA`) — use for deploy |
|
|
| `npm run start` | Run production build |
|
|
| `npm run build:cli` | Build CLI package |
|
|
| `npm run lint` | ESLint on all source files |
|
|
| `npm run typecheck:core` | TypeScript core type checking |
|
|
| `npm run typecheck:noimplicit:core` | Strict checking (no implicit any) |
|
|
| `npm run check` | Run lint + test |
|
|
| `npm run check:cycles` | Check for circular dependencies |
|
|
| `npm run electron:dev` | Run Electron app in dev mode |
|
|
| `npm run electron:build` | Build Electron app for current OS |
|
|
|
|
**Build output layout:**
|
|
|
|
| Directory | Purpose | Gitignored |
|
|
| --------- | -------------------------------------------------- | ---------- |
|
|
| `src/` | Application source (TypeScript / TSX) | No |
|
|
| `.build/` | Build intermediates (`distDir = .build/next`) | Yes |
|
|
| `dist/` | Shippable bundle assembled by `assembleStandalone` | Yes |
|
|
|
|
The pipeline is a single `next build` pass — intermediates land in `.build/next/`, the
|
|
assembled bundle in `dist/`. VPS deploys rsync `dist/` into the remote
|
|
`/usr/lib/node_modules/omniroute/app/` directory (VPS image path is unchanged).
|
|
|
|
### Running Tests
|
|
|
|
```bash
|
|
# All tests (unit + vitest + ecosystem + e2e)
|
|
npm run test:all
|
|
|
|
# Single test file (Node.js native test runner — most tests use this)
|
|
node --import tsx/esm --test tests/unit/your-file.test.ts
|
|
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
|
|
|
|
# Integration tests
|
|
node --import tsx/esm --test tests/integration/*.test.ts
|
|
|
|
# Vitest (MCP server, autoCombo)
|
|
npm run test:vitest
|
|
|
|
# E2E with Playwright
|
|
npm run test:e2e
|
|
|
|
# Protocol clients E2E (MCP transports, A2A)
|
|
npm run test:protocols:e2e
|
|
|
|
# Ecosystem compatibility tests
|
|
npm run test:ecosystem
|
|
|
|
# Coverage (see CONTRIBUTING.md)
|
|
npm run test:coverage
|
|
```
|
|
|
|
**For authoritative coverage requirements, test execution, and PR gates, see [`CONTRIBUTING.md`](CONTRIBUTING.md#running-tests).**
|
|
|
|
---
|
|
|
|
## Code Style Guidelines
|
|
|
|
### Formatting (Prettier — enforced via lint-staged)
|
|
|
|
2 spaces · semicolons required · double quotes (`"`) · 100 char width · es5 trailing commas.
|
|
Always run `prettier --write` on changed files.
|
|
|
|
### TypeScript
|
|
|
|
- **Target**: ES2022 · **Module**: `esnext` · **Resolution**: `bundler`
|
|
- `strict: false` — prefer explicit types, don't rely on inference
|
|
- Path aliases: `@/*` → `src/`, `@omniroute/open-sse` → `open-sse/`, `@omniroute/open-sse/*` → `open-sse/*`
|
|
|
|
### ESLint Rules
|
|
|
|
- **Security (error, everywhere)**: `no-eval`, `no-implied-eval`, `no-new-func`
|
|
- **Relaxed in `open-sse/` and `tests/`**: `@typescript-eslint/no-explicit-any` = warn
|
|
- React hooks rules and `@next/next/no-assign-module-variable` disabled in `open-sse/` and `tests/`
|
|
|
|
### Naming
|
|
|
|
| Element | Convention | Example |
|
|
| ------------------- | -------------------------------- | ------------------------------------ |
|
|
| Files | camelCase / kebab-case | `chatCore.ts`, `tokenHealthCheck.ts` |
|
|
| React components | PascalCase | `Dashboard.tsx`, `ProviderCard.tsx` |
|
|
| Functions/variables | camelCase | `getHealth()`, `switchCombo()` |
|
|
| Constants | UPPER_SNAKE | `MAX_RETRIES`, `DEFAULT_TIMEOUT` |
|
|
| Interfaces | PascalCase (`I` prefix optional) | `ProviderConfig` |
|
|
| Enums | PascalCase (members too) | `LogLevel.Error` |
|
|
|
|
### Imports
|
|
|
|
- **Order**: external → internal (`@/`, `@omniroute/open-sse`) → relative (`./`, `../`)
|
|
- **No barrel imports** from `localDb.ts` — import from the specific `db/` module instead
|
|
|
|
### Error Handling
|
|
|
|
- try/catch with specific error types; always log with context (pino logger)
|
|
- Never silently swallow errors in SSE streams — use abort signals for cleanup
|
|
- Return proper HTTP status codes (4xx client, 5xx server)
|
|
|
|
### Security
|
|
|
|
- **NEVER** commit API keys, secrets, or credentials
|
|
- Validate all user inputs with Zod schemas
|
|
- Auth middleware required on all API routes
|
|
- Never log SQLite encryption keys
|
|
- Sanitize user content (dompurify for HTML)
|
|
- **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`.
|
|
- **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`.
|
|
- **`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`.
|
|
- 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.).
|
|
|
|
---
|
|
|
|
## Architecture
|
|
|
|
### Data Layer (`src/lib/db/`)
|
|
|
|
All persistence uses SQLite through **83 domain-specific modules** in `src/lib/db/`. Top modules:
|
|
|
|
- Core: `core.ts`, `migrationRunner.ts`, `encryption.ts`, `stateReset.ts`
|
|
- Providers / catalog: `providers.ts`, `models.ts`, `providerLimits.ts`, `compressionAnalytics.ts`
|
|
- Routing: `combos.ts`, `modelComboMappings.ts`, `domainState.ts`, `commandCodeAuth.ts`
|
|
- Auth: `apiKeys.ts`, `secrets.ts`, `registeredKeys.ts`, `sessionAccountAffinity.ts`
|
|
- Usage / billing: `quotaSnapshots.ts`, `creditBalance.ts`, `usage*.ts`, `compressionCacheStats.ts`
|
|
- Storage: `backup.ts`, `cleanup.ts`, `jsonMigration.ts`, `healthCheck.ts`, `databaseSettings.ts`
|
|
- 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`
|
|
|
|
Live count: `ls src/lib/db/*.ts | wc -l` (currently 83). Drift detection: `npm run check:docs-counts`.
|
|
Schema migrations live in `db/migrations/` (**97 files** as of v3.8.24) and run via `migrationRunner.ts`.
|
|
`src/lib/localDb.ts` is a **re-export layer only** — never add logic there.
|
|
|
|
#### DB Internals
|
|
|
|
- **`core.ts`**: `getDbInstance()` returns a singleton `better-sqlite3` instance with WAL
|
|
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`.
|
|
- **`migrationRunner.ts`**: Applies versioned SQL files from `db/migrations/` inside transactions.
|
|
Tracks applied migrations in `_omniroute_migrations` table.
|
|
- **Migrations**: 97 files (`001_initial_schema.sql` → `099_*.sql`).
|
|
Each migration is idempotent and runs in a transaction. Live count: `ls src/lib/db/migrations/*.sql | wc -l`.
|
|
- **Domain modules** import `getDbInstance()` from `core.ts` for all CRUD operations.
|
|
Each module owns a specific table/set of tables (e.g., `providers.ts` → `provider_connections`,
|
|
`combos.ts` → `combos`). Encryption helpers protect sensitive fields at rest.
|
|
- **`localDb.ts`** re-exports all domain modules — consumers import from here for convenience.
|
|
|
|
### API Route Layer (`src/app/api/v1/`)
|
|
|
|
Next.js App Router routes — each follows a consistent pattern:
|
|
|
|
```
|
|
Route → CORS preflight → Body validation (Zod) → Optional auth (extractApiKey/isValidApiKey)
|
|
→ API key policy enforcement (enforceApiKeyPolicy) → Handler delegation (open-sse)
|
|
```
|
|
|
|
| Route | Handler | Notes |
|
|
| ------------------------------- | ------------------------- | ------------------------------------------------------------- |
|
|
| `chat/completions/route.ts` | `handleChat()` | + prompt injection guard (clones request) |
|
|
| `responses/route.ts` | `handleChat()` (unified) | Responses API format |
|
|
| `embeddings/route.ts` | `handleEmbedding()` | Model listing + creation |
|
|
| `images/generations/route.ts` | `handleImageGeneration()` | Model listing + creation |
|
|
| `audio/transcriptions/route.ts` | audio handler | Multipart form data |
|
|
| `audio/speech/route.ts` | TTS handler | Binary audio response |
|
|
| `videos/generations/route.ts` | video handler | ComfyUI/SD WebUI |
|
|
| `music/generations/route.ts` | music handler | ComfyUI workflows |
|
|
| `moderations/route.ts` | moderation handler | Content safety |
|
|
| `rerank/route.ts` | rerank handler | Document relevance |
|
|
| `search/route.ts` | search handler | Web search (12 providers per `open-sse/handlers/search.ts:6`) |
|
|
|
|
**No global Next.js middleware file** — interception is route-specific. Auth is optional
|
|
(controlled by `REQUIRE_API_KEY` env). Prompt injection guard is unique to chat completions.
|
|
|
|
### Request Pipeline (`open-sse/`)
|
|
|
|
The `open-sse/` workspace is the core streaming engine. Full request flow:
|
|
|
|
```
|
|
Client Request
|
|
→ src/app/api/v1/.../route.ts (Next.js route)
|
|
→ open-sse/handlers/chatCore.ts::handleChatCore()
|
|
→ Semantic/signature cache check
|
|
→ Rate limit check (rateLimitManager)
|
|
→ Combo routing? → open-sse/services/combo.ts::handleComboChat()
|
|
→ resolveComboTargets() → ordered ResolvedComboTarget[]
|
|
→ For each target: handleSingleModel() (wraps chatCore)
|
|
→ translateRequest() (open-sse/translator/)
|
|
→ Convert source format (e.g., OpenAI) → target format (e.g., Claude)
|
|
→ getExecutor() → provider-specific executor instance
|
|
→ executor.execute() (BaseExecutor → DefaultExecutor or provider-specific)
|
|
→ buildUrl() + buildHeaders() + transformRequest()
|
|
→ fetch() to upstream provider
|
|
→ Retry logic with exponential backoff
|
|
→ Response translation back to client format
|
|
→ If Responses API: responsesTransformer.ts TransformStream
|
|
→ SSE stream or JSON response to client
|
|
```
|
|
|
|
**Handlers** (`open-sse/handlers/`): `chatCore.ts`, `responsesHandler.ts`, `embeddings.ts`,
|
|
`imageGeneration.ts`, `videoGeneration.ts`, `musicGeneration.ts`, `audioSpeech.ts`,
|
|
`audioTranscription.ts`, `moderations.ts`, `rerank.ts`, `search.ts`.
|
|
|
|
**Upstream headers**: merged after default auth; same header name replaces executor value.
|
|
**T5 intra-family fallback** recomputes headers using only the fallback model id.
|
|
Forbidden header names: `src/shared/constants/upstreamHeaders.ts` — keep sanitize,
|
|
Zod schemas, and unit tests aligned when editing.
|
|
|
|
### Provider Categories
|
|
|
|
- **Free** (4): Qoder AI, Qwen Code, Gemini CLI (deprecated), Kiro AI
|
|
- **OAuth** (14): Claude Code, Antigravity, Codex, GitHub Copilot, Cursor, Kimi Coding, Kilo Code, Cline, Qwen (⚠️ free tier discontinued 2026-04-15), Kiro, Qoder, Gemini, Windsurf (v3.8), GitLab Duo (v3.8)
|
|
- **API Key** (120+): OpenAI, Anthropic, Gemini, DeepSeek, Groq, xAI, Mistral, Perplexity,
|
|
Together, Fireworks, Cerebras, Cohere, NVIDIA, Nebius, SiliconFlow, Hyperbolic,
|
|
HuggingFace, OpenRouter, Vertex AI, Cloudflare AI, Scaleway, AI/ML API, Pollinations,
|
|
Puter, Longcat, Alibaba, Kimi, Minimax, Blackbox, Synthetic, Kilo Gateway,
|
|
Z.AI, GLM, Deepgram, AssemblyAI, ElevenLabs, Cartesia, PlayHT, Inworld,
|
|
NanoBanana, SD WebUI, ComfyUI, Ollama Cloud, Perplexity Search, Serper, Brave, Exa,
|
|
Tavily, OpenCode Zen/Go, Bailian Coding Plan, DeepInfra, Vercel AI Gateway,
|
|
Lambda AI, SambaNova, nScale, OVHcloud AI, Baseten, PublicAI, Moonshot AI,
|
|
Meta Llama API, v0 (Vercel), Morph, Featherless AI, FriendliAI, LlamaGate,
|
|
Galadriel, Weights & Biases Inference, Volcengine, AI21 Labs, Venice.ai,
|
|
Codestral, Upstage, Maritalk, Xiaomi MiMo, Inference.net, NanoGPT, Predibase,
|
|
Bytez, Heroku AI, Databricks, Snowflake Cortex, GigaChat (Sber), CrofAI,
|
|
AgentRouter, ChatGPT Web, Baidu Qianfan, AWS Polly, RunwayML, GitLab Duo,
|
|
Amazon Q, Empower, Poe, and many more.
|
|
- **Self-Hosted** (8+): LM Studio, vLLM, Lemonade, Llamafile, Triton, Docker Model Runner, Xinference, Oobabooga
|
|
- **Custom**: OpenAI-compatible (`openai-compatible-*`) and Anthropic-compatible (`anthropic-compatible-*`) prefixes
|
|
|
|
Providers are registered in `src/shared/constants/providers.ts` with Zod validation at module load.
|
|
|
|
### Executors (`open-sse/executors/`)
|
|
|
|
Provider-specific request executors: `base.ts`, `default.ts`, `cursor.ts`, `codex.ts`,
|
|
`antigravity.ts`, `github.ts`, `gemini-cli.ts`, `kiro.ts`, `qoder.ts`, `vertex.ts`,
|
|
`cloudflare-ai.ts`, `opencode.ts`, `pollinations.ts`, `puter.ts`.
|
|
|
|
#### Executor Internals
|
|
|
|
- **`base.ts`** (`BaseExecutor`): Abstract base with `buildUrl()`, `buildHeaders()`,
|
|
`transformRequest()`, retry logic (exponential backoff), and `execute()`. Subclasses
|
|
override URL/header/transform methods for provider-specific behavior.
|
|
- **`default.ts`** (`DefaultExecutor extends BaseExecutor`): Handles most OpenAI-compatible
|
|
providers. Reads provider config from `providerRegistry.ts` to resolve base URL, auth
|
|
header format, and request transformations.
|
|
- **`getExecutor()`** (`executors/index.ts`): Factory that returns the correct executor
|
|
instance based on provider ID. Provider-specific executors (Cursor, Codex, Vertex, etc.)
|
|
override only what differs from the default.
|
|
|
|
### Translator (`open-sse/translator/`)
|
|
|
|
Translates between API formats (OpenAI-format ↔ Anthropic, Gemini, etc.).
|
|
Includes request/response translators with helpers for image handling.
|
|
|
|
#### Translator Internals
|
|
|
|
- **`translator/index.ts`**: Exports `translateRequest()` and format constants. Called by
|
|
`chatCore.ts` before executor dispatch.
|
|
- **Flow**: `translateRequest(body, sourceFormat, targetFormat)` → detects source format
|
|
(OpenAI, Anthropic, Gemini) → applies the matching translator module → returns
|
|
transformed body ready for the target provider.
|
|
- **Response translation** runs in reverse after upstream response, converting back to
|
|
the client's expected format.
|
|
|
|
### Transformer (`open-sse/transformer/`)
|
|
|
|
`responsesTransformer.ts` — transforms Responses API format to/from Chat Completions format.
|
|
|
|
#### Transformer Internals
|
|
|
|
- **`createResponsesApiTransformStream()`**: Returns a `TransformStream` that converts
|
|
Chat Completions SSE chunks (`data: {"choices":[...]}`) into Responses API SSE events
|
|
(`response.output_item.added`, `response.output_text.delta`, etc.).
|
|
- Used when the client sends a Responses API request: the request is internally converted
|
|
to Chat Completions format, dispatched normally, and the response is piped through this
|
|
transform stream before reaching the client.
|
|
|
|
### Services (`open-sse/services/`)
|
|
|
|
115 service modules in `open-sse/services/` (top-level only; 184 including sub-dirs like `autoCombo/` and `compression/`). Refresh: `ls open-sse/services/*.ts | wc -l`. Key modules:
|
|
`combo.ts` (routing engine), `usage.ts`, `tokenRefresh.ts`,
|
|
`rateLimitManager.ts`, `accountFallback.ts`, `sessionManager.ts`, `wildcardRouter.ts`,
|
|
`autoCombo/`, `intentClassifier.ts`, `taskAwareRouter.ts`, `thinkingBudget.ts`,
|
|
`contextManager.ts`, `modelDeprecation.ts`, `modelFamilyFallback.ts`,
|
|
`emergencyFallback.ts`, `workflowFSM.ts`, `backgroundTaskDetector.ts`, `ipFilter.ts`,
|
|
`signatureCache.ts`, `volumeDetector.ts`, `contextHandoff.ts`, `compression/` (prompt
|
|
compression pipeline), and more.
|
|
|
|
#### Prompt Compression Pipeline (`compression/`)
|
|
|
|
Modular prompt compression that runs proactively before the existing reactive context manager.
|
|
|
|
- **`strategySelector.ts`**: Selects compression mode based on config, compression combo assignments,
|
|
combo overrides, auto-trigger thresholds, and defaults. Priority: assigned compression combo >
|
|
combo override > auto-trigger > default mode > off.
|
|
- **`lite.ts`**: 5 lite-mode techniques: `collapseWhitespace`, `dedupSystemPrompt`,
|
|
`compressToolResults`, `removeRedundantContent`, `replaceImageUrls`. Target: 10-15% savings at
|
|
<1ms latency.
|
|
- **`caveman.ts` / `cavemanRules.ts`**: Caveman-style semantic condensation backed by built-in
|
|
rules plus file-loaded language packs under `compression/rules/`.
|
|
- **`engines/rtk/`**: Rule-based terminal/tool-output compression inspired by RTK patterns. Detects
|
|
command output classes, applies JSON filter packs, deduplicates repeated lines, strips ANSI/code
|
|
noise, and preserves errors/actionable context. The RTK JSON DSL supports replace,
|
|
match-output short-circuit, strip/keep, per-line truncation, head/tail/max-line truncation,
|
|
inline tests, trust-gated project/global custom filters, and optional redacted raw-output
|
|
retention for authenticated recovery.
|
|
- **`engines/registry.ts`**: Registers engines (`caveman`, `rtk`) and powers stacked pipelines.
|
|
- **`stats.ts`**: Per-request compression stats tracking (original tokens, compressed tokens,
|
|
savings %, techniques used, engine breakdown, compression combo id).
|
|
- **`types.ts`**: `CompressionMode` (off/lite/standard/aggressive/ultra/rtk/stacked),
|
|
`CompressionConfig`, `CompressionStats`, `CompressionResult`.
|
|
- DB settings in `src/lib/db/compression.ts`, compression combos in
|
|
`src/lib/db/compressionCombos.ts`, API routes under `src/app/api/settings/compression/`,
|
|
`src/app/api/context/*`, and preview/language-pack routes under `src/app/api/compression/*`.
|
|
|
|
#### Combo Routing Engine (`combo.ts`)
|
|
|
|
- **`handleComboChat()`**: Entry point for combo-routed requests. Receives the combo config
|
|
and iterates through targets in order until one succeeds or all fail.
|
|
- **`resolveComboTargets()`**: Expands a combo configuration into an ordered array of
|
|
`ResolvedComboTarget[]`, each specifying provider + model + account + credentials.
|
|
- **Strategies** (15): priority, weighted, fill-first, round-robin, P2C, random, least-used, reset-aware (v3.8),
|
|
reset-window, cost-optimized, strict-random, auto, lkgp, context-optimized, context-relay. Source: `ROUTING_STRATEGY_VALUES` in `src/shared/constants/routingStrategies.ts`.
|
|
- Each target calls **`handleSingleModel()`** which wraps `handleChatCore()` with
|
|
per-target error handling and circuit breaker checks.
|
|
|
|
### Domain Layer (`src/domain/`)
|
|
|
|
Policy engine modules: `policyEngine.ts`, `comboResolver.ts`, `costRules.ts`,
|
|
`degradation.ts`, `fallbackPolicy.ts`, `lockoutPolicy.ts`, `modelAvailability.ts`,
|
|
`providerExpiration.ts`, `quotaCache.ts`, `responses.ts`, `configAudit.ts`.
|
|
|
|
### MCP Server (`open-sse/mcp-server/`)
|
|
|
|
**87 tools** total (`TOTAL_MCP_TOOL_COUNT`, `open-sse/mcp-server/server.ts`): a 33-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), gamification (8), plugin (8), notion (6), obsidian (22). 3 transports (stdio / SSE / Streamable HTTP). Scoped auth (30 scopes — see `OMNIROUTE_MCP_SCOPES`), Zod schemas. See [`docs/frameworks/MCP-SERVER.md`](docs/frameworks/MCP-SERVER.md).
|
|
|
|
**Core tools** (20): get_health, list_combos, get_combo_metrics, switch_combo, check_quota,
|
|
route_request, cost_report, list_models_catalog, web_search, simulate_route, set_budget_guard,
|
|
set_routing_strategy, set_resilience_profile, test_combo, get_provider_metrics,
|
|
best_combo_for_task, explain_route, get_session_snapshot, db_health_check, sync_pricing.
|
|
|
|
**Cache tools** (2): cache_stats, cache_flush.
|
|
|
|
**Compression tools** (5): compression_status, compression_configure, set_compression_engine,
|
|
list_compression_combos, compression_combo_stats.
|
|
|
|
**1proxy tools** (3): oneproxy_fetch, oneproxy_rotate, oneproxy_stats.
|
|
|
|
**Memory tools** (3): memory_search, memory_add, memory_clear.
|
|
|
|
**Skill tools** (4): skills_list, skills_enable, skills_execute, skills_executions.
|
|
|
|
**Agent-skill tools** (3): A2A skill discovery / invocation bridges.
|
|
|
|
**Gamification tools** (8): levels, badges, leaderboard, and community-federation queries.
|
|
|
|
**Plugin tools** (8): plugin marketplace listing, install/enable/disable, and runtime inspection.
|
|
|
|
**Notion tools** (6) + **Obsidian tools** (22): knowledge-base read/write integrations (the largest tool family — vault search, note CRUD, WebDAV-backed file ops).
|
|
|
|
#### MCP Internals
|
|
|
|
- **Tool registration**: Each tool is an object with `{ name, description, inputSchema: ZodSchema,
|
|
handler: async (args) => {...} }`. Zod validates inputs before the handler fires.
|
|
- **`createMcpServer()`** and **`startMcpStdio()`** exported from `mcp-server/index.ts`.
|
|
`createMcpServer()` wires all tool sets; `startMcpStdio()` launches the stdio transport.
|
|
- **Transports**: stdio (CLI `omniroute --mcp`), SSE (`/api/mcp/sse`), Streamable HTTP
|
|
(`/api/mcp/stream`). All share the same tool/scope engine.
|
|
- **Scopes** (30): Control which tool categories an API key can access. Enforcement happens
|
|
before handler dispatch.
|
|
- **Audit**: Every tool invocation is logged to SQLite (`mcp_audit` table) with tool name,
|
|
args, success/failure, API key attribution, and timestamp.
|
|
|
|
### A2A Server (`src/lib/a2a/`)
|
|
|
|
JSON-RPC 2.0, SSE streaming, Task Manager with TTL cleanup.
|
|
Agent Card at `/.well-known/agent.json`.
|
|
Skills (6): `smartRouting.ts`, `quotaManagement.ts`, `providerDiscovery.ts`, `costAnalysis.ts`, `healthReport.ts`, `listCapabilities.ts`.
|
|
|
|
#### A2A Internals
|
|
|
|
- **`taskManager.ts`**: State machine lifecycle for tasks: `submitted → working →
|
|
completed | failed | canceled`. Tasks have TTL and are cleaned up automatically.
|
|
- **JSON-RPC methods**: `message/send` (sync), `message/stream` (SSE), `tasks/get`,
|
|
`tasks/cancel`. Dispatched via `POST /a2a`.
|
|
- **Skills**: Registered in a DB-backed registry. Each skill receives task context
|
|
(messages, metadata) and returns structured results. `quotaManagement.ts` summarizes
|
|
quota; `smartRouting.ts` recommends routing decisions.
|
|
- **Agent Card**: `/.well-known/agent.json` exposes capabilities, skills, and metadata
|
|
for client auto-discovery.
|
|
|
|
### ACP Module (`src/lib/acp/`)
|
|
|
|
Agent Communication Protocol registry and manager.
|
|
|
|
### Memory System (`src/lib/memory/`)
|
|
|
|
Extraction, injection, retrieval, summarization, and store modules for persistent
|
|
conversational memory across sessions.
|
|
|
|
### Skills System (`src/lib/skills/`)
|
|
|
|
Extensible skill framework: registry, executor, sandbox, built-in skills,
|
|
custom skill support, interception, and injection.
|
|
|
|
#### Skills Internals
|
|
|
|
- **`registry.ts`**: DB-backed skill registration and discovery. Skills have metadata
|
|
(name, description, version, enabled status) stored in SQLite.
|
|
- **`executor.ts`**: Execution engine with configurable timeout and retry logic.
|
|
Receives skill name + input, looks up the skill, runs it in the sandbox.
|
|
- **`sandbox.ts`**: Isolation layer for custom (user-provided) skills. Limits resource
|
|
access and execution time.
|
|
- **Built-in skills**: Ship with OmniRoute (e.g., quota management, routing). Located
|
|
alongside the registry.
|
|
- **Interception/Injection**: Skills can intercept requests in the pipeline (pre/post
|
|
processing) or inject context into prompts.
|
|
|
|
### Compliance (`src/lib/compliance/`)
|
|
|
|
Policy index for compliance enforcement.
|
|
|
|
### MITM Proxy (`src/mitm/`)
|
|
|
|
MITM proxy capability with certificate management, DNS handling, and target routing.
|
|
|
|
### Middleware (`src/middleware/`)
|
|
|
|
Request middleware including `promptInjectionGuard.ts`.
|
|
|
|
### Guardrails (`src/lib/guardrails/`)
|
|
|
|
Hot-reloadable guardrails framework (3 built-in: pii-masker, prompt-injection, vision-bridge). Fail-open; per-request opt-out via header. See [`docs/security/GUARDRAILS.md`](docs/security/GUARDRAILS.md).
|
|
|
|
### Cloud Agents (`src/lib/cloudAgent/`)
|
|
|
|
`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).
|
|
|
|
### Evals (`src/lib/evals/`)
|
|
|
|
Generic eval framework: `evalRunner.ts`, `runtime.ts`. Targets: combo / model / suite-default. See [`docs/frameworks/EVALS.md`](docs/frameworks/EVALS.md).
|
|
|
|
### Webhooks (`src/lib/webhookDispatcher.ts`)
|
|
|
|
HMAC-signed delivery, exponential backoff, auto-disable after 10 failures. 7 event types. See [`docs/frameworks/WEBHOOKS.md`](docs/frameworks/WEBHOOKS.md).
|
|
|
|
### Authorization Pipeline (`src/server/authz/`)
|
|
|
|
`classify → policies → enforce`. 3 route classes (PUBLIC / CLIENT_API / MANAGEMENT). See [`docs/architecture/AUTHZ_GUIDE.md`](docs/architecture/AUTHZ_GUIDE.md).
|
|
|
|
### Reasoning Replay (`src/lib/db/reasoningCache.ts` + `open-sse/services/reasoningCache.ts`)
|
|
|
|
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).
|
|
|
|
### Tunnels (`src/lib/{cloudflaredTunnel,ngrokTunnel}.ts` + `src/app/api/tunnels/`)
|
|
|
|
Cloudflare Quick/Named, ngrok, Tailscale Funnel. See [`docs/ops/TUNNELS_GUIDE.md`](docs/ops/TUNNELS_GUIDE.md).
|
|
|
|
### Adding a New Provider
|
|
|
|
1. Register in `src/shared/constants/providers.ts`
|
|
2. Add executor in `open-sse/executors/` (if custom logic needed)
|
|
3. Add translator in `open-sse/translator/` (if non-OpenAI format)
|
|
4. Add OAuth config in `src/lib/oauth/constants/oauth.ts` (if OAuth-based)
|
|
5. Add models in `open-sse/config/providerRegistry.ts`
|
|
|
|
---
|
|
|
|
## Subdirectory AGENTS.md Files
|
|
|
|
- **[`src/lib/db/AGENTS.md`](src/lib/db/AGENTS.md)** — SQLite persistence, domain modules, migrations
|
|
- **[`open-sse/services/AGENTS.md`](open-sse/services/AGENTS.md)** — Routing engine, combo resolution, strategy selection
|
|
|
|
## Reference Documentation (docs/)
|
|
|
|
For any non-trivial change, read the matching deep-dive first:
|
|
|
|
| Area | Doc |
|
|
| ------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------- |
|
|
| Repo navigation | [`docs/architecture/REPOSITORY_MAP.md`](docs/architecture/REPOSITORY_MAP.md) |
|
|
| Architecture | [`docs/architecture/ARCHITECTURE.md`](docs/architecture/ARCHITECTURE.md) |
|
|
| Engineering reference | [`docs/architecture/CODEBASE_DOCUMENTATION.md`](docs/architecture/CODEBASE_DOCUMENTATION.md) |
|
|
| Auto-Combo (12-factor, 15 strategies) | [`docs/routing/AUTO-COMBO.md`](docs/routing/AUTO-COMBO.md) |
|
|
| Resilience (3 layers) | [`docs/architecture/RESILIENCE_GUIDE.md`](docs/architecture/RESILIENCE_GUIDE.md) |
|
|
| Skills | [`docs/frameworks/SKILLS.md`](docs/frameworks/SKILLS.md) |
|
|
| Memory | [`docs/frameworks/MEMORY.md`](docs/frameworks/MEMORY.md) |
|
|
| Cloud agents | [`docs/frameworks/CLOUD_AGENT.md`](docs/frameworks/CLOUD_AGENT.md) |
|
|
| Guardrails | [`docs/security/GUARDRAILS.md`](docs/security/GUARDRAILS.md) |
|
|
| Evals | [`docs/frameworks/EVALS.md`](docs/frameworks/EVALS.md) |
|
|
| Compliance | [`docs/security/COMPLIANCE.md`](docs/security/COMPLIANCE.md) |
|
|
| Webhooks | [`docs/frameworks/WEBHOOKS.md`](docs/frameworks/WEBHOOKS.md) |
|
|
| Authz | [`docs/architecture/AUTHZ_GUIDE.md`](docs/architecture/AUTHZ_GUIDE.md) |
|
|
| Stealth | [`docs/security/STEALTH_GUIDE.md`](docs/security/STEALTH_GUIDE.md) |
|
|
| Reasoning replay | [`docs/routing/REASONING_REPLAY.md`](docs/routing/REASONING_REPLAY.md) |
|
|
| Agent protocols (A2A / ACP / Cloud) | [`docs/frameworks/AGENT_PROTOCOLS_GUIDE.md`](docs/frameworks/AGENT_PROTOCOLS_GUIDE.md) |
|
|
| MCP server | [`docs/frameworks/MCP-SERVER.md`](docs/frameworks/MCP-SERVER.md) |
|
|
| A2A server | [`docs/frameworks/A2A-SERVER.md`](docs/frameworks/A2A-SERVER.md) |
|
|
| API reference | [`docs/reference/API_REFERENCE.md`](docs/reference/API_REFERENCE.md) + [`docs/reference/openapi.yaml`](docs/reference/openapi.yaml) |
|
|
| Provider catalog (auto-generated) | [`docs/reference/PROVIDER_REFERENCE.md`](docs/reference/PROVIDER_REFERENCE.md) |
|
|
| Tunnels | [`docs/ops/TUNNELS_GUIDE.md`](docs/ops/TUNNELS_GUIDE.md) |
|
|
| Electron desktop | [`docs/guides/ELECTRON_GUIDE.md`](docs/guides/ELECTRON_GUIDE.md) |
|
|
| Release flow | [`docs/ops/RELEASE_CHECKLIST.md`](docs/ops/RELEASE_CHECKLIST.md) |
|
|
| Quality gates (35 gates, allowlist policy) | [`docs/architecture/QUALITY_GATES.md`](docs/architecture/QUALITY_GATES.md) |
|
|
|
|
---
|
|
|
|
## Fork / Upstream Workflow
|
|
|
|
This repository is a fork of `diegosouzapw/OmniRoute`. Keep fork-only operational
|
|
changes (for example GHCR image publishing, personal deployment workflows, or local
|
|
automation) out of upstream contribution PRs.
|
|
|
|
When preparing a PR for upstream, always start the work branch from `upstream/main`,
|
|
not from this fork's `main`:
|
|
|
|
```bash
|
|
git fetch upstream
|
|
git switch -c <branch-name> upstream/main
|
|
```
|
|
|
|
Only cherry-pick or reapply the changes intended for the upstream PR.
|
|
|
|
---
|
|
|
|
## Review Focus
|
|
|
|
- **DB ops** go through `src/lib/db/` modules, never raw SQL in routes
|
|
- **Provider requests** flow through `open-sse/handlers/`
|
|
- **MCP/A2A pages** are tabs inside `/dashboard/endpoint`, not standalone routes
|
|
- **No memory leaks** in SSE streams (abort signals, cleanup)
|
|
- **Rate limit headers** must be parsed correctly
|
|
- All API inputs validated with **Zod schemas**
|
|
- **Provider constants** validated at module load via Zod (`src/shared/validation/providerSchema.ts`)
|
|
- **Pricing data** syncs from LiteLLM via `src/lib/pricingSync.ts`
|
|
- **Memory/Skills** are cross-cutting: affect MCP tools, request pipeline, and A2A skills
|
|
- **⛔ 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.
|