Handoff Trail
Who handled a request, in order, and why each handover happened. The hardest question in a multi-agent system is “why did this agent answer”, and the answer is never in one agent’s transcript — it is in the chain.
src/components/ui/handoff-trail.tsxAbout
From the component's own source, where it was written.
Who handled a request, in order, and why each handover happened.
A multi-agent system's hardest question is "why did *this* agent answer" — and the answer is never in one agent's transcript, it is in the chain. Each hop records the agent that took over and the reason it was routed there, so a bad answer can be traced to the routing decision rather than blamed on the agent that happened to be holding it at the end.
**A handover to a person is a first-class hop, not an exception.** Escalation is the outcome an agent system is usually judged on, and burying it in a status field makes it invisible in the one view meant to explain the run.
The last hop is marked current. A trail whose final entry looks identical to the ones before it reads as finished when the run may still be live.
Install
npx astralyx-ui add handoff-trail
Usage
import { HandoffTrail } from '@/components/ui/handoff-trail'
<HandoffTrail handoffs={hops} live />Composer
Change the props and the source below follows.
Triage agent
09:41:02Entry point for every inbound message.
Billing operator
09:41:08Classified as a billing dispute with 0.91 confidence.
handled 4 turns
Refund specialist
Escalated on failure09:43:20Requested refund exceeded the billing agent’s £200 cap.
Dana Whitfield
HumanCurrent09:43:22No agent is permitted to approve over £200. Escalated for sign-off.
<HandoffTrail handoffs={hops} live={true} />Examples
Escalating to a person
<HandoffTrail handoffs={hops} live />One agent handled it
<HandoffTrail handoffs={[]} />API
Generated from the source, so it cannot fall behind the code. One table per exported component, then hooks, functions and types.
HandoffTrail
Who handled a request, in order, and why each handover happened.
Extends Omit<ComponentProps<'div'>, 'children'>.
| Prop | Type | Default | Description |
|---|---|---|---|
| handoffs | Handoff[] | required | { id, to, human?, reason?, at?, failed?, meta? }. reason is the point of the component — a chain without reasons explains nothing. |
| live | boolean | false | Marks the last hop as still holding the request, so a running trail does not read as finished. |
| currentLabel | string | 'Current' | — |
| humanLabel | string | 'Human' | — |
| failedLabel | string | 'Escalated on failure' | — |
| emptyLabel | string | 'No handoffs — one agent handled this.' | — |
| label | string | 'Handoff trail' | — |
Types
Handoff
| Field | Type | Description |
|---|---|---|
| id | string | — |
| to | string | Who took over. |
| human? | boolean | A handover to a person is a first-class hop, not a status field. Escalation is usually what the system is judged on. |
| reason? | ReactNode | Why it was routed here. The reason is the point of the component. |
| at? | string | Pre-formatted. This component does not own your locale. |
| failed? | boolean | Marks a handover caused by a failure rather than by routing. |
| meta? | ReactNode | Trailing slot — a duration, a confidence, a link to that leg's trace. |