Skip to content

Agent backends

Warden was built around Claude Code, but the agent layer is an adapter layer: each console coding agent is normalized behind a neutral Backend interface (internal/agentbackend), and warden core never references a concrete agent binary directly. You pick the backend per agent at spawn time.

Stable1 backend
claudeStableFull-capability reference backend — structured transcript, resume, approvals, priced spend, system-prompt injection.
Beta2 backends
codexBetaOpenAI Codex CLI. JSONL rollout transcript, session fork (wd agent fork), review command, dir-scoped resume with discover-then-pin.
antigravityBetaGoogle Antigravity (agy) — trajectory JSONL transcript, dual-bucket usage, multi-vendor model menu via OAuth.
Experimental5 backends
aiderExperimentalBring-your-own-model; autonomous one-shot task that exits when done. Structured Tier-A transcript; no resume.
opencodeExperimentalStructured Tier-A transcript (via opencode export) and dir-scoped resume; bring-your-own-model including local Ollama.
crushExperimentalSQLite-backed Bubble Tea TUI; transcript via crush session show —json; dir-scoped resume; prompt auto-typed post-launch.
gooseExperimentalInteractive REPL agent; name-deterministic resume; transcript via goose session export; BYO provider via env vars.
cursorExperimentalCursor CLI agent — live state, approval & trust detection, triple-bucket subscription usage. No digest (Tier-C transcript).
BackendidTierSummary
Claude Code (default)claudeAFull fidelity — digests, savings, priced spend, resume, all permission modes
AideraiderA🧪 Experimental. Bring-your-own-model; structured markdown transcript ⇒ real digests; no resume, no priced spend
OpenCodeopencodeA🧪 Experimental. Bring-your-own-model; structured JSON transcript (via opencode export) ⇒ real digests; resumes the worktree’s last session; no priced spend (BYO model)
Codex CLIcodexAβ Beta. BYO provider (Codex config / -m); structured JSONL transcript (rollout files) ⇒ real digests; resumes dir-scoped, upgraded to exact-id via discover-then-pin; live state + approval detection; context injection (AGENTS.md); no priced spend
CrushcrushA🧪 Experimental. BYO model (config-driven TUI); structured JSON transcript (via crush session show --json) ⇒ real digests; resumes dir-scoped; initial prompt auto-typed post-launch via PromptSeeder (see Typed-prompt seeding); no priced spend
GoosegooseA🧪 Experimental. BYO provider (GOOSE_PROVIDER/GOOSE_MODEL env); structured JSON transcript (via goose session export) ⇒ real digests; resumes name-deterministic; no model flag on session launch; initial prompt seeded via PromptSeeder; no priced spend
Cursor CLIcursorC🧪 Experimental. Hosted plan (cursor-agent, billed to your Cursor subscription); rich native permission modes (plan/ask/auto-review/force); resumes dir-scoped; live state + approval/trust detection; initial prompt seeded via PromptSeeder; no structured transcript yet ⇒ no digests; no priced spend
Antigravity CLIantigravityAβ Beta. Google-hosted free tier (agy, multi-vendor model menu); structured trajectory JSONL (incl. tool calls / files changed) ⇒ real digests; resumes dir-scoped; live state + approval/trust detection; no priced spend

A plain terminal is not an AI CLI choice — it’s a first-class session kind (kind=terminal): a managed $SHELL seat beside the fleet with warden’s normal worktree/git/tmux lifecycle but no AI features. Spawn one with warden start --kind terminal / spawn_agent {kind:"terminal"}. A terminal always belongs to a project: pass project_id or a directory that resolves to one, otherwise the spawn is refused. See the TUI cockpit guide.

Terminal window
# Claude (default)
warden start --role general "review the auth module"
# Aider against a local Ollama model (free, offline)
export OLLAMA_API_BASE=http://127.0.0.1:11434
warden start --role general "implement the add function" \
--aicli aider --model ollama_chat/qwen2.5-coder:3b --dir .
# OpenCode against a local Ollama model (free, offline)
warden start --role general "implement the add function" \
--aicli opencode --model ollama/qwen2.5-coder:3b --dir .
# Codex — configure provider in ~/.codex/config.toml first
warden start --role general "implement the add function" --aicli codex --dir .
# Crush — configure provider in ~/.config/crush/crush.json first
warden start --role general "implement the add function" --aicli crush --dir .
# Goose against a local Ollama model ($0)
GOOSE_PROVIDER=ollama GOOSE_MODEL=qwen2.5-coder:3b \
warden start --role general "implement the add function" --aicli goose --dir .
# Cursor — log in once with `cursor-agent login` (hosted, billed to your Cursor plan)
warden start --role general "implement the add function" --aicli cursor --dir .
# Antigravity — Google-hosted free tier; `agy` resolves its model from config
warden start --role general "implement the add function" --aicli antigravity --dir .
# Terminal — a plain shell in the directory, no AI (prompt is ignored)
# terminal is a session kind, not a backend (back-compat: --backend terminal still works as a hidden alias)
warden start --kind terminal --dir .

Over MCP, pass the backend param to spawn_agent (kept at parity with the --aicli CLI flag); spawn_agent also takes a kind arg (e.g. kind:"terminal" for a plain shell). The selection is stored on the session (Session.Backend; empty means claude, so existing stores need no migration), and an unknown backend id is rejected before any tmux/worktree side effect.

Backends disagree on exactly the seams warden depends on, so each declares capability flags and warden degrades a feature rather than crashing when a capability is missing:

Capability missingWhat warden does instead
Structured transcriptDigest falls back to a pane-scrape summary; savings (which need real token deltas) are disabled for that agent
Pricingwd usage spend shows tokens (heuristic) not dollars; wd usage savings omits the agent
Resumerotate/handoff re-spawn a fresh agent instead of --resume; restore refuses with a clear message
System-prompt injectionWarden delivers its pipeline/collab/git hints via a rules file the agent reads on startup (InjectContext: AGENTS.md for Codex/OpenCode/Cursor/Antigravity, CRUSH.md, .goosehints); only a backend that auto-reads no such file (Aider) skips the hints entirely — no invalid flags ever reach the agent
Session-id controlWarden discovers the agent-generated id post-launch (DiscoverSessionID, e.g. Codex) and pins it, or falls back to a workdir-based transcript path, instead of assigning one

Automatic backend switching on hard limits

Section titled “Automatic backend switching on hard limits”

When an agent hits a confirmed provider hard limit (session, weekly, or monthly cap), warden’s reactive backend recovery coordinator automatically tries the next eligible subscription backend from the backend registry — no operator action needed. It reads live usage windows from internal/backendusage, ranks candidates by minimum known headroom, switches the agent via the existing hot-swap lifecycle, and only clears the recovery once the new backend stays live for a configurable stabilization window. If all candidates are exhausted, the session persists waiting_for_capacity and retries automatically on the earliest known reset.

Session identity, pipeline job, Autopilot ownership, worktree, branch, role, and tags are preserved through recovery. A manual warden switch, stop, or delete always supersedes automatic work. See Backend hard-limit recovery guide for full details.

Backend superpowers (wd git review, wd backend model, wd agent fork)

Section titled “Backend superpowers (wd git review, wd backend model, wd agent fork)”

Degradation is the deficit side — making a feature warden has work everywhere. The flip side is surfacing a backend’s native strengths that Claude doesn’t have, as first-class verbs added on top (never a restriction):

VerbWhat it surfacesBackends
wd git reviewthe backend’s OWN one-shot diff reviewer against the worktree (agent-native counterpart to wd check / a pr-review agent); --json emits neutral machine-readable findingsCodex (codex review / codex exec review)
wd backend modelthe backend’s live runtime model menu (vs warden’s static aliases); ids feed --model verbatim, listing spends no quotaAntigravity (agy models), Cursor (cursor-agent --list-models)
wd agent forkbranch the source agent’s recorded conversation/reasoning into a new managed agent — a fresh sibling worktree off its branch, carrying its uncommitted tracked changes; the source keeps running. Shorthand for start --fork-fromCodex (codex fork)

wd git review and wd backend model are CLI-only by design — like wd check they exec in the agent’s worktree with no daemon round-trip, so they have no MCP/web/TUI twin. Each is an optional, type-asserted interface (Reviewer/StructuredReviewer, ModelLister); a backend that doesn’t implement one simply isn’t offered the verb (Claude degrades non-zero with a pointer to the alternative). wd agent fork is the exception — it’s a managed spawn that crosses the daemon (a thin wrapper over the fork_from spawn field, gated by the SessionForker interface), so it has MCP + CLI parity via the fork_agent tool. Cursor’s server-side --auto-review (“Smart Auto”) is surfaced not as a verb but as the auto-review permission mode. Full walkthrough: Backend superpowers.

Typed-prompt seeding (Aider, Crush, Goose, Cursor)

Section titled “Typed-prompt seeding (Aider, Crush, Goose, Cursor)”

Backends that take their first task only as typed input have it pasted into the pane after launch, once the UI is ready. A failed paste is retried a few times with a growing backoff, and the outcome is recorded on the session as seed_status (pending → delivered | failed, plus seed_error) — visible in GET /sessions/{id} and wd agent status <id> (prompt seed: / seed error:).

If every retry fails, spawn still succeeds but the failure is no longer silent: warden writes a prompt_seed_failed audit event and sends one operator notification naming the agent and the saved prompt file, <data_dir>/prompts/<id> (written for every spawn path that seeds, mode 0600). Paste it in by hand or wd agent send it. Under autopilot, the guardian re-delivers a failed manager digest once and otherwise escalates it as a spawn failure on the normal heal ladder; the overwatch tells the manager when a worker never got its prompt. Launch-line backends (Claude, Codex, OpenCode, Antigravity, …) are unaffected.

  • Bring-your-own-model: pass --model (any provider, or a local Ollama model like ollama_chat/qwen2.5-coder:3b). Because the model is BYO, warden can’t price it — spend is tokens-only and savings omits the agent.
  • Tier A transcript: Aider’s .aider.chat.history.md is parsed into warden’s neutral turns, so completion digests work on real structured data.
  • Interactive REPL + PromptSeeder: warden launches the bare Aider REPL and types the task in once Repo-map: appears (see Typed-prompt seeding). Headless classify/summarize still uses --message (one-shot).
  • No resume / no session id: Aider continues from repo history, not a pinned id, so warden re-spawns fresh on rotate/handoff rather than resuming.
  • Bring-your-own-model: pass -m provider/model via --model (any provider, or a local Ollama model like ollama/qwen2.5-coder:3b). Spend is tokens-only — OpenCode tracks its own cost/tokens (first-class for paid providers), but warden’s spend integration reads them only once the transcript-usage wiring lands (see #52).
  • Tier A transcript (SQLite, sourced via export): OpenCode stores transcripts in a SQLite DB, not a flat file. The adapter sources the transcript through opencode export <session> — one command that emits the whole session as clean {info, messages[]} JSON — and parses it into warden’s neutral turns, so digests run on real structured data. (This is the design’s “TranscriptSource = DB query, not file read” case; sourcing via export avoids coupling to the DB schema.)
  • Resumes — dir-scoped: unlike Aider, OpenCode does resume. OpenCode mints its own session id (warden can’t assign one), so the adapter keys resume off the agent’s worktree: opencode -c continues that directory’s last session (verified dir-scoped). rotate/handoff/restore therefore work. When a future phase captures and pins OpenCode’s real ses_… id (discover-then-pin, #52), the adapter automatically upgrades to exact-id resume/transcript with no changes.
  • Persistent loop: an OpenCode agent runs its TUI with the task seeded via --prompt, staying interactive (like Claude), rather than running once and exiting (like Aider).
  • Context injection (AGENTS.md): OpenCode has no --append-system-prompt flag, so warden delivers its pipeline/collab/git hints via the AGENTS.md rules file OpenCode reads on startup (InjectContext); SystemPromptInject Caps stays false (it tracks a launch flag specifically).
  • Interactive approvals not yet mapped: headless runs use --dangerously-skip-permissions (no prompts); the TUI’s permission prompts are not yet parsed into warden’s approval queue, so warden infers idle from staleness for OpenCode agents (deferred — see #52).
  • BYO provider: Codex resolves its model/provider from ~/.codex/config.toml (or a -p <profile>). Pass -m <model> via --model when needed. No warden-side dollar pricing (spend shows tokens, savings omits).
  • Tier A transcript: Codex persists sessions as JSONL rollout files ($CODEX_HOME/sessions/<Y>/<M>/<D>/rollout-*.jsonl). The adapter locates the newest rollout whose session_meta.cwd matches the agent’s worktree and parses response_item records into neutral Turns.
  • Resumes — dir-scoped, now exact-id (discover-then-pin): codex resume --last continues the most-recent session in the working directory, and warden additionally discovers the minted session_id from the rollout’s session_meta header post-launch and pins it (DiscoverSessionID), so resume/transcript resolve by exact id rather than dir-scope.
  • Initial prompt: Codex’s TUI accepts a trailing positional prompt at launch (like Claude), so warden can seed the first task normally.
  • Live state + approval detection: warden classifies the Codex TUI pane (esc to interrupt ⇒ working; the numbered “Would you like to …?” permission prompt ⇒ needs-input) and normalizes that prompt into the approvals inbox — so auto-approve works for Codex agents (the headless codex exec surface raises no prompts).
  • Context injection (AGENTS.md): Codex has no --append-system-prompt flag, so warden delivers its pipeline/collab/git hints by writing an AGENTS.md rules file Codex reads on startup (InjectContext). SystemPromptInject Caps stays false — it tracks the launch flag specifically, not whether the addendum is delivered.
  • Full gap doc: docs/agent-backends/codex.md
  • BYO model: the interactive crush TUI is config-driven (model set in ~/.config/crush/crush.json); headless crush run accepts -m. Pass --model for headless use; TUI launch ignores it.
  • Tier A transcript: Crush stores sessions per-project in .crush/crush.db (SQLite). The adapter runs crush session show <id> --json to source the session as {messages[]} JSON and parses it into neutral Turns.
  • Resumes — dir-scoped: crush --continue continues the most recent session in the working directory (the DB is project-scoped, so no global filter needed). Exact-id resume (--session <id>) activates once the id is discovered-then-pinned.
  • Initial prompt seeding: warden launches the bare crush TUI, waits for the ready footer, then auto-types the task prompt via PromptSeeder (retried with backoff; see Typed-prompt seeding for seed_status / recovery). Headless crush run "<prompt>" remains the non-interactive path.
  • Context injection (CRUSH.md): warden delivers its collab/git/pipeline hints via the CRUSH.md context file Crush reads on startup (InjectContext); SystemPromptInject Caps stays false (it tracks a launch flag specifically).
  • No TUI approval parsing yet — warden infers idle from staleness for Crush agents (crush run and --yolo raise no prompts).
  • Full gap doc: docs/agent-backends/crush.md
  • BYO provider: goose session has no --model or --provider flag — Goose resolves these from GOOSE_PROVIDER/GOOSE_MODEL env vars (or ~/.config/goose/config.yaml). Set those before spawning.
  • Tier A transcript: Goose stores sessions in a global SQLite DB. The adapter sources the transcript via goose session export --format json — one command emitting {conversation:[...]} JSON — and parses it into neutral Turns.
  • Resumes — name-deterministic: warden pins its own agent id as the Goose --name at launch (goose session --name <warden-id>), so goose session -r --name <id> resumes the exact session — strictly more reliable than dir-scoped guessing.
  • No model flag on session launch. The headless goose run path does accept --model/--provider.
  • Initial prompt seeding: post-launch via PromptSeeder (see Typed-prompt seeding).
  • Context injection (.goosehints): warden delivers its collab/git/pipeline hints via the .goosehints file Goose auto-loads on startup (InjectContext); SystemPromptInject Caps stays false (it tracks a launch flag specifically).
  • No TUI approval parsing yet — warden infers idle from staleness for Goose agents (goose run in auto mode raises no prompts).
  • Full gap doc: docs/agent-backends/goose.md
  • Hosted, not $0-local: the cursor-agent CLI runs against your Cursor subscription (log in once with cursor-agent login). There is no free local rig, and warden never surfaces dollars for it (billing is your Cursor plan); spend shows tokens, savings omits the agent.
  • Rich permission modes: Cursor exposes a finer approval surface than a prompt/auto toggle, and warden surfaces it honestly — PermissionModes = default | plan | ask | auto-review | force. The Claude-flavored “just do it” aliases fold onto -f/--yolo.
  • Tier C — no structured transcript yet: an interactive Cursor session persists to an undocumented binary SQLite store.db with no export verb, so wd agent digest shows “no transcript” for Cursor agents rather than guessing. The headless stream-json parser is implemented and tested but not wired (the TUI writes no on-disk NDJSON); the day warden gains a store.db reader it flips to Tier A.
  • Resumes — dir-scoped: cursor-agent --continue continues the workspace’s latest session (verified), so rotate/handoff work; exact-id resume lands with discover-then-pin.
  • Initial prompt seeding: post-launch via PromptSeeder (see Typed-prompt seeding).
  • Live state + approval/trust detection: warden classifies the Cursor TUI pane (working / idle / needs-input) and normalizes both its command-allowlist menu and its one-time workspace-trust prompt into the approvals inbox.
  • Context injection (AGENTS.md): cursor-agent has no --append-system-prompt flag, so warden delivers its pipeline/collab/git hints via the AGENTS.md rules file cursor-agent reads on startup (InjectContext); SystemPromptInject Caps stays false (it tracks a launch flag specifically).
  • Double-worktree hazard: cursor-agent ships its own -w/--worktree; warden never passes it (warden already owns the worktree) and launches in warden’s worktree dir directly.
  • Full gap doc: docs/agent-backends/cursor.md
  • Hosted free tier, multi-vendor models: the agy CLI (backend id antigravity) runs on Google’s free tier (a daily-ish quota cap), and one agent can run Gemini, Claude, and GPT-OSS models under a single login (agy models). Tokens show in agy’s /usage TUI only — warden surfaces no dollars; spend shows tokens, savings omits the agent.
  • Tier A transcript: the durable conversation store is encrypted/proto, but agy also writes a plaintext JSONL trajectory log that warden parses into neutral Turns — including tool calls and files changed (tool_calls on planner records, verified against a captured file-edit fixture) — so digests run on real structured data.
  • Resumes — dir-scoped: agy -c continues the most recent conversation for the workspace (verified); exact-id resume lands with discover-then-pin.
  • Initial prompt: seeded via -i/--prompt-interactive, then stays interactive (a persistent loop, like Claude).
  • Live state + approval/trust detection: warden classifies agy’s TUI status bar (idle / working / needs-input) and maps its command-permission menu (Run this command?; Do you want to proceed? before v1.2) and its launch-time workspace-trust prompt into the approvals inbox (shell-command + trust shapes verified; other prompt variants degrade safely until captured).
  • Context injection (AGENTS.md): agy has no --append-system-prompt flag, so warden delivers its pipeline/collab/git hints via the AGENTS.md rules file agy reads on startup (InjectContext); SystemPromptInject Caps stays false (it tracks a launch flag specifically).
  • Full gap doc: docs/agent-backends/antigravity.md