affectUI › Components › Agents
Agent Fleet
Several agents side by side – each card sums up its sub-agents, and a click opens its tree to the right while the last one folds back into its card.
Part of affectUI Pro. Install: npx shadcn@latest add https://www.affectui.com/r/agent-fleet.json · Import from @/components/ui/agent-fleet
When to use
- Several agents working at the same time – a launch, the support queue, a release – each with sub-agents of its own
- Seeing at a glance which agent needs you, which one failed and what the whole fleet costs – without every tree open at once
- A fleet overview on a dashboard: one tree open, the others folded to a single card
When not to use
- One run with one orchestrator – use Agent Tree
- When things happened, in time order – use Agent Lanes
- Handing tasks to agents yourself – use Delegate Board
Usage
import { AgentFleet, type FleetAgent } from "@/components/ui/agent-fleet";
const fleet: FleetAgent[] = [
{
id: "launch", name: "Launch lead", model: "Opus 5.5", task: "Launch the new pricing page",
status: "running", cost: 0.42, started: Date.now() - 840_000,
children: [
{ id: "research", name: "Researcher", task: "Compare five competitors", status: "done", cost: 0.31, started: Date.now() - 780_000, ended: Date.now() - 360_000 },
{ id: "copy", name: "Writer", task: "Write the plan copy", status: "waiting", cost: 0.18, started: Date.now() - 720_000 },
],
},
{ id: "support", name: "Support lead", task: "Answer the open tickets", status: "running", cost: 0.12, started: Date.now() - 1_560_000 },
];
export function Fleet() {
return <AgentFleet agents={fleet} onSelect={(agent) => openLog(agent.id)} />;
}Props
| Name | Type | Default | Description |
|---|---|---|---|
agents | FleetAgent[] | { id, name, task, model?, avatar?, tools?, status, steps?, cost?, started?, ended?, children? } – the main agents, each with its sub-agents; pass them again whenever something changes; tools are small marks shown as overlapping tiles before the task | |
steps | FleetStep[] | Per sub-agent, { title, status?, tool?, took? } – the tasks it worked through; a click on its card opens them in a small window | |
status | "queued" | "running" | "waiting" | "done" | "failed" | "stopped" | Per agent – "waiting" reads "Needs you"; queued and stopped sub-agents and their connectors are muted | |
cost | number | Per agent, its own cost – a main agent shows its whole branch, the bar the whole fleet | |
open / defaultOpen | string | null | the first agent | Id of the main agent whose tree is open, controlled or uncontrolled – null folds them all |
onOpenChange | (id: string | null) => void | An agent was opened – or folded with a second click | |
ask / error | string | Per sub-agent – what a waiting one needs you to decide, and what went wrong for a failed one; shown at the top of its window above Approve and Reject, or Retry and Dismiss | |
onResolve | (agent, action: "approve" | "reject" | "retry" | "dismiss") => void | An answer for a sub-agent that needs you or failed – update its status in your data; without it, those cards get no Answer or Resolve button | |
onSelect | (agent: FleetAgent) => void | A click on a sub-agent – makes the sub-agent cards clickable; one with steps opens its tasks window as well | |
now | number | Date.now() | The clock for running times – without it the cards tick every second while an agent runs |
formatCost | (amount: number) => string | dollars, e.g. "$0.42" | Formats every cost |
title | string | "Agents" | Heading of the bar |
className | string | Extra class on the root |
Keyboard
- ↑ / ↓: Moves between the main agents
- Home / End: Jumps to the first or the last main agent
- Enter / Space: On a main agent: opens its tree, or folds it when it's open – on a sub-agent: opens or closes its tasks window
- Tab: Moves on into the open tree's cards – those with steps, an answer to give or onSelect; in an answer window, Approve or Retry has focus and Tab reaches the other
- Esc: Closes the tasks window and puts focus back on its card
Accessibility
- Every main agent is a button with aria-expanded, pointing at the open tree with aria-controls
- The open tree is a tree labelled "Sub-agents of …"; every sub-agent is a treeitem labelled with its name and status
- The totals in the bar – agents, running, needs you, cost – are announced politely when they change
- Status is never color alone: every card spells it out, and the phrase under each main agent says what the ring shows
- The small Answer or Resolve button on a card is a pointer shortcut – keyboard users open the same window from the card itself
- A sub-agent with steps, or one that needs you or failed, has aria-haspopup and aria-expanded; its tasks window is a dialog labelled "Tasks of …", closed by Esc or a click elsewhere
Responsive
- Wide enough for the column, a gap and the deepest tree, the tree opens to the right of the column
- Narrower, the tree opens under its card and pushes the agents below down – and folds back up before the next one opens
- Every card has a fixed height – one line of task, one line of numbers – so nothing jumps when a status changes
- Dark mode built in: switches when an ancestor (usually <html>) has the class dark or data-theme="dark"
Requirements
- React 19
Related
- Agent Tree – Who started whom – an orchestrator and its sub-agents as a live tree, with status, cost and time on every card, costs rolling up each branch, and branches that fold to one line
- Agent Lanes – Where every agent is right now – a live timeline with a lane per agent, tasks growing into “now”, hand-offs drawn between agents and approvals right on the block
- Agent Monitor – One agent at work, as a card – its current step with a clock and the bill so far, a time strip and the finished steps with the logo of each tool, a click on any of them for the details; the exact command when it needs your OK; folds into a small bar with the tools it has used
- Delegate Board – Hand work to your agents by dragging it onto them – each agent previews its new “busy until” time, or switch to Auto and every new task goes to whoever is free first