AgentTrace
An expandable agent trace covering step, reasoning, search, and tool forms.
AgentTrace
Basic Usage
Step Trace
The trace opens as it starts and folds once it settles; rows arrive in two batches. The timeline belongs to the demo — the component itself only consumes rows and working.
The Other Three Forms
reasoning is wrapping prose, search adds a query line and coloured source dots, and coding adds mono filenames with diff counters.
Reasoning / search / coding
All three share one header and disclosure grammar; only the row form changes.
Ownership of the Open State
Open resolves through three layers, highest first: userOpen (host-held) → the component's own click override → defaultOpen → working.
Used standalone, the trace opens and folds with working, and the reader's first click takes over from then on. But a streaming host re-renders on every delta, and a branch realignment can recreate the instance — an override kept only in instance state dies with it, which reads as "clicking does nothing". Lift toggle into the host and feed it back through userOpen to make the reader's choice survive any rebuild.
API
Props
| Prop | Type | Default | Description |
|---|---|---|---|
rows | AgentTraceRow[] | — | The trace rows. Required. |
variant | 'steps' | 'reasoning' | 'search' | 'coding' | 'steps' | Row form and typography. |
query | string | — | search: the query echoed above the results. |
working | boolean | false | Whether the trace is still running; drives the shimmer and the spinner. |
activeLabel | string | per variant | Header text while working. |
doneLabel | string | per variant | Header text once settled. |
moreLabel | string | — | Trailing overflow note, e.g. +7 more. |
defaultOpen | boolean | — | Open state before any interaction. Falls back to working. |
userOpen | boolean | — | Host-held override that survives a rebuild. |
selectedId | string | — | coding: the selected row id. Binding it hands ownership to the host. |
AgentTraceRow is { id, primary, secondary?, mono?, added?, removed?, href?, status? }, where status is 'pending' | 'active' | 'done' | 'error'.
Per-variant default copy: steps and reasoning use Thinking / Thought, search uses Searching the web / Searched the web, and coding uses Running tools / Ran tools. Counted copy (upstream's "Thought for 4 seconds", "Ran 3 tools") folds in a measurement only the host has — pass doneLabel for those.
Events
| Event | Payload | Description |
|---|---|---|
toggle | (open: boolean) | Emitted on header click, carrying the state after the toggle. |
open | (row: AgentTraceRow) | search: emitted when a linked row is clicked. The component never navigates. |
select | (id: string | null) | coding: emitted on select or deselect; null when cleared. |
Slots
| Name | Scope | Description |
|---|---|---|
icon | { working } | Replaces the header starburst. |
label | { working } | Replaces the header text. |
row | { row, index } | Replaces the row content, keeping the row container and entrance. |
Interaction Contract
- Links never navigate on their own. A
searchrow still renders a realhref(copyable, middle-clickable), but a click callspreventDefaultand emitsopenso the host decides how to open it. This is required inside an Electron renderer. - The collapse carries
inertwhile closed: a0frgrid still leaves its rows in the tab order, so they have to be taken out of it too. - The header is a
buttonwitharia-expandedand anaria-controlspointing at the collapse. - The
stepsglyph readsrow.statusfirst:activespins,errormarks, anything else checks. With nostatusit falls back to upstream behaviour — only the last row spins whileworking. codingrows arebuttonelements witharia-pressed. LeaveselectedIdunbound and the component keeps the selection; bind it and the host owns it outright.- Diff counters use U+2212 MINUS SIGN (
−), not a hyphen, so the pair matches the plus in width and weight. - Per-row entrance delay runs through the
--tx-bui-agent-trace-indexCSS variable rather than inline styles, so one rule turns it all off under reduced motion. - The rail is a pure CSS
::before. Upstream measures the row stack in a layout effect and tweens its height over 500ms; dropping the measurement is safe because the collapse is already clipping it.
Best Practices
- Treat it as a controlled primitive: keep the timeline in the host, grow
rowsand flipworking, and leave playback scripts out of the component. - Always handle
@openin search surfaces, or clicking a source does nothing at all. - Cap long traces with
moreLabelinstead of laying out dozens of rows. - Use
reasoningfor paragraph prose — it wraps and never truncates. Usestepsfor single-line labels. - In streaming UIs, store
togglein the host and feed it back throughuserOpenso a rebuild does not discard the reader's choice. - The component sizes to its container; upstream's 380px frame is the host's decision.
Source
- Component source:
packages/tuffex/packages/components/src/agent-trace/src/TxAgentTrace.vue. - Types:
packages/tuffex/packages/components/src/agent-trace/src/types.ts. - Verified coverage:
packages/tuffex/packages/components/src/agent-trace/__tests__/agent-trace.test.ts(23 cases) covers the three-layer open precedence,inertwhile collapsed, non-navigating links, controlled and uncontrolledcodingselection, thestepsglyph fallback, U+2212, and the per-row stagger variable. - Adapted from Beautiful UI (https://www.beautifului.dev), © 2026 Shane Levine, MIT.