CORALCORAL
Guides

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.yaml and 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.yaml

The location honors $XDG_CONFIG_HOME (so it follows your XDG setup) and can be overridden wholesale with $CORAL_AGENTS_CONFIG (mainly for testing).

~/.config/coral/agents.yaml
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: high

Creating 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]: 1

For 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=high

Each 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 name

coral agents doctor runs five checks per binding:

  • the binding resolves to a valid agent spec,
  • the CLI is found on PATH (or at the configured command path),
  • the CLI's --version works,
  • 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:

task.yaml
agents:
  binding: claude-opus
  count: 4

Mixed team — one binding per assignment:

task.yaml
agents:
  assignments:
    - binding: researcher
      count: 1
    - binding: implementer
      count: 3

Precedence

When a binding is expanded, fields resolve in this order:

  1. Explicit task fields win. Anything you write in task.yaml overrides the binding.
  2. Binding fields win over runtime defaults.
  3. 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:

task.yaml
agents:
  binding: claude-opus
  model: sonnet   # wins over the binding's opus

Role 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.