One run of an AI agent, step by step: every model call, tool call and approval on a timeline, with what went in and what came out, the time, tokens and cost. It waits for you where it should, with the files it would change to review, and retries from the step that failed. Live, on motion.
Installation
When to use
- Seeing what an AI agent did on one task: each model call, tool call and approval, what went in and came out, the time and the cost.
- Agent products where people need to trust a result, find why a run failed, and run it again from the step that broke.
When not to use
- The answer itself in a conversation: Tool Activity and Reasoning show the steps inline, under the message.
- Many runs side by side: a Data Table of runs, each opening this.
Usage
Agent run viewer with your own data: every prop has the sample as its default, so pass only what you have.
"use client"
import { useState } from "react"
import { AgentRunViewer, type AgentRun } from "@/components/dashral/blocks/agent-run-viewer"
export default function Run() {
// Your agent's run, kept in state: pass a new one as it reports progress (a step added, a status changed).
const [run] = useState<AgentRun>({
id: "run_41",
title: "Summarize this week's support tickets",
agent: "Support agent",
status: "succeeded",
time: "Today, 10:12",
steps: [
{ id: "1", kind: "tool", name: "Search tickets", tool: "tickets.search", status: "done", start: 0, duration: 900, note: "86 tickets", input: { from: "monday" }, output: { matches: 86 } },
{ id: "2", kind: "model", name: "Write the summary", tool: "model-large", status: "done", start: 950, duration: 2400, tokens: 1830, cost: 0.009, output: "Most tickets were about sign-in." },
],
})
return (
<AgentRunViewer
run={run}
// While the promise is pending the button shows it; a thrown Error's message shows under it.
onRetry={async (step) => console.log("retry from", step.id)}
// A step with status "waiting" (a review, an approval) shows Reject and Approve; tell your agent the answer.
onRespond={async (step, decision) => console.log(step.id, decision)}
onStop={() => console.log("stop", run.id)}
/>
)
}API reference
AgentRunViewer
One run of an AI agent: what it was asked, its status, how long it took, the tokens and what it cost, and every step (model calls, tool calls, waits for approval) on a timeline, each with its bar. Pick a step to see what went in and what came out; a failed step opens itself with its error and a way to retry from it. While the run is running the step at work is followed, until you pick another. ↑ and ↓ move along the steps.
| Prop | Type | Default |
|---|---|---|
run | AgentRunThe run, as your agent reports it: a new one replaces what's shown. Its steps in the order they started. Without | sample |
onRetry | (step: RunStep) => Promise<void> | voidRun the agent again from a step: the button shows it's working while the promise is pending; a thrown Error's message shows under it. | — |
onRespond | (step: RunStep, decision: "approved" | "rejected") => Promise<void> | voidAnswer a step waiting for you (an approval, a review of changes): Approve or Reject, which show they're working while the promise is pending. | — |
onStop | () => Promise<void> | voidStop a running run: the button shows it's working while the promise is pending; a thrown Error's message shows under it. | — |
className | string | — |
RunFileChanges
Files a step would change, a tab a file with what it adds and removes, and the diff as a commit shows it: each row tinted, the new line's number, its sign in a column of its own.
| Prop | Type | Default |
|---|---|---|
changes | RunFileChange[]Required. | — |
RunStepDetail
One step opened: what it is, when it ran and what it cost, why it failed, what went in and what came out (its output grows in as it arrives), and a way to run again from it.
| Prop | Type | Default |
|---|---|---|
step | RunStepRequired. | — |
elapsed | numberRequired. How long it has taken so far, in milliseconds: its duration, or the time since it started while it runs. | — |
onRetry | () => Promise<void> | voidOffers to run the agent again from this step; left out, or while the run is running, it isn't offered. | — |
onRespond | (decision: "approved" | "rejected") => Promise<void> | voidA step waiting for you: Approve or Reject, under what it would do. | — |
type RunStepKind
= "model" | "tool" | "approval"type RunStepStatus
"waiting" is a step paused for a person: an approval, a review of changes.
= "pending" | "running" | "waiting" | "done" | "error"type RunFileChange
A file the step would change, before and after: shown as a diff, one tab a file.
| Prop | Type | Default |
|---|---|---|
file | string | — |
before | string | — |
after | string | — |
type RunStep
| Prop | Type | Default |
|---|---|---|
id | string | — |
kind | RunStepKindA call to the model, a tool, or a wait for a person's approval. | — |
name | stringWhat it did, in words: "Query the warehouse". | — |
tool | stringThe tool as your code names it ("sql.query"), or the model ("model-large"). | — |
status | RunStepStatus | — |
start | numberWhen it started, in milliseconds from the run's start. | — |
duration | numberHow long it took, in milliseconds, once it's done or failed. While it runs it's measured from | — |
tokens | number | — |
cost | numberWhat it cost, in US dollars. | — |
input | unknownWhat went in and what came out: text, or anything JSON can hold. | — |
output | unknown | — |
error | stringWhy it failed. | — |
changes | RunFileChange[]Files it would change (a review before anything is written), shown as a diff a tab a file. | — |
icon | React.ReactNodeIts own icon (reading a file, searching), in place of its kind's. | — |
note | stringOne line under the name: what it found, who approved. | — |
type AgentRunStatus
"waiting": paused on a step that needs a person.
= "running" | "waiting" | "succeeded" | "failed" | "stopped"type AgentRun
| Prop | Type | Default |
|---|---|---|
id | string | — |
title | stringWhat the agent was asked, or the run's name. | — |
agent | string | — |
status | AgentRunStatus | — |
time | stringWhen it started, as shown: "Today, 09:41". | — |
startedAt | numberWhen it started, in epoch milliseconds: while it runs, the running steps' bars and the duration grow from it. | — |
steps | RunStep[] | — |