@assistant-ui/mcp-docs-server 0.1.24 → 0.1.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (151) hide show
  1. package/.docs/organized/code-examples/waterfall.md +8 -6
  2. package/.docs/organized/code-examples/with-a2a.md +676 -0
  3. package/.docs/organized/code-examples/with-ag-ui.md +10 -11
  4. package/.docs/organized/code-examples/with-ai-sdk-v6.md +31 -19
  5. package/.docs/organized/code-examples/with-artifacts.md +8 -8
  6. package/.docs/organized/code-examples/with-assistant-transport.md +6 -6
  7. package/.docs/organized/code-examples/with-chain-of-thought.md +37 -29
  8. package/.docs/organized/code-examples/with-cloud-standalone.md +14 -11
  9. package/.docs/organized/code-examples/with-cloud.md +8 -8
  10. package/.docs/organized/code-examples/with-custom-thread-list.md +10 -10
  11. package/.docs/organized/code-examples/with-elevenlabs-scribe.md +11 -11
  12. package/.docs/organized/code-examples/with-expo.md +571 -520
  13. package/.docs/organized/code-examples/with-external-store.md +6 -6
  14. package/.docs/organized/code-examples/with-ffmpeg.md +8 -8
  15. package/.docs/organized/code-examples/with-google-adk.md +353 -0
  16. package/.docs/organized/code-examples/with-heat-graph.md +304 -0
  17. package/.docs/organized/code-examples/with-interactables.md +778 -0
  18. package/.docs/organized/code-examples/with-langgraph.md +28 -26
  19. package/.docs/organized/code-examples/with-parent-id-grouping.md +7 -7
  20. package/.docs/organized/code-examples/with-react-hook-form.md +9 -9
  21. package/.docs/organized/code-examples/with-react-ink.md +265 -0
  22. package/.docs/organized/code-examples/with-react-router.md +12 -12
  23. package/.docs/organized/code-examples/with-store.md +33 -22
  24. package/.docs/organized/code-examples/with-tanstack.md +10 -10
  25. package/.docs/organized/code-examples/with-tap-runtime.md +12 -10
  26. package/.docs/raw/blog/2025-01-31-changelog/index.mdx +1 -1
  27. package/.docs/raw/blog/2026-03-launch-week/index.mdx +258 -0
  28. package/.docs/raw/docs/(docs)/architecture.mdx +1 -1
  29. package/.docs/raw/docs/(docs)/cli.mdx +74 -9
  30. package/.docs/raw/docs/(docs)/copilots/make-assistant-tool-ui.mdx +8 -3
  31. package/.docs/raw/docs/(docs)/copilots/make-assistant-tool.mdx +5 -1
  32. package/.docs/raw/docs/(docs)/copilots/{make-assistant-readable.mdx → make-assistant-visible.mdx} +14 -5
  33. package/.docs/raw/docs/(docs)/copilots/model-context.mdx +11 -11
  34. package/.docs/raw/docs/(docs)/copilots/motivation.mdx +2 -2
  35. package/.docs/raw/docs/(docs)/devtools.mdx +3 -2
  36. package/.docs/raw/docs/(docs)/guides/attachments.mdx +74 -15
  37. package/.docs/raw/docs/(docs)/guides/branching.mdx +11 -6
  38. package/.docs/raw/docs/(docs)/guides/chain-of-thought.mdx +18 -16
  39. package/.docs/raw/docs/(docs)/guides/context-api.mdx +81 -43
  40. package/.docs/raw/docs/(docs)/guides/dictation.mdx +5 -5
  41. package/.docs/raw/docs/(docs)/guides/editing.mdx +16 -7
  42. package/.docs/raw/docs/(docs)/guides/interactables.mdx +292 -0
  43. package/.docs/raw/docs/(docs)/guides/latex.mdx +3 -0
  44. package/.docs/raw/docs/(docs)/guides/message-timing.mdx +5 -4
  45. package/.docs/raw/docs/(docs)/guides/multi-agent.mdx +174 -0
  46. package/.docs/raw/docs/(docs)/guides/quoting.mdx +55 -206
  47. package/.docs/raw/docs/(docs)/guides/speech.mdx +1 -4
  48. package/.docs/raw/docs/(docs)/guides/suggestions.mdx +9 -15
  49. package/.docs/raw/docs/(docs)/guides/tool-ui.mdx +17 -7
  50. package/.docs/raw/docs/(docs)/guides/tools.mdx +24 -9
  51. package/.docs/raw/docs/(docs)/index.mdx +3 -3
  52. package/.docs/raw/docs/(docs)/installation.mdx +69 -46
  53. package/.docs/raw/docs/(reference)/api-reference/context-providers/text-message-part-provider.mdx +20 -6
  54. package/.docs/raw/docs/(reference)/api-reference/integrations/react-data-stream.mdx +24 -4
  55. package/.docs/raw/docs/(reference)/api-reference/integrations/react-hook-form.mdx +1 -1
  56. package/.docs/raw/docs/(reference)/api-reference/integrations/vercel-ai-sdk.mdx +20 -19
  57. package/.docs/raw/docs/(reference)/api-reference/overview.mdx +28 -53
  58. package/.docs/raw/docs/(reference)/api-reference/primitives/action-bar.mdx +4 -4
  59. package/.docs/raw/docs/(reference)/api-reference/primitives/assistant-modal.mdx +7 -1
  60. package/.docs/raw/docs/(reference)/api-reference/primitives/attachment.mdx +20 -14
  61. package/.docs/raw/docs/(reference)/api-reference/primitives/branch-picker.mdx +1 -1
  62. package/.docs/raw/docs/(reference)/api-reference/primitives/composer.mdx +226 -44
  63. package/.docs/raw/docs/(reference)/api-reference/primitives/message-part.mdx +52 -40
  64. package/.docs/raw/docs/(reference)/api-reference/primitives/message.mdx +343 -23
  65. package/.docs/raw/docs/(reference)/api-reference/primitives/suggestion.mdx +4 -6
  66. package/.docs/raw/docs/(reference)/api-reference/primitives/thread-list-item.mdx +4 -2
  67. package/.docs/raw/docs/(reference)/api-reference/primitives/thread-list.mdx +3 -5
  68. package/.docs/raw/docs/(reference)/api-reference/primitives/thread.mdx +169 -22
  69. package/.docs/raw/docs/(reference)/api-reference/runtimes/assistant-runtime.mdx +14 -4
  70. package/.docs/raw/docs/(reference)/api-reference/runtimes/attachment-runtime.mdx +15 -26
  71. package/.docs/raw/docs/(reference)/api-reference/runtimes/composer-runtime.mdx +39 -21
  72. package/.docs/raw/docs/(reference)/api-reference/runtimes/message-part-runtime.mdx +33 -9
  73. package/.docs/raw/docs/(reference)/api-reference/runtimes/message-runtime.mdx +48 -21
  74. package/.docs/raw/docs/(reference)/api-reference/runtimes/thread-list-item-runtime.mdx +36 -7
  75. package/.docs/raw/docs/(reference)/api-reference/runtimes/thread-list-runtime.mdx +30 -10
  76. package/.docs/raw/docs/(reference)/api-reference/runtimes/thread-runtime.mdx +12 -10
  77. package/.docs/raw/docs/(reference)/migrations/deprecation-policy.mdx +1 -1
  78. package/.docs/raw/docs/(reference)/migrations/react-langgraph-v0-7.mdx +9 -4
  79. package/.docs/raw/docs/(reference)/migrations/v0-11.mdx +7 -5
  80. package/.docs/raw/docs/(reference)/migrations/v0-12.mdx +9 -7
  81. package/.docs/raw/docs/(reference)/migrations/v0-14.mdx +159 -0
  82. package/.docs/raw/docs/(reference)/react-compatibility.mdx +5 -134
  83. package/.docs/raw/docs/cloud/ai-sdk-assistant-ui.mdx +90 -6
  84. package/.docs/raw/docs/cloud/ai-sdk.mdx +95 -5
  85. package/.docs/raw/docs/cloud/langgraph.mdx +13 -3
  86. package/.docs/raw/docs/ink/adapters.mdx +41 -0
  87. package/.docs/raw/docs/ink/custom-backend.mdx +203 -0
  88. package/.docs/raw/docs/ink/hooks.mdx +448 -0
  89. package/.docs/raw/docs/ink/index.mdx +239 -0
  90. package/.docs/raw/docs/ink/migration.mdx +140 -0
  91. package/.docs/raw/docs/ink/primitives.mdx +840 -0
  92. package/.docs/raw/docs/primitives/action-bar.mdx +351 -0
  93. package/.docs/raw/docs/primitives/assistant-modal.mdx +215 -0
  94. package/.docs/raw/docs/primitives/attachment.mdx +216 -0
  95. package/.docs/raw/docs/primitives/branch-picker.mdx +221 -0
  96. package/.docs/raw/docs/primitives/chain-of-thought.mdx +311 -0
  97. package/.docs/raw/docs/primitives/composer.mdx +526 -0
  98. package/.docs/raw/docs/primitives/error.mdx +141 -0
  99. package/.docs/raw/docs/primitives/index.mdx +98 -0
  100. package/.docs/raw/docs/primitives/message.mdx +524 -0
  101. package/.docs/raw/docs/primitives/selection-toolbar.mdx +165 -0
  102. package/.docs/raw/docs/primitives/suggestion.mdx +242 -0
  103. package/.docs/raw/docs/primitives/thread-list.mdx +404 -0
  104. package/.docs/raw/docs/primitives/thread.mdx +482 -0
  105. package/.docs/raw/docs/react-native/adapters.mdx +63 -87
  106. package/.docs/raw/docs/react-native/custom-backend.mdx +11 -14
  107. package/.docs/raw/docs/react-native/hooks.mdx +214 -232
  108. package/.docs/raw/docs/react-native/index.mdx +118 -159
  109. package/.docs/raw/docs/react-native/migration.mdx +144 -0
  110. package/.docs/raw/docs/react-native/primitives.mdx +431 -302
  111. package/.docs/raw/docs/runtimes/a2a/index.mdx +294 -0
  112. package/.docs/raw/docs/runtimes/ai-sdk/v4-legacy.mdx +9 -9
  113. package/.docs/raw/docs/runtimes/ai-sdk/v5-legacy.mdx +14 -3
  114. package/.docs/raw/docs/runtimes/assistant-transport.mdx +59 -25
  115. package/.docs/raw/docs/runtimes/custom/custom-thread-list.mdx +13 -6
  116. package/.docs/raw/docs/runtimes/custom/external-store.mdx +138 -38
  117. package/.docs/raw/docs/runtimes/custom/local.mdx +184 -42
  118. package/.docs/raw/docs/runtimes/data-stream.mdx +92 -19
  119. package/.docs/raw/docs/runtimes/google-adk/index.mdx +624 -0
  120. package/.docs/raw/docs/runtimes/helicone.mdx +6 -6
  121. package/.docs/raw/docs/runtimes/langgraph/index.mdx +38 -27
  122. package/.docs/raw/docs/runtimes/langgraph/tutorial/introduction.mdx +1 -1
  123. package/.docs/raw/docs/runtimes/langgraph/tutorial/part-1.mdx +15 -20
  124. package/.docs/raw/docs/runtimes/langgraph/tutorial/part-2.mdx +7 -11
  125. package/.docs/raw/docs/runtimes/langgraph/tutorial/part-3.mdx +8 -11
  126. package/.docs/raw/docs/runtimes/langserve.mdx +6 -7
  127. package/.docs/raw/docs/runtimes/pick-a-runtime.mdx +18 -3
  128. package/.docs/raw/docs/ui/file.mdx +5 -4
  129. package/.docs/raw/docs/ui/image.mdx +5 -4
  130. package/.docs/raw/docs/ui/markdown.mdx +3 -1
  131. package/.docs/raw/docs/ui/mention.mdx +168 -0
  132. package/.docs/raw/docs/ui/model-selector.mdx +8 -8
  133. package/.docs/raw/docs/ui/part-grouping.mdx +7 -10
  134. package/.docs/raw/docs/ui/quote.mdx +210 -0
  135. package/.docs/raw/docs/ui/reasoning.mdx +12 -11
  136. package/.docs/raw/docs/ui/sources.mdx +88 -17
  137. package/.docs/raw/docs/ui/streamdown.mdx +16 -7
  138. package/.docs/raw/docs/ui/thread-list.mdx +11 -13
  139. package/.docs/raw/docs/ui/thread.mdx +28 -33
  140. package/.docs/raw/docs/ui/tool-fallback.mdx +5 -6
  141. package/.docs/raw/docs/ui/tool-group.mdx +9 -8
  142. package/.docs/raw/docs/utilities/heat-graph.mdx +236 -0
  143. package/.docs/raw/docs/utilities/tw-shimmer.mdx +211 -0
  144. package/package.json +5 -5
  145. package/.docs/raw/docs/(reference)/legacy/styled/assistant-modal.mdx +0 -77
  146. package/.docs/raw/docs/(reference)/legacy/styled/decomposition.mdx +0 -635
  147. package/.docs/raw/docs/(reference)/legacy/styled/markdown.mdx +0 -77
  148. package/.docs/raw/docs/(reference)/legacy/styled/scrollbar.mdx +0 -72
  149. package/.docs/raw/docs/(reference)/legacy/styled/thread-width.mdx +0 -22
  150. package/.docs/raw/docs/(reference)/legacy/styled/thread.mdx +0 -77
  151. /package/.docs/raw/docs/cloud/{overview.mdx → index.mdx} +0 -0
@@ -0,0 +1,292 @@
1
+ ---
2
+ title: Interactables
3
+ description: Persistent UI whose state can be read and updated by the AI assistant.
4
+ ---
5
+
6
+ import { InteractableSample } from "@/components/docs/samples/interactable";
7
+
8
+ Interactables are React components that live outside the chat message flow and have state that both the user and the AI can read and write. This enables AI-driven UI patterns where the assistant controls parts of your application beyond the chat window.
9
+
10
+ <InteractableSample />
11
+
12
+ ## Overview
13
+
14
+ Unlike regular tool UIs that appear inline within messages, interactables:
15
+
16
+ - **Persist across messages** — they live outside the chat thread
17
+ - **Have shared state** — both the user (via React) and the AI (via auto-generated tools) can update them
18
+ - **Support partial updates** — the AI only needs to send the fields it wants to change
19
+ - **Are developer-placed** — you decide where they render in your app
20
+ - **Auto-register tools** — the AI automatically gets a tool to update each interactable's state
21
+
22
+ Common use cases:
23
+
24
+ - Task boards that the AI can add items to
25
+ - Data dashboards that update based on conversation
26
+ - Forms that the AI pre-fills
27
+ - Canvas/editor components that the AI can manipulate
28
+
29
+ ## Quick Start
30
+
31
+ ### 1. Register the Interactables scope
32
+
33
+ ```tsx
34
+ import { useAui, Interactables, AssistantRuntimeProvider } from "@assistant-ui/react";
35
+ import { useChatRuntime } from "@assistant-ui/react-ai-sdk";
36
+
37
+ function MyRuntimeProvider({ children }: { children: React.ReactNode }) {
38
+ const runtime = useChatRuntime();
39
+
40
+ const aui = useAui({
41
+ interactables: Interactables(),
42
+ });
43
+
44
+ return (
45
+ <AssistantRuntimeProvider aui={aui} runtime={runtime}>
46
+ {children}
47
+ </AssistantRuntimeProvider>
48
+ );
49
+ }
50
+ ```
51
+
52
+ ### 2. Create an interactable
53
+
54
+ <Callout type="warn">
55
+ Define the `stateSchema` and `initialState` **outside** the component (or
56
+ memoize them). Creating a new schema on every render will cause the
57
+ interactable to re-register and reset its state.
58
+ </Callout>
59
+
60
+ ```tsx
61
+ import { useInteractable } from "@assistant-ui/react";
62
+ import { z } from "zod";
63
+
64
+ const taskBoardSchema = z.object({
65
+ tasks: z.array(
66
+ z.object({
67
+ id: z.string(),
68
+ title: z.string(),
69
+ done: z.boolean(),
70
+ }),
71
+ ),
72
+ });
73
+
74
+ const taskBoardInitialState = { tasks: [] };
75
+
76
+ function TaskBoard() {
77
+ const [state, setState] = useInteractable("taskBoard", {
78
+ description: "A task board showing the user's tasks",
79
+ stateSchema: taskBoardSchema,
80
+ initialState: taskBoardInitialState,
81
+ });
82
+
83
+ return (
84
+ <div>
85
+ <h2>Tasks</h2>
86
+ <ul>
87
+ {state.tasks.map((task) => (
88
+ <li key={task.id}>
89
+ <label>
90
+ <input
91
+ type="checkbox"
92
+ checked={task.done}
93
+ onChange={() =>
94
+ setState((prev) => ({
95
+ tasks: prev.tasks.map((t) =>
96
+ t.id === task.id ? { ...t, done: !t.done } : t,
97
+ ),
98
+ }))
99
+ }
100
+ />
101
+ {task.title}
102
+ </label>
103
+ </li>
104
+ ))}
105
+ </ul>
106
+ </div>
107
+ );
108
+ }
109
+ ```
110
+
111
+ ### 3. Place it in your layout
112
+
113
+ ```tsx
114
+ function App() {
115
+ return (
116
+ <MyRuntimeProvider>
117
+ <div className="flex">
118
+ <Thread className="flex-1" />
119
+ <TaskBoard /> {/* Lives outside the chat */}
120
+ </div>
121
+ </MyRuntimeProvider>
122
+ );
123
+ }
124
+ ```
125
+
126
+ Now when the user says _"Add a task called 'Buy groceries'"_, the AI will automatically call the `update_taskBoard` tool to update the state. Thanks to partial updates, the AI only needs to send the fields it wants to change.
127
+
128
+ ## Partial Updates
129
+
130
+ Auto-generated tools use a partial schema — all fields become optional. The AI only sends the fields it wants to change; omitted fields keep their current values.
131
+
132
+ ```tsx
133
+ // If the state is { title: "My Note", content: "Hello", color: "yellow" }
134
+ // The AI can call: update_note({ color: "blue" })
135
+ // Result: { title: "My Note", content: "Hello", color: "blue" }
136
+ ```
137
+
138
+ This is especially useful for large state objects where regenerating the entire state would be expensive and error-prone.
139
+
140
+ <Callout type="info">
141
+ Merge is shallow (one level deep). If the AI sends a nested object, it replaces
142
+ that entire field rather than deep-merging into it.
143
+ </Callout>
144
+
145
+ ## Multiple Instances
146
+
147
+ You can render multiple interactables with the same `name` but different `id`s. Each gets its own update tool:
148
+
149
+ ```tsx
150
+ import { useInteractable } from "@assistant-ui/react";
151
+ import { z } from "zod";
152
+
153
+ const noteSchema = z.object({
154
+ title: z.string(),
155
+ content: z.string(),
156
+ color: z.enum(["yellow", "blue", "green", "pink"]),
157
+ });
158
+
159
+ const noteInitialState = { title: "New Note", content: "", color: "yellow" as const };
160
+
161
+ function NoteCard({ noteId }: { noteId: string }) {
162
+ const [state] = useInteractable("note", {
163
+ id: noteId,
164
+ description: "A sticky note",
165
+ stateSchema: noteSchema,
166
+ initialState: noteInitialState,
167
+ });
168
+
169
+ return <div>{state.title}</div>;
170
+ }
171
+
172
+ function App() {
173
+ return (
174
+ <>
175
+ <NoteCard noteId="note-1" /> {/* → update_note_note-1 tool */}
176
+ <NoteCard noteId="note-2" /> {/* → update_note_note-2 tool */}
177
+ </>
178
+ );
179
+ }
180
+ ```
181
+
182
+ When only one instance of a name exists, the tool is named `update_{name}` (e.g., `update_note`). When multiple instances exist, tools are named `update_{name}_{id}` (e.g., `update_note_note-1`).
183
+
184
+ ## Selection
185
+
186
+ When multiple interactables are present, you can mark one as "selected" to tell the AI which one the user is focused on:
187
+
188
+ ```tsx
189
+ function NoteCard({ noteId }: { noteId: string }) {
190
+ const [state, setState, { setSelected }] = useInteractable("note", {
191
+ id: noteId,
192
+ description: "A sticky note",
193
+ stateSchema: noteSchema,
194
+ initialState: noteInitialState,
195
+ });
196
+
197
+ return (
198
+ <div onClick={() => setSelected(true)}>
199
+ {state.title}
200
+ </div>
201
+ );
202
+ }
203
+ ```
204
+
205
+ The AI sees `(SELECTED)` in the system prompt for the focused interactable, allowing it to prioritize that one in responses. For example, the user can say _"Change the color to blue"_ and the AI knows which note to update.
206
+
207
+ ## API Reference
208
+
209
+ ### `useInteractable`
210
+
211
+ Hook that registers an interactable and returns its state with a setter.
212
+
213
+ ```tsx
214
+ const [state, setState, meta] = useInteractable<TState>(name, config);
215
+ ```
216
+
217
+ **Parameters:**
218
+
219
+ | Parameter | Type | Description |
220
+ | --- | --- | --- |
221
+ | `name` | `string` | Name for the interactable (used in tool names) |
222
+ | `config.description` | `string` | Description shown to the AI |
223
+ | `config.stateSchema` | `StandardSchemaV1 \| JSONSchema7` | Schema for the state (e.g., a Zod schema) |
224
+ | `config.initialState` | `TState` | Initial state value |
225
+ | `config.id` | `string?` | Optional unique instance ID (auto-generated if omitted) |
226
+ | `config.selected` | `boolean?` | Whether this interactable is selected |
227
+
228
+ **Returns:** `[state, setState, { id, setSelected }]`
229
+
230
+ | Return | Type | Description |
231
+ | --- | --- | --- |
232
+ | `state` | `TState` | Current state |
233
+ | `setState` | `(updater: TState \| (prev: TState) => TState) => void` | State setter (like `useState`) |
234
+ | `meta.id` | `string` | The instance ID (auto-generated or provided) |
235
+ | `meta.setSelected` | `(selected: boolean) => void` | Mark this interactable as selected |
236
+
237
+ ### `makeInteractable`
238
+
239
+ Declarative API that creates a component which registers an interactable when mounted. Useful for static configurations.
240
+
241
+ ```tsx
242
+ import { makeInteractable } from "@assistant-ui/react";
243
+
244
+ const TaskBoardInteractable = makeInteractable({
245
+ name: "taskBoard",
246
+ description: "A task board showing the user's tasks",
247
+ stateSchema: taskBoardSchema,
248
+ initialState: taskBoardInitialState,
249
+ });
250
+
251
+ // Mount it anywhere — renders nothing, just registers the interactable
252
+ function App() {
253
+ return (
254
+ <>
255
+ <TaskBoardInteractable />
256
+ <Thread />
257
+ </>
258
+ );
259
+ }
260
+ ```
261
+
262
+ ### `Interactables`
263
+
264
+ The scope resource that manages all interactables. Register it via `useAui`:
265
+
266
+ ```tsx
267
+ const aui = useAui({
268
+ interactables: Interactables(),
269
+ });
270
+ ```
271
+
272
+ ## How It Works
273
+
274
+ When you call `useInteractable("taskBoard", config)`:
275
+
276
+ 1. **Registration** — the interactable is registered in the `interactables` scope with its name, description, schema, and initial state.
277
+ 2. **Tool generation** — an `update_taskBoard` frontend tool is automatically created with a partial schema (all fields optional). For multiple instances, tools are named `update_{name}_{id}`.
278
+ 3. **System prompt** — the AI receives a system message describing the interactable, its current state, and whether it is selected.
279
+ 4. **Streaming updates** — as the AI generates the tool arguments, the interactable's state updates progressively rather than waiting for complete arguments. This gives users immediate visual feedback.
280
+ 5. **Partial merge** — only the fields the AI sends are updated; the rest are preserved.
281
+ 6. **Bidirectional updates** — when the AI calls the tool, the state updates and React re-renders. When the user updates state via `setState`, the model context is notified so the AI sees the latest state on the next turn.
282
+
283
+ ## Combining with Tools
284
+
285
+ You can use `Interactables` alongside `Tools`:
286
+
287
+ ```tsx
288
+ const aui = useAui({
289
+ tools: Tools({ toolkit: myToolkit }),
290
+ interactables: Interactables(),
291
+ });
292
+ ```
@@ -32,6 +32,7 @@ import "katex/dist/katex.min.css"; // [!code ++]
32
32
  ### Update `markdown-text.tsx`
33
33
 
34
34
  ```tsx title="/components/assistant-ui/markdown-text.tsx"
35
+ import { MarkdownTextPrimitive } from "@assistant-ui/react-markdown";
35
36
  import remarkMath from "remark-math"; // [!code ++]
36
37
  import rehypeKatex from "rehype-katex"; // [!code ++]
37
38
 
@@ -69,6 +70,8 @@ Many language models generate LaTeX using different delimiter formats:
69
70
  You can use the `preprocess` prop to normalize these formats:
70
71
 
71
72
  ```tsx title="/components/assistant-ui/markdown-text.tsx"
73
+ import { MarkdownTextPrimitive } from "@assistant-ui/react-markdown";
74
+
72
75
  const MarkdownTextImpl = () => {
73
76
  return (
74
77
  <MarkdownTextPrimitive
@@ -14,6 +14,7 @@ Display stream performance metrics — duration, tokens per second, TTFT — on
14
14
  Use `useMessageTiming()` inside a message component to access timing data:
15
15
 
16
16
  ```tsx
17
+ import type { FC } from "react";
17
18
  import { useMessageTiming } from "@assistant-ui/react";
18
19
 
19
20
  const MessageTimingDisplay: FC = () => {
@@ -39,7 +40,7 @@ Place it inside `MessagePrimitive.Root`, typically near the action bar:
39
40
  const AssistantMessage: FC = () => {
40
41
  return (
41
42
  <MessagePrimitive.Root>
42
- <MessagePrimitive.Parts components={{ ... }} />
43
+ <MessagePrimitive.Parts>{...}</MessagePrimitive.Parts>
43
44
  <ActionBarPrimitive.Root>
44
45
  <ActionBarPrimitive.Copy />
45
46
  <ActionBarPrimitive.Reload />
@@ -57,8 +58,8 @@ const AssistantMessage: FC = () => {
57
58
  | `streamStartTime` | `number` | Unix timestamp when stream started |
58
59
  | `firstTokenTime` | `number?` | Time to first text token (ms) |
59
60
  | `totalStreamTime` | `number?` | Total stream duration (ms) |
60
- | `tokenCount` | `number?` | Real or estimated output token count |
61
- | `tokensPerSecond` | `number?` | Throughput (tokens/sec); see [accuracy note](#ai-sdk-usechatruntime) |
61
+ | `tokenCount` | `number?` | Output token count from message metadata usage |
62
+ | `tokensPerSecond` | `number?` | Throughput (tokens/sec), when token usage is available |
62
63
  | `totalChunks` | `number` | Total stream chunks received |
63
64
  | `toolCallCount` | `number` | Number of tool calls |
64
65
 
@@ -89,7 +90,7 @@ const runtime = useDataStreamRuntime({ api: "/api/chat" });
89
90
  Timing is tracked automatically on the client side by observing streaming state transitions and content changes. Timing is finalized when each stream completes.
90
91
 
91
92
  <Callout type="warn">
92
- `tokenCount` and `tokensPerSecond` are **estimated** using a 4 characters per token approximation real token counts from the server are not extracted. This can overcount significantly for short messages.
93
+ `tokenCount` and `tokensPerSecond` require usage metadata from `finish` or `finish-step` in your AI SDK route. If usage metadata is not emitted, duration and TTFT metrics still work, but token-based metrics are omitted.
93
94
  </Callout>
94
95
 
95
96
  ```tsx
@@ -0,0 +1,174 @@
1
+ ---
2
+ title: Multi-Agent
3
+ description: Render sub-agent conversations inside tool call UIs.
4
+ ---
5
+
6
+ In a multi-agent (orchestrator) architecture, a main agent invokes sub-agents via tool calls. Each sub-agent may produce its own conversation (user/assistant messages, tool calls, etc.). assistant-ui supports rendering these nested conversations using the `MessagePartPrimitive.Messages` primitive.
7
+
8
+ ## Overview
9
+
10
+ When a tool call includes a `messages` field (`ToolCallMessagePart.messages`), it represents a sub-agent's conversation history. `MessagePartPrimitive.Messages` reads this field from the current tool call part and renders it as a nested thread.
11
+
12
+ Key behaviors:
13
+
14
+ - **Scope inheritance** — Parent tool UI registrations are available in sub-agent messages. A `makeAssistantToolUI` registered at the top level works inside sub-agent conversations too.
15
+ - **Recursive** — Sub-agent messages can contain tool calls that themselves have nested messages. Just use `MessagePartPrimitive.Messages` again.
16
+ - **Read-only** — Sub-agent messages are rendered in a readonly context. No editing, branching, or composing.
17
+
18
+ ## Quick Start
19
+
20
+ <Steps>
21
+ <Step>
22
+
23
+ ### Register a Tool UI for the Sub-Agent
24
+
25
+ ```tsx
26
+ import {
27
+ makeAssistantToolUI,
28
+ MessagePartPrimitive,
29
+ } from "@assistant-ui/react";
30
+
31
+ const ResearchAgentToolUI = makeAssistantToolUI({
32
+ toolName: "invoke_researcher",
33
+ render: ({ args, status }) => (
34
+ <div className="my-2 rounded-lg border p-4">
35
+ <div className="mb-2 text-sm font-medium text-gray-500">
36
+ Researcher Agent {status.type === "running" && "(working...)"}
37
+ </div>
38
+ <MessagePartPrimitive.Messages>
39
+ {({ message }) => {
40
+ if (message.role === "user") return <MyUserMessage />;
41
+ return <MyAssistantMessage />;
42
+ }}
43
+ </MessagePartPrimitive.Messages>
44
+ </div>
45
+ ),
46
+ });
47
+ ```
48
+
49
+ </Step>
50
+ <Step>
51
+
52
+ ### Provide the Messages from the Backend
53
+
54
+ Your backend must populate the `messages` field on the tool call result. For example, with the AI SDK:
55
+
56
+ ```ts title="api/chat/route.ts"
57
+ tools: {
58
+ invoke_researcher: tool({
59
+ description: "Invoke the researcher sub-agent",
60
+ parameters: z.object({ query: z.string() }),
61
+ execute: async ({ query }) => {
62
+ const subAgentMessages = await runResearcherAgent(query);
63
+ return {
64
+ answer: subAgentMessages.at(-1)?.content,
65
+ // The messages field is picked up by assistant-ui
66
+ messages: subAgentMessages,
67
+ };
68
+ },
69
+ }),
70
+ },
71
+ ```
72
+
73
+ <Callout type="info">
74
+ The exact mechanism for populating `messages` depends on your backend
75
+ framework. The key requirement is that the tool result's corresponding
76
+ `ToolCallMessagePart` includes a `messages` array of `ThreadMessage` objects.
77
+ </Callout>
78
+
79
+ </Step>
80
+ <Step>
81
+
82
+ ### Register the Tool UI Component
83
+
84
+ ```tsx
85
+ function App() {
86
+ return (
87
+ <AssistantRuntimeProvider runtime={runtime}>
88
+ <Thread />
89
+ <ResearchAgentToolUI />
90
+ </AssistantRuntimeProvider>
91
+ );
92
+ }
93
+ ```
94
+
95
+ </Step>
96
+ </Steps>
97
+
98
+ ## Recursive Sub-Agents
99
+
100
+ If a sub-agent's tool calls also have nested messages, the same pattern applies recursively:
101
+
102
+ ```tsx
103
+ const OuterAgentToolUI = makeAssistantToolUI({
104
+ toolName: "invoke_planner",
105
+ render: () => (
106
+ <div className="rounded border p-3">
107
+ <h4>Planner Agent</h4>
108
+ <MessagePartPrimitive.Messages>
109
+ {({ message }) => {
110
+ if (message.role === "user") return <MyUserMessage />;
111
+ return (
112
+ <MessagePrimitive.Parts>
113
+ {({ part }) => {
114
+ if (part.type === "text") return <MyText />;
115
+ if (part.type === "tool-call" && part.toolName === "invoke_researcher") return (
116
+ <div className="ml-4 rounded border p-3">
117
+ <h5>Researcher Agent</h5>
118
+ {/* Nested sub-agent renders recursively */}
119
+ <MessagePartPrimitive.Messages>
120
+ {({ message }) => {
121
+ if (message.role === "user") return <MyUserMessage />;
122
+ return <MyAssistantMessage />;
123
+ }}
124
+ </MessagePartPrimitive.Messages>
125
+ </div>
126
+ );
127
+ if (part.type === "tool-call") return <MyToolFallback {...part} />;
128
+ return null;
129
+ }}
130
+ </MessagePrimitive.Parts>
131
+ );
132
+ }}
133
+ </MessagePartPrimitive.Messages>
134
+ </div>
135
+ ),
136
+ });
137
+ ```
138
+
139
+ ## ReadonlyThreadProvider
140
+
141
+ For advanced use cases where you have a `ThreadMessage[]` array and want to render it as a thread outside of a tool call context, use `ReadonlyThreadProvider` directly:
142
+
143
+ ```tsx
144
+ import {
145
+ ReadonlyThreadProvider,
146
+ ThreadPrimitive,
147
+ type ThreadMessage,
148
+ } from "@assistant-ui/react";
149
+
150
+ function SubConversation({
151
+ messages,
152
+ }: {
153
+ messages: readonly ThreadMessage[];
154
+ }) {
155
+ return (
156
+ <ReadonlyThreadProvider messages={messages}>
157
+ <ThreadPrimitive.Messages>
158
+ {({ message }) => {
159
+ if (message.role === "user") return <MyUserMessage />;
160
+ return <MyAssistantMessage />;
161
+ }}
162
+ </ThreadPrimitive.Messages>
163
+ </ReadonlyThreadProvider>
164
+ );
165
+ }
166
+ ```
167
+
168
+ `ReadonlyThreadProvider` inherits the parent's tool UI registrations and model context through scope inheritance.
169
+
170
+ ## Related
171
+
172
+ - [Generative UI](/docs/guides/tool-ui) — Creating tool call UIs
173
+ - [MessagePartPrimitive](/docs/api-reference/primitives/message-part) — API reference for message part primitives
174
+ - [Sub-Agent Model Tracking](/docs/cloud/ai-sdk#sub-agent-model-tracking) — Track delegated model usage and costs in the Cloud dashboard