Activity Trail
The searches, reads and tool calls behind an answer, so the user can see how it was made.
≈ 1,189 tokens per use (tool 636 · guidance 266 · typical payload 287)How token costs are estimated
Example: Activity Trail
Why are users dropping off at the consent screen?
Finished. 5 steps, 12 seconds.
When to use
- Your answer depends on searches, files or tool calls the user may want to check.
- The work took several steps or noticeable time, and showing it builds trust in the result.
When not to use
- You answered from general knowledge without using tools. There's nothing to show.
- There was a single trivial lookup. Mention it in a sentence.
- You want the user to approve an action before it happens. Use a Change Preview.
How the agent should use it
- Only list steps you actually performed, in the order you performed them. The steps are data-bound: they come from your own tool calls, never from a plausible-sounding story.
- Write labels in the past tense and name the thing, e.g. "Searched 'consent screen errors'" or "Read release-notes-4.12.md".
- Keep
detailto a few words: a count, a status code, a short finding. - Add
inputandoutputto the steps worth checking (a query, a tool call) and leave them off the rest. Keep each to a short excerpt of the real call. - Report failures. A failed step stays in the trail as
error, with the reason indetail, even when a retry succeeded.
How it works
- The header sums up the work: step count, total time, and how many steps failed. While a step is
runningit reads Working…, and the running step's label shimmers. - The list starts open. The header toggles it, and the list stays in the DOM while collapsed.
- Each step is one line: the label, then its outcome or source in quiet text ("12 results", or the page it read as a link). The kind and each step's duration are read to screen readers but not shown; the header carries the total time. Failed steps are shown in the destructive color and announced as failed.
- Steps can open. A step with an
input(what it was given) or anoutput(what it returned) gets a small chevron. Opening it shows both as short plain-text blocks (inputs are capped at 400 characters and outputs at 600, so they never need to scroll). With two or more such steps the header offers Expand all and Collapse all. They're short excerpts from the real call, never the raw payload, and never secrets or personal data. - A live clock. While the trail is working the header counts up each second; once it finishes it shows the steps' own durations, summed. A trail restored from history never ticks.
- Targets can link out. Only
http:andhttps:URLs become links, and they open in a new tab withrel="noopener noreferrer nofollow". - Streaming. Steps appear as they arrive and flip from
runningtodoneorerror. When the work finishes, a polite live region announces the summary once.
Installation
npx shadcn@latest add https://your-site.com/r/activity-trail.json
Usage
The Activity Trail is display-only, so the tool can return immediately:
import { tool } from "ai"
import { activityTrailTool } from "@/components/genui/activity-trail.schema"
const tools = {
[activityTrailTool.name]: tool({
description: activityTrailTool.description,
inputSchema: activityTrailTool.inputSchema,
execute: async () => ({ shown: true }),
}),
}Render it through the Renderer under show_activity, or directly:
<ActivityTrail {...part.input} status={part.state === "input-streaming" ? "streaming" : "ready"} />A host can also build the trail from the run's own tool calls instead of asking the agent to describe them. Map each tool part to a step:
<ActivityTrail
steps={toolParts.map((p) => ({
id: p.toolCallId,
kind: "tool",
label: describeCall(p),
status: p.state === "output-available" ? "done" : p.state === "output-error" ? "error" : "running",
}))}
/>With LangGraph
Push it from a graph node with ui.push({ name: "show_activity", props }, { message }), and update it with the same id as steps finish. Render the thread's UI messages with the Renderer.
Schema
| Prop | Type | Notes |
|---|---|---|
title | string? | e.g. "Researched consent screen errors". The header always shows the step count |
steps | Step[] | 1–40 steps. Data-bound |
Step
| Field | Type | Notes |
|---|---|---|
id | string | Keep it stable while the step updates |
kind | search | read | tool | think | write | Optional. Read to screen readers ("Search: …") |
label | string | What was done, past tense |
detail | string? | Short outcome: "12 results", "Rate limited (429)" |
status | running | done | error | Default done |
durationMs | number? | Omit while running. Summed in the header; per step it's spoken only |
target | { label, url? }? | The file, page or record. Only http(s) URLs are linked |
input | string? | What the step was given, up to 400 characters. Shown when the step is opened |
output | string? | What the step returned, up to 600 characters. Shown when the step is opened |
Host props: status (streaming or ready) and className.