Skip to main content
Agents can transfer conversations to other specialized agents when needed.

Quick Start

1

Simple Usage

2

With Configuration


User Interaction Flow


Configuration Levels


Generated tool name

Each handoff is exposed to the model as a tool named transfer_to_<lowercased_snake_of_agent_name>. The SDK sanitizes the agent name so any name — including spaces or punctuation — becomes a provider-legal tool name (capped at 64 characters). Pass a name: on handoff() to override it. This matters when you reference the tool in prompts or read it in logs.

Generated tool description

The tool’s description is what the model reads to decide when to hand off. When you don’t pass a description:, the SDK builds one from the target agent’s name, role, and goal.
Define role and goal on handoff targets and the main agent routes more accurately — no custom description: needed.

When to Transfer


Safety Features

Handoffs include built-in protections, all controllable per handoff:
  • Cycle detection: Prevents A → B → A loops. Toggle via detectCycles; enabled by default. Raises HandoffCycleError before touching the target.
  • Depth limits: Maximum 10 handoffs in a chain by default; override with maxDepth. Raises HandoffDepthError before touching the target.
  • Timeouts: Handoffs time out after 5 minutes by default; override with timeoutSeconds (in seconds; <= 0 disables). Raises HandoffTimeoutError (always retryable).
  • Concurrency: Each Handoff instance has its own concurrency semaphore (default 5); override with maxConcurrent. Not process-wide.
The cycle check runs before the depth check, and both run before anything is pushed onto the chain — a rejected handoff never consumes a chain slot.

Controlling context, safety, and concurrency

handoff() accepts eight settings that steer what the target sees and how the transfer behaves. Add them one at a time.
1

Steer what the target sees

Show the target only the last N messages instead of the summary default.
2

Cap tokens and keep the system prompt

Drop the oldest messages until the context fits a token budget, but never drop system messages.
3

Bound execution

Give the target a time limit and cap how many copies run at once.
4

Add safety guards

Refuse cycles and cap the chain depth.
5

Set everything at once

Combine all eight in one config object.

Which context policy should I pick?

contextPolicy decides how much history the target sees.

Inside a handoff

execute() runs a safety check, selects the context, seeds it onto the target, runs the target, then restores the target’s original history.

HandoffConfig reference

Every option lives on the HandoffConfig object passed to handoff() (or new Handoff({ agent, ... })).
Python’s HandoffConfig declares max_context_tokens and serialises it, but no code path reads it. TypeScript implements the documented meaning, so maxContextTokens actually caps tokens here. If you port behavioural tests from Python, expect this one divergence.

Chain threading

The handoff chain tracks the agents already traversed to reach the current point. HandoffContext carries it on an optional field:
Leave it unset and the chain in force for the current execution is used — what nested handoffs want. Set it to resume a chain that crossed a process boundary (a queued handoff, a rehydrated request) or to start one deliberately deep. execute() writes the extended chain back onto the result’s context, so passing result.context onward threads it by hand. The handoff module exposes helpers for advanced flows: currentHandoffChain() reads the chain in scope (oldest first), currentHandoffDepth() its length, runWithHandoffChain(chain, fn) runs fn under a given chain, selectHandoffMessages(messages, config) is the standalone context selector, and resetHandoffChain() clears the module-level fallback used where AsyncLocalStorage is unavailable (Node 18, browsers). parallelHandoffs snapshots the current chain once and passes each sibling an explicit copy, so fan-out never reads the ambient value — each sibling gets an isolated chain, and cycle detection and maxDepth are enforced per task, not shared across siblings.

API Reference

Agent Module

Agent module with handoff support

AgentConfig

Agent configuration

Best Practices

Each agent should handle one domain well. This makes handoffs accurate.
Set role and goal on each handoff target. They are woven into the auto-generated handoff description (Transfer task to <name> (<role>) - <goal>), so the main agent routes more accurately without a custom description:.
The main agent uses descriptions to decide when to transfer.
Let users know they’re being connected to a specialist.

Tool-Boundary Policy & Parallel Handoffs

The TypeScript handoff stack matches the Python surface: a HandoffToolPolicy controls which tools survive a transfer, and parallel_handoffs fans a source agent’s work out across several specialists at once.
resolveHandoffToolPolicy and HandoffToolPolicyMode enforce the tool boundary during a transfer — decide whether the target keeps the source’s tools, only its own, or a filtered set. DEFAULT_HANDOFF_TOOL_POLICY is applied when you don’t pass one.
A HandoffTimeoutError sets isRetryable === true unconditionally — a timeout may succeed on a second attempt. Cycle and depth failures raise HandoffCycleError (carrying context.cycle_path) and HandoffDepthError (carrying context.max_depth / context.current_depth).
The handoff context accepts the legacy aliases chain, depth, and max_depth so older Python-shaped snippets keep working without a rewrite.

Agent

Create AI agents

Teams

Multi-agent teams