Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .claude/skills/customize/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ Before making changes, understand the codebase:
- `server/src/warren/app.py` β€” FastAPI app factory, all endpoints, SSE streaming
- `server/src/warren/adapters/` β€” Backend adapter interface and implementations
- `__init__.py` β€” BackendAdapter protocol, BackendMessage, BackendEvent types
- `nanoclaw.py` β€” NanoClawAdapter (file-based IPC + HTTP callback)
- `websocket.py` β€” AgentWebSocketAdapter (WebSocket agent proxy, default)
- `orca.py` β€” OrcaAdapter (local Orca terminal control)
- `server/src/warren/config.py` β€” pydantic-settings (env) + YAML config
- `server/src/warren/db.py` β€” SQLite via aiosqlite
- `server/src/warren/lifecycle.py` β€” Background session lifecycle sweep
Expand Down
16 changes: 8 additions & 8 deletions .claude/skills/debug/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,15 @@ print(f'Backend config: {c.backend_config}')
```

### 3. Check adapter connectivity
For direct backend:
- Verify `claude` binary exists: `which claude`
- Test Claude CLI: `claude --version`
- Check API key: verify `ANTHROPIC_API_KEY` is set
For the websocket backend:
- Confirm an agent is connected: `GET /monitor/health` reports
`agent_websocket.connected_agents_count`
- Verify the agent uses the correct `WARREN_INTERNAL_SECRET` token
- Check the agent can reach `ws[s]://<host>/api/agent/ws` (TLS, firewall, NAT)

For NanoClaw backend:
- Check IPC directory exists and is writable
- Check NanoClaw process is running
- Check Docker socket is accessible
For the orca backend:
- Verify the `orca` CLI exists: `which orca`
- Check the Orca runtime is reachable: `orca status --json`

### 4. Check database
```bash
Expand Down
4 changes: 2 additions & 2 deletions .claude/skills/setup/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ configuration. Then:

1. **config.yaml** β€” Create or update with repo paths. Ask the user which
repos they want Warren to manage.
2. **.env** β€” Set `WARREN_ANTHROPIC_API_KEY`, `WARREN_ADMIN_TOKEN` (generate
if not set), `WARREN_BACKEND` (direct or nanoclaw).
2. **.env** β€” Set `WARREN_ADMIN_TOKEN` and `WARREN_INTERNAL_SECRET` (generate
if not set), and `WARREN_BACKEND` (`websocket` or `orca`).
3. **Deployment** β€” Based on their deployment method:
- Dokploy: guide service creation, volume mounts, env vars
- systemd: create service unit file
Expand Down
4 changes: 0 additions & 4 deletions .gitmodules

This file was deleted.

54 changes: 21 additions & 33 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@

## What is this

Warren bridges Rabbit R1 to Claude Code sessions over HTTPS. A FastAPI server
communicates with NanoClaw via IPC. An R1 Creation provides the 240x282 UI.
Warren bridges Rabbit R1 to arbitrary personal AI agents over HTTPS. A FastAPI
server proxies sessions to an agent over a WebSocket protocol (see
`PROTOCOL.md`), or to local Orca terminals. An R1 Creation provides the 240x282 UI.

## Commands

Expand All @@ -12,25 +13,19 @@ cd server && uv run ruff check src/ tests/ # Lint
cd server && uv run pytest tests/ -v # Unit tests
cd server && uv run python -m warren # Run

# E2E tests (requires Docker):
docker compose -f tests/e2e/docker-compose.test.yml up -d --build
WARREN_E2E_URL=http://localhost:14030 uv run --directory server pytest tests/e2e/ -v
docker compose -f tests/e2e/docker-compose.test.yml down -v

## Architecture

server/src/warren/
app.py # FastAPI, endpoints, SSE, WebSocket
adapters/ # Backend adapter interface + implementations
__init__.py # BackendAdapter protocol, BackendMessage, BackendEvent
nanoclaw.py # NanoClawAdapter (file-based IPC + HTTP callback)
websocket.py # AgentWebSocketAdapter (WebSocket proxy, default)
orca.py # OrcaAdapter (local Orca terminal control)
bus.py # SessionBus pub/sub (multi-consumer event delivery)
voice.py # Voice WebSocket handler (OpenClaw protocol)
voice_protocol.py # OpenClaw message builders/parsers
lifecycle.py # Background session lifecycle sweep
auth.py # QR pairing, device tokens, voice QR
ipc.py # NanoClaw IPC file writer (atomic JSON)
nanoclaw_db.py # Read-only NanoClaw SQLite for monitoring
db.py # SQLite (sessions, tokens)
config.py # pydantic-settings + YAML

Expand All @@ -53,23 +48,18 @@ to Claude Code when working in the Warren repo.
### Key abstractions
- `BackendAdapter` (`server/src/warren/adapters/__init__.py`) β€” protocol for AI backends
- `BackendMessage` / `BackendEvent` β€” message types for adapter communication
- `NanoClawAdapter` (`server/src/warren/adapters/nanoclaw.py`) β€” NanoClaw IPC adapter
- `AgentWebSocketAdapter` (`server/src/warren/adapters/websocket.py`) β€” WebSocket agent proxy
- `OrcaAdapter` (`server/src/warren/adapters/orca.py`) β€” local Orca terminal control
- `SessionBus` (`server/src/warren/bus.py`) β€” pub/sub for multi-consumer event delivery
- `Database` (`server/src/warren/db.py`) β€” SQLite via aiosqlite
- `WarrenConfig` / `Settings` (`server/src/warren/config.py`) β€” YAML + env config
- `NanoClawDbReader` (`server/src/warren/nanoclaw_db.py`) β€” read-only monitoring

### Deploy with NanoClaw
Configure `backend_config` in config.yaml or use environment variables:
```yaml
backend_config:
nanoclaw:
ipc_dir: /opt/warren-data/ipc
db_path: /opt/warren-data/nanoclaw.db
```
Or set `WARREN_NANOCLAW_IPC_DIR` and `WARREN_NANOCLAW_DB` environment variables.
Both services need the shared volume `/opt/warren-data/` mounted.
NanoClaw needs `WARREN_CALLBACK_URL=http://warren:4030/internal/nanoclaw/callback`.

### Choose a backend
Set `WARREN_BACKEND` (or `backend` in config). Options:
- `websocket` (default) β€” agents connect as clients to `ws[s]://<host>/api/agent/ws`.
Set `WARREN_INTERNAL_SECRET` to require token auth on that endpoint. See `PROTOCOL.md`
and `examples/agent.py` for the client protocol.
- `orca` β€” control local Orca-managed terminals from the R1 (requires the `orca` CLI).

## R1 Creation Constraints

Expand All @@ -93,7 +83,7 @@ for the R1's native voice assistant pairing.

```
R1 Browser (Creation) ──HTTP/SSE──┐
β”œβ”€β”€ Warren ──adapter──▢ NanoClaw
β”œβ”€β”€ Warren ──adapter──▢ Agent (WebSocket) / Orca
R1 PTT (Voice) ───────WebSocketβ”€β”€β”€β”˜
```

Expand All @@ -107,15 +97,13 @@ R1 PTT (Voice) ───────WebSocketβ”€β”€β”€β”˜
| `status` | `state` (working/waiting) | Typing indicator |
| `error` | `message` | Error occurred |

### NanoClaw Callback Types (NanoClaw β†’ Warren)

POST `/internal/nanoclaw/callback`:
- `{type: "text", session_id, text}` β€” agent text
- `{type: "tool", session_id, tool, summary}` β€” tool progress
- `{type: "result", session_id, text, nanoclaw_session_id}` β€” completion
### Agent WebSocket Protocol (Agent ↔ Warren)

POST `/internal/nanoclaw/typing`:
- `{session_id, is_typing: bool}` β€” typing state
The default `websocket` backend exposes `ws[s]://<host>/api/agent/ws`. Agents
connect as clients, receive `BackendMessage` frames (`new_session`,
`user_message`, `cancel`) and stream back `BackendEvent` frames (`text`, `tool`,
`result`, `status`, `error`). Full schema and a reference client live in
`PROTOCOL.md` and `examples/agent.py`.

## Coding Guidelines

Expand Down
202 changes: 202 additions & 0 deletions PROTOCOL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
# Warren Agent Proxy Protocol

Warren acts as a high-performance, general-purpose proxy layer bridging user interactions from a **Rabbit R1** (via Voice PTT or its 240x282 Creation WebView) to **arbitrary personal AI agents** (running on your home lab, VPS, local machine, etc.).

By default, Warren decouples itself from any specific agent implementation (like Claude Code) by exposing a bidirectional, real-time, NAT-friendly **WebSocket Proxy Protocol**.

---

## πŸ—οΈ Architecture

```
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Rabbit R1 β”‚
β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜
β”‚ (Voice/SSE)
β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Warren β”‚
β”‚ (Proxy VPS) β”‚
β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜
β”‚ (WebSocket Server)
β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚Personal Agentβ”‚
β”‚ (Home Lab) β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
```

Because the **Personal Agent** connects to **Warren** as a WebSocket client, your agent can reside securely behind NAT, firewalls, or dynamic home IP addresses without exposing any public web servers or webhooks.

---

## πŸ”Œ Connection details

* **Endpoint URL:** `ws://<your-warren-host>:<port>/api/agent/ws` (or `wss://` behind secure proxies).
* **Authentication:** Provide either:
1. A `token` query parameter: `?token=YOUR_WARREN_INTERNAL_SECRET`
2. An `Authorization` header: `Authorization: Bearer YOUR_WARREN_INTERNAL_SECRET`

---

## πŸ“₯ Incoming Messages (Warren βž” Agent)

When a user initiates an action or sends a message, Warren serializes the interaction and pushes it over the WebSocket as a JSON object:

```json
{
"kind": "new_session" | "user_message" | "cancel",
"session_id": "string",
"payload": {
"message": "string",
"repo_path": "string",
"model": "string",
"append_system_prompt": "string"
}
}
```

### Event Types
* **`new_session`**: Triggered when a new chat session is initiated.
* **`user_message`**: Triggered when a message is added to an existing session.
* **`cancel`**: Sent when the user taps the **[STOP]** button on their R1 screen to interrupt the agent's current task.

---

## πŸ“€ Outgoing Events (Agent βž” Warren)

The agent streams its progress, text tokens, status indicators, and tool executions back to Warren using JSON frames. Warren proxies these directly to the Rabbit R1:

```json
{
"kind": "text" | "tool" | "result" | "status" | "error" | "typing",
"session_id": "string",
"data": {
"type": "text" | "tool" | "result" | "status" | "error" | "typing",
"...": "fields mapping to type"
}
}
```

### Event Payload Schema

#### 1. Streaming Text Response (`kind: "text"`)
Streams the assistant's verbal or terminal text response:
```json
{
"kind": "text",
"session_id": "session-xyz",
"data": {
"type": "text",
"content": "This is a stream of text... "
}
}
```

#### 2. Tool Execution Log (`kind: "tool"`)
Logs tool-execution output to the R1 terminal monitor:
```json
{
"kind": "tool",
"session_id": "session-xyz",
"data": {
"type": "tool",
"tool": "run_tests",
"summary": "running unit tests..."
}
}
```

#### 3. Agent Status (`kind: "status"`)
Controls the connection dot state and controls on the R1:
```json
{
"kind": "status",
"session_id": "session-xyz",
"data": {
"type": "status",
"state": "working" | "active" | "waiting" | "done" | "error"
}
}
```
* `working`: Shows a yellow status dot on the R1 (indicates background agent calculation/tool execution).
* `waiting`: Shows a green status dot (indicates agent is idling/awaiting user input).
* `error`: Shows a red status dot.

#### 4. Final Task Result (`kind: "result"`)
Signals the final resolution/summary of a task:
```json
{
"kind": "result",
"session_id": "session-xyz",
"data": {
"type": "result",
"summary": "Completed the refactoring successfully!"
}
}
```

#### 5. Errors (`kind: "error"`)
Pushes exception logs to the screen:
```json
{
"kind": "error",
"session_id": "session-xyz",
"data": {
"type": "error",
"message": "Failed to connect to database"
}
}
```

#### 6. Typing Indicator (`kind: "typing"`)
Provides visual feedback that the agent is actively preparing a response:
```json
{
"kind": "typing",
"session_id": "session-xyz",
"data": {
"type": "typing",
"is_typing": true
}
}
```

---

## πŸ“ˆ Step-by-Step Progress Tracking

To leverage the Rabbit R1 Creation's built-in step progress visualization (the checklist under the status indicator), format progress logs in your `text` content using the bracketed step format:

`[STEP:current_step/total_steps:status] Step description`

Where `status` can be `pending`, `running`, `done`, or `failed`.

### Example text block:
```
[STEP:1/3:done] Read codebase
[STEP:2/3:running] Apply edits
[STEP:3/3:pending] Verify build
```
When Warren receives this text block, it parses the bracketed meta-tags and updates the checklists dynamically on the R1 device screen.
- ---

## πŸ‹ Native Orca Integration (Zero-Hop Architecture)

If you are running **Orca** on your local machine or server, Warren can act as a **zero-hop native proxy**. In this setup, Warren connects directly to your live Orca runtime and pipes R1 voice/text inputs directly to active Orca terminals.

### βš™οΈ How to Configure
Set the `WARREN_BACKEND` environment variable to `"orca"` (or configure `backend: orca` in `config.yaml`):
```bash
export WARREN_BACKEND="orca"
warren
```

### πŸ” How it works under the hood
1. When Warren starts, it automatically detects your computer's local Wi-Fi/network IP address and prints a pairing QR code right on your console.
2. Scan the QR code with your Rabbit R1 to pair it.
3. When you send a prompt from your R1:
* Warren queries the running Orca runtime for active terminal sessions via `orca terminal list`.
* If an active terminal exists (e.g. running your custom LLM or agent), Warren pipes your prompt into it using `orca terminal send`.
* If no active terminals are found, Warren automatically spawns a fresh terminal in your active workspace: `orca terminal create --worktree active --title "R1 Workspace"`.
* Warren reads the terminal stream back using `orca terminal read` and streams the text in real-time straight to your R1.
Loading
Loading