mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-26 09:52:11 +03:00
Integrated into release/v3.8.8. Applied review fixes: moved the SELECT 1 into a pingDb() db helper (no raw SQL in route, Hard Rule #5) + the 503 catch no longer leaks err.message (Hard Rule #12). Thanks @herjarsa!
This commit is contained in:
committed by
GitHub
parent
009ad13a91
commit
fd26e601a2
@@ -139,18 +139,32 @@ curl -X POST http://localhost:20128/a2a \
|
||||
|
||||
## Available Skills
|
||||
|
||||
OmniRoute exposes 5 A2A skills wired in `src/lib/a2a/taskExecution.ts::A2A_SKILL_HANDLERS`. Each skill module lives in `src/lib/a2a/skills/`.
|
||||
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 |
|
||||
| :----------------- | :------------------- | :------------------------------------------------------------------------------------------ |
|
||||
| Smart Routing | `smart-routing` | Routes a prompt through the optimal provider/combo using OmniRoute's combo engine + scoring |
|
||||
| Quota Management | `quota-management` | Reports per-provider quota state, helps callers decide when to throttle/switch |
|
||||
| Provider Discovery | `provider-discovery` | Lists installed providers with capabilities, free-tier flags, OAuth status |
|
||||
| Cost Analysis | `cost-analysis` | Estimates cost of a request/conversation given the catalog + recent usage |
|
||||
| Health Report | `health-report` | Aggregates circuit breaker, cooldown, lockout state per provider |
|
||||
| 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).
|
||||
|
||||
### `list-capabilities` Skill Detail
|
||||
|
||||
The `list-capabilities` skill is particularly useful for external agents that need to discover what OmniRoute exposes before sending API calls. It returns a structured markdown table artifact:
|
||||
|
||||
```
|
||||
| ID | Name | Category | Area | Endpoints/Commands | Raw URL |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| omni-auth | Auth & Sessions | api | auth | POST /api/auth/login, ... | https://raw.githubusercontent.com/... |
|
||||
...
|
||||
```
|
||||
|
||||
Each row includes the `rawUrl` column so agents can immediately fetch the full SKILL.md. The `metadata.totalSkills` field is always `42`. Implementation: `src/lib/a2a/skills/listCapabilities.ts`. See also [AGENT-SKILLS.md](./AGENT-SKILLS.md).
|
||||
|
||||
---
|
||||
|
||||
## REST API (auxiliary)
|
||||
|
||||
301
docs/frameworks/AGENT-SKILLS.md
Normal file
301
docs/frameworks/AGENT-SKILLS.md
Normal file
@@ -0,0 +1,301 @@
|
||||
---
|
||||
title: "OmniRoute Agent Skills Catalog"
|
||||
version: 3.8.6
|
||||
lastUpdated: 2026-05-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
|
||||
|
||||
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.
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
The catalog contains **42 canonical Agent Skills** (22 REST API + 20 CLI). Each skill has:
|
||||
|
||||
- A **canonical ID** (`omni-auth`, `cli-serve`, etc.)
|
||||
- A **SKILL.md** file in `skills/{id}/SKILL.md` with YAML frontmatter (`name`, `description`) + rich markdown body
|
||||
- **REST endpoints** (API skills) or **CLI subcommands** (CLI skills) derived from the OpenAPI spec and CLI registry
|
||||
- A **GitHub raw URL** for live fetch: `https://raw.githubusercontent.com/diegosouzapw/OmniRoute/refs/heads/main/skills/{id}/SKILL.md`
|
||||
|
||||
---
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
src/shared/constants/agentSkills.ts — 42-entry curated list (name/desc/category/area/icon)
|
||||
src/lib/agentSkills/
|
||||
catalog.ts — getCatalog(), getSkillById(), filterCatalog(), computeCoverage()
|
||||
generator.ts — generateAgentSkills() writes SKILL.md to skills/{id}/
|
||||
openapiParser.ts — extracts REST endpoints from docs/reference/openapi.yaml
|
||||
cliRegistryParser.ts — extracts CLI subcommands from bin/cli-registry.ts
|
||||
schemas.ts — Zod schemas: AgentSkillSchema, SkillCoverageSchema, etc.
|
||||
types.ts — TypeScript interfaces: AgentSkill, SkillCoverage, etc.
|
||||
|
||||
skills/{id}/SKILL.md — Generated + curated markdown files (42 total)
|
||||
|
||||
src/app/api/agent-skills/
|
||||
route.ts — GET /api/agent-skills
|
||||
[id]/route.ts — GET /api/agent-skills/{id}
|
||||
[id]/raw/route.ts — GET /api/agent-skills/{id}/raw (text/markdown)
|
||||
coverage/route.ts — GET /api/agent-skills/coverage
|
||||
generate/route.ts — POST /api/agent-skills/generate (auth required)
|
||||
|
||||
open-sse/mcp-server/tools/agentSkillTools.ts — 3 MCP tools (list, get, coverage)
|
||||
src/lib/a2a/skills/listCapabilities.ts — A2A skill: list-capabilities
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## SKILL.md Format
|
||||
|
||||
```markdown
|
||||
---
|
||||
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 -->
|
||||
```
|
||||
|
||||
The generator preserves content between `<!-- skill:custom-start -->` and `<!-- skill:custom-end -->` on regeneration. Ten skills have curated custom blocks:
|
||||
|
||||
`omni-mcp`, `omni-compression`, `cli-providers`, `cli-eval`, `omni-agents-a2a`, `omni-combos-routing`, `omni-auth`, `omni-resilience`, `omni-inference`, `cli-serve`.
|
||||
|
||||
---
|
||||
|
||||
## 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 |
|
||||
|
||||
Example — list all API skills:
|
||||
|
||||
```bash
|
||||
curl "http://localhost:20128/api/agent-skills?category=api"
|
||||
```
|
||||
|
||||
Example — fetch a single SKILL.md:
|
||||
|
||||
```bash
|
||||
curl -H "Accept: text/markdown" "http://localhost:20128/api/agent-skills/omni-providers/raw"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## MCP Discovery
|
||||
|
||||
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) |
|
||||
|
||||
See [MCP-SERVER.md](./MCP-SERVER.md) for scope wiring and authentication.
|
||||
|
||||
---
|
||||
|
||||
## A2A Discovery
|
||||
|
||||
The A2A skill `list-capabilities` returns the full 42-skill catalog as a markdown table artifact. External orchestrators can invoke it via:
|
||||
|
||||
```json
|
||||
{
|
||||
"jsonrpc": "2.0", "id": "1",
|
||||
"method": "message/send",
|
||||
"params": {
|
||||
"skill": "list-capabilities",
|
||||
"messages": [{"role": "user", "content": "List all capabilities"}]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
See [A2A-SERVER.md](./A2A-SERVER.md) for protocol details.
|
||||
|
||||
---
|
||||
|
||||
## Catalog — 42 Skill IDs
|
||||
|
||||
### 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 (37 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` |
|
||||
|
||||
---
|
||||
|
||||
## How External Agents Consume Skills
|
||||
|
||||
### 1. Discovery via REST
|
||||
|
||||
```bash
|
||||
# Get the full catalog
|
||||
curl "http://your-omniroute/api/agent-skills" | jq '.skills[] | {id, name, category}'
|
||||
|
||||
# Get SKILL.md for context injection
|
||||
curl "http://your-omniroute/api/agent-skills/omni-providers/raw" > omni-providers.md
|
||||
```
|
||||
|
||||
### 2. Discovery via MCP
|
||||
|
||||
```typescript
|
||||
// In a Claude Desktop / Cursor MCP client:
|
||||
const result = await client.callTool("omniroute_agent_skills_list", { category: "api" });
|
||||
// result.skills → array of AgentSkill with rawUrl for each
|
||||
```
|
||||
|
||||
### 3. Discovery via A2A
|
||||
|
||||
```python
|
||||
import requests
|
||||
|
||||
resp = requests.post("http://your-omniroute/a2a", json={
|
||||
"jsonrpc": "2.0", "id": "1",
|
||||
"method": "message/send",
|
||||
"params": {"skill": "list-capabilities", "messages": [{"role": "user", "content": "list"}]}
|
||||
})
|
||||
table = resp.json()["result"]["artifacts"][0]["content"]
|
||||
# table is a markdown table with all 42 skill IDs + rawUrl columns
|
||||
```
|
||||
|
||||
### 4. Direct GitHub raw fetch (no server required)
|
||||
|
||||
```bash
|
||||
BASE="https://raw.githubusercontent.com/diegosouzapw/OmniRoute/refs/heads/main/skills"
|
||||
curl "${BASE}/omni-providers/SKILL.md"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Generator
|
||||
|
||||
The generator reads the curated catalog + OpenAPI spec + CLI registry and writes `skills/{id}/SKILL.md` for each entry:
|
||||
|
||||
```bash
|
||||
# Preview (dry run, no writes)
|
||||
curl -X POST http://localhost:20128/api/agent-skills/generate \
|
||||
-H "Authorization: Bearer <admin-key>" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"dryRun":true}'
|
||||
|
||||
# Full regeneration
|
||||
curl -X POST http://localhost:20128/api/agent-skills/generate \
|
||||
-H "Authorization: Bearer <admin-key>" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"dryRun":false,"prune":false}'
|
||||
|
||||
# Regenerate specific IDs
|
||||
curl -X POST http://localhost:20128/api/agent-skills/generate \
|
||||
-H "Authorization: Bearer <admin-key>" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"dryRun":false,"onlyIds":["omni-providers","cli-serve"]}'
|
||||
```
|
||||
|
||||
The generator response is a `GeneratorReport`:
|
||||
|
||||
```json
|
||||
{
|
||||
"generated": ["omni-providers", "cli-serve"],
|
||||
"unchanged": [],
|
||||
"pruned": [],
|
||||
"orphansDetected": [],
|
||||
"errors": []
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Coverage API
|
||||
|
||||
```bash
|
||||
curl "http://localhost:20128/api/agent-skills/coverage"
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"api": {"have": 22, "total": 22},
|
||||
"cli": {"have": 20, "total": 20},
|
||||
"totalSkills": 42,
|
||||
"generatedAt": "2026-05-28T00:00:00.000Z"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Related
|
||||
|
||||
- [SKILLS.md](./SKILLS.md) — Omni Skills framework (LLM tool injection + marketplace)
|
||||
- [MCP-SERVER.md](./MCP-SERVER.md) — MCP tool catalog (`omniroute_agent_skills_*` tools)
|
||||
- [A2A-SERVER.md](./A2A-SERVER.md) — A2A protocol (`list-capabilities` skill)
|
||||
- `src/lib/agentSkills/` — catalog, generator, parsers
|
||||
- `skills/` — generated SKILL.md files (42 entries)
|
||||
406
docs/frameworks/AGENTBRIDGE.md
Normal file
406
docs/frameworks/AGENTBRIDGE.md
Normal file
@@ -0,0 +1,406 @@
|
||||
---
|
||||
title: "AgentBridge"
|
||||
version: 3.8.6
|
||||
lastUpdated: 2026-05-28
|
||||
---
|
||||
|
||||
# AgentBridge
|
||||
|
||||
AgentBridge is OmniRoute's MITM (Man-in-the-Middle) proxy that intercepts HTTPS traffic from IDE AI agents and reroutes it through OmniRoute's unified routing engine. It supports **9 IDE agents** — Antigravity, Kiro, GitHub Copilot, OpenAI Codex, Cursor, Zed, Claude Code, Open Code, and Trae (investigating) — making OmniRoute the broadest-coverage MITM proxy for AI coding assistants on the market.
|
||||
|
||||
**Dashboard location:** `/dashboard/tools/agent-bridge`
|
||||
**Sidebar group:** Tools (after Cloud Agents)
|
||||
**See also:** [`TRAFFIC_INSPECTOR.md`](./TRAFFIC_INSPECTOR.md) — monitor all intercepted traffic in real-time.
|
||||
|
||||
---
|
||||
|
||||
## §1 Overview
|
||||
|
||||
### What is AgentBridge?
|
||||
|
||||
When an IDE agent (e.g., GitHub Copilot, Cursor, Claude Code) makes an API call, it connects directly to the upstream AI provider (OpenAI, Anthropic, etc.). AgentBridge intercepts that connection transparently at the TLS level — without requiring any agent configuration change — and rewrites the request through OmniRoute.
|
||||
|
||||
This means you can:
|
||||
|
||||
- **Reroute any agent to any provider**: Copilot talking to OpenAI? Redirect it to Anthropic Claude, Gemini, or any of OmniRoute's 160+ providers.
|
||||
- **Apply model mappings**: `gemini-3-flash` → `claude-sonnet-4.7` transparently at the handler level.
|
||||
- **Observe all agent traffic**: every intercepted request is published to the [Traffic Inspector](./TRAFFIC_INSPECTOR.md).
|
||||
- **Apply OmniRoute resilience**: combo routing, circuit breakers, fallbacks, and cost tracking work for IDE agent traffic too.
|
||||
|
||||
### 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 | ✗ | ✗ | ✓ | ✓ |
|
||||
|
||||
---
|
||||
|
||||
## §2 Architecture
|
||||
|
||||
### 2.1 Components overview
|
||||
|
||||
```
|
||||
IDE Agent (VS Code / Cursor / etc.)
|
||||
│ HTTPS (port 443)
|
||||
▼
|
||||
/etc/hosts — 127.0.0.1 api.githubcopilot.com ← DNS redirect
|
||||
│
|
||||
▼
|
||||
src/mitm/server.cjs (port 443, CJS child process)
|
||||
│ resolves target by Host header SNI
|
||||
│ generates per-SNI TLS cert signed by AgentBridge CA
|
||||
├── Bypass list match? → TCP passthrough (no decrypt)
|
||||
├── Target match? → fetch → OmniRoute router (port 20128)
|
||||
│ └── handler.intercept() — TypeScript
|
||||
│ ├── maskSecrets() on request body/headers
|
||||
│ ├── TrafficBuffer.push() — publishes to Traffic Inspector
|
||||
│ └── fetchRouter() → /v1/chat/completions
|
||||
└── No match? → TCP passthrough (no decrypt)
|
||||
```
|
||||
|
||||
### 2.2 MITM server (`src/mitm/server.cjs`)
|
||||
|
||||
The core MITM server runs as a Node.js CJS child process (to avoid rewriting the existing CJS codebase). It:
|
||||
|
||||
- Listens on port 443 (requires privilege or `authbind`/`setcap`)
|
||||
- Receives CONNECT tunnels from the OS (via `/etc/hosts` DNS redirect)
|
||||
- Generates per-SNI TLS certificates signed by the AgentBridge CA (`DATA_DIR/mitm/ca.crt`)
|
||||
- Resolves the target agent by Host header via `targets/index.ts` registry
|
||||
- Dispatches to the TypeScript handler layer via HTTP to `http://127.0.0.1:20128`
|
||||
|
||||
`TARGET_HOSTS` is loaded from `DATA_DIR/mitm/targets.json` (written by `targets/index.ts` at boot), allowing dynamic updates without restarting the CJS server.
|
||||
|
||||
### 2.3 Handler base (`src/mitm/handlers/base.ts`)
|
||||
|
||||
All agent handlers extend `MitmHandlerBase`:
|
||||
|
||||
```ts
|
||||
export abstract class MitmHandlerBase {
|
||||
abstract readonly agentId: AgentId;
|
||||
|
||||
abstract intercept(
|
||||
req: IncomingMessage,
|
||||
res: ServerResponse,
|
||||
body: Buffer,
|
||||
mappedModel: string,
|
||||
): Promise<void>;
|
||||
|
||||
// Protected helpers: fetchRouter, pipeSSE, hookBufferStart, hookBufferUpdate
|
||||
}
|
||||
```
|
||||
|
||||
Each handler calls `hookBufferStart()` before proxying and `hookBufferUpdate()` when complete. These push `InterceptedRequest` entries into `globalTrafficBuffer` (see [Traffic Inspector](./TRAFFIC_INSPECTOR.md) §4).
|
||||
|
||||
### 2.4 Targets registry (`src/mitm/targets/`)
|
||||
|
||||
Each agent has a declarative target file:
|
||||
|
||||
```ts
|
||||
// src/mitm/targets/copilot.ts
|
||||
export const COPILOT_TARGET: MitmTarget = {
|
||||
id: "copilot",
|
||||
name: "GitHub Copilot",
|
||||
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" },
|
||||
],
|
||||
handler: () => import("../handlers/copilot"),
|
||||
riskNoticeKey: "providers.riskNotice.oauth",
|
||||
};
|
||||
```
|
||||
|
||||
The registry (`targets/index.ts`) exports `ALL_TARGETS` and emits `DATA_DIR/mitm/targets.json` on boot.
|
||||
|
||||
### 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
|
||||
```
|
||||
|
||||
### 2.6 Upstream CA cert (`src/mitm/upstreamTrust.ts`)
|
||||
|
||||
For corporate network environments with a custom CA:
|
||||
|
||||
```bash
|
||||
AGENTBRIDGE_UPSTREAM_CA_CERT=/path/to/corporate-ca.pem
|
||||
```
|
||||
|
||||
When set, configures `undici`'s global dispatcher with the extra CA cert, allowing AgentBridge to reach upstream providers through corporate TLS termination proxies.
|
||||
|
||||
### 2.7 Secret masking (`src/mitm/maskSecrets.ts`)
|
||||
|
||||
Applied to all request bodies and headers **before** they enter the Traffic Inspector buffer or any log:
|
||||
|
||||
- `sk-` / `ak-` / `pk-` prefixed tokens (OpenAI/Anthropic-style)
|
||||
- `Authorization: Bearer <token>` headers
|
||||
- Generic long tokens (≥40 chars)
|
||||
|
||||
---
|
||||
|
||||
## §3 Setup
|
||||
|
||||
### 3.1 Start/stop the MITM server
|
||||
|
||||
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 |
|
||||
| Regenerate Cert | Creates a new CA keypair (all existing per-agent certs are invalidated) |
|
||||
|
||||
### 3.2 Trust the certificate
|
||||
|
||||
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
|
||||
```
|
||||
|
||||
Or use the "Trust Cert" button in the dashboard (runs the appropriate command for your OS, with sudo prompt if needed).
|
||||
|
||||
### 3.3 DNS routing
|
||||
|
||||
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
|
||||
```
|
||||
|
||||
### 3.4 Model mapping
|
||||
|
||||
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` |
|
||||
|
||||
Wildcard `*` maps any unrecognized model to the specified target. Persisted in `agent_bridge_mappings` table.
|
||||
|
||||
### 3.5 Risk notice
|
||||
|
||||
AgentBridge intercepts credentials (OAuth tokens, API keys) that the IDE uses to authenticate with upstream providers. These are **masked before logging** (see §2.7) but are visible to OmniRoute's MITM layer. First activation of each agent shows a dismissible risk notice modal.
|
||||
|
||||
---
|
||||
|
||||
## §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 |
|
||||
|
||||
### Setup wizard steps (per agent)
|
||||
|
||||
Each agent card has a 3-step setup wizard:
|
||||
|
||||
1. **Verify prerequisites** — Server running? Cert trusted? IDE installed (auto-detected)?
|
||||
2. **Enable DNS** — Adds `/etc/hosts` entries (requires sudo). Shows exactly which lines will be added.
|
||||
3. **Map models** — Optional model mapping table. Wildcards accepted.
|
||||
|
||||
### Agent detection
|
||||
|
||||
For agents 1–8, AgentBridge attempts to auto-detect IDE installation:
|
||||
|
||||
```ts
|
||||
export async function detectAgent(agentId: AgentId): Promise<DetectionResult>
|
||||
// Returns: { installed: boolean, version?: string, path?: string }
|
||||
```
|
||||
|
||||
Detection uses OS-specific paths and binary checks (e.g., `code --list-extensions | grep github.copilot` for Copilot, `~/.config/antigravity/` for Antigravity).
|
||||
|
||||
---
|
||||
|
||||
## §5 Security
|
||||
|
||||
### 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 |
|
||||
| **#15 + #17** `isLocalOnlyPath()` | `/api/tools/agent-bridge/` is LOCAL_ONLY + SPAWN_CAPABLE — loopback enforced before auth |
|
||||
|
||||
### Bypass list for sensitive hosts
|
||||
|
||||
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)
|
||||
|
||||
User-added bypass patterns are stored in `agent_bridge_bypass` table and take precedence over everything.
|
||||
|
||||
### Secret masking
|
||||
|
||||
`maskSecrets()` from `src/mitm/maskSecrets.ts` is applied:
|
||||
- On every request body before `TrafficBuffer.push()`
|
||||
- On every header before logging or broadcasting
|
||||
|
||||
Patterns: `sk-`/`ak-`/`pk-` prefix tokens, `Bearer` tokens, and generic tokens ≥40 characters.
|
||||
|
||||
### Upstream CA cert
|
||||
|
||||
When `AGENTBRIDGE_UPSTREAM_CA_CERT` is set, the file is read at startup. If the path exists but the file is unreadable, AgentBridge logs a clear error and refuses to start (prevents silent TLS failures in corporate environments).
|
||||
|
||||
### Known limitations
|
||||
|
||||
- **Port 443 requires privilege**: On Linux, AgentBridge needs `setcap 'cap_net_bind_service=+ep'` on the Node binary, or run via `authbind`. The Setup Wizard displays OS-specific instructions.
|
||||
- **IDE restart required**: After DNS redirect, the IDE must be restarted for the new host resolution to take effect.
|
||||
- **Hardcoded OAuth tokens**: Some agents (Kiro, Antigravity) store OAuth refresh tokens locally. These are transparent to AgentBridge — it sees the Bearer token in each request, which is masked before logging.
|
||||
|
||||
---
|
||||
|
||||
## §6 Troubleshooting
|
||||
|
||||
### Port 443 conflict
|
||||
|
||||
If another process is already listening on port 443 (web server, VPN, etc.):
|
||||
|
||||
```bash
|
||||
lsof -i :443 # find the process
|
||||
sudo fuser -k 443/tcp # force-kill (use with care)
|
||||
```
|
||||
|
||||
Alternatively, configure a non-privileged port in AgentBridge settings and set up `iptables` / `pf` redirect rules.
|
||||
|
||||
### Certificate not trusted
|
||||
|
||||
If the IDE shows TLS errors after starting AgentBridge:
|
||||
|
||||
1. Verify the cert was installed: `security find-certificate -c "OmniRoute AgentBridge"` (macOS) or `certutil -L -d sql:$HOME/.pki/nssdb` (Linux/NSS)
|
||||
2. Some apps maintain their own trust store (Firefox, Chrome on Linux). Run "Trust Cert" again and check the NSS/Firefox-specific cert store.
|
||||
3. Restart the IDE after trusting — in-flight TLS sessions use the old trust state.
|
||||
|
||||
### 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
|
||||
# Linux (systemd-resolved)
|
||||
sudo systemctl restart systemd-resolved
|
||||
# Windows
|
||||
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`)
|
||||
|
||||
---
|
||||
|
||||
## §7 API reference
|
||||
|
||||
All routes are `LOCAL_ONLY` (loopback-only, enforced before auth) and `SPAWN_CAPABLE`. See `src/server/authz/routeGuard.ts`.
|
||||
|
||||
Base path: `/api/tools/agent-bridge/`
|
||||
|
||||
| Method | Path | Description |
|
||||
|--------|------|-------------|
|
||||
| GET | `/api/tools/agent-bridge/agents` | List all 9 agents with current state |
|
||||
| GET | `/api/tools/agent-bridge/state` | Global server state (running, port, cert info) |
|
||||
| POST | `/api/tools/agent-bridge/server` | Start/stop/restart server (`action: "start"\|"stop"\|"restart"\|"trust-cert"\|"regenerate-cert"`) |
|
||||
| GET | `/api/tools/agent-bridge/agents/{id}/state` | State of one agent (dns_enabled, cert_trusted, etc.) |
|
||||
| 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` | Update model mappings |
|
||||
| GET | `/api/tools/agent-bridge/bypass` | List bypass patterns |
|
||||
| PUT | `/api/tools/agent-bridge/bypass` | Update bypass patterns |
|
||||
| POST | `/api/tools/agent-bridge/cert` | Download or regenerate CA cert |
|
||||
| GET | `/api/tools/agent-bridge/upstream-ca` | Get configured upstream CA path |
|
||||
| POST | `/api/tools/agent-bridge/upstream-ca` | Set upstream CA cert path |
|
||||
|
||||
Full OpenAPI schemas: `docs/reference/openapi.yaml` → tag `AgentBridge`.
|
||||
|
||||
---
|
||||
|
||||
## §8 Roadmap
|
||||
|
||||
### Trae investigation
|
||||
|
||||
Trae is a relatively new AI coding assistant. Before implementing a handler:
|
||||
|
||||
1. Identify the binary/extension in VS Code / JetBrains marketplaces or as a standalone app
|
||||
2. Capture traffic with mitmproxy to discover API hosts and endpoint shapes
|
||||
3. Determine authentication mechanism
|
||||
4. Assess go/no-go based on TOS and API discoverability
|
||||
|
||||
Until investigation completes, the Trae card in the dashboard shows a "Investigating" badge with a "Report viability" link. The handler stub at `src/mitm/handlers/trae.ts` throws a structured `Not yet implemented` error.
|
||||
|
||||
### Backlog agents (MITM required — no custom base URL support)
|
||||
|
||||
The following tools do not support custom base URLs in their current versions, making MITM the only interception path. Viability assessment is pending:
|
||||
|
||||
- **Windsurf** (Codeium/Cognition)
|
||||
- **Amp** (Sourcegraph)
|
||||
- **Amazon Q / Kiro CLI** (AWS Bedrock — separate from Kiro IDE)
|
||||
- **Cowork** (Anthropic desktop)
|
||||
|
||||
Note: GitHub Copilot CLI ≥v1.0.19 supports `COPILOT_PROVIDER_BASE_URL` — use direct config instead of MITM for that tool.
|
||||
@@ -12,7 +12,7 @@ lastUpdated: 2026-05-30
|
||||
|
||||

|
||||
|
||||
> Source: [diagrams/mcp-tools-43.mmd](../diagrams/mcp-tools-43.mmd) (update from `mcp-tools-37` when regenerating)
|
||||
> Source: [diagrams/mcp-tools-43.mmd](../diagrams/mcp-tools-43.mmd) (regenerate via `npm run docs:render-diagrams`).
|
||||
|
||||
## Installation
|
||||
|
||||
@@ -203,6 +203,18 @@ curl -X DELETE http://localhost:20128/api/settings/notion
|
||||
| `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) |
|
||||
|
||||
## Agent Skill Catalog Tools (3)
|
||||
|
||||
Defined in `open-sse/mcp-server/tools/agentSkillTools.ts`. Backed by `src/lib/agentSkills/catalog`. These tools expose the 42-entry Agent Skills documentation catalog to MCP clients and external agents. Scope: `read:catalog`.
|
||||
|
||||
| Tool | Scopes | Description |
|
||||
| :--------------------------------- | :------------- | :--------------------------------------------------------------------------------------------------------------- |
|
||||
| `omniroute_agent_skills_list` | `read:catalog` | List all 42 agent skills with optional `category` (api\|cli) and `area` filters; returns metadata + coverage |
|
||||
| `omniroute_agent_skills_get` | `read:catalog` | Get full metadata + SKILL.md content for a single skill by canonical `id` |
|
||||
| `omniroute_agent_skills_coverage` | `read:catalog` | Coverage stats: how many of the 22 API and 20 CLI skills have SKILL.md files on the filesystem vs catalog totals |
|
||||
|
||||
See [AGENT-SKILLS.md](./AGENT-SKILLS.md) for the full catalog and how external agents consume it.
|
||||
|
||||
## Related Frameworks (v3.8.0)
|
||||
|
||||
The MCP tool inventory above (43 tools = 30 core + 3 memory + 4 skills + 6 notion) is intentionally
|
||||
@@ -282,6 +294,7 @@ MCP tools are authenticated through API key scopes. Scope enforcement is central
|
||||
| `read:skills` | `skills_list`, `skills_executions` |
|
||||
| `write:skills` | `skills_enable` |
|
||||
| `execute:skills` | `skills_execute` |
|
||||
| `read:catalog` | `agent_skills_list`, `agent_skills_get`, `agent_skills_coverage` |
|
||||
|
||||
Wildcard scopes are supported: `read:*` grants all read-scopes, `*` grants full access.
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
---
|
||||
title: "Memory System"
|
||||
version: 3.8.2
|
||||
lastUpdated: 2026-05-13
|
||||
version: 3.8.6
|
||||
lastUpdated: 2026-05-28
|
||||
---
|
||||
|
||||
# Memory System
|
||||
|
||||
> **Source of truth:** `src/lib/memory/` and `src/app/api/memory/`
|
||||
> **Last updated:** 2026-05-13 — v3.8.0
|
||||
> **Last updated:** 2026-05-28 — v3.8.6 (plan 21 — Memory Engine Redesign)
|
||||
|
||||
OmniRoute provides persistent conversational memory keyed by API key (and
|
||||
optionally session id). Memories are extracted automatically from LLM responses
|
||||
@@ -27,17 +27,143 @@ Client → /v1/chat/completions (apiKeyInfo resolved upstream)
|
||||
→ resolveMemoryOwnerId(apiKeyInfo) # extracts id
|
||||
→ getMemorySettings() # cached settings
|
||||
→ shouldInjectMemory(body, {enabled}) # gate
|
||||
→ retrieveMemories(apiKeyId, config) # SQL + optional FTS5
|
||||
→ retrieveMemories(apiKeyId, config) # SQL + FTS5 + optional vector
|
||||
→ injectMemory(body, memories, provider) # system or user message
|
||||
→ upstream provider call
|
||||
→ on response: extractFacts(text, apiKeyId, sessionId) # non-blocking
|
||||
→ setImmediate → createMemory(fact) per match
|
||||
→ embed(content) + upsertVector(id, vec)
|
||||
```
|
||||
|
||||
The injection and extraction call-sites are wired in
|
||||
`open-sse/handlers/chatCore.ts` (look for `retrieveMemories`, `injectMemory`,
|
||||
and `extractFacts`).
|
||||
|
||||
## Engine architecture (3-tier resolution)
|
||||
|
||||
The Memory Engine resolves the retrieval path at runtime based on available
|
||||
infrastructure and settings. Three tiers exist, applied in priority order:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ TIER 0 — Keyword (FTS5) │
|
||||
│ Always available. SQLite FTS5 full-text search over │
|
||||
│ content + key. Used when strategy = "exact" or as fallback. │
|
||||
└──────────────────────────────────┬──────────────────────────┘
|
||||
│ strategy = semantic|hybrid?
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ TIER 1 — Embedded Vector (sqlite-vec) │
|
||||
│ sqlite-vec v0.1.9 loaded via db.loadExtension(). │
|
||||
│ KNN brute-force over Float32 vectors. Active when: │
|
||||
│ • sqlite-vec loadExtension succeeds │
|
||||
│ • An embedding source is available (remote | static | │
|
||||
│ transformers) that can produce a Float32Array │
|
||||
│ • vec_memories table exists (created on first ready()) │
|
||||
└──────────────────────────────────┬──────────────────────────┘
|
||||
│ qdrant.enabled?
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ TIER 2 — Qdrant (opt-in external vector database) │
|
||||
│ When enabled, replaces sqlite-vec for semantic/hybrid. │
|
||||
│ Requires running Qdrant instance + configured host/port. │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
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
|
||||
is also unavailable).
|
||||
|
||||
## Embedding sources
|
||||
|
||||
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 |
|
||||
|
||||
**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`.
|
||||
4. Otherwise → `null` (degrades to FTS5 keyword search).
|
||||
|
||||
The embedding cache (`src/lib/memory/embedding/cache.ts`) uses an in-memory
|
||||
LRU map keyed by `${source}:${model}:${dim}:${sha256(text)}`, capped at
|
||||
`MEMORY_EMBEDDING_CACHE_MAX` entries (default 1000) with a TTL of
|
||||
`MEMORY_EMBEDDING_CACHE_TTL_MS` (default 5 min). Shared across all callers
|
||||
per process lifecycle.
|
||||
|
||||
## Hybrid RRF (k=60)
|
||||
|
||||
When `strategy = "hybrid"` and the vector store is available, retrieval uses
|
||||
Reciprocal Rank Fusion to merge FTS5 and vector results:
|
||||
|
||||
```
|
||||
RRF(d) = Σ 1 / (k + rank_i(d)) where k = 60 (configurable via MEMORY_RRF_K)
|
||||
i
|
||||
```
|
||||
|
||||
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`:
|
||||
`rrf_score = 1/(60 + fts_rank)` + `1/(60 + vec_rank)` (0 if not in list).
|
||||
4. Sort by `rrf_score` DESC, apply token budget walk.
|
||||
|
||||
RRF is well-known to be effective without needing score normalization across
|
||||
heterogeneous retrieval systems. The default `k=60` is from the original
|
||||
Cormack et al. paper and works well for small corpora (<10k memories).
|
||||
|
||||
## Backfill (lazy + reindex)
|
||||
|
||||
When the embedding model changes (detected via `embedding_signature`), the
|
||||
vector store is rebuilt and all existing memories are marked
|
||||
`needs_reindex = 1` in the `memories` table.
|
||||
|
||||
**Lazy backfill**: On the next retrieval, any memory missing a vector entry is
|
||||
embedded and inserted into `vec_memories` before the search runs. This
|
||||
amortizes the backfill cost across real requests without blocking startup.
|
||||
|
||||
**Explicit reindex**: The Engine tab in `/dashboard/memory` provides a
|
||||
"Reindex Now" button that calls `POST /api/memory/reindex`. The handler calls
|
||||
`runReindexBatch()` from `src/lib/memory/reindex.ts`, which processes up to
|
||||
`limit` pending entries per request. Progress can be polled via
|
||||
`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.
|
||||
- `vec_loaded` — 0/1 flag whether sqlite-vec loaded successfully.
|
||||
|
||||
## Settings extension
|
||||
|
||||
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 |
|
||||
|
||||
These are exposed via `GET /PUT /api/settings/memory` (schema `MemorySettingsExtendedSchema`).
|
||||
|
||||
> **TODO (D20):** Scope `global` (sharing memories across all API keys) is not
|
||||
> implemented in this release. It requires schema changes and a global retrieval
|
||||
> path. Track separately.
|
||||
|
||||
## Storage Layers
|
||||
|
||||
### Primary: SQLite (`memories` table)
|
||||
@@ -78,10 +204,10 @@ Used by `retrieval.ts` for the `semantic` and `hybrid` strategies (see below).
|
||||
The retrieval code guards with `hasTable("memory_fts")` and falls back to
|
||||
chronological order if the FTS table is missing or the FTS query throws.
|
||||
|
||||
### Optional: Qdrant (vector store)
|
||||
### Optional: Qdrant (vector store tier 2)
|
||||
|
||||
`src/lib/memory/qdrant.ts` implements an optional Qdrant integration for true
|
||||
semantic memory:
|
||||
`src/lib/memory/qdrant.ts` implements an optional Qdrant integration as tier 2
|
||||
vector store. Enabled via `qdrantEnabled` in settings / toggle in Engine tab.
|
||||
|
||||
- `upsertSemanticMemoryPoint()` — embed `key + content` with the configured
|
||||
embedding model, ensure the collection exists (creates cosine-distance
|
||||
@@ -90,24 +216,24 @@ apiKeyId, sessionId, key, content, metadata, createdAtUnix, expiresAtUnix}`.
|
||||
- `searchSemanticMemory(query, topK, scope)` — embed the query, search the
|
||||
collection filtered by `kind = "omniroute_memory"` and optionally by
|
||||
`apiKeyId` / `sessionId`. Caps `topK` to `[1, 20]`.
|
||||
- `deleteSemanticMemoryPoint(id)` — single point delete.
|
||||
- `deleteSemanticMemoryPoint(id)` — single point delete. Called by
|
||||
`deleteMemory()` after the SQLite row is removed (D15).
|
||||
- `cleanupSemanticMemoryPoints({retentionDays})` — bulk delete points whose
|
||||
`expiresAtUnix` is in the past or whose `createdAtUnix` is older than the
|
||||
retention cutoff. Counts first so the dashboard can show actual numbers.
|
||||
- `checkQdrantHealth()` — `GET /readyz` health probe with latency.
|
||||
|
||||
> **TODO**: The chat pipeline (`chatCore.ts`) and the in-tree `retrieveMemories()`
|
||||
> implementation do not currently call `upsertSemanticMemoryPoint` or
|
||||
> `searchSemanticMemory`. The Qdrant integration is feature-flagged via
|
||||
> `qdrantEnabled` in settings, but at the time of writing the
|
||||
> `searchSemanticMemory` results are not fused into retrieval — the
|
||||
> `semantic`/`hybrid` retrieval strategies use SQLite FTS5 only. The settings UI
|
||||
> in `dashboard/settings → MemorySkillsTab` exposes Qdrant config, health,
|
||||
> search test, and cleanup, but the corresponding `/api/settings/qdrant`,
|
||||
> `/api/settings/qdrant/health`, `/api/settings/qdrant/search`, and
|
||||
> `/api/settings/qdrant/cleanup` routes are referenced from the UI but **not
|
||||
> present** under `src/app/api/settings/qdrant/` (only `embedding-models/` is
|
||||
> wired). Treat Qdrant as preview/optional plumbing.
|
||||
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 |
|
||||
|
||||
## Memory Types
|
||||
|
||||
@@ -202,6 +328,8 @@ Memory configuration is **stored in the DB settings table**, not in env vars.
|
||||
in-process; `invalidateMemorySettingsCache()` is called by the settings PUT
|
||||
route after writes.
|
||||
|
||||
### Legacy fields (all versions)
|
||||
|
||||
| DB key | Type | Default | UI control |
|
||||
| --------------------- | ------- | -------------------------------------------------- | ----------------------------------------------- |
|
||||
| `memoryEnabled` | boolean | `true` | Memory on/off |
|
||||
@@ -213,14 +341,38 @@ route after writes.
|
||||
Note: the UI strategy `"recent"` maps to the internal `"exact"` retrieval
|
||||
strategy via `toMemoryRetrievalConfig()` (chronological order).
|
||||
|
||||
### New fields (v3.8.6, plan 21 D9)
|
||||
|
||||
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"` |
|
||||
|
||||
Qdrant-related DB keys (`qdrantEnabled`, `qdrantHost`, `qdrantPort`,
|
||||
`qdrantApiKey`, `qdrantCollection` default `"omniroute_memory"`,
|
||||
`qdrantEmbeddingModel` default `"openai/text-embedding-3-small"`) are read by
|
||||
`normalizeQdrantConfig()` in `qdrant.ts`.
|
||||
|
||||
No `MEMORY_*` or `QDRANT_*` env vars exist today — everything is per-instance
|
||||
DB settings. `OMNIROUTE_MEMORY_MB` (commented out in `.env.example`) is
|
||||
unrelated and refers to Node heap sizing.
|
||||
### Environment variables (v3.8.6)
|
||||
|
||||
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 |
|
||||
|
||||
## Summarisation (`summarization.ts`)
|
||||
|
||||
@@ -240,15 +392,39 @@ loss is one-way: original text is overwritten.
|
||||
|
||||
All endpoints require management auth (`requireManagementAuth`).
|
||||
|
||||
| Method | Path | Description |
|
||||
| -------- | ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `GET` | `/api/memory` | Paginated list with filters: `apiKeyId`, `type`, `sessionId`, `q`, `limit`, `page`, `offset`. Response includes `stats.total` and `stats.byType` |
|
||||
| `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 |
|
||||
| `DELETE` | `/api/memory/[id]` | Delete an entry; returns 404 when missing |
|
||||
| `GET` | `/api/memory/health` | Runs `verifyExtractionPipeline("health-check")` — round-trip create→list→delete to confirm the store is alive. Returns `{working, latencyMs, error?}` |
|
||||
| `GET` | `/api/settings/memory` | Current normalised `MemorySettings` |
|
||||
| `PUT` | `/api/settings/memory` | Update one or more of `enabled`, `maxTokens`, `retentionDays`, `strategy`, `skillsEnabled` |
|
||||
### 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?}` |
|
||||
|
||||
### 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}`. |
|
||||
|
||||
### 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. |
|
||||
|
||||
The `/api/memory` list query supports either `page`-based pagination
|
||||
(`parsePaginationParams`) **or** raw `offset` — when `offset` is present it
|
||||
@@ -259,31 +435,55 @@ takes precedence and a derived `page` is computed for the response shape.
|
||||
When the MCP server is enabled, three memory tools are registered:
|
||||
|
||||
- `omniroute_memory_search` — `{apiKeyId, query?, type?, maxTokens?, limit?}`
|
||||
→ wraps `retrieveMemories()` with `retrievalStrategy: "exact"`, optionally
|
||||
filters by `type`, and reports `totalTokens`.
|
||||
→ wraps `retrieveMemories()`. As of v3.8.6 (D16), the `strategy` is read
|
||||
from `getMemorySettings()` instead of being hardcoded to `"exact"`. If
|
||||
`query` is provided and `strategy` is `semantic` or `hybrid`, the vector
|
||||
store is used when available.
|
||||
- `omniroute_memory_add` — `{apiKeyId, sessionId?, type, key, content,
|
||||
metadata?}` → wraps `createMemory()`.
|
||||
metadata?}` → wraps `createMemory()`. Accepts only the 4 canonical types:
|
||||
`factual`, `episodic`, `procedural`, `semantic` (D17).
|
||||
- `omniroute_memory_clear` — `{apiKeyId, type?, olderThan?}` → lists matching
|
||||
entries, optionally filters by created-before timestamp, then deletes each
|
||||
via `deleteMemory()`.
|
||||
via `deleteMemory()` (which also removes vectors from sqlite-vec + Qdrant).
|
||||
|
||||
See [MCP-SERVER.md](./MCP-SERVER.md) for transport and scope details.
|
||||
|
||||
## Dashboard
|
||||
## Dashboard (Memory Studio)
|
||||
|
||||
`src/app/(dashboard)/dashboard/memory/page.tsx` provides:
|
||||
`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).
|
||||
- Add-memory modal (key, content, type).
|
||||
- Delete per row.
|
||||
- Inline edit (pencil button → `PUT /api/memory/[id]`).
|
||||
- Delete per row (with confirmation dialog).
|
||||
- JSON export of the current page; JSON import via file picker.
|
||||
- Stat cards: `totalEntries`, `tokensUsed`, `hitRate`.
|
||||
- "Compact old" button → `POST /api/memory/summarize` (dry-run first shows
|
||||
candidate count, then confirms).
|
||||
- A green/red health dot driven by `GET /api/memory/health`.
|
||||
- Stat cards: `totalEntries`, `tokensUsed`, `hitRate` (the latter two come
|
||||
from the API stats payload).
|
||||
|
||||
Memory and Qdrant settings live under
|
||||
`/dashboard/settings → Memory & Skills` (`MemorySkillsTab.tsx`).
|
||||
### 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`.
|
||||
- Resolution panel showing which embedding source / vector store was used and
|
||||
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`.
|
||||
- Embedding source selector (auto / remote / static / transformers + toggles).
|
||||
- Qdrant config card (enable toggle, host/port/collection/key, test connection,
|
||||
semantic search test, cleanup).
|
||||
- Rerank config card (enable toggle, provider/model selector).
|
||||
|
||||
Memory and Qdrant settings also live under
|
||||
`/dashboard/settings → Memory & Skills` (`MemorySkillsTab.tsx`) for
|
||||
the legacy/global settings surface.
|
||||
|
||||
## Caching
|
||||
|
||||
@@ -316,12 +516,28 @@ default TTL 5 min).
|
||||
- [API_REFERENCE.md](../reference/API_REFERENCE.md) — broader API surface.
|
||||
- Source modules:
|
||||
- `src/lib/memory/types.ts`, `schemas.ts`
|
||||
- `src/lib/memory/store.ts`, `retrieval.ts`, `injection.ts`
|
||||
- `src/lib/memory/store.ts`, `retrieval.ts`, `injection.ts`, `reindex.ts`
|
||||
- `src/lib/memory/extraction.ts`, `summarization.ts`, `verify.ts`
|
||||
- `src/lib/memory/settings.ts`, `qdrant.ts`, `cache.ts`
|
||||
- `src/lib/memory/vectorStore.ts` — sqlite-vec + hybrid RRF
|
||||
- `src/lib/memory/embedding/index.ts` — multi-source embedding layer
|
||||
- `src/lib/memory/embedding/types.ts`, `remote.ts`, `staticPotion.ts`,
|
||||
`transformersLocal.ts`, `cache.ts`
|
||||
- `src/shared/schemas/memory.ts` — Zod schemas for all memory API bodies
|
||||
- `src/shared/schemas/qdrant.ts` — Zod schemas for Qdrant settings/ops
|
||||
- `src/lib/db/memoryVec.ts` — CRUD for `memory_vec_meta`
|
||||
- `src/lib/db/migrations/015_create_memories.sql`,
|
||||
`022_add_memory_fts5.sql`, `023_fix_memory_fts_uuid.sql`
|
||||
`022_add_memory_fts5.sql`, `023_fix_memory_fts_uuid.sql`,
|
||||
`073_memory_vec.sql`
|
||||
- `src/app/api/memory/route.ts`, `[id]/route.ts`, `health/route.ts`
|
||||
- `src/app/api/memory/retrieve-preview/route.ts`
|
||||
- `src/app/api/memory/engine-status/route.ts`
|
||||
- `src/app/api/memory/embedding-providers/route.ts`
|
||||
- `src/app/api/memory/summarize/route.ts`
|
||||
- `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`
|
||||
|
||||
212
docs/frameworks/PLAYGROUND_STUDIO.md
Normal file
212
docs/frameworks/PLAYGROUND_STUDIO.md
Normal file
@@ -0,0 +1,212 @@
|
||||
---
|
||||
title: "Playground Studio"
|
||||
version: 3.8.7
|
||||
lastUpdated: 2026-05-30
|
||||
---
|
||||
|
||||
# Playground Studio
|
||||
|
||||
> **Feature:** Playground Studio — unified AI testing workspace for `/dashboard/playground`.
|
||||
> **Plans:** `17-playground-studio-redesign.plan.md` + `_orchestration/master-plan-group-C.md`
|
||||
> **Status:** Released in v3.8.6
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
Playground Studio transforms `/dashboard/playground` from a simple Monaco-based editor into
|
||||
a full-featured testing workspace. It replaces the legacy `page.tsx` with a `PlaygroundStudio`
|
||||
shell that renders four tabs and a shared config pane.
|
||||
|
||||
```
|
||||
┌ Playground ──────────────────────────────────────────────────────────┐
|
||||
│ [💬 Chat] [⚖ Compare] [{} API] [🔧 Build] 142↑ 38↓ · $0.002 </>│
|
||||
├──────────────────────────────────────────┬───────────────────────────┤
|
||||
│ {active tab content} │ ─ Config │
|
||||
│ │ Endpoint [chat ∨] │
|
||||
│ │ Model [gpt-5.4 ∨] │
|
||||
│ │ System [textarea] │
|
||||
│ │ Temp ▕▕▔▔ 0.7 │
|
||||
│ │ Presets [▾ load][save] │
|
||||
│ │ [✨ Improve prompt] │
|
||||
└──────────────────────────────────────────┴───────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Tabs
|
||||
|
||||
### Chat Tab
|
||||
|
||||
Evolves `ChatPlayground.tsx` into a multi-turn streaming workbench:
|
||||
|
||||
- Full markdown rendering via `MarkdownMessage.tsx` (code blocks, tables, lists, links).
|
||||
- System prompt sourced from the shared Config pane.
|
||||
- Token/cost per message (prompt + completion tokens).
|
||||
- Regenerate last response.
|
||||
- Sends to `POST /v1/chat/completions` with SSE streaming.
|
||||
|
||||
### Compare Tab
|
||||
|
||||
The key differentiator for a proxy: run 1 prompt across up to **4 models in parallel**.
|
||||
|
||||
- Up to 4 columns, each independently streaming from `/v1/chat/completions`.
|
||||
- `+ Add model` button (Cmd+K shortcut) to add columns.
|
||||
- `Run all ▶` triggers all streams simultaneously via `Promise.all` + per-column `AbortController`.
|
||||
- Global **Cancel all** aborts every in-flight stream.
|
||||
- Per-column `ProviderMetrics` shows TTFT, TPS, tokens, and estimated cost in real time.
|
||||
- Metrics labelled **"client-side estimate"** (D12) — measured from first SSE chunk.
|
||||
|
||||
### API Tab
|
||||
|
||||
Preserves 100% of the original Monaco editor for power users (D14):
|
||||
|
||||
- 10 endpoints: chat completions, completions, embeddings, images, audio, speech, transcriptions, moderations, rerank, search.
|
||||
- Multimodal file upload.
|
||||
- SSE streaming with real-time output.
|
||||
- Wrapped as `ApiTab.tsx` (lazy-loaded, `ssr: false`).
|
||||
|
||||
### Build Tab
|
||||
|
||||
Tools/function calling and structured output UI:
|
||||
|
||||
- `ToolsBuilder.tsx` — add/edit/remove `tools[]` with JSON schema editor per tool.
|
||||
Validates parameters via `ToolDefinitionSchema` (Zod).
|
||||
- `StructuredOutputEditor.tsx` — toggle JSON mode + JSON schema editor.
|
||||
Validates response against schema via `StructuredOutputSchema` (Zod).
|
||||
- Sends request to `/v1/chat/completions` with `tools[]` and/or `response_format`.
|
||||
|
||||
---
|
||||
|
||||
## Config Pane (Shared)
|
||||
|
||||
`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` |
|
||||
|
||||
State is lifted to `PlaygroundStudio.tsx` and passed down to all tabs. Switching tabs
|
||||
preserves config state.
|
||||
|
||||
---
|
||||
|
||||
## Top Bar
|
||||
|
||||
`StudioTopBar.tsx`:
|
||||
|
||||
- Tab switcher (role="tablist").
|
||||
- `TokenCostCounter` — live token (↑/↓) and estimated cost display.
|
||||
- Export code button (`</>`) — opens `ExportCodeModal`.
|
||||
|
||||
---
|
||||
|
||||
## Export Code Modal
|
||||
|
||||
`ExportCodeModal.tsx` uses `codeExport.ts` to generate curl / Python / TypeScript snippets
|
||||
from the current `PlaygroundState`. API key placeholder is always `$OMNIROUTE_API_KEY` (D11).
|
||||
|
||||
---
|
||||
|
||||
## Prompt Improver
|
||||
|
||||
`ImprovePromptButton.tsx` → `useImprovePrompt.ts` → `POST /api/playground/improve-prompt`:
|
||||
|
||||
1. Modal warns "will consume quota".
|
||||
2. On confirm, sends `{ system, prompt, model, tone }` to the route.
|
||||
3. Route calls `/v1/chat/completions` internally with `promptImprover.META_SYSTEM_PROMPT`.
|
||||
4. Returns `{ improvedSystem?, improvedPrompt?, tokensIn, tokensOut }`.
|
||||
5. UI patches the Config pane system prompt and the Chat tab user prompt.
|
||||
|
||||
---
|
||||
|
||||
## Presets
|
||||
|
||||
`PresetPicker.tsx` → `usePresets.ts` → `/api/playground/presets/*`:
|
||||
|
||||
- Stored in `playground_presets` SQLite table (migration `076_playground_presets.sql`).
|
||||
- Each preset stores: `name`, `endpoint`, `model`, `system`, `params_json`, `created_at`.
|
||||
- CRUD: `GET` list, `POST` create, `GET /:id`, `PUT /:id`, `DELETE /:id`.
|
||||
|
||||
---
|
||||
|
||||
## Stream Metrics
|
||||
|
||||
`useStreamMetrics.ts` + `streamMetrics.ts` (pure function):
|
||||
|
||||
- `start()` — records request start time.
|
||||
- `onFirstChunk()` — records TTFT.
|
||||
- `onChunk(n)` — accumulates completion token count.
|
||||
- `finish(usage?)` — computes final metrics: `ttftMs`, `totalMs`, `tps`, `tokensIn`, `tokensOut`, `costUsd`.
|
||||
- Pricing from static table in `src/lib/playground/types.ts` (labelled "estimated" — D13).
|
||||
|
||||
---
|
||||
|
||||
## 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 |
|
||||
|
||||
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/076_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"` |
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
- Master plan: `_tasks/features-v3.8.6/refactorpages/_orchestration/master-plan-group-C.md`
|
||||
- Feature plan: `_tasks/features-v3.8.6/refactorpages/17-playground-studio-redesign.plan.md`
|
||||
- Code export: `src/lib/playground/codeExport.ts`
|
||||
- Prompt improver: `src/lib/playground/promptImprover.ts`
|
||||
- Search Tools Studio: `docs/frameworks/SEARCH_TOOLS_STUDIO.md`
|
||||
183
docs/frameworks/SEARCH_TOOLS_STUDIO.md
Normal file
183
docs/frameworks/SEARCH_TOOLS_STUDIO.md
Normal file
@@ -0,0 +1,183 @@
|
||||
---
|
||||
title: "Search Tools Studio"
|
||||
version: 3.8.7
|
||||
lastUpdated: 2026-05-30
|
||||
---
|
||||
|
||||
# Search Tools Studio
|
||||
|
||||
> **Feature:** Search Tools Studio — unified web tools workspace for `/dashboard/search-tools`.
|
||||
> **Plans:** `18-search-tools-studio-redesign.plan.md` + `_orchestration/master-plan-group-C.md`
|
||||
> **Status:** Released in v3.8.6
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
Search Tools Studio transforms `/dashboard/search-tools` from a basic search playground into a
|
||||
three-tab Studio unifying web search, web scraping, and side-by-side provider comparison.
|
||||
|
||||
```
|
||||
┌ Search Tools ──────────────────────────────────────────────────────────┐
|
||||
│ [🔍 Search] [📄 Scrape] [⚖ Compare] 142ms · $0.001 </> │
|
||||
│ ⓘ [Modalities guide] │
|
||||
├──────────────────────────────────────────┬─────────────────────────────┤
|
||||
│ {active tab content} │ ─ Config │
|
||||
│ │ Provider [auto ∨] │
|
||||
│ │ 🟢 Serper $0.001 │
|
||||
│ │ 🟢 Tavily $0.008 │
|
||||
│ │ 🔥 Firecrawl (fetch) │
|
||||
│ │ Type [web | news] │
|
||||
│ │ Full page [ ] (scrape) │
|
||||
│ │ Format [md|text|html] │
|
||||
│ │ Rerank model [∨] │
|
||||
└──────────────────────────────────────────┴─────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Tabs
|
||||
|
||||
### Search Tab
|
||||
|
||||
Evolves the existing `SearchForm` + `ResultsPanel` + `RerankPanel` into a tab:
|
||||
|
||||
- Query → results (title, URL, snippet, relevance score).
|
||||
- Provider metadata in Config pane (cost, quota, status).
|
||||
- Rerank section: pick a rerank model, reorder results, show `positionDelta`.
|
||||
- Empty state with CTA when no search providers are configured.
|
||||
- Search history via `SearchHistory.tsx`.
|
||||
- Calls `POST /v1/search` (existing endpoint, no changes).
|
||||
|
||||
### Scrape Tab
|
||||
|
||||
New tab for extracting content from a URL via `POST /v1/web/fetch` (created in plan 05):
|
||||
|
||||
- Input: URL + full-page toggle + format selector (markdown / text / HTML).
|
||||
- Submit → fetch → render `ScrapeResult.tsx`.
|
||||
- `ScrapeResult` renders markdown preview + raw toggle.
|
||||
- Cap: if response body > **256 KB**, UI shows `(truncated, view raw)` and opens raw in a Monaco modal (D21).
|
||||
- Metadata panel: provider (firecrawl/jina-reader/tavily-search), latency, cost, response size, links count.
|
||||
- Uses `useScrapeFetch.ts` hook.
|
||||
|
||||
### Compare Tab
|
||||
|
||||
Runs the same query/URL across up to **4 providers in parallel** (D22):
|
||||
|
||||
- Side-by-side columns per provider.
|
||||
- Metrics: latency, cost, result count, response size.
|
||||
- URL overlap calculation for search (number of shared URLs vs initial result).
|
||||
- Calls `POST /v1/search` (search) or `POST /v1/web/fetch` (scrape) per provider.
|
||||
|
||||
---
|
||||
|
||||
## Config Pane (Shared)
|
||||
|
||||
`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 |
|
||||
|
||||
---
|
||||
|
||||
## SearchConceptCard
|
||||
|
||||
`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 |
|
||||
|
||||
---
|
||||
|
||||
## Provider Catalog
|
||||
|
||||
`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` |
|
||||
|
||||
The status is **derived at request time** by checking whether credentials exist and whether
|
||||
all keys are currently in cooldown.
|
||||
|
||||
---
|
||||
|
||||
## Export Code
|
||||
|
||||
`ExportCodeModal` (imported from Playground Studio) + `codeExport.ts` generate
|
||||
curl / Python / TypeScript snippets for both `/v1/search` and `/v1/web/fetch` calls.
|
||||
API key placeholder is always `$OMNIROUTE_API_KEY` (D11, shared with Playground Studio).
|
||||
|
||||
---
|
||||
|
||||
## Backend Changes
|
||||
|
||||
Only one backend change was needed for this feature:
|
||||
|
||||
### Extended `GET /api/search/providers`
|
||||
|
||||
`src/app/api/search/providers/route.ts` was extended to:
|
||||
|
||||
- Include all 3 fetch providers (`firecrawl`, `jina-reader`, `tavily-search`) in the array.
|
||||
- Add `kind: "search" | "fetch"` to every item.
|
||||
- Add `status: "configured" | "missing" | "rate_limited"` derived from live credential state.
|
||||
- Maintain backward compatibility — existing fields (`id`, `name`, etc.) unchanged.
|
||||
|
||||
---
|
||||
|
||||
## 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 |
|
||||
|
||||
---
|
||||
|
||||
## 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 |
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
- Master plan: `_tasks/features-v3.8.6/refactorpages/_orchestration/master-plan-group-C.md`
|
||||
- Feature plan: `_tasks/features-v3.8.6/refactorpages/18-search-tools-studio-redesign.plan.md`
|
||||
- Search provider registry: `open-sse/config/searchRegistry.ts`
|
||||
- Playground Studio (shared `ExportCodeModal` + `codeExport.ts`): `docs/frameworks/PLAYGROUND_STUDIO.md`
|
||||
- Web fetch backend: `src/app/api/v1/web/fetch/route.ts`
|
||||
@@ -1,13 +1,13 @@
|
||||
---
|
||||
title: "Skills Framework"
|
||||
version: 3.8.2
|
||||
lastUpdated: 2026-05-13
|
||||
version: 3.8.6
|
||||
lastUpdated: 2026-05-28
|
||||
---
|
||||
|
||||
# Skills Framework
|
||||
|
||||
> **Source of truth:** `src/lib/skills/` and `src/app/api/skills/`
|
||||
> **Last updated:** 2026-05-13 — v3.8.0
|
||||
> **Last updated:** 2026-05-28 — v3.8.6
|
||||
|
||||
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.
|
||||
|
||||
@@ -15,6 +15,28 @@ A skill is a versioned, schema-defined unit of work. OmniRoute can inject skills
|
||||
|
||||
---
|
||||
|
||||
## Agent Skills vs Omni 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` |
|
||||
|
||||
**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.
|
||||
|
||||
For the Agent Skills catalog, generator, MCP tools, and A2A skill, see [docs/frameworks/AGENT-SKILLS.md](./AGENT-SKILLS.md).
|
||||
|
||||
---
|
||||
|
||||
## Concepts
|
||||
|
||||
### Skill Sources
|
||||
|
||||
421
docs/frameworks/TRAFFIC_INSPECTOR.md
Normal file
421
docs/frameworks/TRAFFIC_INSPECTOR.md
Normal file
@@ -0,0 +1,421 @@
|
||||
---
|
||||
title: "Traffic Inspector"
|
||||
version: 3.8.6
|
||||
lastUpdated: 2026-05-28
|
||||
---
|
||||
|
||||
# Traffic Inspector
|
||||
|
||||
Traffic Inspector is OmniRoute's built-in HTTPS traffic debugger — a Charles Proxy / mitmweb / HTTP Toolkit-like tool that is **LLM-aware** and **agent-aware**. It lives at `/dashboard/tools/traffic-inspector` and receives live traffic from up to 4 simultaneous capture sources.
|
||||
|
||||
**Dashboard location:** `/dashboard/tools/traffic-inspector`
|
||||
**Sidebar group:** Tools (after AgentBridge)
|
||||
**See also:** [`AGENTBRIDGE.md`](./AGENTBRIDGE.md) — AgentBridge is capture mode 1.
|
||||
|
||||
---
|
||||
|
||||
## §1 Overview
|
||||
|
||||
### 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) | ✗ | ✓ | ✓ | ✓ |
|
||||
|
||||
### Architecture in one paragraph
|
||||
|
||||
The `TrafficBuffer` (`src/mitm/inspector/buffer.ts`) is a shared in-memory ring buffer (default 1000 entries, configurable via `INSPECTOR_BUFFER_SIZE`). All capture sources write to it via `push()`. The buffer classifies each entry using `kindDetector.ts` (determines if it's an LLM request), computes a `contextKey` (SHA-256 fingerprint of the system prompt), and broadcasts to all WebSocket subscribers via `globalTrafficBuffer.subscribe()`. The dashboard connects via `GET /api/tools/traffic-inspector/ws` and receives a snapshot on connect, followed by `new`/`update`/`clear` events.
|
||||
|
||||
---
|
||||
|
||||
## §2 Capture modes
|
||||
|
||||
Traffic Inspector supports **4 simultaneous capture sources**. Each is independently toggleable.
|
||||
|
||||
### Mode 1 — AgentBridge (default, always on)
|
||||
|
||||
**Source:** AgentBridge handlers (`src/mitm/handlers/base.ts`)
|
||||
**Mechanism:** Every `intercept()` call in `MitmHandlerBase` calls `hookBufferStart()` before forwarding and `hookBufferUpdate()` on completion. Zero extra config — works as soon as AgentBridge is running.
|
||||
**Reach:** The 9 IDE agents configured in AgentBridge
|
||||
**Note:** `source` field in `InterceptedRequest` = `"agent-bridge"`
|
||||
|
||||
### Mode 2 — Custom Hosts (DNS redirect)
|
||||
|
||||
**Source:** User-defined host list (`inspector_custom_hosts` table)
|
||||
**Mechanism:** Adding a host via the UI adds `127.0.0.1 <host>` to `/etc/hosts` (requires sudo). The existing AgentBridge MITM server (port 443) generates a SNI cert dynamically for the new host.
|
||||
**Reach:** Any application using the added host — no app config change needed
|
||||
**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)
|
||||
|
||||
### Mode 3 — HTTP_PROXY listener (port 8080)
|
||||
|
||||
**Source:** Applications using `HTTP_PROXY`/`HTTPS_PROXY` environment variables
|
||||
**Mechanism:** Secondary listener at port 8080 (`src/mitm/inspector/httpProxyServer.ts`) that acts as a standard explicit HTTP/HTTPS proxy. Accepts `CONNECT` tunnels (HTTPS) and direct HTTP requests.
|
||||
**Reach:** Any application that respects `HTTP_PROXY` env — no DNS change, no sudo
|
||||
**Note:** `source` = `"http-proxy"`
|
||||
|
||||
```bash
|
||||
# Quick capture for a single command:
|
||||
HTTPS_PROXY=http://127.0.0.1:8080 curl https://api.openai.com/v1/models
|
||||
|
||||
# Persistent capture in a shell session:
|
||||
export HTTP_PROXY=http://127.0.0.1:8080
|
||||
export HTTPS_PROXY=http://127.0.0.1:8080
|
||||
```
|
||||
|
||||
**TLS limitation:** HTTPS `CONNECT` tunnels are captured as metadata only (host, port, timing) — TLS body is not decrypted by default. Enable "Decrypt HTTPS in proxy mode" toggle (opt-in, requires AgentBridge cert to be trusted) for full body inspection.
|
||||
|
||||
**Port conflict:** If port 8080 is in use, AgentBridge returns a 409 with a structured error. Change the port via `INSPECTOR_HTTP_PROXY_PORT` env var.
|
||||
|
||||
### Mode 4 — System-wide proxy (advanced, opt-in)
|
||||
|
||||
**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"`
|
||||
|
||||
**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
|
||||
- UI shows `⚠ Advanced` badge + explicit confirmation checkbox
|
||||
|
||||
### 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 |
|
||||
|
||||
---
|
||||
|
||||
## §3 UI
|
||||
|
||||
### 3.1 Layout
|
||||
|
||||
```
|
||||
┌─ Traffic Inspector ─────────────────────────────────────────────────────┐
|
||||
│ ┌─ Capture sources toolbar ─────────────────────────────────────────┐ │
|
||||
│ │ [✓ AgentBridge] [✓ Custom hosts (3)] [○ HTTP_PROXY] [○ System]│ │
|
||||
│ └─────────────────────────────────────────────────────────────────────┘ │
|
||||
│ ┌─ Filter/control bar ──────────────────────────────────────────────┐ │
|
||||
│ │ Profile: (●) LLM only (○) Custom (○) All │ │
|
||||
│ │ [⎉ Pause] [🗑 Clear] [⬇ .har] [● REC session] ● live 482/1k │ │
|
||||
│ └─────────────────────────────────────────────────────────────────────┘ │
|
||||
├══◀▶══════════════════════════════╬══════════════════════════════════════╤╡
|
||||
│ REQUEST LIST (resizable) ║ DETAIL PANE ▲ │
|
||||
│ ────────────────────────────── │ ║ [Conversation][Headers][Request] │ │
|
||||
│ ▎ 14:32 POST 200 12k AG openai ║ [Response][Timing][LLM][Stats] │ │
|
||||
│ ▎ 14:31 POST 200 8k CP openai ║ ▼ │
|
||||
│ ▎ 14:31 POST 503 ⚠ KR ... ║ │
|
||||
│ ▎ 14:30 GET 200 3k 🌐 custom ║ │
|
||||
└══════════════════════════════════╝══════════════════════════════════════╝
|
||||
```
|
||||
|
||||
### 3.2 Request list (left panel)
|
||||
|
||||
- **Virtualized** (`useVirtualList` + `ResizeObserver`): handles 1000 items without freezing
|
||||
- **Auto-scroll** with toggle to pause while inspecting
|
||||
- **Color-coded status**: green (2xx), yellow (3xx), red (4xx/5xx), gray (in-flight)
|
||||
- **Agent emoji**: 🔵 Antigravity, 🟢 Copilot, 🟠 Kiro, 🟣 Codex, 🔷 Cursor, 🟤 Zed, 🟡 Claude Code, ⚫ Open Code, 🌐 custom host
|
||||
- **Context color bar**: 1px left border colored by `contextKey` (SHA-256 of system prompt) — visually groups related conversations
|
||||
- **Lazy body**: only the selected request's body is materialized in the detail tabs (avoids rendering 1000 × 1MB bodies)
|
||||
|
||||
### 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 |
|
||||
|
||||
### 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 |
|
||||
|
||||
### 3.5 Resizable panels
|
||||
|
||||
- List and detail pane separated by a drag handle
|
||||
- List width: min 280px, max 720px, persisted in `localStorage` (`inspector.listWidth`)
|
||||
- Collapsible to a 48px rail (icon-only); click a row in the rail to expand
|
||||
|
||||
---
|
||||
|
||||
## §4 LLM-aware features
|
||||
|
||||
### 4.1 Kind detector (`src/mitm/inspector/kindDetector.ts`)
|
||||
|
||||
Classifies each request as `"llm"`, `"app"`, or `"unknown"` using 4 signals:
|
||||
|
||||
1. **Host registry** — ~18 known LLM API hostnames (OpenAI, Anthropic, Gemini, Groq, Mistral, Together, Fireworks, Cohere, Perplexity, Hugging Face, OpenRouter, xAI, Moonshot, etc.)
|
||||
2. **Path patterns** — `/v1/chat/completions`, `/v1/messages`, `/generateContent`, `/v1/responses`, etc.
|
||||
3. **Body shape** — detects `messages[]` (OpenAI/Claude), `contents[]` (Gemini), `prompt`, `input` fields
|
||||
4. **User-agent hints** — `codex`, `claude`, `gemini`, `antigravity`, `kiro`, `copilot`, `cursor` in UA string
|
||||
|
||||
Custom hosts added via Mode 2 inherit their `kind` from the form input (defaults to `"custom"`).
|
||||
|
||||
### 4.2 SSE merger (`src/mitm/inspector/sseMerger.ts`)
|
||||
|
||||
**MIT port from [chouzz/llm-interceptor](https://github.com/chouzz/llm-interceptor)**
|
||||
|
||||
Reconstructs the final assistant message from raw SSE delta events:
|
||||
|
||||
- **Anthropic**: accumulates `content_block_delta` by index; handles `text_delta`, `input_json_delta` (tool calls), `thinking_delta`
|
||||
- **OpenAI**: accumulates `choices[i].delta.content` and `tool_calls` by index
|
||||
- **Gemini**: accumulates `candidates[i].content.parts`
|
||||
- **Unknown**: returns raw events as-is
|
||||
|
||||
The Response tab shows a toggle: **"Raw events ↔ Merged"**.
|
||||
|
||||
### 4.3 Conversation normalizer (`src/mitm/inspector/conversationNormalizer.ts`)
|
||||
|
||||
**MIT port from [chouzz/llm-interceptor](https://github.com/chouzz/llm-interceptor)**
|
||||
|
||||
Converts OpenAI, Anthropic, and Gemini message formats to a single `NormalizedConversation` before rendering:
|
||||
|
||||
```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
|
||||
}
|
||||
```
|
||||
|
||||
Block types: `text`, `tool_use`, `tool_result`. The Conversation tab uses this shape regardless of provider.
|
||||
|
||||
### 4.4 Context key colorization (`src/mitm/inspector/contextKey.ts`)
|
||||
|
||||
- Computes `SHA-256` of the system prompt (first `role:system` message, or `system` field, or Gemini `systemInstruction`)
|
||||
- Returns a 12-character hex prefix (`"a3f9c2..."`)
|
||||
- Frontend maps the key to a deterministic HSL color for the left-border bar
|
||||
- **Filtro "same context"**: clicking the `ctx #a3f` chip adds a filter to show only requests with the same fingerprint
|
||||
|
||||
This makes it easy to visually distinguish different "personas" or tasks running in the same agent session.
|
||||
|
||||
### 4.5 LLM metadata extraction
|
||||
|
||||
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
|
||||
costEstimateUsd: number | null; // estimated cost based on OmniRoute pricing
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## §5 Sessions
|
||||
|
||||
### 5.1 Recording a session
|
||||
|
||||
1. Click **"● Record session"** in the toolbar → enter a name (optional)
|
||||
2. Live tail continues normally; a red pulsing indicator shows `◉ REC · <name> · 00:42 · 23 reqs`
|
||||
3. Click **"⏹ Stop"** → the session snapshot is saved to `inspector_sessions` + `inspector_session_requests`
|
||||
|
||||
### 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
|
||||
|
||||
### 5.3 Export formats
|
||||
|
||||
Each session can be exported as:
|
||||
|
||||
| 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 |
|
||||
|
||||
Export via `GET /api/tools/traffic-inspector/sessions/{id}/export.har` or the ⬇ button in the Sessions dropdown.
|
||||
|
||||
---
|
||||
|
||||
## §6 Security
|
||||
|
||||
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 |
|
||||
|
||||
### Hard Rules applied
|
||||
|
||||
| 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
|
||||
|
||||
- **System-wide proxy mode** affects all applications on the machine, including VPN clients and SSO. Always use with the auto-disable timer. Do not use on shared machines.
|
||||
- **CONNECT tunnel HTTPS**: Mode 3 (HTTP_PROXY) captures only tunnel metadata for HTTPS destinations unless TLS interception is enabled. This is by design — transparent capture without the AgentBridge cert being trusted would break TLS verification for those apps.
|
||||
- **Hardcoded strings in some components**: Some UI components (F7/F8) have a small number of hardcoded strings not yet covered by i18n keys. These are documented as a Known Limitation in the i18n gap report; they will be migrated in a follow-up pass. Affected strings are UI decorative labels that don't require translation for functional use.
|
||||
|
||||
---
|
||||
|
||||
## §7 Troubleshooting
|
||||
|
||||
### 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
|
||||
|
||||
### 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
|
||||
```
|
||||
|
||||
### System proxy not reverted
|
||||
|
||||
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
|
||||
```
|
||||
|
||||
The dashboard will also offer "Revert system proxy" on next load if it detects the DB state indicates proxy was active.
|
||||
|
||||
### 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
|
||||
|
||||
---
|
||||
|
||||
## §8 API reference
|
||||
|
||||
All routes are `LOCAL_ONLY` (loopback-only) and `SPAWN_CAPABLE` (system proxy commands). See `src/server/authz/routeGuard.ts`.
|
||||
|
||||
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 |
|
||||
|
||||
### WebSocket
|
||||
|
||||
| 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 |
|
||||
|
||||
### 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` |
|
||||
|
||||
### Capture modes
|
||||
|
||||
| Method | Path | Description |
|
||||
|--------|------|-------------|
|
||||
| GET | `/capture-modes` | State of all 4 capture modes |
|
||||
| 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 |
|
||||
|
||||
### 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 |
|
||||
|
||||
### Internal ingest (D4 fallback)
|
||||
|
||||
| Method | Path | Description |
|
||||
|--------|------|-------------|
|
||||
| POST | `/internal/ingest` | Accepts intercepted request from `server.cjs` passthrough path; requires `INSPECTOR_INTERNAL_INGEST_TOKEN` header |
|
||||
|
||||
Full OpenAPI schemas: `docs/reference/openapi.yaml` → tag `Traffic Inspector`.
|
||||
Reference in New Issue
Block a user