Skip to content

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

namestringrequired

Unique 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 references
titlestring

Human-readable title. Deprecated in favor of name.

title: 'Customer Support Agent'  // Optional display name
maxSessionTurnsnumber

Maximum 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 session
descriptionstring

Brief description of what this agent does. Useful for UIs and documentation.

description: 'Handles customer inquiries and resolves support tickets'
iconstring

Icon 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.

sideASideConfigrequired

Configuration for side A. Messages from side A are stored with the assistant role.

sideA: {
  label: 'Support Agent',
  prompt: 'customer_support',
  stopOnResponse: true,
  maxSteps: 25,
}
sideBSideConfigrequired

Configuration 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: false

Whether 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: true
toolDescriptionstring

Description 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: true

Whether 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: false

SideConfig

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

humantrue

Marks the side as human-backed. A person takes this side's turns.

sideB: { human: true }
promptstring

Reference to a prompt defined in agents/prompts/. A model takes this side's turns.

prompt: 'customer_support'  // Must exist in prompts/
handlerstring

Reference 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 }]
labelstring

Human-readable label displayed in UI and logs.

label: 'Support Agent'
stopOnResponsebooleandefault: true

When 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 no stopTool, the runtime switches sides after each step.
stopOnResponse: true  // Stop after each response
stopToolstring

Tool name that ends the turn when called.

A stopTool without stopToolResponseProperty ends the turn. It extracts no value.

stopTool: 'complete_checkout'
stopToolResponseProperty: 'orderId'
stopToolResponsePropertystring

Property 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.orderId
maxStepsnumber

maxSteps 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 execution
sessionStopSessionToolBinding

Tool 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' }
sessionFailSessionToolBinding

Tool that ends the session with a failure outcome when called.

sessionFail: { name: 'report_failure', messageProperty: 'reason' }
sessionStatusSessionToolBinding

Tool 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

  1. A successful sessionFail call ends the session with a failure outcome.
  2. A successful sessionStop call ends the session.
  3. stopTool ends the current prompt-backed turn.
  4. stopOnResponse ends 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 assistant role
  • Each AI sees the other's messages as user role
  • 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 2

Prompt × 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 called

File 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