A self-hosted AI gateway with a general-purpose agent at its core.
Not a coding tool — any task requiring AI assistance works.
| 🤖 General Purpose Agent | Stateful AI assistant with multi-turn dialogue, tool execution, and skill invocation | |
| 📦 Context Compression | Intelligent compression to break token limits (via pi-coding-agent) | |
| 🌐 Browser UI | Real-time chat, Markdown rendering, no account needed | |
| 🔌 HTTP / WebSocket API | One POST /api/messages for CI/CD, scripts, integrations |
|
| 💾 Persistent Sessions | Resume any past conversation by sessionId |
|
| 🔧 Tools & Lua | Drop a .lua in lua-tools/, agent can call it immediately |
|
| 🧩 Skills | 7 built-in skills the agent reads in relevant contexts | |
| 🔄 Multi-Model | Switch between DeepSeek / Claude with one env var | |
| 🏖️ Sandbox | WASM-based write command sandbox (wasmtime + coreutils) | |
| 🧠 Memory | Two-phase memory: 50% trigger consolidation, session start injection | |
| 📚 Session History RAG | BM25 retrieval of relevant past sessions at perception phase | |
| 🎯 Goal Constraint | Auto-detect conversation drift and inject correction messages | |
| ✅ Self-Verification | Post-response verification that checks alignment with original task, injects corrections on drift | |
| 🤖 Subagent | Launch autonomous sub-agents, Task queue, Fleet view, Scheduled recurring tasks |
Token Usage
│
├── Perception ── Session History RAG ──── Inject relevant past sessions (BM25)
│
├── 50% ─── Memory ───────────── Consolidate to MEMORY.md
│
├── 70% ─── Micro Compact ───── Prune old tool outputs
│
├── 75% ─── Context Collapse ── Summarize dialogue to projection
│
└── 83.5% ─ Auto Compact ────── Built into pi-coding-agent
Perception Phase ── Goal Constraint ───────── Monitor drift, inject correction
Write commands execute in a WASM sandbox via wasmtime + coreutils.wasm:
| Type | Commands | Routing |
|---|---|---|
| Read | ls, cat, grep, find |
Passthrough to bash |
| Write | echo, mkdir, rm, cp, mv |
WASM sandbox |
cwd via --dir flag50% Token ──► LLM Summarize ──► MEMORY.md (append)
Session Start ──► Read MEMORY.md ──► Inject into systemPrompt
MEMORY.mdMEMORY.md content is injected into agent's system promptUser Message → before_agent_start → BM25 retrieval → Inject relevant past sessions
before_agent_start hook (perception phase), once per sessionContext Event → Token threshold detection → Drift detection → Inject correction
[GOAL CONSTRAINT] correction message to message listAgent Response → Verification Check → Correction injection (if drift detected)
[SELF-VERIFICATION] correction messageThe agent can launch autonomous sub-agents, manage tasks, and schedule recurring jobs:
Main Agent ──► Agent(task) ──► Subagent (runs independently)
──► TaskExecute ──► Task (queued, run by subagent)
──► Schedule ──► Recurring job (cron or interval)
──► FleetView ──► See all running agents
Tools available to the agent:
| Tool | Description |
|---|---|
Agent |
Start a subagent to perform a task |
get_subagent_result |
Get result of a completed subagent |
steer_subagent |
Send a message to a running subagent |
TaskCreate |
Create a named task |
TaskExecute |
Execute a task using a subagent |
TaskList / TaskGet |
List or view task status |
TaskUpdate |
Update task status |
FleetView |
View all running agents |
Schedule / CancelSchedule |
Schedule recurring tasks (cron or interval) |
LoadCustomAgents |
Load custom agent definitions from .pi/agents |
CleanupAgents |
Cleanup all agents for this session |
API rate limits (HTTP 429) are handled with automatic exponential backoff:
429 → retry 1 (2s) → retry 2 (4s) → retry 3 (8s) → fail
[2000, 4000, 8000] msrate_limited event{ type: "abort_retry" } to cancel in-progress retryauto_retry_start/end are forwarded to frontend, but control flow uses custom ws.data.retryState for accurate prompt preservationbun install
export DEEPSEEK_API_KEY=your_key
bun run server/index.ts
open http://localhost:3333
| Variable | Default | Description |
|---|---|---|
DEEPSEEK_API_KEY |
— | DeepSeek API key (recommended) |
ANTHROPIC_API_KEY |
— | Alternative: Anthropic API key |
PORT |
3333 |
HTTP/WebSocket port |
MODEL_PROVIDER |
deepseek |
deepseek or anthropic |
MODEL_BASE_URL |
https://api.deepseek.com/v1 |
Custom model endpoint |
MODEL_ID |
deepseek-chat |
Model name |
SESSION_TIMEOUT_MS |
480000 |
Max agent run time (8 min) |
Browser HTTP Client Script
│ │ │
┌────▼────┐ ┌────▼────┐ ┌────▼────┐
│ Web │ │ HTTP │ │ WS │
│ UI │ │ API │ │ Client │
└────┬────┘ └────┬────┘ └────┬────┘
│ │ │
└─────────────────┼─────────────────┘
│
┌─────────────────▼─────────────────┐
│ Krebs Gateway │
│ │
│ ws-router HTTP routes │
│ ├── PromptHandler /api/messages │
│ ├── SwitchSession /api/sessions │
│ └── (inlined) /api/auth │
└─────────────────┬─────────────────┘
│
┌─────────────────▼─────────────────┐
│ Agent │
│ │
│ session-service │
│ creates / manages runtime │
│ │
│ .pi/extensions/ │
│ ├── context/ (Compression) │
│ ├── memory/ (Consolidation 50%) │
│ ├── memory-context/ (Injection) │
│ ├── session-history-rag/ (RAG) │
│ ├── goal-constraint/ (Drift) │
│ ├── self-verification/ (Verify) │
│ └── subagent/ (Fleet/Tasks) │
│ │
│ server/services/ │
│ ├── compact/ │
│ ├── memory/ │
│ ├── session-history/ (BM25+tools) │
│ ├── goal-constraint/ (Engine) │
│ ├── self-verification/ (LLM) │
│ └── subagent/ (Fleet/Tasks) │
│ │
│ server/sandbox/ │
│ └── wasmtime + coreutils.wasm │
│ │
│ event-subscription │
│ forwards events → WebSocket │
│ │
│ tools/ + lua-tools/ + skills/ │
│ bash 9 Lua scripts 7 skills│
└───────────────────────────────────────┘
│
┌─────────────────▼───────────────────┐
│ SessionManager │
│ persists sessions → ./sessions/ │
└─────────────────────────────────────┘
┌─────────────────────────────────────┐
│ db/sessions_meta (SQLite) │
│ sessionId → sessionFile mapping │
└─────────────────────────────────────┘
When token usage reaches thresholds, compression triggers automatically:
| Layer | Threshold | Trigger | Action |
|---|---|---|---|
| Memory | 50% | Token budget half-used | LLM summarizes recent messages → MEMORY.md |
| Micro Compact | 70% | Too many tool results | Prune old tool outputs, keep key info |
| Context Collapse | 75% | Context too long | Compress middle dialogue into summary projection |
| Auto Compact | 83.5% | Near limit | Built into pi-coding-agent, aggressive compression |
Requires Authorization: Bearer <token> — token printed to console on first start and saved to .env.
curl -X POST http://localhost:3333/api/messages \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"message": "Help me plan a trip to Japan"}'
Returns {sessionId, response, generatedContent}.
curl -X POST http://localhost:3333/api/messages \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"message": "Continue", "sessionId": "session_xxx"}'
# List all sessions
curl http://localhost:3333/api/sessions/list -H "Authorization: Bearer $TOKEN"
# Get session details
curl http://localhost:3333/api/sessions/:sessionId -H "Authorization: Bearer $TOKEN"
# Delete a session
curl -X DELETE http://localhost:3333/api/sessions/:sessionId -H "Authorization: Bearer $TOKEN"
const ws = new WebSocket("ws://localhost:3333/ws");
ws.onopen = () => ws.send(JSON.stringify({ type: "auth" }));
Receive events:
| Event | When |
|---|---|
connected |
Connection open |
text_delta |
Streaming token from agent |
think_block |
<think> tag content |
tool_call_start |
Agent started generating a tool call |
tool_start / tool_end |
Tool execution |
turn_end |
Round complete |
response_end |
Full agent response done |
rate_limited |
API 429 — retry in progress |
retry_success |
Retry succeeded, response delivered |
retry_failed |
All retries exhausted |
retry_aborted |
User aborted retry via abort_retry message |
question_queued |
Follow-up received while agent was streaming |
ws.send(JSON.stringify({ type: "prompt", message: "Hello" }));
ws.send(JSON.stringify({ type: "stop" }));
ws.send(JSON.stringify({ type: "abort_retry" })); // abort in-progress retry
ws.send(JSON.stringify({ type: "switch_session", sessionId: "..." }));
Open http://localhost:3333/. Connects to /ws, authenticates automatically.
Drop a Lua script into lua-tools/, restart the server. Agent can call it by name.
```lua -- lua-tools/json-encode.lua function main(args) local value = args[1]
$ claude mcp add Krebs \
-- python -m otcore.mcp_server <graph>