{
  "markdown": "<h1 align=\"center\">bullpen</h1>\n\n<p align=\"center\">\n  <strong>A durable agent harness in Rust.</strong><br>\n  Agents as managed, resumable workers — not fire-and-forget processes.\n</p>\n\n<p align=\"center\">\n  <a href=\"https://github.com/StructuPath/bullpen/actions/workflows/ci.yml\"><img alt=\"CI\" src=\"https://github.com/StructuPath/bullpen/actions/workflows/ci.yml/badge.svg\"></a>\n  <img alt=\"Rust 1.97\" src=\"https://img.shields.io/badge/rust-1.97-orange?logo=rust&logoColor=white\">\n  <img alt=\"License MIT\" src=\"https://img.shields.io/badge/license-MIT-blue\">\n  <img alt=\"Status v0\" src=\"https://img.shields.io/badge/status-v0-yellow\">\n</p>\n\n<p align=\"center\">\n  <img src=\"docs/media/bullpen-agents.gif\" alt=\"Dispatching three agents from bullpen agents, watching them complete, peeking at one's answer, and seeing the sessions persist\" width=\"100%\">\n</p>\n\nA bullpen is a roster of warmed-up relievers you call in, pull back, and send\nout again. That is the whole thesis: agent runs should survive the process\nthat started them.\n\n## Why it's different\n\n**Every step is durable the moment it happens.** Sessions live in one SQLite\ndatabase (WAL), not in the memory of whatever process launched them. Kill\nbullpen mid-run — crash, `kill -9`, power loss — and the next invocation\nrecovers: interrupted tool calls are marked, the transcript is closed\ncleanly, and the session resumes where it stopped.\n\n**No daemon, no supervisor.** A background session is just a detached\n`bullpen run` coordinating through the store. The dashboard is a read-and-\ndispatch view over that store — close it and the work keeps going, because\nnothing was ever supervising it.\n\n**The sandbox is a feature, not a footnote.** `--sandbox` confines writes to\nthe workspace on every platform and, on macOS, runs shell commands *and their\nchildren* under Seatbelt. `--sandbox-strict` also cuts network — including\nURL fetches through the read tool.\n\n**Edits can't land in the wrong place.** File reads are hashline: every\nline carries a content-hash anchor, and patches address those anchors. An\nedit against a file that drifted is detected — a moved line is followed\nwhile its hash is unique, a changed line fails with fresh context — never\nsilently misapplied.\n\n## Install\n\n```bash\ncargo install --path crates/cli\n```\n\nRust 1.97+ (pinned in `rust-toolchain.toml`, so `rustup` fetches the right\none automatically).\n\n## Quickstart\n\nPoint it at a provider — any one of these is enough:\n\n```bash\nexport ANTHROPIC_API_KEY=sk-ant-...   # streams tokens live\nbullpen login openrouter              # official OAuth PKCE\nbullpen login codex                   # ChatGPT subscription, device-code flow\n```\n\nAlready have the Codex CLI logged in? bullpen borrows its session from\n`~/.codex/auth.json` read-only — nothing to configure, and it never\nrefreshes that token so it can't invalidate the other tool's login.\n\nThen work:\n\n```bash\ncd your-project\nbullpen run \"find the failing test and explain why it fails\"\nbullpen run --sandbox \"refactor the retry logic\"     # confine writes\nbullpen run -v \"...\"                                 # tool activity on stderr\nbullpen run --json \"...\"                             # NDJSON event stream on stdout\n```\n\nSessions are resumable by id prefix, with the provider they were created\nwith:\n\n```bash\nbullpen sessions                       # what have I got\nbullpen sessions --json                # same, machine-readable\nbullpen run -r 6ee4acc9 \"now write the fix\"\n```\n\n## Run many at once\n\n```bash\nbullpen run --bg \"audit the auth module\"   # detached, returns immediately\nbullpen run --bg --worktree \"refactor it\"  # …in its own git worktree\nbullpen agents                             # the dashboard in the GIF above\nbullpen logs 6ee4acc9                      # tail a background session\n```\n\n`bullpen agents` groups sessions by state — **Working** (running, live pid),\n**Failed** (running, dead pid — it crashed), **Completed**, **Idle** — and\nlets you dispatch from the input line, `Space` to peek at output, `Esc` to\nquit. Quitting stops nothing.\n\nPlain `--bg` sessions share your checkout, so two of them edit the same\nfiles. `--worktree` gives a session a git worktree of its own on a\nrun-unique `bullpen/<id>` branch, under `$BULLPEN_HOME/worktrees/<session>`;\nthe path shows up in `bullpen sessions`, in `bullpen sessions --json`, and\nin the peek panel, and `bullpen run -r <id>` returns to it from anywhere. Outside a\ngit repository the flag fails rather than quietly sharing the checkout.\n**Nothing removes a worktree or its branch** — not on success, not on\nfailure, not later. A worktree can hold the only copy of what an agent did,\nso cleaning up is yours to decide.\n\n## The pen\n\nThe model can delegate bounded work to child agents through the `agent`\ntool: `inspect` for read-only reconnaissance, `work` for the full toolset —\noptionally in the child's own git worktree (`worktree: true`), optionally\ndetached (`background: true`) with the `job` tool to list, wait on, and\ncancel what's in flight. Children are ordinary sessions — durable,\nbudgeted, listed by `bullpen sessions`, resumable — with deterministic\nidentities, so a replayed delegation reattaches to its child instead of\nrunning it twice.\n\nPut together: the model can fan out several isolated work children in the\nbackground, keep working, then join on each result — and every child stays\ncrash-recoverable and inspectable from the CLI the whole way.\n\n## Tools\n\n| Tool | What it does |\n|---|---|\n| `bash` | Shell in the workspace; sandboxed with its children under Seatbelt on macOS |\n| `read_file` | One path for files (hashline `line#hash` anchors), directories (sorted listings), SQLite databases (schema view or read-only `query`), zip/tar/tar.gz archives (listing or one `entry`; plain gzip decompresses as text), and http(s) URLs (streamed cap; refused when the sandbox denies network) |\n| `write_file` / `edit_file` | Writes under sandbox confinement; edits by exact string or by anchored hashline patch with stale-anchor recovery |\n| `grep` / `glob` | Regex content search and path patterns, `.gitignore`-aware |\n| `ast_grep` / `ast_edit` | Structural search and rewrite over the syntax tree via [ast-grep](https://ast-grep.github.io) (when installed); rewrites preview by default and write only on `apply: true` |\n| `github` | GitHub CLI operations with your own `gh` login (when installed) — reads run in parallel, mutations stay serial |\n| `agent` | The pen: delegate to durable child agents (above) |\n| `job` | The coordination plane: list children with live state, wait for a result, cancel a background child |\n| `todo` | A durable session plan in the store — survives crashes and resumes; one item in progress at a time, enforced by the runtime |\n| `ask` | One structured question to whoever is driving an interactive run; detached runs get the reason instead of a hang |\n\nParallel safety is decided per call by the runtime, never self-declared by\nthe model: reads, inspect children, isolated work children, and background\ndispatches run concurrently; shared-checkout mutations stay serial.\n[docs/TOOLS.md](docs/TOOLS.md) maps the rest of the planned surface onto\nthe durability contract.\n\n## Providers\n\n| Provider | Wire format | Auth | Verified |\n|---|---|---|---|\n| `anthropic` | Anthropic messages | `ANTHROPIC_API_KEY` | wire-level tests |\n| `codex` | OpenAI Responses (SSE) | `bullpen login codex`, or borrow the Codex CLI | live, incl. tools + resume |\n| `openrouter` | OpenAI chat-completions | `bullpen login openrouter` or `OPENROUTER_API_KEY` | live, incl. tools |\n| `glm` | Anthropic-compatible | `GLM_API_KEY` | config-only |\n| `kimi` | Anthropic-compatible | `KIMI_API_KEY` | config-only |\n\nAdapters are organized by wire format rather than vendor, which is why\ncompatible hosts are configuration instead of code.\n\n## Where state lives\n\n`~/.bullpen/bullpen.db` — SQLite in WAL mode, holding an append-only entry\ntree (the conversation) plus a separate execution log (the orchestration).\nDelete every execution record and you still have a complete, valid\nconversation.\n\nReading it while sessions run needs the immutable flag, since WAL databases\ncan't be opened read-only without their shared-memory file:\n\n```bash\nsqlite3 \"file:${BULLPEN_HOME:-$HOME/.bullpen}/bullpen.db?immutable=1\" \"select id, status from sessions\"\n```\n\nSet `BULLPEN_HOME` to move the whole directory — database, `auth.json`,\nbackground logs, and `--worktree` checkouts (`worktrees/<session-id>`) land\ndirectly in it, with no `.bullpen` segment appended.\n\n## Status\n\n**v0.** Honest about what that means:\n\n| | |\n|---|---|\n| ✅ Shipped | Durable execution + crash recovery · the pen (durable subagents, worktree isolation, background dispatch + `job`) · hashline edits with anchor recovery · durable session plans (`todo`) · follow-up questions (`ask`) · write-confinement sandbox with Seatbelt on macOS · agent view (dispatch, peek, live state) · 5 providers |\n| 🚧 Next | Interactive attach to a live session · needs-input state · notifications · compaction |\n| 📋 Planned | Landlock confinement on Linux · a durable workflow engine (steps in SQLite, resumable from any step) |\n\nOutside `--sandbox`, tools run with the process's full authority. Run it\nsomewhere you would trust the model to act.\n\n## Design\n\n[ARCHITECTURE.md](ARCHITECTURE.md) is the source of truth. The one-sentence\nversion: a policy-free core loop (`bullpen-agent`) that knows nothing about\nvendors, config, or UI, with everything else composed around it at the edge\n— plus a single durable store instead of per-process state.\n\n## Develop\n\n```bash\ncargo test --workspace\ncargo clippy --workspace --all-targets -- -D warnings\ncargo fmt --all --check\n```\n\nAll three gate CI on Linux and macOS. The demo above is reproducible —\n`docs/media/bullpen-agents.tape` drives the real binary through\n[VHS](https://github.com/charmbracelet/vhs).\n\n## License\n\nMIT\n",
  "bytes": 9895,
  "sha": "b79a75edda8736a7bf083468d2d9fca1ee1aa2c78739623cb2dc66cbccfa6f61",
  "repo_slug": "structupath/bullpen",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/okf_structupath_bullpen_openwiki_index_md_3080047d/readme"
}