diff --git a/README.md b/README.md index 5c52da8..7e4330c 100644 --- a/README.md +++ b/README.md @@ -1,84 +1,166 @@ -# bullpen +

bullpen

-A durable agent harness in Rust. A bullpen is a roster of warmed-up relievers -you call in, pull back, and send out again — agents as managed, durable -workers, not fire-and-forget processes. +

+ A durable agent harness in Rust.
+ Agents as managed, resumable workers — not fire-and-forget processes. +

-**Status: v0.** Headless vertical slice — a working agent loop (Anthropic, -six workspace tools, SQLite-backed resumable sessions). TUI, durable -subagents, OS sandboxing, and the workflow engine are on the -[roadmap](ARCHITECTURE.md#roadmap). +

+ CI + Rust 1.97 + License MIT + Status v0 +

-## Use +

+ Dispatching three agents from bullpen agents, watching them complete, peeking at one's answer, and seeing the sessions persist +

+ +A bullpen is a roster of warmed-up relievers you call in, pull back, and send +out again. That is the whole thesis: agent runs should survive the process +that started them. + +## Why it's different + +**Every step is durable the moment it happens.** Sessions live in one SQLite +database (WAL), not in the memory of whatever process launched them. Kill +bullpen mid-run — crash, `kill -9`, power loss — and the next invocation +recovers: interrupted tool calls are marked, the transcript is closed +cleanly, and the session resumes where it stopped. + +**No daemon, no supervisor.** A background session is just a detached +`bullpen run` coordinating through the store. The dashboard is a read-and- +dispatch view over that store — close it and the work keeps going, because +nothing was ever supervising it. + +**The sandbox is a feature, not a footnote.** `--sandbox` confines writes to +the workspace on every platform and, on macOS, runs shell commands *and their +children* under Seatbelt. `--sandbox-strict` also cuts network. + +## Install ```bash cargo install --path crates/cli +``` + +Rust 1.97+ (pinned in `rust-toolchain.toml`, so `rustup` fetches the right +one automatically). + +## Quickstart + +Point it at a provider — any one of these is enough: -# Pick your provider(s): -export ANTHROPIC_API_KEY=sk-ant-... # anthropic (default), streams live -bullpen login openrouter # OpenRouter, official OAuth PKCE +```bash +export ANTHROPIC_API_KEY=sk-ant-... # streams tokens live +bullpen login openrouter # official OAuth PKCE bullpen login codex # ChatGPT subscription, device-code flow -export GLM_API_KEY=... # GLM (Z.ai), Anthropic-compatible -export KIMI_API_KEY=... # Kimi (Moonshot), Anthropic-compatible -# ...or skip codex login entirely: bullpen borrows a logged-in Codex CLI's -# session (~/.codex/auth.json) read-only. +``` + +Already have the Codex CLI logged in? bullpen borrows its session from +`~/.codex/auth.json` read-only — nothing to configure, and it never +refreshes that token so it can't invalidate the other tool's login. +Then work: + +```bash cd your-project -bullpen run "find the failing test, explain why it fails" -bullpen run -p codex "..." # ChatGPT subscription -bullpen run -p glm "..." # or -p kimi, -p openrouter -bullpen run --sandbox "refactor X" # confine writes to the workspace (Seatbelt on macOS) -bullpen run -v "..." # show tool activity on stderr -bullpen sessions # list stored sessions -bullpen sessions --json # machine-readable session list -bullpen run -r "follow-up question" # resumes with the session's provider - -# Dispatch and watch many background sessions from one screen: -bullpen run --bg "audit the auth module" # detached; returns immediately -bullpen agents # dashboard: grouped, live, dispatch + peek -bullpen logs # tail a background session's output +bullpen run "find the failing test and explain why it fails" +bullpen run --sandbox "refactor the retry logic" # confine writes +bullpen run -v "..." # tool activity on stderr +``` + +Sessions are resumable by id prefix, with the provider they were created +with: + +```bash +bullpen sessions # what have I got +bullpen sessions --json # same, machine-readable +bullpen run -r 6ee4acc9 "now write the fix" +``` + +## Run many at once + +```bash +bullpen run --bg "audit the auth module" # detached, returns immediately +bullpen agents # the dashboard in the GIF above +bullpen logs 6ee4acc9 # tail a background session ``` -**Agent view** (`bullpen agents`) manages background sessions from one -screen — dispatch, watch them work, peek their output. It's daemonless: -each background session is a detached `bullpen run` that coordinates through -the SQLite store, so they keep running after you close the dashboard, survive -crashes (a dead process shows as Failed and resumes on `run -r`), and appear -in `bullpen sessions` like anything else. - -Providers: **anthropic** (true token streaming + prompt caching), **codex** -(ChatGPT subscription), **openrouter**, **glm**, **kimi**. `--sandbox` -confines file writes to the workspace on every platform and, on macOS, runs -shell commands under Seatbelt so arbitrary code can't escape either; -`--sandbox-strict` also cuts network. - -Sessions persist in `~/.bullpen/bullpen.db` (SQLite, WAL) as an append-only -entry tree plus an execution log — every step of a run is durable the moment -it happens. Kill bullpen mid-run (crash, `kill -9`, power loss) and the next -invocation recovers: interrupted tool calls get marked, the transcript is -closed cleanly, and the session resumes where it left off. - -The same machinery powers **the pen**: the model can delegate bounded tasks -to child agents via the `agent` tool (`inspect` = read-only, `work` = full -tools). Children are ordinary sessions — durable, budgeted, listed by +`bullpen agents` groups sessions by state — **Working** (running, live pid), +**Failed** (running, dead pid — it crashed), **Completed**, **Idle** — and +lets you dispatch from the input line, `Space` to peek at output, `Esc` to +quit. Quitting stops nothing. + +## The pen + +The model can delegate bounded work to child agents through the `agent` +tool: `inspect` for read-only reconnaissance, `work` for the full toolset. +Children are ordinary sessions — durable, budgeted, listed by `bullpen sessions`, resumable — with deterministic identities, so a replayed delegation reattaches to its child instead of running it twice. +## Providers + +| Provider | Wire format | Auth | Verified | +|---|---|---|---| +| `anthropic` | Anthropic messages | `ANTHROPIC_API_KEY` | wire-level tests | +| `codex` | OpenAI Responses (SSE) | `bullpen login codex`, or borrow the Codex CLI | live, incl. tools + resume | +| `openrouter` | OpenAI chat-completions | `bullpen login openrouter` or `OPENROUTER_API_KEY` | live, incl. tools | +| `glm` | Anthropic-compatible | `GLM_API_KEY` | config-only | +| `kimi` | Anthropic-compatible | `KIMI_API_KEY` | config-only | + +Adapters are organized by wire format rather than vendor, which is why +compatible hosts are configuration instead of code. + +Built-in tools: `bash`, `read_file`, `write_file`, `edit_file`, `grep`, +`glob` — plus `agent` when the pen is enabled. + +## Where state lives + +`~/.bullpen/bullpen.db` — SQLite in WAL mode, holding an append-only entry +tree (the conversation) plus a separate execution log (the orchestration). +Delete every execution record and you still have a complete, valid +conversation. + +Reading it while sessions run needs the immutable flag, since WAL databases +can't be opened read-only without their shared-memory file: + +```bash +sqlite3 "file:$HOME/.bullpen/bullpen.db?immutable=1" "select id, status from sessions" +``` + +## Status + +**v0.** Honest about what that means: + +| | | +|---|---| +| ✅ Shipped | Durable execution + crash recovery · the pen (durable subagents) · write-confinement sandbox with Seatbelt on macOS · agent view (dispatch, peek, live state) · 5 providers | +| 🚧 Next | Interactive attach to a live session · needs-input state · notifications · compaction | +| 📋 Planned | Landlock confinement on Linux · a durable workflow engine (steps in SQLite, resumable from any step) | + +Outside `--sandbox`, tools run with the process's full authority. Run it +somewhere you would trust the model to act. + ## Design -Start with [ARCHITECTURE.md](ARCHITECTURE.md). The one-sentence version: -a policy-free core loop (`bullpen-agent`) that knows nothing about vendors, -config, or UI, with everything else composed around it at the edge — plus a -single durable store instead of per-process state. +[ARCHITECTURE.md](ARCHITECTURE.md) is the source of truth. The one-sentence +version: a policy-free core loop (`bullpen-agent`) that knows nothing about +vendors, config, or UI, with everything else composed around it at the edge +— plus a single durable store instead of per-process state. ## Develop ```bash -cargo test # all crates -cargo clippy --all-targets -cargo run -p bullpen -- run "hello" +cargo test --workspace +cargo clippy --workspace --all-targets -- -D warnings +cargo fmt --all --check ``` +All three gate CI on Linux and macOS. The demo above is reproducible — +`docs/media/bullpen-agents.tape` drives the real binary through +[VHS](https://github.com/charmbracelet/vhs). + ## License MIT diff --git a/docs/media/bullpen-agents.gif b/docs/media/bullpen-agents.gif new file mode 100644 index 0000000..3e372f5 Binary files /dev/null and b/docs/media/bullpen-agents.gif differ diff --git a/docs/media/bullpen-agents.tape b/docs/media/bullpen-agents.tape new file mode 100644 index 0000000..99fae5c --- /dev/null +++ b/docs/media/bullpen-agents.tape @@ -0,0 +1,52 @@ +Output bullpen-agents.gif +Output bullpen-agents.mp4 +Set Shell zsh +Set FontSize 20 +Set Width 1300 +Set Height 470 +Set Padding 18 +Set TypingSpeed 40ms + +Hide +Type "export PS1='$ ' PATH=/Users/vics/.cargo/bin:$PATH HOME=/tmp/bpdemo/home; cd /tmp/bpdemo/work; clear" +Enter +Sleep 1s +Show + +Sleep 700ms +Type "bullpen agents" +Sleep 300ms +Enter +Sleep 1800ms + +Type "find the bug in src/retry.rs" +Sleep 300ms +Enter +Sleep 1500ms + +Type "count unchecked items in notes.md" +Sleep 300ms +Enter +Sleep 1500ms + +Type "what is MAX_ATTEMPTS set to?" +Sleep 300ms +Enter +Sleep 3s + +Sleep 27s + +Down +Sleep 600ms +Space +Sleep 5s + +Escape +Sleep 500ms +Escape +Sleep 800ms + +Type "bullpen sessions" +Sleep 300ms +Enter +Sleep 2500ms