Tools · Coordination¶
Planning, user notifications, multi-agent orchestration, and peer messaging. See Tool Reference for how to read these signatures.
PlanningTools¶
Group planning. All four tier Standard. The three plan tools return JSON the orchestrator intercepts; todo returns text.
create_plan¶
create_plan(goal, steps) → JSON
| Param | Type | Req | Description |
|---|---|---|---|
goal |
string | yes | High-level goal of the plan |
steps |
JSON array | yes | Each step: { description, assigned_agent_id, depends_on:[step_numbers], required_tools:[…], required_outputs:[…], priority:"high\|medium\|low" } (array or stringified array accepted). required_outputs is a list of workspace-relative paths the step owes; when present the required-output gate enforces exactly that list. An entry ending in /, /* or /** is a directory obligation, satisfied by any file beneath it; an entry may carry a " >= N" suffix ("menu/images/ >= 10"), satisfied only by N files of distinct content — counted by content hash, so ten copies of one file is one |
Returns { plan_id, goal, step_count, status, message }; a bare Error… string on invalid/empty steps. The team's plan contract is applied to the steps before the plan is stored and message says what it changed: with plan_serial_steps every step is made to depend on the previous one, and with enforce_node_required_output a step assigned to a node that declares required_output carries that list — filled when the planner declared none, replacing the planner's paths when they differ. Where the node's list replaces the planner's paths, a declared glob of one format (images/*.png >= 1) owes at least as many files as the planner named in that format, so the gate does not ask for fewer pictures than the step describes; a count is never lowered. Two more rules apply to every plan. A page step owing shows DIR/ is moved to run just after the last step that owes files under DIR/, with depends_on renumbered and any dependency the move would turn forward dropped; a template's declared plan_steps keep the author's order. A required output in a format no workspace tool makes (.fbx, .blend, .glb, .umap, .uasset, .usdz, .3ds, .max, .ma, .mb, .c4d, .unitypackage) becomes a markdown brief at the same path (blender_exports/ship.fbx → blender_exports/ship.md), and the step's text is rewritten to that path with a sentence saying no tool here makes the format and asking for a written brief of the asset rather than a stand-in. Each change is reported in message. A create_plan call whose arguments cannot be parsed is retried with the parse error and its path in the next prompt, and the attempt's log row keeps the arguments. Inside a re-plan turn (the supervisor's response to a failed step) create_plan is refused and the supervisor is pointed at update_plan: a re-plan amends the running plan, because a new plan would supersede it and strand the steps already in flight.
The depends_on graph is validated before the plan is stored, and repaired rather than rejected — a bad edge is usually a miscount, and rejecting would spend the whole planning turn for nothing. Three kinds of edge cannot be kept and are dropped with a logged reason: a reference to a step number not in the plan, a self-reference, and an edge that would close a cycle. This matters because every scheduler entry point selects steps whose dependencies have all completed or been skipped, so an unsatisfiable dependency makes its step permanently unreachable — and silently, since a step that is never selected never runs and never fails.
Missing edges are then inferred, which validation cannot do for you: when a step's description names a file that an earlier step's description also names, the later step is made to depend on the earlier one. The inference is deliberately narrow — it matches path-shaped tokens only (a bare word like "brief" is not a path), and edges only ever point backwards, so it cannot create a cycle. Each inferred edge is logged. It will miss a dependency that is never expressed as a file name, which is the intended trade: a wrong edge stalls a plan, a missing one is only the status quo.
One set of edges is imposed rather than inferred: a step that owes the finishing delivery gate (finishing/delivery-gate.md with a verdict) is made to wait for every step that does not already wait for it, so the gate reads the pages after the last edit. Each added edge is logged as [PLAN-GRAPH].
A second inference looks at what a step reads rather than at what two descriptions share: when a step's description, or the system prompt of the node it is assigned to, mentions a file an earlier step owes — that step's required_outputs, else its node's required_output, matched by the path, a glob's folder or a distinctive file name — the later step is made to depend on it. A card writer whose prompt names the menu and the photographs therefore waits for the steps that make them, even when the planner declared no dependency and its step description names no path. It also points only backwards, keeps every declared edge, and is logged as [PLAN-READS] rather than reported in message; steps the planner would have run in parallel can end up serialised.
Step descriptions are also checked against what the assigned agent can actually make. A description that promises a binary file at a workspace path — an image, a PDF, an office document, audio, video, an archive — gets one CONSTRAINT: sentence appended when the agent that step is assigned to has no tool producing that kind of file, telling it to deliver the same content as text or markdown that references the real asset paths. Two details decide the check: it asks the assigned agent's tools, not the team's, so a copywriter is still constrained on a team that has an image generator; and a description that says to assemble, composite, combine, merge, lay out, overlay or compose is constrained unless the agent holds a tool that can compose that kind of file, because generating a picture from a prompt is not the same as laying copy over one and no shipped tool does the latter for images. run_shell is that tool for video, audio and archives, since the media base image carries ffmpeg: an editor with a shell told to "cut and assemble the film" keeps its step, while comfyui_generate and tts_render, which make one asset from a prompt, never excuse an assembly. The planner's own wording survives — only the sentence is added — and the corrected text is both what the worker is instructed with and what the step verifier judges. A step that merely mentions a file name without a path is left alone, so documenting a tool that names an audio file is not read as a promise to produce audio. The sentence names the replacement path rather than leaving the agent to pick one: told only to "deliver a text or markdown file", a small model invented its own filename in eight of ten samples and the step verifier, which reads the step description, was left looking for the path the plan named.
A second guard runs on the same principle during execution. When a step fails the evidence gate its files are moved out of the workspace, and any later step whose description names one of them has a CONSTRAINT: sentence appended saying the file was withdrawn and must not be read, listed, verified, linked, counted or replaced with a placeholder. Without it the plan wins: an agent told to "verify every file exists" reported a pass for files that were no longer on disk.
read_plan¶
read_plan(mode?) → JSON
| Param | Type | Req | Default | Description |
|---|---|---|---|---|
mode |
string | no | all |
all (full plan) or next (next pending step only) |
Returns one of: { message } (no active plan); { plan_id, goal, next_step } for mode="next" (next_step null or { step_id, step_number, description, assigned_agent_id, status, priority, depends_on[], required_tools[] }); or { plan_id, goal, status, step_count, steps[] } for mode="all", each step adding result.
update_plan¶
update_plan(status, step_id? | step_number?, result?) → JSON
| Param | Type | Req | Description |
|---|---|---|---|
status |
string | yes | pending, in_progress, completed, failed, or skipped |
step_number |
int | one of | The 1-based number read_plan shows and depends_on uses, resolved on the project's active plan. |
step_id |
int | one of | Durable DB key from read_plan/create_plan. A legacy step_index alias is accepted when step_id is absent. A step_id that is not a step id but matches a step number on the active plan is taken as that step, and the response's note says so. |
result |
string | no | Result or reason (for completed/failed steps). When a failed step is set back to pending, what its re-run must fix: kept after the failure as a re-plan note, and given to the re-run with the reason its last attempt failed |
Returns { step_id, step_number, new_status, message, note? }, or Error: no step number N on the active plan… / Error: step_id N not found….
Transitions are checked rather than applied blindly. failed → completed is refused: a failed step is only complete once something has actually re-run it, so the refusal points at the real route — set it in_progress, do the work, then mark it completed. failed → skipped is allowed but warned about; it is how a plan legitimately abandons a step it cannot finish and unblocks the steps waiting on it. Inside a re-plan turn in_progress and completed are refused outright, since only the step loop sets them: the turn sets the failed step back to pending or marks it skipped, and a pending that carries a result ends the turn.
todo¶
todo(action, items?, number?) → the list as text
| Param | Type | Req | Description |
|---|---|---|---|
action |
string | yes | write, check, or read |
items |
JSON | for write |
Array of strings, or of { "text": …, "done": true\|false }; the router accepts a real array or a stringified one |
number |
int | for check |
1-based item to mark done |
An agent's own scratch list of steps for the run, separate from the team plan: write replaces the list, check marks one item done, read shows it. It never reads or changes the plan. The store (TodoStore) is process-local, keyed by run and agent, capped at 50 items of 300 characters, renumbered 1..n on every write, and released when the run ends.
CommunicationTools¶
Group comms. Both tier Standard. Both publish through the real-time layer so status reaches the Studio mid-run.
notify_user¶
notify_user(message, severity?) → confirmation
| Param | Type | Req | Default | Description |
|---|---|---|---|---|
message |
string | yes | — | The notification message |
severity |
string | no | info |
info, warning, success, or error (anything else coerces to info) |
Broadcasts over NotificationHub (ReceiveNotification). Returns "Notification sent: [<level>] <message>".
report_progress¶
report_progress(status, message, percent_complete?) → confirmation
| Param | Type | Req | Default | Description |
|---|---|---|---|---|
status |
string | yes | — | in_progress, blocked, needs_review (code also accepts completed, failed) |
message |
string | yes | — | Progress update message |
percent_complete |
int | no | — (clamped 0–100) | Estimated completion percentage |
Broadcasts over ProjectRunHub (AgentProgress); milestone statuses (completed/failed/blocked) also fire a NotificationHub notification. Returns "Progress reported: <status> (<pct>%) — <message>".
OrchestrationTools¶
Group orchestration. delegate and reply are tier Standard; spawn_subagents is Elevated.
Markers vs. executors
All three return JSON marker strings ({ action: … }) the orchestrator intercepts — they do not perform the action in-method.
delegate¶
delegate(target, instruction) → JSON marker
| Param | Type | Req | Description |
|---|---|---|---|
target |
string | yes | Target worker agent ID (must be a team member) |
instruction |
string | yes | Detailed task instructions for the worker |
Returns { action: "delegate", target, instruction }.
reply¶
reply(content) → JSON marker
| Param | Type | Req | Description |
|---|---|---|---|
content |
string | yes | The final response content to send to the user |
Returns { action: "reply", content }.
spawn_subagents¶
spawn_subagents(subagents) → JSON marker · Elevated
| Param | Type | Req | Description |
|---|---|---|---|
subagents |
JSON array | yes | Each: { task, output_file }, optional search_hints (a list of strings, or a single string, which becomes a one-element list). Array or stringified array accepted; max 10. |
Returns { action: "spawn_subagents", subagents:[…] }, or a bare Error… on validation failure. Elevated because it multiplies LLM load — the engine behind the dynamic-parallel topology (Schema). A lead that never calls it within its planning rounds fails the run rather than finishing with its last message as the result; a swarm that outlives the session budget is stopped — each unfinished subagent ends at its next round, its queued model calls are dropped, and the lead synthesises what was written. A thread cut short that way is recorded as skipped rather than failed, and the spawn step counts as succeeding as long as at least one thread landed a file, so a truncated swarm whose lead still wrote the synthesis reports the run honestly instead of failing it.
TeamMessagingTools¶
Group team. Tier Standard. One tool.
send_message_to¶
send_message_to(agent_id, message) → confirmation
| Param | Type | Req | Description |
|---|---|---|---|
agent_id |
string | yes | Recipient agent ID (must match a teammate's ID — not display name) |
message |
string | yes | Message body; include enough context to act without seeing your history |
Delivery is asynchronous — the message lands in the target's mailbox and is read on its next turn. Returns "Message delivered to <agent_id> (they will see it on their next turn).", or an Error… if there is no project context or the agent ID is not a team member.
Not in the default schema set
The team group is excluded from the default /api/tools/mcp-schemas server list — send_message_to only appears when team is requested explicitly (API · ToolsController).
Where to go next¶
- API · Orchestration & Runs — the run loop that interprets the markers.
- Schema · SubagentDefaults — shaping
spawn_subagentschildren. - §04 — Core Concepts — Plans, topologies, and delegation.