Agent Bindings
Register local agent runtimes once with `coral setup agent`, then reference them by name from any task.
A binding is a machine-local preset that bundles an agent runtime, its CLI
command, a default model, runtime options, and an optional role seed under a
short name. Tasks reference a binding by name instead of repeating those
details in every task.yaml.
This separates two concerns that the plain agents.runtime / agents.model
fields conflate:
- Task / research topology — how many agents, which roles, which islands.
This belongs in
task.yamland should stay portable across machines. - Local machine setup — which CLIs are installed, where they live, which model defaults and runtime-specific options this user wants. This belongs in a user-level file and never has to be committed.
Bindings are an additive shorthand. They are not a replacement for
agents.runtime, agents.model, or agents.assignments — they expand into
exactly those fields at config-load time, and the manager only ever consumes
resolved agent specs. Existing task configs keep working unchanged.
Bindings never store API keys, OAuth tokens, or provider credentials. Runtime
authentication is owned by the runtime-native login flows (claude, codex,
cursor-agent login, kiro-cli setup). coral setup agent only records
runtime / command / model metadata.
Where bindings live
~/.config/coral/agents.yamlThe location honors $XDG_CONFIG_HOME (so it follows your XDG setup) and can
be overridden wholesale with $CORAL_AGENTS_CONFIG (mainly for testing).
default: claude-opus
agents:
claude-opus:
runtime: claude_code
command: claude
model: opus
role_file: ~/.config/coral/roles/generalist.md
codex-high:
runtime: codex
command: codex
model: gpt-5.4
runtime_options:
model_reasoning_effort: highCreating a binding
The fastest path is coral setup with no subcommand — it scans PATH for
every supported runtime CLI (claude, codex, cursor-agent, opencode,
kiro-cli, pi) and offers an interactive numbered-selection wizard:
coral setup
# Scanning PATH for agent runtimes (~/.config/coral/agents.yaml):
#
# ✓ claude_code claude /opt/homebrew/bin/claude
# codex codex not found
# ✓ cursor_agent cursor-agent /Users/me/.local/bin/cursor-agent
#
# 2 detected runtime(s):
#
# [1] claude_code (claude, model sonnet)
# [2] cursor_agent (cursor-agent, model auto)
#
# Select runtimes to bind [1-2, comma/space-separated, 'all', or Enter to skip]: 1For each pick the wizard asks for binding name, model, and an optional
role seed file (press Enter to skip; an unreadable path is accepted but
flagged by coral agents doctor). After the binding is created you'll be
asked "Add another binding for X? [y/N]" — say yes to create a second
binding for the same runtime, e.g. claude-opus and claude-sonnet both
pointing at claude_code with different models. Already-bound runtimes stay
in the list (annotated with [N existing binding]) so you can extend them
whenever you like.
Pass --non-interactive to just print the detection report (good for CI or
piping to a file). For more control over a single binding — e.g. setting a
custom command path, model, or role file — use coral setup agent:
coral setup agent…or set everything via flags (also used for scripting and CI):
coral setup agent --name claude-opus --runtime claude_code --model opus
coral setup agent --name codex-high --runtime codex \
--option model_reasoning_effort=highEach coral setup agent finishes with a lightweight doctor pass (see below).
The first binding you create becomes the default; pass --default to change it.
Inspecting and managing bindings
coral agents list # numbered list (default is marked)
coral agents show claude-opus # one binding's resolved fields
coral agents doctor # validate every binding
coral agents doctor claude-opus # validate one
coral agents remove # interactive numbered-selection wizard
coral agents remove claude-opus codex-high # delete one or more by namecoral agents doctor runs five checks per binding:
- the binding resolves to a valid agent spec,
- the CLI is found on
PATH(or at the configuredcommandpath), - the CLI's
--versionworks, - the role file exists, if one is set, and
- (by default) a live hello-ping — spawns the runtime CLI with a one-word prompt and waits for a reply.
The live ping catches the failure modes the cheap checks miss: expired auth,
broken provider credentials, network issues, model name typos. It costs one
LLM round-trip per binding, so pass --no-live to skip it in CI or quick
sanity checks, and --timeout SECS to adjust the per-ping wait (default
30s). Authentication is still owned by the runtime-native login flow — when
the ping fails, doctor points you at it rather than asking for credentials.
Using a binding in a task
Single runtime:
agents:
binding: claude-opus
count: 4Mixed team — one binding per assignment:
agents:
assignments:
- binding: researcher
count: 1
- binding: implementer
count: 3Precedence
When a binding is expanded, fields resolve in this order:
- Explicit task fields win. Anything you write in
task.yamloverrides the binding. - Binding fields win over runtime defaults.
- Runtime defaults fill any remaining gaps (e.g. the default model for a runtime).
So this uses the claude-opus binding's runtime and options but overrides the
model:
agents:
binding: claude-opus
model: sonnet # wins over the binding's opusRole seeds
A binding may carry an initial role_file. At expansion time it compiles into
runtime_options.role_file, which CORAL copies into the run at agent setup.
This is idempotent on resume: an agent's evolved role file is never
overwritten, so a binding's role seed only ever applies to a fresh agent. A
task that sets its own runtime_options.role_file keeps it — the binding seed
is only used as a fallback.
How it fits together
agents.binding (and assignments[].binding) is resolved during config
normalization, before agent specs are built. After expansion the binding key
is gone — the run's stored config.yaml contains only concrete
runtime / model / runtime_options fields, so resumes and the dashboard never
depend on your local bindings file.
