An agent's run as it happens: steps on a rail, each step's tool calls inline, folding into a one-line summary when it settles. Built on Base UI Collapsible and the thinking-core Step model.
When to use
- Showing a multi-step agent (research, coding, booking) while it works, and a compact record afterwards.
When not to use
- A single tool call: use Tool Call Row.
- Reasoning text without steps: use Thinking Bar.
Installation
npx shadcn add @eased-pro/agent-runNeeds the @eased-pro registry and your Pro token in components.json. Create a token and copy the setup from your account .
Usage
import { AgentRun, type AgentRunStep } from "@/components/eased/agent-run/agent-run";const steps: AgentRunStep[] = [ { id: "plan", label: "Planning the comparison", status: "complete", startedAt, endedAt }, { id: "search", label: "Finding CRMs", status: "active", startedAt, tools: [{ id: "web", name: "web_search", label: "Searching", state: "running" }] },];<AgentRun steps={steps} label="Researching" />Update steps with updateStep / advanceSteps from thinking-core; they stamp startedAt and endedAt.
API reference
AgentRun
| Prop | Type | Default | Description |
|---|---|---|---|
steps | AgentRunStep[] | Step (id, label, status: StepStatus, detail, startedAt, endedAt) plus tools. | |
label | string | "Working" | The header while running. |
startedAt | number | first step | |
collapseWhenDone | boolean | true | Folds 900ms after the last step settles, unless the person toggled it. |
halted | boolean | false | The run stopped short with steps still pending (a failure it waits on, a Stop): the header settles, the clock freezes, and it reads "4 of 7 steps · 12s", counting the steps that ran (done or failed), not skipped ones. |
counts | boolean | true | Shows "3 of 7" (the current step's place on the rail) and the seconds in the header while running. Pass false inside a surface that already counts, such as Agent Run Console; the settled line still reads "7 steps · 45s". |
announce | boolean | true | The rail's own live region (the current step, then "Finished: 7 steps in 45s"). Pass false when the surface around it announces the run. |
selectedId / onSelectStep | string / (id) => void | With onSelectStep, each step's label is a button and the selected one is highlighted, for an inspector beside the rail. | |
open / onOpenChange | Controlled expansion. | ||
icon | ReactNode | E.g. a 16px Agent Orb. |
Each step's node and label carry data-step-node and data-step-label with the step's id, so a surface can morph its own list into the rail, as Plan → Execute Board does.
Strike
<Strike struck={cancelled}>{label}</Strike> draws a line through a string with a clip-path wipe, left to right, when struck turns on, and wipes it out to the right when it turns off. Struck on mount, it is simply drawn. The rail uses it for skipped steps; dim the text yourself.
Keyboard
| Key | Action |
|---|---|
Enter / Space on the header | Expands or folds the rail. |
Accessibility
- The rail is an ordered list with
aria-busywhile running; each step's status is spoken after its label. A status region announces the current step and the final summary.
Motion
Rows that stream in rise 4px once on the smooth spring; existing rows never re-animate. The active node breathes (1.6s), a settling node swaps on the snappy spring and its check draws, and the rail segment below it fills top-down on the smooth spring. A step that is skipped is struck through by a clip-path wipe, left to right in 300ms on the in-out curve. The header counts "3 of 7" and seconds on Number Roll, then text-swaps to "7 steps · 45s" as the rail folds on Auto Height. The settled total counts from startedAt when you pass it (else from the first step) and floors like the live counter, so the number lands without a jump. A run that is already settled when it mounts starts folded. Reduced motion: no breathing, fills and rises snap, fades only.
Notes for AI
Give every step a stable id and a human label; put tool calls on the step that made them. Let the component fold itself.
Do not render your own spinner or timer next to it: the header already counts.