{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "agent-run-block",
  "type": "registry:block",
  "title": "Agent run",
  "description": "One autonomous agent run: its plan step by step, the tool calls behind each step, approvals it waits on, and what it produced.",
  "dependencies": [
    "lucide-react@^1.47.0"
  ],
  "registryDependencies": [
    "https://control-ui.dev/r/activity.json",
    "https://control-ui.dev/r/badge.json",
    "https://control-ui.dev/r/card.json",
    "https://control-ui.dev/r/core.json",
    "https://control-ui.dev/r/live-status.json",
    "https://control-ui.dev/r/sidebar.json",
    "https://control-ui.dev/r/timeline.json",
    "https://control-ui.dev/r/typography.json"
  ],
  "files": [
    {
      "path": "src/registry/blocks/control-ui/agent-run-clock.ts",
      "target": "@components/control-ui/blocks/agent-run-clock.ts",
      "type": "registry:block",
      "content": "import { useSyncExternalStore } from \"react\";\n\nconst TICK_MS = 1000;\n\nlet now: number | null = null;\nconst listeners = new Set<() => void>();\nlet interval: ReturnType<typeof setInterval> | undefined;\n\nfunction tick() {\n  if (document.hidden) return;\n  now = Date.now();\n  for (const listener of listeners) listener();\n}\n\nfunction onVisibilityChange() {\n  if (!document.hidden) tick();\n}\n\nfunction subscribeToRunClock(listener: () => void) {\n  listeners.add(listener);\n  if (listeners.size === 1) {\n    now = Date.now();\n    interval = setInterval(tick, TICK_MS);\n    document.addEventListener(\"visibilitychange\", onVisibilityChange);\n  }\n  return () => {\n    listeners.delete(listener);\n    if (listeners.size > 0) return;\n    clearInterval(interval);\n    interval = undefined;\n    document.removeEventListener(\"visibilitychange\", onVisibilityChange);\n  };\n}\n\nconst subscribeToNothing = () => () => {};\n\nexport function useRunClock(active: boolean): number | null {\n  return useSyncExternalStore(\n    active ? subscribeToRunClock : subscribeToNothing,\n    () => now,\n    () => null,\n  );\n}\n\nfunction splitElapsed(ms: number) {\n  const totalSeconds = Math.floor(Math.max(0, ms) / 1000);\n  return {\n    hours: Math.floor(totalSeconds / 3600),\n    minutes: Math.floor((totalSeconds % 3600) / 60),\n    seconds: totalSeconds % 60,\n  };\n}\n\nconst pad2 = (value: number) => String(value).padStart(2, \"0\");\n\nexport function formatElapsed(ms: number): string {\n  const { hours, minutes, seconds } = splitElapsed(ms);\n  return hours > 0 ? `${hours}:${pad2(minutes)}:${pad2(seconds)}` : `${minutes}:${pad2(seconds)}`;\n}\n\nexport function elapsedIsoDuration(ms: number): string {\n  const { hours, minutes, seconds } = splitElapsed(ms);\n  return `PT${hours ? `${hours}H` : \"\"}${minutes ? `${minutes}M` : \"\"}${seconds}S`;\n}\n"
    },
    {
      "path": "src/registry/blocks/control-ui/agent-run-sidebar.tsx",
      "target": "@components/control-ui/blocks/agent-run-sidebar.tsx",
      "type": "registry:block",
      "content": "\"use client\";\n\nimport { BotIcon, Check, CircleAlert, CircleDashed, CirclePause, CircleSlash, LoaderCircle, PlusIcon } from \"lucide-react\";\nimport type { ReactNode } from \"react\";\nimport {\n  Sidebar,\n  SidebarContent,\n  SidebarFooter,\n  SidebarGroup,\n  SidebarGroupLabel,\n  SidebarHeader,\n  SidebarMenu,\n  SidebarMenuButton,\n  SidebarMenuItem,\n  SidebarRail,\n  useSidebar,\n} from \"@/components/control-ui/ui/sidebar\";\nimport { Text } from \"@/components/control-ui/ui/typography\";\nimport { type AgentRunStatus, type AgentRunSummary, agentRunStatusLabels } from \"./agent-run-status\";\n\nconst runStatusIcons = {\n  running: <LoaderCircle className=\"size-3.5 shrink-0 motion-safe:animate-spin\" aria-hidden=\"true\" />,\n  waiting: <CirclePause className=\"size-3.5 shrink-0 text-warning-text\" aria-hidden=\"true\" />,\n  success: <Check className=\"size-3.5 shrink-0 text-success-text\" aria-hidden=\"true\" />,\n  error: <CircleAlert className=\"size-3.5 shrink-0 text-destructive-text\" aria-hidden=\"true\" />,\n  cancelled: <CircleSlash className=\"size-3.5 shrink-0 text-muted-foreground\" aria-hidden=\"true\" />,\n  pending: <CircleDashed className=\"size-3.5 shrink-0 text-muted-foreground\" aria-hidden=\"true\" />,\n} satisfies Record<AgentRunStatus, ReactNode>;\n\nexport function AgentRunSidebar({\n  appName,\n  brand,\n  runs,\n  activeRunId,\n  sidebarFooter,\n  onRunSelect,\n  onNewRun,\n}: {\n  appName: string;\n  brand?: ReactNode;\n  runs: readonly AgentRunSummary[];\n  activeRunId?: string;\n  sidebarFooter?: ReactNode;\n  onRunSelect?: (run: AgentRunSummary) => void;\n  onNewRun?: () => void;\n}) {\n  const { setOpenMobile } = useSidebar();\n\n  return (\n    <Sidebar collapsible=\"offcanvas\">\n      <SidebarHeader className=\"gap-1.5 border-b border-sidebar-border px-2 pb-2 pt-3\">\n        <div className=\"flex min-w-0 items-center gap-2 px-1 py-1\">\n          {brand ?? (\n            <span className=\"flex size-6 shrink-0 items-center justify-center rounded-md bg-foreground text-background\">\n              <BotIcon className=\"size-3.5\" aria-hidden=\"true\" />\n            </span>\n          )}\n          <Text size=\"label\" weight=\"semibold\" className=\"truncate\" title={appName}>\n            {appName}\n          </Text>\n        </div>\n        {onNewRun ? (\n          <SidebarMenu>\n            <SidebarMenuItem>\n              <SidebarMenuButton\n                size=\"sm\"\n                onClick={() => {\n                  onNewRun();\n                  setOpenMobile(false);\n                }}\n              >\n                <PlusIcon />\n                <span>New run</span>\n              </SidebarMenuButton>\n            </SidebarMenuItem>\n          </SidebarMenu>\n        ) : null}\n      </SidebarHeader>\n\n      <SidebarContent>\n        <SidebarGroup className=\"gap-1 pt-1\">\n          <SidebarGroupLabel>Runs</SidebarGroupLabel>\n          {runs.length === 0 ? (\n            <Text size=\"caption\" tone=\"muted\" className=\"px-2\">\n              No runs yet\n            </Text>\n          ) : (\n            <SidebarMenu>\n              {runs.map((run) => (\n                <SidebarMenuItem key={run.id}>\n                  <SidebarMenuButton\n                    size=\"sm\"\n                    isActive={run.id === activeRunId}\n                    onClick={() => {\n                      onRunSelect?.(run);\n                      setOpenMobile(false);\n                    }}\n                  >\n                    {runStatusIcons[run.status]}\n                    <span className=\"truncate\">{run.title}</span>\n                    <span className=\"sr-only\">, {agentRunStatusLabels[run.status]}</span>\n                  </SidebarMenuButton>\n                </SidebarMenuItem>\n              ))}\n            </SidebarMenu>\n          )}\n        </SidebarGroup>\n      </SidebarContent>\n\n      {sidebarFooter ? <SidebarFooter>{sidebarFooter}</SidebarFooter> : null}\n      <SidebarRail />\n    </Sidebar>\n  );\n}\n"
    },
    {
      "path": "src/registry/blocks/control-ui/agent-run-status.ts",
      "target": "@components/control-ui/blocks/agent-run-status.ts",
      "type": "registry:block",
      "content": "import type { BadgeColor } from \"@/components/control-ui/ui/badge\";\nimport type { TimelineState } from \"@/components/control-ui/ui/timeline\";\n\nexport type AgentRunStatus = \"pending\" | \"running\" | \"waiting\" | \"success\" | \"error\" | \"cancelled\";\n\nexport type AgentRunSummary = { id: string; title: string; status: AgentRunStatus };\n\nexport const agentRunStatusLabels = {\n  pending: \"Queued\",\n  running: \"Running\",\n  waiting: \"Needs approval\",\n  success: \"Done\",\n  error: \"Failed\",\n  cancelled: \"Cancelled\",\n} satisfies Record<AgentRunStatus, string>;\n\nexport const agentRunStatusColors = {\n  pending: \"neutral\",\n  running: \"blue\",\n  waiting: \"yellow\",\n  success: \"green\",\n  error: \"red\",\n  cancelled: \"neutral\",\n} satisfies Record<AgentRunStatus, BadgeColor>;\n\nexport const agentRunStepLabels = {\n  neutral: \"Step\",\n  pending: \"Pending\",\n  running: \"Running\",\n  waiting: \"Waiting for approval\",\n  success: \"Done\",\n  error: \"Failed\",\n} satisfies Record<TimelineState, string>;\n"
    },
    {
      "path": "src/registry/blocks/control-ui/agent-run.tsx",
      "target": "@components/control-ui/blocks/agent-run.tsx",
      "type": "registry:block",
      "content": "\"use client\";\n\nimport { Check, Circle, CircleAlert, CircleDashed, CirclePause, LoaderCircle } from \"lucide-react\";\nimport type { ComponentProps, ReactNode } from \"react\";\nimport { createContext, useContext, useId } from \"react\";\nimport type { ActivityProps } from \"@/components/control-ui/activity\";\nimport {\n  Activity,\n  ActivityContent,\n  ActivityDetail,\n  ActivityDetailContent,\n  ActivityDetailLabel,\n  ActivityIcon,\n  ActivityRow,\n  ActivityStatus,\n  ActivityTitle,\n  ActivityTrigger,\n} from \"@/components/control-ui/activity\";\nimport { cn } from \"@/components/control-ui/lib/cn\";\nimport { Badge } from \"@/components/control-ui/ui/badge\";\nimport { Card, CardAction, CardContent, CardHeader } from \"@/components/control-ui/ui/card\";\nimport { LiveStatus } from \"@/components/control-ui/ui/live-status\";\nimport {\n  SidebarInset,\n  SidebarProvider,\n  type SidebarProviderProps,\n  type SidebarStyle,\n  SidebarTrigger,\n} from \"@/components/control-ui/ui/sidebar\";\nimport type { TimelineItemProps, TimelineState } from \"@/components/control-ui/ui/timeline\";\nimport {\n  Timeline,\n  TimelineContent,\n  TimelineDescription,\n  TimelineIndicator,\n  TimelineItem,\n  TimelineMeta,\n  TimelineSeparator,\n  TimelineTitle,\n} from \"@/components/control-ui/ui/timeline\";\nimport { Heading, Text } from \"@/components/control-ui/ui/typography\";\nimport { elapsedIsoDuration, formatElapsed, useRunClock } from \"./agent-run-clock\";\nimport { AgentRunSidebar } from \"./agent-run-sidebar\";\nimport {\n  type AgentRunStatus,\n  type AgentRunSummary,\n  agentRunStatusColors,\n  agentRunStatusLabels,\n  agentRunStepLabels,\n} from \"./agent-run-status\";\n\nexport type { AgentRunStatus, AgentRunSummary } from \"./agent-run-status\";\n\ntype AgentRunLayout = \"viewport\" | \"contained\";\n\nconst AgentRunLayoutContext = createContext<AgentRunLayout>(\"viewport\");\n\nfunction useHeadingLevels() {\n  const layout = useContext(AgentRunLayoutContext);\n  return layout === \"contained\" ? ({ title: 2, section: 3 } as const) : ({ title: 1, section: 2 } as const);\n}\n\nexport type AgentRunBlockProps = Omit<ComponentProps<\"div\">, \"children\"> &\n  Pick<SidebarProviderProps, \"persistOpen\" | \"keyboardShortcut\"> & {\n    appName?: string;\n    brand?: ReactNode;\n    runs: readonly AgentRunSummary[];\n    activeRunId?: string;\n    onRunSelect?: (run: AgentRunSummary) => void;\n    onNewRun?: () => void;\n    sidebarFooter?: ReactNode;\n    children: ReactNode;\n    layout?: AgentRunLayout;\n  };\n\nexport function AgentRunBlock({\n  appName = \"Agents\",\n  brand,\n  runs,\n  activeRunId,\n  onRunSelect,\n  onNewRun,\n  sidebarFooter,\n  children,\n  layout = \"viewport\",\n  className,\n  style,\n  ...props\n}: AgentRunBlockProps) {\n  const providerStyle: SidebarStyle = { \"--sidebar-width\": \"16rem\", ...style };\n\n  return (\n    <SidebarProvider\n      layout={layout}\n      className={cn(\"bg-background text-foreground\", layout === \"viewport\" && \"h-svh overflow-hidden\", className)}\n      style={providerStyle}\n      {...props}\n    >\n      <AgentRunSidebar\n        appName={appName}\n        brand={brand}\n        runs={runs}\n        activeRunId={activeRunId}\n        sidebarFooter={sidebarFooter}\n        onRunSelect={onRunSelect}\n        onNewRun={onNewRun}\n      />\n      <SidebarInset className=\"@container/agent-run flex h-full min-h-0 min-w-0 flex-col overflow-hidden\">\n        <AgentRunLayoutContext.Provider value={layout}>{children}</AgentRunLayoutContext.Provider>\n      </SidebarInset>\n    </SidebarProvider>\n  );\n}\n\nexport type AgentRunElapsedProps = Omit<ComponentProps<\"time\">, \"dateTime\" | \"children\"> & {\n  startedAt: number;\n  endedAt?: number;\n};\n\nexport function AgentRunElapsed({ startedAt, endedAt, className, ...props }: AgentRunElapsedProps) {\n  const now = useRunClock(endedAt === undefined);\n  const end = endedAt ?? now;\n  const ms = end === null ? null : Math.max(0, end - startedAt);\n\n  return (\n    <time\n      dateTime={ms === null ? undefined : elapsedIsoDuration(ms)}\n      className={cn(\"text-label tabular-nums text-muted-foreground\", className)}\n      {...props}\n    >\n      {ms === null ? \"\" : formatElapsed(ms)}\n    </time>\n  );\n}\n\nexport type AgentRunHeaderProps = Omit<ComponentProps<\"header\">, \"title\"> & {\n  title: string;\n  status: AgentRunStatus;\n  startedAt?: number;\n  endedAt?: number;\n  actions?: ReactNode;\n};\n\nexport function AgentRunHeader({ title, status, startedAt, endedAt, actions, className, ...props }: AgentRunHeaderProps) {\n  const levels = useHeadingLevels();\n\n  return (\n    <header className={cn(\"flex h-12 shrink-0 items-center gap-2 border-b border-border/70 px-3\", className)} {...props}>\n      <SidebarTrigger size=\"xs\" className=\"lg:hidden\" />\n      <Heading level={levels.title} size=\"label\" weight=\"medium\" className=\"min-w-0 truncate\" title={title}>\n        {title}\n      </Heading>\n      <Badge size=\"sm\" color={agentRunStatusColors[status]}>\n        {agentRunStatusLabels[status]}\n      </Badge>\n      {startedAt === undefined ? null : <AgentRunElapsed startedAt={startedAt} endedAt={endedAt} className=\"text-body\" />}\n      <div className=\"ms-auto flex shrink-0 items-center gap-1\">{actions}</div>\n      <LiveStatus message={`${title}: ${agentRunStatusLabels[status]}`} />\n    </header>\n  );\n}\n\nexport type AgentRunBodyProps = ComponentProps<\"div\"> & { aside?: ReactNode };\n\nexport function AgentRunBody({ aside, className, children, ...props }: AgentRunBodyProps) {\n  return (\n    <div\n      className={cn(\n        \"grid min-h-0 flex-1 content-start overflow-y-auto @3xl/agent-run:grid-cols-[minmax(0,1fr)_minmax(18rem,22rem)] @3xl/agent-run:content-stretch @3xl/agent-run:overflow-hidden\",\n        className,\n      )}\n      {...props}\n    >\n      <div className=\"min-w-0 px-4 py-5 @lg/agent-run:px-6 @3xl/agent-run:overflow-y-auto\">{children}</div>\n      {aside}\n    </div>\n  );\n}\n\nexport type AgentRunPlanProps = Omit<ComponentProps<\"section\">, \"title\"> & { title?: ReactNode; description?: ReactNode };\n\nexport function AgentRunPlan({ title = \"Plan\", description, className, children, ...props }: AgentRunPlanProps) {\n  const levels = useHeadingLevels();\n  const headingId = useId();\n\n  return (\n    <section aria-labelledby={headingId} className={cn(\"min-w-0\", className)} {...props}>\n      <Heading id={headingId} level={levels.section} size=\"label\" weight=\"medium\">\n        {title}\n      </Heading>\n      {description ? (\n        <Text size=\"caption\" tone=\"muted\">\n          {description}\n        </Text>\n      ) : null}\n      <Timeline className=\"mt-3\">{children}</Timeline>\n    </section>\n  );\n}\n\nconst stepIcons = {\n  neutral: <Circle aria-hidden=\"true\" />,\n  pending: <CircleDashed aria-hidden=\"true\" />,\n  running: <LoaderCircle className=\"motion-safe:animate-spin\" aria-hidden=\"true\" />,\n  waiting: <CirclePause aria-hidden=\"true\" />,\n  success: <Check aria-hidden=\"true\" />,\n  error: <CircleAlert aria-hidden=\"true\" />,\n} satisfies Record<TimelineState, ReactNode>;\n\nexport type AgentRunStepProps = Omit<TimelineItemProps, \"title\"> & {\n  title: ReactNode;\n  detail?: ReactNode;\n  startedAt?: number;\n  endedAt?: number;\n  actions?: ReactNode;\n};\n\nexport function AgentRunStep({ title, detail, startedAt, endedAt, actions, state = \"pending\", children, ...props }: AgentRunStepProps) {\n  const hasMeta = startedAt !== undefined || actions !== undefined;\n\n  return (\n    <TimelineItem state={state} {...props}>\n      <TimelineIndicator aria-label={agentRunStepLabels[state]}>{stepIcons[state]}</TimelineIndicator>\n      <TimelineSeparator />\n      <TimelineContent>\n        <TimelineTitle>{title}</TimelineTitle>\n        {detail ? <TimelineDescription>{detail}</TimelineDescription> : null}\n        {hasMeta ? (\n          <TimelineMeta>\n            {startedAt === undefined ? null : <AgentRunElapsed startedAt={startedAt} endedAt={endedAt} />}\n            {actions}\n          </TimelineMeta>\n        ) : null}\n        {children ? <div className=\"mt-2 grid gap-2\">{children}</div> : null}\n      </TimelineContent>\n    </TimelineItem>\n  );\n}\n\nfunction formatToolValue(value: unknown): string {\n  return typeof value === \"string\" ? value : JSON.stringify(value, null, 2);\n}\n\nexport type AgentRunToolCallProps = Omit<ActivityProps, \"kind\" | \"name\" | \"children\"> & {\n  name: string;\n  summary?: ReactNode;\n  icon?: ReactNode;\n  input?: unknown;\n  output?: unknown;\n  errorText?: string;\n  startedAt?: number;\n  endedAt?: number;\n};\n\nexport function AgentRunToolCall({ name, summary, icon, input, output, errorText, startedAt, endedAt, ...props }: AgentRunToolCallProps) {\n  const hasDetails = input !== undefined || output !== undefined || errorText !== undefined;\n  const Row = hasDetails ? ActivityTrigger : ActivityRow;\n\n  return (\n    <Activity kind=\"tool\" name={name} {...props}>\n      <Row>\n        {icon ? <ActivityIcon>{icon}</ActivityIcon> : <ActivityIcon />}\n        <ActivityTitle>\n          {name}\n          {summary ? <span className=\"text-muted-foreground\"> · {summary}</span> : null}\n        </ActivityTitle>\n        {startedAt === undefined ? null : <AgentRunElapsed startedAt={startedAt} endedAt={endedAt} className=\"ms-2\" />}\n        <ActivityStatus className=\"sr-only\" />\n      </Row>\n      {hasDetails ? (\n        <ActivityContent>\n          {input === undefined ? null : (\n            <ActivityDetail>\n              <ActivityDetailLabel>Input</ActivityDetailLabel>\n              <ActivityDetailContent format=\"code\">{formatToolValue(input)}</ActivityDetailContent>\n            </ActivityDetail>\n          )}\n          {output === undefined ? null : (\n            <ActivityDetail>\n              <ActivityDetailLabel>Output</ActivityDetailLabel>\n              <ActivityDetailContent format=\"code\">{formatToolValue(output)}</ActivityDetailContent>\n            </ActivityDetail>\n          )}\n          {errorText === undefined ? null : (\n            <ActivityDetail>\n              <ActivityDetailLabel>Error</ActivityDetailLabel>\n              <ActivityDetailContent className=\"text-destructive-text\">{errorText}</ActivityDetailContent>\n            </ActivityDetail>\n          )}\n        </ActivityContent>\n      ) : null}\n    </Activity>\n  );\n}\n\nexport type AgentRunAsideProps = ComponentProps<\"aside\">;\n\nexport function AgentRunAside({ className, ...props }: AgentRunAsideProps) {\n  return (\n    <aside\n      aria-label=\"Run output\"\n      className={cn(\n        \"grid content-start gap-4 border-t border-border/70 px-4 py-5 @lg/agent-run:px-6 @3xl/agent-run:overflow-y-auto @3xl/agent-run:border-t-0 @3xl/agent-run:border-s\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\nexport type AgentRunSectionProps = Omit<ComponentProps<typeof Card>, \"title\"> & { title: ReactNode; actions?: ReactNode };\n\nexport function AgentRunSection({ title, actions, children, ...props }: AgentRunSectionProps) {\n  const levels = useHeadingLevels();\n\n  return (\n    <Card {...props}>\n      <CardHeader>\n        <Heading level={levels.section} size=\"label\" weight=\"medium\">\n          {title}\n        </Heading>\n        {actions ? <CardAction>{actions}</CardAction> : null}\n      </CardHeader>\n      <CardContent>{children}</CardContent>\n    </Card>\n  );\n}\n\nexport type AgentRunDetail = { label: string; value: ReactNode };\n\nexport type AgentRunDetailsProps = Omit<AgentRunSectionProps, \"title\" | \"children\"> & {\n  title?: ReactNode;\n  items: readonly AgentRunDetail[];\n};\n\nexport function AgentRunDetails({ title = \"Details\", items, ...props }: AgentRunDetailsProps) {\n  if (items.length === 0) return null;\n\n  return (\n    <AgentRunSection title={title} {...props}>\n      <dl className=\"grid grid-cols-[auto_minmax(0,1fr)] gap-x-4 gap-y-2 text-body\">\n        {items.map((item) => (\n          <div key={item.label} className=\"contents\">\n            <dt className=\"text-muted-foreground\">{item.label}</dt>\n            <dd className=\"min-w-0 break-words\">{item.value}</dd>\n          </div>\n        ))}\n      </dl>\n    </AgentRunSection>\n  );\n}\n"
    }
  ]
}
