Delegation allows the lead to assign work to member agents via the task board. Handoff transfers conversation control between agents without interrupting the user's session.
Delegation works through the team_tasks tool — the lead creates a task with an assignee, and the system auto-dispatches it to the assigned member:
flowchart TD
LEAD["Lead receives user request"] --> CREATE["1. Create task on board<br/>team_tasks(action=create,<br/>assignee=member)"]
CREATE --> DISPATCH["2. System auto-dispatches<br/>to assigned member"]
DISPATCH --> MEMBER["Member agent executes<br/>in isolated session"]
MEMBER --> COMPLETE["3. Task auto-completed<br/>with result"]
COMPLETE --> ANNOUNCE["4. Result announced<br/>back to lead"]
subgraph "Parallel Delegation"
CREATE2["create task → member_A"] --> RUNA["Member A works"]
CREATE3["create task → member_B"] --> RUNB["Member B works"]
RUNA --> COLLECT["Results accumulate"]
RUNB --> COLLECT
COLLECT --> ANNOUNCE2["Single combined<br/>announcement to lead"]
end
Note: The
spawntool is for self-clone subagents only — it does not accept anagentparameter. To delegate to a team member, always useteam_tasks(action="create", assignee=...).
Use the team_tasks tool with action: "create" and a required assignee:
{
"action": "create",
"subject": "Analyze the market trends in the Q1 report",
"description": "Focus on Q1 revenue data and competitor analysis",
"assignee": "analyst_agent"
}The system validates and auto-dispatches:
assigneeis required — every task must be assigned to a team member- Assignee must be a team member — non-members are rejected
- Lead cannot self-assign — prevents dual-session execution loops
- Auto-dispatch: after the lead's turn ends, pending tasks are dispatched to their assigned agents
Guards enforced:
- Max 3 dispatches per task — auto-fails after 3 attempts to prevent infinite loops
- Task dispatched to lead agent is blocked and auto-failed
- Member requests (non-lead) can optionally require leader approval before dispatch
V2 leads: Team V2 leads cannot manually create tasks before a spawn has been issued in the current turn. This prevents premature task creation that would break the structured orchestration flow.
Create multiple tasks in the same turn — they dispatch simultaneously after the turn:
// Lead creates 2 tasks in one turn
{"action": "create", "subject": "Extract facts", "assignee": "analyst1"}
{"action": "create", "subject": "Extract opinions", "assignee": "analyst2"}Results are collected via a producer-consumer announce queue (BatchQueue[T]) that merges staggered completions into a single LLM announcement run. This means the lead receives one combined message rather than separate interruptions per member — reducing token overhead significantly.
Beyond team member delegation, the lead can spawn self-clone subagents using the spawn tool for parallel workloads that don't require a specific team member:
{"action": "spawn", "task": "Summarize the PDF report", "label": "pdf-summarizer"}Key behaviors introduced in the parallel sub-agent enhancement:
The leader delegation prompt is conditional — it only activates when the situation genuinely requires delegation, rather than being forced on every spawn. This avoids wasted LLM turns when a direct response is more appropriate.
Block the parent until all spawned children complete:
{"action": "wait", "timeout": 300}- Parent turn pauses until all active subagents finish (or timeout expires)
- Enables coordinated multi-step workflows where the lead needs results before proceeding
- Default timeout: 300 seconds
Subagent LLM failures trigger automatic retry. Configuration via SubagentConfig:
| Field | Default | Description |
|---|---|---|
MaxRetries |
2 |
Maximum retry attempts per subagent |
| Backoff | linear | Each retry waits attempt × 2s before re-running |
Tenant-scoped concurrency limits on the Edition struct:
| Limit | Field | Description |
|---|---|---|
| Concurrent subagents | MaxSubagentConcurrent |
Max simultaneous subagents per tenant |
| Spawn depth | MaxSubagentDepth |
Max nesting depth (subagent spawning subagents) |
When limits are hit, the spawn is rejected with a clear error so the LLM can adjust.
Subagent task state is persisted to the subagent_tasks database table (migration 000034). The SubagentTaskStore interface with PostgreSQL implementation provides:
- Durable task tracking across restarts
- Write-through persistence from
SubagentManager - Token cost storage per task
Per-subagent input and output token counts are accumulated and included in:
- The announce message delivered to the lead
- The
subagent_tasksDB record for billing and observability
When the lead agent's context is compacted (summarized), pending subagent and team task state is preserved in the compaction prompt. Work continuity is maintained — the lead does not lose track of in-flight tasks after summarization.
Two Telegram bot commands are available for monitoring subagent work:
| Command | Description |
|---|---|
/subagents |
Lists all active subagent tasks with status |
/subagent <id> |
Shows detailed view of a specific subagent task from DB |
team_tasks is blocked inside subagents via SubagentDenyAlways. Subagents cannot create team tasks or perform team orchestration — only the lead can coordinate the team board.
When a delegation finishes:
- Linked task is marked
completedwith delegation result - Result summary is persisted
- Media files (images, documents) are forwarded
- Delegation artifacts stored with team context
- Session cleaned up
Announcement includes:
- Results from each member agent
- Deliverables and media files
- Elapsed time statistics
- Guidance: present results to user, delegate follow-ups, or ask for revisions
When an agent has too many targets for static AGENTS.md (>15), use delegation search:
{
"query": "data analysis and visualization",
"max_results": 5
}Call the delegate_search tool with the above parameters.
What it searches:
- Agent name and key (full-text search)
- Agent description (full-text search)
- Semantic similarity (if embedding provider available)
Result:
{
"agents": [
{
"agent_key": "analyst_agent",
"display_name": "Data Analyst",
"frontmatter": "Analyzes data and creates visualizations"
}
],
"count": 1
}Hybrid search: Uses both keyword matching (FTS) and semantic embeddings for best results.
Each delegation link (lead → member) can have its own access control:
{
"user_allow": ["user_123", "user_456"],
"user_deny": []
}Concurrency limits:
- Per-link: configurable via
max_concurrenton the agent link - Per-agent: default 5 total concurrent delegations targeting any single member (configurable via agent's
max_delegation_load)
When limits hit, error message: "Agent at capacity. Try a different agent or handle it yourself."
Transfer conversation control to another agent without interrupting the user:
{
"action": "transfer",
"agent": "specialist_agent",
"reason": "You need specialist expertise for the next part of your request",
"transfer_context": true
}Call the handoff tool with the above parameters.
- Routing override set: future messages from user go to target agent
- Conversation context (summary) passed to target agent
- Target agent receives handoff notification with context
- Event broadcast to UI
- User's next message routes to new agent
- Deliverable workspace files copied to the target agent's team workspace
action:transfer(default) orclearagent: Target agent key (required fortransfer)reason: Why the handoff (required fortransfer)transfer_context: Pass conversation summary (default true)
{
"action": "clear"
}Messages will route to default agent for this chat.
Handoff notification sent to the target agent:
[Handoff from researcher_agent]
Reason: You need specialist expertise for the next part of your request
Conversation context:
[summary of recent conversation]
Please greet the user and continue the conversation.
- User's question becomes specialized → handoff to expert
- Agent reaches capacity → handoff to another instance
- Complex problem needs multiple specialties → handoff after partial solution
- Shift from research to implementation → handoff to engineer
For iterative work, use the evaluate pattern with task creation:
{"action": "create", "subject": "Generate initial proposal", "assignee": "generator_agent"}
// Wait for result, then:
{"action": "create", "subject": "Review proposal and provide feedback", "assignee": "evaluator_agent"}
// Generator refines based on feedback...Note: The system does not enforce a maximum number of iterations for this pattern. Set your own limit in the lead's instructions to avoid infinite loops.
For async delegations, the lead receives periodic grouped updates (if progress notifications are enabled for the team):
🏗 Your team is working on it...
- Data Analyst (analyst_agent): 2m15s
- Report Writer (writer_agent): 45s
Interval: 30 seconds. Enabled/disabled via team settings (progress_notifications).
- Use
team_tasksto delegate: create tasks withassignee— system auto-dispatches - Don't use
spawnfor delegation:spawnis self-clone only, not for team members - Create multiple tasks in one turn: they dispatch in parallel after the turn ends
- Use
blocked_by: coordinate task ordering with dependencies - Use
spawn(action=wait): when lead needs all results before continuing - Handle handoffs gracefully: Notify user of transfer; pass context
- Set iteration limits in instructions: Prevent infinite evaluate loops