Early bird: 30% off Dashral ProSee pricing

Dashral

Agent run viewerPro

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.

example.tsx
"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.

PropTypeDefault
runAgentRun

The run, as your agent reports it: a new one replaces what's shown. Its steps in the order they started. Without startedAt, a running step's bar waits at its start until it reports its duration.

sample
onRetry(step: RunStep) => Promise<void> | void

Run 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> | void

Answer 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> | void

Stop a running run: the button shows it's working while the promise is pending; a thrown Error's message shows under it.

—
classNamestring—

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.

PropTypeDefault
changesRunFileChange[]

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.

PropTypeDefault
stepRunStep

Required.

—
elapsednumber

Required. How long it has taken so far, in milliseconds: its duration, or the time since it started while it runs.

—
onRetry() => Promise<void> | void

Offers 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> | void

A 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.

PropTypeDefault
filestring—
beforestring—
afterstring—

type RunStep

PropTypeDefault
idstring—
kindRunStepKind

A call to the model, a tool, or a wait for a person's approval.

—
namestring

What it did, in words: "Query the warehouse".

—
toolstring

The tool as your code names it ("sql.query"), or the model ("model-large").

—
statusRunStepStatus—
startnumber

When it started, in milliseconds from the run's start.

—
durationnumber

How long it took, in milliseconds, once it's done or failed. While it runs it's measured from startedAt.

—
tokensnumber—
costnumber

What it cost, in US dollars.

—
inputunknown

What went in and what came out: text, or anything JSON can hold.

—
outputunknown—
errorstring

Why it failed.

—
changesRunFileChange[]

Files it would change (a review before anything is written), shown as a diff a tab a file.

—
iconReact.ReactNode

Its own icon (reading a file, searching), in place of its kind's.

—
notestring

One 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

PropTypeDefault
idstring—
titlestring

What the agent was asked, or the run's name.

—
agentstring—
statusAgentRunStatus—
timestring

When it started, as shown: "Today, 09:41".

—
startedAtnumber

When it started, in epoch milliseconds: while it runs, the running steps' bars and the duration grow from it.

—
stepsRunStep[]—