Release v3.8.40

v3.8.40 cycle integration → main. All test gates green (Unit/Integration/Coverage/Node-compat/Quality-Ratchet). The only red check, 'PR Test Policy', is the test-masking heuristic firing on the cumulative ~57-commit release diff (legitimate assert consolidations already reviewed per-PR — Gemini CLI removal #5246, retired GPT models #5280, provider catalog refreshes); overridden with --admin per the documented release-PR convention. CodeQL/SonarQube advisory scans non-blocking; #5278's code already passed CodeQL on main. Homologated on VPS 192.168.0.15 (v3.8.40 healthy).
This commit is contained in:
Diego Rodrigues de Sa e Souza
2026-06-29 08:40:06 -03:00
committed by GitHub
parent 1c18be4f8f
commit 7c23dab64d
1007 changed files with 16451 additions and 21343 deletions

View File

@@ -1,7 +1,7 @@
---
title: "OmniRoute A2A Server Documentation"
version: 3.8.2
lastUpdated: 2026-05-13
version: 3.8.40
lastUpdated: 2026-06-28
---
# OmniRoute A2A Server Documentation
@@ -141,14 +141,14 @@ curl -X POST http://localhost:20128/a2a \
OmniRoute exposes 6 A2A skills wired in `src/lib/a2a/taskExecution.ts::A2A_SKILL_HANDLERS`. Each skill module lives in `src/lib/a2a/skills/`.
| Skill | ID | Description | Tags | Examples |
| :------------------ | :-------------------- | :------------------------------------------------------------------------------------------------------------ | :---------------------- | :-------------------------------------- |
| Smart Routing | `smart-routing` | Routes a prompt through the optimal provider/combo using OmniRoute's combo engine + scoring | routing, providers | "Route this prompt via the best model" |
| Quota Management | `quota-management` | Reports per-provider quota state, helps callers decide when to throttle/switch | quota, providers | "Check quota for anthropic" |
| Provider Discovery | `provider-discovery` | Lists installed providers with capabilities, free-tier flags, OAuth status | providers, discovery | "What providers are available?" |
| Cost Analysis | `cost-analysis` | Estimates cost of a request/conversation given the catalog + recent usage | cost, usage | "Estimate cost for this conversation" |
| Health Report | `health-report` | Aggregates circuit breaker, cooldown, lockout state per provider | health, resilience | "Show health status of all providers" |
| List Capabilities | `list-capabilities` | Returns the full 42-entry Agent Skills catalog as a markdown table with raw SKILL.md URLs for context injection | catalog, discovery, skills | "List all OmniRoute capabilities" |
| Skill | ID | Description | Tags | Examples |
| :----------------- | :------------------- | :-------------------------------------------------------------------------------------------------------------- | :------------------------- | :------------------------------------- |
| Smart Routing | `smart-routing` | Routes a prompt through the optimal provider/combo using OmniRoute's combo engine + scoring | routing, providers | "Route this prompt via the best model" |
| Quota Management | `quota-management` | Reports per-provider quota state, helps callers decide when to throttle/switch | quota, providers | "Check quota for anthropic" |
| Provider Discovery | `provider-discovery` | Lists installed providers with capabilities, free-tier flags, OAuth status | providers, discovery | "What providers are available?" |
| Cost Analysis | `cost-analysis` | Estimates cost of a request/conversation given the catalog + recent usage | cost, usage | "Estimate cost for this conversation" |
| Health Report | `health-report` | Aggregates circuit breaker, cooldown, lockout state per provider | health, resilience | "Show health status of all providers" |
| List Capabilities | `list-capabilities` | Returns the full 42-entry Agent Skills catalog as a markdown table with raw SKILL.md URLs for context injection | catalog, discovery, skills | "List all OmniRoute capabilities" |
> Note: the Agent Card description currently advertises "36+ providers" (`src/app/.well-known/agent.json/route.ts:26` and `:55`). The actual catalog has grown to 180+ providers — the string should be updated in a follow-up change (tracked as a separate doc/code TODO; not modified here).

View File

@@ -4,7 +4,7 @@ title: ACP (Agent Client Protocol)
# ACP (Agent Client Protocol)
> **TL;DR**: ACP lets OmniRoute spawn CLI agents (like Claude Code, Codex, Gemini CLI) as child processes instead of using HTTP APIs. This gives you "CLI-as-backend" transport.
> **TL;DR**: ACP lets OmniRoute spawn CLI agents (like Claude Code, Codex) as child processes instead of using HTTP APIs. This gives you "CLI-as-backend" transport.
---
@@ -34,7 +34,6 @@ ACP supports **14 built-in CLI agents** out of the box:
| `codex` | OpenAI Codex CLI | `codex` | stdio |
| `claude` | Claude Code CLI | `claude` | stdio |
| `goose` | Goose CLI | `goose` | stdio |
| `gemini-cli` | Gemini CLI | `gemini` | stdio |
| `openclaw` | OpenClaw | `openclaw` | stdio |
| `aider` | Aider | `aider` | stdio |
| `opencode` | OpenCode | `opencode` | stdio |

View File

@@ -1,15 +1,15 @@
---
title: "OmniRoute Agent Skills Catalog"
version: 3.8.6
lastUpdated: 2026-05-28
version: 3.8.40
lastUpdated: 2026-06-28
---
# OmniRoute Agent Skills Catalog
> **Source of truth:** `src/lib/agentSkills/` (catalog, generator, parsers) + `skills/` directory (SKILL.md files)
> **Last updated:** 2026-05-28 — v3.8.6
> **Last updated:** 2026-06-28 — v3.8.40
Agent Skills are structured SKILL.md files that teach external agents, MCP clients, and A2A orchestrators how to use OmniRoute's REST API and CLI. Unlike [Omni Skills](./SKILLS.md) (which are LLM tool definitions executed inside OmniRoute), Agent Skills are a *documentation catalog* — static markdown that can be fed directly into agent context.
Agent Skills are structured SKILL.md files that teach external agents, MCP clients, and A2A orchestrators how to use OmniRoute's REST API and CLI. Unlike [Omni Skills](./SKILLS.md) (which are LLM tool definitions executed inside OmniRoute), Agent Skills are a _documentation catalog_ — static markdown that can be fed directly into agent context.
---
@@ -58,20 +58,27 @@ src/lib/a2a/skills/listCapabilities.ts — A2A skill: list-capabilities
name: omni-providers
description: "Manage provider connections: add, test, rotate, and remove credentials."
---
<!-- generated by src/lib/agentSkills/generator.ts; manual edits will be overwritten -->
## Overview
...
## Authentication
...
## Endpoints
...
<!-- skill:custom-start -->
## Custom Section (preserved across regeneration)
...
<!-- skill:custom-end -->
```
@@ -83,13 +90,13 @@ The generator preserves content between `<!-- skill:custom-start -->` and `<!--
## REST API Discovery
| Endpoint | Method | Description | Auth |
| :--- | :--- | :--- | :--- |
| `/api/agent-skills` | GET | List catalog (optional `?category=api\|cli&area=<area>`) | none |
| `/api/agent-skills/{id}` | GET | Get single skill metadata | none |
| `/api/agent-skills/{id}/raw` | GET | Fetch SKILL.md as `text/markdown` | none |
| `/api/agent-skills/coverage` | GET | Coverage stats (how many SKILL.md files exist) | none |
| `/api/agent-skills/generate` | POST | Trigger generator (dryRun/prune/onlyIds) | management |
| Endpoint | Method | Description | Auth |
| :--------------------------- | :----- | :------------------------------------------------------- | :--------- |
| `/api/agent-skills` | GET | List catalog (optional `?category=api\|cli&area=<area>`) | none |
| `/api/agent-skills/{id}` | GET | Get single skill metadata | none |
| `/api/agent-skills/{id}/raw` | GET | Fetch SKILL.md as `text/markdown` | none |
| `/api/agent-skills/coverage` | GET | Coverage stats (how many SKILL.md files exist) | none |
| `/api/agent-skills/generate` | POST | Trigger generator (dryRun/prune/onlyIds) | management |
Example — list all API skills:
@@ -109,11 +116,11 @@ curl -H "Accept: text/markdown" "http://localhost:20128/api/agent-skills/omni-pr
Three MCP tools are registered under scope `read:catalog`:
| Tool | Description |
| :--- | :--- |
| `omniroute_agent_skills_list` | List skills (optional `category` / `area` filters) |
| `omniroute_agent_skills_get` | Get metadata + SKILL.md for one skill by `id` |
| `omniroute_agent_skills_coverage` | Coverage stats (API/CLI have/total) |
| Tool | Description |
| :-------------------------------- | :------------------------------------------------- |
| `omniroute_agent_skills_list` | List skills (optional `category` / `area` filters) |
| `omniroute_agent_skills_get` | Get metadata + SKILL.md for one skill by `id` |
| `omniroute_agent_skills_coverage` | Coverage stats (API/CLI have/total) |
See [MCP-SERVER.md](./MCP-SERVER.md) for scope wiring and authentication.
@@ -125,11 +132,12 @@ The A2A skill `list-capabilities` returns the full 42-skill catalog as a markdow
```json
{
"jsonrpc": "2.0", "id": "1",
"jsonrpc": "2.0",
"id": "1",
"method": "message/send",
"params": {
"skill": "list-capabilities",
"messages": [{"role": "user", "content": "List all capabilities"}]
"messages": [{ "role": "user", "content": "List all capabilities" }]
}
}
```
@@ -142,55 +150,55 @@ See [A2A-SERVER.md](./A2A-SERVER.md) for protocol details.
### API Skills (22)
| ID | Area | Entry Point |
| :--- | :--- | :--- |
| `omni-auth` | auth | Auth + session management |
| `omni-providers` | providers | Provider connection management |
| `omni-models` | models | Model catalog and capabilities |
| `omni-combos-routing` | combos-routing | Combo routing strategies |
| `omni-api-keys` | api-keys | API key management |
| `omni-usage-logs` | usage-logs | Usage and cost logs |
| `omni-budget` | budget | Budget guards |
| `omni-settings` | settings | Global settings |
| `omni-proxies` | proxies | Proxy pool management |
| `omni-cache` | cache | Semantic + prompt cache |
| `omni-compression` | compression | Context compression engines |
| `omni-context-rtk` | context-rtk | RTK compression |
| `omni-resilience` | resilience | Circuit breakers + cooldowns |
| `omni-cli-tools` | cli-tools | CLI tools REST proxy |
| `omni-tunnels` | tunnels | Tunnel management |
| `omni-sync-cloud` | sync-cloud | Cloud sync |
| `omni-db-backups` | db-backups | Database backups |
| `omni-webhooks` | webhooks | Webhook event dispatcher |
| `omni-mcp` | mcp | MCP server (87 tools, 3 transports) |
| `omni-agents-a2a` | agents-a2a | A2A agent protocol |
| `omni-version-manager` | version-manager | Version and update management |
| `omni-inference` | inference | Direct inference / completions |
| ID | Area | Entry Point |
| :--------------------- | :-------------- | :---------------------------------- |
| `omni-auth` | auth | Auth + session management |
| `omni-providers` | providers | Provider connection management |
| `omni-models` | models | Model catalog and capabilities |
| `omni-combos-routing` | combos-routing | Combo routing strategies |
| `omni-api-keys` | api-keys | API key management |
| `omni-usage-logs` | usage-logs | Usage and cost logs |
| `omni-budget` | budget | Budget guards |
| `omni-settings` | settings | Global settings |
| `omni-proxies` | proxies | Proxy pool management |
| `omni-cache` | cache | Semantic + prompt cache |
| `omni-compression` | compression | Context compression engines |
| `omni-context-rtk` | context-rtk | RTK compression |
| `omni-resilience` | resilience | Circuit breakers + cooldowns |
| `omni-cli-tools` | cli-tools | CLI tools REST proxy |
| `omni-tunnels` | tunnels | Tunnel management |
| `omni-sync-cloud` | sync-cloud | Cloud sync |
| `omni-db-backups` | db-backups | Database backups |
| `omni-webhooks` | webhooks | Webhook event dispatcher |
| `omni-mcp` | mcp | MCP server (87 tools, 3 transports) |
| `omni-agents-a2a` | agents-a2a | A2A agent protocol |
| `omni-version-manager` | version-manager | Version and update management |
| `omni-inference` | inference | Direct inference / completions |
### CLI Skills (20)
| ID | Area | CLI Command Root |
| :--- | :--- | :--- |
| `cli-serve` | cli-serve | `omniroute serve` |
| `cli-health` | cli-health | `omniroute health` |
| `cli-providers` | cli-providers | `omniroute providers` |
| `cli-keys` | cli-keys | `omniroute keys` |
| `cli-models` | cli-models | `omniroute models` |
| `cli-chat` | cli-chat | `omniroute chat` |
| `cli-routing` | cli-routing | `omniroute routing` |
| `cli-resilience` | cli-resilience | `omniroute resilience` |
| `cli-compression` | cli-compression | `omniroute compression` |
| `cli-contexts` | cli-contexts | `omniroute contexts` |
| `cli-cost-usage` | cli-cost-usage | `omniroute cost` |
| `cli-mcp` | cli-mcp | `omniroute mcp` |
| `cli-a2a` | cli-a2a | `omniroute a2a` |
| `cli-tunnel` | cli-tunnel | `omniroute tunnel` |
| `cli-backup-sync` | cli-backup-sync | `omniroute backup` |
| `cli-policy-audit` | cli-policy-audit | `omniroute policy` |
| `cli-batches` | cli-batches | `omniroute batch` |
| `cli-eval` | cli-eval | `omniroute eval` |
| `cli-plugins-skills` | cli-plugins-skills | `omniroute plugins` |
| `cli-setup` | cli-setup | `omniroute setup` |
| ID | Area | CLI Command Root |
| :------------------- | :----------------- | :---------------------- |
| `cli-serve` | cli-serve | `omniroute serve` |
| `cli-health` | cli-health | `omniroute health` |
| `cli-providers` | cli-providers | `omniroute providers` |
| `cli-keys` | cli-keys | `omniroute keys` |
| `cli-models` | cli-models | `omniroute models` |
| `cli-chat` | cli-chat | `omniroute chat` |
| `cli-routing` | cli-routing | `omniroute routing` |
| `cli-resilience` | cli-resilience | `omniroute resilience` |
| `cli-compression` | cli-compression | `omniroute compression` |
| `cli-contexts` | cli-contexts | `omniroute contexts` |
| `cli-cost-usage` | cli-cost-usage | `omniroute cost` |
| `cli-mcp` | cli-mcp | `omniroute mcp` |
| `cli-a2a` | cli-a2a | `omniroute a2a` |
| `cli-tunnel` | cli-tunnel | `omniroute tunnel` |
| `cli-backup-sync` | cli-backup-sync | `omniroute backup` |
| `cli-policy-audit` | cli-policy-audit | `omniroute policy` |
| `cli-batches` | cli-batches | `omniroute batch` |
| `cli-eval` | cli-eval | `omniroute eval` |
| `cli-plugins-skills` | cli-plugins-skills | `omniroute plugins` |
| `cli-setup` | cli-setup | `omniroute setup` |
---
@@ -283,8 +291,8 @@ curl "http://localhost:20128/api/agent-skills/coverage"
```json
{
"api": {"have": 22, "total": 22},
"cli": {"have": 20, "total": 20},
"api": { "have": 22, "total": 22 },
"cli": { "have": 20, "total": 20 },
"totalSkills": 42,
"generatedAt": "2026-05-28T00:00:00.000Z"
}

View File

@@ -1,7 +1,7 @@
---
title: "AgentBridge"
version: 3.8.31
lastUpdated: 2026-06-20
version: 3.8.40
lastUpdated: 2026-06-28
---
# AgentBridge
@@ -29,23 +29,23 @@ This means you can:
### Positioning vs. the market
| Feature | 9router | anti-api | llm-interceptor | **OmniRoute AgentBridge** |
|---------|:-------:|:--------:|:---------------:|:-------------------------:|
| Antigravity | ✓ | ✓ | — | ✓ |
| GitHub Copilot | ✓ | ✓ | — | ✓ |
| Kiro (AWS) | ✓ | ✓ | — | ✓ |
| OpenAI Codex | — | ✓ | — | ✓ |
| Cursor IDE | ✓ | ✓ | — | ✓ |
| Zed Industries | — | ✓ | — | ✓ |
| Claude Code | — | — | ✓ | ✓ |
| Open Code | — | — | ✓ | ✓ |
| Trae | — | — | — | 🔍 Investigating |
| Dashboard UI | ✓ | ✗ | ✗ | ✓ |
| Traffic Inspector | ✗ | ✗ | ✓ | ✓ |
| OmniRoute routing | ✗ | ✗ | ✗ | ✓ |
| Model mapping UI | ✗ | ✗ | ✗ | ✓ |
| Bypass list | ✗ | ✗ | ✓ | ✓ |
| Upstream CA cert | ✗ | ✗ | ✓ | ✓ |
| Feature | 9router | anti-api | llm-interceptor | **OmniRoute AgentBridge** |
| ----------------- | :-----: | :------: | :-------------: | :-----------------------: |
| Antigravity | ✓ | ✓ | — | ✓ |
| GitHub Copilot | ✓ | ✓ | — | ✓ |
| Kiro (AWS) | ✓ | ✓ | — | ✓ |
| OpenAI Codex | — | ✓ | — | ✓ |
| Cursor IDE | ✓ | ✓ | — | ✓ |
| Zed Industries | — | ✓ | — | ✓ |
| Claude Code | — | — | ✓ | ✓ |
| Open Code | — | — | ✓ | ✓ |
| Trae | — | — | — | 🔍 Investigating |
| Dashboard UI | ✓ | ✗ | ✗ | ✓ |
| Traffic Inspector | ✗ | ✗ | ✓ | ✓ |
| OmniRoute routing | ✗ | ✗ | ✗ | ✓ |
| Model mapping UI | ✗ | ✗ | ✗ | ✓ |
| Bypass list | ✗ | ✗ | ✓ | ✓ |
| Upstream CA cert | ✗ | ✗ | ✓ | ✓ |
---
@@ -96,7 +96,7 @@ export abstract class MitmHandlerBase {
req: IncomingMessage,
res: ServerResponse,
body: Buffer,
mappedModel: string,
mappedModel: string
): Promise<void>;
// Protected helpers: fetchRouter, pipeSSE, hookBufferStart, hookBufferUpdate
@@ -117,9 +117,7 @@ export const COPILOT_TARGET: MitmTarget = {
hosts: ["api.githubcopilot.com", "copilot-proxy.githubusercontent.com"],
port: 443,
endpointPatterns: ["/chat/completions", "/v1/chat/completions"],
defaultModels: [
{ id: "gpt-4o", name: "GPT-4o", alias: "gpt-4o" },
],
defaultModels: [{ id: "gpt-4o", name: "GPT-4o", alias: "gpt-4o" }],
handler: () => import("../handlers/copilot"),
riskNoticeKey: "providers.riskNotice.oauth",
};
@@ -130,15 +128,18 @@ The registry (`targets/index.ts`) exports `ALL_TARGETS` and emits `DATA_DIR/mitm
### 2.5 Passthrough and bypass list (`src/mitm/passthrough.ts`)
**Bypass list** (checked first, with precedence over target match):
- Default patterns: banking hosts, `.gov.`, OAuth/SSO providers (Okta, Auth0), etc.
- User patterns: stored in DB table `agent_bridge_bypass`
- Bypassed hosts receive a transparent TCP tunnel — TLS is **never decrypted**
**Passthrough default** (no target match and not in bypass):
- Also receives a TCP tunnel — connections are never broken
- Prevents the AgentBridge from disrupting general system HTTPS traffic
Routing precedence:
```
bypass list → target match → passthrough
```
@@ -169,13 +170,13 @@ Applied to all request bodies and headers **before** they enter the Traffic Insp
Use the AgentBridge Server Card at `/dashboard/tools/agent-bridge`:
| Action | Description |
|--------|-------------|
| Start Server | Spawns `src/mitm/server.cjs` on port 443 |
| Stop Server | Gracefully shuts down the child process |
| Restart Server | Stop + start (picks up target changes) |
| Trust Cert | Installs `DATA_DIR/mitm/ca.crt` into OS trust store |
| Download Cert | Downloads `ca.crt` for manual installation |
| Action | Description |
| --------------- | ----------------------------------------------------------------------- |
| Start Server | Spawns `src/mitm/server.cjs` on port 443 |
| Stop Server | Gracefully shuts down the child process |
| Restart Server | Stop + start (picks up target changes) |
| Trust Cert | Installs `DATA_DIR/mitm/ca.crt` into OS trust store |
| Download Cert | Downloads `ca.crt` for manual installation |
| Regenerate Cert | Creates a new CA keypair (all existing per-agent certs are invalidated) |
### 3.2 Trust the certificate
@@ -183,17 +184,20 @@ Use the AgentBridge Server Card at `/dashboard/tools/agent-bridge`:
The AgentBridge CA certificate must be trusted by the OS before IDEs will accept the MITM connection.
**Linux (NSS — Chrome/Firefox):**
```bash
certutil -A -d sql:$HOME/.pki/nssdb -n "OmniRoute AgentBridge" -t CT,, -i ~/.omniroute/mitm/ca.crt
```
**macOS (Keychain):**
```bash
sudo security add-trusted-cert -d -r trustRoot \
-k /Library/Keychains/System.keychain ~/.omniroute/mitm/ca.crt
```
**Windows (certmgr):**
```powershell
certutil -addstore -f Root $env:USERPROFILE\.omniroute\mitm\ca.crt
```
@@ -206,7 +210,7 @@ Some IDEs — notably **Antigravity IDE**, and other Electron / VS Code-derived
their own Node.js runtime that **does not consult the OS trust store** for outbound
`fetch`/HTTPS. Trusting the CA at the OS/NSS level is enough for the IDE's native **backend**
(e.g. a Go language server, which uses the OS CA bundle), but the **Electron frontend** will
still fail TLS — it surfaces as the app being *logged out* or showing a *"connection error"*
still fail TLS — it surfaces as the app being _logged out_ or showing a _"connection error"_
even though the MITM log shows the backend's bootstrap calls returning `200`. Two steps are
required, and both matter:
@@ -228,6 +232,7 @@ locally-trusted CA overrides). `NODE_EXTRA_CA_CERTS` covers the Node `fetch` pat
For each agent you want to intercept, its API host(s) must resolve to `127.0.0.1`. AgentBridge manages `/etc/hosts` entries automatically when you toggle DNS for an agent in the Setup Wizard.
Example `/etc/hosts` entries for GitHub Copilot:
```
127.0.0.1 api.githubcopilot.com
127.0.0.1 copilot-proxy.githubusercontent.com
@@ -238,9 +243,9 @@ Example `/etc/hosts` entries for GitHub Copilot:
Use the Model Mapping Table in each agent card to define source → target mappings:
| Source model (agent native) | Target model (OmniRoute) |
|-----------------------------|--------------------------|
| `gpt-4o` | `claude-sonnet-4.7` |
| `*` (wildcard) | `claude-haiku-4.7` |
| --------------------------- | ------------------------ |
| `gpt-4o` | `claude-sonnet-4.7` |
| `*` (wildcard) | `claude-haiku-4.7` |
Wildcard `*` maps any unrecognized model to the specified target. Persisted in `agent_bridge_mappings` table.
@@ -257,27 +262,27 @@ AgentBridge intercepts credentials (OAuth tokens, API keys) that the IDE uses to
### 3.6 Maintenance & Diagnostics
The dashboard exposes a **Maintenance & Diagnostics** card (`AgentBridgeMaintenanceCard`, in `src/app/(dashboard)/dashboard/tools/agent-bridge/components/`) that surfaces operational MITM routes which previously had no UI. Its subtitle: *"Self-test the capture pipeline, undo leftover system state, and move your setup between machines."* The card client helpers live in `src/lib/inspector/agentBridgeMaintenanceApi.ts`.
The dashboard exposes a **Maintenance & Diagnostics** card (`AgentBridgeMaintenanceCard`, in `src/app/(dashboard)/dashboard/tools/agent-bridge/components/`) that surfaces operational MITM routes which previously had no UI. Its subtitle: _"Self-test the capture pipeline, undo leftover system state, and move your setup between machines."_ The card client helpers live in `src/lib/inspector/agentBridgeMaintenanceApi.ts`.
| Button | Route | What it does |
|--------|-------|--------------|
| **Diagnose** | `GET /api/tools/agent-bridge/diagnose` | Runs the capture-pipeline self-test and shows a per-check report (✓/✗ + remediation hint). |
| **Repair** | `POST /api/tools/agent-bridge/repair` | Undoes orphaned MITM system state (DNS spoof entries, root CA, system proxy) left behind by a crash or SIGKILL. Idempotent — reports "Nothing to repair" when state is clean. |
| **Remove CA** | `DELETE /api/tools/agent-bridge/cert` | Untrusts and removes the MITM root CA from the OS trust store (explicit, idempotent). Shown only when the CA is currently trusted; requires an inline "Remove CA?" confirmation. |
| **Export config** | `GET /api/tools/agent-bridge/config` | Downloads the portable config JSON (see §3.7). |
| **Import config** | `POST /api/tools/agent-bridge/config` | Uploads a previously-exported config JSON (see §3.7). |
| Button | Route | What it does |
| ----------------- | -------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Diagnose** | `GET /api/tools/agent-bridge/diagnose` | Runs the capture-pipeline self-test and shows a per-check report (✓/✗ + remediation hint). |
| **Repair** | `POST /api/tools/agent-bridge/repair` | Undoes orphaned MITM system state (DNS spoof entries, root CA, system proxy) left behind by a crash or SIGKILL. Idempotent — reports "Nothing to repair" when state is clean. |
| **Remove CA** | `DELETE /api/tools/agent-bridge/cert` | Untrusts and removes the MITM root CA from the OS trust store (explicit, idempotent). Shown only when the CA is currently trusted; requires an inline "Remove CA?" confirmation. |
| **Export config** | `GET /api/tools/agent-bridge/config` | Downloads the portable config JSON (see §3.7). |
| **Import config** | `POST /api/tools/agent-bridge/config` | Uploads a previously-exported config JSON (see §3.7). |
**Diagnostics checks** (`summarizeDiagnostics()` in `src/mitm/inspector/diagnostics.ts`). The route runs the effectful probe for each and feeds the booleans into the pure summarizer; a single `healthy` verdict plus a per-failure hint is returned:
| Check name | What it verifies | Hint on failure |
|------------|------------------|-----------------|
| `server-running` | The MITM server process is active | "The MITM server is not running. Start it from the AgentBridge tab." |
| `server-reachable` | The MITM server accepts connections on its port (TCP probe) | "The MITM server is not accepting connections on its port. Check that the port is free and that you have privileges to bind it." |
| `cert-exists` | The MITM certificate has been generated on disk | "No MITM certificate has been generated yet. Generate one from the AgentBridge tab." |
| `cert-trusted` | The MITM root CA is in the OS trust store | "The MITM root CA is not trusted by the OS store, so TLS interception will fail. Trust the certificate from the AgentBridge tab." |
| `dns-configured` | Target hostnames are spoofed in `/etc/hosts` | "Target hostnames are not spoofed in /etc/hosts, so traffic never reaches the proxy. Enable DNS for the agent(s) you want to capture." |
| Check name | What it verifies | Hint on failure |
| ------------------ | ----------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| `server-running` | The MITM server process is active | "The MITM server is not running. Start it from the AgentBridge tab." |
| `server-reachable` | The MITM server accepts connections on its port (TCP probe) | "The MITM server is not accepting connections on its port. Check that the port is free and that you have privileges to bind it." |
| `cert-exists` | The MITM certificate has been generated on disk | "No MITM certificate has been generated yet. Generate one from the AgentBridge tab." |
| `cert-trusted` | The MITM root CA is in the OS trust store | "The MITM root CA is not trusted by the OS store, so TLS interception will fail. Trust the certificate from the AgentBridge tab." |
| `dns-configured` | Target hostnames are spoofed in `/etc/hosts` | "Target hostnames are not spoofed in /etc/hosts, so traffic never reaches the proxy. Enable DNS for the agent(s) you want to capture." |
**Orphaned-state banner:** when the page detects state left behind by a crash (DNS spoof / CA / system proxy), the card shows an amber banner — *"A previous session left system state behind (DNS spoof, CA, or system proxy). Run Repair to clean it up."* — and highlights the **Repair** button. `Repair` is the application-layer analogue of ProxyBridge's `--cleanup` flag (it delegates to `repairMitm()` in `src/mitm/manager.ts`).
**Orphaned-state banner:** when the page detects state left behind by a crash (DNS spoof / CA / system proxy), the card shows an amber banner — _"A previous session left system state behind (DNS spoof, CA, or system proxy). Run Repair to clean it up."_ — and highlights the **Repair** button. `Repair` is the application-layer analogue of ProxyBridge's `--cleanup` flag (it delegates to `repairMitm()` in `src/mitm/manager.ts`).
> The MITM root CA is kept installed across stop/start to avoid repeated sudo
> prompts (the same behavior as mitmproxy/Charles), so removing it is an explicit
@@ -289,11 +294,11 @@ AgentBridge can serialize the **operator-tunable** state into a versioned JSON b
The export includes exactly three pieces (built-in defaults are intentionally **NOT** exported, so importing never duplicates or fights them):
| Field | Source | Notes |
|-------|--------|-------|
| `bypassPatterns` | user-defined bypass patterns (`agent_bridge_bypass`) | default bank/gov/okta patterns are excluded |
| `customHosts` | Traffic Inspector custom hosts (`inspector_custom_hosts`) | each: `{ host, kind: "llm"\|"app"\|"custom", label? }` |
| `agentMappings` | per-agent model mappings (`agent_bridge_mappings`) | `{ [agentId]: [{ source, target }] }` for every agent that has mappings |
| Field | Source | Notes |
| ---------------- | --------------------------------------------------------- | ----------------------------------------------------------------------- |
| `bypassPatterns` | user-defined bypass patterns (`agent_bridge_bypass`) | default bank/gov/okta patterns are excluded |
| `customHosts` | Traffic Inspector custom hosts (`inspector_custom_hosts`) | each: `{ host, kind: "llm"\|"app"\|"custom", label? }` |
| `agentMappings` | per-agent model mappings (`agent_bridge_mappings`) | `{ [agentId]: [{ source, target }] }` for every agent that has mappings |
```jsonc
// GET /api/tools/agent-bridge/config
@@ -301,7 +306,7 @@ The export includes exactly three pieces (built-in defaults are intentionally **
"version": 1,
"bypassPatterns": ["*.internal.example.com"],
"customHosts": [{ "host": "api.example.com", "kind": "llm", "label": null }],
"agentMappings": { "copilot": [{ "source": "gpt-4o", "target": "claude-sonnet-4.7" }] }
"agentMappings": { "copilot": [{ "source": "gpt-4o", "target": "claude-sonnet-4.7" }] },
}
```
@@ -317,17 +322,17 @@ What is **NOT** in the config: server running state, cert paths, per-agent DNS s
## §4 Per-agent reference
| # | Agent | Status | Hosts intercepted | Auth type |
|---|-------|--------|-------------------|-----------|
| 1 | **Antigravity** | ✅ Supported | `daily-cloudcode-pa.googleapis.com`, `cloudcode-pa.googleapis.com` | Firebase OAuth |
| 2 | **Kiro (AWS)** | ✅ Supported | `prod.kiro.aws`, `dev.kiro.aws` | AWS SigV4 |
| 3 | **GitHub Copilot** | ✅ Supported | `api.githubcopilot.com`, `copilot-proxy.githubusercontent.com` | GitHub OAuth |
| 4 | **OpenAI Codex** | ✅ Supported | `api.openai.com` (Codex paths), `chatgpt.com` | OpenAI key |
| 5 | **Cursor IDE** | ✅ Supported | `api2.cursor.sh`, `api.cursor.sh` | Cursor OAuth |
| 6 | **Zed Industries** | ✅ Supported | `api.zed.dev`, `llm.zed.dev` | Zed OAuth |
| 7 | **Claude Code** | ✅ Supported | `api.anthropic.com` (opt-in) | Anthropic key |
| 8 | **Open Code** | ✅ Supported | `openrouter.ai`, `api.openai.com` (zen paths) | API key |
| 9 | **Trae** | 🔍 Investigating | TBD — see §8 | TBD |
| # | Agent | Status | Hosts intercepted | Auth type |
| --- | ------------------ | ---------------- | ------------------------------------------------------------------ | -------------- |
| 1 | **Antigravity** | ✅ Supported | `daily-cloudcode-pa.googleapis.com`, `cloudcode-pa.googleapis.com` | Firebase OAuth |
| 2 | **Kiro (AWS)** | ✅ Supported | `prod.kiro.aws`, `dev.kiro.aws` | AWS SigV4 |
| 3 | **GitHub Copilot** | ✅ Supported | `api.githubcopilot.com`, `copilot-proxy.githubusercontent.com` | GitHub OAuth |
| 4 | **OpenAI Codex** | ✅ Supported | `api.openai.com` (Codex paths), `chatgpt.com` | OpenAI key |
| 5 | **Cursor IDE** | ✅ Supported | `api2.cursor.sh`, `api.cursor.sh` | Cursor OAuth |
| 6 | **Zed Industries** | ✅ Supported | `api.zed.dev`, `llm.zed.dev` | Zed OAuth |
| 7 | **Claude Code** | ✅ Supported | `api.anthropic.com` (opt-in) | Anthropic key |
| 8 | **Open Code** | ✅ Supported | `openrouter.ai`, `api.openai.com` (zen paths) | API key |
| 9 | **Trae** | 🔍 Investigating | TBD — see §8 | TBD |
### Setup wizard steps (per agent)
@@ -342,7 +347,7 @@ Each agent card has a 3-step setup wizard:
For agents 18, AgentBridge attempts to auto-detect IDE installation:
```ts
export async function detectAgent(agentId: AgentId): Promise<DetectionResult>
export async function detectAgent(agentId: AgentId): Promise<DetectionResult>;
// Returns: { installed: boolean, version?: string, path?: string }
```
@@ -354,10 +359,10 @@ Detection uses OS-specific paths and binary checks (e.g., `code --list-extension
### Hard Rules applied
| Rule | Application |
|------|-------------|
| **#12** `sanitizeErrorMessage` | All handler errors are sanitized before response or buffer entry |
| **#13** Shell env-passing | `/etc/hosts` edits use `env` option — no string interpolation of paths |
| Rule | Application |
| --------------------------------- | ---------------------------------------------------------------------------------------- |
| **#12** `sanitizeErrorMessage` | All handler errors are sanitized before response or buffer entry |
| **#13** Shell env-passing | `/etc/hosts` edits use `env` option — no string interpolation of paths |
| **#15 + #17** `isLocalOnlyPath()` | `/api/tools/agent-bridge/` is LOCAL_ONLY + SPAWN_CAPABLE — loopback enforced before auth |
### Bypass list for sensitive hosts
@@ -365,6 +370,7 @@ Detection uses OS-specific paths and binary checks (e.g., `code --list-extension
The bypass list ensures that financial institutions, OAuth/SSO providers, and other sensitive hosts are **never decrypted**. Their TLS traffic passes through as a transparent TCP tunnel — OmniRoute never sees the plaintext.
Default bypass patterns include:
- `*.bank.*`, `*.gov.*` (financial/government)
- `*.okta.com`, `*.auth0.com`, `*.microsoft.com` (SSO/identity)
- `*.apple.com`, `*.icloud.com` (Apple system services)
@@ -374,6 +380,7 @@ User-added bypass patterns are stored in `agent_bridge_bypass` table and take pr
### Secret masking
`maskSecrets()` from `src/mitm/maskSecrets.ts` is applied:
- On every request body before `TrafficBuffer.push()`
- On every header before logging or broadcasting
@@ -434,11 +441,13 @@ variant fails under the same setup.
### DNS not propagated
Check that `/etc/hosts` was updated:
```bash
grep "omniroute\|127.0.0.1.*github\|127.0.0.1.*cursor" /etc/hosts
```
Flush DNS cache:
```bash
# macOS
sudo dscacheutil -flushcache && sudo killall -HUP mDNSResponder
@@ -451,12 +460,14 @@ ipconfig /flushdns
### IDE not detected
Auto-detection uses common installation paths. If detection fails but the IDE is installed:
- Check if the IDE binary is in a non-standard location
- The Setup Wizard still works — detection failure just means the badge won't show the install path
### Handler errors (upstream fetch fails)
If AgentBridge intercepts but all requests fail:
1. Verify at least one provider is connected at `/dashboard/providers`
2. Check OmniRoute server logs: `APP_LOG_LEVEL=debug` in `.env`
3. Verify `OMNIROUTE_BASE_URL` points to the correct router endpoint (default: `http://127.0.0.1:20128`)
@@ -469,33 +480,33 @@ All routes are `LOCAL_ONLY` (loopback-only, enforced before auth) and `SPAWN_CAP
Base path: `/api/tools/agent-bridge/`
| Method | Path | Description |
|--------|------|-------------|
| GET | `/api/tools/agent-bridge/state` | Global server state + per-agent detection/status |
| GET | `/api/tools/agent-bridge/agents` | List registered agents (id, name, hosts, viability, state) |
| GET | `/api/tools/agent-bridge/agents/{id}` | State of one agent (target config + detection + stored state) |
| PATCH | `/api/tools/agent-bridge/agents/{id}` | Update `setup_completed` for agent |
| GET | `/api/tools/agent-bridge/agents/{id}/detect` | Run detection probe for agent (`installed`, `version?`, `path?`) |
| POST | `/api/tools/agent-bridge/agents/{id}/dns` | Enable/disable DNS for agent (`{enabled: boolean}`) |
| GET | `/api/tools/agent-bridge/agents/{id}/mappings` | Model mappings for agent |
| PUT | `/api/tools/agent-bridge/agents/{id}/mappings` | Replace model mappings |
| POST | `/api/tools/agent-bridge/server` | Start/stop/restart server (`action: "start"\|"stop"\|"restart"\|"trust-cert"\|"regenerate-cert"`) |
| GET | `/api/tools/agent-bridge/cert` | Cert status (`exists`, `trusted`, `path`) |
| POST | `/api/tools/agent-bridge/cert` | Trust (install) the MITM root CA |
| DELETE | `/api/tools/agent-bridge/cert` | Untrust (remove) the MITM root CA — idempotent (see §3.6) |
| POST | `/api/tools/agent-bridge/cert/regenerate` | Regenerate the self-signed MITM cert |
| GET | `/api/tools/agent-bridge/cert/download` | Stream the PEM cert for download |
| GET | `/api/tools/agent-bridge/bypass` | List bypass patterns (`default` + `user`) |
| POST | `/api/tools/agent-bridge/bypass` | Replace user-defined bypass patterns wholesale |
| DELETE | `/api/tools/agent-bridge/bypass?pattern=...` | Remove a single user-defined bypass pattern |
| GET | `/api/tools/agent-bridge/diagnose` | Capture-pipeline self-test (see §3.6) |
| POST | `/api/tools/agent-bridge/repair` | Undo orphaned MITM system state (see §3.6) |
| GET | `/api/tools/agent-bridge/config` | Export portable config JSON (see §3.7) |
| POST | `/api/tools/agent-bridge/config` | Import portable config JSON (see §3.7) |
| GET | `/api/tools/agent-bridge/upstream-ca` | Get configured upstream CA path |
| POST | `/api/tools/agent-bridge/upstream-ca` | Validate + persist upstream CA path |
| POST | `/api/tools/agent-bridge/upstream-ca/test` | Validate-only (dry-run) an upstream CA path — does not persist |
| GET / POST / DELETE | `/api/tools/agent-bridge/tproxy` | TPROXY transparent-decrypt capture mode — see [`docs/security/MITM-TPROXY-DECRYPT.md`](../security/MITM-TPROXY-DECRYPT.md) |
| Method | Path | Description |
| ------------------- | ---------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
| GET | `/api/tools/agent-bridge/state` | Global server state + per-agent detection/status |
| GET | `/api/tools/agent-bridge/agents` | List registered agents (id, name, hosts, viability, state) |
| GET | `/api/tools/agent-bridge/agents/{id}` | State of one agent (target config + detection + stored state) |
| PATCH | `/api/tools/agent-bridge/agents/{id}` | Update `setup_completed` for agent |
| GET | `/api/tools/agent-bridge/agents/{id}/detect` | Run detection probe for agent (`installed`, `version?`, `path?`) |
| POST | `/api/tools/agent-bridge/agents/{id}/dns` | Enable/disable DNS for agent (`{enabled: boolean}`) |
| GET | `/api/tools/agent-bridge/agents/{id}/mappings` | Model mappings for agent |
| PUT | `/api/tools/agent-bridge/agents/{id}/mappings` | Replace model mappings |
| POST | `/api/tools/agent-bridge/server` | Start/stop/restart server (`action: "start"\|"stop"\|"restart"\|"trust-cert"\|"regenerate-cert"`) |
| GET | `/api/tools/agent-bridge/cert` | Cert status (`exists`, `trusted`, `path`) |
| POST | `/api/tools/agent-bridge/cert` | Trust (install) the MITM root CA |
| DELETE | `/api/tools/agent-bridge/cert` | Untrust (remove) the MITM root CA — idempotent (see §3.6) |
| POST | `/api/tools/agent-bridge/cert/regenerate` | Regenerate the self-signed MITM cert |
| GET | `/api/tools/agent-bridge/cert/download` | Stream the PEM cert for download |
| GET | `/api/tools/agent-bridge/bypass` | List bypass patterns (`default` + `user`) |
| POST | `/api/tools/agent-bridge/bypass` | Replace user-defined bypass patterns wholesale |
| DELETE | `/api/tools/agent-bridge/bypass?pattern=...` | Remove a single user-defined bypass pattern |
| GET | `/api/tools/agent-bridge/diagnose` | Capture-pipeline self-test (see §3.6) |
| POST | `/api/tools/agent-bridge/repair` | Undo orphaned MITM system state (see §3.6) |
| GET | `/api/tools/agent-bridge/config` | Export portable config JSON (see §3.7) |
| POST | `/api/tools/agent-bridge/config` | Import portable config JSON (see §3.7) |
| GET | `/api/tools/agent-bridge/upstream-ca` | Get configured upstream CA path |
| POST | `/api/tools/agent-bridge/upstream-ca` | Validate + persist upstream CA path |
| POST | `/api/tools/agent-bridge/upstream-ca/test` | Validate-only (dry-run) an upstream CA path — does not persist |
| GET / POST / DELETE | `/api/tools/agent-bridge/tproxy` | TPROXY transparent-decrypt capture mode — see [`docs/security/MITM-TPROXY-DECRYPT.md`](../security/MITM-TPROXY-DECRYPT.md) |
Full OpenAPI schemas: `docs/openapi.yaml` → tag `AgentBridge`.

View File

@@ -1,13 +1,13 @@
---
title: "Agent Protocols Guide"
version: 3.8.2
lastUpdated: 2026-05-13
version: 3.8.40
lastUpdated: 2026-06-28
---
# Agent Protocols Guide
> **Source:** `src/lib/{a2a,acp,cloudAgent}/`, `src/app/api/{a2a,acp,cloud}/`, `src/app/api/v1/agents/`
> **Last updated:** 2026-05-13 — v3.8.0
> **Last updated:** 2026-06-28 — v3.8.40
OmniRoute exposes three different agent-related surfaces. They look similar at first glance but solve different problems. Use this page to pick the right one.
@@ -17,7 +17,7 @@ OmniRoute exposes three different agent-related surfaces. They look similar at f
| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------- | -------------------- |
| **A2A — Agent-to-Agent** | Cross-agent collaboration with peer agents that speak the A2A protocol | JSON-RPC 2.0 over HTTP | A2A v0.3 (open spec) |
| **ACP — CLI Agents Registry** | Detecting / registering / launching CLI coding agents installed on the user's machine (Cursor, Cline, Codex CLI, Claude Code, Aider, etc.) | HTTP REST | OmniRoute-specific |
| **Cloud Agents** | Submitting long-running coding tasks to external cloud services (Codex Cloud, Devin, Jules) | HTTP REST + DB-backed tasks | OmniRoute-specific |
| **Cloud Agents** | Submitting long-running coding tasks to external cloud services (Codex Cloud, Devin, Jules, Cursor Cloud) | HTTP REST + DB-backed tasks | OmniRoute-specific |
The three are independent — pick any subset.
@@ -56,13 +56,14 @@ Do you need a cloud service to do work outside this machine (Codex Cloud / Devin
- `tasks/get` — read task by ID
- `tasks/cancel` — cancel a running task
### Built-in skills (5)
### Built-in skills (6)
- `smart-routing` — route a prompt through the optimal combo
- `quota-management` — report per-provider quota state
- `provider-discovery` — list installed providers with capabilities
- `cost-analysis` — estimate cost of a request/conversation
- `health-report` — aggregate breaker/cooldown/lockout state per provider
- `list-capabilities` — enumerate the agent's available skills and metadata
### Deep dive

View File

@@ -1,13 +1,13 @@
---
title: "Cloud Agents"
version: 3.8.31
lastUpdated: 2026-06-20
version: 3.8.40
lastUpdated: 2026-06-28
---
# Cloud Agents
> **Source of truth:** `src/lib/cloudAgent/` and `src/app/api/v1/agents/tasks/`
> **Last updated:** 2026-06-20 — v3.8.31 (frontmatter refresh; 4 agents incl. cursor-cloud)
> **Last updated:** 2026-06-28 — v3.8.40 (frontmatter refresh; 4 agents incl. cursor-cloud)
OmniRoute orchestrates third-party cloud-hosted coding agents (Codex Cloud, Cursor,
Devin, Jules) as long-running tasks. Each agent is wrapped behind a uniform interface so
@@ -24,11 +24,11 @@ artifact, and supports follow-up messages and (in some providers) plan approval
## Supported Agents
| Provider ID | Class | Source | Upstream Base URL | Plan Approval |
| ------------- | ----------------- | ------------------------------------ | --------------------------------------- | ------------- |
| `jules` | `JulesAgent` | `src/lib/cloudAgent/agents/jules.ts` | `https://jules.googleapis.com/v1alpha` | Yes |
| `devin` | `DevinAgent` | `src/lib/cloudAgent/agents/devin.ts` | `https://api.devin.ai/v1` | Yes |
| `codex-cloud` | `CodexCloudAgent` | `src/lib/cloudAgent/agents/codex.ts` | `https://api.openai.com/v1/codex/cloud` | No (auto) |
| Provider ID | Class | Source | Upstream Base URL | Plan Approval |
| -------------- | ------------------ | ------------------------------------- | --------------------------------------- | ------------- |
| `jules` | `JulesAgent` | `src/lib/cloudAgent/agents/jules.ts` | `https://jules.googleapis.com/v1alpha` | Yes |
| `devin` | `DevinAgent` | `src/lib/cloudAgent/agents/devin.ts` | `https://api.devin.ai/v1` | Yes |
| `codex-cloud` | `CodexCloudAgent` | `src/lib/cloudAgent/agents/codex.ts` | `https://api.openai.com/v1/codex/cloud` | No (auto) |
| `cursor-cloud` | `CursorCloudAgent` | `src/lib/cloudAgent/agents/cursor.ts` | `https://api.cursor.com/v0` | No (auto) |
Registry: `src/lib/cloudAgent/registry.ts` — exports `getAgent(providerId)`,

View File

@@ -6,7 +6,7 @@ description: "Reference for 9Router and CLIProxyAPI"
# Embedded Services
> **Version:** v3.8.4
> **Last updated:** 2026-05-25
> **Last updated:** 2026-06-28
> **Audience:** Engineers adding, maintaining, or debugging embedded services (9Router, CLIProxyAPI).
Embedded services are locally-installed process sidecar tools that OmniRoute installs, supervises, and

View File

@@ -1,13 +1,13 @@
---
title: "Evaluations (Evals)"
version: 3.8.2
lastUpdated: 2026-05-13
version: 3.8.40
lastUpdated: 2026-06-28
---
# Evaluations (Evals)
> **Source of truth:** `src/lib/evals/`, `src/lib/db/evals.ts`, `src/app/api/evals/`
> **Last updated:** 2026-05-13 — v3.8.0
> **Last updated:** 2026-06-28 — v3.8.40
OmniRoute ships a generic evaluation framework you can use to benchmark routing
configurations, single providers/models, or the bundled "golden set" suites.

View File

@@ -1,13 +1,13 @@
---
title: "Gamification & Leaderboard System"
version: 3.8.2
lastUpdated: 2026-05-19
version: 3.8.40
lastUpdated: 2026-06-28
---
# Gamification & Leaderboard System
> **Source of truth:** `src/lib/gamification/`, `src/lib/db/gamification.ts`, `src/app/api/gamification/`
> **Last updated:** 2026-05-19 — v3.8.0
> **Last updated:** 2026-06-28 — v3.8.40
OmniRoute includes a local-first gamification layer that rewards users for
engaging with the platform — making requests, switching providers, creating

View File

@@ -1,18 +1,18 @@
---
title: "OmniRoute MCP Server Documentation"
version: 3.8.31
lastUpdated: 2026-06-20
version: 3.8.40
lastUpdated: 2026-06-28
---
# OmniRoute MCP Server Documentation
> Model Context Protocol server with 87 tools across routing, cache, compression, memory, skills, proxy, and context source operations.
> Model Context Protocol server with 94 tools across routing, cache, compression, memory, skills, proxy, pool, and context source operations.
>
> Source of truth: `open-sse/mcp-server/schemas/tools.ts` (33 base) + `memoryTools.ts` (3) + `skillTools.ts` (4) + `agentSkillTools.ts` (3) + `gamificationTools.ts` (8) + `pluginTools.ts` (8) + `notionTools.ts` (6) + `obsidianTools.ts` (22) = **87** (`TOTAL_MCP_TOOL_COUNT`). Tool registration and scope wiring lives in `open-sse/mcp-server/server.ts`.
> Source of truth: `open-sse/mcp-server/schemas/tools.ts` (34 base) + `memoryTools.ts` (3) + `skillTools.ts` (4) + `agentSkillTools.ts` (3) + `poolTools.ts` (6) + `gamificationTools.ts` (8) + `pluginTools.ts` (8) + `notionTools.ts` (6) + `obsidianTools.ts` (22) = **94** (`TOTAL_MCP_TOOL_COUNT`). Tool registration and scope wiring lives in `open-sse/mcp-server/server.ts`.
![MCP tool inventory (87 tools by category)](../diagrams/exported/mcp-tools-87.svg)
![MCP tool inventory (94 tools by category)](../diagrams/exported/mcp-tools-94.svg)
> Source: [diagrams/mcp-tools-87.mmd](../diagrams/mcp-tools-87.mmd) (regenerate via `npm run docs:render-diagrams`).
> Source: [diagrams/mcp-tools-94.mmd](../diagrams/mcp-tools-94.mmd) (regenerate via `npm run docs:render-diagrams`).
## Installation
@@ -194,14 +194,14 @@ curl http://localhost:20128/api/settings/notion
curl -X DELETE http://localhost:20128/api/settings/notion
```
| Tool | Scopes | Description |
| :-------------------------------- | :------------- | :------------------------------------------------------------- |
| `omniroute_notion_search` | `read:notion` | Full-text search across all pages and databases |
| `omniroute_notion_list_databases` | `read:notion` | List all accessible databases with schema metadata |
| `omniroute_notion_get_database` | `read:notion` | Get database schema by ID |
| `omniroute_notion_query_database` | `read:notion` | Query a database with filters, sorts, and pagination |
| `omniroute_notion_read` | `read:notion` | Read a page or block by ID with its content |
| `omniroute_notion_append_blocks` | `write:notion` | Append children blocks to a parent block (max 100 per request) |
| Tool | Scopes | Description |
| :--------------------------- | :------------- | :------------------------------------------------------------- |
| `notion_search` | `read:notion` | Full-text search across all pages and databases |
| `notion_get_page` | `read:notion` | Get a page by ID with its properties |
| `notion_list_block_children` | `read:notion` | List the child blocks of a page or block |
| `notion_query_database` | `read:notion` | Query a database with filters, sorts, and pagination |
| `notion_get_database` | `read:notion` | Get database schema by ID |
| `notion_append_blocks` | `write:notion` | Append children blocks to a parent block (max 100 per request) |
## Agent Skill Catalog Tools (3)
@@ -217,7 +217,7 @@ See [AGENT-SKILLS.md](./AGENT-SKILLS.md) for the full catalog and how external a
## Related Frameworks (v3.8.0)
The MCP tool inventory above (87 tools = 33 core + 3 memory + 4 skills + 3 agent-skills + 8 gamification + 8 plugins + 6 notion + 22 obsidian) is intentionally
The MCP tool inventory above (94 tools = 34 core + 3 memory + 4 skills + 3 agent-skills + 6 pool + 8 gamification + 8 plugins + 6 notion + 22 obsidian) is intentionally
scoped to runtime routing/cache/compression/memory/skills/proxy/context-source operations. Two adjacent
frameworks ship alongside the MCP server in v3.8.0 and are documented separately:
@@ -329,9 +329,9 @@ MCP tool, prompt, and resource registries can compress descriptions at registrat
## Tool Cardinality Reduction (F4.3)
Description compression shrinks each tool's metadata; **tool-cardinality reduction** goes one step further by reducing *how many* tools are announced at all. Advertising fewer tools in the `tools/list` manifest cuts the per-request token cost the client's model pays for the tool catalog ("layer 5" compression). The implementation is a pure, stateless filter in `open-sse/mcp-server/toolCardinality.ts` (`reduceToolManifest`), wired into the registration loop in `createMcpServer()` (`open-sse/mcp-server/server.ts`).
Description compression shrinks each tool's metadata; **tool-cardinality reduction** goes one step further by reducing _how many_ tools are announced at all. Advertising fewer tools in the `tools/list` manifest cuts the per-request token cost the client's model pays for the tool catalog ("layer 5" compression). The implementation is a pure, stateless filter in `open-sse/mcp-server/toolCardinality.ts` (`reduceToolManifest`), wired into the registration loop in `createMcpServer()` (`open-sse/mcp-server/server.ts`).
**Opt-in, off by default.** The filter only runs when at least one of two environment variables is set; with neither set, all 87 tools are announced unchanged.
**Opt-in, off by default.** The filter only runs when at least one of two environment variables is set; with neither set, all 94 tools are announced unchanged.
| Variable | Mode |
| :--------------- | :-------------------------------------------------------------------------------------- |
@@ -398,7 +398,7 @@ Use the dashboard or the `/api/mcp/audit` and `/api/mcp/audit/stats` REST endpoi
| `open-sse/mcp-server/audit.ts` | Tool call audit logging (`mcp_tool_audit`) |
| `open-sse/mcp-server/runtimeHeartbeat.ts` | stdio heartbeat writer (`mcp-heartbeat.json`) |
| `open-sse/mcp-server/descriptionCompressor.ts` | Description compression for tool / prompt / resource registries |
| `open-sse/mcp-server/schemas/tools.ts` | Zod schemas + tool registry (`MCP_TOOLS`, 30 entries) |
| `open-sse/mcp-server/schemas/tools.ts` | Zod schemas + tool registry (`MCP_TOOLS`, 34 entries) |
| `open-sse/mcp-server/tools/advancedTools.ts` | Phase 2 + cache + 1proxy tool handlers |
| `open-sse/mcp-server/tools/compressionTools.ts` | Compression tool handlers |
| `open-sse/mcp-server/tools/memoryTools.ts` | Memory tool definitions (3 tools) |

View File

@@ -1,13 +1,13 @@
---
title: "Memory System"
version: 3.8.31
lastUpdated: 2026-06-20
version: 3.8.40
lastUpdated: 2026-06-28
---
# Memory System
> **Source of truth:** `src/lib/memory/` and `src/app/api/memory/`
> **Last updated:** 2026-06-20 — v3.8.31 (off-by-default + int8 quantization catch-up)
> **Last updated:** 2026-06-28 — v3.8.40 (off-by-default + int8 quantization catch-up)
OmniRoute provides persistent conversational memory keyed by API key (and
optionally session id). Memories are extracted automatically from LLM responses
@@ -83,6 +83,7 @@ infrastructure and settings. Three tiers exist, applied in priority order:
```
Degradation is automatic and transparent:
- If sqlite-vec fails to load, tier 1 is unavailable → falls back to tier 0.
- If embedding source returns an error, tier 1 falls back to tier 0.
- If Qdrant is unhealthy, tier 2 falls back to tier 1 (or tier 0 if tier 1
@@ -93,14 +94,15 @@ Degradation is automatic and transparent:
The embedding layer (`src/lib/memory/embedding/`) resolves which source to use
based on `MemorySettingsExtended.embeddingSource`:
| Source | Description | Key required | Cold start |
| -------------- | ------------------------------------------------------------------------------- | ------------ | ---------- |
| `remote` | Uses a configured provider's embedding API (OpenAI, Cohere, etc.) | Yes | None |
| `static` | Local lookup-table embedding via `potion-base-8M` (WordPiece + mean pooling) | No | ~200ms |
| `transformers` | Local ONNX inference via `@huggingface/transformers` v4, `all-MiniLM-L6-v2` | No | ~3s + ~400MB RAM |
| `auto` | Runtime resolution: remote (if key exists) → static → transformers → null | Depends | Depends |
| Source | Description | Key required | Cold start |
| -------------- | ---------------------------------------------------------------------------- | ------------ | ---------------- |
| `remote` | Uses a configured provider's embedding API (OpenAI, Cohere, etc.) | Yes | None |
| `static` | Local lookup-table embedding via `potion-base-8M` (WordPiece + mean pooling) | No | ~200ms |
| `transformers` | Local ONNX inference via `@huggingface/transformers` v4, `all-MiniLM-L6-v2` | No | ~3s + ~400MB RAM |
| `auto` | Runtime resolution: remote (if key exists) → static → transformers → null | Depends | Depends |
**Resolution order for `auto`:**
1. Find first provider in `listEmbeddingProviders()` with `hasKey === true``remote`.
2. If `settings.staticEnabled === true``static`.
3. If `settings.transformersEnabled === true``transformers`.
@@ -123,6 +125,7 @@ RRF(d) = Σ 1 / (k + rank_i(d)) where k = 60 (configurable via MEMORY_RRF_
```
Concretely:
1. Run FTS5 search → ranked list `R_fts` (position 1..N).
2. Run KNN vector search → ranked list `R_vec` (position 1..M).
3. For each unique `memoryId`:
@@ -150,6 +153,7 @@ amortizes the backfill cost across real requests without blocking startup.
`GET /api/memory/engine-status` (`vectorStore.needsReindex`).
The `memory_vec_meta` table (migration `073_memory_vec.sql`) stores:
- `active_dim` — current vector dimension (null = not yet calibrated).
- `embedding_signature``${source}:${model}:${dim}` used to detect changes.
- `last_reset_at` — timestamp of last full reset.
@@ -160,15 +164,15 @@ The `memory_vec_meta` table (migration `073_memory_vec.sql`) stores:
Seven new fields were added to `MemorySettingsExtended` (plan 21, D9) in
`src/shared/schemas/memory.ts`, persisted via `src/lib/db/settings.ts`:
| Field | Type | Default | Description |
| ---------------------- | --------------------------------------------- | ------------ | --------------------------------------------- |
| `embeddingSource` | `"remote" \| "static" \| "transformers" \| "auto"` | `"auto"` | Which embedding source to use |
| `embeddingProviderModel` | `string \| null` | `null` | Provider/model in `provider/model` format |
| `transformersEnabled` | `boolean` | `false` | Opt-in for Transformers.js (MiniLM, ~400MB) |
| `staticEnabled` | `boolean` | `false` | Opt-in for static potion-base-8M local model |
| `rerankEnabled` | `boolean` | `false` | Enable reranking step (adds +200-500ms/req) |
| `rerankProviderModel` | `string \| null` | `null` | Rerank provider/model in `provider/model` format |
| `vectorStore` | `"sqlite-vec" \| "qdrant" \| "auto"` | `"auto"` | Which vector backend to use |
| Field | Type | Default | Description |
| ------------------------ | -------------------------------------------------- | -------- | ------------------------------------------------ |
| `embeddingSource` | `"remote" \| "static" \| "transformers" \| "auto"` | `"auto"` | Which embedding source to use |
| `embeddingProviderModel` | `string \| null` | `null` | Provider/model in `provider/model` format |
| `transformersEnabled` | `boolean` | `false` | Opt-in for Transformers.js (MiniLM, ~400MB) |
| `staticEnabled` | `boolean` | `false` | Opt-in for static potion-base-8M local model |
| `rerankEnabled` | `boolean` | `false` | Enable reranking step (adds +200-500ms/req) |
| `rerankProviderModel` | `string \| null` | `null` | Rerank provider/model in `provider/model` format |
| `vectorStore` | `"sqlite-vec" \| "qdrant" \| "auto"` | `"auto"` | Which vector backend to use |
These are exposed via `GET /PUT /api/settings/memory` (schema `MemorySettingsExtendedSchema`).
@@ -239,13 +243,13 @@ The settings UI exposes Qdrant config, health check, semantic search test,
and cleanup in the **Engine tab** of `/dashboard/memory`. The corresponding
routes under `src/app/api/settings/qdrant/` are all wired as of v3.8.6:
| Route | Method | Description |
| ----- | ------ | ----------- |
| `/api/settings/qdrant` | `GET` / `PUT` | Read / update Qdrant settings |
| `/api/settings/qdrant/health` | `GET` | Liveness probe + latency |
| `/api/settings/qdrant/search` | `POST` | Semantic search test |
| `/api/settings/qdrant/cleanup` | `POST` | Remove expired / old points |
| `/api/settings/qdrant/embedding-models` | `GET` | List available embedding models |
| Route | Method | Description |
| --------------------------------------- | ------------- | ------------------------------- |
| `/api/settings/qdrant` | `GET` / `PUT` | Read / update Qdrant settings |
| `/api/settings/qdrant/health` | `GET` | Liveness probe + latency |
| `/api/settings/qdrant/search` | `POST` | Semantic search test |
| `/api/settings/qdrant/cleanup` | `POST` | Remove expired / old points |
| `/api/settings/qdrant/embedding-models` | `GET` | List available embedding models |
### Vector quantization (int8 — opt-in, both backends)
@@ -254,10 +258,10 @@ footprint of stored vectors (~4× smaller than Float32) at a small recall cost.
Default is **off** on both — vectors stay full-precision unless explicitly
enabled.
| Backend | Setting | Type | Default | Where read |
| ------------ | -------------------------------- | ----------------------------- | -------- | --------------------------------------------------- |
| Qdrant | `qdrantQuantization` (DB key) | `"none" \| "int8" \| "binary"` | `"none"` | `src/lib/memory/qdrant.ts::normalizeQdrantConfig()` |
| sqlite-vec | `MEMORY_VEC_QUANTIZATION` (env) | `"none" \| "int8"` | `"none"` | `src/lib/memory/vectorStore.ts::requestedVecQuantization()` |
| Backend | Setting | Type | Default | Where read |
| ---------- | ------------------------------- | ------------------------------ | -------- | ----------------------------------------------------------- |
| Qdrant | `qdrantQuantization` (DB key) | `"none" \| "int8" \| "binary"` | `"none"` | `src/lib/memory/qdrant.ts::normalizeQdrantConfig()` |
| sqlite-vec | `MEMORY_VEC_QUANTIZATION` (env) | `"none" \| "int8"` | `"none"` | `src/lib/memory/vectorStore.ts::requestedVecQuantization()` |
- **Qdrant** is configured per-instance via the `qdrantQuantization` setting
key (exposed as the `quantization` field on `PUT /api/settings/qdrant`). When
@@ -381,15 +385,15 @@ strategy via `toMemoryRetrievalConfig()` (chronological order).
See also the "Settings extension" section above for field descriptions.
| DB key | API field | Default |
| ------------------------- | ---------------------- | ------------- |
| `memoryEmbeddingSource` | `embeddingSource` | `"auto"` |
| `memoryEmbeddingModel` | `embeddingProviderModel` | `null` |
| `memoryTransformersEnabled` | `transformersEnabled` | `false` |
| `memoryStaticEnabled` | `staticEnabled` | `false` |
| `memoryRerankEnabled` | `rerankEnabled` | `false` |
| `memoryRerankModel` | `rerankProviderModel` | `null` |
| `memoryVectorStore` | `vectorStore` | `"auto"` |
| DB key | API field | Default |
| --------------------------- | ------------------------ | -------- |
| `memoryEmbeddingSource` | `embeddingSource` | `"auto"` |
| `memoryEmbeddingModel` | `embeddingProviderModel` | `null` |
| `memoryTransformersEnabled` | `transformersEnabled` | `false` |
| `memoryStaticEnabled` | `staticEnabled` | `false` |
| `memoryRerankEnabled` | `rerankEnabled` | `false` |
| `memoryRerankModel` | `rerankProviderModel` | `null` |
| `memoryVectorStore` | `vectorStore` | `"auto"` |
Qdrant-related DB keys (`qdrantEnabled`, `qdrantHost`, `qdrantPort`,
`qdrantApiKey`, `qdrantCollection` default `"omniroute_memory"`,
@@ -400,16 +404,16 @@ Qdrant-related DB keys (`qdrantEnabled`, `qdrantHost`, `qdrantPort`,
Six optional env vars tune the engine's runtime behaviour (documented in `.env.example`):
| Variable | Default | Description |
| ------------------------------- | ------- | -------------------------------------------------- |
| `MEMORY_EMBEDDING_CACHE_TTL_MS` | `300000` | Embedding cache TTL (5 min) |
| `MEMORY_EMBEDDING_CACHE_MAX` | `1000` | Max entries in embedding LRU cache |
| `MEMORY_TRANSFORMERS_MODEL` | `Xenova/all-MiniLM-L6-v2` | HF repo for Transformers.js model |
| `MEMORY_STATIC_MODEL` | `minishlab/potion-base-8M` | HF repo for static potion model |
| `MEMORY_STATIC_CACHE_DIR` | `<DATA_DIR>/embeddings` | Where to store downloaded models |
| `MEMORY_VEC_TOP_K` | `20` | Default top-K for vector search |
| `MEMORY_RRF_K` | `60` | RRF k constant for hybrid search |
| `MEMORY_VEC_QUANTIZATION` | `none` | Set to `int8` to store local sqlite-vec vectors quantized (~4× smaller; opt-in). Mode change forces a reindex. |
| Variable | Default | Description |
| ------------------------------- | -------------------------- | -------------------------------------------------------------------------------------------------------------- |
| `MEMORY_EMBEDDING_CACHE_TTL_MS` | `300000` | Embedding cache TTL (5 min) |
| `MEMORY_EMBEDDING_CACHE_MAX` | `1000` | Max entries in embedding LRU cache |
| `MEMORY_TRANSFORMERS_MODEL` | `Xenova/all-MiniLM-L6-v2` | HF repo for Transformers.js model |
| `MEMORY_STATIC_MODEL` | `minishlab/potion-base-8M` | HF repo for static potion model |
| `MEMORY_STATIC_CACHE_DIR` | `<DATA_DIR>/embeddings` | Where to store downloaded models |
| `MEMORY_VEC_TOP_K` | `20` | Default top-K for vector search |
| `MEMORY_RRF_K` | `60` | RRF k constant for hybrid search |
| `MEMORY_VEC_QUANTIZATION` | `none` | Set to `int8` to store local sqlite-vec vectors quantized (~4× smaller; opt-in). Mode change forces a reindex. |
## Summarisation (`summarization.ts`)
@@ -431,37 +435,37 @@ All endpoints require management auth (`requireManagementAuth`).
### Core memory endpoints (existing + updated)
| Method | Path | Description |
| ---------- | --------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `GET` | `/api/memory` | Paginated list with filters: `apiKeyId`, `type`, `sessionId`, `q`, `limit`, `page`, `offset`. Response includes `stats.total`, `stats.tokensUsed`, `stats.hitRate`, `cacheStats` |
| `POST` | `/api/memory` | Create entry (Zod-validated: `content`, `key`, optional `type`, `sessionId`, `apiKeyId`, `metadata`, `expiresAt`). Calls `createMemory()` which upserts on `(apiKeyId, key)` |
| `GET` | `/api/memory/[id]` | Fetch a single entry by UUID |
| `PUT` | `/api/memory/[id]` | Update entry fields (`type`, `key`, `content`, `metadata`). Body: `MemoryUpdatePutSchema`. Also syncs vector if embedding source available. |
| `DELETE` | `/api/memory/[id]` | Delete an entry; also deletes from `vec_memories` (D15) and Qdrant best-effort. Returns 404 when missing. |
| `GET` | `/api/memory/health` | Runs `verifyExtractionPipeline("health-check")` — round-trip create→list→delete. Returns `{working, latencyMs, error?}` |
| Method | Path | Description |
| -------- | -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `GET` | `/api/memory` | Paginated list with filters: `apiKeyId`, `type`, `sessionId`, `q`, `limit`, `page`, `offset`. Response includes `stats.total`, `stats.tokensUsed`, `stats.hitRate`, `cacheStats` |
| `POST` | `/api/memory` | Create entry (Zod-validated: `content`, `key`, optional `type`, `sessionId`, `apiKeyId`, `metadata`, `expiresAt`). Calls `createMemory()` which upserts on `(apiKeyId, key)` |
| `GET` | `/api/memory/[id]` | Fetch a single entry by UUID |
| `PUT` | `/api/memory/[id]` | Update entry fields (`type`, `key`, `content`, `metadata`). Body: `MemoryUpdatePutSchema`. Also syncs vector if embedding source available. |
| `DELETE` | `/api/memory/[id]` | Delete an entry; also deletes from `vec_memories` (D15) and Qdrant best-effort. Returns 404 when missing. |
| `GET` | `/api/memory/health` | Runs `verifyExtractionPipeline("health-check")` — round-trip create→list→delete. Returns `{working, latencyMs, error?}` |
### New memory engine endpoints (plan 21)
| Method | Path | Description |
| -------- | ---------------------------------- | --------------------------------------------------------------------------------------- |
| `POST` | `/api/memory/retrieve-preview` | Dry-run of `retrieveMemories` — returns ranked results with score, tier, tokens. Body: `RetrievePreviewSchema`. Does NOT inject or modify memories. |
| `GET` | `/api/memory/embedding-providers` | Lists providers with embedding models, indicating which have a configured API key. |
| `GET` | `/api/memory/engine-status` | Returns full engine status: keyword tier, embedding resolution, vector store stats, Qdrant health, rerank config. Shape: `MemoryEngineStatusSchema`. |
| `POST` | `/api/memory/summarize` | Manually trigger memory compaction. Body: `MemorySummarizeSchema` (`olderThanDays`, `apiKeyId?`, `dryRun`). Returns `{candidates, tokensSaved}`. |
| `POST` | `/api/memory/reindex` | Trigger vector reindex for memories with `needs_reindex=1`. Body: `MemoryReindexSchema` (`force`). Returns `{started, pending}`. |
| Method | Path | Description |
| ------ | --------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| `POST` | `/api/memory/retrieve-preview` | Dry-run of `retrieveMemories` — returns ranked results with score, tier, tokens. Body: `RetrievePreviewSchema`. Does NOT inject or modify memories. |
| `GET` | `/api/memory/embedding-providers` | Lists providers with embedding models, indicating which have a configured API key. |
| `GET` | `/api/memory/engine-status` | Returns full engine status: keyword tier, embedding resolution, vector store stats, Qdrant health, rerank config. Shape: `MemoryEngineStatusSchema`. |
| `POST` | `/api/memory/summarize` | Manually trigger memory compaction. Body: `MemorySummarizeSchema` (`olderThanDays`, `apiKeyId?`, `dryRun`). Returns `{candidates, tokensSaved}`. |
| `POST` | `/api/memory/reindex` | Trigger vector reindex for memories with `needs_reindex=1`. Body: `MemoryReindexSchema` (`force`). Returns `{started, pending}`. |
### Settings endpoints
| Method | Path | Description |
| -------- | ---------------------------------- | ------------------------------------------------------------------------ |
| `GET` | `/api/settings/memory` | Current normalised `MemorySettingsExtended` (7 new fields + legacy) |
| `PUT` | `/api/settings/memory` | Update any field from `MemorySettingsExtendedSchema` (12 total fields) |
| `GET` | `/api/settings/qdrant` | Current Qdrant settings (`QdrantSettingsSchema`) |
| `PUT` | `/api/settings/qdrant` | Update Qdrant settings. Body: `QdrantSettingsUpdateSchema`. `apiKey` = empty string removes key. |
| `GET` | `/api/settings/qdrant/health` | Liveness probe against configured Qdrant instance. Returns `QdrantHealthResultSchema`. |
| `POST` | `/api/settings/qdrant/search` | Semantic search test against Qdrant. Body: `QdrantSearchSchema` (`query`, `topK`). |
| `POST` | `/api/settings/qdrant/cleanup` | Remove Qdrant points for expired / old memories. |
| `GET` | `/api/settings/qdrant/embedding-models` | List embedding models available for Qdrant. |
| Method | Path | Description |
| ------ | --------------------------------------- | ------------------------------------------------------------------------------------------------ |
| `GET` | `/api/settings/memory` | Current normalised `MemorySettingsExtended` (7 new fields + legacy) |
| `PUT` | `/api/settings/memory` | Update any field from `MemorySettingsExtendedSchema` (12 total fields) |
| `GET` | `/api/settings/qdrant` | Current Qdrant settings (`QdrantSettingsSchema`) |
| `PUT` | `/api/settings/qdrant` | Update Qdrant settings. Body: `QdrantSettingsUpdateSchema`. `apiKey` = empty string removes key. |
| `GET` | `/api/settings/qdrant/health` | Liveness probe against configured Qdrant instance. Returns `QdrantHealthResultSchema`. |
| `POST` | `/api/settings/qdrant/search` | Semantic search test against Qdrant. Body: `QdrantSearchSchema` (`query`, `topK`). |
| `POST` | `/api/settings/qdrant/cleanup` | Remove Qdrant points for expired / old memories. |
| `GET` | `/api/settings/qdrant/embedding-models` | List embedding models available for Qdrant. |
The `/api/memory` list query supports either `page`-based pagination
(`parsePaginationParams`) **or** raw `offset` — when `offset` is present it
@@ -490,6 +494,7 @@ See [MCP-SERVER.md](./MCP-SERVER.md) for transport and scope details.
`src/app/(dashboard)/dashboard/memory/page.tsx` is now a **3-tab Studio**:
### Tab: Memórias / Memories
- Concept card (collapsible "How it works" explainer).
- Real-time list, search, and pagination (debounced 300 ms).
- Type filter (`factual` / `episodic` / `procedural` / `semantic` / all).
@@ -503,6 +508,7 @@ See [MCP-SERVER.md](./MCP-SERVER.md) for transport and scope details.
- A green/red health dot driven by `GET /api/memory/health`.
### Tab: Playground
- Query input + strategy selector (Exact / Semantic / Hybrid) + token budget.
- "Simulate" → `POST /api/memory/retrieve-preview` — shows ranked results with
`score`, `tier`, `tokens`, `vecScore`, `ftsScore`.
@@ -510,6 +516,7 @@ See [MCP-SERVER.md](./MCP-SERVER.md) for transport and scope details.
whether a fallback occurred.
### Tab: Engine
- Engine status panel (keyword FTS5 chip, embedding chip, vector store chip,
Qdrant health chip, rerank chip).
- "Reindex Now" button → `POST /api/memory/reindex`.
@@ -574,10 +581,10 @@ default TTL 5 min).
- `src/app/api/memory/reindex/route.ts`
- `src/app/api/settings/memory/route.ts`
- `src/app/api/settings/qdrant/route.ts` + sub-routes
- `src/app/(dashboard)/dashboard/memory/` — Studio UI (page + components +
tabs + hooks)
- `open-sse/handlers/chatCore.ts` (injection / extraction wiring)
- `open-sse/mcp-server/tools/memoryTools.ts`
- `src/app/(dashboard)/dashboard/memory/` — Studio UI (page + components +
tabs + hooks)
- `open-sse/handlers/chatCore.ts` (injection / extraction wiring)
- `open-sse/mcp-server/tools/memoryTools.ts`
---
@@ -587,12 +594,12 @@ OmniRoute's memory engine supports **four embedding sources** (`src/lib/memory/e
### The Four Providers
| Provider | Source | Latency | Cost | Quality | Setup |
|----------|--------|---------|------|---------|-------|
| `transformers` | Local ONNX model (Xenova/all-MiniLM-L6-v2) | ~50-150ms (CPU) | Free | Good | `npm install` only |
| `static` | Pre-computed vectors (cached) | <1ms | Free | N/A (depends on cache hit) | None |
| `remote` | OpenAI / Cohere / Voyage API | ~100-300ms | $0.02-0.10/1M tokens | Excellent | API key |
| `cache` | In-memory LRU layer over any source | <1ms (hit), full latency (miss) | Free | Same as underlying | None |
| Provider | Source | Latency | Cost | Quality | Setup |
| -------------- | ------------------------------------------ | ------------------------------- | -------------------- | -------------------------- | ------------------ |
| `transformers` | Local ONNX model (Xenova/all-MiniLM-L6-v2) | ~50-150ms (CPU) | Free | Good | `npm install` only |
| `static` | Pre-computed vectors (cached) | <1ms | Free | N/A (depends on cache hit) | None |
| `remote` | OpenAI / Cohere / Voyage API | ~100-300ms | $0.02-0.10/1M tokens | Excellent | API key |
| `cache` | In-memory LRU layer over any source | <1ms (hit), full latency (miss) | Free | Same as underlying | None |
### Decision Tree
@@ -643,16 +650,17 @@ The cache is always on by default and configured via env vars:
MEMORY_EMBEDDING_CACHE_MAX=1000 # Max cached items
MEMORY_EMBEDDING_CACHE_TTL_MS=300000 # TTL (5 min)
```
### Performance Numbers
Benchmark on a typical 4-core x86 server (texts ~100 tokens each):
| Provider | p50 | p95 | p99 | Cost / 1M embeddings |
|----------|-----|-----|-----|-----------------------|
| `transformers` (CPU) | 80ms | 180ms | 350ms | Free |
| `remote` (OpenAI) | 120ms | 220ms | 400ms | ~$0.02 (ada-002) / $0.13 (3-large) |
| `static` (Qdrant) | 15ms | 30ms | 60ms | Depends on Qdrant hosting |
| `cache` (hit) | <1ms | <1ms | 2ms | Free |
| Provider | p50 | p95 | p99 | Cost / 1M embeddings |
| -------------------- | ----- | ----- | ----- | ---------------------------------- |
| `transformers` (CPU) | 80ms | 180ms | 350ms | Free |
| `remote` (OpenAI) | 120ms | 220ms | 400ms | ~$0.02 (ada-002) / $0.13 (3-large) |
| `static` (Qdrant) | 15ms | 30ms | 60ms | Depends on Qdrant hosting |
| `cache` (hit) | <1ms | <1ms | 2ms | Free |
---
@@ -662,43 +670,43 @@ The `extraction.ts` module (`src/lib/memory/extraction.ts`) uses **regex pattern
### Default Pattern Categories
| Category | Example pattern | Captures |
|----------|-----------------|----------|
| PREFERENCE_PATTERNS | `"I prefer <X>"`, `"I like <X>"`, `"I hate <X>"` | User preferences |
| DECISION_PATTERNS | `"I'll use <X>"`, `"I decided to <X>"`, `"I went with <X>"` | User decisions (episodic) |
| PATTERN_PATTERNS | `"I usually <X>"`, `"I always <X>"`, `"I never <X>"` | Persistent behavioral patterns |
| Category | Example pattern | Captures |
| ------------------- | ----------------------------------------------------------- | ------------------------------ |
| PREFERENCE_PATTERNS | `"I prefer <X>"`, `"I like <X>"`, `"I hate <X>"` | User preferences |
| DECISION_PATTERNS | `"I'll use <X>"`, `"I decided to <X>"`, `"I went with <X>"` | User decisions (episodic) |
| PATTERN_PATTERNS | `"I usually <X>"`, `"I always <X>"`, `"I never <X>"` | Persistent behavioral patterns |
### Example Patterns (Simplified)
```ts
// From src/lib/memory/extraction.ts
const PREFERENCE_PATTERNS = [
/\bI\s+(?:really\s+)?prefer\s+([^.,\n]+)/gi,
/\bI\s+(?:really\s+)?like\s+([^.,\n]+)/gi,
/\bI\s+(?:hate|dislike|avoid)\s+([^.,\n]+)/gi
/\bI\s+(?:hate|dislike|avoid)\s+([^.,\n]+)/gi,
];
const DECISION_PATTERNS = [
/\bI'?(?:ll|will)\s+use\s+([^.,\n]+)/gi,
/\bI\s+(?:have\s+)?decided\s+(?:to\s+)?([^.,\n]+)/gi
];
const PATTERN_PATTERNS = [
/\bI\s+usually\s+([^.,\n]+)/gi,
/\bI\s+always\s+([^.,\n]+)/gi
/\bI\s+(?:have\s+)?decided\s+(?:to\s+)?([^.,\n]+)/gi,
];
const PATTERN_PATTERNS = [/\bI\s+usually\s+([^.,\n]+)/gi, /\bI\s+always\s+([^.,\n]+)/gi];
```
### What Gets Extracted
When a user says:
> "I prefer TypeScript. I'll use Postgres for this project. I always commit before pushing. I don't like Python."
Extraction produces 4 memories:
| Key | Category | Type | Content |
|-----|----------|------|---------|
| `preference:typescript` | preference | factual | "TypeScript" |
| `decision:postgres_for_this_project` | decision | episodic | "Postgres for this project" |
| `pattern:commit_before_pushing` | pattern | factual | "commit before pushing" |
| `preference:python` | preference | factual | "Python" |
> Extraction produces 4 memories:
> | Key | Category | Type | Content |
> |-----|----------|------|---------|
> | `preference:typescript` | preference | factual | "TypeScript" |
> | `decision:postgres_for_this_project` | decision | episodic | "Postgres for this project" |
> | `pattern:commit_before_pushing` | pattern | factual | "commit before pushing" |
> | `preference:python` | preference | factual | "Python" |
### Extraction Limits
To prevent runaway extraction, the following limits apply:
| Min content length | 3 chars |
@@ -709,6 +717,7 @@ To prevent runaway extraction, the following limits apply:
Extraction runs automatically whenever memory is enabled; there is no separate
extraction-only toggle. To turn it off, disable memory entirely (`enabled: false`
via `PUT /api/settings/memory`). Consider doing so when:
- You have high message volume and the extraction cost is non-trivial
- Your conversations are mostly transient (chat, debugging) with no long-term value
- You're already capturing context via custom plugins
@@ -728,18 +737,19 @@ RRF(d) = Σ 1 / (k + rank_i(d))
```
Where:
- `k` is the constant (default 60)
- `rank_i(d)` is the rank of document `d` in the i-th retrieval system (FTS, vector)
- The sum runs over all retrieval systems
### How `k` Affects Results
| `k` value | Effect | Best for |
|-----------|--------|----------|
| `k=0` | Pure rank fusion (no smoothing) | Theoretical baseline |
| `k=10-30` | Heavily weights top results, low-rank barely contributes | When top-3 results are usually correct |
| **`k=60`** (default) | Balanced — top-10 results all contribute meaningfully | General-purpose retrieval |
| `k=100+` | Flatter — even low-rank results can dominate if they appear in multiple systems | When recall > precision is critical |
| `k` value | Effect | Best for |
| -------------------- | ------------------------------------------------------------------------------- | -------------------------------------- |
| `k=0` | Pure rank fusion (no smoothing) | Theoretical baseline |
| `k=10-30` | Heavily weights top results, low-rank barely contributes | When top-3 results are usually correct |
| **`k=60`** (default) | Balanced — top-10 results all contribute meaningfully | General-purpose retrieval |
| `k=100+` | Flatter — even low-rank results can dominate if they appear in multiple systems | When recall > precision is critical |
### Tuning `k` in Practice
@@ -755,12 +765,14 @@ MEMORY_RRF_K=120
```
**Example with `k=20`:**
- FTS rank 1 → contribution `1/21 = 0.048`
- FTS rank 10 → contribution `1/30 = 0.033`
- Vector rank 1 → contribution `0.048`
- Combined max: `0.096`
**Example with `k=60`:**
- FTS rank 1 → contribution `1/61 = 0.016`
- FTS rank 10 → contribution `1/70 = 0.014`
- Vector rank 1 → contribution `0.016`
@@ -770,12 +782,12 @@ With higher `k`, the **relative difference** between top-1 and rank-10 is smalle
### When to Change `k`
| Symptom | Try |
|---------|-----|
| Top result always wins, but it's wrong | **Lower** k (e.g., 20) — top-rank confidence matters more |
| Symptom | Try |
| -------------------------------------- | ------------------------------------------------------------ |
| Top result always wins, but it's wrong | **Lower** k (e.g., 20) — top-rank confidence matters more |
| Right answer is in top-5 but not top-1 | **Higher** k (e.g., 100) — flatter scoring rewards consensus |
| Recall is high but precision is low | **Lower** k — sharpen the ranking |
| Recall is low (missing relevant docs) | **Higher** k — give lower-ranked docs a chance |
| Recall is high but precision is low | **Lower** k — sharpen the ranking |
| Recall is low (missing relevant docs) | **Higher** k — give lower-ranked docs a chance |
### RRF Weighting
@@ -795,9 +807,9 @@ The `summarization.ts` module (`src/lib/memory/summarization.ts`) compresses old
### When Summarization Triggers
| Trigger | Threshold (default) |
|---------|---------------------|
| Manual trigger via API | n/a |
| Trigger | Threshold (default) |
| ---------------------- | ------------------- |
| Manual trigger via API | n/a |
### What Gets Summarized

View File

@@ -1,7 +1,7 @@
---
title: "Notion Context Source"
version: 3.8.24
lastUpdated: 2026-06-13
version: 3.8.40
lastUpdated: 2026-06-28
---
# Notion Context Source

View File

@@ -1,7 +1,7 @@
---
title: "Obsidian Context Source"
version: 3.8.24
lastUpdated: 2026-06-13
version: 3.8.40
lastUpdated: 2026-06-28
---
# Obsidian Context Source

View File

@@ -1,7 +1,7 @@
---
title: "OpenCode Integration"
version: 3.8.2
lastUpdated: 2026-05-14
version: 3.8.40
lastUpdated: 2026-06-28
---
# OpenCode Integration

View File

@@ -1,14 +1,14 @@
---
title: "open-sse Architecture"
version: 3.8.16
lastUpdated: 2026-06-08
version: 3.8.40
lastUpdated: 2026-06-28
---
# open-sse Architecture
> **TL;DR**: `open-sse/` is the core streaming engine that powers every LLM request in OmniRoute. It contains ~406 files implementing the request pipeline, executors, services, MCP server, and translation layer. This guide explains how the pieces fit together.
> **TL;DR**: `open-sse/` is the core streaming engine that powers every LLM request in OmniRoute. It contains ~900 files implementing the request pipeline, executors, services, MCP server, and translation layer. This guide explains how the pieces fit together.
**Source:** `open-sse/` (workspace package, ~143K LOC across 406 files)
**Source:** `open-sse/` (workspace package, ~900 files; 811 `.ts`)
---
@@ -36,11 +36,11 @@ open-sse/
├── types.d.ts # Public type exports
├── package.json # @omniroute/open-sse
├── config/ # Provider configs, constants, registries
├── executors/ # Per-provider HTTP executors (59 files)
├── executors/ # Per-provider HTTP executors (67 + base.ts/index.ts)
├── handlers/ # Request handlers (chatCore, responses, etc.)
├── lib/ # Internal utilities
├── mcp-server/ # Model Context Protocol server
├── services/ # ~114 service modules
├── services/ # ~298 service modules
├── transformer/ # Responses API format transformer
├── translator/ # Format translation (OpenAI ↔ Claude ↔ Gemini)
└── utils/ # Shared utilities (logging, error, stream, etc.)
@@ -49,12 +49,12 @@ open-sse/
### Module Counts
| Directory | Files | Purpose |
| `executors/` | 62 | Per-provider HTTP executors (unified via DefaultExecutor factory) |
| `handlers/` | ~15 | Request entry points (chatCore, responses, embeddings) |
| `services/` | ~114 | Routing, caching, rate limiting, refresh, etc. |
| `translator/` | ~10 | Format conversion (OpenAI ↔ Claude ↔ Gemini) |
| `mcp-server/` | 30 | MCP tools and transports |
| `utils/` | ~30 | Cross-cutting utilities (logging, error, stream) |
| `executors/` | 68 | Per-provider HTTP executors (unified via DefaultExecutor factory) |
| `handlers/` | 16 | Request entry points (chatCore, responses, embeddings) |
| `services/` | ~298 | Routing, caching, rate limiting, refresh, etc. |
| `translator/` | ~27 | Format conversion (OpenAI ↔ Claude ↔ Gemini) |
| `mcp-server/` | 32 | MCP tools and transports |
| `utils/` | ~65 | Cross-cutting utilities (logging, error, stream) |
| `config/` | ~10 | Provider configs, constants, registries |
---
@@ -104,6 +104,7 @@ Resolves the request to a concrete `(provider, model, account, credentials)` tup
- Pick the next viable target
For `auto/*` models, this stage also:
- Runs the **9-factor scoring** algorithm (`services/autoCombo/`)
- Selects a `provider+model` pair based on health, cost, latency, etc.
@@ -136,6 +137,7 @@ All providers use `DefaultExecutor` (`executors/default.ts`) via the `getExecuto
- Handles auth refresh if needed (OAuth providers)
All executors extend `BaseExecutor` (`executors/base.ts`, 1170 LOC) which provides:
- Common retry logic
- Proxy integration
- Circuit breaker integration
@@ -177,17 +179,17 @@ export async function handleChat(request: NextRequest) {
// 1. Auth + CORS
await authenticateRequest(request);
applyCorsHeaders(response);
// 2. Body validation
const body = await parseRequestBody(request);
// 3. Format detection + translation
const sourceFormat = detectFormat(request);
const targetFormat = getTargetFormat(providerId);
if (needsTranslation(sourceFormat, targetFormat)) {
body = translateRequest(body, sourceFormat, targetFormat);
}
// 4. Combo routing
const targets = await resolveComboTargets(comboId, body);
for (const target of targets) {
@@ -199,7 +201,7 @@ export async function handleChat(request: NextRequest) {
// Continue to next target
}
}
// 5. Emergency fallback
return await emergencyFallback(body);
}
@@ -228,29 +230,29 @@ export async function handleComboChat(body, comboId): Promise<ChatResult> {
Supports **17 routing strategies** (see `src/shared/constants/routingStrategies.ts`):
| Strategy | Behavior |
|----------|----------|
| `priority` | First-target ordered list |
| `weighted` | Probabilistic by per-target weight |
| `round-robin` | Cycle through targets in order |
| `context-relay` | Hand off context across targets |
| `fill-first` | Fill quota before moving to next |
| `p2c` | Power of two choices |
| `random` | Uniform random |
| `least-used` | Pick the one with fewest recent uses |
| `cost-optimized` | Cheapest healthy target first |
| `reset-aware` | Aware of provider reset windows |
| `reset-window` | Reset window-based routing |
| `headroom` | Most remaining quota headroom first |
| `strict-random` | Truly uniform (no quality weighting) |
| `auto` | Use 9-factor scoring (`autoCombo/`) |
| `lkgp` | Last known good provider first |
| `context-optimized` | Best for long-context requests |
| `fusion` | Fan out to a panel in parallel, then synthesize via a judge (`fusion.ts`) |
| Strategy | Behavior |
| ------------------- | ------------------------------------------------------------------------- |
| `priority` | First-target ordered list |
| `weighted` | Probabilistic by per-target weight |
| `round-robin` | Cycle through targets in order |
| `context-relay` | Hand off context across targets |
| `fill-first` | Fill quota before moving to next |
| `p2c` | Power of two choices |
| `random` | Uniform random |
| `least-used` | Pick the one with fewest recent uses |
| `cost-optimized` | Cheapest healthy target first |
| `reset-aware` | Aware of provider reset windows |
| `reset-window` | Reset window-based routing |
| `headroom` | Most remaining quota headroom first |
| `strict-random` | Truly uniform (no quality weighting) |
| `auto` | Use 9-factor scoring (`autoCombo/`) |
| `lkgp` | Last known good provider first |
| `context-optimized` | Best for long-context requests |
| `fusion` | Fan out to a panel in parallel, then synthesize via a judge (`fusion.ts`) |
### base.ts (1170 LOC)
The **abstract executor** that all 59 executors extend. It contains:
The **abstract executor** that all 67 executors extend. It contains:
- `buildUrl()` — default URL construction (subclasses override for custom)
- `buildHeaders()` — default headers (auth, content-type)
@@ -266,7 +268,8 @@ export class DefaultExecutor extends BaseExecutor {
```
Provider-specific behavior (auth headers, base URL, version headers) is configured via the provider registry, not separate executor classes.
```
````
---
@@ -343,12 +346,13 @@ export default {
id: "together",
baseURL: "https://api.together.xyz/v1/chat/completions",
}
```
````
**Custom auth** is handled through the provider registry's auth configuration (API key, OAuth, header profiles).
**Custom request body** transformations (e.g., Anthropic separating `system` from `messages`) are registered per-provider in `open-sse/translator/`.
```
````
### The Executor Factory
@@ -362,7 +366,7 @@ const result = await executor.execute({
model: "claude-sonnet-4-5",
messages: [...],
});
```
````
The factory is generated from `config/providerRegistry.ts` which lists all 212+ providers and their executor class.
@@ -383,6 +387,7 @@ if (needsTranslation(sourceFormat, targetFormat)) {
```
Common translations:
- `OpenAI → Anthropic`: separate `system` field, `x-api-key` header
- `OpenAI → Gemini`: `contents` instead of `messages`, `systemInstruction`
- `OpenAI → Responses API`: `input` array, `previous_response_id` state
@@ -404,6 +409,7 @@ Common translations:
- **30+ tools** (provider management, combos, memory, cache, compression, 1proxy, skills)
- **3 transports**: stdio, SSE, Streamable HTTP
- **13 scopes** for fine-grained authorization
### Tool Registration
Tools are registered as standalone files in `open-sse/mcp-server/tools/`, each exporting a name, schema, handler, and scope:
@@ -467,6 +473,7 @@ createResponsesApiTransformStream(): TransformStream
```
This handles:
- `response.output_item.added` events
- `response.output_text.delta` events
- `response.completed` event
@@ -478,13 +485,13 @@ This handles:
`open-sse/config/` holds the configuration layer:
| File | Purpose |
|------|---------|
| `providerRegistry.ts` | 212+ provider definitions |
| `providerModels.ts` | Model aliases, format mapping |
| `constants.ts` | Timeouts, limits, status codes |
| File | Purpose |
| ----------------------------- | --------------------------------- |
| `providerRegistry.ts` | 212+ provider definitions |
| `providerModels.ts` | Model aliases, format mapping |
| `constants.ts` | Timeouts, limits, status codes |
| `defaultThinkingSignature.ts` | Default Claude thinking signature |
| `modelStrip.ts` (in services) | Per-provider field stripping |
| `modelStrip.ts` (in services) | Per-provider field stripping |
### Provider Registry Schema
@@ -509,13 +516,13 @@ Zod validation at module load ensures all provider configs are valid.
The routing engine has strict performance budgets:
| Operation | Target | Measurement |
|-----------|--------|-------------|
| Combo resolution | <10ms | For 50 targets |
| Rate limit check | <1ms | In-memory token bucket |
| Model family fallback | <5ms | Cached family definitions |
| Request routing dispatch | <2ms | Hot path |
| **No blocking I/O in routing hot path** | — | All async |
| Operation | Target | Measurement |
| --------------------------------------- | ------ | ------------------------- |
| Combo resolution | <10ms | For 50 targets |
| Rate limit check | <1ms | In-memory token bucket |
| Model family fallback | <5ms | Cached family definitions |
| Request routing dispatch | <2ms | Hot path |
| **No blocking I/O in routing hot path** | — | All async |
---

View File

@@ -1,7 +1,7 @@
---
title: "Playground Studio"
version: 3.8.7
lastUpdated: 2026-05-30
version: 3.8.40
lastUpdated: 2026-06-28
---
# Playground Studio
@@ -82,14 +82,14 @@ Tools/function calling and structured output UI:
`StudioConfigPane.tsx` — always visible, collapsible.
| Field | Component | Notes |
|-------|-----------|-------|
| Endpoint | `<select>` | 10 options matching `PlaygroundEndpoint` |
| Model | `<input>` | free text, e.g. `openai/gpt-4o` |
| System prompt | `<textarea>` | fed into all tabs |
| Parameters | `ParamSliders` | temperature, max_tokens, top_p, presence/frequency penalty, seed, stop |
| Presets | `PresetPicker` | load/save named config snapshots (persisted in DB) |
| Improve prompt | `ImprovePromptButton` | opens quota-warning modal, calls `/api/playground/improve-prompt` |
| Field | Component | Notes |
| -------------- | --------------------- | ---------------------------------------------------------------------- |
| Endpoint | `<select>` | 10 options matching `PlaygroundEndpoint` |
| Model | `<input>` | free text, e.g. `openai/gpt-4o` |
| System prompt | `<textarea>` | fed into all tabs |
| Parameters | `ParamSliders` | temperature, max_tokens, top_p, presence/frequency penalty, seed, stop |
| Presets | `PresetPicker` | load/save named config snapshots (persisted in DB) |
| Improve prompt | `ImprovePromptButton` | opens quota-warning modal, calls `/api/playground/improve-prompt` |
State is lifted to `PlaygroundStudio.tsx` and passed down to all tabs. Switching tabs
preserves config state.
@@ -149,14 +149,14 @@ from the current `PlaygroundState`. API key placeholder is always `$OMNIROUTE_AP
## Backend Routes
| Method | Path | Handler |
|--------|------|---------|
| `POST` | `/api/playground/improve-prompt` | Zod-validates `ImprovePromptRequestSchema`; calls `/v1/chat/completions` with meta-prompt |
| `GET` | `/api/playground/presets` | Returns `{ presets: PlaygroundPresetListItem[] }` |
| `POST` | `/api/playground/presets` | Creates preset; validates `PlaygroundPresetCreateSchema` |
| `GET` | `/api/playground/presets/:id` | Returns one preset or 404 |
| `PUT` | `/api/playground/presets/:id` | Partial update |
| `DELETE` | `/api/playground/presets/:id` | 204 |
| Method | Path | Handler |
| -------- | -------------------------------- | ----------------------------------------------------------------------------------------- |
| `POST` | `/api/playground/improve-prompt` | Zod-validates `ImprovePromptRequestSchema`; calls `/v1/chat/completions` with meta-prompt |
| `GET` | `/api/playground/presets` | Returns `{ presets: PlaygroundPresetListItem[] }` |
| `POST` | `/api/playground/presets` | Creates preset; validates `PlaygroundPresetCreateSchema` |
| `GET` | `/api/playground/presets/:id` | Returns one preset or 404 |
| `PUT` | `/api/playground/presets/:id` | Partial update |
| `DELETE` | `/api/playground/presets/:id` | 204 |
Auth: optional (`REQUIRE_API_KEY`). Errors via `buildErrorBody()` (Hard Rule #12).
@@ -164,42 +164,42 @@ Auth: optional (`REQUIRE_API_KEY`). Errors via `buildErrorBody()` (Hard Rule #12
## Key Files
| Path | Purpose |
|------|---------|
| `src/app/(dashboard)/dashboard/playground/PlaygroundStudio.tsx` | Shell component, tab orchestrator |
| `src/app/(dashboard)/dashboard/playground/components/StudioTopBar.tsx` | Tabs + counter + export button |
| `src/app/(dashboard)/dashboard/playground/components/StudioConfigPane.tsx` | Shared config panel |
| `src/app/(dashboard)/dashboard/playground/components/tabs/ChatTab.tsx` | Chat workbench |
| `src/app/(dashboard)/dashboard/playground/components/tabs/CompareTab.tsx` | Multi-model compare |
| `src/app/(dashboard)/dashboard/playground/components/tabs/ApiTab.tsx` | Monaco editor (preserved) |
| `src/app/(dashboard)/dashboard/playground/components/tabs/BuildTab.tsx` | Tools + structured output |
| `src/app/(dashboard)/dashboard/playground/components/ExportCodeModal.tsx` | Code export modal |
| `src/app/(dashboard)/dashboard/playground/components/CompareColumn.tsx` | Single compare column |
| `src/app/(dashboard)/dashboard/playground/components/ProviderMetrics.tsx` | TTFT/TPS display |
| `src/app/(dashboard)/dashboard/playground/hooks/useStreamMetrics.ts` | Client-side metric hook |
| `src/app/(dashboard)/dashboard/playground/hooks/usePresets.ts` | Presets CRUD hook |
| `src/app/(dashboard)/dashboard/playground/hooks/useImprovePrompt.ts` | Improve-prompt hook |
| `src/lib/playground/codeExport.ts` | curl/Python/TS generator (shared with Search Tools) |
| `src/lib/playground/promptImprover.ts` | Meta-prompt builder |
| `src/lib/playground/streamMetrics.ts` | Pure metrics computation |
| `src/lib/db/playgroundPresets.ts` | DB module (CRUD) |
| `src/app/api/playground/improve-prompt/route.ts` | Improve-prompt REST route |
| `src/app/api/playground/presets/route.ts` | Presets list + create |
| `src/app/api/playground/presets/[id]/route.ts` | Presets get/update/delete |
| `src/lib/db/migrations/084_playground_presets.sql` | DB migration |
| Path | Purpose |
| -------------------------------------------------------------------------- | --------------------------------------------------- |
| `src/app/(dashboard)/dashboard/playground/PlaygroundStudio.tsx` | Shell component, tab orchestrator |
| `src/app/(dashboard)/dashboard/playground/components/StudioTopBar.tsx` | Tabs + counter + export button |
| `src/app/(dashboard)/dashboard/playground/components/StudioConfigPane.tsx` | Shared config panel |
| `src/app/(dashboard)/dashboard/playground/components/tabs/ChatTab.tsx` | Chat workbench |
| `src/app/(dashboard)/dashboard/playground/components/tabs/CompareTab.tsx` | Multi-model compare |
| `src/app/(dashboard)/dashboard/playground/components/tabs/ApiTab.tsx` | Monaco editor (preserved) |
| `src/app/(dashboard)/dashboard/playground/components/tabs/BuildTab.tsx` | Tools + structured output |
| `src/app/(dashboard)/dashboard/playground/components/ExportCodeModal.tsx` | Code export modal |
| `src/app/(dashboard)/dashboard/playground/components/CompareColumn.tsx` | Single compare column |
| `src/app/(dashboard)/dashboard/playground/components/ProviderMetrics.tsx` | TTFT/TPS display |
| `src/app/(dashboard)/dashboard/playground/hooks/useStreamMetrics.ts` | Client-side metric hook |
| `src/app/(dashboard)/dashboard/playground/hooks/usePresets.ts` | Presets CRUD hook |
| `src/app/(dashboard)/dashboard/playground/hooks/useImprovePrompt.ts` | Improve-prompt hook |
| `src/lib/playground/codeExport.ts` | curl/Python/TS generator (shared with Search Tools) |
| `src/lib/playground/promptImprover.ts` | Meta-prompt builder |
| `src/lib/playground/streamMetrics.ts` | Pure metrics computation |
| `src/lib/db/playgroundPresets.ts` | DB module (CRUD) |
| `src/app/api/playground/improve-prompt/route.ts` | Improve-prompt REST route |
| `src/app/api/playground/presets/route.ts` | Presets list + create |
| `src/app/api/playground/presets/[id]/route.ts` | Presets get/update/delete |
| `src/lib/db/migrations/084_playground_presets.sql` | DB migration |
---
## Troubleshooting
| Symptom | Cause | Fix |
|---------|-------|-----|
| Monaco editor not rendering in API tab | SSR loaded Monaco | Verify `ApiTab` uses `dynamic(..., { ssr: false })` |
| Compare streams fire sequentially | Wrong `Promise.all` usage | All stream starts must be dispatched in one `Promise.all` call |
| Metrics show `null` TTFT | First chunk handler not wired | Check `useStreamMetrics.onFirstChunk()` is called in the SSE reader loop |
| Preset not persisting | DB migration not run | Run `npm run db:migrate` or restart the server (migration auto-runs on startup) |
| Improve prompt returns 502 | Model not set in Config | User must enter a model name in the Config pane before improving |
| Export code shows `MISSING_API_KEY` | Placeholder not inserted | `codeExport.ts` always uses `API_KEY_PLACEHOLDER = "$OMNIROUTE_API_KEY"` |
| Symptom | Cause | Fix |
| -------------------------------------- | ----------------------------- | ------------------------------------------------------------------------------- |
| Monaco editor not rendering in API tab | SSR loaded Monaco | Verify `ApiTab` uses `dynamic(..., { ssr: false })` |
| Compare streams fire sequentially | Wrong `Promise.all` usage | All stream starts must be dispatched in one `Promise.all` call |
| Metrics show `null` TTFT | First chunk handler not wired | Check `useStreamMetrics.onFirstChunk()` is called in the SSE reader loop |
| Preset not persisting | DB migration not run | Run `npm run db:migrate` or restart the server (migration auto-runs on startup) |
| Improve prompt returns 502 | Model not set in Config | User must enter a model name in the Config pane before improving |
| Export code shows `MISSING_API_KEY` | Placeholder not inserted | `codeExport.ts` always uses `API_KEY_PLACEHOLDER = "$OMNIROUTE_API_KEY"` |
---

114
docs/frameworks/PLUGINS.md Normal file
View File

@@ -0,0 +1,114 @@
---
title: "OmniRoute CLI Plugin System"
version: 3.8.40
lastUpdated: 2026-06-28
---
# OmniRoute CLI Plugin System
Extend the `omniroute` CLI without modifying its core. Plugins follow the `omniroute-cmd-*` naming convention, similar to `gh extension` or `kubectl plugin`.
## Quick start
```bash
# Install a plugin from npm
omniroute plugin install stripe
# Install a local plugin in development
omniroute plugin install ./my-plugin
# List installed plugins
omniroute plugin list
# Scaffold a new plugin
omniroute plugin scaffold myplugin
cd omniroute-cmd-myplugin
omniroute plugin install .
```
## Plugin anatomy
A plugin is an npm package named `omniroute-cmd-<name>` (or `@scope/omniroute-cmd-<name>`).
```
omniroute-cmd-myplugin/
├── package.json # must have "type": "module" and "main": "index.mjs"
├── index.mjs # exports register(program, ctx) + optional meta
└── README.md
```
### `package.json`
```json
{
"name": "omniroute-cmd-myplugin",
"version": "0.1.0",
"type": "module",
"main": "index.mjs",
"engines": { "omniroute": ">=4.0.0" },
"keywords": ["omniroute-plugin", "omniroute-cmd"]
}
```
### `index.mjs`
```js
export const meta = {
name: "myplugin",
version: "0.1.0",
description: "My plugin for OmniRoute",
omnirouteApi: ">=4.0.0",
};
export function register(program, ctx) {
program
.command("myplugin")
.description(meta.description)
.option("-n, --name <name>")
.action(async (opts, cmd) => {
const gOpts = cmd.optsWithGlobals();
const res = await ctx.apiFetch("/api/combos", {
baseUrl: gOpts.baseUrl,
apiKey: gOpts.apiKey,
});
const data = await res.json();
ctx.emit(data, gOpts);
});
}
```
## Plugin context API
The `ctx` object passed to `register(program, ctx)`:
| Property | Type | Description |
| ---------------------------- | ---------------- | -------------------------------------------------- |
| `ctx.apiFetch(path, opts)` | `async function` | Authenticated fetch to the OmniRoute server |
| `ctx.emit(data, opts)` | `function` | Output in table/json/jsonl/csv per `--output` flag |
| `ctx.t(key)` | `async function` | i18n translation lookup |
| `ctx.withSpinner(label, fn)` | `async function` | Wraps async fn with ora spinner |
| `ctx.baseUrl` | `string` | Resolved base URL |
| `ctx.apiKey` | `string \| null` | API key if provided |
## Discovery
Plugins are discovered from:
1. `~/.omniroute/plugins/<name>/` — user-local installs
2. `OMNIROUTE_PLUGIN_PATH` env var — custom directory
Loading errors are caught and printed as warnings — a broken plugin never crashes the CLI.
## Security
Plugins run with the same Node.js process privileges as `omniroute`. Only install plugins from sources you trust. `omniroute plugin install` shows an explicit warning and requires `--yes` or interactive confirmation.
## Publishing
1. Ensure `package.json` has `"keywords": ["omniroute-plugin"]`
2. `npm publish` as normal
3. Users discover via `omniroute plugin search <query>` (searches npm registry)
## Example plugin
See [`examples/omniroute-cmd-hello/`](../../examples/omniroute-cmd-hello/index.mjs) for a minimal working example with `meta` + `register()`.

View File

@@ -1,7 +1,7 @@
---
title: "Plugin Marketplace"
version: 3.8.24
lastUpdated: 2026-06-13
version: 3.8.40
lastUpdated: 2026-06-28
---
# Plugin Marketplace
@@ -9,7 +9,7 @@ lastUpdated: 2026-06-13
> **Source of truth:** `src/lib/plugins/` (`marketplace.ts`, `manager.ts`, `manifest.ts`,
> `scanner.ts`, `loader.ts`), `src/app/api/plugins/`, and
> `src/app/(dashboard)/dashboard/plugins/`
> **Last updated:** 2026-06-13 — v3.8.24
> **Last updated:** 2026-06-28 — v3.8.40
OmniRoute ships a WordPress-style plugin system. Plugins are self-contained
directories — each with a `plugin.json` manifest and an entry file — that hook

View File

@@ -0,0 +1,253 @@
---
title: "OmniRoute Plugin SDK"
version: 3.8.40
lastUpdated: 2026-06-28
---
# OmniRoute Plugin SDK
## Quick Start
```ts
import { definePlugin } from "omniroute/plugins/sdk";
export default definePlugin({
name: "my-plugin",
priority: 50,
onRequest: async (ctx) => {
console.log(`Request ${ctx.requestId} for ${ctx.model}`);
},
onResponse: async (ctx, response) => {
console.log(`Response for ${ctx.requestId}`);
return response;
},
onError: async (ctx, error) => {
console.error(`Error: ${error.message}`);
},
});
```
## API Reference
### `definePlugin(def: PluginDefinition): Plugin`
Factory function that creates a Plugin object with defaults.
**Parameters:**
- `name` (string, required) — Plugin name in kebab-case
- `priority` (number, optional, default: 100) — Lower runs first
- `enabled` (boolean, optional, default: true) — Start enabled?
- `onRequest` (function, optional) — Runs before chat handler
- `onResponse` (function, optional) — Runs after chat handler
- `onError` (function, optional) — Runs on handler error
### `blockRequest(response?): BlockingHookResult`
Block the request and optionally return a custom response.
```ts
onRequest: (ctx) => {
if (!ctx.headers["authorization"]) {
return blockRequest({ error: "Unauthorized", status: 401 });
}
};
```
### `modifyBody(body): PluginResult`
Modify the request body before it reaches the provider.
```ts
onRequest: (ctx) => {
return modifyBody({ ...ctx.body, temperature: 0.7 });
};
```
### `addMetadata(metadata): PluginResult`
Attach metadata to the request context.
```ts
onRequest: (ctx) => {
return addMetadata({ source: "my-plugin", version: "1.0.0" });
};
```
## Plugin Context (`PluginContext`)
| Field | Type | Description |
| ----------- | ------------------------- | ------------------------- |
| `requestId` | `string` | Unique request identifier |
| `model` | `string` | Requested model name |
| `provider` | `string` | Target provider ID |
| `body` | `Record<string, unknown>` | Request body |
| `headers` | `Record<string, string>` | Request headers |
| `metadata` | `Record<string, unknown>` | Mutable metadata |
| `timestamp` | `number` | Request timestamp |
## Manifest (`plugin.json`)
```json
{
"name": "my-plugin",
"version": "1.0.0",
"description": "A sample plugin",
"author": "your-name",
"main": "index.js",
"hooks": {
"onRequest": { "enabled": true, "priority": 50 },
"onResponse": true,
"onError": false
},
"requires": {
"permissions": ["network", "file-read"]
},
"enabledByDefault": false,
"configSchema": {
"apiKey": { "type": "string", "description": "API key for external service" },
"maxRetries": { "type": "number", "min": 1, "max": 10, "default": 3 },
"debug": { "type": "boolean", "default": false },
"mode": { "type": "string", "enum": ["fast", "slow"], "default": "fast" }
}
}
```
### Hook Priority
Hooks can be configured with priority (lower = runs first):
```json
{
"hooks": {
"onRequest": { "enabled": true, "priority": 10 },
"onResponse": { "enabled": true, "priority": 100 }
}
}
```
Or as simple booleans (default priority 100):
```json
{
"hooks": {
"onRequest": true,
"onResponse": true
}
}
```
## Permission System
Plugins run in a sandboxed VM context. Access to external resources requires explicit permissions:
| Permission | Grants |
| ------------ | ------------------------------------------------------------ |
| `network` | `fetch`, `AbortController`, `Headers`, `Request`, `Response` |
| `file-read` | `fs.readFile`, `fs.readdir`, `fs.stat` |
| `file-write` | `fs.writeFile`, `fs.mkdir`, `fs.rm` |
| `env` | Read-only `process.env` proxy |
| `exec` | `child_process.exec`, `child_process.execSync` |
Without a permission, the corresponding globals are simply not available in the sandbox.
## Config Schema
Define configurable settings in `configSchema`:
```json
{
"configSchema": {
"apiKey": { "type": "string", "description": "External API key" },
"maxRetries": { "type": "number", "min": 1, "max": 10, "default": 3 },
"debug": { "type": "boolean", "default": false },
"mode": { "type": "string", "enum": ["fast", "slow"], "default": "fast" }
}
}
```
Field types: `string`, `number`, `boolean`, `select`
Field options: `default`, `min`, `max`, `enum`, `description`
Config values are persisted in the database and accessible via the dashboard config page.
## Built-in Events
| Event | When | Payload |
| ----------------- | ----------------------------------------- | ----------------------------- |
| `onRequest` | Before chat handler | Request context |
| `onResponse` | After chat handler | Response data |
| `onError` | On handler error | Error object |
| `onModelSelect` | Model selected for routing | Model info |
| `onComboResolve` | Combo routing resolved | Combo targets |
| `onRateLimit` | Rate limit hit | Limit info |
| `onQuotaExhaust` | Quota exhausted | Quota info |
| `onProviderError` | Provider returned error | Error details |
| `onStreamStart` | SSE stream started | Stream info |
| `onStreamEnd` | SSE stream ended | Stream stats |
| `onInstall` | Plugin installed | `{ name, version, manifest }` |
| `onActivate` | Plugin activated | `{ name, version, manifest }` |
| `onDeactivate` | Plugin deactivated | `{ name, version, manifest }` |
| `onUninstall` | Plugin uninstalled (before files deleted) | `{ name, version, manifest }` |
## Examples
### Request Logger
```ts
import { definePlugin } from "omniroute/plugins/sdk";
export default definePlugin({
name: "request-logger",
onRequest: async (ctx) => {
console.log(`[${new Date().toISOString()}] ${ctx.method} ${ctx.model} -> ${ctx.provider}`);
},
});
```
### Rate Limiter
```ts
import { definePlugin, blockRequest } from "omniroute/plugins/sdk";
const requests = new Map<string, number[]>();
export default definePlugin({
name: "rate-limiter",
priority: 10,
onRequest: async (ctx) => {
const key = ctx.headers["x-api-key"] || "anonymous";
const now = Date.now();
const window = 60000; // 1 minute
const maxRequests = 100;
const timestamps = (requests.get(key) || []).filter((t) => t > now - window);
timestamps.push(now);
requests.set(key, timestamps);
if (timestamps.length > maxRequests) {
return blockRequest({ error: "Rate limit exceeded", status: 429 });
}
},
});
```
### Response Transformer
```ts
import { definePlugin } from "omniroute/plugins/sdk";
export default definePlugin({
name: "response-transformer",
onResponse: async (ctx, response) => {
if (response.choices) {
response.choices = response.choices.map((c: any) => ({
...c,
message: { ...c.message, content: c.message.content.trim() },
}));
}
return response;
},
});
```

View File

@@ -1,7 +1,7 @@
---
title: "Search Tools Studio"
version: 3.8.7
lastUpdated: 2026-05-30
version: 3.8.40
lastUpdated: 2026-06-28
---
# Search Tools Studio
@@ -75,14 +75,14 @@ Runs the same query/URL across up to **4 providers in parallel** (D22):
`SearchToolsConfigPane.tsx` — always visible, collapsible.
| Field | Notes |
|-------|-------|
| Provider | Dropdown with status badge (configured / missing / rate limited) |
| Type | `web` or `news` (search only) |
| Full page | Toggle for scrape — fetches entire page vs first visible content |
| Format | `markdown`, `text`, or `html` (scrape only) |
| Rerank model | Optional model for post-search reranking |
| History | Collapsible search history section |
| Field | Notes |
| ------------ | ---------------------------------------------------------------- |
| Provider | Dropdown with status badge (configured / missing / rate limited) |
| Type | `web` or `news` (search only) |
| Full page | Toggle for scrape — fetches entire page vs first visible content |
| Format | `markdown`, `text`, or `html` (scrape only) |
| Rerank model | Optional model for post-search reranking |
| History | Collapsible search history section |
---
@@ -90,13 +90,13 @@ Runs the same query/URL across up to **4 providers in parallel** (D22):
`SearchConceptCard.tsx` — always visible, collapsible accordion. Explains:
| Concept | One-liner |
|---------|-----------|
| **Search** | Fetches a list of web results (title, URL, snippet, relevance score) |
| **Scrape** | Extracts the full content of a URL (markdown, text or HTML) |
| **Compare** | Runs the same query in N providers side-by-side |
| **Rerank** | Reorders results via LLM to improve query relevance |
| **Auto (cheapest)** | Picks the cheapest available provider automatically |
| Concept | One-liner |
| ------------------- | -------------------------------------------------------------------- |
| **Search** | Fetches a list of web results (title, URL, snippet, relevance score) |
| **Scrape** | Extracts the full content of a URL (markdown, text or HTML) |
| **Compare** | Runs the same query in N providers side-by-side |
| **Rerank** | Reorders results via LLM to improve query relevance |
| **Auto (cheapest)** | Picks the cheapest available provider automatically |
---
@@ -105,15 +105,15 @@ Runs the same query/URL across up to **4 providers in parallel** (D22):
`ProviderCatalog.tsx` exposes the full provider list from `GET /api/search/providers`
(extended in F4 to include fetch providers):
| Field | Source |
|-------|--------|
| `id`, `name` | `searchRegistry.ts` |
| `kind` | `"search"` (12 providers) or `"fetch"` (firecrawl, jina-reader, tavily-search) |
| `costPerQuery` | Registry data |
| `freeMonthlyQuota` | Registry data |
| `searchTypes` / `fetchFormats` | Registry data |
| `status` | `"configured"` / `"missing"` / `"rate_limited"` — derived at runtime from credential store |
| `configureHref` | `/dashboard/providers` |
| Field | Source |
| ------------------------------ | ------------------------------------------------------------------------------------------ |
| `id`, `name` | `searchRegistry.ts` |
| `kind` | `"search"` (12 providers) or `"fetch"` (firecrawl, jina-reader, tavily-search) |
| `costPerQuery` | Registry data |
| `freeMonthlyQuota` | Registry data |
| `searchTypes` / `fetchFormats` | Registry data |
| `status` | `"configured"` / `"missing"` / `"rate_limited"` — derived at runtime from credential store |
| `configureHref` | `/dashboard/providers` |
The status is **derived at request time** by checking whether credentials exist and whether
all keys are currently in cooldown.
@@ -145,32 +145,32 @@ Only one backend change was needed for this feature:
## Key Files
| Path | Purpose |
|------|---------|
| `src/app/(dashboard)/dashboard/search-tools/SearchToolsClient.tsx` | Studio shell, tab orchestrator |
| `src/app/(dashboard)/dashboard/search-tools/components/SearchToolsTopBar.tsx` | Tabs + metrics + export button |
| `src/app/(dashboard)/dashboard/search-tools/components/SearchToolsConfigPane.tsx` | Shared config panel |
| `src/app/(dashboard)/dashboard/search-tools/components/SearchConceptCard.tsx` | Explainer cards (always visible) |
| `src/app/(dashboard)/dashboard/search-tools/components/ProviderCatalog.tsx` | Provider list with metadata |
| `src/app/(dashboard)/dashboard/search-tools/components/ScrapeResult.tsx` | Markdown preview + raw toggle |
| `src/app/(dashboard)/dashboard/search-tools/components/tabs/SearchTab.tsx` | Search + rerank tab |
| `src/app/(dashboard)/dashboard/search-tools/components/tabs/ScrapeTab.tsx` | Scrape tab |
| `src/app/(dashboard)/dashboard/search-tools/components/tabs/CompareTab.tsx` | Multi-provider compare tab |
| `src/app/(dashboard)/dashboard/search-tools/hooks/useScrapeFetch.ts` | Scrape fetch hook |
| `src/app/api/search/providers/route.ts` | Extended with `kind` + `status` + fetch providers |
| `open-sse/config/searchRegistry.ts` | Source of truth for search provider metadata |
| Path | Purpose |
| --------------------------------------------------------------------------------- | ------------------------------------------------- |
| `src/app/(dashboard)/dashboard/search-tools/SearchToolsClient.tsx` | Studio shell, tab orchestrator |
| `src/app/(dashboard)/dashboard/search-tools/components/SearchToolsTopBar.tsx` | Tabs + metrics + export button |
| `src/app/(dashboard)/dashboard/search-tools/components/SearchToolsConfigPane.tsx` | Shared config panel |
| `src/app/(dashboard)/dashboard/search-tools/components/SearchConceptCard.tsx` | Explainer cards (always visible) |
| `src/app/(dashboard)/dashboard/search-tools/components/ProviderCatalog.tsx` | Provider list with metadata |
| `src/app/(dashboard)/dashboard/search-tools/components/ScrapeResult.tsx` | Markdown preview + raw toggle |
| `src/app/(dashboard)/dashboard/search-tools/components/tabs/SearchTab.tsx` | Search + rerank tab |
| `src/app/(dashboard)/dashboard/search-tools/components/tabs/ScrapeTab.tsx` | Scrape tab |
| `src/app/(dashboard)/dashboard/search-tools/components/tabs/CompareTab.tsx` | Multi-provider compare tab |
| `src/app/(dashboard)/dashboard/search-tools/hooks/useScrapeFetch.ts` | Scrape fetch hook |
| `src/app/api/search/providers/route.ts` | Extended with `kind` + `status` + fetch providers |
| `open-sse/config/searchRegistry.ts` | Source of truth for search provider metadata |
---
## Troubleshooting
| Symptom | Cause | Fix |
|---------|-------|-----|
| Scrape tab shows "endpoint not available" | `/v1/web/fetch` not wired | Verify plan 05 is merged; check `src/app/api/v1/web/fetch/route.ts` exists |
| Provider catalog shows all as "missing" | Credentials not configured | Add credentials in `/dashboard/providers` |
| Scrape content is truncated | Response > 256 KB cap | Expected behavior (D21). Use "view raw" button for full content |
| Compare tab only shows 2 providers | Rate limit active | Two or more providers may be in cooldown — check provider status in Config pane |
| "Size" shown as raw key in table | Missing i18n key | Verify `search.size` exists in the locale file; rebuild i18n |
| Symptom | Cause | Fix |
| ----------------------------------------- | -------------------------- | ------------------------------------------------------------------------------- |
| Scrape tab shows "endpoint not available" | `/v1/web/fetch` not wired | Verify plan 05 is merged; check `src/app/api/v1/web/fetch/route.ts` exists |
| Provider catalog shows all as "missing" | Credentials not configured | Add credentials in `/dashboard/providers` |
| Scrape content is truncated | Response > 256 KB cap | Expected behavior (D21). Use "view raw" button for full content |
| Compare tab only shows 2 providers | Rate limit active | Two or more providers may be in cooldown — check provider status in Config pane |
| "Size" shown as raw key in table | Missing i18n key | Verify `search.size` exists in the locale file; rebuild i18n |
---

View File

@@ -1,13 +1,13 @@
---
title: "Skills Framework"
version: 3.8.6
lastUpdated: 2026-05-28
version: 3.8.40
lastUpdated: 2026-06-28
---
# Skills Framework
> **Source of truth:** `src/lib/skills/` and `src/app/api/skills/`
> **Last updated:** 2026-05-28 — v3.8.6
> **Last updated:** 2026-06-28 — v3.8.40
OmniRoute exposes an extensible Skills framework that lets language models (and operators) compose reusable capabilities — from filesystem reads and HTTP requests to sandboxed code execution and curated marketplace skills.
@@ -19,19 +19,19 @@ A skill is a versioned, schema-defined unit of work. OmniRoute can inject skills
OmniRoute has two distinct but complementary skill systems:
| Dimension | **Omni Skills** (this doc) | **Agent Skills** |
| :--- | :--- | :--- |
| Purpose | LLM tool injection + sandboxed execution | SKILL.md catalog for external agents to discover and consume |
| Source of truth | `src/lib/skills/` + marketplace | `src/lib/agentSkills/` + `skills/` directory |
| Runtime mode | Injected into outbound requests, executed on tool-call events | Static markdown catalog + REST/MCP/A2A discovery endpoints |
| Who uses it | OmniRoute itself (combo routing, inbound LLM calls) | External agents, MCP clients, A2A orchestrators |
| Count | Variable (marketplace-driven) | 42 canonical entries (22 API + 20 CLI) |
| Format | `SkillDefinition` with tool schema + handler | `SKILL.md` frontmatter + markdown body |
| Discovery | `/api/skills/*` REST + `omniroute_skills_*` MCP tools | `/api/agent-skills/*` REST + `omniroute_agent_skills_*` MCP tools + A2A `list-capabilities` |
| Dimension | **Omni Skills** (this doc) | **Agent Skills** |
| :-------------- | :------------------------------------------------------------ | :------------------------------------------------------------------------------------------ |
| Purpose | LLM tool injection + sandboxed execution | SKILL.md catalog for external agents to discover and consume |
| Source of truth | `src/lib/skills/` + marketplace | `src/lib/agentSkills/` + `skills/` directory |
| Runtime mode | Injected into outbound requests, executed on tool-call events | Static markdown catalog + REST/MCP/A2A discovery endpoints |
| Who uses it | OmniRoute itself (combo routing, inbound LLM calls) | External agents, MCP clients, A2A orchestrators |
| Count | Variable (marketplace-driven) | 42 canonical entries (22 API + 20 CLI) |
| Format | `SkillDefinition` with tool schema + handler | `SKILL.md` frontmatter + markdown body |
| Discovery | `/api/skills/*` REST + `omniroute_skills_*` MCP tools | `/api/agent-skills/*` REST + `omniroute_agent_skills_*` MCP tools + A2A `list-capabilities` |
**Omni Skills** are the execution engine — they define what OmniRoute *can do* when an LLM invokes a tool.
**Omni Skills** are the execution engine — they define what OmniRoute _can do_ when an LLM invokes a tool.
**Agent Skills** are the documentation catalog — they explain to external agents *how to use* OmniRoute's REST API and CLI, with structured SKILL.md files that can be fed directly into agent prompts.
**Agent Skills** are the documentation catalog — they explain to external agents _how to use_ OmniRoute's REST API and CLI, with structured SKILL.md files that can be fed directly into agent prompts.
For the Agent Skills catalog, generator, MCP tools, and A2A skill, see [docs/frameworks/AGENT-SKILLS.md](./AGENT-SKILLS.md).
@@ -332,10 +332,10 @@ The `SkillExecutor` (`src/lib/skills/executor.ts`) is a **singleton** that manag
### Default Configuration
| Setting | Default | Configurable via |
|---------|---------|------------------|
| `timeout` | `30000` (30s) | `skillExecutor.setTimeout(ms)` |
| `maxRetries` | `3` | `skillExecutor.setMaxRetries(count)` |
| Setting | Default | Configurable via |
| ------------ | ------------- | ------------------------------------ |
| `timeout` | `30000` (30s) | `skillExecutor.setTimeout(ms)` |
| `maxRetries` | `3` | `skillExecutor.setMaxRetries(count)` |
> **Important**: The executor is a singleton — calling `setTimeout()` affects all subsequent invocations globally. Per-skill timeouts are not currently supported; if you need different timeouts per skill, submit separate processes or fork the executor.
@@ -345,11 +345,11 @@ From `src/lib/skills/types.ts`:
```ts
enum SkillStatus {
PENDING = "pending", // Queued, not yet started
RUNNING = "running", // Handler invoked
SUCCESS = "success", // Handler returned valid output
ERROR = "error", // Handler threw an exception
TIMEOUT = "timeout", // Exceeded the executor's timeout
PENDING = "pending", // Queued, not yet started
RUNNING = "running", // Handler invoked
SUCCESS = "success", // Handler returned valid output
ERROR = "error", // Handler threw an exception
TIMEOUT = "timeout", // Exceeded the executor's timeout
}
```
@@ -413,9 +413,9 @@ The `SkillMode` enum (`src/lib/skills/types.ts`) controls **when and how** skill
```ts
enum SkillMode {
AUTO = "auto", // LLM decides when to call the skill
MANUAL = "manual", // Only invoked by explicit user request
HYBRID = "hybrid", // AUTO scoring + manual override
AUTO = "auto", // LLM decides when to call the skill
MANUAL = "manual", // Only invoked by explicit user request
HYBRID = "hybrid", // AUTO scoring + manual override
}
```
@@ -423,11 +423,11 @@ enum SkillMode {
### When to Use Each Mode
| Mode | LLM behavior | Use case |
|------|--------------|----------|
| `AUTO` | LLM can call the skill when it deems necessary | General-purpose skills (file reads, HTTP requests) |
| `MANUAL` | LLM cannot call the skill; only an explicit `executeSkill` API call invokes it | Sensitive operations (database writes, payments) |
| `HYBRID` | LLM can suggest the skill; user must confirm | Skills that have side effects but aren't dangerous |
| Mode | LLM behavior | Use case |
| -------- | ------------------------------------------------------------------------------ | -------------------------------------------------- |
| `AUTO` | LLM can call the skill when it deems necessary | General-purpose skills (file reads, HTTP requests) |
| `MANUAL` | LLM cannot call the skill; only an explicit `executeSkill` API call invokes it | Sensitive operations (database writes, payments) |
| `HYBRID` | LLM can suggest the skill; user must confirm | Skills that have side effects but aren't dangerous |
### AUTO Scoring
@@ -454,25 +454,26 @@ The browser skill (`src/lib/skills/builtin/browser.ts`) provides headless browse
// Enable in your config
const config: SkillConfig = {
enabled: true,
mode: SkillMode.MANUAL, // Always require explicit invocation
mode: SkillMode.MANUAL, // Always require explicit invocation
allowedSkills: ["browser"],
timeout: 60000, // 60s for page loads
timeout: 60000, // 60s for page loads
maxRetries: 1,
};
```
### Other Built-in Categories
| Category | Skills | Mode |
|----------|--------|------|
| File I/O | `file_read`, `file_write` | AUTO |
| HTTP | `http_request` | AUTO |
| Search | `web_search` | AUTO |
| Code Exec | `eval_code` (sandboxed JavaScript/Python) | HYBRID |
| System | `execute_command` (sandboxed CLI execution) | MANUAL |
| Category | Skills | Mode |
| --------- | ------------------------------------------- | ------ |
| File I/O | `file_read`, `file_write` | AUTO |
| HTTP | `http_request` | AUTO |
| Search | `web_search` | AUTO |
| Code Exec | `eval_code` (sandboxed JavaScript/Python) | HYBRID |
| System | `execute_command` (sandboxed CLI execution) | MANUAL |
### Adding a Custom Skill
See the [Plugin SDK & Skills Integration](../plugins/PLUGIN_SDK.md) for how to add a custom skill via the plugin system.
See the [Plugin SDK & Skills Integration](./PLUGIN_SDK.md) for how to add a custom skill via the plugin system.
---

View File

@@ -1,7 +1,7 @@
---
title: "Traffic Inspector"
version: 3.8.31
lastUpdated: 2026-06-20
version: 3.8.40
lastUpdated: 2026-06-28
---
# Traffic Inspector
@@ -18,21 +18,21 @@ Traffic Inspector is OmniRoute's built-in HTTPS traffic debugger — a Charles P
### What makes Traffic Inspector unique
| Feature | mitmweb | Charles | Fiddler | **OmniRoute Traffic Inspector** |
|---------|:-------:|:-------:|:-------:|:-------------------------------:|
| Web-based | ✓ | ✗ | ✗ | ✓ |
| Open-source | ✓ | ✗ | partial | ✓ |
| **Agent-aware** (knows if request is from Antigravity/Copilot/etc.) | ✗ | ✗ | ✗ | ✓ |
| **LLM-aware** (parses OpenAI/Anthropic/Gemini shape, tokens, model) | ✗ | ✗ | ✗ | ✓ |
| **Model mapping visible** (gemini-3-flash → claude-sonnet-4.7) | ✗ | ✗ | ✗ | ✓ |
| **Proxy/upstream latency split** | partial | ✗ | ✗ | ✓ |
| **Integrated with OmniRoute** routing, fallback, cost | ✗ | ✗ | ✗ | ✓ |
| **System-wide proxy debug** (any app on the machine) | ✓ | ✓ | ✓ | ✓ |
| **Custom host capture** (per-host DNS redirect) | ✓ | ✓ | ✓ | ✓ |
| **HTTP_PROXY env mode** | ✓ | ✓ | ✓ | ✓ |
| **Conversation view** (multi-turn bubbles, tool_use/tool_result) | ✗ | ✗ | ✗ | ✓ |
| **SSE stream merger** (reconstruct from delta events) | ✗ | ✗ | ✗ | ✓ |
| **Session recording** (named, exportable .har/.jsonl) | ✗ | ✓ | ✓ | ✓ |
| Feature | mitmweb | Charles | Fiddler | **OmniRoute Traffic Inspector** |
| ------------------------------------------------------------------- | :-----: | :-----: | :-----: | :-----------------------------: |
| Web-based | ✓ | ✗ | ✗ | ✓ |
| Open-source | ✓ | ✗ | partial | ✓ |
| **Agent-aware** (knows if request is from Antigravity/Copilot/etc.) | ✗ | ✗ | ✗ | ✓ |
| **LLM-aware** (parses OpenAI/Anthropic/Gemini shape, tokens, model) | ✗ | ✗ | ✗ | ✓ |
| **Model mapping visible** (gemini-3-flash → claude-sonnet-4.7) | ✗ | ✗ | ✗ | ✓ |
| **Proxy/upstream latency split** | partial | ✗ | ✗ | ✓ |
| **Integrated with OmniRoute** routing, fallback, cost | ✗ | ✗ | ✗ | ✓ |
| **System-wide proxy debug** (any app on the machine) | ✓ | ✓ | ✓ | ✓ |
| **Custom host capture** (per-host DNS redirect) | ✓ | ✓ | ✓ | ✓ |
| **HTTP_PROXY env mode** | ✓ | ✓ | ✓ | ✓ |
| **Conversation view** (multi-turn bubbles, tool_use/tool_result) | ✗ | ✗ | ✗ | ✓ |
| **SSE stream merger** (reconstruct from delta events) | ✗ | ✗ | ✗ | ✓ |
| **Session recording** (named, exportable .har/.jsonl) | ✗ | ✓ | ✓ | ✓ |
### Architecture in one paragraph
@@ -59,6 +59,7 @@ Traffic Inspector supports **5 simultaneous capture sources**. Each is independe
**Note:** `source` = `"custom-host"`
Example use cases:
- Monitor `api.openai.com` from Python scripts
- Debug `my-internal-llm.company.com`
- Capture traffic from mobile devices on the same network (via ARP spoofing — advanced)
@@ -87,13 +88,15 @@ export HTTPS_PROXY=http://127.0.0.1:8080
**Source:** OS-level proxy settings (applies to all apps on the machine)
**Mechanism:** Uses OS APIs to redirect all HTTP/HTTPS traffic through the HTTP_PROXY listener:
- **macOS:** `networksetup -setwebproxy / -setsecurewebproxy`
- **Linux:** `gsettings set org.gnome.system.proxy` + `/etc/environment`
- **Windows:** `netsh winhttp set proxy 127.0.0.1:8080`
**Reach:** Every application on the machine that respects system proxy settings
**Note:** `source` = `"system-proxy"`
**Reach:** Every application on the machine that respects system proxy settings
**Note:** `source` = `"system-proxy"`
**Safety mechanisms:**
- Auto-disable timer (default 30 min, configurable via `INSPECTOR_SYSTEM_PROXY_GUARD_MINUTES`)
- Previous system proxy state is saved in DB and restored on revert
- Dashboard shows "Reverting system proxy" prompt if user navigates away while active
@@ -112,12 +115,12 @@ This is a substantial subsystem with its own dedicated operator guide — see **
### Capture mode comparison
| Mode | Setup | Sudo? | Reach | Notes |
|------|-------|:-----:|-------|-------|
| 1. AgentBridge | Automatic | Once (cert+hosts) | 9 IDE agents | Default on |
| 2. Custom Hosts | Per-host input | Yes (hosts file) | Any app using that host | Persisted in DB |
| 3. HTTP_PROXY | `export HTTPS_PROXY=...` | No | Apps respecting env | Port 8080, no TLS decrypt by default |
| 4. System-wide | Toggle + confirm | Yes | All apps on machine | Auto-disable in 30 min |
| Mode | Setup | Sudo? | Reach | Notes |
| ----------------- | ----------------------------- | :---------------------: | --------------------------- | ----------------------------------------------------------------------------------------------------------- |
| 1. AgentBridge | Automatic | Once (cert+hosts) | 9 IDE agents | Default on |
| 2. Custom Hosts | Per-host input | Yes (hosts file) | Any app using that host | Persisted in DB |
| 3. HTTP_PROXY | `export HTTPS_PROXY=...` | No | Apps respecting env | Port 8080, no TLS decrypt by default |
| 4. System-wide | Toggle + confirm | Yes | All apps on machine | Auto-disable in 30 min |
| 5. TPROXY decrypt | Toggle (Linux + native addon) | Yes (root + CA install) | Any host on the target port | Decrypts arbitrary hosts; off by default — see [MITM-TPROXY-DECRYPT.md](../security/MITM-TPROXY-DECRYPT.md) |
---
@@ -156,30 +159,30 @@ This is a substantial subsystem with its own dedicated operator guide — see **
### 3.3 Detail pane — 7 tabs
| Tab | Content | Notes |
|-----|---------|-------|
| **Conversation** | Multi-turn chat bubbles (system/user/assistant + tool_use/tool_result) | Normalized from any provider format; only shown for `detectedKind === "llm"` |
| **Headers** | Request + response header tables | Sensitive headers (Authorization, Cookie, api-key) masked by default; "Show secrets" toggle |
| **Request** | Raw body, JSON tree view, model field badge | Pretty-printed JSON or raw text |
| **Response** | Raw body or SSE event list; toggle "Raw ↔ Merged" | SSE merger reconstructs final message from delta events |
| **Timing** | Waterfall: proxy overhead vs upstream latency | Total, TTFB, and size |
| **LLM Details** | Provider, model, messages count, tokens in/out, cost estimate, mapped target | Only shown for LLM requests |
| **Stats** | Recharts: latency timeline, token bar chart, tool call scatter | Only shown when a recorded session is loaded |
| Tab | Content | Notes |
| ---------------- | ---------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- |
| **Conversation** | Multi-turn chat bubbles (system/user/assistant + tool_use/tool_result) | Normalized from any provider format; only shown for `detectedKind === "llm"` |
| **Headers** | Request + response header tables | Sensitive headers (Authorization, Cookie, api-key) masked by default; "Show secrets" toggle |
| **Request** | Raw body, JSON tree view, model field badge | Pretty-printed JSON or raw text |
| **Response** | Raw body or SSE event list; toggle "Raw ↔ Merged" | SSE merger reconstructs final message from delta events |
| **Timing** | Waterfall: proxy overhead vs upstream latency | Total, TTFB, and size |
| **LLM Details** | Provider, model, messages count, tokens in/out, cost estimate, mapped target | Only shown for LLM requests |
| **Stats** | Recharts: latency timeline, token bar chart, tool call scatter | Only shown when a recorded session is loaded |
### 3.4 Toolbar controls
| Control | Action |
|---------|--------|
| ⎉ Pause | Stops rendering new requests; "X new" badge accumulates |
| 🗑 Clear | Clears the UI list (server buffer is not affected) |
| ⬇ Export .har | Downloads current filtered list as HAR file |
| ● Record session | Starts a named recording session |
| Profile selector | LLM only / Custom hosts / All |
| Host filter | Substring match on `host` field |
| Agent filter | Dropdown: All / per-agent |
| Status filter | All / 2xx / 3xx / 4xx / 5xx / error |
| Source filter | All / agent-bridge / custom-host / http-proxy / system-proxy / tproxy |
| **Live** filter | Show only in-flight (open) requests — `liveOnly` toggle (see §4.6) |
| Control | Action |
| ---------------- | --------------------------------------------------------------------- |
| ⎉ Pause | Stops rendering new requests; "X new" badge accumulates |
| 🗑 Clear | Clears the UI list (server buffer is not affected) |
| ⬇ Export .har | Downloads current filtered list as HAR file |
| ● Record session | Starts a named recording session |
| Profile selector | LLM only / Custom hosts / All |
| Host filter | Substring match on `host` field |
| Agent filter | Dropdown: All / per-agent |
| Status filter | All / 2xx / 3xx / 4xx / 5xx / error |
| Source filter | All / agent-bridge / custom-host / http-proxy / system-proxy / tproxy |
| **Live** filter | Show only in-flight (open) requests — `liveOnly` toggle (see §4.6) |
### 3.5 Resizable panels
@@ -223,9 +226,9 @@ Converts OpenAI, Anthropic, and Gemini message formats to a single `NormalizedCo
```ts
interface NormalizedConversation {
request: NormalizedTurn[]; // messages / contents / prompt from request body
response: NormalizedTurn[]; // assistant response (merged via sseMerger)
contextKey: string | null; // SHA-256 system-prompt fingerprint
request: NormalizedTurn[]; // messages / contents / prompt from request body
response: NormalizedTurn[]; // assistant response (merged via sseMerger)
contextKey: string | null; // SHA-256 system-prompt fingerprint
}
```
@@ -246,14 +249,14 @@ For LLM requests, the LLM Details tab extracts:
```ts
interface LlmMetadata {
provider: string | null; // "openai" | "anthropic" | "gemini" | ...
apiKind: string | null; // "chat.completions" | "messages" | "embeddings" | ...
model: string | null; // from request body or response
messages: number; // turn count
tokensIn: number | null; // usage.prompt_tokens / usage.input_tokens
tokensOut: number | null; // usage.completion_tokens / usage.output_tokens
streamed: boolean; // true if SSE response
mappedTo: string | null; // x-omniroute-mapped header
provider: string | null; // "openai" | "anthropic" | "gemini" | ...
apiKind: string | null; // "chat.completions" | "messages" | "embeddings" | ...
model: string | null; // from request body or response
messages: number; // turn count
tokensIn: number | null; // usage.prompt_tokens / usage.input_tokens
tokensOut: number | null; // usage.completion_tokens / usage.output_tokens
streamed: boolean; // true if SSE response
mappedTo: string | null; // x-omniroute-mapped header
costEstimateUsd: number | null; // estimated cost based on OmniRoute pricing
}
```
@@ -286,7 +289,7 @@ pid?: number; // originating process id (Linux only)
processName?: string; // originating process name (Linux only)
```
`src/mitm/inspector/processAttribution.ts` maps the connection's *client*
`src/mitm/inspector/processAttribution.ts` maps the connection's _client_
ephemeral port to a PID + name by:
1. Reading `/proc/net/tcp` and `/proc/net/tcp6` to find the socket inode for the
@@ -312,6 +315,7 @@ support is a follow-up).
### 5.2 Viewing a recorded session
The **Sessions** dropdown in the toolbar lists saved sessions. Selecting one:
- Loads the session's snapshot (frozen state)
- A banner shows: `Viewing recorded session "<name>" — [Back to live]`
- The Stats tab becomes available with Recharts aggregates
@@ -320,10 +324,10 @@ The **Sessions** dropdown in the toolbar lists saved sessions. Selecting one:
Each session can be exported as:
| Format | Use |
|--------|-----|
| Format | Use |
| -------------------------- | ------------------------------------------------------------------------------- |
| **HAR** (HTTP Archive 1.2) | Compatible with Chrome DevTools, Charles, Fiddler — import for offline analysis |
| **JSONL** | One `InterceptedRequest` per line — compatible with `llm-interceptor` format |
| **JSONL** | One `InterceptedRequest` per line — compatible with `llm-interceptor` format |
Export via `GET /api/tools/traffic-inspector/sessions/{id}/export.har` or the ⬇ button in the Sessions dropdown.
@@ -333,20 +337,20 @@ Export via `GET /api/tools/traffic-inspector/sessions/{id}/export.har` or the
Traffic Inspector shows **all intercepted HTTPS traffic**, including authorization headers and request bodies. The following controls are in place:
| Control | Details |
|---------|---------|
| **LOCAL_ONLY** | All routes and the WebSocket endpoint are loopback-only (enforced in `routeGuard.ts` before auth) |
| **Secret masking** | `maskSecrets()` applied to all headers and bodies before `TrafficBuffer.push()` — enabled by default (`INSPECTOR_MASK_SECRETS=true`) |
| **Body size cap** | Bodies > `INSPECTOR_MAX_BODY_KB` (default 1024 KB) are truncated with `"(truncated for performance)"` notice |
| **Sensitive header masking** | `authorization`, `cookie`, `api-key`, `x-api-key`, `proxy-authorization``Bearer ***` in Headers tab; "Show secrets" toggle |
| **CSP** | Strict Content Security Policy on Traffic Inspector pages to prevent XSS via injected response bodies |
| **No persistence by default** | The `TrafficBuffer` is in-memory and lost on server restart. Sessions are persisted only when explicitly recorded |
| Control | Details |
| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| **LOCAL_ONLY** | All routes and the WebSocket endpoint are loopback-only (enforced in `routeGuard.ts` before auth) |
| **Secret masking** | `maskSecrets()` applied to all headers and bodies before `TrafficBuffer.push()` — enabled by default (`INSPECTOR_MASK_SECRETS=true`) |
| **Body size cap** | Bodies > `INSPECTOR_MAX_BODY_KB` (default 1024 KB) are truncated with `"(truncated for performance)"` notice |
| **Sensitive header masking** | `authorization`, `cookie`, `api-key`, `x-api-key`, `proxy-authorization``Bearer ***` in Headers tab; "Show secrets" toggle |
| **CSP** | Strict Content Security Policy on Traffic Inspector pages to prevent XSS via injected response bodies |
| **No persistence by default** | The `TrafficBuffer` is in-memory and lost on server restart. Sessions are persisted only when explicitly recorded |
### Hard Rules applied
| Rule | Application |
|------|-------------|
| **#12** `sanitizeErrorMessage` | All HTTP error responses from Traffic Inspector routes are sanitized |
| Rule | Application |
| --------------------------------- | ------------------------------------------------------------------------------------- |
| **#12** `sanitizeErrorMessage` | All HTTP error responses from Traffic Inspector routes are sanitized |
| **#15 + #17** `isLocalOnlyPath()` | `/api/tools/traffic-inspector/` is LOCAL_ONLY + SPAWN_CAPABLE (system proxy commands) |
### Known limitations
@@ -362,6 +366,7 @@ Traffic Inspector shows **all intercepted HTTPS traffic**, including authorizati
### WebSocket disconnection
If the live tail shows "Disconnected":
1. Check the server is still running: `GET /api/tools/traffic-inspector/capture-modes`
2. Reload the page — the WebSocket reconnects and receives a fresh snapshot
3. If the server was restarted, the in-memory buffer was cleared — old entries are gone unless a session was recorded
@@ -369,11 +374,13 @@ If the live tail shows "Disconnected":
### Port 8080 conflict
If HTTP_PROXY mode fails to start:
```bash
lsof -i :8080 # find the process
```
Change the port:
```bash
# .env
INSPECTOR_HTTP_PROXY_PORT=8888
@@ -384,17 +391,20 @@ INSPECTOR_HTTP_PROXY_PORT=8888
If OmniRoute crashes while system-wide proxy mode is active:
**macOS:**
```bash
networksetup -setwebproxystate Wi-Fi off
networksetup -setsecurewebproxystate Wi-Fi off
```
**Linux (GNOME):**
```bash
gsettings set org.gnome.system.proxy mode 'none'
```
**Windows:**
```cmd
netsh winhttp reset proxy
```
@@ -404,6 +414,7 @@ The dashboard will also offer "Revert system proxy" on next load if it detects t
### Buffer full
When the buffer reaches `INSPECTOR_BUFFER_SIZE` (default 1000), new entries rotate out the oldest. If important requests are being lost:
- Increase `INSPECTOR_BUFFER_SIZE` (e.g., 5000) — trades memory for retention
- Record a session to persist the relevant window to DB
@@ -417,43 +428,43 @@ Base path: `/api/tools/traffic-inspector/`
### Request management
| Method | Path | Description |
|--------|------|-------------|
| GET | `/requests` | List requests (filterable: `?profile=llm&host=&agent=&status=&source=&sessionId=`) |
| GET | `/requests/{id}` | Single request details |
| DELETE | `/requests` | Clear the in-memory buffer |
| POST | `/requests/{id}/replay` | Re-execute the same request through OmniRoute router |
| PUT | `/requests/{id}/annotation` | Save or update a note on a request |
| Method | Path | Description |
| ------ | --------------------------- | ---------------------------------------------------------------------------------- |
| GET | `/requests` | List requests (filterable: `?profile=llm&host=&agent=&status=&source=&sessionId=`) |
| GET | `/requests/{id}` | Single request details |
| DELETE | `/requests` | Clear the in-memory buffer |
| POST | `/requests/{id}/replay` | Re-execute the same request through OmniRoute router |
| PUT | `/requests/{id}/annotation` | Save or update a note on a request |
### WebSocket
| Method | Path | Description |
|--------|------|-------------|
| GET | `/ws` | Live WebSocket stream. Sends `snapshot` on connect, then `new`/`update`/`clear` events |
| Method | Path | Description |
| ------ | ----- | -------------------------------------------------------------------------------------- |
| GET | `/ws` | Live WebSocket stream. Sends `snapshot` on connect, then `new`/`update`/`clear` events |
### Export
| Method | Path | Description |
|--------|------|-------------|
| GET | `/export.har` | Export current filtered list as HAR 1.2 |
| Method | Path | Description |
| ------ | ------------- | --------------------------------------- |
| GET | `/export.har` | Export current filtered list as HAR 1.2 |
### Custom hosts
| Method | Path | Description |
|--------|------|-------------|
| GET | `/hosts` | List custom hosts |
| POST | `/hosts` | Add host (auto-edits `/etc/hosts`) |
| DELETE | `/hosts/{host}` | Remove host |
| PATCH | `/hosts/{host}` | Toggle `enabled` |
| Method | Path | Description |
| ------ | --------------- | ---------------------------------- |
| GET | `/hosts` | List custom hosts |
| POST | `/hosts` | Add host (auto-edits `/etc/hosts`) |
| DELETE | `/hosts/{host}` | Remove host |
| PATCH | `/hosts/{host}` | Toggle `enabled` |
### Capture modes
| Method | Path | Description |
|--------|------|-------------|
| GET | `/capture-modes` | State of the AgentBridge / custom-hosts / HTTP_PROXY / system-proxy modes + the `tls-intercept` toggle |
| POST | `/capture-modes/http-proxy` | Start/stop HTTP_PROXY listener (`{action: "start"\|"stop"}`) |
| POST | `/capture-modes/system-proxy` | Apply/revert system-wide proxy (`{action: "apply"\|"revert"}`) |
| POST | `/capture-modes/tls-intercept` | Toggle HTTPS body decryption in proxy mode (`{enabled: boolean}`) |
| Method | Path | Description |
| ------ | ------------------------------ | ------------------------------------------------------------------------------------------------------ |
| GET | `/capture-modes` | State of the AgentBridge / custom-hosts / HTTP_PROXY / system-proxy modes + the `tls-intercept` toggle |
| POST | `/capture-modes/http-proxy` | Start/stop HTTP_PROXY listener (`{action: "start"\|"stop"}`) |
| POST | `/capture-modes/system-proxy` | Apply/revert system-wide proxy (`{action: "apply"\|"revert"}`) |
| POST | `/capture-modes/tls-intercept` | Toggle HTTPS body decryption in proxy mode (`{enabled: boolean}`) |
> **TPROXY decrypt** (capture mode 5) is driven by a **separate** route under the
> AgentBridge prefix — `GET / POST / DELETE /api/tools/agent-bridge/tproxy` — not
@@ -462,19 +473,19 @@ Base path: `/api/tools/traffic-inspector/`
### Sessions
| Method | Path | Description |
|--------|------|-------------|
| POST | `/sessions` | Start recording (`{name?: string}`) |
| PATCH | `/sessions/{id}` | Stop or rename (`{action: "stop"\|"rename", name?: string}`) |
| GET | `/sessions` | List all saved sessions |
| GET | `/sessions/{id}` | Session snapshot (all requests) |
| DELETE | `/sessions/{id}` | Delete session |
| GET | `/sessions/{id}/export.har` | Export session as HAR 1.2 |
| Method | Path | Description |
| ------ | --------------------------- | ------------------------------------------------------------ |
| POST | `/sessions` | Start recording (`{name?: string}`) |
| PATCH | `/sessions/{id}` | Stop or rename (`{action: "stop"\|"rename", name?: string}`) |
| GET | `/sessions` | List all saved sessions |
| GET | `/sessions/{id}` | Session snapshot (all requests) |
| DELETE | `/sessions/{id}` | Delete session |
| GET | `/sessions/{id}/export.har` | Export session as HAR 1.2 |
### Internal ingest (D4 fallback)
| Method | Path | Description |
|--------|------|-------------|
| POST | `/internal/ingest` | Accepts intercepted request from `server.cjs` passthrough path; requires `INSPECTOR_INTERNAL_INGEST_TOKEN` header |
| Method | Path | Description |
| ------ | ------------------ | ----------------------------------------------------------------------------------------------------------------- |
| POST | `/internal/ingest` | Accepts intercepted request from `server.cjs` passthrough path; requires `INSPECTOR_INTERNAL_INGEST_TOKEN` header |
Full OpenAPI schemas: `docs/openapi.yaml` → tag `Traffic Inspector`.

View File

@@ -1,13 +1,13 @@
---
title: "Webhooks"
version: 3.8.2
lastUpdated: 2026-05-13
version: 3.8.40
lastUpdated: 2026-06-28
---
# Webhooks
> **Source of truth:** `src/lib/webhookDispatcher.ts`, `src/lib/db/webhooks.ts`, `src/app/api/webhooks/`
> **Last updated:** 2026-05-13 — v3.8.0
> **Last updated:** 2026-06-28 — v3.8.40
OmniRoute can fire HTTP webhooks on platform events. Use them to integrate with
Slack, PagerDuty, Datadog, internal alerting services, or any HTTP receiver.

View File

@@ -13,6 +13,8 @@
"OBSIDIAN_CONTEXT",
"OPENCODE",
"PLUGIN_MARKETPLACE",
"PLUGINS",
"PLUGIN_SDK",
"SKILLS",
"WEBHOOKS"
]