{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "agent-dashboard",
  "title": "Agent Dashboard",
  "description": "An agent-ops overview — KPI tiles whose trends are derived, a runs-over-time bar chart, a recent-runs table whose failing rows expand to the error line, and a live-agent rail that filters the table without moving the window figures.",
  "dependencies": [
    "lucide-react",
    "zod"
  ],
  "registryDependencies": [
    "utils"
  ],
  "files": [
    {
      "path": "src/registry/blocks/agent-dashboard.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport {\n  ArrowDown,\n  ArrowUp,\n  Ban,\n  Check,\n  ChevronRight,\n  CircleCheck,\n  Coins,\n  Copy,\n  CopyX,\n  Filter,\n  Gauge,\n  Inbox,\n  LoaderCircle,\n  Minus,\n  OctagonAlert,\n  RefreshCcw,\n  ServerCrash,\n  Timer,\n  TriangleAlert,\n  Workflow,\n  X,\n} from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\nimport type {\n  AgentDashboardAgent,\n  AgentDashboardData,\n  AgentDashboardInstant,\n  AgentDashboardStat,\n  AgentDashboardStatKey,\n  AgentLiveState,\n  AgentRunOutcome,\n} from \"./agent-dashboard.contract\"\n\n/* --------------------------------------------------------------- vocabulary */\n\n/**\n * Tone per outcome, kept as one editable table. Every value is a theme variable,\n * so the host palette (and dark mode) decides the actual colour. `partial` is\n * deliberately NOT destructive: it is a run that mostly worked, and painting it\n * red trains people to ignore red.\n */\nconst OUTCOME_TONE: Record<AgentRunOutcome, string> = {\n  running: \"var(--chart-1)\",\n  succeeded: \"var(--chart-2)\",\n  partial: \"var(--chart-4)\",\n  failed: \"var(--destructive)\",\n  cancelled: \"var(--muted-foreground)\",\n}\n\nconst OUTCOME_WORD: Record<AgentRunOutcome, string> = {\n  running: \"Running\",\n  succeeded: \"Succeeded\",\n  partial: \"Partial\",\n  failed: \"Failed\",\n  cancelled: \"Stopped\",\n}\n\n/** Which outcomes an on-call reader is scanning for. Drives the \"problems only\" filter. */\nconst PROBLEM_OUTCOMES: ReadonlySet<AgentRunOutcome> = new Set<AgentRunOutcome>([\"failed\", \"partial\"])\n\nconst STATE_TONE: Record<AgentLiveState, string> = {\n  thinking: \"var(--chart-1)\",\n  \"using-tool\": \"var(--chart-3)\",\n  waiting: \"var(--chart-4)\",\n  idle: \"var(--muted-foreground)\",\n  error: \"var(--destructive)\",\n}\n\nconst STATE_WORD: Record<AgentLiveState, string> = {\n  thinking: \"thinking\",\n  \"using-tool\": \"using a tool\",\n  waiting: \"waiting on you\",\n  idle: \"idle\",\n  error: \"errored\",\n}\n\nconst STAT_ORDER: readonly AgentDashboardStatKey[] = [\"runs\", \"successRate\", \"avgDurationMs\", \"cost\"]\n\nconst STAT_LABEL: Record<AgentDashboardStatKey, string> = {\n  runs: \"Runs\",\n  successRate: \"Success rate\",\n  avgDurationMs: \"Avg duration\",\n  cost: \"Cost\",\n}\n\n/**\n * Which direction of travel is good news for each KPI. `runs` is `null` on\n * purpose — more runs is neither good nor bad, and colouring it green would be\n * an editorial opinion the data never expressed.\n */\nconst STAT_GOOD_DIRECTION: Record<AgentDashboardStatKey, \"up\" | \"down\" | null> = {\n  runs: null,\n  successRate: \"up\",\n  avgDurationMs: \"down\",\n  cost: \"down\",\n}\n\nconst TONE_GOOD = \"var(--chart-2)\"\nconst TONE_BAD = \"var(--destructive)\"\n\nconst MINUTE = 60_000\nconst HOUR = 60 * MINUTE\n\n/** Stable identities, so the memos and the \"nothing is expanded\" reset do not churn. */\nconst EMPTY_SET: ReadonlySet<string> = new Set<string>()\n\n/* --------------------------------------------------------------- formatting */\n\ninterface Formatters {\n  count: (value: number) => string\n  decimal: (value: number, digits: number) => string\n  money: (value: number | undefined) => string\n  time: (epoch: number) => string\n  day: (epoch: number) => string\n  stamp: (epoch: number) => string\n}\n\n/**\n * An unknown locale, an unknown IANA zone or a non-ISO currency code makes the\n * `Intl` constructors throw. A dashboard must not take the page down over a typo\n * in a prop, so a bad option set falls back instead of escaping the render.\n */\nfunction safeNumberFormat(locale: string, options: Intl.NumberFormatOptions): Intl.NumberFormat {\n  try {\n    return new Intl.NumberFormat(locale, options)\n  } catch {\n    return new Intl.NumberFormat(\"en-US\", options.currency ? { ...options, currency: \"USD\" } : options)\n  }\n}\n\nfunction safeDateFormat(locale: string, options: Intl.DateTimeFormatOptions): Intl.DateTimeFormat {\n  try {\n    return new Intl.DateTimeFormat(locale, options)\n  } catch {\n    return new Intl.DateTimeFormat(\"en-US\", { ...options, timeZone: \"UTC\" })\n  }\n}\n\nfunction buildFormatters(locale: string, timeZone: string, currency: string): Formatters {\n  const plain = safeNumberFormat(locale, {})\n  const decimalCache = new Map<number, Intl.NumberFormat>()\n  const decimal = (value: number, digits: number) => {\n    let nf = decimalCache.get(digits)\n    if (!nf) {\n      nf = safeNumberFormat(locale, { maximumFractionDigits: digits, minimumFractionDigits: digits })\n      decimalCache.set(digits, nf)\n    }\n    return nf.format(value)\n  }\n  const cash = safeNumberFormat(locale, { currency, style: \"currency\" })\n  // Token pricing lives in the fourth decimal; rounding a $0.0037 run to \"$0.00\"\n  // is the difference between a cost column and a shrug.\n  const micro = safeNumberFormat(locale, {\n    currency,\n    maximumFractionDigits: 4,\n    minimumFractionDigits: 2,\n    style: \"currency\",\n  })\n  // `hourCycle: \"h23\"` rather than `hour12: false`: the latter resolves to h24 in\n  // some ICU builds and prints midnight as \"24:00\", which reads as tomorrow.\n  const timeFmt = safeDateFormat(locale, { hour: \"2-digit\", hourCycle: \"h23\", minute: \"2-digit\", timeZone })\n  const dayFmt = safeDateFormat(locale, { day: \"numeric\", month: \"short\", timeZone })\n  const stampFmt = safeDateFormat(locale, { dateStyle: \"medium\", timeStyle: \"short\", timeZone })\n\n  return {\n    count: value => (Number.isFinite(value) ? plain.format(Math.round(value)) : \"—\"),\n    day: epoch => (Number.isFinite(epoch) ? dayFmt.format(epoch) : \"—\"),\n    decimal,\n    money: value => {\n      if (value === undefined || !Number.isFinite(value) || value < 0) return \"—\"\n      if (value === 0) return cash.format(0)\n      if (value < 0.0001) return `< ${micro.format(0.0001)}`\n      if (value < 0.01) return micro.format(value)\n      return cash.format(value)\n    },\n    stamp: epoch => (Number.isFinite(epoch) ? stampFmt.format(epoch) : \"—\"),\n    time: epoch => (Number.isFinite(epoch) ? timeFmt.format(epoch) : \"—\"),\n  }\n}\n\nfunction toEpoch(value: AgentDashboardInstant): number {\n  if (typeof value === \"number\") return value\n  // `Date.parse`, not `new Date(...)`: a pure call keeps the render pure.\n  if (typeof value === \"string\") return Date.parse(value)\n  return value.getTime()\n}\n\n/**\n * A duration is read as prose (\"42.8 s\"), never ranked against a neighbouring\n * row, so the unit shifts with the magnitude instead of making the reader divide\n * 291400 by anything.\n */\nfunction defaultFormatDuration(ms: number): string {\n  if (!Number.isFinite(ms) || ms < 0) return \"—\"\n  if (ms < 1000) return `${Math.round(ms)} ms`\n  if (ms < 60_000) return `${(ms / 1000).toFixed(1)} s`\n  const totalSeconds = Math.round(ms / 1000)\n  const hours = Math.floor(totalSeconds / 3600)\n  const minutes = Math.floor((totalSeconds % 3600) / 60)\n  const seconds = totalSeconds % 60\n  if (hours > 0) return `${hours} h ${minutes} m`\n  return `${minutes} m ${seconds} s`\n}\n\n/**\n * Success as a percentage. Two lies are ruled out by construction: reading\n * \"100%\" while runs are still failing, and reading \"0%\" while some run\n * succeeded. Near either boundary the label gains a decimal so it can tell the\n * truth.\n */\nfunction formatRate(f: Formatters, value: number): string {\n  const ratio = Number.isFinite(value) ? Math.min(1, Math.max(0, value)) : 0\n  const pct = ratio * 100\n  if (ratio > 0 && pct < 0.1) return \"<0.1%\"\n  const digits = (pct > 99 && pct < 100) || (pct > 0 && pct < 10) ? 1 : 0\n  const factor = 10 ** digits\n  let shown = Math.round(pct * factor) / factor\n  if (ratio < 1 && shown >= 100) shown = 100 - 1 / factor\n  if (ratio > 0 && shown <= 0) shown = 1 / factor\n  return `${f.decimal(shown, digits)}%`\n}\n\ninterface StatDelta {\n  text: string\n  direction: \"up\" | \"down\" | \"flat\"\n  /** `null` ⇒ neutral, rendered muted. A colour here is a judgement, so it is only set when there is one to make. */\n  tone: string | null\n}\n\n/**\n * The trend line under a KPI, derived from `value` and `previous` alone.\n *\n * A rate is compared in percentage POINTS (94.6% → 96.0% is \"+1.4 pts\", never\n * \"+1.5%\", which is a different and wrong claim). Everything else is compared\n * relatively, with a zero baseline handled in words because \"+∞%\" is not a\n * number anybody can act on.\n */\nfunction deriveDelta(key: AgentDashboardStatKey, stat: AgentDashboardStat, f: Formatters): StatDelta | null {\n  const previous = stat.previous\n  if (previous === undefined || !Number.isFinite(previous) || !Number.isFinite(stat.value)) return null\n\n  const good = STAT_GOOD_DIRECTION[key]\n  const toneFor = (direction: \"up\" | \"down\"): string | null =>\n    good === null ? null : direction === good ? TONE_GOOD : TONE_BAD\n\n  if (key === \"successRate\") {\n    const points = (stat.value - previous) * 100\n    if (Math.abs(points) < 0.05) return { direction: \"flat\", text: \"flat vs previous\", tone: null }\n    const direction = points > 0 ? \"up\" : \"down\"\n    return {\n      direction,\n      text: `${points > 0 ? \"+\" : \"-\"}${f.decimal(Math.abs(points), 1)} pts vs previous`,\n      tone: toneFor(direction),\n    }\n  }\n\n  if (previous === 0) {\n    if (stat.value === 0) return { direction: \"flat\", text: \"flat vs previous\", tone: null }\n    return { direction: \"up\", text: \"up from zero\", tone: toneFor(\"up\") }\n  }\n\n  const ratio = (stat.value - previous) / previous\n  if (Math.abs(ratio) < 0.005) return { direction: \"flat\", text: \"flat vs previous\", tone: null }\n  const direction = ratio > 0 ? \"up\" : \"down\"\n  const magnitude = Math.abs(ratio) * 100\n  return {\n    direction,\n    text: `${ratio > 0 ? \"+\" : \"-\"}${f.decimal(magnitude, magnitude < 10 ? 1 : 0)}% vs previous`,\n    tone: toneFor(direction),\n  }\n}\n\n/* -------------------------------------------------------------------- chart */\n\nconst CHART_H = 100\n/** Horizontal slot per bucket, in viewBox units. The gap is carved out of it. */\nconst SLOT = 10\nconst BAR_GAP = 2.4\nconst BAR_W = SLOT - BAR_GAP\n/** A bucket with any runs at all is never invisible, however tall the peak is. */\nconst MIN_BAR = 2.5\n\ninterface ChartBar {\n  key: string\n  x: number\n  runs: number\n  failed: number\n  okY: number\n  okH: number\n  failY: number\n  failH: number\n  time: string\n  title: string\n}\n\ninterface ChartModel {\n  bars: ChartBar[]\n  width: number\n  max: number\n  total: number\n  failed: number\n  peak: ChartBar | null\n  mean: number\n  meanY: number\n  /** Inferred bucket width, used in prose: \"runs per hour\". */\n  unit: string\n  first: string\n  last: string\n}\n\n/**\n * Turn the bucket array into geometry. Everything the caption and the accessible\n * label say is read off this one model, so the picture and its text alternative\n * cannot drift apart.\n */\nfunction buildChart(series: AgentDashboardData[\"series\"], f: Formatters): ChartModel | null {\n  const points = series\n    .map(bucket => ({\n      epoch: toEpoch(bucket.start),\n      failed: Math.max(0, Math.round(bucket.failed)),\n      runs: Math.max(0, Math.round(bucket.runs)),\n    }))\n    .filter(point => Number.isFinite(point.epoch))\n    .sort((a, b) => a.epoch - b.epoch)\n\n  if (points.length === 0) return null\n\n  const gaps: number[] = []\n  for (let i = 1; i < points.length; i += 1) gaps.push(points[i].epoch - points[i - 1].epoch)\n  gaps.sort((a, b) => a - b)\n  // Median gap, not mean: one missing bucket must not redefine the whole axis.\n  const step = gaps.length > 0 ? gaps[Math.floor(gaps.length / 2)] : 0\n  const unit = step >= 20 * HOUR ? \"day\" : step >= 45 * MINUTE ? \"hour\" : \"bucket\"\n  const label = (epoch: number) => (unit === \"day\" ? f.day(epoch) : f.time(epoch))\n\n  let max = 0\n  let total = 0\n  let failedTotal = 0\n  for (const point of points) {\n    // `failed` is a subset of `runs`; a payload that says otherwise is clamped\n    // rather than trusted, so no bar can draw taller than its own run count.\n    const failed = Math.min(point.failed, point.runs)\n    max = Math.max(max, point.runs)\n    total += point.runs\n    failedTotal += failed\n  }\n\n  const bars: ChartBar[] = points.map((point, index) => {\n    const failed = Math.min(point.failed, point.runs)\n    const height = max > 0 && point.runs > 0 ? Math.max(MIN_BAR, (point.runs / max) * CHART_H) : 0\n    const failH = failed > 0 ? Math.min(height, Math.max(MIN_BAR, (failed / max) * CHART_H)) : 0\n    const okH = Math.max(0, height - failH)\n    const time = label(point.epoch)\n    return {\n      failH,\n      failY: CHART_H - height,\n      failed,\n      key: `${point.epoch}-${index}`,\n      okH,\n      okY: CHART_H - okH,\n      runs: point.runs,\n      time,\n      title: `${time} — ${point.runs} ${point.runs === 1 ? \"run\" : \"runs\"}, ${failed} failed`,\n      x: index * SLOT,\n    }\n  })\n\n  let peak: ChartBar | null = null\n  for (const bar of bars) if (!peak || bar.runs > peak.runs) peak = bar\n  const mean = total / bars.length\n\n  return {\n    bars,\n    failed: failedTotal,\n    first: bars[0].time,\n    last: bars[bars.length - 1].time,\n    max,\n    mean,\n    meanY: max > 0 ? CHART_H - (mean / max) * CHART_H : CHART_H,\n    peak: peak && peak.runs > 0 ? peak : null,\n    total,\n    unit,\n    width: bars.length * SLOT,\n  }\n}\n\nfunction RunsChart({ ariaLabel, model }: { ariaLabel: string; model: ChartModel }) {\n  return (\n    <div className=\"border-b\">\n      <svg\n        aria-label={ariaLabel}\n        className=\"h-24 w-full\"\n        preserveAspectRatio=\"none\"\n        role=\"img\"\n        viewBox={`0 0 ${model.width} ${CHART_H}`}\n      >\n        {model.total > 0 && (\n          <line\n            className=\"stroke-muted-foreground/40\"\n            strokeDasharray=\"4 4\"\n            vectorEffect=\"non-scaling-stroke\"\n            x1={0}\n            x2={model.width}\n            y1={model.meanY}\n            y2={model.meanY}\n          />\n        )}\n        {model.bars.map(bar => (\n          // The whole slot is the hover target, so a quiet bucket is as easy to\n          // interrogate as a busy one — the native <title> is the readout.\n          <g className=\"group\" key={bar.key}>\n            <title>{bar.title}</title>\n            <rect\n              className=\"fill-muted opacity-0 transition-opacity group-hover:opacity-70 motion-reduce:transition-none\"\n              height={CHART_H}\n              width={SLOT}\n              x={bar.x}\n              y={0}\n            />\n            {bar.runs === 0 && (\n              <rect className=\"fill-border\" height={1} width={BAR_W} x={bar.x + BAR_GAP / 2} y={CHART_H - 1} />\n            )}\n            {bar.okH > 0 && (\n              <rect\n                height={bar.okH}\n                style={{ fill: \"var(--chart-2)\" }}\n                width={BAR_W}\n                x={bar.x + BAR_GAP / 2}\n                y={bar.okY}\n              />\n            )}\n            {bar.failH > 0 && (\n              <rect\n                height={bar.failH}\n                style={{ fill: \"var(--destructive)\" }}\n                width={BAR_W}\n                x={bar.x + BAR_GAP / 2}\n                y={bar.failY}\n              />\n            )}\n          </g>\n        ))}\n      </svg>\n    </div>\n  )\n}\n\n/* -------------------------------------------------------------------- parts */\n\nfunction DeltaIcon({ direction }: { direction: StatDelta[\"direction\"] }) {\n  if (direction === \"up\") return <ArrowUp aria-hidden=\"true\" className=\"size-3 shrink-0\" />\n  if (direction === \"down\") return <ArrowDown aria-hidden=\"true\" className=\"size-3 shrink-0\" />\n  return <Minus aria-hidden=\"true\" className=\"size-3 shrink-0\" />\n}\n\nfunction StatTile({\n  delta,\n  icon,\n  label,\n  note,\n  statKey,\n  value,\n}: {\n  delta: StatDelta | null\n  icon: React.ReactNode\n  label: string\n  note?: string\n  statKey: string\n  value: string\n}) {\n  return (\n    // The 1px grid gap over a bg-border parent draws the dividers, so the strip\n    // keeps hairlines between tiles at every wrap point without a border-r that\n    // would dangle at the end of a row.\n    <div className=\"flex min-w-0 flex-col gap-1 bg-card p-3\" data-stat={statKey}>\n      <dt className=\"flex items-center gap-1.5 text-[11px] tracking-wide text-muted-foreground uppercase\">\n        <span aria-hidden=\"true\" className=\"shrink-0\">\n          {icon}\n        </span>\n        <span className=\"min-w-0 truncate\">{label}</span>\n      </dt>\n      <dd className=\"flex min-w-0 flex-col gap-0.5\">\n        <span className=\"min-w-0 text-xl leading-tight font-medium tabular-nums wrap-anywhere\">{value}</span>\n        {delta && (\n          <span\n            className={cn(\"flex items-center gap-1 text-[11px]\", delta.tone === null && \"text-muted-foreground\")}\n            style={delta.tone === null ? undefined : { color: delta.tone }}\n          >\n            <DeltaIcon direction={delta.direction} />\n            <span className=\"min-w-0 wrap-anywhere\">{delta.text}</span>\n          </span>\n        )}\n        {note && <span className=\"min-w-0 text-[11px] text-muted-foreground wrap-anywhere\">{note}</span>}\n      </dd>\n    </div>\n  )\n}\n\nfunction OutcomeIcon({ outcome }: { outcome: AgentRunOutcome }) {\n  const shared = \"size-3.5 shrink-0\"\n  switch (outcome) {\n    case \"running\":\n      // Shape plus word carry the state; the spin is decoration and stops dead\n      // under prefers-reduced-motion.\n      return <LoaderCircle aria-hidden=\"true\" className={cn(shared, \"animate-spin motion-reduce:animate-none\")} />\n    case \"succeeded\":\n      return <CircleCheck aria-hidden=\"true\" className={shared} />\n    case \"partial\":\n      return <TriangleAlert aria-hidden=\"true\" className={shared} />\n    case \"failed\":\n      return <OctagonAlert aria-hidden=\"true\" className={shared} />\n    case \"cancelled\":\n      return <Ban aria-hidden=\"true\" className={shared} />\n  }\n}\n\nfunction OutcomeBadge({ outcome }: { outcome: AgentRunOutcome }) {\n  const tone = OUTCOME_TONE[outcome]\n  return (\n    <span\n      className=\"inline-flex items-center gap-1.5 rounded-full border px-2 py-0.5 text-[11px] whitespace-nowrap\"\n      style={{\n        backgroundColor: `color-mix(in oklab, ${tone} 10%, transparent)`,\n        borderColor: `color-mix(in oklab, ${tone} 35%, transparent)`,\n        color: tone,\n      }}\n    >\n      <OutcomeIcon outcome={outcome} />\n      {OUTCOME_WORD[outcome]}\n    </span>\n  )\n}\n\nfunction Toolbar({\n  active,\n  children,\n  onClick,\n  title,\n}: {\n  active?: boolean\n  children: React.ReactNode\n  onClick: () => void\n  title?: string\n}) {\n  return (\n    <button\n      aria-pressed={active === undefined ? undefined : active}\n      className={cn(\n        \"inline-flex cursor-pointer items-center gap-1.5 rounded-md border px-2 py-1 text-[11px] transition-colors\",\n        \"focus-visible:ring-ring focus-visible:ring-2 focus-visible:outline-none motion-reduce:transition-none\",\n        active ? \"border-primary bg-primary/10 text-foreground\" : \"text-muted-foreground hover:bg-muted\",\n      )}\n      onClick={onClick}\n      title={title}\n      type=\"button\"\n    >\n      {children}\n    </button>\n  )\n}\n\nfunction SkeletonBlock({ className, style }: { className?: string; style?: React.CSSProperties }) {\n  return <div className={cn(\"animate-pulse rounded bg-muted motion-reduce:animate-none\", className)} style={style} />\n}\n\n/* ---------------------------------------------------------------- component */\n\nexport interface AgentDashboardProps\n  extends AgentDashboardData,\n    Omit<React.HTMLAttributes<HTMLElement>, \"children\" | \"title\"> {\n  /** Block heading; doubles as the region's accessible name when `label` is left alone. */\n  heading?: React.ReactNode\n  /** Accessible name for the whole region. */\n  label?: string\n  /** Which KPI tiles to draw, in order. Unknown keys are ignored; an empty list falls back to all four. */\n  tiles?: readonly AgentDashboardStatKey[]\n  /** Rows drawn before an explicit \"Show all N\" control. Clamped to >= 1. There is no silent cap. */\n  maxRuns?: number\n  /** Drop the runs-over-time chart for a text-only ops strip. */\n  showChart?: boolean\n  /** Drop the active-agents rail; the runs table then owns the full width. */\n  showAgents?: boolean\n  /** Renders the \"Copy digest\" button. It writes the KPI row plus every row the current filters select. */\n  copyable?: boolean\n  /** BCP 47 tag for every number, currency and time. Pinned by default so a server and a client render agree. */\n  locale?: string\n  /** IANA zone for every instant. Defaults to UTC — the visitor's zone is never guessed. */\n  timeZone?: string\n  /** Override the duration wording. */\n  formatDuration?: (ms: number) => string\n  /** Turns run rows into buttons. Omit it and the rows stay plain text — no cursor, no hover, no dead affordance. */\n  onSelectRun?: (runId: string) => void\n  /** Notified whenever the rail filter changes, so a host can mirror it into the URL. The filter itself is internal. */\n  onSelectAgent?: (agentId: string | null) => void\n  /** Renders the header refresh button. */\n  onRefresh?: () => void\n  /** Renders \"Try again\" in the `status=\"error\"` branch; omit it to hide the affordance. */\n  onRetry?: () => void\n  /** Message shown in the `status=\"error\"` branch. */\n  errorMessage?: string\n  /** Replaces the default `status=\"empty\"` body. */\n  emptyState?: React.ReactNode\n}\n\n/**\n * The agent-ops overview: a KPI strip whose trends are derived rather than fed,\n * a runs-over-time bar chart that stacks failures onto the same bar, a recent-runs\n * table whose failing rows expand to the error line, and a rail of live agents\n * that filters the table without ever touching the window-scope figures above it.\n */\nexport const AgentDashboard = React.forwardRef<HTMLElement, AgentDashboardProps>(\n  (\n    {\n      status,\n      windowLabel,\n      currency = \"USD\",\n      stats,\n      series,\n      runs,\n      agents,\n      heading = \"Agent operations\",\n      label = \"Agent operations\",\n      tiles = STAT_ORDER,\n      maxRuns = 6,\n      showChart = true,\n      showAgents = true,\n      copyable = true,\n      locale = \"en-US\",\n      timeZone = \"UTC\",\n      formatDuration = defaultFormatDuration,\n      onSelectRun,\n      onSelectAgent,\n      onRefresh,\n      onRetry,\n      errorMessage = \"The control plane didn't answer.\",\n      emptyState,\n      className,\n      onKeyDown,\n      ...props\n    },\n    ref,\n  ) => {\n    const uid = React.useId()\n\n    const formatters = React.useMemo(\n      () => buildFormatters(locale, timeZone, currency),\n      [locale, timeZone, currency],\n    )\n\n    /* ------------------------------------------------------------ selection */\n\n    const [agentFilter, setAgentFilter] = React.useState<string | null>(null)\n    const [problemsOnly, setProblemsOnly] = React.useState(false)\n    const [expanded, setExpanded] = React.useState<ReadonlySet<string>>(EMPTY_SET)\n    const [showAll, setShowAll] = React.useState(false)\n    const [copyState, setCopyState] = React.useState<\"idle\" | \"copied\" | \"error\">(\"idle\")\n\n    // The dataset's identity, not its contents: when the runs change, the\n    // disclosure and the reveal must not carry over onto rows that no longer\n    // mean the same thing.\n    const signature = React.useMemo(() => runs.map(run => run.id).join(\"|\"), [runs])\n    const [prevSignature, setPrevSignature] = React.useState(signature)\n    if (prevSignature !== signature) {\n      // Adjust-state-during-render, not an effect: there is never a frame in\n      // which the previous payload's expansion is applied to the new one.\n      setPrevSignature(signature)\n      setExpanded(EMPTY_SET)\n      setShowAll(false)\n      setCopyState(\"idle\")\n    }\n\n    // The filter is CLAMPED rather than reset — an agent that drops off the rail\n    // simply stops filtering, instead of leaving the table mysteriously empty.\n    const activeAgent: AgentDashboardAgent | null =\n      agentFilter === null ? null : (agents.find(agent => agent.id === agentFilter) ?? null)\n\n    const problemCount = runs.filter(run => PROBLEM_OUTCOMES.has(run.outcome)).length\n    // A toggle that can only ever empty the table is noise, so it disappears\n    // when there is nothing to filter to — and stops filtering with it.\n    const problemsFilterAvailable = problemCount > 0\n    const filterProblems = problemsOnly && problemsFilterAvailable\n\n    const filtered = runs.filter(\n      run =>\n        (activeAgent === null || run.agentId === activeAgent.id) &&\n        (!filterProblems || PROBLEM_OUTCOMES.has(run.outcome)),\n    )\n\n    const cap = Math.max(1, Math.floor(Number.isFinite(maxRuns) ? maxRuns : 6))\n    const truncated = !showAll && filtered.length > cap\n    const visible = truncated ? filtered.slice(0, cap) : filtered\n\n    const filterSummary = `${filtered.length} of ${runs.length} recent runs${\n      activeAgent ? ` from ${activeAgent.name}` : \"\"\n    }${filterProblems ? \", problems only\" : \"\"}.`\n\n    /* ---------------------------------------------------------- live region */\n\n    const [announcement, setAnnouncement] = React.useState(\"\")\n    const announced = React.useRef(false)\n    React.useEffect(() => {\n      // Transition-only: the first paint is read by the page, not by a live\n      // region, and announcing it would talk over the reader on arrival.\n      if (!announced.current) {\n        announced.current = true\n        return\n      }\n      setAnnouncement(`Runs table: ${filterSummary}`)\n    }, [filterSummary])\n\n    /* ----------------------------------------------------------------- copy */\n\n    const copyTimer = React.useRef<ReturnType<typeof setTimeout> | null>(null)\n    React.useEffect(\n      () => () => {\n        if (copyTimer.current) clearTimeout(copyTimer.current)\n      },\n      [],\n    )\n\n    /**\n     * The one-shot lock. It lives in a REF, not in state, because the second\n     * click of a double-click lands while the clipboard promise is still in\n     * flight — a state-only guard reads the stale \"idle\" and issues a second\n     * write, and if the two settle differently the button reports the loser\n     * over a clipboard that actually holds the digest. The handler slams it\n     * shut; every commit re-syncs it to the committed state, which is what\n     * reopens it when the tick times out, when a failure asks for a retry, and\n     * when a new payload clears the copy state.\n     */\n    const copyLock = React.useRef(false)\n    React.useEffect(() => {\n      copyLock.current = copyState === \"copied\"\n    }, [copyState])\n\n    const selectAgent = (id: string | null) => {\n      setAgentFilter(id)\n      setShowAll(false)\n      onSelectAgent?.(id)\n    }\n\n    const clearFilters = () => {\n      setProblemsOnly(false)\n      selectAgent(null)\n    }\n\n    /* ---------------------------------------------------------------- chart */\n\n    const chart = React.useMemo(() => buildChart(series, formatters), [series, formatters])\n\n    /* ------------------------------------------------------------ envelopes */\n\n    const rootClass = cn(\n      \"w-full min-w-0 overflow-hidden rounded-xl border bg-card text-sm text-card-foreground\",\n      className,\n    )\n\n    if (status === \"loading\") {\n      return (\n        <section aria-busy=\"true\" aria-label={label} className={rootClass} onKeyDown={onKeyDown} ref={ref} {...props}>\n          <span className=\"sr-only\" role=\"status\">\n            Loading agent operations\n          </span>\n          {/* The skeleton keeps the ready layout's boxes — four tiles, a chart\n              band, a table and a rail — so nothing jumps when the payload lands. */}\n          <div aria-hidden=\"true\" className=\"flex items-center justify-between gap-3 border-b p-4\">\n            <SkeletonBlock className=\"h-4 w-40\" />\n            <SkeletonBlock className=\"h-6 w-24\" />\n          </div>\n          <div\n            aria-hidden=\"true\"\n            className=\"grid gap-px border-b bg-border [grid-template-columns:repeat(auto-fit,minmax(min(11rem,100%),1fr))]\"\n          >\n            {STAT_ORDER.map(key => (\n              <div className=\"flex flex-col gap-2 bg-card p-3\" key={key}>\n                <SkeletonBlock className=\"h-2.5 w-20\" />\n                <SkeletonBlock className=\"h-5 w-24\" />\n              </div>\n            ))}\n          </div>\n          <div aria-hidden=\"true\" className=\"flex flex-col lg:flex-row\">\n            <div className=\"flex min-w-0 flex-1 flex-col\">\n              <div className=\"flex h-24 items-end gap-1 border-b p-4\">\n                {[38, 62, 45, 80, 30, 68, 52, 74, 41, 58].map((height, index) => (\n                  <SkeletonBlock className=\"min-w-0 flex-1\" key={index} style={{ height: `${height}%` }} />\n                ))}\n              </div>\n              <div className=\"flex flex-col gap-3 p-4\">\n                {[0, 1, 2, 3].map(row => (\n                  <div className=\"flex items-center gap-3\" key={row}>\n                    <SkeletonBlock className=\"h-3 w-24 shrink-0\" />\n                    <SkeletonBlock className=\"h-3 min-w-0 flex-1\" />\n                    <SkeletonBlock className=\"h-3 w-16 shrink-0\" />\n                  </div>\n                ))}\n              </div>\n            </div>\n            <div className=\"flex flex-col gap-3 border-t p-4 lg:w-64 lg:shrink-0 lg:border-t-0 lg:border-l\">\n              {[0, 1, 2].map(row => (\n                <div className=\"flex flex-col gap-1.5\" key={row}>\n                  <SkeletonBlock className=\"h-3 w-28\" />\n                  <SkeletonBlock className=\"h-2.5 w-40 max-w-full\" />\n                </div>\n              ))}\n            </div>\n          </div>\n        </section>\n      )\n    }\n\n    if (status === \"error\") {\n      return (\n        <section aria-label={label} className={rootClass} onKeyDown={onKeyDown} ref={ref} {...props}>\n          <div className=\"flex flex-col items-start gap-2 p-4\" role=\"alert\">\n            <p className=\"flex items-center gap-2 font-medium\">\n              <ServerCrash aria-hidden=\"true\" className=\"size-4 shrink-0 text-destructive\" />\n              Couldn&apos;t load agent operations\n            </p>\n            <p className=\"min-w-0 whitespace-pre-wrap text-muted-foreground wrap-anywhere\">{errorMessage}</p>\n            {onRetry && (\n              <button\n                className=\"focus-visible:ring-ring inline-flex cursor-pointer items-center gap-1.5 rounded-md border px-2.5 py-1 text-xs transition-colors hover:bg-muted focus-visible:ring-2 focus-visible:outline-none motion-reduce:transition-none\"\n                onClick={onRetry}\n                type=\"button\"\n              >\n                <RefreshCcw aria-hidden=\"true\" className=\"size-3.5\" />\n                Try again\n              </button>\n            )}\n          </div>\n        </section>\n      )\n    }\n\n    if (status === \"empty\") {\n      return (\n        <section aria-label={label} className={rootClass} onKeyDown={onKeyDown} ref={ref} {...props}>\n          {emptyState ?? (\n            <div className=\"flex items-start gap-2 p-4 text-muted-foreground\">\n              <Inbox aria-hidden=\"true\" className=\"mt-0.5 size-4 shrink-0\" />\n              <p className=\"min-w-0 wrap-anywhere\">\n                No agent has ever run in this workspace. Runs, costs and failures appear here as soon as one does.\n              </p>\n            </div>\n          )}\n        </section>\n      )\n    }\n\n    /* ---------------------------------------------------------------- ready */\n\n    const windowRuns = stats.runs.value\n    const sampleNote =\n      runs.length === 0\n        ? \"No runs were sampled for this window.\"\n        : `The table samples the ${runs.length} most recent of ${formatters.count(windowRuns)} runs in this window; the tiles above cover all of them.`\n\n    const statValue: Record<AgentDashboardStatKey, string> = {\n      runs: formatters.count(stats.runs.value),\n      // A success rate over zero runs is not 0%, it is undefined — printing\n      // \"0%\" here is the fastest way to start a false incident.\n      successRate: windowRuns > 0 ? formatRate(formatters, stats.successRate.value) : \"—\",\n      avgDurationMs: windowRuns > 0 ? formatDuration(stats.avgDurationMs.value) : \"—\",\n      cost: formatters.money(stats.cost.value),\n    }\n\n    const statNote: Partial<Record<AgentDashboardStatKey, string>> = {\n      successRate: windowRuns > 0 ? undefined : \"no runs in this window\",\n      avgDurationMs: windowRuns > 0 ? undefined : \"no runs in this window\",\n    }\n\n    const statIcon: Record<AgentDashboardStatKey, React.ReactNode> = {\n      runs: <Workflow className=\"size-3.5\" />,\n      successRate: <CircleCheck className=\"size-3.5\" />,\n      avgDurationMs: <Timer className=\"size-3.5\" />,\n      cost: <Coins className=\"size-3.5\" />,\n    }\n\n    const allowed = new Set<string>(STAT_ORDER)\n    const picked = Array.from(new Set(tiles.filter(key => allowed.has(key))))\n    const statKeys: readonly AgentDashboardStatKey[] = picked.length > 0 ? picked : STAT_ORDER\n\n    const chartCaption = (() => {\n      if (!chart) return null\n      if (chart.total === 0) return \"No runs in this window.\"\n      const parts = [\n        `${formatters.count(chart.total)} runs`,\n        `${formatters.count(chart.failed)} failed`,\n        chart.peak ? `peak ${formatters.count(chart.peak.runs)} at ${chart.peak.time}` : null,\n        `mean ${formatters.decimal(chart.mean, 1)} per ${chart.unit}`,\n      ].filter((part): part is string => part !== null)\n      return parts.join(\" · \")\n    })()\n\n    const chartLabel = chart\n      ? chart.total === 0\n        ? `Runs per ${chart.unit} across ${chart.bars.length} buckets from ${chart.first} to ${chart.last}: no runs.`\n        : `Runs per ${chart.unit} from ${chart.first} to ${chart.last}: ${chart.total} runs, ${chart.failed} failed${\n            chart.peak ? `, peak ${chart.peak.runs} at ${chart.peak.time}` : \"\"\n          }.`\n      : \"\"\n\n    /**\n     * The digest the copy button writes, built eagerly so the click handler\n     * closes over a plain string. It honours the FILTERS (that is the view you\n     * are pasting about) but never the \"show all\" truncation — what lands in the\n     * incident channel is every row the filter selects, not the part that\n     * happened to be on screen.\n     */\n    const digest = [\n      `Agent operations — ${windowLabel}`,\n      statKeys\n        .map(key => {\n          const delta = deriveDelta(key, stats[key], formatters)\n          return `${STAT_LABEL[key]} ${statValue[key]}${delta ? ` (${delta.text})` : \"\"}`\n        })\n        .join(\" · \"),\n      chartCaption ? `Runs over time: ${chartCaption}` : null,\n      \"\",\n      `Recent runs — ${filterSummary}`,\n      ...filtered.map(run => {\n        const head = `  [${OUTCOME_WORD[run.outcome].toLowerCase()}] ${run.agent} — ${run.task} · ${formatDuration(run.durationMs)} · ${formatters.money(run.cost)}`\n        if (!run.error) return head\n        return `${head}\\n      ${run.error.code ? `${run.error.code}: ` : \"\"}${run.error.message}`\n      }),\n    ]\n      .filter((line): line is string => line !== null)\n      .join(\"\\n\")\n\n    const handleCopy = () => {\n      // Locked from the click until the write settles, and kept locked while the\n      // confirmation is up, so a double-click cannot issue a second write or\n      // queue a second reset that blanks the tick early.\n      if (copyLock.current) return\n      copyLock.current = true\n      const settle = (next: \"copied\" | \"error\") => {\n        // Reopen here, not only in the effect: settling on the state we are\n        // already in — a second blocked clipboard in a row — is a React bail-out,\n        // so the effect would never run and the lock would stay shut over a\n        // button that still reads \"Copy failed\" and invites the retry.\n        copyLock.current = next === \"copied\"\n        setCopyState(next)\n        if (copyTimer.current) clearTimeout(copyTimer.current)\n        copyTimer.current = setTimeout(() => setCopyState(\"idle\"), 2000)\n      }\n      if (typeof navigator === \"undefined\" || !navigator.clipboard) {\n        settle(\"error\")\n        return\n      }\n      navigator.clipboard.writeText(digest).then(\n        () => settle(\"copied\"),\n        () => settle(\"error\"),\n      )\n    }\n\n    const handleRootKeyDown = (event: React.KeyboardEvent<HTMLElement>) => {\n      onKeyDown?.(event)\n      if (event.defaultPrevented || event.key !== \"Escape\") return\n      if (activeAgent === null && !filterProblems) return\n      event.preventDefault()\n      clearFilters()\n    }\n\n    const filtersActive = activeAgent !== null || filterProblems\n\n    return (\n      <section\n        aria-label={label}\n        className={rootClass}\n        onKeyDown={handleRootKeyDown}\n        ref={ref}\n        {...props}\n      >\n        <span aria-live=\"polite\" className=\"sr-only\" role=\"status\">\n          {announcement}\n        </span>\n\n        <header className=\"flex flex-wrap items-start justify-between gap-3 border-b p-4\">\n          <div className=\"flex min-w-0 flex-col gap-0.5\">\n            <h3 className=\"flex items-center gap-2 font-medium\">\n              <Gauge aria-hidden=\"true\" className=\"size-4 shrink-0 text-muted-foreground\" />\n              <span className=\"min-w-0 wrap-anywhere\">{heading}</span>\n            </h3>\n            <p className=\"min-w-0 text-xs text-muted-foreground wrap-anywhere\">\n              {windowLabel} · {formatters.count(windowRuns)} runs\n            </p>\n          </div>\n          <div className=\"flex shrink-0 items-center gap-2\">\n            {onRefresh && (\n              <Toolbar onClick={onRefresh} title=\"Reload the window\">\n                <RefreshCcw aria-hidden=\"true\" className=\"size-3.5\" />\n                Refresh\n              </Toolbar>\n            )}\n            {copyable && (\n              <Toolbar onClick={handleCopy} title=\"Copy a plain-text digest of the current view\">\n                {copyState === \"copied\" ? (\n                  <Check aria-hidden=\"true\" className=\"size-3.5\" />\n                ) : copyState === \"error\" ? (\n                  <CopyX aria-hidden=\"true\" className=\"size-3.5\" />\n                ) : (\n                  <Copy aria-hidden=\"true\" className=\"size-3.5\" />\n                )}\n                {copyState === \"copied\" ? \"Copied\" : copyState === \"error\" ? \"Copy failed\" : \"Copy digest\"}\n              </Toolbar>\n            )}\n          </div>\n        </header>\n\n        <dl className=\"grid gap-px border-b bg-border [grid-template-columns:repeat(auto-fit,minmax(min(11rem,100%),1fr))]\">\n          {statKeys.map(key => (\n            <StatTile\n              delta={deriveDelta(key, stats[key], formatters)}\n              icon={statIcon[key]}\n              key={key}\n              label={STAT_LABEL[key]}\n              note={statNote[key]}\n              statKey={key}\n              value={statValue[key]}\n            />\n          ))}\n        </dl>\n\n        <div className=\"flex min-w-0 flex-col lg:flex-row\">\n          <div className=\"flex min-w-0 flex-1 flex-col\">\n            {showChart && chart && (\n              <figure aria-label=\"Runs over time\" className=\"flex flex-col\">\n                <div className=\"flex flex-wrap items-center justify-between gap-2 px-4 pt-4 pb-2\">\n                  <h4 className=\"text-[11px] font-medium tracking-wide text-muted-foreground uppercase\">\n                    Runs per {chart.unit}\n                  </h4>\n                  <div className=\"flex items-center gap-3 text-[11px] text-muted-foreground\">\n                    <span className=\"flex items-center gap-1.5\">\n                      <span\n                        aria-hidden=\"true\"\n                        className=\"size-2 rounded-sm\"\n                        style={{ backgroundColor: \"var(--chart-2)\" }}\n                      />\n                      completed\n                    </span>\n                    <span className=\"flex items-center gap-1.5\">\n                      <span\n                        aria-hidden=\"true\"\n                        className=\"size-2 rounded-sm\"\n                        style={{ backgroundColor: \"var(--destructive)\" }}\n                      />\n                      failed\n                    </span>\n                  </div>\n                </div>\n                <RunsChart ariaLabel={chartLabel} model={chart} />\n                <figcaption className=\"flex flex-wrap items-center justify-between gap-2 px-4 py-2 text-[11px] text-muted-foreground\">\n                  <span className=\"min-w-0 wrap-anywhere\">{chartCaption}</span>\n                  <span className=\"shrink-0 tabular-nums\">\n                    {chart.first} → {chart.last}\n                  </span>\n                </figcaption>\n              </figure>\n            )}\n\n            <section\n              aria-label=\"Recent runs\"\n              className={cn(\"flex min-w-0 flex-col gap-3 p-4\", showChart && chart && \"border-t\")}\n            >\n              <div className=\"flex flex-wrap items-start justify-between gap-2\">\n                <div className=\"flex min-w-0 flex-col gap-0.5\">\n                  <h4 className=\"text-[11px] font-medium tracking-wide text-muted-foreground uppercase\">\n                    Recent runs\n                  </h4>\n                  <p className=\"min-w-0 text-[11px] text-muted-foreground wrap-anywhere\">{sampleNote}</p>\n                </div>\n                <div className=\"flex shrink-0 flex-wrap items-center gap-2\">\n                  {problemsFilterAvailable && (\n                    <Toolbar\n                      active={filterProblems}\n                      onClick={() => setProblemsOnly(value => !value)}\n                      title=\"Only failed and partial runs\"\n                    >\n                      <Filter aria-hidden=\"true\" className=\"size-3.5\" />\n                      Problems only ({problemCount})\n                    </Toolbar>\n                  )}\n                  {filtersActive && (\n                    <Toolbar onClick={clearFilters} title=\"Clear every filter (Esc)\">\n                      <X aria-hidden=\"true\" className=\"size-3.5\" />\n                      Clear\n                    </Toolbar>\n                  )}\n                </div>\n              </div>\n\n              {activeAgent && (\n                <p className=\"min-w-0 rounded-md border border-dashed px-3 py-2 text-[11px] text-muted-foreground wrap-anywhere\">\n                  Filtered to <span className=\"font-medium text-foreground\">{activeAgent.name}</span>\n                  {activeAgent.runs === undefined\n                    ? \". \"\n                    : ` — ${formatters.count(activeAgent.runs)} runs in this window, ${filtered.length} of them in this sample. `}\n                  The tiles and the chart above still cover the whole window.\n                </p>\n              )}\n\n              {visible.length === 0 ? (\n                <p className=\"rounded-md border border-dashed p-4 text-xs text-muted-foreground\">\n                  {runs.length === 0\n                    ? \"No runs have been recorded in this window yet.\"\n                    : \"No run matches the current filters.\"}\n                </p>\n              ) : (\n                // The narrowest the row can get (badge + duration + cost) is still\n                // wider than a phone card, so the table scrolls instead of having\n                // the Cost column clipped away by the card's overflow-hidden.\n                <div className=\"min-w-0 overflow-x-auto\">\n                  <table className=\"w-full border-collapse text-left\">\n                    <caption className=\"sr-only\">\n                      Recent agent runs. {filterSummary} Failing rows expand to the error that ended them.\n                    </caption>\n                    <thead>\n                      <tr className=\"text-[11px] tracking-wide text-muted-foreground uppercase\">\n                        <th className=\"w-8 py-1.5\" scope=\"col\">\n                          <span className=\"sr-only\">Error detail</span>\n                        </th>\n                        <th className=\"hidden py-1.5 pr-3 font-medium sm:table-cell\" scope=\"col\">\n                          Agent\n                        </th>\n                        <th className=\"py-1.5 pr-3 font-medium\" scope=\"col\">\n                          Task\n                        </th>\n                        <th className=\"py-1.5 pr-3 font-medium\" scope=\"col\">\n                          Outcome\n                        </th>\n                        <th className=\"py-1.5 pr-3 text-right font-medium\" scope=\"col\">\n                          Duration\n                        </th>\n                        <th className=\"py-1.5 text-right font-medium\" scope=\"col\">\n                          Cost\n                        </th>\n                      </tr>\n                    </thead>\n                    <tbody>\n                      {visible.map(run => {\n                        const detailId = `${uid}-detail-${run.id}`\n                        const open = expanded.has(run.id)\n                        // No error payload ⇒ no chevron. A disclosure that opens\n                        // nothing is worse than no disclosure at all.\n                        const expandable = run.error !== undefined\n                        const tone = OUTCOME_TONE[run.outcome]\n                        return (\n                          <React.Fragment key={run.id}>\n                            <tr className=\"border-t align-top\">\n                              <td className=\"py-2 pr-1\">\n                                {expandable && (\n                                  <button\n                                    aria-controls={detailId}\n                                    aria-expanded={open}\n                                    className=\"focus-visible:ring-ring inline-flex cursor-pointer items-center rounded-sm p-1 text-muted-foreground transition-colors hover:text-foreground focus-visible:ring-2 focus-visible:outline-none motion-reduce:transition-none\"\n                                    onClick={() =>\n                                      setExpanded(current => {\n                                        const next = new Set(current)\n                                        if (!next.delete(run.id)) next.add(run.id)\n                                        return next\n                                      })\n                                    }\n                                    type=\"button\"\n                                  >\n                                    <ChevronRight\n                                      aria-hidden=\"true\"\n                                      className={cn(\n                                        \"size-3.5 transition-transform motion-reduce:transition-none\",\n                                        open && \"rotate-90\",\n                                      )}\n                                    />\n                                    <span className=\"sr-only\">\n                                      {open ? \"Hide\" : \"Show\"} the error that ended {run.task}\n                                    </span>\n                                  </button>\n                                )}\n                              </td>\n                              <th\n                                className=\"hidden min-w-0 py-2 pr-3 text-xs font-normal text-muted-foreground wrap-anywhere sm:table-cell\"\n                                scope=\"row\"\n                              >\n                                {run.agent}\n                              </th>\n                              <td className=\"min-w-0 py-2 pr-3\">\n                                {onSelectRun ? (\n                                  <button\n                                    className=\"focus-visible:ring-ring cursor-pointer rounded-sm text-left underline-offset-2 hover:underline focus-visible:ring-2 focus-visible:outline-none\"\n                                    onClick={() => onSelectRun(run.id)}\n                                    type=\"button\"\n                                  >\n                                    <span className=\"min-w-0 wrap-anywhere\">{run.task}</span>\n                                  </button>\n                                ) : (\n                                  <span className=\"min-w-0 wrap-anywhere\">{run.task}</span>\n                                )}\n                                <span className=\"mt-0.5 block text-[11px] text-muted-foreground sm:hidden\">\n                                  {run.agent}\n                                </span>\n                              </td>\n                              <td className=\"py-2 pr-3\">\n                                <OutcomeBadge outcome={run.outcome} />\n                                {run.outcome === \"failed\" && !expandable && (\n                                  <span className=\"mt-0.5 block text-[11px] text-muted-foreground\">\n                                    no error reported\n                                  </span>\n                                )}\n                              </td>\n                              <td className=\"py-2 pr-3 text-right whitespace-nowrap tabular-nums\">\n                                {formatDuration(run.durationMs)}\n                                {run.outcome === \"running\" && (\n                                  <span className=\"block text-[11px] text-muted-foreground\">so far</span>\n                                )}\n                              </td>\n                              <td className=\"py-2 text-right whitespace-nowrap tabular-nums\">\n                                {formatters.money(run.cost)}\n                              </td>\n                            </tr>\n                            {/* Rendered even while collapsed so `aria-controls`\n                                always points at a real element. */}\n                            <tr hidden={!open} id={detailId}>\n                              <td className=\"pb-3\" colSpan={6}>\n                                {run.error && (\n                                  <div\n                                    className=\"flex flex-col gap-1.5 rounded-md border p-3 text-xs\"\n                                    style={{\n                                      backgroundColor: `color-mix(in oklab, ${tone} 6%, transparent)`,\n                                      borderColor: `color-mix(in oklab, ${tone} 30%, transparent)`,\n                                    }}\n                                  >\n                                    <div className=\"flex flex-wrap items-center gap-2\">\n                                      {run.error.code && (\n                                        <code className=\"rounded-sm bg-muted px-1.5 py-0.5 font-mono text-[11px]\">\n                                          {run.error.code}\n                                        </code>\n                                      )}\n                                      {run.error.step && (\n                                        <span className=\"min-w-0 text-[11px] text-muted-foreground wrap-anywhere\">\n                                          {run.error.step}\n                                        </span>\n                                      )}\n                                      <span className=\"text-[11px] text-muted-foreground\">\n                                        started {formatters.stamp(toEpoch(run.startedAt))} {timeZone}\n                                      </span>\n                                    </div>\n                                    <p className=\"min-w-0 font-mono whitespace-pre-wrap wrap-anywhere\">\n                                      {run.error.message}\n                                    </p>\n                                  </div>\n                                )}\n                              </td>\n                            </tr>\n                          </React.Fragment>\n                        )\n                      })}\n                    </tbody>\n                  </table>\n                </div>\n              )}\n\n              {filtered.length > cap && (\n                <div>\n                  <Toolbar onClick={() => setShowAll(value => !value)}>\n                    {truncated ? `Show all ${filtered.length} runs` : \"Show fewer\"}\n                  </Toolbar>\n                </div>\n              )}\n            </section>\n          </div>\n\n          {showAgents && (\n            <aside\n              aria-label=\"Active agents\"\n              className=\"flex shrink-0 flex-col gap-2 border-t p-4 lg:w-64 lg:border-t-0 lg:border-l\"\n            >\n              <div className=\"flex items-baseline justify-between gap-2\">\n                <h4 className=\"text-[11px] font-medium tracking-wide text-muted-foreground uppercase\">\n                  Active agents\n                </h4>\n                <span className=\"text-[11px] text-muted-foreground tabular-nums\">{agents.length}</span>\n              </div>\n\n              {agents.length === 0 ? (\n                <p className=\"text-[11px] text-muted-foreground\">\n                  No agent is awake right now. The window above still covers everything that ran.\n                </p>\n              ) : (\n                <>\n                  <p className=\"text-[11px] text-muted-foreground\">Pick one to filter the runs table.</p>\n                  <ul className=\"flex flex-col gap-1\">\n                    {agents.map(agent => {\n                      const selected = activeAgent?.id === agent.id\n                      return (\n                        <li key={agent.id}>\n                          <button\n                            aria-pressed={selected}\n                            className={cn(\n                              \"focus-visible:ring-ring flex w-full cursor-pointer flex-col gap-1 rounded-md border px-2.5 py-2 text-left transition-colors focus-visible:ring-2 focus-visible:outline-none motion-reduce:transition-none\",\n                              selected ? \"border-primary bg-primary/10\" : \"border-transparent hover:bg-muted\",\n                            )}\n                            onClick={() => selectAgent(selected ? null : agent.id)}\n                            type=\"button\"\n                          >\n                            <span className=\"flex min-w-0 items-center gap-2\">\n                              <span\n                                aria-hidden=\"true\"\n                                className={cn(\n                                  \"size-2 shrink-0 rounded-full\",\n                                  (agent.state === \"thinking\" || agent.state === \"using-tool\") &&\n                                    \"animate-pulse motion-reduce:animate-none\",\n                                )}\n                                style={{ backgroundColor: STATE_TONE[agent.state] }}\n                              />\n                              <span className=\"min-w-0 flex-1 truncate text-xs font-medium\">{agent.name}</span>\n                              {agent.runs !== undefined && (\n                                <span className=\"shrink-0 text-[11px] text-muted-foreground tabular-nums\">\n                                  {formatters.count(agent.runs)}\n                                </span>\n                              )}\n                            </span>\n                            <span className=\"text-[11px]\" style={{ color: STATE_TONE[agent.state] }}>\n                              {STATE_WORD[agent.state]}\n                            </span>\n                            {agent.task && (\n                              <span className=\"line-clamp-2 min-w-0 text-[11px] text-muted-foreground\">\n                                {agent.task}\n                              </span>\n                            )}\n                          </button>\n                        </li>\n                      )\n                    })}\n                  </ul>\n                </>\n              )}\n            </aside>\n          )}\n        </div>\n      </section>\n    )\n  },\n)\nAgentDashboard.displayName = \"AgentDashboard\"\n\nexport default AgentDashboard\n",
      "type": "registry:block"
    },
    {
      "path": "src/registry/blocks/agent-dashboard.contract.ts",
      "content": "import { z } from \"zod\"\n\n/**\n * The operations overview for a FLEET of agents over one time window.\n *\n * Three scopes live in this envelope and the block never lets them blur:\n *\n * - `stats` and `series` are WINDOW-scope facts. They describe every run in the\n *   window, including the ones nobody will ever scroll to.\n * - `runs` is a SAMPLE — the most recent handful, the thing you actually read.\n *   Summing it will not reproduce `stats`, and the block says so in words rather\n *   than letting a reader discover it during an incident.\n * - `agents` is NOW-scope: who is awake this second, which has nothing to do\n *   with the window at all.\n *\n * The component owns no clock. Every instant it prints comes from this payload,\n * so a server render, a client render and a screenshot of the same record all\n * agree.\n */\n\n/** ISO string, epoch ms, or a Date — whatever your transport already speaks. */\nexport const agentDashboardInstantSchema = z.union([z.string(), z.number(), z.date()])\nexport type AgentDashboardInstant = z.infer<typeof agentDashboardInstantSchema>\n\n/**\n * Five outcomes, not two.\n *\n * `partial` exists because most agent runs end somewhere between \"done\" and\n * \"broken\", and collapsing that into a green check teaches people to distrust\n * the check. `cancelled` exists because a run a human stopped is not a failure.\n * `running` exists because a dashboard read at 09:00 always has rows that have\n * not finished yet, and pretending otherwise makes the duration column lie.\n */\nexport const AGENT_RUN_OUTCOMES = [\"running\", \"succeeded\", \"partial\", \"failed\", \"cancelled\"] as const\nexport const agentRunOutcomeSchema = z.enum(AGENT_RUN_OUTCOMES)\nexport type AgentRunOutcome = z.infer<typeof agentRunOutcomeSchema>\n\n/** What a rail agent is doing right this second. Each state renders as a word AND a shape, never colour alone. */\nexport const AGENT_LIVE_STATES = [\"thinking\", \"using-tool\", \"waiting\", \"idle\", \"error\"] as const\nexport const agentLiveStateSchema = z.enum(AGENT_LIVE_STATES)\nexport type AgentLiveState = z.infer<typeof agentLiveStateSchema>\n\n/**\n * One KPI: this window's figure, plus optionally the comparable figure from the\n * window before it.\n *\n * The delta is DERIVED by the component, never handed to it. A pre-computed\n * \"+12%\" is a number that can disagree with the two numbers beside it; a\n * derived one cannot. Omit `previous` and the tile simply shows no trend —\n * which is the honest rendering of \"we have nothing to compare against\".\n */\nexport const agentDashboardStatSchema = z.object({\n  value: z.number(),\n  previous: z.number().optional(),\n})\nexport type AgentDashboardStat = z.infer<typeof agentDashboardStatSchema>\n\nexport const agentDashboardStatsSchema = z.object({\n  /** How many runs started in the window. Drives the \"the table is a sample\" wording. */\n  runs: agentDashboardStatSchema,\n  /** Share of finished runs that succeeded, as a ratio in 0..1 — not 0..100. */\n  successRate: agentDashboardStatSchema,\n  /** Mean wall-clock duration of the window's runs, in milliseconds. */\n  avgDurationMs: agentDashboardStatSchema,\n  /** What the window cost, in `currency`. */\n  cost: agentDashboardStatSchema,\n})\nexport type AgentDashboardStats = z.infer<typeof agentDashboardStatsSchema>\n\n/**\n * One column of the runs-over-time chart.\n *\n * `failed` is a SUBSET of `runs`, not a sibling of it — the chart stacks the\n * failed share on top of the rest of the same bar, so a bucket can never draw\n * taller than the run count it reports. Values above `runs` are clamped rather\n * than trusted.\n */\nexport const agentDashboardBucketSchema = z.object({\n  /** Start of the bucket. Bucket width is inferred from the median gap between starts. */\n  start: agentDashboardInstantSchema,\n  runs: z.number().int().nonnegative(),\n  failed: z.number().int().nonnegative(),\n})\nexport type AgentDashboardBucket = z.infer<typeof agentDashboardBucketSchema>\n\n/**\n * The failure line an on-call reader needs. Keep `message` as the raw provider\n * text: a rewritten error is an error you cannot grep for.\n */\nexport const agentRunErrorSchema = z.object({\n  message: z.string(),\n  /** Provider or tool error code, rendered as a monospace chip, e.g. `card_declined`. */\n  code: z.string().optional(),\n  /** Which step blew up, e.g. `tool: create_refund (attempt 3/3)`. */\n  step: z.string().optional(),\n})\nexport type AgentRunError = z.infer<typeof agentRunErrorSchema>\n\nexport const agentDashboardRunSchema = z.object({\n  id: z.string(),\n  /**\n   * Links this run to an entry in `agents`, which is what the side rail filters\n   * on. Runs whose agent has gone offline still render — they just cannot be\n   * reached from the rail.\n   */\n  agentId: z.string().optional(),\n  /** Display name of the agent that produced the run. */\n  agent: z.string(),\n  /** The task in the reader's words, one line. */\n  task: z.string(),\n  outcome: agentRunOutcomeSchema,\n  startedAt: agentDashboardInstantSchema,\n  /** Wall-clock ms. For `running` rows this is elapsed-so-far, and the cell says \"so far\". */\n  durationMs: z.number().nonnegative(),\n  /** Omitted = not reported. Renders \"—\" and is never counted as zero. */\n  cost: z.number().nonnegative().optional(),\n  /**\n   * Present ⇒ the row is expandable and the disclosure reveals this. Absent on a\n   * failed run ⇒ no chevron at all, and the row admits the failure went\n   * unreported instead of offering a control that opens nothing.\n   */\n  error: agentRunErrorSchema.optional(),\n})\nexport type AgentDashboardRun = z.infer<typeof agentDashboardRunSchema>\n\nexport const agentDashboardAgentSchema = z.object({\n  id: z.string(),\n  name: z.string(),\n  state: agentLiveStateSchema,\n  /** One line about what it is doing right now. Absent for idle workers. */\n  task: z.string().optional(),\n  /**\n   * How many runs this agent produced in the WINDOW — not how many of its rows\n   * are in `runs`. The two are printed side by side when the rail filter is on,\n   * precisely so nobody mistakes one for the other.\n   */\n  runs: z.number().int().nonnegative().optional(),\n})\nexport type AgentDashboardAgent = z.infer<typeof agentDashboardAgentSchema>\n\n/**\n * The BLOCK's own render state — \"is there an ops payload to show at all\".\n *\n * Note what `empty` is not: a window in which nothing happened is `ready` with\n * zeros, and it draws a flat chart plus \"no runs in this window\". `empty` is\n * reserved for a workspace that has never run an agent, which is a different\n * sentence and a different next action.\n */\nexport const agentDashboardStatusSchema = z.enum([\"loading\", \"empty\", \"error\", \"ready\"])\nexport type AgentDashboardStatus = z.infer<typeof agentDashboardStatusSchema>\n\nexport const agentDashboardSchema = z.object({\n  status: agentDashboardStatusSchema,\n  /** What the KPI row and the chart cover, in the reader's words: \"Today\", \"Last 24 hours\". */\n  windowLabel: z.string(),\n  /** ISO 4217 for every cost figure. A code `Intl` rejects falls back to USD instead of throwing. */\n  currency: z.string().optional(),\n  stats: agentDashboardStatsSchema,\n  series: z.array(agentDashboardBucketSchema),\n  /** The most recent runs — a sample of the window, never the whole of it. */\n  runs: z.array(agentDashboardRunSchema),\n  /** Who is awake right now. May be empty while the window is busy: the two scopes are independent. */\n  agents: z.array(agentDashboardAgentSchema),\n})\nexport type AgentDashboardData = z.infer<typeof agentDashboardSchema>\n\n/** Which KPI tiles the strip draws, and in which order. */\nexport const AGENT_DASHBOARD_STAT_KEYS = [\"runs\", \"successRate\", \"avgDurationMs\", \"cost\"] as const\nexport const agentDashboardStatKeySchema = z.enum(AGENT_DASHBOARD_STAT_KEYS)\nexport type AgentDashboardStatKey = z.infer<typeof agentDashboardStatKeySchema>\n",
      "type": "registry:ui"
    }
  ],
  "type": "registry:block"
}