{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "agent-13",
  "title": "Tool servers",
  "description": "The servers that give an agent its tools, each with its status and a drawer of tools on switches with their call counts that opens under it, Reconnect running a short sign-in sequence, and a failed server that says why.",
  "dependencies": [],
  "registryDependencies": [
    "https://ui.ndekocode.dev/r/brand-logos.json",
    "https://ui.ndekocode.dev/r/brand-tile.json",
    "https://ui.ndekocode.dev/r/button.json",
    "https://ui.ndekocode.dev/r/chat-motion.json",
    "https://ui.ndekocode.dev/r/company-mark.json",
    "https://ui.ndekocode.dev/r/demonstration.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/motion-number.json",
    "https://ui.ndekocode.dev/r/replay-button.json",
    "https://ui.ndekocode.dev/r/run-session.json",
    "https://ui.ndekocode.dev/r/switch.json",
    "https://ui.ndekocode.dev/r/theme.json",
    "https://ui.ndekocode.dev/r/use-kept-height.json",
    "https://ui.ndekocode.dev/r/use-session.json",
    "https://ui.ndekocode.dev/r/use-timeouts.json",
    "https://ui.ndekocode.dev/r/utils.json",
    "https://ui.ndekocode.dev/r/words.json"
  ],
  "files": [
    {
      "path": "registry/ndeko/components/agent/agent-13.tsx",
      "content": "'use client';\n\nimport { useId } from 'react';\n\nimport { MotionNumber } from '@/components/core/motion-number';\nimport { useKeptHeight } from '@/registry/ndeko/hooks/use-kept-height';\nimport {\n  useSession,\n  type SessionStep,\n} from '@/registry/ndeko/hooks/use-session';\nimport { useTimeouts } from '@/registry/ndeko/hooks/use-timeouts';\nimport type { BrandSlug } from '@/registry/ndeko/lib/brand-logos';\nimport {\n  pressedQuietOf,\n  spokenUnless,\n} from '@/registry/ndeko/lib/demonstration';\nimport {\n  ChevronDownIcon,\n  LoaderIcon,\n  RefreshCwIcon,\n} from '@/registry/ndeko/lib/icons';\nimport { restingOf } from '@/registry/ndeko/lib/run-session';\nimport { cn } from '@/registry/ndeko/lib/utils';\nimport { countOf } from '@/registry/ndeko/lib/words';\nimport { BrandTile } from '@/registry/ndeko/ui/brand-tile';\nimport { Button } from '@/registry/ndeko/ui/button';\nimport { Fold, Swap } from '@/registry/ndeko/ui/chat-motion';\nimport { CompanyMark } from '@/registry/ndeko/ui/company-mark';\nimport { ClockGate } from '@/registry/ndeko/ui/live-duration';\nimport { LiveRegion } from '@/registry/ndeko/ui/live-region';\nimport { ReplayButton } from '@/registry/ndeko/ui/replay-button';\nimport { Switch } from '@/registry/ndeko/ui/switch';\n\nexport type ServerStatus =\n  'connected' | 'needs-sign-in' | 'asking' | 'connecting' | 'failed';\n\nexport type ServerTool = {\n  id: string;\n  name: string;\n  /** How many times the agent called it this week. */\n  calls: number;\n  isOn: boolean;\n};\n\nexport type ToolServer = {\n  id: string;\n  name: string;\n  /** A known product wears its real mark. Without one, the server wears a mark drawn from its name. */\n  brand?: BrandSlug;\n  status: ServerStatus;\n  /** True for the moment after a sign-in went through: the status says \"Signed in\". */\n  isSignedIn?: boolean;\n  failure?: string;\n  tools: ServerTool[];\n};\n\nconst STATUS_WORD: Record<ServerStatus, string> = {\n  connected: 'Connected',\n  'needs-sign-in': 'Needs sign-in',\n  asking: 'Asking for sign-in',\n  connecting: 'Connecting',\n  failed: 'Failed',\n};\n\nconst STATUS_DOT: Record<ServerStatus, string> = {\n  connected: 'bg-success',\n  'needs-sign-in': 'bg-warning',\n  asking: 'bg-info',\n  connecting: 'bg-info',\n  failed: 'bg-destructive',\n};\n\nfunction chevronTurnOf(isOpen: boolean) {\n  if (isOpen) {\n    return 'rotate-180';\n  }\n  return 'rotate-0';\n}\n\nfunction statusWordOf(server: ToolServer) {\n  if (server.status === 'connected' && server.isSignedIn) {\n    return 'Signed in';\n  }\n  return STATUS_WORD[server.status];\n}\n\nfunction actionLabelOf(status: ServerStatus) {\n  if (status === 'needs-sign-in') {\n    return 'Reconnect';\n  }\n  if (status === 'failed') {\n    return 'Retry';\n  }\n  return null;\n}\n\nfunction isBusy(status: ServerStatus) {\n  const isAsking = status === 'asking';\n  const isConnecting = status === 'connecting';\n  return isAsking || isConnecting;\n}\n\nfunction onCountOf(server: ToolServer) {\n  return server.tools.filter((tool) => tool.isOn).length;\n}\n\nfunction ServerMark({ server }: { server: ToolServer }) {\n  if (server.brand) {\n    return <BrandTile brand={server.brand} size='lg' />;\n  }\n  return <CompanyMark seed={server.name} size='lg' />;\n}\n\nfunction StatusLine({ server }: { server: ToolServer }) {\n  const word = statusWordOf(server);\n  const isWorking = isBusy(server.status);\n\n  return (\n    <span className='text-muted-foreground flex items-center gap-1.5 text-xs'>\n      <Swap swapKey={String(isWorking)}>\n        {isWorking && <LoaderIcon className='size-3 animate-spin' />}\n        {!isWorking && (\n          <span\n            aria-hidden='true'\n            className={cn('size-1.5 rounded-full', STATUS_DOT[server.status])}\n          />\n        )}\n      </Swap>\n      <Swap swapKey={word}>{word}</Swap>\n    </span>\n  );\n}\n\nfunction ToolCount({ server }: { server: ToolServer }) {\n  const total = server.tools.length;\n  if (server.status === 'connected') {\n    return (\n      <span className='text-muted-foreground shrink-0 text-xs'>\n        <MotionNumber value={onCountOf(server)} /> of {countOf(total, 'tool')}{' '}\n        on\n      </span>\n    );\n  }\n  return (\n    <span className='text-muted-foreground shrink-0 text-xs tabular-nums'>\n      {countOf(total, 'tool')}\n    </span>\n  );\n}\n\ntype ToolRowsProps = {\n  server: ToolServer;\n  onToolChange: (serverId: string, toolId: string, isOn: boolean) => void;\n};\n\nfunction ToolRows({ server, onToolChange }: ToolRowsProps) {\n  const isConnected = server.status === 'connected';\n\n  return (\n    <ul aria-label={`${server.name} tools`} className='flex flex-col'>\n      {server.tools.map((tool) => (\n        <li key={tool.id} className='flex min-h-9 items-center gap-3'>\n          <span\n            className={cn(\n              'min-w-0 flex-1 truncate font-mono text-xs transition-opacity duration-200',\n              toolToneOf(isConnected, tool.isOn)\n            )}\n          >\n            {tool.name}\n          </span>\n          <span className='text-muted-foreground shrink-0 text-xs tabular-nums'>\n            {countOf(tool.calls, 'call')}\n          </span>\n          <Switch\n            checked={tool.isOn}\n            disabled={!isConnected}\n            aria-label={`Allow ${tool.name}`}\n            onCheckedChange={(isOn) => onToolChange(server.id, tool.id, isOn)}\n          />\n        </li>\n      ))}\n    </ul>\n  );\n}\n\nfunction toolToneOf(isConnected: boolean, isOn: boolean) {\n  if (!isConnected) {\n    return 'opacity-50';\n  }\n  if (!isOn) {\n    return 'opacity-60';\n  }\n  return 'opacity-100';\n}\n\nfunction DrawerNote({ server }: { server: ToolServer }) {\n  if (server.status === 'failed') {\n    return (\n      <p className='text-destructive-ink text-xs leading-5'>{server.failure}</p>\n    );\n  }\n  if (server.status === 'needs-sign-in') {\n    return (\n      <p className='text-muted-foreground text-xs leading-5'>\n        Sign in to {server.name} so the agent can use these tools.\n      </p>\n    );\n  }\n  if (isBusy(server.status)) {\n    return (\n      <p className='text-muted-foreground text-xs leading-5'>\n        Waiting for {server.name} to answer.\n      </p>\n    );\n  }\n  return (\n    <p className='text-muted-foreground text-xs leading-5'>\n      The agent only calls the tools that are on.\n    </p>\n  );\n}\n\ntype ServerRowProps = {\n  server: ToolServer;\n  isOpen: boolean;\n  isPressed: boolean;\n  isActionPressed: boolean;\n  onToggle: (serverId: string) => void;\n  onToolChange: (serverId: string, toolId: string, isOn: boolean) => void;\n  onReconnect: (serverId: string) => void;\n};\n\nfunction ServerRow({\n  server,\n  isOpen,\n  isPressed,\n  isActionPressed,\n  onToggle,\n  onToolChange,\n  onReconnect,\n}: ServerRowProps) {\n  const drawerId = useId();\n  const actionLabel = actionLabelOf(server.status);\n  const hasAction = actionLabel !== null;\n\n  function toggle() {\n    onToggle(server.id);\n  }\n\n  function reconnect() {\n    onReconnect(server.id);\n  }\n\n  return (\n    <li\n      data-status={server.status}\n      className={cn(\n        'rounded-lg transition-colors duration-200',\n        surfaceOf(isOpen)\n      )}\n    >\n      <div className='flex flex-wrap items-center gap-x-2 gap-y-1 ps-1 pe-2'>\n        <button\n          type='button'\n          aria-expanded={isOpen}\n          aria-controls={drawerId}\n          onClick={toggle}\n          className={cn(\n            'focus-visible:ring-ring/25 hover:bg-foreground/[0.03] flex min-h-12 min-w-0 flex-1 basis-56 cursor-pointer items-center gap-3 rounded-lg px-2 py-2 text-start transition-[background-color,scale] outline-none focus-visible:ring-[3px]',\n            pressedQuietOf(isPressed)\n          )}\n        >\n          <ServerMark server={server} />\n          <span className='flex min-w-0 flex-1 flex-col'>\n            <span className='truncate text-[13px] font-medium'>\n              {server.name}\n            </span>\n            <StatusLine server={server} />\n          </span>\n          <ToolCount server={server} />\n          <ChevronDownIcon\n            className={cn(\n              'text-muted-foreground size-4 shrink-0 transition-transform duration-200 ease-(--ease-out-strong)',\n              chevronTurnOf(isOpen)\n            )}\n          />\n        </button>\n        {hasAction && (\n          <Button\n            type='button'\n            variant='outline'\n            size='sm'\n            onClick={reconnect}\n            aria-label={`${actionLabel} ${server.name}`}\n            className={pressedQuietOf(isActionPressed)}\n          >\n            <RefreshCwIcon />\n            {actionLabel}\n          </Button>\n        )}\n      </div>\n      <Fold open={isOpen} id={drawerId}>\n        <div className='flex flex-col gap-1 px-3 ps-[3.75rem] pt-1 pb-3'>\n          <DrawerNote server={server} />\n          <ToolRows server={server} onToolChange={onToolChange} />\n        </div>\n      </Fold>\n    </li>\n  );\n}\n\nfunction surfaceOf(isOpen: boolean) {\n  if (isOpen) {\n    return 'bg-foreground/[0.04] dark:bg-foreground/[0.06]';\n  }\n  return 'bg-transparent';\n}\n\nexport type ToolServersProps = {\n  servers: ToolServer[];\n  /** The server whose tools show, or null when every drawer is closed. */\n  openId: string | null;\n  onOpenChange: (serverId: string | null) => void;\n  onToolChange: (serverId: string, toolId: string, isOn: boolean) => void;\n  /** Reconnect for a server that needs signing in, Retry for one that failed. */\n  onReconnect: (serverId: string) => void;\n  /** The control a demonstration holds down: `row:github`, `reconnect:notion`. */\n  pressedKey?: string | null;\n  className?: string;\n};\n\nexport function ToolServers({\n  servers,\n  openId,\n  onOpenChange,\n  onToolChange,\n  onReconnect,\n  pressedKey = null,\n  className,\n}: ToolServersProps) {\n  function toggle(serverId: string) {\n    if (openId === serverId) {\n      onOpenChange(null);\n      return;\n    }\n    onOpenChange(serverId);\n  }\n\n  return (\n    <section\n      aria-label='Tool servers'\n      className={cn(\n        'bg-card hairline-ring ring-border rounded-xl p-2 shadow-[0_1px_2px_rgb(0_0_0/0.04)]',\n        className\n      )}\n    >\n      <ul className='flex flex-col gap-1'>\n        {servers.map((server) => (\n          <ServerRow\n            key={server.id}\n            server={server}\n            isOpen={openId === server.id}\n            isPressed={pressedKey === `row:${server.id}`}\n            isActionPressed={pressedKey === `reconnect:${server.id}`}\n            onToggle={toggle}\n            onToolChange={onToolChange}\n            onReconnect={onReconnect}\n          />\n        ))}\n      </ul>\n    </section>\n  );\n}\n\nconst SIGN_IN_MS = 1600;\n\nfunction toolsOf(names: [string, number][], idPrefix: string): ServerTool[] {\n  return names.map(([name, calls]) => ({\n    id: `${idPrefix}-${name}`,\n    name,\n    calls,\n    isOn: true,\n  }));\n}\n\nconst SERVERS: ToolServer[] = [\n  {\n    id: 'github',\n    name: 'GitHub',\n    brand: 'github',\n    status: 'connected',\n    tools: toolsOf(\n      [\n        ['search_issues', 18],\n        ['read_file', 41],\n        ['list_commits', 7],\n        ['create_pull_request', 3],\n        ['add_comment', 5],\n      ],\n      'github'\n    ),\n  },\n  {\n    id: 'notion',\n    name: 'Notion',\n    brand: 'notion',\n    status: 'needs-sign-in',\n    tools: toolsOf(\n      [\n        ['search_pages', 12],\n        ['read_page', 9],\n        ['create_page', 4],\n        ['query_database', 6],\n      ],\n      'notion'\n    ),\n  },\n  {\n    id: 'postgres',\n    name: 'Postgres',\n    brand: 'postgresql',\n    status: 'connected',\n    tools: toolsOf(\n      [\n        ['run_query', 26],\n        ['list_tables', 9],\n        ['describe_table', 14],\n      ],\n      'postgres'\n    ),\n  },\n  {\n    id: 'brightmoor',\n    name: 'Brightmoor ERP',\n    status: 'failed',\n    failure: 'The server did not answer within 10 seconds.',\n    tools: toolsOf(\n      [\n        ['get_invoice', 8],\n        ['list_orders', 2],\n      ],\n      'brightmoor'\n    ),\n  },\n];\n\ntype ServersState = {\n  servers: ToolServer[];\n  openId: string | null;\n};\n\ntype ServersAction =\n  | { type: 'open'; serverId: string | null }\n  | { type: 'tool'; serverId: string; toolId: string; isOn: boolean }\n  | { type: 'reconnect'; serverId: string }\n  | { type: 'settle'; serverId: string }\n  | { type: 'hold' };\n\ntype ServersStep = SessionStep<ServersState, ServersAction>;\n\nconst INITIAL: ServersState = { servers: SERVERS, openId: null };\n\nfunction withServer(\n  state: ServersState,\n  serverId: string,\n  change: (server: ToolServer) => ToolServer\n): ServersState {\n  const servers = state.servers.map((server) => {\n    if (server.id === serverId) {\n      return change(server);\n    }\n    return server;\n  });\n  return { ...state, servers };\n}\n\nfunction withTool(\n  server: ToolServer,\n  toolId: string,\n  isOn: boolean\n): ToolServer {\n  const tools = server.tools.map((tool) => {\n    if (tool.id === toolId) {\n      return { ...tool, isOn };\n    }\n    return tool;\n  });\n  return { ...server, tools };\n}\n\nfunction askedAgain(server: ToolServer): ToolServer {\n  if (server.status === 'needs-sign-in') {\n    return { ...server, status: 'asking' };\n  }\n  if (server.status === 'failed') {\n    return { ...server, status: 'connecting', failure: undefined };\n  }\n  return server;\n}\n\nfunction settled(server: ToolServer): ToolServer {\n  if (server.status === 'asking') {\n    return { ...server, status: 'connected', isSignedIn: true };\n  }\n  if (server.status === 'connecting') {\n    return { ...server, status: 'connected' };\n  }\n  return server;\n}\n\nfunction reducer(state: ServersState, action: ServersAction): ServersState {\n  switch (action.type) {\n    case 'open':\n      return { ...state, openId: action.serverId };\n    case 'tool':\n      return withServer(state, action.serverId, (server) =>\n        withTool(server, action.toolId, action.isOn)\n      );\n    case 'reconnect':\n      return withServer(state, action.serverId, askedAgain);\n    case 'settle':\n      return withServer(state, action.serverId, settled);\n    case 'hold':\n      return state;\n  }\n}\n\nconst PULL_REQUEST_TOOL = 'github-create_pull_request';\n\nconst SCRIPT: ServersStep[] = [\n  {\n    after: 1600,\n    by: 'demo',\n    press: 'row:github',\n    action: { type: 'open', serverId: 'github' },\n  },\n  {\n    after: 1700,\n    by: 'demo',\n    action: {\n      type: 'tool',\n      serverId: 'github',\n      toolId: PULL_REQUEST_TOOL,\n      isOn: false,\n    },\n  },\n  {\n    after: 1800,\n    by: 'demo',\n    press: 'row:notion',\n    action: { type: 'open', serverId: 'notion' },\n  },\n  {\n    after: 1500,\n    by: 'demo',\n    press: 'reconnect:notion',\n    action: { type: 'reconnect', serverId: 'notion' },\n  },\n  { after: SIGN_IN_MS, action: { type: 'settle', serverId: 'notion' } },\n  { after: 3800, action: { type: 'hold' } },\n];\n\nconst SESSION = {\n  reducer,\n  initial: INITIAL,\n  resting: restingOf(INITIAL, SCRIPT, reducer),\n  script: SCRIPT,\n  loopAfter: 3000,\n};\n\nfunction announcementOf(state: ServersState) {\n  const asking = state.servers.find((server) => server.status === 'asking');\n  if (asking) {\n    return `Asking ${asking.name} for sign-in`;\n  }\n  const signedIn = state.servers.find((server) => server.isSignedIn);\n  if (signedIn) {\n    return `Signed in to ${signedIn.name}`;\n  }\n  return '';\n}\n\nexport default function Agent13() {\n  const { schedule, clearAll } = useTimeouts();\n  const {\n    ref,\n    isInView,\n    isAutoplaying,\n    state,\n    act,\n    pressed,\n    isFinished,\n    restart,\n  } = useSession(SESSION);\n  const hasSignedIn = state.servers.some((server) => server.isSignedIn);\n  const canReplay = hasSignedIn && isFinished && !isAutoplaying;\n  const announcement = announcementOf(state);\n\n  function changeOpen(serverId: string | null) {\n    act({ type: 'open', serverId });\n  }\n\n  function changeTool(serverId: string, toolId: string, isOn: boolean) {\n    act({ type: 'tool', serverId, toolId, isOn });\n  }\n\n  function reconnect(serverId: string) {\n    act({ type: 'reconnect', serverId });\n    schedule(() => act({ type: 'settle', serverId }), SIGN_IN_MS);\n  }\n\n  function replay() {\n    clearAll();\n    restart();\n  }\n\n  useKeptHeight(ref);\n\n  return (\n    <div\n      ref={ref}\n      className='flex min-h-[28rem] w-full max-w-[32rem] flex-col gap-2'\n    >\n      <ClockGate isActive={isInView}>\n        <ToolServers\n          servers={state.servers}\n          openId={state.openId}\n          onOpenChange={changeOpen}\n          onToolChange={changeTool}\n          onReconnect={reconnect}\n          pressedKey={pressed}\n        />\n      </ClockGate>\n      <div className='flex h-7 items-center justify-end'>\n        <ReplayButton isShown={canReplay} onReplay={replay} />\n      </div>\n      <LiveRegion message={spokenUnless(isAutoplaying, announcement)} />\n    </div>\n  );\n}\n",
      "type": "registry:component"
    }
  ],
  "categories": [
    "components",
    "agent"
  ],
  "type": "registry:component"
}