{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "agent-07",
  "title": "Agent monitor",
  "description": "One agent at work: the step it is on with its clock, a cost figure that rolls each time a step ends and once more when the run does, a time strip that fills, the exact command waiting for Allow or Deny, and a fold into a slim bar.",
  "dependencies": [
    "motion"
  ],
  "registryDependencies": [
    "https://ui.ndekocode.dev/r/agent-run.json",
    "https://ui.ndekocode.dev/r/button.json",
    "https://ui.ndekocode.dev/r/chat-motion.json",
    "https://ui.ndekocode.dev/r/copy-button.json",
    "https://ui.ndekocode.dev/r/demonstration.json",
    "https://ui.ndekocode.dev/r/hatch.json",
    "https://ui.ndekocode.dev/r/icons.json",
    "https://ui.ndekocode.dev/r/live-duration.json",
    "https://ui.ndekocode.dev/r/live-region.json",
    "https://ui.ndekocode.dev/r/mesh-tile.json",
    "https://ui.ndekocode.dev/r/motion-number.json",
    "https://ui.ndekocode.dev/r/motion-presets.json",
    "https://ui.ndekocode.dev/r/motion-word-roll.json",
    "https://ui.ndekocode.dev/r/replay-button.json",
    "https://ui.ndekocode.dev/r/run-session.json",
    "https://ui.ndekocode.dev/r/theme.json",
    "https://ui.ndekocode.dev/r/tooltip.json",
    "https://ui.ndekocode.dev/r/use-kept-height.json",
    "https://ui.ndekocode.dev/r/use-prefers-reduced-motion.json",
    "https://ui.ndekocode.dev/r/use-session.json",
    "https://ui.ndekocode.dev/r/utils.json",
    "https://ui.ndekocode.dev/r/words.json"
  ],
  "files": [
    {
      "path": "registry/ndeko/components/agent/agent-07.tsx",
      "content": "'use client';\n\nimport { AnimatePresence, motion } from 'motion/react';\nimport { useId, useRef, useState } from 'react';\n\nimport { WordRoll } from '@/components/core/word-roll';\nimport { useKeptHeight } from '@/registry/ndeko/hooks/use-kept-height';\nimport { usePrefersReducedMotion } from '@/registry/ndeko/hooks/use-prefers-reduced-motion';\nimport {\n  useSession,\n  type SessionStep,\n} from '@/registry/ndeko/hooks/use-session';\nimport {\n  elapsedOf,\n  emptyRun,\n  formatDuration,\n  seriesColorOf,\n  type AgentStatus as AgentState,\n  type ApprovalRequest,\n  type RunAgent,\n  type RunEvent,\n  type RunSnapshot,\n  type RunStatus,\n  type SeriesSlot,\n  type ToolCall,\n} from '@/registry/ndeko/lib/agent-run';\nimport {\n  pressedSolidOf,\n  spokenUnless,\n} from '@/registry/ndeko/lib/demonstration';\nimport { hatchOf } from '@/registry/ndeko/lib/hatch';\nimport {\n  CheckIcon,\n  ChevronsUpDownIcon,\n  ListCollapseIcon,\n  PauseIcon,\n  PlayIcon,\n  StopIcon,\n} from '@/registry/ndeko/lib/icons';\nimport {\n  fadePresence,\n  rowPresenceOf,\n} from '@/registry/ndeko/lib/motion-presets';\nimport { applyBatchAt, restingOf } from '@/registry/ndeko/lib/run-session';\nimport { cn } from '@/registry/ndeko/lib/utils';\nimport { countOf } from '@/registry/ndeko/lib/words';\nimport { Button } from '@/registry/ndeko/ui/button';\nimport { Swap } from '@/registry/ndeko/ui/chat-motion';\nimport { CopyButton } from '@/registry/ndeko/ui/copy-button';\nimport {\n  ClockGate,\n  LiveDuration,\n  useLiveNow,\n} from '@/registry/ndeko/ui/live-duration';\nimport { LiveRegion } from '@/registry/ndeko/ui/live-region';\nimport { ReplayButton } from '@/registry/ndeko/ui/replay-button';\nimport {\n  Tooltip,\n  TooltipContent,\n  TooltipTrigger,\n} from '@/registry/ndeko/ui/tooltip';\n\nimport { AgentStatusDot } from './_parts/agent-status';\nimport { AgentTile } from './_parts/agent-tile';\nimport { CostFigure } from './_parts/cost-figure';\nimport { CODER } from './_parts/crew';\nimport { DECIDE_AFTER_MS } from './_parts/demo';\nimport { focusLater } from './_parts/focus-later';\nimport { KeyedHeight } from './_parts/keyed-height';\nimport { ToolTile } from './_parts/tool-tile';\n\nexport type MonitorMode =\n  'idle' | 'running' | 'waiting' | 'paused' | 'stopped' | 'done' | 'failed';\n\nconst MODE_WORD: Record<MonitorMode, string> = {\n  idle: 'Not started',\n  running: 'Running',\n  waiting: 'Waiting on you',\n  paused: 'Paused',\n  stopped: 'Stopped',\n  done: 'Done',\n  failed: 'Failed',\n};\n\nconst MODE_WORDS = Object.values(MODE_WORD);\n\nconst MODE_DOT: Record<MonitorMode, AgentState> = {\n  idle: 'idle',\n  running: 'running',\n  waiting: 'waiting',\n  paused: 'idle',\n  stopped: 'idle',\n  done: 'done',\n  failed: 'failed',\n};\n\nconst WAITING_PART: React.CSSProperties = {\n  backgroundColor: 'color-mix(in oklab, var(--warning) 14%, transparent)',\n  backgroundImage: hatchOf('var(--warning)', 60),\n};\n\nconst FAILED_PART: React.CSSProperties = {\n  backgroundColor: 'color-mix(in oklab, var(--destructive) 12%, transparent)',\n  backgroundImage: hatchOf('var(--destructive)', 55),\n};\n\n/** A ticking duration keeps its width, so nothing beside it shifts ten times a second. */\nconst TICKING_TIME = 'inline-block min-w-8';\n\nexport function monitorModeOf(status: RunStatus, isPaused: boolean) {\n  if (status === 'cancelled') {\n    return 'stopped';\n  }\n  if (status === 'done') {\n    return 'done';\n  }\n  if (status === 'failed') {\n    return 'failed';\n  }\n  if (isPaused) {\n    return 'paused';\n  }\n  if (status === 'waiting') {\n    return 'waiting';\n  }\n  if (status === 'running') {\n    return 'running';\n  }\n  return 'idle';\n}\n\nfunction isEndedMode(mode: MonitorMode) {\n  const isDone = mode === 'done';\n  const isStopped = mode === 'stopped';\n  const isFailed = mode === 'failed';\n  return isDone || isStopped || isFailed;\n}\n\nfunction isOpenCall(call: ToolCall) {\n  const isRunning = call.status === 'running';\n  const isWaiting = call.status === 'waiting';\n  return isRunning || isWaiting;\n}\n\nfunction isFinishedCall(call: ToolCall) {\n  const isDone = call.status === 'done';\n  const isFailed = call.status === 'failed';\n  return isDone || isFailed;\n}\n\nexport function currentCallOf(calls: ToolCall[]) {\n  const openCalls = calls.filter(isOpenCall);\n  return openCalls.at(-1);\n}\n\nfunction pendingApprovalFor(\n  approvals: ApprovalRequest[],\n  call: ToolCall | undefined\n) {\n  if (!call) {\n    return undefined;\n  }\n  return approvals.find((approval) => {\n    const isForCall = approval.id === call.id;\n    const isPending = approval.status === 'pending';\n    return isForCall && isPending;\n  });\n}\n\nfunction finishedDurationOf(call: ToolCall) {\n  const end = call.endedAt ?? 0;\n  const elapsed = elapsedOf(call, end);\n  return formatDuration(elapsed);\n}\n\nfunction lowerFirst(text: string) {\n  const first = text.charAt(0).toLowerCase();\n  const rest = text.slice(1);\n  return `${first}${rest}`;\n}\n\ntype StripPart = {\n  key: string;\n  /** Where the part starts on the strip and how much of it is its own, both from 0 to 1. */\n  start: number;\n  share: number;\n  isOpen: boolean;\n  style: React.CSSProperties;\n};\n\n/**\n * The strip is drawn one step ahead and travels there at a constant speed, so one transition plays\n * per step and no part is ever laid out again. `GROW` below lasts exactly as long.\n */\nconst GROW_STEP_MS = 1000;\n\nconst MIN_PART_SHARE = 0.012;\n\nconst GROW =\n  'transition-transform duration-1000 ease-linear motion-reduce:transition-none';\n\nconst GLIDE =\n  'transition-transform duration-300 ease-(--ease-in-out-strong) motion-reduce:transition-none';\n\n/** Without motion nothing travels, so nothing is drawn ahead of the present. */\nfunction leadOf(isTicking: boolean, shouldReduceMotion: boolean | null) {\n  if (shouldReduceMotion) {\n    return 0;\n  }\n  if (isTicking) {\n    return GROW_STEP_MS;\n  }\n  return 0;\n}\n\nfunction travelOf(isTicking: boolean) {\n  if (isTicking) {\n    return GROW;\n  }\n  return GLIDE;\n}\n\nfunction solidPartOf(\n  call: ToolCall,\n  series: SeriesSlot,\n  isCurrent: boolean\n): React.CSSProperties {\n  const color = seriesColorOf(series);\n  if (call.status === 'failed') {\n    return FAILED_PART;\n  }\n  if (isCurrent) {\n    return { backgroundColor: color };\n  }\n  return { backgroundColor: `color-mix(in oklab, ${color} 42%, transparent)` };\n}\n\nfunction isGateCall(call: ToolCall, approvals: ApprovalRequest[]) {\n  return approvals.some((approval) => approval.id === call.id);\n}\n\n/** A step that waited on a person keeps its hatch after the decision: the wait stays in the history. */\nfunction partStyleOf(\n  call: ToolCall,\n  approvals: ApprovalRequest[],\n  series: SeriesSlot,\n  current: ToolCall | undefined\n) {\n  if (isGateCall(call, approvals)) {\n    return WAITING_PART;\n  }\n  const isCurrent = call.id === current?.id;\n  return solidPartOf(call, series, isCurrent);\n}\n\nfunction firstStartOf(calls: ToolCall[]) {\n  for (const call of calls) {\n    if (call.startedAt !== undefined) {\n      return call.startedAt;\n    }\n  }\n  return undefined;\n}\n\nfunction lastMomentOf(calls: ToolCall[], reach: number) {\n  let last: number | undefined;\n  for (const call of calls) {\n    const hasStarted = call.startedAt !== undefined;\n    if (!hasStarted) {\n      continue;\n    }\n    const end = call.endedAt ?? reach;\n    const previous = last ?? end;\n    last = Math.max(previous, end);\n  }\n  return last ?? reach;\n}\n\n/**\n * Where the strip stops: the present while the agent works, the end of its last step once it rests.\n * A run that ended keeps the times it was given, which can be far from the clock of the page.\n */\nfunction stripEndOf(calls: ToolCall[], reach: number, isTicking: boolean) {\n  if (isTicking) {\n    return reach;\n  }\n  return lastMomentOf(calls, reach);\n}\n\n/**\n * Every step sits where it happened in time. While the run fits in the window, a finished part\n * never moves and the open one only grows; a longer run takes the whole strip and every part\n * gives way.\n */\nfunction stripPartsOf(\n  calls: ToolCall[],\n  approvals: ApprovalRequest[],\n  series: SeriesSlot,\n  current: ToolCall | undefined,\n  reach: number,\n  end: number,\n  windowMs: number\n) {\n  const origin = firstStartOf(calls) ?? reach;\n  const lived = end - origin;\n  const whole = Math.max(1, lived, windowMs);\n  const parts: StripPart[] = [];\n  for (const call of calls) {\n    const startedAt = call.startedAt;\n    if (startedAt === undefined) {\n      continue;\n    }\n    const sinceOrigin = Math.max(0, startedAt - origin);\n    const elapsed = elapsedOf(call, reach);\n    const share = elapsed / whole;\n    parts.push({\n      key: call.id,\n      start: sinceOrigin / whole,\n      share: Math.max(share, MIN_PART_SHARE),\n      isOpen: isOpenCall(call),\n      style: partStyleOf(call, approvals, series, current),\n    });\n  }\n  return parts;\n}\n\ntype PartFrameStyle = React.CSSProperties & {\n  '--part-at': string;\n  '--part-born': string;\n};\n\ntype PartFillStyle = React.CSSProperties & { '--fill-at': string };\n\n/**\n * A part is a window on the strip with its fill behind it: the window ends where the part ends and\n * the fill starts where it starts, so only what both cover shows. Two moves on `translate`, and a\n * hatch is never stretched the way a scale would stretch it. The places are custom properties read\n * by a class: a place written inline would win over the starting style a part is born from.\n */\nfunction partFrameStyleOf(part: StripPart): PartFrameStyle {\n  const end = part.start + part.share;\n  const offset = (end - 1) * 100;\n  const bornAt = (part.start - 1) * 100;\n  return { '--part-at': `${offset}%`, '--part-born': `${bornAt}%` };\n}\n\nfunction partFillStyleOf(part: StripPart): PartFillStyle {\n  const offset = (1 - part.share) * 100;\n  return { ...part.style, '--fill-at': `${offset}%` };\n}\n\nfunction boundaryStyleOf(part: StripPart): React.CSSProperties {\n  const offset = part.start * 100;\n  return { transform: `translateX(${offset}%)` };\n}\n\n/** A step that is open starts from nothing, so it is seen growing from its first frame. */\nfunction frameBirthOf(isOpen: boolean) {\n  if (isOpen) {\n    return 'starting:translate-x-(--part-born)';\n  }\n  return undefined;\n}\n\nfunction fillBirthOf(isOpen: boolean) {\n  if (isOpen) {\n    return 'starting:translate-x-full';\n  }\n  return undefined;\n}\n\nfunction TimeStrip({\n  calls,\n  approvals,\n  series,\n  current,\n  isTicking,\n  windowMs,\n}: {\n  calls: ToolCall[];\n  approvals: ApprovalRequest[];\n  series: SeriesSlot;\n  current: ToolCall | undefined;\n  isTicking: boolean;\n  windowMs: number;\n}) {\n  const shouldReduceMotion = usePrefersReducedMotion();\n  const now = useLiveNow(GROW_STEP_MS, isTicking);\n  const lead = leadOf(isTicking, shouldReduceMotion);\n  const reach = now + lead;\n  const end = stripEndOf(calls, reach, isTicking);\n  const parts = stripPartsOf(\n    calls,\n    approvals,\n    series,\n    current,\n    reach,\n    end,\n    windowMs\n  );\n  const laterParts = parts.slice(1);\n  const travel = travelOf(isTicking);\n\n  return (\n    <div\n      aria-hidden='true'\n      className='bg-foreground/[0.05] relative h-1.5 overflow-hidden rounded-full'\n    >\n      {parts.map((part) => (\n        <span\n          key={part.key}\n          className={cn(\n            'absolute inset-0 translate-x-(--part-at) overflow-hidden',\n            travel,\n            frameBirthOf(part.isOpen)\n          )}\n          style={partFrameStyleOf(part)}\n        >\n          <span\n            className={cn(\n              'absolute inset-0 translate-x-(--fill-at)',\n              travel,\n              fillBirthOf(part.isOpen)\n            )}\n            style={partFillStyleOf(part)}\n          />\n        </span>\n      ))}\n      {laterParts.map((part) => (\n        <span\n          key={part.key}\n          className={cn('absolute inset-0', travel)}\n          style={boundaryStyleOf(part)}\n        >\n          <span className='bg-card absolute inset-y-0 -left-px w-0.5' />\n        </span>\n      ))}\n    </div>\n  );\n}\n\nfunction CommandAsk({\n  call,\n  approval,\n  isAllowPressed,\n  onAllow,\n  onDeny,\n}: {\n  call: ToolCall;\n  approval: ApprovalRequest;\n  isAllowPressed: boolean;\n  onAllow?: (approvalId: string) => void;\n  onDeny?: (approvalId: string) => void;\n}) {\n  const command = approval.command ?? call.label;\n  const waitingSince = { startedAt: approval.requestedAt };\n\n  return (\n    <div className='flex flex-col gap-3 pt-3'>\n      <div className='bg-foreground/[0.04] flex items-start gap-2 rounded-lg py-2 ps-3 pe-1.5'>\n        <code className='min-w-0 flex-1 py-0.5 font-mono text-xs leading-5 break-all'>\n          <span\n            aria-hidden='true'\n            className='text-muted-foreground select-none'\n          >\n            ${' '}\n          </span>\n          {command}\n        </code>\n        <CopyButton text={command} label='Copy the command' />\n      </div>\n      {approval.detail && (\n        <p className='text-muted-foreground text-xs leading-5'>\n          {approval.detail}\n        </p>\n      )}\n      <div className='flex flex-wrap items-center gap-2'>\n        <Button\n          type='button'\n          size='sm'\n          onClick={() => onAllow?.(approval.id)}\n          className={pressedSolidOf(isAllowPressed)}\n        >\n          <CheckIcon />\n          Allow\n        </Button>\n        <Button\n          type='button'\n          size='sm'\n          variant='outline'\n          onClick={() => onDeny?.(approval.id)}\n        >\n          Deny\n        </Button>\n        <span className='text-muted-foreground ms-auto text-xs'>\n          Waiting{' '}\n          <LiveDuration\n            item={waitingSince}\n            isRunning\n            className={TICKING_TIME}\n          />\n        </span>\n      </div>\n    </div>\n  );\n}\n\nfunction summaryOf(mode: MonitorMode, finishedCount: number) {\n  const steps = countOf(finishedCount, 'step');\n  if (mode === 'done') {\n    return `Done after ${steps}`;\n  }\n  if (mode === 'stopped') {\n    return `Stopped by you after ${steps}`;\n  }\n  if (mode === 'failed') {\n    return `Failed after ${steps}`;\n  }\n  if (mode === 'idle') {\n    return 'Waits for its first step';\n  }\n  return 'Thinking about the next step';\n}\n\nfunction stepTitleOf(call: ToolCall, pending: ApprovalRequest | undefined) {\n  if (pending) {\n    return pending.title;\n  }\n  return call.label;\n}\n\n/** What the agent is on: the step that runs, the request that waits, or a line on where it stands. */\nfunction StepIdentity({\n  agent,\n  mode,\n  current,\n  pending,\n  finishedCount,\n  isTicking,\n}: {\n  agent: RunAgent;\n  mode: MonitorMode;\n  current: ToolCall | undefined;\n  pending: ApprovalRequest | undefined;\n  finishedCount: number;\n  isTicking: boolean;\n}) {\n  if (!current) {\n    return (\n      <p className='flex min-h-11 items-center text-[15px] leading-6 font-medium'>\n        {summaryOf(mode, finishedCount)}\n      </p>\n    );\n  }\n  const isAsking = pending !== undefined;\n\n  return (\n    <div className='flex min-h-11 items-start gap-3'>\n      <ToolTile tool={current.tool} size='lg' />\n      <div className='min-w-0 flex-1'>\n        {isAsking && (\n          <p className='text-warning-foreground text-xs leading-5'>\n            {agent.name} needs your OK\n          </p>\n        )}\n        {!isAsking && (\n          <p className='text-muted-foreground flex items-baseline gap-2 text-xs leading-5'>\n            <span className='font-mono'>{current.tool}</span>\n            <LiveDuration\n              item={current}\n              isRunning={isTicking}\n              className={TICKING_TIME}\n            />\n          </p>\n        )}\n        <p className='text-[15px] leading-6 font-medium text-balance'>\n          {stepTitleOf(current, pending)}\n        </p>\n      </div>\n    </div>\n  );\n}\n\nfunction costNoteOf(isEnded: boolean) {\n  if (isEnded) {\n    return 'in total';\n  }\n  return 'so far';\n}\n\n/**\n * The figure of the monitor: what the agent has spent. It stays in place while the steps change\n * beside it, so it rolls once each time a step ends, and a last time when the run does.\n */\nfunction MonitorFigure({ usd, isEnded }: { usd: number; isEnded: boolean }) {\n  return (\n    <div className='shrink-0 text-end'>\n      <CostFigure usd={usd} className='text-2xl leading-8 font-medium' />\n      <p className='text-muted-foreground text-xs'>{costNoteOf(isEnded)}</p>\n    </div>\n  );\n}\n\nfunction noteOf(call: ToolCall) {\n  if (call.status === 'failed') {\n    return call.error ?? '';\n  }\n  return '';\n}\n\nfunction FinishedSteps({ calls }: { calls: ToolCall[] }) {\n  const shouldReduceMotion = usePrefersReducedMotion();\n  const rowPresence = rowPresenceOf(shouldReduceMotion);\n  const finished = calls.filter(isFinishedCall);\n  const newestFirst = [...finished].reverse();\n  const hasFinished = newestFirst.length > 0;\n\n  return (\n    <div className='flex flex-col gap-1'>\n      <p className='text-muted-foreground text-xs font-medium'>\n        Finished steps\n      </p>\n      {!hasFinished && (\n        <p className='text-muted-foreground text-xs leading-6'>\n          Each step lands here when it ends\n        </p>\n      )}\n      <ol aria-label='Finished steps' className='flex flex-col'>\n        <AnimatePresence initial={false}>\n          {newestFirst.map((call) => {\n            const isFailed = call.status === 'failed';\n            const note = noteOf(call);\n            const hasNote = note !== '';\n\n            return (\n              <motion.li\n                key={call.id}\n                className='overflow-hidden'\n                {...rowPresence}\n              >\n                <div className='flex items-start gap-3 py-1'>\n                  <ToolTile tool={call.tool} size='sm' />\n                  <div className='min-w-0 flex-1'>\n                    <p\n                      className={cn(\n                        'truncate text-[13px] leading-6',\n                        isFailed && 'text-muted-foreground'\n                      )}\n                    >\n                      {call.label}\n                    </p>\n                    {hasNote && (\n                      <p className='text-destructive-ink text-xs'>{note}</p>\n                    )}\n                  </div>\n                  <span className='text-muted-foreground shrink-0 text-xs leading-6 tabular-nums'>\n                    {finishedDurationOf(call)}\n                  </span>\n                </div>\n              </motion.li>\n            );\n          })}\n        </AnimatePresence>\n      </ol>\n    </div>\n  );\n}\n\nfunction FoldedBar({\n  agent,\n  mode,\n  current,\n  isTicking,\n}: {\n  agent: RunAgent;\n  mode: MonitorMode;\n  current: ToolCall | undefined;\n  isTicking: boolean;\n}) {\n  const word = lowerFirst(MODE_WORD[mode]);\n\n  return (\n    <div className='flex min-h-11 items-center gap-2.5 py-2 ps-3.5 pe-12'>\n      <AgentStatusDot status={MODE_DOT[mode]} />\n      <p className='min-w-0 flex-1 truncate text-[13px] leading-5'>\n        <span className='font-medium'>{agent.name}</span>,{' '}\n        {current && (\n          <>\n            {lowerFirst(current.label)},{' '}\n            <LiveDuration item={current} isRunning={isTicking} />,{' '}\n          </>\n        )}\n        {!current && <>{word}, </>}\n        <CostFigure usd={agent.costUsd ?? 0} />\n      </p>\n    </div>\n  );\n}\n\nfunction pauseLabelOf(isPaused: boolean, name: string) {\n  if (isPaused) {\n    return `Resume ${name}`;\n  }\n  return `Pause ${name}`;\n}\n\nfunction foldLabelOf(isFolded: boolean) {\n  if (isFolded) {\n    return 'Unfold the monitor';\n  }\n  return 'Fold the monitor into a bar';\n}\n\nfunction IconAction({\n  label,\n  onClick,\n  children,\n  className,\n}: {\n  label: string;\n  onClick: () => void;\n  children: React.ReactNode;\n  className?: string;\n}) {\n  return (\n    <Tooltip>\n      <TooltipTrigger\n        render={\n          <Button\n            type='button'\n            variant='ghost'\n            size='icon-sm'\n            aria-label={label}\n            onClick={onClick}\n            className={cn('text-muted-foreground', className)}\n          />\n        }\n      >\n        {children}\n      </TooltipTrigger>\n      <TooltipContent>{label}</TooltipContent>\n    </Tooltip>\n  );\n}\n\nfunction canPauseIn(mode: MonitorMode) {\n  const isRunning = mode === 'running';\n  const isPaused = mode === 'paused';\n  return isRunning || isPaused;\n}\n\nfunction canStopIn(mode: MonitorMode) {\n  const canPause = canPauseIn(mode);\n  const isWaiting = mode === 'waiting';\n  return canPause || isWaiting;\n}\n\nconst NO_APPROVALS: ApprovalRequest[] = [];\n\nexport type AgentMonitorProps = {\n  agent: RunAgent;\n  /** The agent's steps in order. An approval that gates a step shares its id. */\n  calls: ToolCall[];\n  status: RunStatus;\n  approvals?: ApprovalRequest[];\n  isPaused?: boolean;\n  onPauseChange?: (isPaused: boolean) => void;\n  onStop?: () => void;\n  onAllow?: (approvalId: string) => void;\n  onDeny?: (approvalId: string) => void;\n  /**\n   * How much time the strip spans from the first step. While the run fits in it a finished part\n   * never moves and the open one only grows. Without it the strip follows the length of the run.\n   */\n  windowMs?: number;\n  /** Shows Allow held down, for a demonstration that presses it. */\n  isAllowPressed?: boolean;\n  defaultFolded?: boolean;\n  className?: string;\n};\n\nexport function AgentMonitor({\n  agent,\n  calls,\n  status,\n  approvals = NO_APPROVALS,\n  isPaused = false,\n  onPauseChange,\n  onStop,\n  onAllow,\n  onDeny,\n  windowMs = 0,\n  isAllowPressed = false,\n  defaultFolded = false,\n  className,\n}: AgentMonitorProps) {\n  const [isFolded, setIsFolded] = useState(defaultFolded);\n  const rootRef = useRef<HTMLElement>(null);\n  const bodyId = useId();\n  const shouldReduceMotion = usePrefersReducedMotion();\n  const rowPresence = rowPresenceOf(shouldReduceMotion);\n  const fade = fadePresence();\n  const mode = monitorModeOf(status, isPaused);\n  const current = currentCallOf(calls);\n  const pending = pendingApprovalFor(approvals, current);\n  const isRunning = mode === 'running';\n  const isWaiting = mode === 'waiting';\n  const isTicking = isRunning || isWaiting;\n  const finished = calls.filter(isFinishedCall);\n  const showsPause = canPauseIn(mode);\n  const showsStop = canStopIn(mode);\n  const pauseLabel = pauseLabelOf(isPaused, agent.name);\n  const foldLabel = foldLabelOf(isFolded);\n  const currentKey = current?.id ?? 'rest';\n  const pendingKey = pending?.id ?? 'free';\n  const stepKey = `${currentKey}-${pendingKey}-${mode}`;\n  const isAsking = current !== undefined && pending !== undefined;\n  const cost = agent.costUsd ?? 0;\n\n  function toggleFolded() {\n    setIsFolded((value) => !value);\n  }\n\n  function togglePause() {\n    onPauseChange?.(!isPaused);\n  }\n\n  function keepFocus() {\n    focusLater(rootRef, '[data-monitor-toggle]');\n  }\n\n  function stop() {\n    onStop?.();\n    keepFocus();\n  }\n\n  function allow(approvalId: string) {\n    onAllow?.(approvalId);\n    keepFocus();\n  }\n\n  function deny(approvalId: string) {\n    onDeny?.(approvalId);\n    keepFocus();\n  }\n\n  return (\n    <section\n      ref={rootRef}\n      aria-label={`${agent.name} at work`}\n      className={cn(\n        'bg-card hairline-ring ring-border relative rounded-xl shadow-[0_1px_2px_rgb(0_0_0/0.04)]',\n        className\n      )}\n    >\n      <Button\n        type='button'\n        variant='ghost'\n        size='icon-sm'\n        aria-expanded={!isFolded}\n        aria-controls={bodyId}\n        aria-label={foldLabel}\n        onClick={toggleFolded}\n        data-monitor-toggle\n        className='text-muted-foreground absolute end-2.5 top-2.5 z-10'\n      >\n        <Swap swapKey={String(isFolded)}>\n          {isFolded && <ChevronsUpDownIcon />}\n          {!isFolded && <ListCollapseIcon />}\n        </Swap>\n      </Button>\n      <KeyedHeight changeKey={String(isFolded)}>\n        <div className='relative'>\n          <AnimatePresence mode='popLayout' initial={false}>\n            {isFolded && (\n              <motion.div key='bar' {...fade}>\n                <FoldedBar\n                  agent={agent}\n                  mode={mode}\n                  current={current}\n                  isTicking={isTicking}\n                />\n              </motion.div>\n            )}\n            {!isFolded && (\n              <motion.div\n                key='card'\n                id={bodyId}\n                className='flex flex-col gap-4 p-4'\n                {...fade}\n              >\n                <header className='flex items-center gap-3 pe-9'>\n                  <AgentTile\n                    name={agent.name}\n                    series={agent.series}\n                    size='lg'\n                  />\n                  <div className='min-w-0 flex-1'>\n                    <p className='flex items-baseline gap-2'>\n                      <span className='text-sm font-medium'>{agent.name}</span>\n                      <span className='text-muted-foreground truncate text-xs'>\n                        {agent.model}\n                      </span>\n                    </p>\n                    <p className='text-muted-foreground flex items-center gap-1.5 text-xs'>\n                      <AgentStatusDot status={MODE_DOT[mode]} size='sm' />\n                      <WordRoll text={MODE_WORD[mode]} labels={MODE_WORDS} />\n                    </p>\n                  </div>\n                  <div className='flex items-center'>\n                    {showsPause && (\n                      <IconAction label={pauseLabel} onClick={togglePause}>\n                        <Swap swapKey={String(isPaused)}>\n                          {isPaused && <PlayIcon />}\n                          {!isPaused && <PauseIcon />}\n                        </Swap>\n                      </IconAction>\n                    )}\n                    {showsStop && (\n                      <IconAction\n                        label={`Stop ${agent.name}`}\n                        onClick={stop}\n                        className='hover:text-destructive-ink'\n                      >\n                        <StopIcon />\n                      </IconAction>\n                    )}\n                  </div>\n                </header>\n                <div>\n                  <div className='flex items-start gap-3'>\n                    <div className='relative min-w-0 flex-1'>\n                      <AnimatePresence mode='popLayout' initial={false}>\n                        <motion.div key={stepKey} {...fade}>\n                          <StepIdentity\n                            agent={agent}\n                            mode={mode}\n                            current={current}\n                            pending={pending}\n                            finishedCount={finished.length}\n                            isTicking={isTicking}\n                          />\n                        </motion.div>\n                      </AnimatePresence>\n                    </div>\n                    <MonitorFigure usd={cost} isEnded={isEndedMode(mode)} />\n                  </div>\n                  <AnimatePresence initial={false}>\n                    {isAsking && (\n                      <motion.div\n                        key={pending.id}\n                        className='overflow-hidden'\n                        {...rowPresence}\n                      >\n                        <CommandAsk\n                          call={current}\n                          approval={pending}\n                          isAllowPressed={isAllowPressed}\n                          onAllow={allow}\n                          onDeny={deny}\n                        />\n                      </motion.div>\n                    )}\n                  </AnimatePresence>\n                </div>\n                <TimeStrip\n                  calls={calls}\n                  approvals={approvals}\n                  series={agent.series}\n                  current={current}\n                  isTicking={isTicking}\n                  windowMs={windowMs}\n                />\n                <FinishedSteps calls={calls} />\n              </motion.div>\n            )}\n          </AnimatePresence>\n        </div>\n      </KeyedHeight>\n    </section>\n  );\n}\n\nconst GATE_ID = 'tests-ask';\n\nconst ALLOWED_RUN_ID = 'tests-repeat';\n\nconst COMMAND = 'pnpm test checkout.spec.ts --repeat 20';\n\nconst GATE: ApprovalRequest = {\n  id: GATE_ID,\n  title: 'Run the checkout tests 20 times',\n  risk: 'low',\n  status: 'pending',\n  agentId: CODER.id,\n  command: COMMAND,\n  detail:\n    'It runs in the sandbox and writes only the test report. About 30 seconds.',\n};\n\nconst INITIAL_RUN = emptyRun({\n  id: 'coder-monitor',\n  title: 'Fix the flaky checkout test',\n  agents: [{ ...CODER, parentId: undefined, costUsd: 0 }],\n});\n\nfunction started(id: string, tool: string, label: string): RunEvent {\n  return {\n    type: 'call.started',\n    call: { id, tool, label, status: 'running', agentId: CODER.id },\n  };\n}\n\nfunction finished(id: string, label: string): RunEvent {\n  return { type: 'call.updated', id, patch: { status: 'done', label } };\n}\n\nfunction spent(costUsd: number): RunEvent {\n  return { type: 'agent.updated', id: CODER.id, patch: { costUsd } };\n}\n\ntype MonitorState = {\n  run: RunSnapshot;\n  isPaused: boolean;\n  pausedAt: number;\n};\n\ntype MonitorAction =\n  | { type: 'events'; events: RunEvent[] }\n  | { type: 'pause' }\n  | { type: 'resume' }\n  | { type: 'allow'; approvalId: string }\n  | { type: 'deny'; approvalId: string }\n  | { type: 'stop' };\n\ntype MonitorStep = SessionStep<MonitorState, MonitorAction>;\n\nconst INITIAL: MonitorState = {\n  run: INITIAL_RUN,\n  isPaused: false,\n  pausedAt: 0,\n};\n\nfunction withEvents(state: MonitorState, events: RunEvent[], now: number) {\n  const run = applyBatchAt(state.run, { events }, now);\n  return { ...state, run };\n}\n\n/** The time spent paused is not the step's: its start moves forward by as much when the agent resumes. */\nfunction withResume(state: MonitorState, now: number): MonitorState {\n  const resumed = { ...state, isPaused: false };\n  const current = currentCallOf(state.run.calls);\n  const startedAt = current?.startedAt;\n  if (!current || startedAt === undefined) {\n    return resumed;\n  }\n  const pausedFor = now - state.pausedAt;\n  const shifted: RunEvent = {\n    type: 'call.updated',\n    id: current.id,\n    patch: { startedAt: startedAt + pausedFor },\n  };\n  return withEvents(resumed, [shifted], now);\n}\n\nfunction withAllow(state: MonitorState, approvalId: string, now: number) {\n  const events: RunEvent[] = [\n    { type: 'approval.resolved', id: approvalId, decision: 'approved' },\n    finished(GATE_ID, 'You allowed the 20 test runs'),\n    started(ALLOWED_RUN_ID, 'shell', 'Running the checkout tests 20 times'),\n  ];\n  return withEvents(state, events, now);\n}\n\nfunction withDeny(state: MonitorState, approvalId: string, now: number) {\n  const events: RunEvent[] = [\n    { type: 'approval.resolved', id: approvalId, decision: 'denied' },\n    {\n      type: 'call.updated',\n      id: GATE_ID,\n      patch: {\n        status: 'failed',\n        label: 'Did not run the tests 20 times',\n        error: 'Denied by you',\n      },\n    },\n  ];\n  return withEvents(state, events, now);\n}\n\nfunction withStop(state: MonitorState, now: number): MonitorState {\n  const current = currentCallOf(state.run.calls);\n  const events: RunEvent[] = [];\n  if (current) {\n    events.push({\n      type: 'call.updated',\n      id: current.id,\n      patch: { status: 'failed', error: 'Stopped by you' },\n    });\n  }\n  events.push(\n    { type: 'agent.updated', id: CODER.id, patch: { status: 'idle' } },\n    { type: 'run.finished', status: 'cancelled' }\n  );\n  const stopped = withEvents(state, events, now);\n  return { ...stopped, isPaused: false };\n}\n\nfunction applyAt(\n  state: MonitorState,\n  action: MonitorAction,\n  now: number\n): MonitorState {\n  if (action.type === 'pause') {\n    return { ...state, isPaused: true, pausedAt: now };\n  }\n  if (action.type === 'resume') {\n    return withResume(state, now);\n  }\n  if (action.type === 'allow') {\n    return withAllow(state, action.approvalId, now);\n  }\n  if (action.type === 'deny') {\n    return withDeny(state, action.approvalId, now);\n  }\n  if (action.type === 'stop') {\n    return withStop(state, now);\n  }\n  return withEvents(state, action.events, now);\n}\n\n/** Times are stamped when an action lands, as a backend would stream it. */\nfunction reducer(state: MonitorState, action: MonitorAction) {\n  return applyAt(state, action, Date.now());\n}\n\nfunction landing(after: number, ...events: RunEvent[]): MonitorStep {\n  return { after, action: { type: 'events', events } };\n}\n\nfunction isGateDecided(state: MonitorState) {\n  const gate = state.run.calls.find((call) => call.id === GATE_ID);\n  return gate?.status !== 'waiting';\n}\n\n/** The agent's steps wait while a person paused it, and for good once the run is stopped. */\nfunction isHeld(state: MonitorState) {\n  const isStopped = state.run.status === 'cancelled';\n  return state.isPaused || isStopped;\n}\n\nconst SCRIPT: MonitorStep[] = [\n  landing(\n    500,\n    { type: 'run.started' },\n    { type: 'agent.updated', id: CODER.id, patch: { status: 'running' } }\n  ),\n  landing(300, started('read-spec', 'read_file', 'Reading checkout.spec.ts')),\n  landing(1300, finished('read-spec', 'Read checkout.spec.ts'), spent(0.004)),\n  landing(400, started('run-tests', 'shell', 'Running the checkout tests')),\n  landing(\n    2600,\n    finished('run-tests', 'Ran the checkout tests: 1 of 12 failed'),\n    spent(0.011)\n  ),\n  landing(500, started('read-mock', 'read_file', 'Reading payment-mock.ts')),\n  landing(1100, finished('read-mock', 'Read payment-mock.ts'), spent(0.016)),\n  landing(\n    400,\n    started('edit-mock', 'edit_file', 'Raising the payment timeout to 8s')\n  ),\n  landing(\n    1500,\n    finished('edit-mock', 'Raised the payment timeout to 8s'),\n    spent(0.024)\n  ),\n  landing(\n    400,\n    started(GATE_ID, 'shell', 'Asking to run the tests 20 times'),\n    { type: 'call.updated', id: GATE_ID, patch: { status: 'waiting' } },\n    { type: 'approval.requested', approval: GATE }\n  ),\n  {\n    after: DECIDE_AFTER_MS,\n    by: 'person',\n    press: 'allow',\n    action: { type: 'allow', approvalId: GATE_ID },\n    isAnswered: isGateDecided,\n  },\n  landing(\n    3000,\n    finished(ALLOWED_RUN_ID, 'Ran the checkout tests 20 times: all passed'),\n    spent(0.031)\n  ),\n  landing(\n    500,\n    started('changelog', 'edit_file', 'Writing the fix into CHANGELOG.md')\n  ),\n  landing(\n    1200,\n    finished('changelog', 'Wrote the fix into CHANGELOG.md'),\n    spent(0.035)\n  ),\n  landing(300, started('commit', 'git', 'Committing the fix')),\n  landing(\n    900,\n    finished('commit', 'Committed “Raise the payment mock timeout”'),\n    spent(0.038),\n    { type: 'agent.updated', id: CODER.id, patch: { status: 'done' } },\n    { type: 'run.finished', status: 'done' }\n  ),\n];\n\n/** The run takes about seventeen seconds with its approval, so every step fits in a strip that never moves. */\nconst WINDOW_MS = 18_000;\n\nconst SESSION = {\n  reducer,\n  initial: INITIAL,\n  resting: restingOf(INITIAL, SCRIPT, applyAt),\n  script: SCRIPT,\n  loopAfter: 3000,\n  holdWhile: isHeld,\n};\n\nfunction announcementOf(run: RunSnapshot, isPaused: boolean) {\n  const mode = monitorModeOf(run.status, isPaused);\n  const current = currentCallOf(run.calls);\n  const word = MODE_WORD[mode];\n  if (!current) {\n    return `${CODER.name}: ${word}`;\n  }\n  return `${CODER.name}: ${word}. ${current.label}`;\n}\n\nexport default function Agent07() {\n  const { ref, isInView, isAutoplaying, state, act, pressed, restart } =\n    useSession(SESSION);\n  const { run, isPaused } = state;\n  const [agent] = run.agents;\n  const isEnded = run.endedAt !== undefined;\n  const canReplay = isEnded && !isAutoplaying;\n  const announcement = announcementOf(run, isPaused);\n\n  function changePause(shouldPause: boolean) {\n    if (shouldPause) {\n      act({ type: 'pause' });\n      return;\n    }\n    act({ type: 'resume' });\n  }\n\n  function allow(approvalId: string) {\n    act({ type: 'allow', approvalId });\n  }\n\n  function deny(approvalId: string) {\n    act({ type: 'deny', approvalId });\n  }\n\n  function stop() {\n    act({ type: 'stop' });\n  }\n\n  useKeptHeight(ref);\n\n  return (\n    <div\n      ref={ref}\n      className='flex min-h-[31.5rem] w-full max-w-[30rem] flex-col gap-2'\n    >\n      <ClockGate isActive={isInView}>\n        <AgentMonitor\n          agent={agent}\n          calls={run.calls}\n          status={run.status}\n          approvals={run.approvals}\n          isPaused={isPaused}\n          onPauseChange={changePause}\n          onStop={stop}\n          onAllow={allow}\n          onDeny={deny}\n          windowMs={WINDOW_MS}\n          isAllowPressed={pressed === 'allow'}\n        />\n      </ClockGate>\n      <div className='flex h-7 items-center justify-between gap-2 px-1'>\n        <span className='text-muted-foreground text-xs'>\n          Costs are illustrative\n        </span>\n        <ReplayButton isShown={canReplay} onReplay={restart} />\n      </div>\n      <LiveRegion message={spokenUnless(isAutoplaying, announcement)} />\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/agent/agent-07.tsx"
    },
    {
      "path": "registry/ndeko/components/agent/_parts/agent-status.tsx",
      "content": "'use client';\n\nimport { WordRoll } from '@/components/core/word-roll';\nimport type {\n  AgentStatus as AgentState,\n  RunAgent,\n} from '@/registry/ndeko/lib/agent-run';\nimport {\n  CircleCheckIcon,\n  CircleXIcon,\n  HandIcon,\n} from '@/registry/ndeko/lib/icons';\nimport { cn } from '@/registry/ndeko/lib/utils';\nimport { Swap } from '@/registry/ndeko/ui/chat-motion';\n\nexport const AGENT_STATUS_WORD: Record<AgentState, string> = {\n  idle: 'Idle',\n  running: 'Running',\n  waiting: 'Waiting on you',\n  failed: 'Failed',\n  done: 'Done',\n};\n\nexport const AGENT_STATES: AgentState[] = [\n  'idle',\n  'running',\n  'waiting',\n  'failed',\n  'done',\n];\n\nconst STATUS_WORDS = AGENT_STATES.map((state) => AGENT_STATUS_WORD[state]);\n\nconst DOT_TONE: Record<AgentState, string> = {\n  idle: 'bg-muted-foreground/45',\n  running: 'bg-info',\n  waiting: 'bg-warning',\n  failed: 'bg-destructive',\n  done: 'bg-success',\n};\n\nconst LABEL_TONE: Record<AgentState, string> = {\n  idle: 'text-muted-foreground',\n  running: 'text-foreground',\n  waiting: 'text-warning-foreground',\n  failed: 'text-destructive-ink',\n  done: 'text-muted-foreground',\n};\n\nconst DOT_SIZE = {\n  sm: 'size-1.5',\n  md: 'size-2',\n  lg: 'size-2.5',\n} as const;\n\nconst MARK_SIZE = {\n  sm: 'size-3',\n  md: 'size-3.5',\n  lg: 'size-4',\n} as const;\n\nexport type AgentStatusSize = keyof typeof DOT_SIZE;\n\nconst RING_TIMING: React.CSSProperties = { animationDuration: '2s' };\n\n/** A piece lets this many dots pulse at once. Past it, a running agent is told by its colour and its word. */\nexport const MAX_PULSING = 3;\n\n/** The running agents whose dot may pulse: the first ones, in the order given. */\nexport function pulsingIdsOf(\n  agents: Pick<RunAgent, 'id' | 'status'>[],\n  limit = MAX_PULSING\n) {\n  const running = agents.filter((agent) => agent.status === 'running');\n  const pulsing = running.slice(0, limit);\n  const ids = pulsing.map((agent) => agent.id);\n  return new Set(ids);\n}\n\nfunction reservedLabelsOf(shouldReserve: boolean) {\n  if (shouldReserve) {\n    return STATUS_WORDS;\n  }\n  return undefined;\n}\n\ntype AgentStatusDotProps = {\n  status: AgentState;\n  size?: AgentStatusSize;\n  /** Without a visible word next to it, the dot says its state to screen readers. */\n  isLabelled?: boolean;\n  /** Holds a running dot still: where three others already pulse, or where nothing is really running. */\n  isStill?: boolean;\n  className?: string;\n};\n\n/**\n * A running dot sends a soft ring out: one CSS keyframe on transform and opacity, which the theme\n * stops under reduced motion. Every other state holds still and is told by its colour.\n */\nexport function AgentStatusDot({\n  status,\n  size = 'md',\n  isLabelled = false,\n  isStill = false,\n  className,\n}: AgentStatusDotProps) {\n  const isRunning = status === 'running';\n  const isPulsing = isRunning && !isStill;\n  const word = AGENT_STATUS_WORD[status];\n\n  return (\n    <span\n      data-status={status}\n      className={cn('relative inline-flex shrink-0', DOT_SIZE[size], className)}\n    >\n      {isPulsing && (\n        <span\n          aria-hidden='true'\n          style={RING_TIMING}\n          className='bg-info/40 absolute inset-0 animate-ping rounded-full motion-reduce:hidden'\n        />\n      )}\n      <span\n        aria-hidden='true'\n        className={cn(\n          'relative size-full rounded-full transition-colors duration-200',\n          DOT_TONE[status]\n        )}\n      />\n      {isLabelled && <span className='sr-only'>{word}</span>}\n    </span>\n  );\n}\n\ntype StatusGlyphProps = {\n  status: AgentState;\n  size: AgentStatusSize;\n  isStill: boolean;\n};\n\nfunction StatusGlyph({ status, size, isStill }: StatusGlyphProps) {\n  const box = MARK_SIZE[size];\n  if (status === 'waiting') {\n    return <HandIcon className={cn('text-warning-foreground', box)} />;\n  }\n  if (status === 'failed') {\n    return <CircleXIcon className={cn('text-destructive-ink', box)} />;\n  }\n  if (status === 'done') {\n    return <CircleCheckIcon className={cn('text-success-foreground', box)} />;\n  }\n  return (\n    <span className={cn('flex items-center justify-center', box)}>\n      <AgentStatusDot status={status} size={size} isStill={isStill} />\n    </span>\n  );\n}\n\ntype AgentStatusMarkProps = {\n  status: AgentState;\n  size?: AgentStatusSize;\n  isStill?: boolean;\n  className?: string;\n};\n\n/**\n * The mark of a state, drawn so it reads without its colour: a dot while the agent is idle or runs,\n * a hand while it waits, a cross when it failed, a check when it ended. It swaps when the state changes.\n */\nexport function AgentStatusMark({\n  status,\n  size = 'md',\n  isStill = false,\n  className,\n}: AgentStatusMarkProps) {\n  const box = MARK_SIZE[size];\n\n  return (\n    <span\n      data-status={status}\n      aria-hidden='true'\n      className={cn('inline-flex shrink-0', box, className)}\n    >\n      <Swap swapKey={status} className={box}>\n        <StatusGlyph status={status} size={size} isStill={isStill} />\n      </Swap>\n    </span>\n  );\n}\n\nexport type AgentStatusProps = {\n  status: AgentState;\n  /** Leave the word out where space is short: a dot then carries it for screen readers. */\n  showLabel?: boolean;\n  /** Keeps the width of the longest word, so the row beside it never shifts. */\n  reserveWidth?: boolean;\n  size?: AgentStatusSize;\n  /** Holds a running mark still: a piece lets three pulse at most. */\n  isStill?: boolean;\n  className?: string;\n};\n\nexport function AgentStatus({\n  status,\n  showLabel = true,\n  reserveWidth = false,\n  size = 'md',\n  isStill = false,\n  className,\n}: AgentStatusProps) {\n  const word = AGENT_STATUS_WORD[status];\n  const labels = reservedLabelsOf(reserveWidth);\n\n  if (!showLabel) {\n    return (\n      <AgentStatusDot\n        status={status}\n        size={size}\n        isLabelled\n        isStill={isStill}\n        className={className}\n      />\n    );\n  }\n\n  return (\n    <span\n      className={cn(\n        'inline-flex items-center gap-1 text-xs transition-colors duration-200',\n        LABEL_TONE[status],\n        className\n      )}\n    >\n      <AgentStatusMark status={status} size={size} isStill={isStill} />\n      <WordRoll text={word} labels={labels} />\n    </span>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/agent/_parts/agent-status.tsx"
    },
    {
      "path": "registry/ndeko/components/agent/_parts/agent-tile.tsx",
      "content": "import { seriesColorOf, type SeriesSlot } from '@/registry/ndeko/lib/agent-run';\nimport { cn } from '@/registry/ndeko/lib/utils';\nimport { MeshTile } from '@/registry/ndeko/ui/mesh-tile';\n\nconst SIZE_CLASS = {\n  sm: 'size-5 rounded-[5px]',\n  md: 'size-6 rounded-md',\n  lg: 'size-8 rounded-lg',\n} as const;\n\nexport type AgentTileSize = keyof typeof SIZE_CLASS;\n\ntype AgentTileProps = {\n  name: string;\n  series: SeriesSlot;\n  size?: AgentTileSize;\n  className?: string;\n};\n\n/** The picture of an agent: a gradient drawn from its name, in its series colour. Its name always sits beside it. */\nexport function AgentTile({\n  name,\n  series,\n  size = 'md',\n  className,\n}: AgentTileProps) {\n  const color = seriesColorOf(series);\n  const sizeClass = SIZE_CLASS[size];\n\n  return (\n    <MeshTile seed={name} color={color} className={cn(sizeClass, className)} />\n  );\n}\n",
      "type": "registry:component",
      "target": "components/agent/_parts/agent-tile.tsx"
    },
    {
      "path": "registry/ndeko/components/agent/_parts/cost-figure.tsx",
      "content": "'use client';\n\nimport { MotionNumber } from '@/components/core/motion-number';\nimport { cn } from '@/registry/ndeko/lib/utils';\n\nconst SMALL_COST: Intl.NumberFormatOptions = {\n  style: 'currency',\n  currency: 'USD',\n  minimumFractionDigits: 3,\n  maximumFractionDigits: 3,\n};\n\nconst LARGE_COST: Intl.NumberFormatOptions = {\n  style: 'currency',\n  currency: 'USD',\n  minimumFractionDigits: 2,\n  maximumFractionDigits: 2,\n};\n\n/** Matches `formatCost`: three decimals under a dollar, two above. */\nexport function costFormatOf(usd: number) {\n  if (usd < 1) {\n    return SMALL_COST;\n  }\n  return LARGE_COST;\n}\n\ntype CostFigureProps = {\n  usd: number;\n  className?: string;\n};\n\nexport function CostFigure({ usd, className }: CostFigureProps) {\n  const format = costFormatOf(usd);\n\n  return (\n    <MotionNumber\n      value={usd}\n      format={format}\n      className={cn('tabular-nums', className)}\n    />\n  );\n}\n",
      "type": "registry:component",
      "target": "components/agent/_parts/cost-figure.tsx"
    },
    {
      "path": "registry/ndeko/components/agent/_parts/crew.ts",
      "content": "import type { RunAgent } from '@/registry/ndeko/lib/agent-run';\n\nexport const RUN_TITLE = 'Q3 vendor review for Brightmoor Supply';\n\nexport const PLANNER: RunAgent = {\n  id: 'planner',\n  name: 'Planner',\n  series: 1,\n  status: 'idle',\n  role: 'Lead',\n  model: 'Claude Opus',\n};\n\nexport const RESEARCHER: RunAgent = {\n  id: 'researcher',\n  name: 'Researcher',\n  series: 2,\n  status: 'idle',\n  model: 'Claude Sonnet',\n  parentId: 'planner',\n};\n\nexport const WRITER: RunAgent = {\n  id: 'writer',\n  name: 'Writer',\n  series: 3,\n  status: 'idle',\n  model: 'Claude Haiku',\n  parentId: 'planner',\n};\n\nexport const REVIEWER: RunAgent = {\n  id: 'reviewer',\n  name: 'Reviewer',\n  series: 4,\n  status: 'idle',\n  model: 'Claude Sonnet',\n  parentId: 'planner',\n};\n\nexport const CODER: RunAgent = {\n  id: 'coder',\n  name: 'Coder',\n  series: 5,\n  status: 'idle',\n  model: 'Claude Opus',\n  parentId: 'planner',\n};\n\nexport const CREW: RunAgent[] = [PLANNER, RESEARCHER, WRITER, REVIEWER, CODER];\n\nexport const PAGE_ERROR = 'The pricing page refused the reader (403)';\n",
      "type": "registry:component",
      "target": "components/agent/_parts/crew.ts"
    },
    {
      "path": "registry/ndeko/components/agent/_parts/demo.ts",
      "content": "/** A control that waits on a person is pressed by the demonstration after this long. */\nexport const DECIDE_AFTER_MS = 2500;\n\n/** The row a demonstration presses, when the control it holds down is the one named by `key`. */\nexport function pressedIdOf(pressed: string | null, key: string, id: string) {\n  if (pressed === key) {\n    return id;\n  }\n  return null;\n}\n",
      "type": "registry:component",
      "target": "components/agent/_parts/demo.ts"
    },
    {
      "path": "registry/ndeko/components/agent/_parts/focus-later.ts",
      "content": "/** Moves focus once React has committed the change that moved, replaced or removed the focused control. */\nexport function focusLater(\n  root: React.RefObject<HTMLElement | null>,\n  selector: string\n) {\n  requestAnimationFrame(() => {\n    const target = root.current?.querySelector<HTMLElement>(selector);\n    target?.focus();\n  });\n}\n",
      "type": "registry:component",
      "target": "components/agent/_parts/focus-later.ts"
    },
    {
      "path": "registry/ndeko/components/agent/_parts/keyed-height.tsx",
      "content": "'use client';\n\nimport { useLayoutEffect, useRef } from 'react';\n\nimport { usePrefersReducedMotion } from '@/registry/ndeko/hooks/use-prefers-reduced-motion';\nimport { cn } from '@/registry/ndeko/lib/utils';\n\nconst TRAVEL: KeyframeAnimationOptions = {\n  duration: 300,\n  easing: 'cubic-bezier(0.23, 1, 0.32, 1)',\n};\n\ntype KeyedHeightProps = {\n  /** The frame travels to its new height when this changes, and only then. */\n  changeKey: string;\n  className?: string;\n  children: React.ReactNode;\n};\n\n/**\n * A frame that changes height with a movement only when its key changes: a fold, another view. Any\n * other change of its content (a row that arrives, a panel that opens) is followed at once, since\n * that content already moves by itself and a frame chasing it would animate the same change twice.\n * The padding leaves room for focus rings and the margin takes it back.\n */\nexport function KeyedHeight({\n  changeKey,\n  className,\n  children,\n}: KeyedHeightProps) {\n  const frameRef = useRef<HTMLDivElement>(null);\n  const knownHeight = useRef<number | null>(null);\n  const shouldReduceMotion = usePrefersReducedMotion();\n\n  useLayoutEffect(() => {\n    const frame = frameRef.current;\n    if (!frame) {\n      return;\n    }\n    const observer = new ResizeObserver(() => {\n      knownHeight.current = frame.offsetHeight;\n    });\n    observer.observe(frame);\n    return () => observer.disconnect();\n  }, []);\n\n  useLayoutEffect(() => {\n    const frame = frameRef.current;\n    const from = knownHeight.current;\n    if (!frame || from === null) {\n      return;\n    }\n    if (shouldReduceMotion) {\n      return;\n    }\n    const to = frame.offsetHeight;\n    if (from === to) {\n      return;\n    }\n    const keyframes = [{ height: `${from}px` }, { height: `${to}px` }];\n    const travel = frame.animate(keyframes, TRAVEL);\n    return () => travel.cancel();\n  }, [changeKey, shouldReduceMotion]);\n\n  return (\n    <div\n      ref={frameRef}\n      data-slot='keyed-height'\n      className={cn('-m-1 overflow-hidden', className)}\n    >\n      <div className='p-1'>{children}</div>\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/agent/_parts/keyed-height.tsx"
    },
    {
      "path": "registry/ndeko/components/agent/_parts/tool-tile.tsx",
      "content": "import {\n  BlocksIcon,\n  FileSpreadsheetIcon,\n  FileTextIcon,\n  GlobeIcon,\n  GitBranchIcon,\n  ListChecksIcon,\n  MailIcon,\n  MemoryIcon,\n  PencilIcon,\n  SearchIcon,\n  TerminalIcon,\n} from '@/registry/ndeko/lib/icons';\nimport { cn } from '@/registry/ndeko/lib/utils';\n\nconst TOOL_GLYPHS: Record<string, React.ReactNode> = {\n  web_search: <SearchIcon />,\n  fetch_page: <GlobeIcon />,\n  read_sheet: <FileSpreadsheetIcon />,\n  read_file: <FileTextIcon />,\n  edit_file: <PencilIcon />,\n  write_doc: <FileTextIcon />,\n  send_email: <MailIcon />,\n  draft_email: <MailIcon />,\n  run_python: <TerminalIcon />,\n  shell: <TerminalIcon />,\n  run_tests: <ListChecksIcon />,\n  recall: <MemoryIcon />,\n  git: <GitBranchIcon />,\n};\n\nconst ANY_TOOL_GLYPH = <BlocksIcon />;\n\nexport function toolGlyphOf(tool: string) {\n  return TOOL_GLYPHS[tool] ?? ANY_TOOL_GLYPH;\n}\n\nconst SIZE_CLASS = {\n  sm: 'size-6 rounded-md [&_svg]:size-3.5',\n  md: 'size-7 rounded-md [&_svg]:size-3.5',\n  lg: 'size-8 rounded-lg [&_svg]:size-4',\n} as const;\n\ntype ToolTileProps = {\n  tool: string;\n  size?: keyof typeof SIZE_CLASS;\n  className?: string;\n};\n\n/** The tool name sits beside the tile or in the label of its control, so screen readers skip the glyph. */\nexport function ToolTile({ tool, size = 'md', className }: ToolTileProps) {\n  const glyph = toolGlyphOf(tool);\n\n  return (\n    <span\n      aria-hidden='true'\n      className={cn(\n        'keycap-3d text-muted-foreground inline-flex shrink-0 items-center justify-center',\n        SIZE_CLASS[size],\n        className\n      )}\n    >\n      {glyph}\n    </span>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/agent/_parts/tool-tile.tsx"
    }
  ],
  "categories": [
    "components",
    "agent"
  ],
  "type": "registry:component"
}