67 / 122 · Agents
Handoff
Control passing between agents, with the reason and what came along.
TriageMaintainer
Triage reproduced the report and narrowed it to the converter, so the fix goes to the agent that can write and verify a patch.
carried overThe failing test and its outputThe two files the reader already narrowed to
Installation
npx shadcn@latest add "@assistant-ui/elements-agent-handoff"
Usage
import { AgentHandoff } from "@/components/elements/agent-handoff";
<AgentHandoff from="Triage" to="Maintainer" reason={reason} carried={carried} settled />Props
from*stringAgent giving up control.
to*stringAgent taking it.
reason*stringWhy the baton moved. Without it a handoff reads as an unexplained jump.
carried*string[]Context that travelled across. Pass an empty array to hide the section.
settled*booleanCools the arrow and the receiving badge once the new agent is underway.
classNamestringExtra classes merged onto the root.
Source
agent-handoff.tsx"use client";
import { ArrowRightIcon, BotIcon } from "lucide-react";
import { cn } from "@/lib/utils";
import { field, mono } from "./surfaces";
export function AgentHandoff({
from,
to,
reason,
carried,
settled,
className,
}: {
from: string;
to: string;
reason: string;
carried: readonly string[];
settled: boolean;
className?: string;
}) {
return (
<div className={cn("flex w-full max-w-sm flex-col gap-2", className)}>
<div className="flex items-center gap-2">
<span
className={cn(
field,
"text-foreground/45 flex items-center gap-1.5 rounded-full px-2.5 py-1 text-xs transition-opacity duration-500",
settled && "opacity-45",
)}
>
<BotIcon className="size-3" />
{from}
</span>
<ArrowRightIcon
className={cn(
"size-3.5 shrink-0 transition-colors duration-500",
settled ? "text-foreground/25" : "text-blue-500 dark:text-blue-400",
)}
/>
<span
className={cn(
"flex items-center gap-1.5 rounded-full px-2.5 py-1 text-xs transition-colors duration-500",
settled
? cn(field, "text-foreground/80")
: "bg-blue-500/12 text-blue-700 dark:bg-blue-400/15 dark:text-blue-300",
)}
>
<BotIcon className="size-3" />
{to}
</span>
</div>
<p className="text-foreground/55 text-xs leading-relaxed">{reason}</p>
{carried.length > 0 && (
<div className="flex flex-col gap-1">
<span className={cn(mono, "text-foreground/30")}>carried over</span>
{carried.map((item) => (
<span
key={item}
className="text-foreground/60 border-foreground/12 border-s ps-2.5 text-xs leading-relaxed"
>
{item}
</span>
))}
</div>
)}
</div>
);
}