Agents
An agent is the top-level conversation orchestrator in Standard Agents. Agents define:
- conversation type (
ai_humanordual_ai) - side prompts and stop behavior
- session lifecycle tool bindings
- optional tool exposure for handoff or subagent usage
A subagent is not a separate concept — it is a dual_ai agent invoked by a parent thread as a tool (see §7 Subagents).
1. AgentDefinition
1.1 Required
| Property | Type | Description |
|---|---|---|
name | string | Unique agent identifier |
sideA | SideConfig | Side A configuration |
1.2 Optional
| Property | Type | Default | Description |
|---|---|---|---|
type | 'ai_human' | 'dual_ai' | 'ai_human' | Conversation mode |
sideB | SideConfig | - | Required for dual_ai |
maxSessionTurns | number | - | Session turn safety cap |
title | string | - | Human-readable label (legacy) |
description | string | - | Agent summary |
icon | string | - | Display icon |
exposeAsTool | boolean | false | Expose callable entrypoint |
toolDescription | string | - | Callable description |
env | Record<string, string> | - | Agent-level default variable values |
hooks | string[] | - | Fallback hook IDs |
AgentDefinition also accepts optional packaging metadata — packageName, version, author, license — consumed only when the agent is packed for distribution. See Packaging.
2. Agent Types
2.1 ai_human
- Side A is AI.
- Side B is human (no Side B config required).
- Typical usage: user-facing assistants.
2.2 dual_ai
- Both sides are AI.
- Each side sees itself as
assistant, other side asuser. maxSessionTurnsshould be set for bounded execution.dual_aiagents can be used as autonomous subagents (§7).
3. SideConfig
| Property | Type | Default | Description |
|---|---|---|---|
prompt | string | Required | Prompt name for this side |
label | string | - | UI/log label |
stopOnResponse | boolean | true | Stop side turn on text response without tool calls |
stopTool | string | - | Stop side turn when this tool is called |
stopToolResponseProperty | string | - | Extracted property for stop tool outcomes |
maxSteps | number | - | Per-side step safety limit |
sessionStop | SessionToolBinding | - | End session successfully |
sessionFail | SessionToolBinding | - | End session with failure |
sessionStatus | SessionToolBinding | - | Publish status updates |
4. SessionToolBinding
Session lifecycle bindings can be string or object form:
type SessionToolBinding =
| string
| {
name: string;
messageProperty?: string;
attachmentsProperty?: string;
};
name: tool that controls lifecycle actionmessageProperty: which arg field becomes lifecycle message textattachmentsProperty: which arg field contains attachment path(s)
For subagents, mapped stop/fail payloads are the canonical child → parent result/failure payload.
5. Stop Semantics
Runtime stop evaluation order:
- session-level terminal bindings (
sessionStop/sessionFail) - side-level stop bindings (
stopTool) - response stop (
stopOnResponse) - safety limits (
maxSteps,maxSessionTurns)
6. Tool Exposure
When exposeAsTool: true, how the callable behaves depends on agent type:
| Agent type | Callable semantics |
|---|---|
ai_human | Handoff: the tool-call transfers control to the target agent on the same thread, preserving history. |
dual_ai | Subagent: the tool-call spawns a new child thread governed by §7 Subagents. |
7. Subagents
Subagents are autonomous dual_ai child agents spawned from a parent thread. They are the composition primitive for multi-agent workflows.
7.1 What a Subagent Is
- Always a
dual_aiagent. - Invoked from a parent prompt’s tool list.
- Executed in its own isolated thread (messages, filesystem, queue, lifecycle).
- Linked to its parent via thread hierarchy and child registry state.
This lets orchestrators delegate bounded work to specialized children without merging storage or execution state.
7.2 Why Compose
- Parent orchestrates planning, sequencing, and aggregation.
- Child performs focused work with its own prompts/tools.
- Child can run synchronously (blocking) or asynchronously (non-blocking).
- Child can be disposable (non-resumable) or persistent (resumable).
A common pattern: side_a produces, side_b reviews/validates before completion.
7.3 Invocation Modes
| Blocking | Non-blocking | |
|---|---|---|
| Non-resumable | Parent waits; child ends after result | Parent continues; child returns result later |
| Resumable | Parent waits per invocation; child persists | Parent can message anytime; child persists |
7.4 Configuration
Subagents are configured in definePrompt({ tools: [...] }) via SubagentToolConfig. The type and per-field semantics are documented in Tools §5.
The key knobs:
blocking— parent waits for child result.immediate— execute as soon as the prompt becomes active, before the first model step. Object form allows per-instancenameEnv/descriptionEnvhints andscopedEnvtransfer.optional— environment flag gating whether this branch is enabled.resumable— persistent child instances addressable via lifecycle tools.resumable.parentCommunication—implicit(default) auto-queues completion/failure to the parent;explicitleaves escalation to tools/hooks.parentCommunication(top level) — the same modes for non-resumable subagents;explicitsuppresses the auto-queued completion so a background child’s finish does not wake the parent.resumable.parentCommunicationtakes precedence when both are set. See §7.11.hidden— keep the relationship declared (runtime code can still spawn the child viainvokeTool/queueTool) while excluding the tool from every LLM request, so the model can never call it. For infrastructure children like background context compaction.
7.5 Optional Branches
When optional is set, the branch is enabled only when the named environment variable resolves to one of true, 1, or yes (case-insensitive). Disabled branches are not available for creation or runtime invocation.
7.6 Immediate Execution
When immediate is enabled, runtimes execute the subagent tool immediately when its prompt becomes active:
- thread creation bootstrap
- handoff to a new active prompt
Immediate execution is recursive: if an immediate subagent boots and its active prompt has immediate tools, those execute as soon as the child thread activates.
In the object form:
- runtimes MAY use
nameEnvanddescriptionEnvas model-visible hints when deriving initial child arguments via a bootstrap pass. - runtimes MUST treat
scopedEnvas runtime-only transfer data. - runtimes MUST NOT expose
scopedEnvvalues to the model unless the same env is explicitly declared asnameEnvordescriptionEnv.
7.7 Resumable Lifecycle Tools
For resumable instances, runtimes expose built-in lifecycle tools:
subagent_createsubagent_message
subagent_create MUST require a non-empty name argument for the spawned child instance.
subagent_create SHOULD also accept an arguments object shaped by the prompt schema of the child side that receives parent messages (resumable.receives_messages). Runtimes MUST persist those initial arguments for the lifetime of the child thread and make them available to both child sides through ThreadState.arguments and prompt variable interpolation, including future resumptions.
Non-resumable subagents behave like direct tool calls (no persistent addressing).
When maxInstances is reached, new creation attempts SHOULD return a tool error explaining the cap and suggesting messaging an existing instance.
7.8 Persistent Invocation Arguments
The receiving agent — through the requiredSchema of its side_a prompt — solely dictates the invocation arguments a subagent accepts. Required schema fields are mandatory; optional fields are merely suggested. The invoking parent supplies them when calling the subagent tool, or via a structured arguments object on subagent_create.
Unlike a plain tool call, these arguments are not ephemeral:
- runtimes MUST persist the validated arguments for the life of the child thread.
- runtimes MUST hydrate them into prompt interpolation for both
side_aandside_bon every activation — the initial invocation and every later resume — so a system prompt can reference spawn-time context (for example a stylist subagent that needs the user’s gender, sizes, and style preferences) for as long as the thread lives. - if required arguments are missing, runtimes MUST reject creation with a structured
subagent_arguments_requirederror rather than spawning the child.
This lets the receiving side declare what it needs once and rely on those values throughout the conversation, instead of only seeing them in the first message.
7.9 Scoped Variable Bootstrap
If subagent_create is called but required scoped variables for that child graph are missing, runtimes SHOULD return a structured error (e.g. subagent_env_required) and expose a temporary bootstrap endpoint:
GET /threads/{parent_thread_id}/variables/{request_id}POST /threads/{parent_thread_id}/variables/{request_id}
Expected behavior:
GETreturns required/missing variable names.POSTstores the provided values and immediately boots the deferred subagent using the original creation payload.
7.10 Session Lifecycle Bindings on Child Sides
Child completion, failure, and status are defined on each side’s SideConfig:
sessionStopsessionFailsessionStatus
Each supports the string and object forms from §4. Mapped properties are the canonical payload for child → parent communication.
7.11 Parent ↔ Child Communication
Parent → Child
Parent communication occurs via:
- initial invocation mapping (
initUserMessageProperty,initAttachmentsProperty) - resumable messaging (
subagent_message)
Attachment paths MUST be copied from the parent filesystem to the child filesystem before queuing (§7.13).
Child → Parent: Completion
When the child finishes via sessionStop and parentCommunication is implicit, the parent receives a queued silent message:
Subagent (reference: {uuid}) has returned the following result:
{result}
Returned attachments (if any) are copied child → parent and included as parent-local paths.
Child → Parent: Failure
When sessionFail is called and parentCommunication is implicit, the parent receives:
Subagent (reference: {uuid}) has reported a failure:
{failure details}
Returned attachments are copied child → parent.
Child → Parent: Status
sessionStatus updates the parent’s child registry status text without ending child execution.
Child → Parent: Explicit Escalation
When the effective parentCommunication mode is explicit, runtimes MUST NOT auto-queue completion/failure to the parent. Tools or hooks escalate explicitly:
state.notifyParent(content)state.setStatus(status)state.stopSession({ result, notifyParent, status })
The effective mode is resolved as resumable.parentCommunication when set, otherwise the top-level SubagentToolConfig.parentCommunication, defaulting to implicit. The top-level form is how a non-resumable background subagent opts out: because the auto-queued completion message is what triggers a parent turn, explicit means the child’s completion does not wake the parent at all. Such children deliver results through another channel — for example, writing to the parent’s thread key-value store — which the parent picks up on its next natural turn (e.g. via a filter_messages hook).
See Threads for the host-side surface.
7.12 Child Registry
Resumable instances are tracked in ThreadState.children:
interface SubagentRegistryEntry {
reference: string;
name: string;
title?: string;
description: string;
resumable?: boolean;
blocking?: boolean;
threadName?: string;
initialArguments?: Record<string, unknown>;
spawnGroupId?: string;
createdAt?: number;
status: string;
parentCommunication?: 'implicit' | 'explicit';
}
When children exist, parent context SHOULD include a registry system message so the orchestrator can reason about active instances.
7.13 Attachment Copying
Subagent communication that crosses thread boundaries MUST copy filesystem references:
- parent → child for initial and resumable messaging attachments
- child → parent for completion and failure payload attachments
The destination thread MUST receive destination-local attachment paths.
7.14 Queue and Termination
These are properties of the underlying thread, not of subagents specifically. See Threads for the full queue and termination semantics:
- queued messages are durable and ordered; injected before the next model step when executing, otherwise forcing the next turn.
terminate()is a soft shutdown that aborts in-flight execution, rejects new execution/queued-message entry, and updates parent registry status toterminatedwhen applicable.
8. Example
defineAgent({
name: 'asset_subagent',
type: 'dual_ai',
maxSessionTurns: 40,
exposeAsTool: true,
toolDescription: 'Generate and QA top-down game assets.',
sideA: {
label: 'Worker',
prompt: 'asset_worker',
stopOnResponse: true,
sessionFail: {
name: 'fail_asset',
messageProperty: 'reason',
attachmentsProperty: 'attachments',
},
},
sideB: {
label: 'Reviewer',
prompt: 'asset_reviewer',
stopOnResponse: false,
sessionStop: {
name: 'approve_asset',
messageProperty: 'summary',
attachmentsProperty: 'attachments',
},
sessionStatus: {
name: 'update_asset_status',
messageProperty: 'status',
},
},
});