Skip to main content

Handoffs

Handoffs allow one agent to delegate a task to another specialized agent while preserving full context.

How Handoffs Work

When a tool returns a Handoff object, the SDK:

  1. Suspends the current agent run
  2. Emits a handoff_started event with the target agent name
  3. Returns { handoff } from agent.run() for you to act on

Basic Handoff

import { Agent, Tool, Handoff, DeepSeekProvider, InMemoryProvider } from 'gravity-sdk';
import { z } from 'zod';

const transferTool = new Tool({
name: 'transferToSupport',
description: 'Transfer angry or frustrated users to the support agent',
schema: z.object({ reason: z.string() }),
execute: ({ reason }) => new Handoff('SupportAgent', { reason }),
});

const triageAgent = new Agent({
name: 'TriageBot',
instructions: 'If users are upset or need help, use transferToSupport immediately.',
model: new DeepSeekProvider({ model: 'deepseek-v4-flash' }),
memory: new InMemoryProvider(),
tools: [transferTool],
});

const res = await triageAgent.run('I am very angry!', 'session-1');

if (res.handoff) {
console.log(`Handed off to: ${res.handoff.targetAgent}`);
console.log(`Payload:`, res.handoff.payload);
// → Handed off to: SupportAgent
// → Payload: { reason: 'User is angry' }
}

Chaining Agents

Use the handoff result to run the next agent:

const agents: Record<string, Agent> = {
TriageBot: triageAgent,
SupportAgent: supportAgent,
};

let sessionId = 'user-123';
let currentAgent = agents['TriageBot'];
let input = 'I need urgent help!';

while (true) {
const res = await currentAgent.run(input, sessionId);
if (res.handoff) {
currentAgent = agents[res.handoff.targetAgent];
input = res.handoff.payload?.reason || 'Continuing conversation';
} else {
console.log('Final answer:', res.output);
break;
}
}

Loop Prevention

Gravity tracks handoff depth automatically. If an agent tries to hand off to itself or creates a cycle, the run will stop and throw a clear error rather than spinning indefinitely.

Events

emitter.on('handoff_started', (e) => {
if (e.type === 'handoff_started') {
console.log(`Handoff → ${e.targetAgent} | run: ${e.runId}`);
}
});