{"_id":"@adambossy/agent-ui","_rev":"4-5f598a0dbff0b284950f04cc2b767074","name":"@adambossy/agent-ui","dist-tags":{"latest":"0.6.1"},"versions":{"0.2.0":{"name":"@adambossy/agent-ui","version":"0.2.0","license":"MIT","_id":"@adambossy/agent-ui@0.2.0","maintainers":[{"name":"adambossy","email":"adambossy@gmail.com"}],"dist":{"shasum":"a13d4df1dd25005f48b22cd2f17b54e285947754","tarball":"https://registry.npmjs.org/@adambossy/agent-ui/-/agent-ui-0.2.0.tgz","fileCount":6,"integrity":"sha512-gcwP6SMw/spTNzWSBfNSKXqJRSH1XhQaCTfFsK31ilHox1M7QuEdGZUSc6ASeF9y2FjCh6AtUuQN8J7PyKRWqA==","signatures":[{"sig":"MEUCIBZwbodP0ybZwr82Dh1fu9rd3/actkJ27xQpENuDa773AiEAlHCZ/ocJeBuirgPug1mr7krH4q8HDsk3nvB/ECZ/XGw=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":164283},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./styles.css":"./dist/styles.css"},"gitHead":"c3bddd523c007e8fdbfc9f4b952e509239082bca","private":false,"scripts":{"dev":"tsup --watch","build":"tsup && cp src/styles.css dist/styles.css","typecheck":"tsc --noEmit"},"_npmUser":{"name":"adambossy","email":"adambossy@gmail.com"},"_npmVersion":"11.12.1","description":"React components and a live-component runtime for building agent chat UIs.","directories":{},"sideEffects":["**/*.css"],"_nodeVersion":"24.15.0","dependencies":{"zod":"^4.4.3","zustand":"^5.0.13","streamdown":"^2.5.0","lucide-react":"^1.16.0","@tailwindcss/typography":"^0.5.19"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.0","typescript":"~6.0.2","@types/react":"^19.2.14","@types/react-dom":"^19.2.3"},"peerDependencies":{"react":"^19.0.0","react-dom":"^19.0.0"},"_npmOperationalInternal":{"tmp":"tmp/agent-ui_0.2.0_1783260275845_0.36548123491431683","host":"s3://npm-registry-packages-npm-production"}},"0.5.0":{"name":"@adambossy/agent-ui","version":"0.5.0","license":"MIT","_id":"@adambossy/agent-ui@0.5.0","maintainers":[{"name":"adambossy","email":"adambossy@gmail.com"}],"dist":{"shasum":"2a404b0d9b9b6222fb7023449c35880e799d3bb3","tarball":"https://registry.npmjs.org/@adambossy/agent-ui/-/agent-ui-0.5.0.tgz","fileCount":6,"integrity":"sha512-HQDLW8Cf/FJ9No/VkP9o/XmTYY5ptD3A95WvJ3JlgpM904zl0k5i1uInUTm5P9tUKkmkks15qfCohwIxC4FVMg==","signatures":[{"sig":"MEUCIQDB/3YXZ3xwYAEi/lSYUug6kL611JA9IhiS234GlRpplgIgO+br2ZSXZJTe7qjaHl153iylN0J1VpyK/LGB4gtWcms=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":213570},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./styles.css":"./dist/styles.css"},"gitHead":"dafc2199e847ed7c4b9c5993c21f32d15c385150","private":false,"scripts":{"dev":"tsup --watch","test":"vitest run","build":"tsup && cp src/styles.css dist/styles.css","typecheck":"tsc --noEmit"},"_npmUser":{"name":"adambossy","email":"adambossy@gmail.com"},"_npmVersion":"11.12.1","description":"React components and a live-component runtime for building agent chat UIs.","directories":{},"sideEffects":["**/*.css"],"_nodeVersion":"24.15.0","dependencies":{"zod":"^4.4.3","zustand":"^5.0.13","streamdown":"^2.5.0","lucide-react":"^1.16.0","@tailwindcss/typography":"^0.5.19"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.0","jsdom":"^29.1.1","vitest":"^4.1.10","typescript":"~6.0.2","@types/react":"^19.2.14","@types/react-dom":"^19.2.3","@testing-library/react":"^16.3.2"},"peerDependencies":{"react":"^19.0.0","react-dom":"^19.0.0"},"_npmOperationalInternal":{"tmp":"tmp/agent-ui_0.5.0_1785618935737_0.8485519381095195","host":"s3://npm-registry-packages-npm-production"}},"0.6.0":{"name":"@adambossy/agent-ui","version":"0.6.0","license":"MIT","_id":"@adambossy/agent-ui@0.6.0","maintainers":[{"name":"adambossy","email":"adambossy@gmail.com"}],"dist":{"shasum":"3172920c4b67902dac0f63f932c3c4a8582fa5c7","tarball":"https://registry.npmjs.org/@adambossy/agent-ui/-/agent-ui-0.6.0.tgz","fileCount":6,"integrity":"sha512-/RQJvzHcRFsrOsU4wlBVVk5MKfSZIVxXFN9Y/i0vCcAtMHjEeEJ6voJySBbPD5x9vxSFUfb1m2MhQ7ej1Kr5oQ==","signatures":[{"sig":"MEUCIAXIG0z8XASodBazAjB3t/q8qHkJsFCgSFCJRLnq3vmLAiEAv0SnpeG2Sj8M2JCU4m37AvQLrOetth8iN4JPOBomJf8=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":224092},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./styles.css":"./dist/styles.css"},"gitHead":"38f451007933422537ad5c55f538644770816939","private":false,"scripts":{"dev":"tsup --watch","test":"vitest run","build":"tsup && cp src/styles.css dist/styles.css","typecheck":"tsc --noEmit"},"_npmUser":{"name":"adambossy","email":"adambossy@gmail.com"},"_npmVersion":"11.12.1","description":"React components and a live-component runtime for building agent chat UIs.","directories":{},"sideEffects":["**/*.css"],"_nodeVersion":"24.15.0","dependencies":{"zod":"^4.4.3","zustand":"^5.0.13","streamdown":"^2.5.0","lucide-react":"^1.16.0","@tailwindcss/typography":"^0.5.19"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.0","jsdom":"^29.1.1","vitest":"^4.1.10","typescript":"~6.0.2","@types/react":"^19.2.14","@types/react-dom":"^19.2.3","@testing-library/react":"^16.3.2"},"peerDependencies":{"react":"^19.0.0","react-dom":"^19.0.0"},"_npmOperationalInternal":{"tmp":"tmp/agent-ui_0.6.0_1785808680823_0.10337321889787043","host":"s3://npm-registry-packages-npm-production"}},"0.6.1":{"name":"@adambossy/agent-ui","version":"0.6.1","description":"React components and a live-component runtime for building agent chat UIs.","private":false,"type":"module","license":"MIT","sideEffects":["**/*.css"],"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./styles.css":"./dist/styles.css"},"main":"./dist/index.js","module":"./dist/index.js","types":"./dist/index.d.ts","scripts":{"build":"tsup && cp src/styles.css dist/styles.css","dev":"tsup --watch","typecheck":"tsc --noEmit","test":"vitest run"},"dependencies":{"@tailwindcss/typography":"^0.5.19","lucide-react":"^1.16.0","streamdown":"^2.5.0","zod":"^4.4.3","zustand":"^5.0.13"},"peerDependencies":{"react":"^19.0.0","react-dom":"^19.0.0"},"devDependencies":{"@testing-library/react":"^16.3.2","@types/react":"^19.2.14","@types/react-dom":"^19.2.3","jsdom":"^29.1.1","tsup":"^8.5.0","typescript":"~6.0.2","vitest":"^4.1.10"},"gitHead":"43ca8f52e4c15ba69e1a188f02393f980953acc7","_id":"@adambossy/agent-ui@0.6.1","_nodeVersion":"24.15.0","_npmVersion":"11.12.1","dist":{"integrity":"sha512-PX1wkz3ve4zQoqxZ1Gy7tQrTEM068lvQWxGFuyt82Dbpu4qx4uXEIeMCa1RbCbFBb6wqr+93cRwQNJ/+980GCw==","shasum":"666cc78ec5296bf4b3c3b59068f718369160432d","tarball":"https://registry.npmjs.org/@adambossy/agent-ui/-/agent-ui-0.6.1.tgz","fileCount":6,"unpackedSize":238368,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQDFu0qbuCQorIWHkUy+7L2fOeBfLJqkl8EFrPuXm0mk6AIhAJLVDMuBSVW8wty2+WOJ9DwnN9R7Du1/4vAR6O7pPr6z"}]},"_npmUser":{"name":"adambossy","email":"adambossy@gmail.com"},"directories":{},"maintainers":[{"name":"adambossy","email":"adambossy@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/agent-ui_0.6.1_1787272341292_0.26673648548811757"},"_hasShrinkwrap":false}},"time":{"created":"2026-07-05T14:04:35.643Z","modified":"2026-08-21T00:32:21.720Z","0.2.0":"2026-07-05T14:04:35.982Z","0.5.0":"2026-08-01T21:15:35.856Z","0.6.0":"2026-08-04T01:58:00.980Z","0.6.1":"2026-08-21T00:32:21.454Z"},"license":"MIT","description":"React components and a live-component runtime for building agent chat UIs.","maintainers":[{"name":"adambossy","email":"adambossy@gmail.com"}],"readme":"# @adambossy/agent-ui\n\nReact components and a live-component runtime for building agent chat UIs:\nstreaming messages, reasoning, tool-call rendering, subagent expansion, and a\nside-panel \"live document\" system driven by server-streamed ops.\n\n> Backend-agnostic. The library renders UI and manages client state; you supply\n> the transport (e.g. the Vercel AI SDK's `useChat`) and wire its data parts to\n> the stores. The [playground](../../apps/playground) is a complete reference\n> integration.\n\n## Install\n\nThe package is currently private. Within this monorepo it resolves through the\nworkspace; external consumers can use a git dependency or `npm pack`.\n\n```ts\nimport {\n  Message,\n  Composer,\n  LiveDocsPanel,\n  useLiveDocStore,\n  useLiveUIStore,\n  useSubagentStore,\n  registerLiveComponent,\n  registerToolRenderer,\n} from \"@adambossy/agent-ui\";\nimport type { UIMessage, LiveOpEvent } from \"@adambossy/agent-ui\";\n```\n\n## Styles (Tailwind v4)\n\nThe library is styled with Tailwind v4 utilities plus a small set of design\ntokens and custom classes. Consumers process the shipped stylesheet through\ntheir own Tailwind build:\n\n```css\n@import \"tailwindcss\";\n@import \"@adambossy/agent-ui/styles.css\";\n\n/* Let Tailwind generate the utility classes the components use: */\n@source \"../node_modules/@adambossy/agent-ui/dist\";\n```\n\n## Public API\n\n- **Components** — `Message`, `Composer`, `Markdown`, `Reasoning`\n- **Tool renderers** — `registerToolRenderer`, `resolveToolRenderer`,\n  `markAsSubagent`, `isSubagentTool`, `isNonUITool`, `DefaultTool`\n- **Run status** — `registerToolStatusText`, `statusTextFor`,\n  `humanizeToolName`, `configureRunStatus`, `getRunStatusConfig`,\n  `deriveRunStatus`\n- **Tool display modes** — `ToolDisplayProvider`, `useToolDisplay`,\n  `ToolDisplayMode`, `registerToolSummaryText`, `summaryTextFor`,\n  `ToolSummaryTextFn`, `deriveTurnSummary`, `TurnSummaryEntry`\n- **Subagent store** — `useSubagentStore`, `useSubagent`\n- **Live-component runtime** — `registerLiveComponent`, `LiveComponentHost`,\n  `LiveDocsPanel`, `useLiveDocStore`, `useLiveUIStore`, `LiveOpClient`,\n  `zodCodec`, and the live-op protocol types\n- **Types** — `UIMessage`, `UIMessagePart*`, `LiveOpEvent`,\n  `LiveComponentManifest`, `LiveRendererProps`, `SubagentEvent`, …\n\nAnything not re-exported from the package root is internal and may change\nwithout notice.\n\n## Run status\n\nTools without a registered renderer are **ephemeral by default**: instead of a\npersistent JSON card, `Message` shows a shimmering in-run status line\n(\"Running webSearch…\") that disappears once the run moves past that tool call.\nThe status line also covers `approval-requested` (\"Waiting for approval:\nwebSearch…\"), so a tool call stalled on human approval doesn't vanish from the\nrun. It does **not** cover `output-error` or `output-denied` — a failed or\ndenied ephemeral tool call renders nothing at all once it leaves the active\nstates above. If a tool's failures need to stay visible to the user, register\n`DefaultTool` for it (see below) instead of leaving it ephemeral.\n\nThe line is **pinned to the top of the turn** — it renders as the first child\nof the assistant message, above any widgets, streamed text, and the\nsummary-mode receipt, in both display modes. That keeps current activity in a\nfixed spot instead of trailing behind whatever has already rendered, and lets\nit reappear mid-turn (e.g. the model starts reasoning again after already\nproducing some text) without disturbing anything above it.\n\n**Reasoning parts are not rendered inline.** While a reasoning part streams,\nthe pinned line reads \"Thinking…\" — the same placeholder shown before any\ncontent exists — instead of an inline `<Reasoning>` block. In `\"summary\"`\ndisplay mode, finished reasoning is folded into the `TurnSummary` receipt,\nwhich shares the pinned top slot with the status line (see below); in\n`\"ephemeral\"` mode it leaves zero residue once it completes, matching how\nephemeral tool calls behave. The `Reasoning` component itself still ships\nand is exported for consumers who want to compose it manually.\n\n> **Migrating from ≤0.2:** previously every unregistered tool rendered a\n> collapsible JSON card. As of the ephemeral-by-default change, an\n> unregistered tool renders only a transient shimmer line and leaves no card\n> once it completes. To restore the old per-tool behavior — including\n> visibility into `output-error`/`output-denied` — call\n> `registerToolRenderer(name, DefaultTool)` for that tool name.\n\nRegister `DefaultTool` for a tool name to opt back into the old collapsible\nJSON card:\n\n```tsx\nimport { registerToolRenderer, DefaultTool } from \"@adambossy/agent-ui\";\n\nregisterToolRenderer(\"debugTool\", DefaultTool); // opt back into the JSON card\n```\n\nCustomize the shimmer text with `registerToolStatusText`, which is called\nwith the live tool part so it can refine its wording as streamed `input`\nfields arrive (`part.input` is typed `unknown`, so narrow or cast it):\n\n```tsx\nimport { registerToolStatusText, configureRunStatus } from \"@adambossy/agent-ui\";\n\nregisterToolStatusText(\"webSearch\", (part) => {\n  const query = (part.input as { query?: string } | undefined)?.query;\n  return query ? `Searching for ${query}` : \"Searching the web\";\n});\n\nconfigureRunStatus({ expandableParallel: true }); // stack parallel tool calls into separate lines\n```\n\n`configureRunStatus` is a setup-time global, not a per-render prop: call it\nonce during app initialization (alongside your `registerToolRenderer` /\n`registerToolStatusText` calls). It is not reactive: a mid-session call\ntakes effect only at each status line's next re-render (frequent while\nstreaming, but not forced by the call itself).\n\n`Composer` takes the same `status` (`\"ready\" | \"submitted\" | \"streaming\" |\n\"error\"`) your transport already tracks (e.g. the AI SDK's `useChat`), plus\n`onStop`: while `status` is `\"submitted\"` or `\"streaming\"` the send button\nswaps to a stop button that calls `onStop`.\n\n```tsx\n<Composer status={status} onStop={stop} onSend={sendMessage} />\n```\n\n### Display modes\n\nThe run-status line above is one of two **per-conversation** display modes\nfor non-UI tool activity (unregistered tools, and subagent-marked tools —\nsee `isNonUITool` below), selected by mounting a `ToolDisplayProvider`\naround a conversation's transcript:\n\n- **`\"ephemeral\"` (default)** — the behavior described above: a shimmering\n  status line while a non-UI tool runs, and nothing once it settles. This is\n  the behavior you get with no provider mounted at all.\n- **`\"summary\"`** — finished non-UI tool calls, plus finished reasoning,\n  additionally collect into a past-tense `TurnSummary` receipt — a single\n  collapsed line (\"Thought, Searched the web, Read your calendar\") that\n  expands into per-entry detail: tool entries via the same `DefaultTool` /\n  `SubagentTool` cards, so `output-error` and `output-denied` — invisible in\n  ephemeral mode — are visible here; reasoning entries via a compact\n  \"Thought\" block showing the reasoning text. The receipt shares the pinned\n  top slot with the run-status line rather than trailing the turn: the line\n  wins while there's active work, and the receipt takes the slot back once\n  it yields (prose streaming, or the turn has ended).\n\n`deriveTurnSummary(message, isNonUI)` returns `TurnSummaryEntry[]`, a\ndiscriminated union of `{ kind: \"tool\"; part }` and\n`{ kind: \"reasoning\"; part }` entries in message order — a reasoning entry\nappears once its part is finished (`state !== \"streaming\"`) and non-empty; an\nactively streaming reasoning part stays covered by the pinned run-status line\ninstead (see above), so a part is never represented in both places.\n\n`Message` reads the mode via `useToolDisplay()` internally, so nothing\noutside the provider is affected — mount one per conversation (or per tab)\nto let each run its own mode independently:\n\n```tsx\nimport { useState } from \"react\";\nimport {\n  ToolDisplayProvider,\n  Message,\n  type ToolDisplayMode,\n  type UIMessage,\n} from \"@adambossy/agent-ui\";\n\nfunction Conversation({ messages }: { messages: UIMessage[] }) {\n  const [mode, setMode] = useState<ToolDisplayMode>(\"ephemeral\");\n  return (\n    <ToolDisplayProvider mode={mode}>\n      {messages.map((m) => (\n        <Message key={m.id} message={m} />\n      ))}\n    </ToolDisplayProvider>\n  );\n}\n```\n\nGive a summarized tool call its own past-tense wording with\n`registerToolSummaryText` — the past-tense counterpart to\n`registerToolStatusText` above. It only runs once a call has reached a\nterminal state, so `part.input`/`part.output` are fully settled (still typed\n`unknown`, so narrow or cast it):\n\n```tsx\nimport { registerToolSummaryText } from \"@adambossy/agent-ui\";\n\nregisterToolSummaryText(\"webSearch\", (part) => {\n  const query = (part.input as { query?: string } | undefined)?.query;\n  return query ? `Searched for ${query}` : \"Searched the web\";\n});\n```\n\nTools with no registered summary text fall back to `` `Ran\n${humanizeToolName(toolName)}` ``.\n\n**Subagents:** a subagent call (`markAsSubagent`) now counts as a non-UI\ntool (see `isNonUITool` below), which changes how it renders. Previously a\nsubagent always drew a persistent top-level `SubagentTool` card. Now, like\nany other non-UI tool, it appears only in the run-status line while\nrunning; once it finishes, in `\"ephemeral\"` mode it leaves no residue, and\nin `\"summary\"` mode it becomes an entry in the `TurnSummary` receipt\ninstead of a standing top-level card. Expanding that entry renders the same\n`SubagentTool` component — the nested transcript inside it (the subagent's\nown reasoning, text, and tool parts) is unaffected by any of this.\n\n**`isNonUITool`:** the predicate that decides which tools skip inline\nrendering covers unregistered tools *and* subagent-marked tools (an earlier\nrevision called this `isEphemeralTool`; that name never shipped in a\nrelease).\n\n## Build\n\n```bash\nnpm run build      # tsup -> dist/{index.js, index.d.ts, styles.css}\nnpm run typecheck\n```\n","readmeFilename":"README.md"}