Documentation

Subagent Lifecycle

The internal lifecycle of subagents — spawn, interact, wait, resume, and close.

Edit on GitHub

This page documents the internal lifecycle of subagents — from spawn to completion — including the manager, status tracking, tool fencing, and context propagation.

Spawn Flow

When spawn_agent is called, here’s what happens internally:

  1. Validate input — check that the role and message are valid
  2. Resolve role — look up in user roles → built-in roles → fallback
  3. Build config — create AgentConfig with role overrides:
    • System prompt override
    • Max steps limit
    • Optional model override
  4. Apply runtime override — layer session-scoped /subagent-config model overrides
  5. Inject briefing — append context briefing from shared parent state
  6. Inject notepad — append latest notepad wisdom (if available)
  7. Compute tool filter — apply allowed_tools / disallowed_tools
  8. Spawn via manager — hand off to AgentManager::spawn_agent

Agent Manager

The AgentManager handles the actual agent lifecycle:

  1. Generate ID — unique UUID for the agent
  2. Assign nickname — from a name pool (for human-readable identification)
  3. Enforce limits — check max_threads and max_depth
  4. Create thread — dedicated thread with status channels
  5. Start task — begin execution and forward events
  6. Track handle — maintain reference for interaction and cleanup

Status Progression

Every subagent follows this status lifecycle:

PendingInit → Running → Completed / Errored / Shutdown
StatusMeaning
PendingInitCreated but not yet running
RunningActively executing
Completed(result)Finished successfully (with optional result text)
Errored(message)Failed with an error
ShutdownManually closed
NotFoundAgent ID doesn’t exist

Parent-Facing Events

The parent agent receives events about subagent activity:

EventWhen
SubAgentSpawnedA new subagent is created
SubAgentStatusChangedStatus transitions
SubAgentCompletedAgent reaches terminal state

Interaction Tools

send_input

Send follow-up instructions to a running agent:

send_input(agent_id: "abc-123", message: "Also add error handling")

Used for iterative refinement without spawning a new agent.

wait

Block until one or more agents reach a terminal state:

wait(agent_ids: ["abc-123", "def-456"])
wait()  # wait for all

Prevents busy-polling loops. Supports optional timeout.

close_agent

Shut down an agent and free its slot:

close_agent(agent_id: "abc-123")

The agent’s status moves to Shutdown.

resume_agent

Reopen a completed or errored agent for more work:

resume_agent(agent_id: "abc-123", message: "Continue with the test file")

The agent’s full context is preserved.

Tool Visibility

Each subagent has a scoped tool set determined at spawn time:

  • allowed_tools from the role → whitelist
  • disallowed_tools from the role → subtracted from whitelist
  • At runtime, ToolRegistry::execute enforces the gate

A subagent can only use tools in its allowed set. This prevents, for example, an explorer role from writing files.

Context Propagation

When a subagent is spawned, its ToolContext inherits from the parent with modifications:

FieldValue
depthParent’s depth + 1
team_nameInherited
agent_nameSet from role
is_team_leadfalse (unless spawned as team lead)
todo_storeShared handle
indexShared handle
sandbox_levelInherited
subagent_model_overridesInherited runtime override map
shared_contextShared handle (for briefing)

Depth Limits

Subagents can spawn subagents, but depth is limited:

[agent.agents]
max_threads = 4   # max concurrent agents
max_depth = 2     # max nesting depth

At depth 0 (main agent), spawning creates depth-1 agents. Depth-1 agents can spawn depth-2 agents. Depth-2 agents cannot spawn further.

Exceeding either limit returns an explicit error.

Registration Note

Subagent lifecycle tools (spawn_agent, send_input, wait, close_agent, resume_agent) are only registered in the TUI runtime. They are not available in nyz run or nyz exec.

This means automated CLI scripts cannot directly use subagents — use teams or sequential nyz exec calls instead.

Next Steps