mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-26 09:52:11 +03:00
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:
committed by
GitHub
parent
1c18be4f8f
commit
7c23dab64d
@@ -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).
|
||||
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
@@ -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 1–8, 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`.
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)`,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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`.
|
||||
|
||||

|
||||

|
||||
|
||||
> 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) |
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 |
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -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
114
docs/frameworks/PLUGINS.md
Normal 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()`.
|
||||
@@ -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
|
||||
|
||||
253
docs/frameworks/PLUGIN_SDK.md
Normal file
253
docs/frameworks/PLUGIN_SDK.md
Normal 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;
|
||||
},
|
||||
});
|
||||
```
|
||||
@@ -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 |
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -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`.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
"OBSIDIAN_CONTEXT",
|
||||
"OPENCODE",
|
||||
"PLUGIN_MARKETPLACE",
|
||||
"PLUGINS",
|
||||
"PLUGIN_SDK",
|
||||
"SKILLS",
|
||||
"WEBHOOKS"
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user