Overview
SubagentRegistry runs agent tasks in the background on a ThreadPoolExecutor, tracking each as a SubagentTask with status, retries, depth, and parent linkage. Use it when one agent needs to fan out work to other agents — recursively if needed — and gather results later.
The module exports three symbols:
Installation
TaskStatus
(str, Enum), so the values compare equal to plain strings (TaskStatus.PENDING == "pending"). spawn() sets a task to RUNNING as soon as it submits it, so a task waiting for a free worker thread also reports RUNNING.
SubagentTask
Dataclass describing a single in-flight or completed task. Populated bySubagentRegistry.spawn().
str
Unique task ID, e.g.
task-a1b2c3d4.Any
The agent instance assigned to this task.
str
The prompt/task handed to
agent.run(...).TaskStatus
default:"TaskStatus.PENDING"
Current execution status.
Any
default:"None"
Return value from the agent — set when
status == COMPLETED.Exception | None
default:"None"
Last exception raised — set when
status == FAILED.concurrent.futures.Future | None
default:"None"
Underlying
Future returned by the thread pool.str | None
default:"None"
ID of the parent task that spawned this one, if any.
int
default:"0"
Recursion depth — incremented when an agent spawns another subagent.
int
default:"0"
Number of retries used so far.
int
default:"0"
Retry budget for this task.
List[Type[Exception]] | None
default:"None"
Whitelist of exception classes that trigger retries.
spawn() stores [] when you pass None; an empty list retries on any exception.float
Unix timestamp when the task was spawned.
float | None
default:"None"
Unix timestamp when the task entered a terminal state.
SubagentRegistry
Constructor
int
default:"3"
Maximum recursion depth.
spawn() raises ValueError if depth > max_depth.int | None
default:"None"
Thread-pool size.
None defers to ThreadPoolExecutor’s default.Methods
spawn()
Submit an agent task to the pool. Returns the new task ID synchronously; the agent runs in the background.Any
required
Agent instance with a
.run(task) method.str
required
The prompt to run.
str | None
default:"None"
Set when this task was spawned by another task. Used for tracking trees.
int
default:"0"
Recursion depth. Spawning from inside another task increments this.
int
default:"0"
Retry budget on failure.
List[Type[Exception]] | None
default:"None"
Only retry on these exception types.
None retries on any exception.bool
default:"True"
When
True, the underlying thread re-raises on final failure (the exception surfaces when you call future.result() or gather()). When False, the failure is captured on the SubagentTask.error and the thread returns None. Either way the task’s status becomes FAILED.ValueError if depth > max_depth.
get_task()
Look up aSubagentTask by ID.
KeyError if the ID is not in the registry.
get_results()
Return aDict[task_id, Any] for every completed or failed task. Failed tasks map to their exception object. Running and cancelled tasks are left out. It does not wait, so call gather() first if you need every result.
cancel()
Attempt to cancel a task that is still queued. ReturnsTrue and sets the status to CANCELLED if the underlying Future accepted the cancellation. A task that has started running cannot be cancelled.
KeyError if the ID is not in the registry.
gather()
Block until tasks complete and return a list of results."wait_all" | "wait_first"
default:"\"wait_all\""
Wait policy.
"wait_all" returns once every pending task settles; "wait_first" returns as soon as one does.float | None
default:"None"
Max seconds to wait.
None blocks indefinitely.gather() come first, in spawn order: a completed task contributes its result and a failed task its exception. Tasks that finish during the wait follow in no guaranteed order, contributing their return value or the exception their future raised (a cancelled task contributes a CancelledError). Tasks still running when timeout expires, or when "wait_first" returns, are left out.
gather() results are not keyed by task. When you need to know which result belongs to which task, keep the IDs from spawn() and read get_task(task_id).result (or get_results()) after gather() returns. A task spawned with fail_fast=False that fails during the wait contributes None here; its exception is on get_task(task_id).error.shutdown()
Shut down the thread pool without waiting. Tasks already spawned still run in the background; callingspawn() afterwards raises RuntimeError.
tasks
Read-only property — snapshot of all known tasks asDict[task_id, SubagentTask].
Usage Examples
Fan Out, Gather, Map by Agent
Retries on Specific Exceptions
Retry only when the model call fails; surface anything else immediately.Agent.run() raises AgentLLMError once its own retries and any fallback_models are exhausted.
Take the First Successful Result
Depth-Limited Recursion
How autonomous agents use the registry
AnAgent running with max_loops="auto" delegates through the same registry. Its sub-agent tools work like this:
create_sub_agentbuilds each sub-agent with the parent’smodel_nameandtools. A sub-agent with tools getsmax_loops=5so it can call a tool and read the result; one without tools getsmax_loops=1. The budget stays finite even when the parent runs withmax_loops="auto".assign_taskspawns each assignment on aSubagentRegistrystored on the parent agent (created on first use withmax_depth=3), withfail_fast=Falseand no retries. By default it waits for every task and returns one result or error per assignment.check_sub_agent_statusandcancel_sub_agent_tasksread and cancel tasks on that registry.