Agents API Reference
Complete technical reference for agent configuration and types
Overview
Agents in Standard Agents Builder define conversational AI experiences with precise control over participants, prompts, tools, and stop conditions. This document provides the complete API specification.
defineAgent
The defineAgent function creates an agent configuration with full TypeScript support.
Function Signature
function defineAgent<N extends string>(
config: AgentDefinition<N>
): AgentDefinition<N>AgentDefinition Type
interface AgentDefinition<N extends string = string> {
// Required fields
name: N;
sideA: SideConfig;
sideB: SideConfig;
// Optional fields
title?: string;
description?: string;
icon?: string;
maxSessionTurns?: number;
primary?: boolean;
exposeAsTool?: boolean;
toolDescription?: string;
env?: Record<string, string>;
hooks?: string[];
parentHooks?: ParentHookRequirement[];
packageName?: string;
version?: string;
author?: string;
license?: string;
}Configuration Options
Core Properties
namestringrequiredUnique identifier for this agent. Use snake_case.
Naming conventions:
- Use descriptive names:
customer_support,billing_specialist - Avoid:
agent1,bot,helper
name: 'support_agent' // Used in API calls and referencestitlestringHuman-readable title. Deprecated in favor of name.
title: 'Customer Support Agent' // Optional display namemaxSessionTurnsnumberMaximum number of exchanges across both sides before the runtime ends the session. This field is optional for every agent and has no default. Set it when both sides are backed by prompts or handlers, because no person paces that conversation.
Reaching the cap ends the session. The thread stays addressable.
maxSessionTurns: 20 // Max 20 exchanges in one sessiondescriptionstringBrief description of what this agent does. Useful for UIs and documentation.
description: 'Handles customer inquiries and resolves support tickets'iconstringIcon URL or absolute path for the agent. Absolute paths (starting with /) are converted to full URLs in API responses.
// Full URL
icon: 'https://example.com/icons/support.svg'
// Absolute path (converted to full URL in API responses)
icon: '/icons/support.svg'Side Configuration
Both sides are required. Each side declares exactly one backing: human, prompt, or handler. An agent with two human-backed sides is rejected.
sideASideConfigrequiredConfiguration for side A. Messages from side A are stored with the assistant role.
sideA: {
label: 'Support Agent',
prompt: 'customer_support',
stopOnResponse: true,
maxSteps: 25,
}sideBSideConfigrequiredConfiguration for side B. Messages from side B are stored with the user role.
For a chat agent, a person takes side B's turns:
sideB: { human: true }A second model can take side B's turns:
sideB: {
label: 'Con',
prompt: 'debate_con',
stopOnResponse: false,
maxSteps: 10,
}Tool Exposure
exposeAsToolbooleandefault: falseWhether to expose this agent as a callable tool for other prompts/agents.
An exposed agent in a prompt's tools array performs a handoff in the current thread. Its human-backed side must match the thread's conversational surface. To run an agent in a child thread, declare a relationship in the owning side's subagents array.
exposeAsTool: truetoolDescriptionstringDescription shown when agent is exposed as a tool. Required if exposeAsTool: true.
Should be specific about what the agent handles and when to use it.
toolDescription: 'Hand off billing and payment issues to a specialist with deep knowledge of payment systems'primarybooleandefault: trueWhether the agent is a user-facing entry point. Set false for an agent that exists only as a handoff target or as infrastructure.
GET /api/agents?primary=true returns only primary agents. Without the parameter the list includes non-primary agents, and a thread can always be created for one.
primary: falseSideConfig
Configuration for individual sides (A or B).
SideConfig Type
import type {
SideConfig,
HumanSide,
PromptSide,
HandlerSide,
} from '@standardagents/spec';
type SideConfig = HumanSide | PromptSide | HandlerSide;
interface HumanSide {
human: true;
label?: string;
}
interface PromptSide {
// Required
prompt: StandardAgentSpec.Prompts;
// Optional
label?: string;
subagents?: SubagentRelationship[];
stopOnResponse?: boolean;
stopTool?: StandardAgentSpec.Callables;
stopToolResponseProperty?: string;
maxSteps?: number;
sessionStop?: SessionToolBinding;
sessionFail?: SessionToolBinding;
sessionStatus?: SessionToolBinding;
}
interface HandlerSide {
handler: StandardAgentSpec.Handlers;
label?: string;
subagents?: SubagentRelationship[];
}
interface SessionToolBinding {
name: StandardAgentSpec.Callables;
messageProperty?: string;
attachmentsProperty?: string;
}A human-backed side accepts only human and label. A handler-backed side accepts only handler, label, and subagents. The stop, step, and session properties below apply to prompt-backed sides.
Side Properties
humantrueMarks the side as human-backed. A person takes this side's turns.
sideB: { human: true }promptstringReference to a prompt defined in agents/prompts/. A model takes this side's turns.
prompt: 'customer_support' // Must exist in prompts/handlerstringReference to a handler defined in agents/handlers/. Deterministic code takes this side's turns.
handler: 'relay_pipe' // Must exist in handlers/subagentsSubagentRelationship[]Child relationships that this side owns. Prompt-backed and handler-backed sides can declare them. See Subagents.
subagents: [{ agent: 'topdown_asset_subagent', blocking: true }]labelstringHuman-readable label displayed in UI and logs.
label: 'Support Agent'stopOnResponsebooleandefault: trueWhen true, the side's turn ends when the AI returns text content without tool calls.
Behavior:
true: Turn ends after text response (conversational agents)false: Text alone does not match a turn stop condition. With two runtime-backed sides and nostopTool, the runtime switches sides after each step.
stopOnResponse: true // Stop after each responsestopToolstringTool name that ends the turn when called.
A stopTool without stopToolResponseProperty ends the turn. It extracts no value.
stopTool: 'complete_checkout'
stopToolResponseProperty: 'orderId'stopToolResponsePropertystringProperty to extract from stop tool result.
This property is optional. It is valid only when stopTool is set on the same side.
stopToolResponseProperty: 'orderId' // Extract result.orderIdmaxStepsnumbermaxSteps limits the total model steps a prompt-backed side takes in one execution. A step is one LLM request and response cycle. The count is cumulative across that side's turns, and a change of sides does not reset it. Use the agent's maxSessionTurns to limit session exchanges.
maxSteps: 5 // Max 5 model steps for this side in one executionsessionStopSessionToolBindingTool that ends the session for both sides when called. Ending a session leaves the thread addressable.
Session bindings require object form. messageProperty maps a tool argument to lifecycle message text. attachmentsProperty maps attachment paths.
sessionStop: { name: 'close_ticket', messageProperty: 'summary' }sessionFailSessionToolBindingTool that ends the session with a failure outcome when called.
sessionFail: { name: 'report_failure', messageProperty: 'reason' }sessionStatusSessionToolBindingTool that publishes a status update to the parent thread when the agent runs as a subagent. A status update does not end the session.
sessionStatus: { name: 'update_status', messageProperty: 'status' }Stop Conditions
Step and session limits are checked at their execution boundaries. After a model response, the runtime checks tool and response conditions in the order below.
Priority Order
- A successful
sessionFailcall ends the session with a failure outcome. - A successful
sessionStopcall ends the session. stopToolends the current prompt-backed turn.stopOnResponseends the current prompt-backed turn after non-empty text without tools.
A failed session-tool call does not end the session. A prompt with
toolChoice: 'required' can retry a text-only response before it ends the turn.
Code can also stop imperatively:
if (state.execution) state.execution.stop();
// To end the active session:
await state.stopSession();Stop Condition Types
Turn Stop Conditions
Control when a single turn ends:
| Condition | Configuration | Description |
|---|---|---|
| Text Response | stopOnResponse: true |
Stop when AI returns text without tools |
| Specific Tool | stopTool: 'tool_name' |
Stop when specific tool is called |
| Max steps | maxSteps: N |
Stop at the side's model-step safety limit |
Session End Conditions
Control when the session ends:
| Condition | Configuration | Description |
|---|---|---|
| Session stop tool | sessionStop: { name: 'tool_name' } |
End the session when the tool is called |
| Session fail tool | sessionFail: { name: 'tool_name' } |
End the session with a failure outcome when the tool is called |
| Max Session | maxSessionTurns: N |
End the session after N exchanges |
Type Definitions
Generated Types
After defining agents, Standard Agents Builder generates type-safe references:
// Auto-generated in .agents/types.d.ts
declare global {
namespace StandardAgentSpec {
interface AgentRegistry {
support_agent: true;
billing_specialist: true;
debate_agent: true;
}
}
}
// Usage in code
function startAgent(agentName: StandardAgentSpec.Agents) {
// TypeScript validates agentName must be a defined agent
}StandardAgentSpec.Agents resolves to the names registered in AgentRegistry.
AgentDefinition Interface
Core fields of the TypeScript definition:
interface AgentDefinition<N extends string = string> {
/**
* Unique identifier for this agent
* @example 'customer_support_agent'
*/
name: N;
/**
* Human-readable title (deprecated, use name)
* @deprecated
*/
title?: string;
/**
* Brief description of what this agent does
* @example 'Handles customer inquiries and resolves support tickets'
*/
description?: string;
/**
* Icon URL or absolute path (converted to full URL in API responses)
* @example 'https://example.com/icon.svg' or '/icons/support.svg'
*/
icon?: string;
/**
* Maximum exchanges across both sides before the session ends.
* Optional for every agent. No default.
*/
maxSessionTurns?: number;
/**
* Side A configuration. Backed by a human, a prompt, or a handler.
*/
sideA: SideConfig;
/**
* Side B configuration. Backed by a human, a prompt, or a handler.
* For a chat agent this is { human: true }.
*/
sideB: SideConfig;
/**
* Whether the agent is a user-facing entry point
* @default true
*/
primary?: boolean;
/**
* Expose as tool for handoffs and composition
* @default false
*/
exposeAsTool?: boolean;
/**
* Description when used as tool (required if exposeAsTool)
*/
toolDescription?: string;
/**
* Agent-level default environment values
*/
env?: Record<string, string>;
/**
* Hook IDs that run when the active prompt declares no hooks
*/
hooks?: StandardAgentSpec.HookIds[];
/**
* Hooks this agent requires on a direct parent when composed as a child
*/
parentHooks?: ParentHookRequirement[];
packageName?: string;
version?: string;
author?: string;
license?: string;
}packageName, version, author, and license describe a packed agent
package. They are optional fields on AgentDefinition.
SideConfig Interface
type SideConfig = HumanSide | PromptSide | HandlerSide;
interface HumanSide {
/**
* Marks this side as human-backed
*/
human: true;
/**
* Display label for this side
*/
label?: string;
}
interface HandlerSide {
/**
* Handler to use (must be defined in agents/handlers/)
*/
handler: StandardAgentSpec.Handlers;
/**
* Display label for this side
*/
label?: string;
/**
* Child relationships owned by this side
*/
subagents?: SubagentRelationship[];
}
interface PromptSide {
/**
* Display label for this side
*/
label?: string;
/**
* Prompt to use (must be defined in agents/prompts/)
*/
prompt: StandardAgentSpec.Prompts;
/**
* Child relationships owned by this side
*/
subagents?: SubagentRelationship[];
/**
* Stop turn when AI returns text content
* @default true
*/
stopOnResponse?: boolean;
/**
* Stop turn when this tool is called
*/
stopTool?: StandardAgentSpec.Callables;
/**
* Optional property to extract from the stop tool result.
* Valid only when stopTool is set on this side.
*/
stopToolResponseProperty?: string;
/**
* Maximum model steps for this side in one execution, cumulative across its turns
*/
maxSteps?: number;
/**
* Tool that ends the session successfully
*/
sessionStop?: SessionToolBinding;
/**
* Tool that ends the session with a failure outcome
*/
sessionFail?: SessionToolBinding;
/**
* Tool that publishes a status update without ending the session
*/
sessionStatus?: SessionToolBinding;
}
interface SessionToolBinding {
/**
* Tool that controls the lifecycle action
*/
name: StandardAgentSpec.Callables;
/**
* Tool argument used as lifecycle message text
*/
messageProperty?: string;
/**
* Tool argument that contains attachment path(s)
*/
attachmentsProperty?: string;
}Complete Examples
Basic Support Agent
// agents/agents/support_agent.ts
import { defineAgent } from '@standardagents/spec';
export default defineAgent({
name: 'support_agent',
description: 'Handles customer inquiries and resolves support tickets',
icon: '/icons/support.svg',
sideA: {
label: 'Support',
prompt: 'customer_support',
stopOnResponse: true,
},
sideB: { human: true },
});Multi-Step Workflow Agent
// agents/agents/checkout_agent.ts
import { defineAgent } from '@standardagents/spec';
export default defineAgent({
name: 'checkout_agent',
sideA: {
label: 'Checkout Assistant',
prompt: 'checkout_flow',
stopOnResponse: false,
stopTool: 'confirm_order',
stopToolResponseProperty: 'orderId',
sessionStop: { name: 'complete_checkout' },
maxSteps: 30,
},
sideB: { human: true },
});Specialist Agent for Handoffs
// agents/agents/billing_specialist.ts
import { defineAgent } from '@standardagents/spec';
export default defineAgent({
name: 'billing_specialist',
description: 'Expert handling of billing inquiries, payment issues, and account balance questions',
icon: '/icons/billing.svg',
sideA: {
label: 'Billing Specialist',
prompt: 'billing_expert',
stopOnResponse: true,
sessionStop: { name: 'resolve_billing' },
},
sideB: { human: true },
exposeAsTool: true,
toolDescription: 'Expert handling of billing and payment issues',
});Prompt × prompt debate agent
// agents/agents/debate_agent.ts
import { defineAgent } from '@standardagents/spec';
export default defineAgent({
name: 'debate_agent',
maxSessionTurns: 20,
sideA: {
label: 'Pro',
prompt: 'debate_pro',
stopOnResponse: false,
maxSteps: 10,
},
sideB: {
label: 'Con',
prompt: 'debate_con',
stopOnResponse: false,
maxSteps: 10,
sessionStop: { name: 'conclude_debate' },
},
});Iterative Evaluation Agent
// agents/agents/evaluation_agent.ts
import { defineAgent } from '@standardagents/spec';
export default defineAgent({
name: 'evaluation_agent',
maxSessionTurns: 10,
sideA: {
label: 'Writer',
prompt: 'content_writer',
stopOnResponse: false,
maxSteps: 3,
},
sideB: {
label: 'Critic',
prompt: 'content_critic',
stopOnResponse: false,
maxSteps: 3,
sessionStop: { name: 'approve_content' },
},
});Execution Details
Message Role Encoding
In an agent with two prompt-backed sides, message roles encode side information:
| Side | Stored Role | From Own Perspective | From Other's Perspective |
|---|---|---|---|
| Side A | assistant |
assistant |
user |
| Side B | user |
assistant |
user |
How it works:
- Each AI sees its own messages as
assistantrole - Each AI sees the other's messages as
userrole - Stored roles remain consistent for replay
Execution Flow
Prompt × human execution
1. User sends message
↓
2. Side A (AI) executes turn
- Load prompt
- Build context
- Call LLM
- Execute tools (if any)
- Check stop condition
↓
3. Response returned to user
↓
4. Wait for next user message
↓
5. Repeat from step 2Prompt × prompt execution
1. Initial trigger
↓
2. Side A executes turn
- Load prompt
- Build context
- Call LLM
- Execute tools
- Check stop condition
↓
3. If not stopped:
Side B executes turn
- Same process
↓
4. If not stopped:
Return to step 2
↓
5. Continue until:
- Session ends (maxSessionTurns)
- sessionStop or sessionFail tool calledFile Structure
agents/
└── agents/
├── support_agent.ts # export default defineAgent({...})
├── billing_specialist.ts # export default defineAgent({...})
├── debate_agent.ts # export default defineAgent({...})
└── checkout_agent.ts # export default defineAgent({...})Requirements:
- One agent per file
- Default export required
- Use snake_case for file names
- No nested directories