---
title: "AgentTrace"
description: "An expandable trace of an agent's steps."
category: AiAgent
status: beta
since: 0.3.9
tags: [ai, agent, trace, disclosure]
syncStatus: reviewed
verified: true
---

## Usage

### Step Trace
The host drives the timeline: append `rows`, then set `working` to `false` when the run ends.
:::TuffDemoWrapper{demo="AgentTraceStepsDemo" code-lang="vue"}
---
code: |
  <template>
    <TxAgentTrace
      :rows="rows"
      :working="working"
      :default-open="open"
      done-label="Thought for 4 seconds"
    />
  </template>
---
:::

### Four Forms
`variant` changes only the row form; the header and the disclosure stay the same.
:::TuffDemoWrapper{demo="AgentTraceVariantsDemo" code-lang="vue"}
---
code: |
  <template>
    <TxAgentTrace
      :key="variant"
      :variant="variant"
      :rows="datasets[variant]"
      :query="variant === 'search' ? 'best waffle cone supplier' : undefined"
      :more-label="variant === 'search' ? '+7 more' : undefined"
      default-open
      @open="openInBrowser"
    />
  </template>
---
:::

### Best Practices

- Keep the timeline in the host: append `rows` and flip `working`; don't put playback scripts in the component.
- Always handle `open` in the `search` form, or clicking a source does nothing.
- Cap long traces with `moreLabel` instead of laying out dozens of rows.
- Use `reasoning` for paragraph prose (it wraps, never truncates) and `steps` for single-line labels.
- In streaming UIs, store `toggle` in the host and pass it back through `userOpen` so the reader's choice survives a rebuild.

## API Reference

### 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 open state; wins over everything and survives a rebuild. |
| `selectedId` | `string` | — | `coding`: the selected row id. Binding it hands selection to the host. |

Default header text per variant is below. Counted text such as "Thought for 4 seconds" depends on a measurement only the host has, so pass `doneLabel` for it.

| Variant | `activeLabel` | `doneLabel` |
|------|------|------|
| `steps` / `reasoning` | `Thinking` | `Thought` |
| `search` | `Searching the web` | `Searched the web` |
| `coding` | `Running tools` | `Ran tools` |

### Events

| Event | Payload | Description |
|------|------|------|
| `toggle` | `(open: boolean)` | Fires on a header click with the new state. |
| `open` | `(row: AgentTraceRow)` | `search`: fires when a linked row is clicked; the component never navigates. |
| `select` | `(id: string \| null)` | `coding`: fires 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. |

### Types

#### AgentTraceRow

| Field | Type | Description |
|------|------|------|
| `id` | `string` | Stable row key. |
| `primary` | `string` | Step name, prose sentence, site title, or tool name. |
| `secondary` | `string` | Right-hand detail: a count, domain, file path, or command. |
| `mono` | `boolean` | Sets `secondary` in the monospace face. |
| `added` / `removed` | `number` | Diff counters, shown as `+N −M` with a true minus sign (U+2212). |
| `href` | `string` | In the `search` form, renders the row as a link whose click emits `open`. |
| `status` | `'pending' \| 'active' \| 'done' \| 'error'` | `steps` glyph: `active` spins, `error` marks, anything else checks. |

## Overview

- Open resolves from `userOpen`, then the reader's click, then `defaultOpen`, then `working`; the click lives only in the instance.
- The header is a `button` with `aria-expanded` and `aria-controls`; while collapsed the body is `inert`, so its rows leave the tab order.
- A `search` row keeps a real `href` (copyable, middle-clickable); a click calls `preventDefault` and emits `open`, and the host decides how to open it.
- Without `status`, only the last row spins while `working`.
- `coding` rows are `button` elements with `aria-pressed`.
- Under reduced motion only the animation stops; opening, closing, and state work as usual.

## Technologies

- Adapted from [Beautiful UI](https://www.beautifului.dev) (© 2026 Shane Levine, MIT).
- Source: `packages/tuffex/packages/components/src/agent-trace/`.

<TuffDocSourceLink />

## Related components

- [Chain of Thought](./chain-of-thought.en.mdc): a thinking chain with Markdown bodies.
- [Reasoning Disclosure](./reasoning-disclosure.en.mdc): a single reasoning passage.
- [Sources](./sources.en.mdc): a source list.
