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

> **Info:**
>
> Agents are auto-discovered from the `agents/agents/` directory. Each file should export a default agent definition using `defineAgent()`.

## defineAgent

The `defineAgent` function creates an agent configuration with full TypeScript support.

### Function Signature

```typescript
function defineAgent<N extends string>(
  config: AgentDefinition<N>
): AgentDefinition<N>
```

### AgentDefinition Type

```typescript
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

- name (string), required

  Unique identifier for this agent. Use snake_case.

  **Naming conventions:**

  - Use descriptive names: `customer_support`, `billing_specialist`
  - Avoid: `agent1`, `bot`, `helper`

  ```typescript
  name: 'support_agent'  // Used in API calls and references
  ```

* title (string)

  Human-readable title. Deprecated in favor of `name`.

  ```typescript
  title: 'Customer Support Agent'  // Optional display name
  ```

- maxSessionTurns (number)

  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.

  ```typescript
  maxSessionTurns: 20  // Max 20 exchanges in one session
  ```

* description (string)

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

  ```typescript
  description: 'Handles customer inquiries and resolves support tickets'
  ```

- icon (string)

  Icon URL or absolute path for the agent. Absolute paths (starting with `/`) are converted to full URLs in API responses.

  ```typescript
  // 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.

- sideA (SideConfig), required

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

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

* sideB (SideConfig), required

  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:

  ```typescript
  sideB: { human: true }
  ```

  A second model can take side B's turns:

  ```typescript
  sideB: {
    label: 'Con',
    prompt: 'debate_con',
    stopOnResponse: false,
    maxSteps: 10,
  }
  ```

### Tool Exposure

- exposeAsTool (boolean), default: 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.

  ```typescript
  exposeAsTool: true
  ```

* toolDescription (string)

  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.

  ```typescript
  toolDescription: 'Hand off billing and payment issues to a specialist with deep knowledge of payment systems'
  ```

- primary (boolean), default: 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.

  ```typescript
  primary: false
  ```

## SideConfig

Configuration for individual sides (A or B).

### SideConfig Type

```typescript
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

- human (true)

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

  ```typescript
  sideB: { human: true }
  ```

* prompt (string)

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

  ```typescript
  prompt: 'customer_support'  // Must exist in prompts/
  ```

- handler (string)

  Reference to a handler defined in `agents/handlers/`. Deterministic code takes this side's turns.

  ```typescript
  handler: 'relay_pipe'  // Must exist in handlers/
  ```

* subagents (SubagentRelationship\[])

  Child relationships that this side owns. Prompt-backed and handler-backed sides can declare them. See [Subagents](/docs/core-concepts/subagents).

  ```typescript
  subagents: [{ agent: 'topdown_asset_subagent', blocking: true }]
  ```

- label (string)

  Human-readable label displayed in UI and logs.

  ```typescript
  label: 'Support Agent'
  ```

* stopOnResponse (boolean), default: 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.

  ```typescript
  stopOnResponse: true  // Stop after each response
  ```

- stopTool (string)

  Tool name that ends the turn when called.

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

  ```typescript
  stopTool: 'complete_checkout'
  stopToolResponseProperty: 'orderId'
  ```

* stopToolResponseProperty (string)

  Property to extract from stop tool result.

  This property is optional. It is valid only when `stopTool` is set on the same side.

  ```typescript
  stopToolResponseProperty: 'orderId'  // Extract result.orderId
  ```

- maxSteps (number)

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

  ```typescript
  maxSteps: 5  // Max 5 model steps for this side in one execution
  ```

* sessionStop (SessionToolBinding)

  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.

  ```typescript
  sessionStop: { name: 'close_ticket', messageProperty: 'summary' }
  ```

- sessionFail (SessionToolBinding)

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

  ```typescript
  sessionFail: { name: 'report_failure', messageProperty: 'reason' }
  ```

* sessionStatus (SessionToolBinding)

  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.

  ```typescript
  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:

```typescript
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:

```typescript
// 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:

```typescript
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

```typescript
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

```typescript
// 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

```typescript
// 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

```typescript
// 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

```typescript
// 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

```typescript
// 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

## Related Documentation

- [Core Concepts](/docs/core-concepts/agents)

  Learn agent fundamentals

* [Prompts API](/docs/api-reference/prompts)

  Configure prompts for agents

- [Tools API](/docs/api-reference/tools)

  Create tools for agents

* [Hooks API](/docs/api-reference/hooks)

  Intercept agent execution
