> ## Documentation Index
> Fetch the complete documentation index at: https://docs.agentium.in/llms.txt
> Use this file to discover all available pages before exploring further.

# Agent Handoff

> Transfer conversations between specialist agents mid-conversation

# Agent Handoff

Agent Handoff enables seamless mid-conversation transfers between agents. When a user's request falls outside the current agent's expertise, it can transfer to a specialist agent while carrying over the full conversation context.

## Quick Start

```typescript theme={null}
import { Agent, openai } from "@agentium/core";

const billing = new Agent({
  name: "billing",
  model: openai("gpt-4o-mini"),
  instructions: "You are a billing specialist.",
});

const support = new Agent({
  name: "support",
  model: openai("gpt-4o-mini"),
  instructions: "You are a tech support agent.",
});

const frontDesk = new Agent({
  name: "front-desk",
  model: openai("gpt-4o"),
  instructions: "Route the user to the right specialist.",
  handoff: {
    targets: [
      { agent: billing, description: "Billing, invoices, payments" },
      { agent: support, description: "Technical issues, debugging" },
    ],
    maxHandoffs: 3,
  },
});

const result = await frontDesk.run("I need help with my invoice");
// Automatically transfers to billing agent
```

## How It Works

1. The agent receives a `transfer_to_agent` tool based on the configured targets
2. When the LLM decides a handoff is needed, it calls the tool with the target agent name
3. The `HandoffManager` catches the signal and transfers the conversation
4. The target agent receives the full conversation history and continues naturally

## Configuration

```typescript theme={null}
interface HandoffConfig {
  targets: HandoffTarget[];       // Specialist agents
  maxHandoffs?: number;           // Prevent infinite loops (default: 5)
  carryMessages?: boolean;        // Pass conversation history (default: true)
  carrySessionState?: boolean;    // Pass session state (default: true)
}

interface HandoffTarget {
  agent: Agent;
  description: string;            // LLM sees this to decide when to handoff
  onHandoff?: (ctx: RunContext) => Promise<void>;  // Hook before handoff
}
```

## Team Handoff Mode

For managed teams, use `TeamMode.Handoff`:

```typescript theme={null}
import { Agent, Team, TeamMode, openai } from "@agentium/core";

const billing = new Agent({
  name: "billing",
  model: openai("gpt-4o-mini"),
  instructions: `You are a billing specialist. Handle invoices, payments, and refunds.
When the issue is resolved, use the complete tool to end the conversation.`,
});

const support = new Agent({
  name: "technical-support",
  model: openai("gpt-4o-mini"),
  instructions: `You are a tech support agent. Troubleshoot software and hardware issues.
When the issue is resolved, use the complete tool to end the conversation.`,
});

const frontDesk = new Agent({
  name: "front-desk",
  model: openai("gpt-4o"),
  instructions: `You are the front desk receptionist. Understand the user's needs
and route them to the right specialist. Do not try to solve issues yourself.`,
});

const team = new Team({
  name: "support-team",
  mode: TeamMode.Handoff,
  model: openai("gpt-4o"),
  members: [frontDesk, billing, support],
  maxRounds: 5,
});

const result = await team.run("I got charged twice for my subscription");
console.log(result.text);
// The front-desk routes to billing, which handles the duplicate charge
console.log(result.handoffChain);
// ["front-desk", "billing"]
```

## Events

| Event              | Payload                                  |
| ------------------ | ---------------------------------------- |
| `handoff.transfer` | `{ runId, fromAgent, toAgent, reason }`  |
| `handoff.complete` | `{ runId, chain: string[], finalAgent }` |

***

## Cycle Detection

Agentium prevents infinite handoff loops automatically. If agent A hands off to agent B, which hands off back to A, the system detects the cycle and stops:

```typescript theme={null}
const agentA = new Agent({
  name: "agent-a",
  model: openai("gpt-4o-mini"),
  instructions: "Transfer to agent-b for everything.",
  handoff: {
    targets: [{ agent: agentB, description: "Agent B" }],
    maxHandoffs: 5,
  },
});

const agentB = new Agent({
  name: "agent-b",
  model: openai("gpt-4o-mini"),
  instructions: "Transfer to agent-a for everything.",
  handoff: {
    targets: [{ agent: agentA, description: "Agent A" }],
    maxHandoffs: 5,
  },
});

// This won't loop forever — stops after maxHandoffs is reached
const result = await agentA.run("Help me");
// result.handoffChain: ["agent-a", "agent-b", "agent-a", "agent-b", "agent-a"]
```

***

## onHandoff Callback

Use `onHandoff` to log, validate, or modify behavior when a handoff occurs:

```typescript theme={null}
const frontDesk = new Agent({
  name: "front-desk",
  model: openai("gpt-4o"),
  instructions: "Route users to the right agent.",
  handoff: {
    targets: [
      {
        agent: billing,
        description: "Billing and payment issues",
        onHandoff: async (ctx) => {
          console.log(`[Handoff] ${ctx.runId}: front-desk → billing`);
          // Log to analytics
          await analytics.track("agent_handoff", {
            from: "front-desk",
            to: "billing",
            sessionId: ctx.sessionId,
          });
        },
      },
      {
        agent: support,
        description: "Technical issues",
        onHandoff: async (ctx) => {
          console.log(`[Handoff] ${ctx.runId}: front-desk → support`);
        },
      },
    ],
  },
});
```

***

## The Complete Tool

Each agent in a handoff chain receives a `complete` tool. Calling it signals that the task is done and no further handoffs are needed:

```typescript theme={null}
const billing = new Agent({
  name: "billing",
  model: openai("gpt-4o-mini"),
  instructions: `You handle billing. When the issue is resolved,
call the complete tool with a summary of what was done.`,
  handoff: { targets: [] }, // No further handoff targets
});

// When the agent calls complete({ summary: "Refund of $12.99 issued" }),
// the handoff chain ends and the result is returned to the caller.
```

***

## Cross-References

* [Teams](/teams/overview) — Team orchestration modes (Handoff, Broadcast, Sequential)
* [Events](/webhooks/overview) — Listen for `handoff.transfer` and `handoff.complete` events
* [Observability](/observability/overview) — Trace handoff chains across agents
