{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "agent-trace",
  "title": "Agent Trace",
  "description": "Tool calls as ruled rows, with state, tool, argument and duration. Expand a row to see its output.",
  "registryDependencies": [
    "https://spotcheckui.com/r/spotcheck-base.json"
  ],
  "files": [
    {
      "path": "registry/spotcheck/components/spotcheck/agent-trace.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst ROW = \"grid grid-cols-[1.6rem_5.2rem_minmax(0,1fr)_4.4rem] items-end gap-2.5\"\n\nexport type TraceStep = {\n  id: string\n  tool: string\n  /** The main argument, shortened to one line. */\n  argument: string\n  state: \"queued\" | \"running\" | \"done\" | \"error\"\n  /** How long it took, or is expected to take, in milliseconds. */\n  ms?: number\n  output?: React.ReactNode\n}\n\n/** Share of the run's time that's finished, from 0 to 1. */\nexport function traceProgress(steps: TraceStep[]) {\n  const total = steps.reduce((sum, step) => sum + (step.ms ?? 0), 0)\n  if (total === 0) return 0\n  const finished = steps.filter((step) => step.state === \"done\" || step.state === \"error\")\n  return finished.reduce((sum, step) => sum + (step.ms ?? 0), 0) / total\n}\n\ntype AgentTraceProps = {\n  steps: TraceStep[]\n  /** Set while the run is going, so the summary reads as a step count. */\n  live?: boolean\n  className?: string\n}\n\nexport function AgentTrace({ steps, live = false, className }: AgentTraceProps) {\n  const [open, setOpen] = React.useState<string | null>(null)\n  const progress = traceProgress(steps)\n  const total = steps.reduce((sum, step) => sum + (step.ms ?? 0), 0)\n  const running = steps.findIndex((step) => step.state === \"running\")\n\n  return (\n    <div className={cn(\"font-(family-name:--f-mono) text-[12.5px]\", className)}>\n      {steps.map((step) => {\n        const expanded = open === step.id\n        return (\n          <div key={step.id} data-state={step.state} className={cn(step.state === \"queued\" && \"text-mute\")}>\n            <button\n              type=\"button\"\n              aria-expanded={expanded}\n              onClick={() => setOpen(expanded ? null : step.id)}\n              className={cn(\n                ROW,\n                \"spotcheck-focus hover:bg-wash w-full cursor-pointer border-b border-[color-mix(in_srgb,var(--ink)_18%,transparent)] pt-2.5 pb-[5px] text-left\",\n              )}\n            >\n              <span\n                aria-hidden\n                className={cn(\n                  \"size-3 self-center border-(length:--rule)\",\n                  step.state === \"done\" && \"border-ink bg-ink\",\n                  step.state === \"running\" && \"border-accent-ink bg-accent-ink motion-safe:animate-pulse\",\n                  step.state === \"error\" && \"border-bad bg-bad\",\n                  step.state === \"queued\" && \"border-ink\",\n                )}\n              />\n              <span className=\"font-semibold uppercase\">{step.tool}</span>\n              <span className=\"min-w-0 truncate\">{step.argument}</span>\n              <span className=\"text-mute text-right tabular-nums\">{step.state === \"queued\" ? \"—\" : `${step.ms ?? 0} ms`}</span>\n              <span className=\"sr-only\">{step.state}</span>\n            </button>\n            {expanded && (\n              <div className=\"text-mute pt-1.5 pb-2.5 pl-[2.6rem]\">\n                → {step.state === \"done\" || step.state === \"error\" ? step.output : \"Not finished yet\"}\n              </div>\n            )}\n          </div>\n        )\n      })}\n      <div\n        role=\"progressbar\"\n        aria-label=\"Run progress\"\n        aria-valuemin={0}\n        aria-valuemax={100}\n        aria-valuenow={Math.round(progress * 100)}\n        className=\"bg-wash mt-3 h-1.5\"\n      >\n        <div\n          className=\"bg-accent-ink h-full transition-[width] duration-300 motion-reduce:transition-none\"\n          style={{ width: `${progress * 100}%` }}\n        />\n      </div>\n      <div role=\"status\" className=\"spotcheck-label mt-2\">\n        {live && running >= 0 ? `Step ${running + 1} of ${steps.length}` : `Done in ${(total / 1000).toFixed(2)} s`}\n      </div>\n    </div>\n  )\n}\n",
      "type": "registry:component"
    }
  ],
  "categories": [
    "ai"
  ],
  "type": "registry:component"
}