Agent timeline
ProA compact timeline of one orchestrated agent run: the orchestrator and its subagents share a time axis, handoffs draw in between rows, and a playhead replays thinking, tool calls and waiting while the header counts time, tokens and cost. Hover a span for what it did, or click it to trace its handoff path.
Checkout conversion drop on Oct 14
RunningPausedReady 0.0s 0 tokens $0.00, 1 agents working
- Showing how an orchestrator split work across subagents and how long each part took, in a run history, a trace viewer or a debugging panel.
- Replaying a finished agent run in a product demo or changelog, with real numbers for time, tokens and cost.
- Following a multi-agent run live as events stream in from your backend.
- Use agent-orchestrator for a full screen of agent lanes with streaming text and messages; this timeline is the compact, reusable view of timing.
- Use observability-trace for distributed request tracing across services rather than agents.
- Use agent-run or research-run when people need the agent's steps, sources and answer, not the timing.
- Use tool-call for the detail of a single tool invocation, and agent-status for one agent's current state.
Installation
Pro source and install commands unlock with a Pro plan.
import { AgentTimeline } from "@/components/arc/agent-timeline/agent-timeline"; // The sample run, replaying.<AgentTimeline /> // A finished run from your traces renders complete, ready to replay.<AgentTimeline run={{ title: "Weekly churn report", agents: [ { id: "lead", name: "Orchestrator", model: "Claude Opus 5.5", price: 8 }, { id: "sql", name: "Analyst", model: "Claude Haiku 5.5", price: 0.2 }, ], spans: [ { agentId: "lead", kind: "thinking", start: 0, end: 1.4, label: "Planned the report", tokens: 1200 }, { agentId: "lead", kind: "waiting", start: 1.4, end: 6.2, label: "Waiting on the analyst" }, { agentId: "sql", kind: "tool", tool: "PostgreSQL", start: 1.7, end: 4.1, label: "Pulled churn by plan", tokens: 2400 }, { agentId: "sql", kind: "thinking", start: 4.1, end: 5.9, label: "Summarized the numbers", tokens: 900 }, ], handoffs: [ { from: "lead", to: "sql", at: 1.4, label: "Churn by plan, last 8 weeks" }, { from: "sql", to: "lead", at: 6.2, label: "Churn is up on the Team plan" }, ], }} onSpanSelect={span => console.log(span?.label)}/> // Live: keep appending events to the run, leave end off open spans.<AgentTimeline run={liveRun} live={status === "running"} />AgentTimeline
A compact timeline of one orchestrated agent run. Each row is an agent with a color dot, its name and its model with the provider mark, the orchestrator on top. Spans on a shared time axis show thinking (a tint of the agent color), tool calls (solid) and waiting (hatched), and thin elbow connectors show work handed to a subagent and results coming back, with a dot where they meet the orchestrator. The run replays live: a playhead sweeps, the active span grows with a square leading edge and a short shine, connectors draw in as handoffs happen, and the header counts elapsed time, tokens and cost until it settles into a summary such as "5 agents · 26.4s · 1.5x faster than sequential · $0.16". Hover or focus a span for a card with what it did, its tool, duration, tokens and handoffs; click to pin it and dim everything off its handoff path. A failed tool call turns into a cross with a Failed label when it ends, and its retry shows a retry icon. Play, pause, restart, a speed control and scrubbing along the axis drive the replay; passing a finished run renders it complete, and live mode follows a run in progress.
runAgentTimelineRunsampleRunThe run: { title?, agents, spans, handoffs?, duration?, startedAt? }. Rows follow agents in order and the first agent is the orchestrator. Defaults to a sample incident investigation with five agents, a failed query and a retry.agentsAgentTimelineAgent[]–Overrides run.agents. Each agent is { id, name, model?, role?, color?, mark?, price? }. color defaults to neutral for the first agent and --series-1 to --series-4 after it; mark is inferred from the model name for Claude, GPT and Gemini; price is a blended USD rate per million tokens used for cost.spansAgentTimelineSpan[]–Overrides run.spans. Each span is { id?, agentId, kind: "thinking" | "tool" | "waiting", start, end?, label, detail?, tool?, tokens?, cost?, status?: "ok" | "failed", retry? } with times in seconds from the start of the run. tool is a name (GitHub, Web search, PostgreSQL and Notion have built-in marks) or { name, mark }. Leave end out while a span is open in a live run.handoffsAgentTimelineHandoff[]–Overrides run.handoffs. Each handoff is { from, to, at, label? }. The connector shape is inferred: a dispatch drops from the source at `at` and enters the target's next span, a result leaves the end of the source's last span and rises into the target.titlestring–Heading. Defaults to run.title, then "Agent run".autoPlaybooleantrue for the sample, false with dataReplay from zero when the timeline scrolls into view. Without it a run renders finished, ready to replay. Reduced motion always starts finished until the person presses play.speednumber1Starting playback speed. 1 is real time.speedsnumber[][1, 2]Options in the speed control. The control hides when only one speed remains.livebooleanfalseFollow a run in progress: the clock runs from run.startedAt (epoch ms) or from mount, open spans grow to now, the axis extends in calm steps and the playback controls hide. Append spans and handoffs as events arrive and set live to false when the run ends; the timeline settles on the summary.onSpanSelect(span: AgentTimelineSpan | null) => void–Called when a span is pinned, with the span, or unpinned, with null.onComplete() => void–Called once each time a replay reaches the end or a live run ends.classNamestring–Class on the root element.styleCSSProperties–Inline style on the root, for example to override --series-1 to --series-4.- Tab
- Moves to the timeline's spans (one tab stop), the time scrubber, then play or pause, restart and the speed control.
- Arrow leftorArrow right
- On a span, moves to the previous or next span in the same row. On the scrubber, steps time by half a second; with Shift, by five seconds.
- Arrow uporArrow down
- On a span, moves to the nearest span in the row above or below.
- HomeorEnd
- On a span, jumps to the first or last span in its row. On the scrubber, jumps to the start or the end of the run.
- Page uporPage down
- On the scrubber, steps time by five seconds.
- Enter, Space
- On a span, pins it and highlights its handoff path; again to unpin.
- Escape
- Unpins the pinned span.
- The root is a labelled section. Each row of spans is a group named by its agent label, and every span is a button whose name reads the agent, the kind, the tool, failure, what it did, its time range and tokens; aria-pressed marks the pinned span.
- Spans use a roving tab index, so the chart is one tab stop and arrow keys move between spans by row and by time.
- The scrubber is a slider with a value text such as "12.4s of 26.4s" and full keyboard support; dragging anywhere on the axis moves it.
- Status is never color alone: a failed call shows a cross icon and the word Failed in its card and the legend, a retry shows a retry icon and its note, and the finished state shows a check with the summary.
- A polite live region announces replay, pause and the finished summary. The counting status line is not live, so it never floods a screen reader.
- The span card repeats what the span's accessible name already says, so it is hidden from assistive technology.
- Pointer focus draws no ring; keyboard focus shows Arc's shared focus ring on spans, the scrubber thumb and the controls.
- Bars and the playhead read one CSS variable for the current time, updated every animation frame, so spans grow smoothly without React rendering per frame; React updates counters and states about twenty times a second.
- An active span has a square, solid leading edge and a short shine that stops when it ends; its corner rounds off as it completes. A failed call changes to its failed look with a quick color fade when it returns.
- Connectors draw in with a clip reveal from the side the handoff leaves, about 360ms, and the port dot pops in at the start of a dispatch or after a result lands.
- The span card glides between spans on the morph spring, crossfades its content and springs to its new height; the status line and play button swap with one minimal crossfade.
- When the run ends the playhead fades out and the status settles into the summary.
- Reduced motion starts on the finished run and drops the shine, the connector draw, the card glide and the status crossfades. Playback still works when the person asks for it.
- Fills its container up to the canvas width. Below a 520px container the label column narrows to dot and name, and the model moves into the span card.
- When the plot would be narrower than 300px it scrolls horizontally inside its own frame with the agent labels pinned, and the replay keeps the playhead in view unless the person has just scrolled.
- The status line wraps after a separator, never before, and the counters reserve the width of their final value, so the header never shifts while it counts.
- Hover cards are anchored inside the component and flip below the row when there is no room above.
- The run is normalized once per data change: spans sorted, connector shapes and totals memoized.
- Per frame the clock writes one CSS variable; widths, the playhead and connectors are pure CSS from it. React re-renders at most about twenty times a second while playing, and memoized span buttons only update when their state changes.
- The replay clock only runs while the timeline is in view, and stops when the run ends.
- Handles a few hundred spans comfortably; for long traces, pass a window of the run or aggregate tiny spans.
Notes for AI
Give your coding assistant the Markdown reference instead of screenshots.
- Choose it to show one orchestrated multi-agent run as a timeline: which agent did what, when, in parallel, with handoffs. It fits a card 360 to 760px wide.
- Put the orchestrator first. Order subagents so connectors pass through idle rows: the agent that returns first sits right under the orchestrator, and dispatches go to the deepest row first.
- Give each handoff a time that matches the spans around it: a dispatch just before the target's first span, a result just after the source's last span. The shape is inferred from those gaps.
- Use kind "waiting" for an agent that is blocked on others, so parallelism and idle time read at a glance and the summary's speedup counts only real work.
- Mark a failed tool call with status "failed" and a detail message, and the call that retried it with retry: true.
- Real model and tool marks are built in by name; pass mark for anything else. Brand marks are sample content, never implied endorsements.
The full library index for assistants is at /llms.txt.