Skip to content

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