Supervisor
Run subagents and child sessions, manage their worktrees, and follow them in the fleet.
The Supervisor coordinates work across child sessions. Each child runs in its own session id with its own mode, model, task panel and memory, and reports its result back to the session that started it. Two tools work together: the Supervisor tool runs subagents, and the Session tool creates and manages full child sessions.
The session tool is part of the standard tool set. The Orchestrator agent is built around it; turn that agent on with the orchestrator switch (HORDECODE_EXPERIMENTAL_ORCHESTRATOR) in the global config; see Switches.
Supervisor tool
The supervisor tool runs subagents and tracks them as persistent rows. It has these actions:
| Action | What it does |
|---|---|
run | Spawn a subagent and block until it finishes. The result is returned inline. |
spawn | Spawn a subagent in the background and return its supervisor_id immediately. |
status | Report a subagent’s liveness without blocking. |
wait | Block until a subagent finishes. The default timeout is 10 minutes. |
cancel | Stop a running subagent. |
send | Deliver a message to another supervisor’s inbox. |
models | List the configured models that run and spawn accept. |
run and spawn take a subagent_type, a description, and a prompt. Optional fields:
model- A model group or tier name, or a literalprovider/model.context-nonefor a clean context, orstateto inject checkpoint summaries as background.task_id- Bind the subagent’s findings to a task, so its verbatim progress is captured totasks/<TID>/progress.md.supervisor_id- Resume an existing supervisor instead of starting a new one.
Resuming a supervisor
Pass supervisor_id with the same subagent_type to run or spawn to run a new turn in an existing supervisor’s session. The supervisor keeps its prior messages, its id does not change, and it returns to its original working directory. A resume is rejected when the id is unknown, the supervisor is still running, the target is a peer (use send for those), or the subagent_type differs from the original.
Inbox
send is fire-and-forget and returns in a few milliseconds. The receiver sees the message wrapped as <inbox from="..." sent_at="...">...</inbox> at the start of its next iteration.
Inbox rows are durable. They are collected after 7 days, and up to 100 messages are drained into a turn. When a supervisor finishes, fails, stalls or is cancelled, the waiting session receives a <supervisor-notification> with the status, summary and result, the error, the stall duration, or the cancellation.
Limits
Two limits bound supervised work:
| Setting | Default | Accepted values | What it controls |
|---|---|---|---|
subagent_depth | 3 | 0 to 8 | How many subagent links can nest. 1 lets a primary agent launch subagents while keeping those subagents from launching more. 0 keeps any agent from launching a subagent. |
supervisor.max_subagents | 8 | 1 to 16 | How many subagents run at once in a session tree. |
The depth count follows subagent links, so peer sessions do not add to it. The breadth limit is shared by every session under one root session, and it is process-wide. A slot is taken synchronously before a spawn, so parallel spawns in one message cannot all pass the check, and the slot is released when the outcome resolves. Child sessions created by the session tool count toward the same limit. Workflow and checkpoint spawns do not go through this limit and are not counted.
When a limit is reached, the tool returns one of these messages:
Subagent depth limit reached (3). Increase "subagent_depth" to allow deeper nesting.8 subagents are already running in this session (limit 8). Wait for one to finish, or raise "supervisor.max_subagents" (up to 16).At the maximum the wording changes to Subagent depth limit reached (8), the deepest level the supervisor can manage. and 16 subagents are already running in this session, the most the supervisor runs at once. Wait for one to finish.
Sessions
The session tool creates and manages full child sessions. Its calls wrap the action in an operation object.
Create
session create starts a child in the background and returns its session id. It accepts:
task- The child’s first-turn prompt. Required.mode-build,planorharness. Defaults tobuild.model- A model group or tier name, or a literalprovider/model.title- A display title for the session.dir- The working directory. Defaults to the orchestrator’s directory.topic- A stable topic label. When a child with that topic already exists, the task is relayed into it instead of starting another session.isolate- Give the child its own git worktree ofdir. A non-git directory falls back to the shared checkout.
Depth and breadth are checked before any worktree is created, so a refused create leaves no directory behind.
{ "operation": { "action": "create", "task": "Design the billing schema", "mode": "harness", "model": "standard", "title": "Billing" } }Manage
| Action | What it does |
|---|---|
send | Relay a task into an existing child. It enqueues the task in the child’s inbox and wakes it, even when the child is idle or has never run. This is also how a finished child is resumed. |
switch | Move the user’s frontend panel to a session. |
list | List this orchestrator’s child sessions, one line each. |
status | Report progressing, stalled or terminal, plus the raw status and turn count. Judge a stall from this, not from the raw running label. |
join | Fan in over several children. Blocks until all of them reach a terminal state, then returns one per-child summary. Takes an optional timeout in milliseconds. |
ask | Ask a child a one-shot, read-only question over a frozen snapshot of its history, without disturbing its running turn. The answering fork can read, grep and glob only. |
setmode | Change a child’s agent mode for its subsequent turns. Takes effect on the next turn; relay a message to continue under the new mode. |
approve | Approve a child’s current pending permission request. |
grant-approval | Pre-authorize future permission asks for one child, or for every child with all. |
dashboard | Render the fleet table. |
cancel | Stop a running child. See Worktrees. |
merge | Merge an isolated child’s branch. See Worktrees. |
Worktrees
session create with isolate gives the child its own git worktree. The runtime names the branch opencode/<name>; workflow worktrees use hordecode/<name>. A fresh worktree is populated before the create call returns, and only start scripts run in the background. The child is told to commit its work on its branch.
Cancel
session cancel <id> stops the child. An isolated child’s worktree is removed only when it holds no uncommitted changes and its branch is already merged into the main checkout’s HEAD. Otherwise the worktree and branch are kept, and the output says why:
Kept its worktree <dir> (branch <branch>) because <reason>. Integrate it with `session merge <id>` or discard it with `session cancel <id> --force`.session cancel <id> --force discards the worktree and branch even when they hold uncommitted or unmerged work. The output reports removed, kept and failed worktrees truthfully.
Merge
session merge <id> [--into <branch>] merges an isolated child’s branch into a target branch. The target defaults to the branch checked out in the main checkout; on a detached HEAD, pass --into.
Before changing anything, the tool checks that the project is a git repository, that the target and child branches exist, that the target is not the child’s own branch, that the target is not checked out in another worktree, and that the main checkout has no dirty tracked files. It also detects conflicts. It asks for permission before running git merge, then merges with --no-ff into a checked-out target, or uses a merge tree for a target that is not checked out. A branch that is already merged returns “nothing to do”.
Task stop gate
Before a root session’s main agent stops, hordecode checks its task board. Blocked tasks are ignored. If tasks are still open or in_progress, the gate injects a synthetic user turn that lists them and asks the agent to complete each one with task done, or to abandon it with task abandon when it is genuinely not needed.
The main agent gets three nudges (MAX_TASK_GATE_MAIN_REACT = 3). After the third, hordecode logs task gate hit cap; allowing stop and lets the turn stop. Subagents use a two-nudge variant. The gate is skipped when the task permission is disabled for the agent, or when the last user turn set tools.task to false.
Orphan rows after a restart
Supervisor rows are tied to the process that owns them. When hordecode restarts, rows that were pending or running under a dead process, and legacy rows with no owner, are marked idle or failed with the prefix orphaned: process restarted. A process counts as dead when its last heartbeat is more than five minutes old.
Nothing resumes automatically. The row’s message says how to continue:
- A subagent resumes with the supervisor tool’s
runorspawn, passing itssupervisor_idandsubagent_type. The prompt runs as a new turn in its session. - A peer resumes with the supervisor tool’s
send, addressed to itssupervisor_id. The message runs as its next turn. - A main session continues when you send a new message in it.
Fleet
session dashboard renders the fleet: every session under the orchestrator, grouped by liveness.
- Progressing - advancing.
- Stalled - no recent activity. Liveness is derived with a five-minute stall window.
- Finished / idle
- Failed
- Cancelled
The table columns are SESSION, LIVENESS, AGE, TURNS, MODE, WORKTREE and TITLE. The worktree cell shows branch (+ahead) @ dir, or shared. The summary line reads:
Fleet: N total — R running (P progressing, S stalled), I idle, F failed, C cancelledThe failed and cancelled parts appear when their count is above zero. The dashboard lists peer child sessions.
TUI
The Subagents panel lists the current session’s subagents and their status glyphs, and ages finished rows out after 60 seconds. The Tasks panel shows the task board the stop gate reads, including the spinner that runs while the session or a subagent is busy.
Configure
{ "$schema": "https://hordecode.com/config.json", "subagent_depth": 3, "supervisor": { "max_subagents": 8 }}subagent_depth- Default3, accepted0to8. See Subagent depth.supervisor.max_subagents- Default8, accepted1to16. See Concurrent subagents.workflow.maxConcurrentAgents- The separate process-wide ceiling for workflow runs.