Coordination
Wait on agents in different ways, wait for a whole subtree to finish, and tell a running agent when another one finishes.
Beyond the plain waitFor of Fan-out and gather, alineod gives you three
ways to coordinate agents. All of them are declared by you (the operator) or in a spawn request. An agent can't
start them from inside its own sandbox yet.
| Need | Use |
|---|---|
| Start a child on the first result, a quorum, or a deadline | waitFor modes |
| Wait until everything under an agent has finished | Subtree completion |
| Tell a running agent when another one finishes, without blocking it | notifyOn |
waitFor modes
waitFor accepts an object as well as a list of IDs:
{
"parentAgentId": "a_7b2e44d1",
"spec": { "name": "judge", "...": "..." },
"prompt": "Pick the best of the drafts in /inputs.",
"waitFor": {
"agents": ["a_c01d9e3a", "a_5e8f2b71", "a_91aa0c4d"],
"mode": "quorum",
"k": 2,
"deadlineSec": 600
}
}mode | The child starts when | Inputs written |
|---|---|---|
settled (default) | every agent has finished, whatever the outcome | all of them |
all | every agent succeeded | all of them |
any | the first agent finishes | that one |
quorum | k agents succeeded | the first k successes |
A plain list is the same as mode: "settled".
onDepFailure(all,quorum):fail(the default) ends the childfailedwithout forking it when a dependency fails, or when a quorum can no longer be reached.proceedstarts it anyway with what there is.deadlineSec: stop waiting after this long. WithonDeadline: "proceed"(the default) the child starts with whatever has finished, and the wait is markedpartial. With"fail"the child endsfailed. The deadline is stored, so it survives a daemon restart.- Agents still running after the child starts, like the losers of an
anyorquorum, keep running. Stop them yourself if you don't need them, e.g. with a subtree stop.
For any wait other than a plain settled one that fully succeeded, /inputs.json also has a __wait entry, so the
child can tell a partial or race result from a complete one:
{
"a_c01d9e3a": { "path": "/inputs/a_c01d9e3a.txt", "outcome": "success" },
"a_5e8f2b71": { "path": "/inputs/a_5e8f2b71.txt", "outcome": "success" },
"__wait": { "mode": "quorum", "outcome": "satisfied", "pending": ["a_91aa0c4d"] }
}Each resolved wait emits wait_resolved. While a dependency the wait still needs is paused,
wait_blocked_on_paused is emitted once for it, so a stalled wait shows up instead of hanging silently.
Waiting for a subtree
A subtree is quiescent when every agent in it has finished and none is still being spawned. A paused agent
counts as not finished, and is listed in blockedOnPaused.
curl -s 'localhost:4600/agents/a_7b2e44d1/await?scope=subtree&wait=120'{
"rootAgentId": "a_7b2e44d1",
"quiescent": true,
"asOf": 214,
"members": [
{
"agentId": "a_7b2e44d1",
"state": "done",
"outcome": "success",
"resultRef": "fs://a_7b2e44d1/result.md"
},
{
"agentId": "a_c01d9e3a",
"state": "done",
"outcome": "success",
"resultRef": "fs://a_c01d9e3a/result.md"
}
],
"blockedOnPaused": []
}The request is held for up to wait seconds (maximum 240) and returns as soon as the subtree is quiescent. A
finished agent that you prompt again makes the subtree active again, so quiescence can come and go.
subtree_quiescent is emitted each time a subtree someone is waiting on becomes quiescent.
To wait on a set of agents without spawning anything, use the same modes as waitFor:
curl -s -X POST localhost:4600/runs/r_3f9a1c20/await \
-H 'content-type: application/json' \
-d '{"agents": ["a_c01d9e3a", "a_5e8f2b71"], "mode": "any", "wait": 60}'{
"outcome": "satisfied",
"selected": ["a_5e8f2b71"],
"settled": [{ "agentId": "a_5e8f2b71", "outcome": "success" }],
"pending": ["a_c01d9e3a"]
}If it's still undecided when wait runs out, outcome is pending. {"subtree": "<agentId>", "wait": N} in the
same body waits for that subtree instead.
notifyOn
waitFor holds a child until its dependencies finish. notifyOn doesn't block anything: the agent keeps working
and is told when an agent it watches finishes.
{
"parentAgentId": "a_7b2e44d1",
"spec": { "name": "integrator", "...": "..." },
"prompt": "Start integrating; the reviewers' findings will arrive as they finish.",
"notifyOn": ["a_c01d9e3a", "a_5e8f2b71"]
}Or subscribe an agent that already exists:
curl -s -X POST localhost:4600/agents/a_2d4c9e10/notify-on \
-H 'content-type: application/json' -d '{"agents": ["a_c01d9e3a"], "wake": true}'When a watched agent finishes, however it finishes, the subscriber gets a line like this:
[alineo] Update from agents you're watching:
- a_c01d9e3a (reviewer-auth) finished: success. Result: fs://a_c01d9e3a/result.md — "Found two issues in …"How it arrives depends on what the subscriber is doing:
| Subscriber is | Delivery |
|---|---|
| running | Steered into its current turn, after its running tool call. |
| paused | Held; delivered as soon as it's resumed. |
| idle | Added to the start of its next prompt. With wake: true, or POST /agents/:id/inbox/deliver, it starts a new turn instead. |
| not forked yet | Held; added to its first prompt. |
| stopped or lost | Dropped. |
- Merged: everything pending when a delivery happens goes out as one message.
- Already finished: subscribing to an agent that has already finished notifies at once.
- Finishing again: an agent prompted again that finishes again notifies again, as "finished again".
- Durable: notifications are stored in the ledger, so pending ones survive a daemon restart.
GET /agents/:id/inbox lists pending and delivered notifications. The events are notify_registered,
inbox_queued, inbox_delivered and inbox_dropped.
A notification can't interrupt a running tool call
Delivery into a running turn uses steer, so it lands after the agent's current tool call. If a
long build is running, the notification waits for it. Use waitFor when the agent can't do
anything useful until the result arrives.