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:
- Suspends the current agent run
- Emits a
handoff_startedevent with the target agent name - Returns
{ handoff }fromagent.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}`);
}
});