Human handoff
Let an agent pass a conversation to a person on your team, and take it back.
Give an agent the humanHandoff() tool and it can pass a conversation to your team. It tells the user a person will reply, and then stays quiet until you hand the conversation back.
import { Agent, humanHandoff, run } from 'cencori/agents';
const support = new Agent({
name: 'Support',
instructions: 'Help with orders. If the user asks for a person or is upset, hand off.',
tools: [
lookupOrder,
humanHandoff({
onHandoff: ({ sessionId, reason }) => slack.post(`#support needs you: ${reason} (${sessionId})`),
}),
],
});
const result = await run(support, 'I want to talk to a real person');
// result.finalOutput → "A person from the team will reply here shortly."While a person has it
While a person is handling the conversation, the agent doesn't run. The user's messages are saved for that person. run() returns withHuman: true and no answer:
const next = await run(support, 'Hello? Anyone there?', { sessionId });
next.withHuman; // true
next.finalOutput; // undefinedYour team reads the conversation and replies from your own tools: a dashboard, a Slack bot, or a script.
import { humanReply, humanStatus, releaseToAgent } from 'cencori/agents';
const status = await humanStatus(sessionId);
// { handledBy: 'human', handoff: { reason, at }, messages: [{ from: 'user', text, at }, ...] }
await humanReply(sessionId, 'Hi, this is Ada. Your refund is approved.');
await releaseToAgent(sessionId);If nobody replies for 30 minutes, the conversation goes back to the agent automatically.
Back with the agent
When the agent takes over again, it is told what was said while it was away. It carries on from there instead of starting over. In the example above, the user's next question gets an answer that already knows the refund was approved.
In Agent UI
<AgentChat> handles this for you:
- When the agent hands off, it shows a notice.
- It checks for your team's replies every few seconds.
- Team replies are labelled Team.
With a server route built using toAgentChatResponse, that's the whole setup.
Events
When stream: true, the run emits these events:
Options
The REST endpoints behind these functions are GET and POST /v1/sessions/:id/human. POST takes { action: 'handoff' | 'reply' | 'release' } and needs a secret key.

