or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

agents.mderrors.mdguardrails.mdhandoffs.mdindex.mdmcp.mdmodels.mdrealtime.mdsessions.mdtools.mdtracing.md
tile.json

handoffs.mddocs/

Handoff System

Transfer control between specialized agents during execution.

Capabilities

Handoff Function

Create handoffs to delegate work to other agents.

/**
 * Create a handoff to delegate to another agent
 * @param agent - Target agent to handoff to
 * @param options - Handoff configuration (inline options object, not a named export)
 * @returns Handoff instance
 */
function handoff<TContext, TOutput, TInputType>(
  agent: Agent<TContext, TOutput>,
  options?: {
    /** Override handoff tool name */
    toolNameOverride?: string;

    /** Override handoff tool description */
    toolDescriptionOverride?: string;

    /** Callback invoked when handoff occurs */
    onHandoff?: (
      context: RunContext<TContext>,
      input?: any
    ) => void | Promise<void>;

    /** Input schema for handoff parameters */
    inputType?: TInputType;

    /** Filter/transform handoff input data */
    inputFilter?: (input: HandoffInputData) => HandoffInputData;

    /** Conditionally enable/disable handoff */
    isEnabled?: boolean | ((options: {
      runContext: RunContext<TContext>;
      agent: Agent;
    }) => boolean | Promise<boolean>);
  }
): Handoff<TContext, TOutput>;

interface HandoffInputData {
  /** Handoff message/context */
  message?: string;

  /** Additional handoff parameters */
  [key: string]: any;
}

Usage Examples:

import { Agent, run, handoff } from '@openai/agents';

// Basic handoff
const specialist = new Agent({
  name: 'Specialist',
  instructions: 'You are a domain expert',
});

const generalist = new Agent({
  name: 'Generalist',
  instructions: 'You handle general queries and delegate to specialists',
  handoffs: [handoff(specialist)],
});

const result = await run(
  generalist,
  'I need expert help with a complex problem'
);

// Custom handoff description
const techSupport = new Agent({
  name: 'TechSupport',
  instructions: 'You provide technical support',
});

const mainAgent = new Agent({
  name: 'MainAgent',
  instructions: 'You are the main customer service agent',
  handoffs: [
    handoff(techSupport, {
      toolDescriptionOverride: 'Transfer to technical support for hardware or software issues',
    }),
  ],
});

// With handoff callback
const salesAgent = new Agent({
  name: 'SalesAgent',
  instructions: 'You handle sales inquiries',
});

const routerAgent = new Agent({
  name: 'Router',
  instructions: 'You route customers to the right department',
  handoffs: [
    handoff(salesAgent, {
      onHandoff: async (context, input) => {
        console.log(`Transferring to sales: ${input?.message}`);
        // Log handoff, update metrics, etc.
      },
    }),
  ],
});

// Conditional handoff
const premiumAgent = new Agent({
  name: 'PremiumSupport',
  instructions: 'You provide premium support',
});

const tieredAgent = new Agent({
  name: 'TieredSupport',
  instructions: 'You provide tiered customer support',
  handoffs: [
    handoff(premiumAgent, {
      isEnabled: ({ runContext }) => {
        // Only enable for premium customers
        return runContext.context.customerTier === 'premium';
      },
    }),
  ],
});

const result2 = await run(tieredAgent, 'I need help', {
  context: { customerTier: 'premium' },
});

// With input filtering
const filteredAgent = new Agent({
  name: 'FilteredAgent',
  instructions: 'You handle filtered requests',
});

const filteringAgent = new Agent({
  name: 'FilteringAgent',
  instructions: 'You route with filtering',
  handoffs: [
    handoff(filteredAgent, {
      inputFilter: (input) => {
        // Remove sensitive data before handoff
        const filtered = { ...input };
        delete filtered.ssn;
        delete filtered.creditCard;
        return filtered;
      },
    }),
  ],
});

Handoff Class

The Handoff class represents a delegation point to another agent.

/**
 * Handoff instance for delegating to another agent
 */
class Handoff<TContext = any, TOutput = any> {
  /** Handoff tool name */
  toolName: string;

  /** Handoff tool description */
  toolDescription: string;

  /** JSON schema for handoff input */
  inputJsonSchema: JsonObjectSchema;

  /** Target agent */
  agent: Agent<TContext, TOutput>;

  /**
   * Invoke the handoff
   * @param context - Run context
   * @param args - Handoff arguments
   * @returns Target agent
   */
  onInvokeHandoff(
    context: RunContext<TContext>,
    args?: any
  ): Agent<TContext, TOutput> | Promise<Agent<TContext, TOutput>>;

  /**
   * Get handoff as a function tool
   * @returns FunctionTool representing the handoff
   */
  getHandoffAsFunctionTool(): FunctionTool;
}

Usage Examples:

import { Agent, Handoff, handoff } from '@openai/agents';

const targetAgent = new Agent({
  name: 'Target',
  instructions: 'You are the target agent',
});

const handoffInstance = handoff(targetAgent, {
  toolNameOverride: 'transfer_to_target',
  toolDescriptionOverride: 'Transfer control to the target agent',
});

// Access handoff properties
console.log(handoffInstance.toolName); // 'transfer_to_target'
console.log(handoffInstance.toolDescription); // 'Transfer control to the target agent'
console.log(handoffInstance.agent.name); // 'Target'

// Convert to function tool
const tool = handoffInstance.getHandoffAsFunctionTool();

Helper Functions

Utility functions for working with handoffs.

/**
 * Get or create Handoff instance from agent or handoff
 * @param agentOrHandoff - Agent or existing Handoff
 * @returns Handoff instance
 */
function getHandoff(agentOrHandoff: Agent | Handoff): Handoff;

/**
 * Generate transfer message for handoff
 * @param agent - Agent being transferred to
 * @returns Transfer message text
 */
function getTransferMessage(agent: Agent): string;

Usage Examples:

import { Agent, handoff, getHandoff, getTransferMessage } from '@openai/agents';

const agent = new Agent({
  name: 'HelperAgent',
  instructions: 'You help with tasks',
});

// Get handoff from agent
const handoffInstance = getHandoff(agent);
console.log(handoffInstance.toolName); // 'transfer_to_HelperAgent'

// Get handoff from existing handoff
const customHandoff = handoff(agent, {
  toolNameOverride: 'custom_transfer',
});
const sameHandoff = getHandoff(customHandoff);
console.log(sameHandoff === customHandoff); // true

// Generate transfer message
const message = getTransferMessage(agent);
console.log(message); // 'Transferring to HelperAgent...'

Inline Handoffs

Define handoffs directly in the agent configuration without explicit handoff() calls.

interface AgentConfig {
  /** Sub-agents this agent can handoff to (agents or handoffs) */
  handoffs?: (Agent | Handoff)[];
}

Usage Examples:

import { Agent, run } from '@openai/agents';

// Define target agents
const techSupport = new Agent({
  name: 'TechSupport',
  handoffDescription: 'Technical support for hardware and software issues',
  instructions: 'You provide technical support',
});

const billing = new Agent({
  name: 'Billing',
  handoffDescription: 'Billing and payment inquiries',
  instructions: 'You handle billing questions',
});

const sales = new Agent({
  name: 'Sales',
  handoffDescription: 'Sales and product information',
  instructions: 'You help with sales inquiries',
});

// Router agent with inline handoffs
const router = new Agent({
  name: 'Router',
  instructions: 'You route customers to the appropriate department',
  handoffs: [techSupport, billing, sales], // Pass agents directly
});

const result = await run(
  router,
  'I have a question about my bill'
);

// Handoff descriptions are used automatically
// The model sees tools like:
// - transfer_to_TechSupport: "Technical support for hardware and software issues"
// - transfer_to_Billing: "Billing and payment inquiries"
// - transfer_to_Sales: "Sales and product information"

Agent.create() with Handoffs

Use Agent.create() for automatic output type inference when using handoffs.

/**
 * Create agent with automatic output type inference from handoffs
 * @param config - Agent configuration
 * @returns Agent with inferred output type
 */
static Agent.create<TOutput, Handoffs>(
  config: AgentConfig
): Agent<any, ResolvedAgentOutput<TOutput, Handoffs>>;

type ResolvedAgentOutput<TOutput, Handoffs> =
  | string  // Text output
  | TOutput  // Structured output
  | Agent;  // Handoff output (union of all handoff target agents)

Usage Examples:

import { Agent, run } from '@openai/agents';

const specialist1 = new Agent({
  name: 'Specialist1',
  instructions: 'You are specialist 1',
});

const specialist2 = new Agent({
  name: 'Specialist2',
  instructions: 'You are specialist 2',
});

// Use Agent.create() for proper type inference
const router = Agent.create({
  name: 'Router',
  instructions: 'You route to specialists',
  handoffs: [specialist1, specialist2],
});

const result = await run(router, 'I need specialist help');

// finalOutput is typed as: string | Agent
if (result.finalOutput instanceof Agent) {
  console.log(`Handed off to: ${result.finalOutput.name}`);
} else {
  console.log(`Response: ${result.finalOutput}`);
}

Multi-Level Handoffs

Create hierarchical agent structures with multiple handoff levels.

Usage Examples:

import { Agent, run } from '@openai/agents';

// Level 3: Specialists
const hardwareExpert = new Agent({
  name: 'HardwareExpert',
  handoffDescription: 'Expert in hardware issues',
  instructions: 'You are a hardware specialist',
});

const softwareExpert = new Agent({
  name: 'SoftwareExpert',
  handoffDescription: 'Expert in software issues',
  instructions: 'You are a software specialist',
});

// Level 2: Department agents
const techSupport = new Agent({
  name: 'TechSupport',
  handoffDescription: 'Technical support department',
  instructions: 'You provide technical support and can escalate to specialists',
  handoffs: [hardwareExpert, softwareExpert],
});

const customerService = new Agent({
  name: 'CustomerService',
  handoffDescription: 'Customer service department',
  instructions: 'You handle general customer inquiries',
});

// Level 1: Main router
const mainRouter = new Agent({
  name: 'MainRouter',
  instructions: 'You are the main customer service router',
  handoffs: [techSupport, customerService],
});

// The agent can handoff through multiple levels
const result = await run(
  mainRouter,
  'My laptop screen is flickering'
);

// Might handoff: MainRouter -> TechSupport -> HardwareExpert

Handoff with Structured Input

Define structured input schemas for handoffs.

Usage Examples:

import { z } from 'zod';
import { Agent, run, handoff } from '@openai/agents';

const orderProcessor = new Agent({
  name: 'OrderProcessor',
  instructions: 'You process customer orders',
});

const salesAgent = new Agent({
  name: 'SalesAgent',
  instructions: 'You handle sales inquiries and can process orders',
  handoffs: [
    handoff(orderProcessor, {
      inputType: z.object({
        orderId: z.string(),
        customerId: z.string(),
        priority: z.enum(['low', 'medium', 'high']),
        notes: z.string().optional(),
      }),
    }),
  ],
});

const result = await run(
  salesAgent,
  'I need to process order #12345 for customer C-9876 with high priority'
);

Handoff Input Filtering

Filter and transform data before handoffs occur.

Usage Examples:

import { Agent, run, handoff, Runner } from '@openai/agents';

const secureAgent = new Agent({
  name: 'SecureAgent',
  instructions: 'You handle sensitive data',
});

// Agent-level input filter
const mainAgent = new Agent({
  name: 'MainAgent',
  instructions: 'You process customer requests',
  handoffs: [
    handoff(secureAgent, {
      inputFilter: (input) => {
        // Remove PII before handoff
        return {
          ...input,
          ssn: '[REDACTED]',
          creditCard: '[REDACTED]',
        };
      },
    }),
  ],
});

// Global handoff input filter
const runner = new Runner({
  handoffInputFilter: (input) => {
    // Apply global filtering to all handoffs
    console.log('Handoff occurring:', input);
    return input;
  },
});

await runner.run(mainAgent, 'Process my information');

Handoff Detection

Access handoff information in RunResult.

Usage Examples:

import { Agent, run } from '@openai/agents';

const specialist = new Agent({
  name: 'Specialist',
  instructions: 'You are a specialist',
});

const router = new Agent({
  name: 'Router',
  instructions: 'You route to specialists',
  handoffs: [specialist],
});

const result = await run(router, 'I need specialist help');

// Check if handoff occurred
if (result.finalOutput instanceof Agent) {
  console.log(`Handed off to: ${result.finalOutput.name}`);
}

// Check last agent
console.log(`Last active agent: ${result.lastAgent?.name}`);

// Inspect run items for handoff calls
for (const item of result.newItems) {
  if (item instanceof RunHandoffCallItem) {
    console.log('Handoff invoked:', item.handoffCall.name);
  } else if (item instanceof RunHandoffOutputItem) {
    console.log('Handoff completed:', item.handoffOutput);
  }
}

Resuming After Handoff

Continue execution after a handoff by running the returned agent.

Usage Examples:

import { Agent, run } from '@openai/agents';

const specialist = new Agent({
  name: 'Specialist',
  instructions: 'You are a specialist. After helping, return to the router.',
  outputType: 'handoffs', // Indicate this agent can handoff back
});

const router = new Agent({
  name: 'Router',
  instructions: 'You route to specialists',
  handoffs: [specialist],
});

// Add bidirectional handoffs
specialist.handoffs = [router];

// Initial run
const result1 = await run(router, 'I need specialist help');

if (result1.finalOutput instanceof Agent) {
  console.log(`Handed off to: ${result1.finalOutput.name}`);

  // Continue conversation with the specialist
  const result2 = await run(
    result1.finalOutput,
    'Thank you, that helps!',
    { context: result1.state.runContext.context }
  );

  if (result2.finalOutput instanceof Agent) {
    console.log(`Handed back to: ${result2.finalOutput.name}`);
  }
}