Chapter 19

A Handoff Passes Proven Context, Not the Model's Opinion

A handoff to an operator is needed on conflicting policy, insufficient data, a repeated tool error, a high-cost decision, or a direct user request. And you must pass not "the model's opinion" but proven context: the handoff draft consists of verified facts and references to events, not of guesses and paraphrase.

TypeScript
type HandoffDraft = {
  id: string;
  category: "refund" | "delivery" | "policy" | "other";
  userQuestion: string;
  verifiedOrderId: string | null;
  verifiedFacts: Array<{ label: string; value: string }>;
  sourceIds: string[];
  toolErrors: string[];
};

async function handoffPrepare(raw: unknown, context: TrustedToolContext) {
  const input = handoffInput.parse(raw);
  return handoffs.createDraft({
    actorId: context.actorId,
    conversationId: input.conversation_id,
    category: input.category
  });
}

What to put in the draft and what not is a question of both security and respect for the operator.

Add · Don't add

  • The user's original question - Hidden reasoning
  • The verified order id - The assumption that the user is "committing fraud"
  • Sources and tool results - Raw personal fields
  • What was already tried and what error came up - Invented urgency

You add the original question, the verified order id, the sources and tool results, what was already tried and what error came up. You don't add hidden reasoning, the assumption "the user is committing fraud," raw personal fields, and invented urgency. The operator should get facts, not an interpretation.

And a caveat about the ticket itself.

Creating a ticket is also a side effect. If the product creates a ticket automatically, tell the user explicitly and ensure deduplication. For sensitive channels, first show the prepared handoff and get consent.

All parts of the example are ready separately. Let's assemble them into one runtime around a registry - and see how the contract, tools, and provider join up on the server.

Links