Designing Agent Handoffs That Preserve Context

The specialist agent did everything right, and still refunded the wrong order.
Triage had spent six turns establishing which of two orders the customer meant: the one with the damaged item, not the duplicate charge on the other. Then it handed off. The refund agent opened the conversation, saw two orders and two complaints, and picked the more recent one. Nothing crashed. The trace looked clean. The context that mattered was in the transcript the whole time; it just was not marked as settled, so the second agent settled it again, differently.
That is the thing about handoffs. We talk about them as plumbing, control passing from one agent to another, but the transfer is where the work's memory gets edited. Whatever the receiving agent believes about what has already been decided, what is still open, and what it is allowed to touch was chosen at the moment of transfer, usually by a framework default nobody picked.
The two defaults both lose something
Most frameworks hand you one of two defaults, and they are opposite failures.
The first is pass everything. In the OpenAI Agents SDK, a handoff shows up to the model as a tool, transfer_to_refund_agent and the like, and when it fires the receiving agent takes over the conversation and sees the entire previous history unless you intervene. Everything survives: the dead ends, the tool output the specialist never needed, the customer's earlier and since-corrected version of their own story. The receiver's first job becomes archaeology, digging through a transcript written for a different agent and guessing which parts hold weight. Sometimes it guesses wrong, as above. It also costs money in a quiet way. Every token of that history gets re-read at the receiver's rates, and a chain of three handoffs re-reads it three times.
The second default is pass a summary. A lead agent writes a short brief, or triage passes a sentence of prose. Compact, fast, and it quietly deletes the difference between what was verified and what was merely said. "Customer reports a damaged item" and "the damage was confirmed from the courier's photo" compress into the same sentence. The receiver cannot tell which claims it may rely on and which it should check again, so it either checks everything (and the user answers the same questions twice) or trusts everything (and acts on a guess).
Anthropic learned this from the delegation side. In their write-up of the Research system, the lead agent started out giving subagents short instructions like "research the semiconductor shortage," and got duplicated work and wrong decades back: one subagent researched the 2021 automotive chip crisis while two others worked on 2025 supply chains. The fix they describe is that each subagent needs an objective, an output format, guidance on tools and sources, and clear task boundaries. Four things a vague brief had left to chance.
A handoff packet is the middle path. Not the transcript, not a summary: a structured transfer object, fields rather than prose, assembled at the moment of transfer and reviewed like any other interface between two components that do not share a memory. The rest of this post is about what belongs in it.

What the packet carries
Five kinds of content. The order matters less than keeping them separate from each other.
The task itself comes first: the objective in a sentence or two, what a finished result looks like, and the boundary, meaning what this agent should not attempt and who it hands back to when it reaches that edge. This is the part Anthropic's delegation lesson is about. Vagueness here does not stay local. The receiver fills it in.
Second, decisions already made, stated as decisions. Not "the customer mentioned order A and order B" but "order in scope: B. Order A is excluded; its duplicate charge was already resolved." Every settled question the receiver might otherwise reopen belongs here with its answer, because reopening settled questions is how users end up repeating themselves and how two agents end up contradicting each other in the same thread.
Third, verified facts with their sources attached: the order status as read from the order system at a stated time, the amount as read from the payment record. Unverified claims travel too, but labelled as claims, in their own field. The packet does not have to make everything true. It has to stop true and untrue things arriving with the same confidence.
Fourth, state the receiver needs but should not have to reconstruct: identifiers, and handles to artifacts rather than the artifacts pasted in full. Anthropic's team makes the same move for a related reason. Their subagents store outputs in external systems and pass lightweight references back to the coordinator, so a large result is not copied, and recopied, through conversation history. A reference can be fetched fresh. A pasted copy is already stale.
Fifth, the constraints that bind the receiver: budget left in the run, which tools it may and may not use, approvals already granted and, just as important, approvals already refused. A refusal that does not survive the handoff is not a control. It is a delay before the next agent asks again, or worse, acts.

The filter is a design decision
The mechanisms differ, but the underlying fact does not. In the SDK above, what the receiver sees is shaped by an input filter: a function that receives the history and returns what the next agent gets, with a ready-made filter that removes tool-call items from the history for receivers who do not need them. In LangGraph, a handoff is carried by a Command that holds both the destination and a state update, so what travels is whatever the sending node puts in that update. Either way, somebody decides what crosses. The only question is whether that decision was made in a design review or inherited from a default.
Two details from the SDK's own documentation are worth designing around. The small structured payload a model can attach when it calls a handoff, a reason or a priority, is metadata for logging and callbacks. It does not replace the receiving agent's input, and treating it as the packet leaves the actual history unmanaged. And summarised history is not redacted history: the documentation notes that its nested-history summaries can still contain tool arguments and results, so a receiver who must not see certain data needs an explicit filter that removes the data, not a summary that might keep it.
So write the filter per handoff, not per system. The refund agent needs the decision and the payment record; it does not need the six turns of troubleshooting that came before them. The agent drafting the customer email needs the outcome and the tone of the thread; it does not need the raw tool output. Each receiver gets the smallest history that still lets it work without re-asking questions the user already answered.
Where this stops helping
A packet cannot fix a bad split of responsibilities. If two agents both believe they own the refund, a well-built transfer object just documents the collision more precisely. Packets also carry a maintenance cost that compounds. Every field is a promise between two agents, and when either side's job changes, the packet has to change with it. A stale field is worse than a missing one, because the receiver trusts it.
There is a class of work where the transcript genuinely is the substance: a negotiation, a support conversation where tone and exact wording carry meaning the user will hold you to. Compress that into fields and you have deleted the evidence. For those, pass the history and accept the archaeology cost. The packet's job shrinks to marking the decisions inside it.
And a packet is only as honest as whoever fills it in. If the sender records a claim as verified when it only heard the claim, the packet launders the error, gives it a field and a timestamp and a confident format. The check that matters sits where the packet is built: verified means a tool returned it, and the packet names the tool and the time. Everything else travels as a claim.
None of this needs a new framework. It needs the handoffs you already have treated as interfaces, designed field by field, the way you would design any contract between two parts of a system that do not share a memory. Because they do not. The packet is the memory.
Work with us
If your agents hand work to each other and users keep repeating themselves, or specialists keep re-deciding questions that were settled upstream, the seam is the place to look, and it is fixable handoff by handoff. Bring the workflow where it happens most through Trida AI's intake, and we will map what should cross each boundary with you.
