# Zenbu Agent Room

Local-first, persistent council for bounded Claude ↔ Codex discussion.

## Safe preview

Mock mode is the default. It writes a real room transcript to SQLite without
calling either paid agent:

```bash
cd /Users/yuto/Developer/zenbu-life
node agent-room/server.mjs
```

Open <http://127.0.0.1:8787/room/>.

Room state is stored in `.zenbu/agent-room.sqlite`. The directory is ignored by
Git. Delete or archive it only deliberately; it is the local transcript ledger.

## Live CLI council

Both local CLIs must already be installed and authenticated:

```bash
ZENBU_AGENT_ROOM_MODE=cli node agent-room/server.mjs
```

Current verification snapshot (2026-07-29):

- Claude authentication was completed by Yuto.
- A full live Claude proposal → Codex critique → Claude synthesis council
  completed and persisted. Claude reported API-equivalent usage estimates of
  `$0.059139` and `$0.061042` (`$0.120181` total). These values are telemetry,
  not invoices; actual billing depends on the authenticated account.
- Codex corrected a false Claude assumption against the repository, and
  Claude's synthesis explicitly incorporated that correction.
- When the room service itself is launched from another sandboxed agent, that
  outer sandbox must permit the nested CLIs to access their own auth/state.
  Launching from Yuto's normal terminal avoids that wrapper restriction.

Live mode is still discussion-only:

- Claude runs in safe/plan mode with built-in tools disabled. It reasons from
  the shared room goal and transcript.
- Codex runs ephemerally in a read-only sandbox.
- Each agent call has a three-turn internal limit.
- Claude has a default `$0.35` API-equivalent usage ceiling per call.
- One council round makes three calls: proposal, critique, synthesis.

Optional controls:

```bash
ZENBU_CLAUDE_MAX_BUDGET_USD=0.20 \
ZENBU_ROOM_AGENT_MAX_TURNS=2 \
ZENBU_ROOM_AGENT_TIMEOUT_MS=120000 \
ZENBU_AGENT_ROOM_MODE=cli \
node agent-room/server.mjs
```

The server binds to `127.0.0.1` by default. Do not expose it publicly. This MVP
has no authentication and inherits the local CLI authentication environment.
It refuses non-loopback hosts, serves only allowlisted public folders, never
serves `.env.local`, `.git`, `.zenbu`, or runner source, and requires a custom
mutation header to prevent cross-site pages from starting a council.

## Protocol

Default one-round council:

1. First agent proposes.
2. Second agent critiques.
3. First agent synthesizes.
4. Room pauses at `awaiting_decision`.
5. Yuto approves, requests another pass, or closes the room.

The room never advances from discussion to editing automatically. Execution,
worktrees, diff review, commits, pushes, deployments, and external actions are
future states requiring separate approval gates.

## Verification

```bash
node --test agent-room/agent-room.test.mjs
node --check agent-room/db.mjs
node --check agent-room/adapters.mjs
node --check agent-room/orchestrator.mjs
node --check agent-room/server.mjs
```

The adapter contract test asserts the complete Claude and Codex CLI argument
vectors. It fails if Claude loses safe/plan mode or its empty tools list, or if
Codex loses ephemeral/read-only isolation.
