Delegation¶
Background task delegation via child rooms. See the Agent Delegation guide for usage examples.
Task Runner¶
TaskRunner ¶
Bases: ABC
ABC for executing delegated tasks in the background.
Follows the same pluggable-backend pattern as ConversationStore
and RoomLockManager.
A task ends exactly once, through on_complete, whatever ends it (RFC
§23.1, §23.3): run to its end, it ends completed or failed; cancelled
(:meth:cancel, :meth:close, even before it ran), it ends with a
cancelled result (:func:~roomkit.tasks.models.cancelled_task_fields).
The framework closes the task's span, fires ON_TASK_COMPLETED and
hands the result back from that call.
The worker is given task.description, what it reads (the brief,
else the task, with the room's conversation when the delegation hands
it over), not task.task, the label the task is named by (RFC §23.3
step 4).
submit
abstractmethod
async
¶
Start background execution of task.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
kit
|
RoomKit
|
The RoomKit instance (used to interact with rooms/channels). |
required |
task
|
DelegatedTask
|
The delegated task handle. |
required |
context
|
dict[str, Any] | None
|
Optional context passed to the agent. |
None
|
on_complete
|
OnCompleteCallback | None
|
Callback invoked when the task finishes. |
None
|
cancel
abstractmethod
async
¶
Cancel a task and end it cancelled through its on_complete,
run to its end even if this call is cancelled. A task that already
ran to its end ends as it stands. Returns True if the task was found
and cancelled.
close
abstractmethod
async
¶
Shutdown the runner: every task it holds ends cancelled, one submitted meanwhile too.
InMemoryTaskRunner ¶
Task Models¶
DelegatedTask ¶
DelegatedTask(*, id, child_room_id, parent_room_id, agent_id, task, brief=None, description=None, hand_back='relay')
Handle for a running delegated task.
Not a Pydantic model — holds mutable state and an asyncio.Event
for callers that want to block until the task completes.
task
instance-attribute
¶
What was asked: the label a hand-back, the StatusBus and a turn's notes name the task by (RFC §23.3 step 4).
description
instance-attribute
¶
What the worker reads: the brief, else the task, with the room's conversation when the delegation hands it over.
hand_back
instance-attribute
¶
How the result reaches the room: relay (the notified agent says
it in a turn of its own) or post (posted as it is, as its message).
wait
async
¶
Block until the task completes or timeout seconds elapse.
Raises:
| Type | Description |
|---|---|
TimeoutError
|
If timeout is exceeded. |
RuntimeError
|
If the task finished without a result. |
cancel ¶
Mark the task as cancelled and unblock its waiters, and nothing
else: the work goes on, and no hook or callback hears of it. To
cancel the work, as every task ends (RFC §23.3), use the task
runner's cancel.
DelegatedTaskResult ¶
Bases: BaseModel
Result of a completed delegated task.
exception
class-attribute
instance-attribute
¶
The failure itself, its type kept (what the worker's turn raised), for a caller that hands it on; held in memory, never serialized (RFC §23.3 step 6).
Tool Integration¶
DELEGATE_TOOL
module-attribute
¶
DELEGATE_TOOL = AITool(name=DELEGATE_TOOL_NAME, description='Delegate a task to a background agent. The agent works in its own room and you can continue the current conversation. Use when: a task needs a specialist, the user wants something done in the background, or you need to run work in parallel.', parameters={'type': 'object', 'properties': {'agent': {'type': 'string', 'description': 'Target agent ID for the delegated task'}, 'task': {'type': 'string', 'description': 'Clear description of what the agent should do'}, 'context': {'type': 'object', 'description': 'Optional context to pass to the agent (e.g. email, repo URL)'}, 'share_channels': {'type': 'array', 'items': {'type': 'string'}, 'description': 'Channel IDs from the current room to share with the background agent'}}, 'required': ['agent', 'task']})
build_delegate_tool ¶
Build a delegate tool with constrained agent enum.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
targets
|
list[tuple[str, str | None]]
|
List of |
required |
Returns:
| Name | Type | Description |
|---|---|---|
An |
AITool
|
class: |
AITool
|
restricting the AI to only the listed agent IDs. |
DelegateHandler ¶
DelegateHandler(kit, *, notify=None, default_share_channels=None, delivery_strategy=None, cache=None, serialize_per_room=False)
Processes delegate_task tool calls by calling kit.delegate().
Supports optional dedup via :class:CompletedTaskCache and
per-room concurrency control via asyncio locks.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
kit
|
RoomKit
|
The RoomKit instance. |
required |
notify
|
str | None
|
Channel ID to notify on completion (defaults to calling agent). |
None
|
default_share_channels
|
list[str] | None
|
Channels shared with background agents by default. |
None
|
delivery_strategy
|
DeliveryStrategy | None
|
How to deliver results back proactively. |
None
|
cache
|
CompletedTaskCache | None
|
Optional completed-task cache for dedup. |
None
|
serialize_per_room
|
bool
|
If True, only one delegation per room at a time. |
False
|
setup_delegation ¶
Serve the delegate tool on an AIChannel.
Declares delegate_task in the channel's turns and serves its calls with
handler. Same pattern as setup_handoff(): the task is delegated from
the room of the call, read from the tool call context (RFC §23.4), so one
agent serving several rooms delegates from each.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
channel
|
AIChannel
|
The AI channel to wire delegation into. |
required |
handler
|
DelegateHandler
|
The delegate handler that processes tool calls. |
required |
tool
|
AITool | None
|
Optional custom delegate tool (e.g. from :func: |
None
|
Raises:
| Type | Description |
|---|---|
RuntimeError
|
Delegation is already set up on the channel. |
setup_realtime_delegation ¶
Serve the delegate tool on a RealtimeVoiceChannel.
Declares delegate_task in the channel's sessions and serves its calls
with handler, delegating from the room of the call: the session's room,
which the channel installs as the tool call context (RFC §21.4, §23.4).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
channel
|
RealtimeVoiceChannel
|
The realtime voice channel to wire delegation into. |
required |
handler
|
DelegateHandler
|
The delegate handler that processes tool calls. |
required |
tool
|
AITool | None
|
Optional custom delegate tool (e.g. from :func: |
None
|
Raises:
| Type | Description |
|---|---|
RuntimeError
|
Delegation is already set up on the channel. |
Background Work¶
RoomKit.setup_background(agent, worker=None, hand_back="relay") gives an
agent the work_in_background tool (BACKGROUND_TOOL, from roomkit.tasks),
and RoomKit.start_task(room_id, agent_id, task, hand_back=None) starts the
same task from the host. See Long work from an
agent.
BackgroundSetup
dataclass
¶
How an agent's background tasks run: on worker (the agent itself when None), their result relayed or posted (hand_back).
BackgroundMixin ¶
setup_background and start_task: an agent's long work runs as a
task, off its turn.
setup_background ¶
Give agent the work_in_background tool (RFC §23.4): it starts
long work as a task and ends its turn, the work running apart.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
agent
|
AIChannel
|
The AI channel given the tool, in every room it serves. |
required |
worker
|
str | None
|
The channel that does its tasks (another agent, an external agent over ACP); the agent itself when None. |
None
|
hand_back
|
str
|
How a task's result reaches the room: |
'relay'
|
Raises:
| Type | Description |
|---|---|
ValueError
|
An unknown hand_back. |
RuntimeError
|
The tool is already set up on the agent. |
start_task
async
¶
Start task for agent_id, an agent of room_id, as its
work_in_background call would (RFC §23.4): on its worker, handed
the room's last messages, its result relayed or posted back to it.
The host's door, and a person's through what the host gives them; an
agent with no background set up does the work itself and has its
result relayed.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
room_id
|
str
|
The room the task is for. |
required |
agent_id
|
str
|
The agent the task is for: an intelligence channel attached to the room, an external agent included. |
required |
task
|
str
|
What to do and report. |
required |
hand_back
|
str | None
|
|
None
|
Raises:
| Type | Description |
|---|---|
ValueError
|
agent_id is no agent of the room, task is blank, or hand_back is unknown. |
Following Tasks¶
TaskStatusTool ¶
task_status: the tasks of the room of the call, read from the
framework's StatusBus (RFC §23.4).
A tool an agent is given on its own — AIChannel(tools=[TaskStatusTool(kit)])
— independently of the delegation helpers. It answers with the tasks posted
for the room of the call (delegations, orchestration workers): those running
and those ended, the latest state of each, with its worker, the task, and its
result when it completed; task_id narrows the answer to one task. It reads
the bus for that room only: one room's tasks never show in another.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
kit
|
RoomKit
|
The framework whose |
required |
window
|
int
|
How many of the bus's latest entries are read. |
500
|
limit
|
int
|
How many tasks, the latest, an answer lists at most. |
10
|
CancelTaskTool ¶
cancel_task: cancel one task of the room of the call (RFC §23.4).
A tool an agent is given on its own, as :class:TaskStatusTool —
AIChannel(tools=[TaskStatusTool(kit), CancelTaskTool(kit)]) — so that a
request the person changed or dropped stops its work. It reaches only the
tasks the StatusBus lists for the room of the call: never another room's,
nor a strategy's worker run, which that strategy follows itself. A running
task ends cancelled; the agent that cancelled it is not handed the
cancellation back, the tool's answer having told it.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
kit
|
RoomKit
|
The framework whose tasks the tool cancels. |
required |
window
|
int
|
How many of the bus's latest entries are read to find the task. |
500
|
room_tasks ¶
One line per task, from its entries in posting order: the task from its
pending entry, its state and result from its latest terminal one, its
progress from its latest info one, which never ends it.
An orchestration worker run posts its pending entry before its task has
an id: the run's terminal entry, which names the task, joins it.
post_task_progress
async
¶
From a tool a worker calls in its task's child room: say how far the task got ("12/30 s", "3 sources of 5"), for the room that delegated it (RFC §23.3).
Posts an info entry under the worker, action task, with the task's
metadata; it does not end the task. Returns whether it posted: a call
outside a task's child room posts nothing.
The tools' names, as a model calls them: TASK_STATUS_TOOL ("task_status") and
CANCEL_TASK_TOOL ("cancel_task"), from roomkit.tasks.
Delegation State Tracking¶
CompletedTaskCache ¶
In-memory TTL cache for completed delegation results.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
ttl_seconds
|
float
|
Seconds before a cached result expires. Defaults to 300 (5 min). |
300.0
|
recent_context ¶
Return summaries of recent tasks for a room (for context injection).
Returns task descriptions from newest to oldest, up to limit. Expired entries are skipped.
Delivery Strategies¶
DeliveryStrategy ¶
Bases: ABC
Controls when content is delivered; report its actual outcome.
deliver
abstractmethod
async
¶
Deliver the request. A custom strategy may leave its outcome unknown.
Immediate ¶
Bases: DeliveryStrategy
Send now. On a voice session, it plays once the playback under way has been sent whole: a response never cuts the agent off (RFC §12.2).
WaitForIdle ¶
Queued ¶
Bases: DeliveryStrategy
Batch compatible unkeyed requests at idle; preserve each caller's outcome.
Keyed requests publish individually, including duplicate keys. One caller owns the drain; followers wait for their own request to be processed.
DeliveryContext
dataclass
¶
DeliveryContext(kit, room_id, content, channel_id=None, metadata=None, addressed_to=None, idempotency_key=None, session_id=None, chain_depth=0, instruction=False, _voice_sessions=None, _voice_channel=None, _wait_for_turn=True)
One request, including its destination pinned before an idle wait.
find_transport_channel_id
async
¶
Prefer voice, then the first other transport bound to the room.
Voice is a voice channel type or any channel whose realtime model is
plugged in at delivery time (a conference with one,
:func:hosts_realtime_model).
An intelligence channel attached as a transport is not one: an
instruction delivered through it would re-enter the channel it is for.
resolve_channel_id
async
¶
Resolve an explicit destination or auto-detect its transport.