Something went wrong. Try again.
wip: experimental harness for learning about agent orchestration
Something went wrong. Try again.
agent-orchestrator agents.example.toml
6.1 kB · 109 lines
TOML
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110# agent-orchestrator — example project config.## One file declares: which agents exist, their backend, model, prompt, kind, and how the# watchdog supervises them. Read by agents.py (driver + watchdog). Runtime state (phase index,# resume ids, limit windows) lives under <log_dir>/state/, NOT here.## This example is self-contained: its agents use a dependency-free `demo` backend (a shell that# just idles), so the whole project can be brought up and torn down with no external agent CLI —# see ./smoke.sh. The `claude` and `opencode` backends below are the real ones; point an agent at# them with `backend = "claude"` for a live run.
# ─────────────────────────── global watchdog cadence ───────────────────────────[watchdog]signal_interval = 30 # s between handoff / stall / limit checks (light)heavy_interval = 300 # s between heal / phase-advance checkslimit_probe_fallback = 300 # flat probe cadence when a reset time can't be parsedlimit_reset_slack = 45 # s past a parsed reset before probingstall_grace = 180 # s of slack past a WAITING-UNTIL marker before a stall reboot
# ─────────────────────────── defaults inherited by every agent ───────────────────────────[defaults]session_prefix = "ao-example-" # REQUIRED — tmux namespace for this project (no implicit default)log_dir = ".ao-state" # REQUIRED — logs + state/, resolved relative to this filebackend = "demo"model = ""watch = "none" # none | heal | heal+stall
# ─────────────────────────── backends (declared as data) ───────────────────────────# A backend is fully described by config. `prompt_delivery` selects how the kickoff reaches the# agent: "arg" (CLI argument, claude-style), "ping" (typed in after a TUI connects, opencode),# or "exec" (a plain command; {kickoff}=prompt file, {session}=session name, {model}=model).
[backend.demo] # dependency-free backend used by this example's smoke runbin = "echo '[demo] {session} up (kickoff: {kickoff})'; exec sleep 1000000"prompt_delivery = "exec"
[backend.claude] # the real claude backendbin = "claude"flags = "--dangerously-skip-permissions"remote_control = truesupports_resume = trueprompt_delivery = "arg"process_name = "claude" # used for backend-mismatch healingsubmit_key = "Enter"stall_idle = 300active_re = "esc to interrupt|Running tool|⠇|⠙|· \\d+"limit_re = "spend limit|usage limit|limit reached|reached your .*limit|out of (credits|tokens)"fatal_re = "redacted_thinking|blocks cannot be modified|cannot be modified"
[backend.opencode] # the real opencode backend (a TUI; prompt typed after connect)bin = "opencode"attach = "{bin} attach {server} --dir {dir}"server = "http://127.0.0.1:4096"supports_resume = falseprompt_delivery = "ping"process_name = "opencode"footer_ui = true # static footer lingers after a turn → only the bottom = activitylog_grace = 180connect_delay = 12submit_key = "C-m"model_env = true # pass the model via OPENCODE_CONFIG_CONTENTstall_idle = 900active_re = "esc interrupt|thinking|inferring|running tool|tool call|preparing patch|reading|searching"limit_re = "spend limit|usage limit|limit reached|reached your .*limit|out of (credits|tokens)"fatal_re = "redacted_thinking|blocks cannot be modified|cannot be modified"
# ─────────────────────────── agents ───────────────────────────# A minimal 2-agent loop pair: a Builder that does the work and an Adversary that verifies it.
[[agent]]name = "builder" # tmux session: ao-example-builderkind = "loop" # kickoff = kickoff_template + prompts/builder.md, per phaserole = "builder"
[[agent]]name = "adversary" # tmux session: ao-example-adversarykind = "loop"role = "adversary"
# A persistent supervisor and a one-shot task are also supported:# [[agent]]# name = "orchestrator"# kind = "persistent"# backend = "claude"# resume = true# watch = "heal"# wake = { interval = 3600, prompt_file = "prompts/supervise.md" }# prompt = "You supervise this project. On startup, check status and report."
# Non-AI helper services (started by a bare `up`, not AI sessions):# [[service]]# name = "cleanlogs"# command = "python3 agent-log.py follow-all"
# ─────────────────────────── the phase machine (kind="loop" agents) ───────────────────────────[loop]state_file = "phase-idx" # under <log_dir>/state/resume_phase = true # keep the current index across restarts (don't reset to 0)auto_advance = true # advance when the current phase's status file says the done_markerdone_marker = "## DONE"kickoff_template = "prompts/kickoff.md" # project preamble; slots {phase_id}/{plan}/{status}/{role}roles_dir = "prompts" # role prompt = <roles_dir>/<role>.mdhandoff = { repo = ".", claim_pings = "adversary", review_pings = "builder", inboxes = ["ADVERSARY-INBOX.md", "BUILDER-INBOX.md"], claim_pattern = "^claim", review_pattern = "^review", state_subdir = "machine-docs" }# on_complete = { trigger_file = ".run-on-complete", run = "reporter" }
phases = [ { id = "demo1", plan = "examples/PLAN-demo1.md", status = "STATUS-demo1.md" }, { id = "demo2", plan = "examples/PLAN-demo2.md", status = "STATUS-demo2.md", models = { builder = "claude-opus-4-8" } },]