[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-80436":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":9,"language":10,"languages":9,"totalLinesOfCode":9,"stars":11,"forks":12,"watchers":13,"openIssues":14,"contributorsCount":14,"subscribersCount":14,"size":14,"stars1d":15,"stars7d":15,"stars30d":16,"stars90d":14,"forks30d":14,"starsTrendScore":17,"compositeScore":18,"rankGlobal":9,"rankLanguage":9,"license":19,"archived":20,"fork":20,"defaultBranch":21,"hasWiki":22,"hasPages":20,"topics":23,"createdAt":9,"pushedAt":9,"updatedAt":24,"readmeContent":25,"aiSummary":26,"trendingCount":14,"starSnapshotCount":14,"syncStatus":27,"lastSyncTime":28,"discoverSource":29},80436,"warren","jayminwest\u002Fwarren","jayminwest","Control plane and UI for cloud-based custom agents that operate in isolation, self-manage, self-repair, and self-improve",null,"TypeScript",69,24,1,0,3,12,9,4.19,"MIT License",false,"main",true,[],"2026-06-12 02:04:02","\u003Cp align=\"center\">\n  \u003Cimg src=\"branding\u002Flogo.png\" alt=\"warren — self-hostable cloud control plane\" width=\"640\">\n\u003C\u002Fp>\n\n# Warren\n\nSpawn cloud agents at your GitHub repos. Watch them work live, steer them mid-run, get a branch back.\n\n[![CI](https:\u002F\u002Fgithub.com\u002Fjayminwest\u002Fwarren\u002Factions\u002Fworkflows\u002Fci.yml\u002Fbadge.svg)](https:\u002F\u002Fgithub.com\u002Fjayminwest\u002Fwarren\u002Factions\u002Fworkflows\u002Fci.yml)\n[![License: MIT](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FLicense-MIT-yellow.svg)](LICENSE)\n\n[**Watch the demo**](https:\u002F\u002Fyoutu.be\u002Fdaa7y8g9BkM)\n\n> A network of interconnected burrows. Agents that operate in isolation, self-manage, self-repair, and self-improve.\n\nWarren is a self-hostable control plane for ephemeral coding agents. Runs are short-lived and sandboxed: they complete a task, validate the changes, push a branch, and spin down. Point it at your repos, dispatch from a browser or CLI, watch the events stream live, and reap the result. **One container, one volume, one HTTP API, one UI.**\n\nA fresh install needs nothing but a GitHub URL and a prompt. The built-in `claude-code` agent ships inline; pick it, paste your repo, write what you want done. Power features (versioned prompt libraries, persistent agent memory, an integrated issue queue, a steerable alternative harness, a shared coordination substrate) light up when you opt into them.\n\n## Who this is for\n\nEngineering teams self-hosting their own agent infrastructure. The deployment unit is one team or one org running one warren on their own box, their own Fly account, or their own cluster. Run it for yourself on a home server today; the [org-readiness roadmap](ROADMAP.md) extends the same architecture to a 50+ engineer organization without forcing a fork.\n\n## Status\n\nStable (`0.6.2`), running on Fly.io in continuous use against real GitHub repos. The end-to-end path is covered by 33 scenario-based acceptance tests in [`scripts\u002Facceptance\u002F`](scripts\u002Facceptance\u002F): manual runs, cron triggers, multi-worker placement, Postgres backend, per-run preview environments, restart recovery, cost tracking, cost analytics, seeds-extensions roundtrip, serial plan-run dispatch, plan-run + Plot composition, Plot-workbench loop. The active frontier is the org-readiness cluster: SSO, remote workers, MCP, audit, budgets, GitHub App auth. See [ROADMAP.md](ROADMAP.md).\n\n## What you get\n\n- **One image, one volume.** The supervisor (`src\u002Fsupervisor\u002Fmain.ts`) is the container ENTRYPOINT. It spawns the sandbox runtime first, waits for the unix socket, then spawns warren. SIGTERM\u002FSIGINT forward to both children; the runtime restarts under a 5-in-60s budget on unexpected exit.\n- **Native sandboxing per run.** Every run gets a fresh `bwrap`-isolated workspace under `\u002Fdata\u002Fburrow\u002F`. The host is unreachable; warren talks to the runtime over a unix socket with a shared bearer token.\n- **Built-in agents.** `claude-code`, `sapling`, and `pi` ship inline (`src\u002Fregistry\u002Fbuiltins\u002F`), so dispatching a run needs no extra setup.\n- **Live event stream.** NDJSON events are persisted to warren's SQLite log and tailed over `GET \u002Fruns\u002F:id\u002Fevents?follow=1`. The UI, CLI (`warren run`), and HTTP clients all consume the same stream.\n- **Steerable mid-run.** `POST \u002Fruns\u002F:id\u002Fsteer` lands a message in the agent's inbox; the next turn picks it up. `POST \u002Fruns\u002F:id\u002Fcancel` aborts cleanly.\n- **Scheduled runs.** `.warren\u002Ftriggers.yaml` defines cron triggers per project; the in-process scheduler dispatches them on the same composition path as manual runs.\n- **Serial plan-run dispatch.** Projects shipping `.seeds\u002F` can `POST \u002Fplan-runs` against a seeds plan; warren walks the plan's children one at a time, spawning one run per child and gating each on the previous PR merging before the next dispatches. Re-dispatching the same plan after some children have closed resumes from the next open child.\n- **Plot-centric UI on opt-in deployments.** When any project ships `.plot\u002F` and at least one Plot exists, warren's default landing flips to `\u002Fplots` and the sidebar reorders Plots above Runs; the standalone path (no `.plot\u002F` projects) stays byte-identical with `\u002Fruns` as the index. The Plot detail page renders intent + substrate + a unified activity feed where human and agent events share one timeline, with inline answer-cards on open questions and a Run-plan button when an `sd_plan` is attached. See [SPEC §11.O.Plot.UI](SPEC.md#11oplotui-plot-centric-ui-surface-pl-9d6a-2026-05-18).\n- **Three thin clients of one pipeline.** Web UI, `warren` admin CLI, and HTTP API all flow through the same composition path ([SPEC §4.3](SPEC.md#43-the-composition-flow)).\n\n## Quickstart (home server)\n\n```bash\ngit clone https:\u002F\u002Fgithub.com\u002Fjayminwest\u002Fwarren && cd warren\ncp .env.example .env && $EDITOR .env\ndocker compose up -d\nopen http:\u002F\u002Flocalhost:8080\n```\n\nPaste your `WARREN_API_TOKEN`, click **Projects → Add**, give it a GitHub URL. Then **Dispatch run**, pick `claude-code`, write a prompt, hit go. The events panel streams; when the run completes warren pushes a branch you can open a PR from.\n\nRequired environment variables (see [`.env.example`](.env.example) for the full list):\n\n| Variable | Purpose |\n|---|---|\n| `WARREN_API_TOKEN` | Bearer token on every route except `\u002Fhealthz`. `openssl rand -hex 32`. |\n| `BURROW_API_TOKEN` | Token the sandbox runtime requires to bind. `openssl rand -hex 32`. |\n| `WARREN_BURROW_TOKEN` | Token warren's runtime client sends. **Must equal `BURROW_API_TOKEN`**; they are the two ends of one channel. |\n| `ANTHROPIC_API_KEY` | Forwarded to agent runtimes that need it. |\n| `GITHUB_TOKEN` | Forwarded for project clones + branch pushes. |\n\nThe compose file applies the four bwrap-required security flags (`apparmor=unconfined`, `seccomp=unconfined`, `systempaths=unconfined`, `cap_add: SYS_ADMIN`). These relax the outer container so the runtime's nested userns sandboxes can come up. Removing any one of them breaks sandbox provisioning.\n\n## Deploy to Fly.io\n\nSame image, same volume layout, same security flags:\n\n```bash\nfly launch                                    # uses .\u002Ffly.toml\nfly volumes create warren_data --size 50 --region sjc\nfly secrets set \\\n    WARREN_API_TOKEN=... \\\n    BURROW_API_TOKEN=... \\\n    WARREN_BURROW_TOKEN=... \\\n    ANTHROPIC_API_KEY=... \\\n    GITHUB_TOKEN=...\n# Optional: attach a managed Postgres instead of the on-volume SQLite.\n# Without this, warren falls back to sqlite:\u002F\u002F\u002Fdata\u002Fwarren.db.\n#   fly secrets set WARREN_DB_URL=postgres:\u002F\u002Fuser:pw@host\u002Fdb\nfly deploy\n```\n\n### Continuous deployment from GitHub Actions\n\nOnce warren is live on Fly, wiring tag-driven auto-deploy is two commands:\n\n```bash\nfly tokens create deploy -a \u003Cyour-warren-app> --name \"github-actions\" --expiry 8760h \\\n  | gh secret set FLY_API_TOKEN -R \u003Cyour-org>\u002F\u003Cyour-fork>\n```\n\nThen add a `deploy` job to your release workflow that runs after release\u002Ftag:\n\n```yaml\ndeploy:\n  needs: release\n  if: needs.release.outputs.release == 'true'\n  runs-on: ubuntu-latest\n  concurrency:\n    group: fly-deploy-\u003Cyour-warren-app>\n    cancel-in-progress: false\n  steps:\n    - uses: actions\u002Fcheckout@v6\n    - uses: superfly\u002Fflyctl-actions\u002Fsetup-flyctl@master\n    - env: { FLY_API_TOKEN: \"${{ secrets.FLY_API_TOKEN }}\" }\n      run: flyctl deploy --remote-only --app \u003Cyour-warren-app>\n```\n\nThe deploy-scoped token is bound to a single app and cannot list secrets, ssh, or touch other apps, so it's safe to live in CI. See `.github\u002Fworkflows\u002Frelease.yml` for the reference shape used by `warren-deployed.fly.dev`.\n\n### Observability on a live deploy\n\nWarren ships enough operator-visible surface for a single-box \u002F single-Fly-app deploy to be inspectable without bolting on extra infrastructure. The pieces:\n\n- **Health & readiness probes.** `GET \u002Fhealthz` is a cheap liveness check (returns `{ok: true}`, auth-exempt — point Fly's `[[services.http_checks]]` or any uptime monitor at it). `GET \u002Freadyz` runs deeper diagnostics (DB reachable, bwrap usable, canopy clone fresh when `CANOPY_REPO_URL` is set) and returns a `DiagnosticCheck[]` payload — use it for deploy gating, not for hot-path liveness. `GET \u002Fversion` returns `{version}` straight from `src\u002Findex.ts` so you can confirm a rollout actually swapped the image.\n- **Structured JSON logs.** The server emits one [pino](https:\u002F\u002Fgetpino.io) JSON line per event on stdout (name `warren`, level controlled by `WARREN_LOG_LEVEL`, default `info`). On Fly that means everything is queryable with `fly logs -a \u003Cyour-warren-app>` and via the **Logs** tab on the Fly dashboard (`https:\u002F\u002Ffly.io\u002Fapps\u002F\u003Cyour-warren-app>\u002Fmonitoring`). Pipe through `| jq` locally for ad-hoc filtering; ship to an external store with a [pino transport](https:\u002F\u002Fgetpino.io\u002F#\u002Fdocs\u002Ftransports) if you need retention beyond Fly's window.\n- **Correlation IDs.** Every HTTP response carries an `X-Request-ID` header (`src\u002Fserver\u002Frequest-id.ts`, warren-30af). Warren honours a well-formed inbound `X-Request-ID` and otherwise mints one; the same id is bound into the per-request pino child logger, so grepping `fly logs | jq 'select(.req_id == \"…\")'` reconstructs the full server-side trace for one client call. Forward the header from any reverse proxy in front of warren to keep the chain unbroken.\n- **Per-run cost & token usage.** `runs.cost_usd` and `runs.tokens_*` columns are populated for the `pi` and `claude-code` built-ins (SPEC §11.K); the UI run-detail page surfaces them and `GET \u002Fanalytics\u002Fcost?from=&to=&projectId=` aggregates across runs (`src\u002Fdb\u002Frepos\u002Fruns.ts:listForAnalytics`). This is reporting, not enforcement — budget caps are deferred to R-17.\n- **Fly dashboards.** The **Metrics** tab on the Fly app dashboard graphs CPU, RAM, and per-volume IO out of the box; pair it with the **Logs** tab above for incident triage. `fly status -a \u003Cyour-warren-app>` and `fly vm status` print machine + volume state from the CLI. `fly ssh console -a \u003Cyour-warren-app>` drops you into the running container if you need to inspect `\u002Fdata\u002Fwarren.db` directly (sqlite default) or tail the canopy clone under `WARREN_CANOPY_DIR`.\n- **Pre-flight checks.** Run `warren doctor` (`src\u002Fcli\u002Fcommands\u002Fdoctor.ts`) against a deployed instance to surface common misconfigurations — empty\u002Fplaceholder bearer tokens, unbalanced preview markers, missing `WARREN_PREVIEW_HOST` when previews are wired, etc. Cheaper than reading the logs after a failed run.\n\nThere is no built-in Prometheus \u002F OpenTelemetry exporter in V1. If you need one, the request-id + pino combination is the seam to extend; the route table (`ROUTE_TABLE` in `src\u002Fserver\u002Fhandlers.ts`, documented in [`docs\u002Fhttp-api.md`](docs\u002Fhttp-api.md)) is the stable surface to instrument against.\n\n## Power features (opt-in)\n\nWarren bundles a small set of [os-eco](https:\u002F\u002Fgithub.com\u002Fjayminwest\u002Fos-eco) tools as built-in features. They're not required for a basic run. Each lights up when you use it and stays silent when you don't.\n\n### Custom agents: bring your own prompt library\n\nThe built-in `claude-code`, `sapling`, and `pi` agents cover the common case. To define custom agents as versioned prompts (with inheritance, mixins, and per-agent sandbox config), point warren at a [canopy](https:\u002F\u002Fgithub.com\u002Fjayminwest\u002Fcanopy) repo:\n\n```bash\nfly secrets set CANOPY_REPO_URL=https:\u002F\u002Fgithub.com\u002F\u003Cyou>\u002Fagents.git\n```\n\nLibrary agents override built-ins by name. See [SPEC §4.2](SPEC.md#42-the-bundle-expressed-in-canopy) for the agent-as-prompt schema.\n\n### Agent memory: persistent expertise across runs\n\nIf a project has a `.mulch\u002F` directory, every run gets that expertise primed into context on spawn. As the agent learns conventions, patterns, and failure modes, it records them with `ml record`; reap merges the new records back to the project's persistent `.mulch\u002F` with last-write-wins by timestamp. Memory accumulates across runs without a database, just files in the repo. See [mulch](https:\u002F\u002Fgithub.com\u002Fjayminwest\u002Fmulch).\n\n### Issue queue: agents work from and write to seeds\n\nIf a project has a `.seeds\u002F` directory, agents can `sd ready` for unblocked work, claim it with `sd update`, file follow-ups with `sd create`, and close completed seeds with `sd close`. Reap closes any seeds the agent marked done. The trigger scheduler can also fire on past-due `extensions.scheduledFor` seed timestamps ([SPEC §11.I](SPEC.md)). See [seeds](https:\u002F\u002Fgithub.com\u002Fjayminwest\u002Fseeds).\n\n`.seeds\u002F` also enables **plan-run dispatch**: `POST \u002Fplan-runs { project, planId, agent }` against a seeds plan walks its children sequentially, one warren run per child, gating each step on the previous PR merging before the next dispatches. Children whose seeds are already closed are skipped, so re-dispatching the same plan after partial completion resumes from the next open child. PlanRun is a dispatch mode on top of the existing single-run primitive — same spawn path, same sandbox, same event stream. Tune the coordinator with `WARREN_PLAN_RUN_TICK_MS` (default 10s) or disable it with `WARREN_PLAN_RUN_DISABLED=1`. See [SPEC §11.P](SPEC.md#11p-planrun-serial-plan-execution-pl-a258-2026-05-18).\n\n### Steerable harness: sapling as an alternative to claude-code\n\nThe built-in `sapling` agent is a headless coding harness with proactive context management. Use it the same way you'd use `claude-code`. See [sapling](https:\u002F\u002Fgithub.com\u002Fjayminwest\u002Fsapling).\n\n### Shared coordination: plot as a peer-network substrate\n\nIf a project has a `.plot\u002F` directory, runs dispatched with a `plot_id` get `PLOT_ID` + `PLOT_ACTOR=agent:\u003Cname>:\u003Crun-id>` injected into the sandbox. The agent inside reads context with `plot get` and appends `decision_made` \u002F `question_posed` \u002F `artifact_produced` events with `plot append`. Warren appends a `run_dispatched` event to the originating Plot on spawn and merges the workspace `.plot\u002F` back at reap, mirroring agent events into the run's event stream tagged with `plot_id`. Projects without `.plot\u002F` are byte-identical to the pre-change behavior. See [plot](https:\u002F\u002Fgithub.com\u002Fjayminwest\u002Fplot) and [SPEC §11.O](SPEC.md#11o-plot-integration-pl-2047-2026-05-17).\n\nWhen a project ships **both** `.plot\u002F` and `.seeds\u002F`, plan-runs compose onto Plot. A `POST \u002Fplan-runs { plot_id }` emits one `plan_run_dispatched` event on the bound Plot at start, threads `plot_id` through every child so each gets `PLOT_ID` + `PLOT_ACTOR` in its sandbox and emits its own `run_dispatched` event, and auto-transitions the Plot from `active` → `done` when the final child merges. Plan-runs dispatched without `plot_id`, or against a project without `.plot\u002F`, are byte-identical to the standalone plan-run baseline. See [SPEC §11.P.Plot](SPEC.md#11pplot-planrun--plot-composition-pl-7937-2026-05-18).\n\n### PR-body template: per-project overrides for the PR warren opens\n\nAfter a successful run, warren opens a PR with a generated body (summary, run link, commits, files-changed, prompt, etc.). Projects override individual sections by shipping a `.warren\u002Fpr-template.md` file: every `## \u003Cfragment_name>` heading replaces the default body for that fragment. Unspecified fragments keep the built-in defaults, so you can override just one piece.\n\n```markdown\n## trailer\n\nReviewed-by: @platform-team\n\nPlease follow our [PR checklist](https:\u002F\u002Fexample.com\u002Fchecklist) before merging.\n```\n\nRecognized fragment names: `title`, `summary`, `run`, `seeds`, `preview_url_or_placeholder`, `commits`, `files_changed`, `prompt`, `trailer`. A whitespace-only body removes the fragment entirely. Unknown names + unbalanced preview markers surface via `warren doctor` so typos are loud. See [SPEC §11.L](SPEC.md) for the full fragment contract.\n\n### Per-run preview environments: click the agent's branch instead of checking it out\n\nWhen a project ships a `.warren\u002Fpreview.yaml`, warren launches `preview.command` as a sidecar inside the same burrow workspace after a successful run, allocates a port, and exposes the running app at `https:\u002F\u002Frun-\u003CrunId>.\u003CWARREN_PREVIEW_HOST>`. Reviewers click the URL instead of `git checkout`-ing the branch. Idle sessions are reaped automatically; the run-detail page surfaces a status badge and a manual teardown button. Opt in with two pieces:\n\n1. **Operator side.** Set `WARREN_PREVIEW_HOST=preview.\u003Cyour-host>` and point a wildcard CNAME at the warren box (see [Per-run previews: operator setup](#per-run-previews-operator-setup) below). Without `WARREN_PREVIEW_HOST` the launch sub-step is a no-op (the run still completes, the URL just has no listener).\n2. **Project side.** Ship `.warren\u002Fpreview.yaml` with the preview block at the top level:\n\n   ```yaml\n   type: server\n   command: bun run dev\n   port: 3000\n   readiness_path: \u002Fhealthz\n   idle_ttl: 30m\n   max_lifetime: 8h\n   ```\n\n   Projects that don't opt in skip the preview sub-step entirely. See [SPEC §11.L](SPEC.md#11l-per-run-preview-environments-2026-05-14) for the full contract.\n\n## Per-run previews: operator setup\n\nEnable the preview proxy by giving warren a host suffix it can route on:\n\n```bash\nWARREN_PREVIEW_HOST=preview.warren.example.com\n```\n\nWarren then matches `Host: run-\u003CrunId>.preview.warren.example.com` as a preamble before its API\u002FUI routes and forwards to the in-sandbox port allocated at reap time. The login route (`GET \u002Fruns\u002F:id\u002Fpreview\u002Flogin?token=…&redirect=…`) accepts the warren bearer in the query and issues a domain-scoped signed cookie (`warren_preview`); the proxy rejects unauthenticated browser requests with 401 (not 502). The HMAC key is derived from `WARREN_API_TOKEN`, so there's no second secret to manage. `warren doctor` warns if the token is empty or matches a placeholder.\n\n**Wildcard DNS.** Point a wildcard CNAME at the warren box so every `run-*` subdomain resolves:\n\n```\n*.preview.warren.example.com   CNAME   warren.example.com\n```\n\n**TLS via Caddy with a wildcard cert.** TLS stays on the operator's edge (SPEC §8.1 \u002F §11.D). Use Caddy's DNS-01 challenge to issue `*.preview.warren.example.com` (HTTP-01 cannot issue wildcards). Minimal Caddyfile snippet:\n\n```caddyfile\n*.preview.warren.example.com {\n    tls {\n        dns cloudflare {env.CLOUDFLARE_API_TOKEN}\n    }\n    reverse_proxy localhost:8080\n}\n```\n\nCaddy's DNS-01 plugin supports Cloudflare, Route 53, DigitalOcean, Hetzner, Linode, OVH, Vultr, and others. See [caddy-dns](https:\u002F\u002Fgithub.com\u002Fcaddy-dns) for the current list. If your provider isn't on it, an operator-controlled per-project subdomain pattern is the alternative.\n\n**Lifecycle knobs.** Tune for scale via `WARREN_PREVIEW_IDLE_TTL` (default `30m`), `WARREN_PREVIEW_MAX_LIFETIME` (`8h`), `WARREN_PREVIEW_MAX_LIVE` (`20`), `WARREN_PREVIEW_PORT_RANGE` (`30000-31000`), and `WARREN_PREVIEW_EVICTION_TICK_MS` (`60000`). Per-project overrides for `idle_ttl` and `max_lifetime` live in `.warren\u002Fpreview.yaml`. `\u002Freadyz` surfaces port-allocator saturation warnings.\n\nCross-host routing for runs landing on remote workers is in progress as R-12. Until then, the proxy returns **501** for off-host runs.\n\nSee [SPEC §11.L](SPEC.md#11l-per-run-preview-environments-2026-05-14) for the full design.\n\n## Architecture\n\n```\n┌──────────────── container (bwrap-friendly host) ────────────────┐\n│  supervisor  ─┬─►  sandbox runtime  (unix socket: \u002Fvar\u002Frun\u002F...) │\n│  (Bun parent) └─►  warren           (Bun.serve :8080, SPA + API)│\n│                                                                 │\n│  \u002Fdata\u002F                                                         │\n│  ├── canopy-repo\u002F         ← optional cloned agent library       │\n│  ├── projects\u002F\u003Co>\u002F\u003Cn>\u002F    ← cloned project repos                │\n│  ├── burrow\u002F              ← runtime home (SQLite, workspaces)   │\n│  └── warren.db            ← warren's SQLite (runs, events)      │\n└─────────────────────────────────────────────────────────────────┘\n                              ▲\n                              │  HTTPS (terminated upstream)\n                          [browser]\n```\n\nUnder the hood, warren talks to [burrow](https:\u002F\u002Fgithub.com\u002Fjayminwest\u002Fburrow) as the sandbox runtime. They are co-tenanted inside the container, share a unix socket, and share a bearer token (`BURROW_API_TOKEN` == `WARREN_BURROW_TOKEN`). See [SPEC §10.3](SPEC.md#103-container-layout) for the full layout.\n\n## CLI\n\nThe `warren` (or `wr`) admin CLI is for ops; the web UI is daily.\n\n| Command | Description |\n|---|---|\n| `warren register-agent \u003Cname>` | Refresh canopy + register one agent |\n| `warren add-project \u003Cgit-url>` | Clone a project under `\u002Fdata\u002Fprojects` |\n| `warren run \u003Cagent> \u003Cproject> -p \"...\"` | One-shot run, no UI |\n| `warren init` | Scaffold a `.warren\u002F` directory in a project |\n| `warren doctor` | Runtime reachable? Bwrap working? DB reachable? |\n| `warren serve` | Start the HTTP server (default in entrypoint) |\n| `warren db migrate-to-postgres --from \u003Csqlite> --to \u003Cpg-url>` | One-shot SQLite → Postgres porter ([R-13](ROADMAP.md)) |\n\n`warren run claude-code \u003Cproject> -p \"...\"` does the full composition end-to-end: resolves the agent (built-in or library), provisions the sandbox, dispatches the run, streams events back, then pushes the branch. If the project has `.mulch\u002F` or `.seeds\u002F`, those round-trip too.\n\n## HTTP API\n\n```\nGET    \u002Fagents                       list registered agents\nPOST   \u002Fagents\u002Frefresh               re-clone the optional canopy library\nGET    \u002Fagents\u002F:name                 rendered agent JSON\n\nGET    \u002Fprojects                     list cloned projects\nPOST   \u002Fprojects                     { gitUrl, defaultBranch? } → clone\nPOST   \u002Fprojects\u002F:id\u002Frefresh         git fetch + reset to upstream HEAD\nDELETE \u002Fprojects\u002F:id                 remove project\nGET    \u002Fprojects\u002F:id\u002Fwarren-config   parsed .warren\u002F envelope\nGET    \u002Fprojects\u002F:id\u002Ftriggers        scheduler state per trigger\nPOST   \u002Fprojects\u002F:id\u002Ftriggers\u002F:tid\u002Frun   dispatch a trigger inline\n\nPOST   \u002Fruns                         { agent, project, prompt } → spawn\nGET    \u002Fruns                         list (filter by status \u002F agent \u002F project)\nGET    \u002Fruns\u002F:id                     detail incl. rendered_agent_json\nGET    \u002Fruns\u002F:id\u002Fevents?follow=1     NDJSON tail (warren log + live)\nPOST   \u002Fruns\u002F:id\u002Fsteer               proxy to runtime inbox\nPOST   \u002Fruns\u002F:id\u002Fcancel              proxy to runtime cancel\nGET    \u002Fruns\u002F:id\u002Fpreview\u002Flogin       issue signed-cookie + 302 (auth-exempt, ?token=)\nPOST   \u002Fruns\u002F:id\u002Fpreview\u002Fteardown    manual preview teardown (idempotent)\n\nPOST   \u002Fplan-runs                    { project, planId, agent } → serial dispatch (.seeds\u002F only)\nGET    \u002Fplan-runs                    list (filter by project \u002F state)\nGET    \u002Fplan-runs\u002F:id                detail + fanned-out child runs[]\nPOST   \u002Fplan-runs\u002F:id\u002Fcancel         cancel; aborts the in-flight child run\nGET    \u002Fplan-runs\u002F:id\u002Fevents         NDJSON tail union over every child run\n\nGET    \u002Fhealthz                      liveness (no auth)\nGET    \u002Freadyz                       runtime + first-render check\n```\n\n`Authorization: Bearer ${WARREN_API_TOKEN}` is required on every non-`\u002Fhealthz` route. Warren does not terminate TLS; front it with Caddy on a home server, or rely on Fly's edge.\n\n## Development\n\nRequires [Bun](https:\u002F\u002Fbun.sh) v1.1+.\n\n```bash\nbun install\nbun test                                          # all unit tests\nbun run lint                                      # biome check --error-on-warnings\nbun run typecheck                                 # tsc --noEmit\nbun test && bun run lint && bun run typecheck     # all quality gates\n```\n\nUI development (separate from the server build):\n\n```bash\nbun run ui:install\nbun run ui:dev\n```\n\nThe acceptance harness in [`scripts\u002Facceptance\u002F`](scripts\u002Facceptance\u002F) drives 33 scenarios against a live container. See [ACCEPTANCE.md](ACCEPTANCE.md) for the runbook.\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md) for branch naming, testing conventions, and PR expectations.\n\n## Project layout\n\n```\nsrc\u002F\n├── index.ts            library entry (currently VERSION constant only)\n├── core\u002F               types, errors, id minting (ag_*, prj_*, run_*)\n├── registry\u002F           agent definition resolution (built-in + library)\n├── projects\u002F           GitHub clone management\n├── runs\u002F               spawn \u002F stream \u002F reap composition flow (SPEC §4.3)\n├── triggers\u002F           cron + scheduled-for dispatcher (SPEC §11.I)\n├── warren-config\u002F      .warren\u002F per-project config loader + cache (SPEC §11.H)\n├── client\u002F             typed SDK for driving warren's HTTP API programmatically\n├── burrow-client\u002F      facade over the sandbox runtime's HttpClient\n├── supervisor\u002F         container entrypoint (spawns warren + runtime)\n├── server\u002F             Bun.serve HTTP API + static UI serving\n├── db\u002F                 drizzle schema + bun:sqlite repos\n├── cli\u002F                warren admin commands\n└── ui\u002F                 React + Vite + shadcn SPA\n```\n\n## Client SDK\n\n`src\u002Fclient\u002F` exports a typed TypeScript client for driving warren programmatically — dispatching runs, streaming events, managing projects and plots — without reimplementing the wire format. Zero server-side imports; intended for scripts, CLIs, acceptance harnesses, and external agents.\n\n### Setup\n\n```bash\nexport WARREN_BASE_URL=https:\u002F\u002Fwarren.example.com   # default: http:\u002F\u002Flocalhost:8080\nexport WARREN_API_TOKEN=\u003Cyour-token>\n```\n\n### Dispatch a run and wait for it\n\n```ts\nimport { WarrenClient } from \".\u002Fsrc\u002Fclient\u002Findex.ts\";\n\nconst warren = WarrenClient.fromEnv();\nawait warren.probe();  \u002F\u002F throws WarrenUnreachableError if warren is down\n\nconst { run } = await warren.dispatch({\n  agent: \"claude-code\",\n  project: \"my-project\",\n  prompt: \"Add input validation to the signup form\",\n  branch: \"main\",          \u002F\u002F optional: git ref to clone from\n  model: \"claude-sonnet-4-6\", \u002F\u002F optional: override the default model\n});\n\nconst final = await warren.waitForRun(run.id, {\n  onTick: (r) => console.log(`${r.id}: ${r.state}`),\n});\nconsole.log(`Run ${final.state}, PR: ${final.prUrl}`);\n```\n\n### Stream events\n\n```ts\nfor await (const event of warren.streamRunEvents(run.id, { follow: true })) {\n  if (event.stream === \"stdout\") process.stdout.write(String(event.payload));\n}\n```\n\n### Steer a running agent\n\n```ts\nawait warren.steer(run.id, {\n  body: \"Focus on the email field first, skip phone for now\",\n  priority: \"high\",\n});\n```\n\n### Plots and plan-runs\n\n```ts\n\u002F\u002F List active plots\nconst { plots } = await warren.listPlots({ status: \"active\" });\n\n\u002F\u002F Get full plot detail (intent + attachments + event log)\nconst plot = await warren.getPlot(plots[0].id);\n\n\u002F\u002F Dispatch a serial plan-run against a seeds plan\nconst { planRun } = await warren.createPlanRun({\n  project: \"my-project\",\n  planId: \"pl-abc123\",\n  agent: \"claude-code\",\n  plotId: plot.id,  \u002F\u002F optional: compose onto the plot\n});\n```\n\n### Error handling\n\n```ts\nimport { WarrenClientError, WarrenUnreachableError } from \".\u002Fsrc\u002Fclient\u002Findex.ts\";\n\ntry {\n  await warren.dispatch({ agent: \"claude-code\", project: \"bad-id\", prompt: \"...\" });\n} catch (err) {\n  if (err instanceof WarrenUnreachableError) {\n    \u002F\u002F warren is down or unreachable\n  } else if (err instanceof WarrenClientError) {\n    \u002F\u002F warren returned an error: err.status, err.code, err.message, err.hint\n  }\n}\n```\n\nThe full type surface (all inputs, outputs, row shapes, enums) is in `src\u002Fclient\u002Ftypes.ts`.\n\n## Operating model\n\nHow the current release is scoped. Full details in [SPEC §11.D](SPEC.md#11d-v1-security-posture-known-limitations):\n\n- **Single bearer token.** Rotation, expiry, and scopes are not supported; rotate by editing `.env` (or `fly secrets set`) and bouncing the container. Per-user identity is on the roadmap (R-09).\n- **TLS is upstream's job.** Direct HTTP on a non-loopback bind is a misconfiguration; `warren doctor` warns.\n- **Trust-the-socket** between warren and the runtime inside the container, which are co-tenanted by design.\n- **No CSRF, single-user.** UI calls warren's API with the bearer; CORS is strict.\n- **SQLite by default; Postgres optional.** Run history and scheduler state live in `\u002Fdata\u002Fwarren.db` on the local volume out of the box. Org-scale deploys can attach a managed Postgres by setting `WARREN_DB_URL=postgres:\u002F\u002Fuser:pw@host\u002Fdb`; burrow's per-run SQLite stays untouched either way.\n- **One host is the concurrency ceiling.** Horizontal scale-out across machines is in flight as R-12.\n\n## Roadmap\n\nThe active direction is org-readiness, extending warren from \"one team, one box\" to \"50-engineer org, their own infra\":\n\n- **Remote sandbox workers** ([R-12](ROADMAP.md)): one warren dispatching across many runtime workers; lifts the single-host ceiling.\n- **SSO \u002F per-user identity** ([R-09](ROADMAP.md)): OIDC login replacing the shared bearer. The bearer stays as a service-account path for CI.\n- **MCP support** ([R-15](ROADMAP.md)): agents declare `mcp_servers` in their prompt frontmatter; warren plumbs credentials into the sandbox.\n- **Cross-project activity UI + stable OpenAPI** ([R-14](ROADMAP.md)): a \"what is every agent doing right now\" view, plus a versioned API contract.\n- **Audit log** ([R-16](ROADMAP.md)) and **cost \u002F concurrency guardrails** ([R-17](ROADMAP.md)): security review and budget control once real user identity lands.\n- **GitHub App auth** ([R-18](ROADMAP.md)): installation-scoped, short-lived per-run tokens replacing the shared PAT.\n\nAll items are additive: none change current behavior when unconfigured. See [ROADMAP.md](ROADMAP.md) for design sketches and sequencing.\n\n## Security\n\nFound a vulnerability? Please follow the disclosure process in [SECURITY.md](SECURITY.md).\n\n## Part of os-eco\n\nWarren is part of the [os-eco](https:\u002F\u002Fgithub.com\u002Fjayminwest\u002Fos-eco) AI agent tooling ecosystem.\n\n## License\n\nMIT. See [LICENSE](LICENSE).\n","Warren 是一个用于在GitHub仓库中部署云代理的控制平面和UI，这些代理能够在隔离环境中自主管理、修复并改进。项目采用TypeScript编写，具备自托管能力，允许用户通过浏览器或命令行界面调度任务，并实时监控执行过程。每个代理运行都是短暂且沙箱化的，完成任务后会自动推送分支并关闭。其核心功能包括内置的`claude-code`等代理、实时事件流以及版本化的提示库等高级特性。适用于希望自建代理基础设施的工程团队，既可以在个人服务器上运行，也支持扩展到50人以上的组织使用。",2,"2026-06-11 04:00:45","CREATED_QUERY"]