@assistant-ui/mcp-docs-server 0.1.35 → 0.1.38

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 (177) hide show
  1. package/.docs/organized/code-examples/waterfall.md +1 -1
  2. package/.docs/organized/code-examples/with-a2a.md +2 -2
  3. package/.docs/organized/code-examples/with-ag-ui.md +2 -2
  4. package/.docs/organized/code-examples/with-ai-sdk-v6.md +4 -4
  5. package/.docs/organized/code-examples/with-artifacts.md +4 -4
  6. package/.docs/organized/code-examples/with-assistant-transport.md +70 -54
  7. package/.docs/organized/code-examples/with-browser-extension.md +4 -4
  8. package/.docs/organized/code-examples/with-chain-of-thought.md +40 -89
  9. package/.docs/organized/code-examples/with-cloud-standalone.md +4 -4
  10. package/.docs/organized/code-examples/with-cloud.md +4 -4
  11. package/.docs/organized/code-examples/with-custom-thread-list.md +4 -4
  12. package/.docs/organized/code-examples/with-elevenlabs-conversational.md +7 -7
  13. package/.docs/organized/code-examples/with-elevenlabs-scribe.md +7 -7
  14. package/.docs/organized/code-examples/with-eve.md +343 -0
  15. package/.docs/organized/code-examples/with-expo.md +910 -933
  16. package/.docs/organized/code-examples/with-external-store.md +2 -2
  17. package/.docs/organized/code-examples/with-ffmpeg.md +5 -8
  18. package/.docs/organized/code-examples/with-generative-ui.md +6 -6
  19. package/.docs/organized/code-examples/with-google-adk.md +3 -3
  20. package/.docs/organized/code-examples/with-heat-graph.md +1 -1
  21. package/.docs/organized/code-examples/with-image-generation.md +4 -4
  22. package/.docs/organized/code-examples/with-interactables.md +166 -338
  23. package/.docs/organized/code-examples/with-langchain.md +4 -4
  24. package/.docs/organized/code-examples/with-langgraph.md +20 -157
  25. package/.docs/organized/code-examples/with-livekit.md +8 -8
  26. package/.docs/organized/code-examples/with-mcp.md +19 -10
  27. package/.docs/organized/code-examples/with-opencode.md +3 -3
  28. package/.docs/organized/code-examples/with-pi.md +9 -7
  29. package/.docs/organized/code-examples/with-react-hook-form.md +13 -6
  30. package/.docs/organized/code-examples/with-react-ink-web.md +5 -5
  31. package/.docs/organized/code-examples/with-react-ink.md +1 -1
  32. package/.docs/organized/code-examples/with-react-router.md +11 -11
  33. package/.docs/organized/code-examples/with-resumable-stream.md +5 -5
  34. package/.docs/organized/code-examples/with-store.md +1 -1
  35. package/.docs/organized/code-examples/with-tanstack.md +7 -7
  36. package/.docs/organized/code-examples/with-tap-runtime.md +2 -2
  37. package/.docs/organized/code-examples/with-virtualized-thread.md +44 -24
  38. package/.docs/raw/docs/(docs)/cli.mdx +5 -3
  39. package/.docs/raw/docs/(docs)/devtools.mdx +25 -2
  40. package/.docs/raw/docs/(docs)/index.mdx +5 -2
  41. package/.docs/raw/docs/(docs)/installation.mdx +5 -2
  42. package/.docs/raw/docs/(reference)/api-reference/external-store/message-conversion.mdx +16 -16
  43. package/.docs/raw/docs/(reference)/api-reference/hooks/composer-triggers.mdx +73 -42
  44. package/.docs/raw/docs/(reference)/api-reference/hooks/model-context.mdx +3 -20
  45. package/.docs/raw/docs/(reference)/api-reference/hooks/primitives.mdx +234 -166
  46. package/.docs/raw/docs/(reference)/api-reference/integrations/eve.mdx +51 -0
  47. package/.docs/raw/docs/(reference)/api-reference/integrations/index.mdx +4 -1
  48. package/.docs/raw/docs/(reference)/api-reference/integrations/react-ai-sdk.mdx +70 -38
  49. package/.docs/raw/docs/(reference)/api-reference/primitives/action-bar.mdx +30 -0
  50. package/.docs/raw/docs/(reference)/api-reference/primitives/attachment.mdx +7 -0
  51. package/.docs/raw/docs/(reference)/api-reference/primitives/branch-picker.mdx +26 -0
  52. package/.docs/raw/docs/(reference)/api-reference/primitives/chain-of-thought.mdx +15 -0
  53. package/.docs/raw/docs/(reference)/api-reference/primitives/composer.mdx +103 -0
  54. package/.docs/raw/docs/(reference)/api-reference/primitives/message-part.mdx +16 -0
  55. package/.docs/raw/docs/(reference)/api-reference/primitives/message.mdx +56 -0
  56. package/.docs/raw/docs/(reference)/api-reference/primitives/queue-item.mdx +12 -0
  57. package/.docs/raw/docs/(reference)/api-reference/primitives/selection-toolbar.mdx +12 -0
  58. package/.docs/raw/docs/(reference)/api-reference/primitives/suggestion.mdx +14 -0
  59. package/.docs/raw/docs/(reference)/api-reference/primitives/thread.mdx +57 -1
  60. package/.docs/raw/docs/(reference)/api-reference/tools/index.mdx +6 -0
  61. package/.docs/raw/docs/(reference)/api-reference/tools/interactables-legacy.mdx +55 -0
  62. package/.docs/raw/docs/(reference)/api-reference/tools/interactables.mdx +151 -0
  63. package/.docs/raw/docs/(reference)/api-reference/tools/rendering.mdx +15 -15
  64. package/.docs/raw/docs/(reference)/api-reference/tools/toolkits.mdx +20 -20
  65. package/.docs/raw/docs/(reference)/api-reference/utilities/miscellaneous.mdx +25 -9
  66. package/.docs/raw/docs/cloud/langgraph.mdx +1 -1
  67. package/.docs/raw/docs/guides/headless-composer-input.mdx +113 -0
  68. package/.docs/raw/docs/guides/latex.mdx +28 -22
  69. package/.docs/raw/docs/guides/mentions.mdx +32 -7
  70. package/.docs/raw/docs/guides/slash-commands.mdx +3 -6
  71. package/.docs/raw/docs/guides/speech.mdx +5 -7
  72. package/.docs/raw/docs/guides/virtualization.mdx +77 -7
  73. package/.docs/raw/docs/guides/voice.mdx +3 -2
  74. package/.docs/raw/docs/integrations/attachments/custom-adapter.mdx +4 -12
  75. package/.docs/raw/docs/integrations/auth/better-auth.mdx +7 -10
  76. package/.docs/raw/docs/integrations/auth/clerk.mdx +3 -9
  77. package/.docs/raw/docs/integrations/auth/next-auth.mdx +3 -8
  78. package/.docs/raw/docs/integrations/frameworks/ai-sdk.mdx +11 -8
  79. package/.docs/raw/docs/integrations/index.mdx +6 -13
  80. package/.docs/raw/docs/integrations/observability/helicone.mdx +3 -4
  81. package/.docs/raw/docs/integrations/observability/langfuse.mdx +3 -5
  82. package/.docs/raw/docs/integrations/observability/langsmith.mdx +5 -4
  83. package/.docs/raw/docs/integrations/persistence/custom-adapter.mdx +4 -3
  84. package/.docs/raw/docs/migrations/react-langgraph-v0-7.mdx +2 -2
  85. package/.docs/raw/docs/primitives/composer.mdx +8 -0
  86. package/.docs/raw/docs/primitives/thread-list.mdx +30 -6
  87. package/.docs/raw/docs/primitives/thread.mdx +24 -0
  88. package/.docs/raw/docs/runtimes/a2a/quickstart.mdx +1 -1
  89. package/.docs/raw/docs/runtimes/ag-ui/quickstart.mdx +1 -1
  90. package/.docs/raw/docs/runtimes/ag-ui/runtime-options.mdx +24 -1
  91. package/.docs/raw/docs/runtimes/ai-sdk/v6.mdx +1 -1
  92. package/.docs/raw/docs/runtimes/custom/external-store.mdx +54 -0
  93. package/.docs/raw/docs/runtimes/eve/overview.mdx +100 -0
  94. package/.docs/raw/docs/runtimes/eve/quickstart.mdx +143 -0
  95. package/.docs/raw/docs/runtimes/google-adk/quickstart.mdx +1 -1
  96. package/.docs/raw/docs/runtimes/langchain.mdx +388 -20
  97. package/.docs/raw/docs/runtimes/langgraph/quickstart.mdx +1 -1
  98. package/.docs/raw/docs/runtimes/langgraph/tutorial/part-1.mdx +1 -1
  99. package/.docs/raw/docs/runtimes/opencode/hooks.mdx +1 -1
  100. package/.docs/raw/docs/runtimes/pick-a-runtime.mdx +6 -0
  101. package/.docs/raw/docs/tools/defining-tools.mdx +9 -0
  102. package/.docs/raw/docs/tools/generative-ui.mdx +37 -0
  103. package/.docs/raw/docs/tools/interactables-legacy.mdx +410 -0
  104. package/.docs/raw/docs/tools/interactables.mdx +892 -223
  105. package/.docs/raw/docs/tools/mcp-apps.mdx +12 -3
  106. package/.docs/raw/docs/tools/mcp.mdx +9 -4
  107. package/.docs/raw/docs/tools/multi-agent.mdx +5 -0
  108. package/.docs/raw/docs/tools/user-managed-mcp.mdx +17 -3
  109. package/.docs/raw/docs/ui/composer-trigger-popover.mdx +2 -0
  110. package/.docs/raw/docs/ui/file.mdx +1 -1
  111. package/.docs/raw/docs/ui/follow-up-suggestions.mdx +80 -0
  112. package/.docs/raw/docs/ui/model-selector.mdx +38 -5
  113. package/.docs/raw/docs/ui/part-grouping.mdx +38 -0
  114. package/.docs/raw/docs/utilities/react-o11y.mdx +92 -6
  115. package/README.md +1 -1
  116. package/dist/constants.d.ts +2 -1
  117. package/dist/constants.d.ts.map +1 -1
  118. package/dist/constants.js +2 -1
  119. package/dist/constants.js.map +1 -1
  120. package/dist/index.d.ts.map +1 -1
  121. package/dist/index.js +30 -2
  122. package/dist/index.js.map +1 -1
  123. package/dist/prepare-docs/copy-raw.d.ts.map +1 -1
  124. package/dist/prepare-docs/copy-raw.js +0 -4
  125. package/dist/prepare-docs/copy-raw.js.map +1 -1
  126. package/dist/tools/docs.d.ts.map +1 -1
  127. package/dist/tools/docs.js +18 -6
  128. package/dist/tools/docs.js.map +1 -1
  129. package/dist/tools/examples.d.ts +3 -1
  130. package/dist/tools/examples.d.ts.map +1 -1
  131. package/dist/tools/examples.js +1 -1
  132. package/dist/tools/examples.js.map +1 -1
  133. package/dist/tools/resources.d.ts +7 -0
  134. package/dist/tools/resources.d.ts.map +1 -0
  135. package/dist/tools/resources.js +74 -0
  136. package/dist/tools/resources.js.map +1 -0
  137. package/dist/tools/search.d.ts +33 -0
  138. package/dist/tools/search.d.ts.map +1 -0
  139. package/dist/tools/search.js +39 -0
  140. package/dist/tools/search.js.map +1 -0
  141. package/dist/tools/tests/test-setup.d.ts.map +1 -1
  142. package/dist/tools/tests/test-setup.js +3 -1
  143. package/dist/tools/tests/test-setup.js.map +1 -1
  144. package/dist/utils/mdx.d.ts +2 -1
  145. package/dist/utils/mdx.d.ts.map +1 -1
  146. package/dist/utils/mdx.js +19 -2
  147. package/dist/utils/mdx.js.map +1 -1
  148. package/dist/utils/paths.d.ts +2 -1
  149. package/dist/utils/paths.d.ts.map +1 -1
  150. package/dist/utils/paths.js +18 -1
  151. package/dist/utils/paths.js.map +1 -1
  152. package/dist/utils/search.d.ts +10 -0
  153. package/dist/utils/search.d.ts.map +1 -0
  154. package/dist/utils/search.js +97 -0
  155. package/dist/utils/search.js.map +1 -0
  156. package/package.json +4 -4
  157. package/src/constants.ts +2 -0
  158. package/src/index.ts +28 -6
  159. package/src/prepare-docs/copy-raw.ts +0 -5
  160. package/src/tools/docs.ts +30 -4
  161. package/src/tools/examples.ts +4 -2
  162. package/src/tools/resources.ts +114 -0
  163. package/src/tools/search.ts +46 -0
  164. package/src/tools/tests/completions.test.ts +46 -0
  165. package/src/tools/tests/directory-size-cap.test.ts +50 -0
  166. package/src/tools/tests/mcp-protocol.test.ts +21 -1
  167. package/src/tools/tests/resources.test.ts +102 -0
  168. package/src/tools/tests/search.test.ts +37 -0
  169. package/src/tools/tests/test-setup.ts +2 -0
  170. package/src/utils/mdx.ts +21 -1
  171. package/src/utils/paths.ts +22 -0
  172. package/src/utils/search.ts +131 -0
  173. package/.docs/raw/blog/2024-07-29-hello/index.mdx +0 -64
  174. package/.docs/raw/blog/2024-09-11/index.mdx +0 -10
  175. package/.docs/raw/blog/2024-12-15/index.mdx +0 -10
  176. package/.docs/raw/blog/2025-01-31-changelog/index.mdx +0 -127
  177. package/.docs/raw/blog/2026-03-launch-week/index.mdx +0 -258
@@ -8,12 +8,9 @@ Mentions let users type `@` in the composer to open a popover picker, select an
8
8
 
9
9
  ## How It Works
10
10
 
11
- ```
12
- User types "@" → Trigger detected → Adapter provides categories/items
13
- ↓
14
- Directive inserted ← User selects item from popover
15
- ↓
16
- Message sent with ":tool[Label]{name=id}" in text
11
+ ```mermaid
12
+ flowchart LR
13
+ type["User types @"] --> trigger["Trigger detected"] --> adapter["Adapter provides<br/>categories / items"] --> select["User selects item<br/>from popover"] --> insert["Directive inserted"] --> sent["Message sent with<br/>:tool[Label]{name=id} text"]
17
14
  ```
18
15
 
19
16
  The mention system has three layers:
@@ -85,7 +82,35 @@ const myAdapter: Unstable_TriggerAdapter = {
85
82
 
86
83
  ### Async Mention Search
87
84
 
88
- The adapter interface is synchronous, but the data it reads can come from any async source. Load results into React state (or a query cache) and read the current snapshot inside the adapter methods. The adapter re-creates on each render, so the popover always sees the latest results.
85
+ The built-in `unstable_useLiveCompletionAdapter` wraps an async fetcher with debouncing, stale-request cancellation (results for an outdated query are dropped), and a single-entry cache. Its `search` returns the last results synchronously and schedules a debounced fetch when the query changes; when results arrive the returned `adapter` re-creates, which re-runs the popover lookup so the fresh items render. It also reports `isLoading`, which you pass to the popover to show a loading state.
86
+
87
+ ```tsx
88
+ import {
89
+ unstable_useLiveCompletionAdapter,
90
+ unstable_defaultDirectiveFormatter,
91
+ } from "@assistant-ui/react";
92
+ import { ComposerTriggerPopover } from "@/components/assistant-ui/composer-trigger-popover";
93
+
94
+ function MentionPopover() {
95
+ const mentions = unstable_useLiveCompletionAdapter({
96
+ fetcher: async (query) => {
97
+ const users = await fetchUsers(query);
98
+ return users.map((u) => ({ id: u.id, type: "user", label: u.name }));
99
+ },
100
+ });
101
+
102
+ return (
103
+ <ComposerTriggerPopover
104
+ char="@"
105
+ adapter={mentions.adapter}
106
+ isLoading={mentions.isLoading}
107
+ directive={{ formatter: unstable_defaultDirectiveFormatter }}
108
+ />
109
+ );
110
+ }
111
+ ```
112
+
113
+ The adapter interface is itself synchronous, so you can also wire async data by hand when you need a custom cache, no debounce, or an existing query client. Load results into React state (or a query cache) and read the current snapshot inside the adapter methods. The adapter re-creates on each render, so the popover always sees the latest results.
89
114
 
90
115
  **With React state and `useEffect`:**
91
116
 
@@ -8,12 +8,9 @@ Slash commands let users type `/` in the composer to open a popover, browse avai
8
8
 
9
9
  ## How It Works
10
10
 
11
- ```
12
- User types "/" → Trigger detected → Adapter provides commands
13
- ↓
14
- Callback fired ← User selects command from popover
15
- ↓
16
- Directive chip left in composer (or removed if removeOnExecute)
11
+ ```mermaid
12
+ flowchart LR
13
+ type["User types /"] --> trigger["Trigger detected"] --> adapter["Adapter provides<br/>commands"] --> select["User selects command<br/>from popover"] --> callback["Callback fired"] --> chip["Directive chip left in composer<br/>(or removed if removeOnExecute)"]
17
14
  ```
18
15
 
19
16
  The slash command system is built on the same [trigger popover architecture](#trigger-popover-architecture) as mentions. A slash command declares its behavior with a `<TriggerPopover.Action>` sub-primitive whose `onExecute` callback fires when an item is chosen.
@@ -57,24 +57,22 @@ const runtime = useChatRuntime({
57
57
  The default action bar does not include a speech button. Add `ActionBarPrimitive.Speak` and `ActionBarPrimitive.StopSpeaking` to your assistant message action bar:
58
58
 
59
59
  ```tsx
60
- import { ActionBarPrimitive, useMessageTTS } from "@assistant-ui/react";
60
+ import { ActionBarPrimitive, AuiIf } from "@assistant-ui/react";
61
61
  import { AudioLinesIcon, StopCircleIcon } from "lucide-react";
62
62
 
63
63
  const AssistantActionBar = () => {
64
- const isSpeaking = useMessageTTS();
65
-
66
64
  return (
67
65
  <ActionBarPrimitive.Root>
68
- {!isSpeaking && (
66
+ <AuiIf condition={(s) => s.message.speech == null}>
69
67
  <ActionBarPrimitive.Speak>
70
68
  <AudioLinesIcon />
71
69
  </ActionBarPrimitive.Speak>
72
- )}
73
- {isSpeaking && (
70
+ </AuiIf>
71
+ <AuiIf condition={(s) => s.message.speech != null}>
74
72
  <ActionBarPrimitive.StopSpeaking>
75
73
  <StopCircleIcon />
76
74
  </ActionBarPrimitive.StopSpeaking>
77
- )}
75
+ </AuiIf>
78
76
  <ActionBarPrimitive.Copy />
79
77
  </ActionBarPrimitive.Root>
80
78
  );
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  title: Thread Virtualization
3
- description: Render very long threads with @tanstack/react-virtual and ThreadPrimitive.MessageByIndex.
3
+ description: Render very long threads with @tanstack/react-virtual, with ThreadPrimitive.Unstable_MessageById and ThreadPrimitive.MessageByIndex.
4
4
  platforms: ["react"]
5
5
  ---
6
6
 
@@ -10,9 +10,35 @@ Virtualization mounts only the messages near the viewport and represents the res
10
10
 
11
11
  Probably not. The default kit already renders message bodies with `content-visibility: auto` and `contain-intrinsic-size`, which skips paint work for off-screen messages, and `ThreadPrimitive.Viewport` handles auto-scroll. That covers typical threads. Reach for virtualization when React mount and update cost itself becomes the bottleneck: threads with hundreds to thousands of messages, or very heavy per-message content, where typing latency degrades because every message stays mounted.
12
12
 
13
- ## Rendering messages by index
13
+ ## Rendering Messages By Id
14
14
 
15
- `ThreadPrimitive.MessageByIndex` renders a single message at a given index and memoizes on the index plus per-field `components` identity. Pass a module-level components object so the memo holds and a streaming delta only re-renders the message it belongs to:
15
+ For virtualized rows, use message ids as the row identity. `unstable_useThreadMessageIds` returns the thread's ids with stable array identity across content-only updates, and `ThreadPrimitive.Unstable_MessageById` renders one message with the same `components` surface as `MessageByIndex`. Unknown ids render `null`, which is useful when a virtual row unmounts after a message was removed.
16
+
17
+ ```tsx
18
+ const MESSAGE_COMPONENTS = { UserMessage, AssistantMessage };
19
+
20
+ const messageIds = unstable_useThreadMessageIds();
21
+
22
+ return messageIds.map((messageId) => (
23
+ <ThreadPrimitive.Unstable_MessageById
24
+ key={messageId}
25
+ messageId={messageId}
26
+ components={MESSAGE_COMPONENTS}
27
+ />
28
+ ));
29
+ ```
30
+
31
+ <Callout type="warn">
32
+ `unstable_useThreadMessageIds` and `ThreadPrimitive.Unstable_MessageById` are
33
+ experimental and may change in any release.
34
+ </Callout>
35
+
36
+ ## Rendering Messages By Index
37
+
38
+ `ThreadPrimitive.MessageByIndex` is still supported and is the smaller API when
39
+ you already have a stable index from a fixed-order list. It renders a single
40
+ message at that index and memoizes on the index plus the per-field `components`
41
+ identity:
16
42
 
17
43
  ```tsx
18
44
  const MESSAGE_COMPONENTS = { UserMessage, AssistantMessage };
@@ -20,15 +46,59 @@ const MESSAGE_COMPONENTS = { UserMessage, AssistantMessage };
20
46
  <ThreadPrimitive.MessageByIndex index={index} components={MESSAGE_COMPONENTS} />;
21
47
  ```
22
48
 
49
+ For virtualizers, prefer the id-based API above when you can. Index rows are more
50
+ fragile when messages are inserted, removed, reordered, or when a virtual row
51
+ briefly outlives the item it was created for.
52
+
23
53
  ## Grouping into turns
24
54
 
25
- Virtualizing per user turn (a user message plus the responses that follow it) gives the virtualizer stable, meaningfully sized items. Subscribe to a single signature string so the selector returns a primitive and the turn array only rebuilds when membership actually changes:
55
+ The example virtualizes per user turn (a user message plus the responses that follow it), which gives the virtualizer stable, meaningfully sized items. Keep the id and role together in a stable row shape so the turn array only rebuilds when message membership or roles change:
26
56
 
27
57
  ```tsx
28
- const signature = useAuiState((s) =>
29
- s.thread.messages.map((m, i) => `${i}:${m.role}:${m.id}`).join("\n"),
58
+ type MessageRow = {
59
+ id: string;
60
+ role: "user" | "assistant" | "system";
61
+ };
62
+
63
+ const useThreadMessageRows = (): readonly MessageRow[] => {
64
+ const prevRowsRef = useRef<readonly MessageRow[]>([]);
65
+
66
+ return useAuiState((s) => {
67
+ const messages = s.thread.messages;
68
+ const prev = prevRowsRef.current;
69
+ if (
70
+ prev.length === messages.length &&
71
+ prev.every((row, index) => {
72
+ const message = messages[index]!;
73
+ return row.id === message.id && row.role === message.role;
74
+ })
75
+ ) {
76
+ return prev;
77
+ }
78
+
79
+ const next = messages.map(({ id, role }) => ({ id, role }));
80
+ prevRowsRef.current = next;
81
+ return next;
82
+ });
83
+ };
84
+
85
+ const messageRows = useThreadMessageRows();
86
+ const turns = useMemo(
87
+ () => buildTurns(messageRows),
88
+ [messageRows],
30
89
  );
31
- const turns = useMemo(() => buildTurns(signature), [signature]);
90
+ ```
91
+
92
+ Then each virtual turn renders the ids it owns:
93
+
94
+ ```tsx
95
+ {turn.messageIds.map((messageId) => (
96
+ <ThreadPrimitive.Unstable_MessageById
97
+ key={messageId}
98
+ messageId={messageId}
99
+ components={MESSAGE_COMPONENTS}
100
+ />
101
+ ))}
32
102
  ```
33
103
 
34
104
  ## Padding spacers, not absolute positioning
@@ -156,8 +156,9 @@ class MyVoiceAdapter implements RealtimeVoiceAdapter {
156
156
 
157
157
  The session status follows the same pattern as other adapters:
158
158
 
159
- ```
160
- starting → running → ended
159
+ ```mermaid
160
+ flowchart LR
161
+ starting["starting"] --> running["running"] --> ended["ended"]
161
162
  ```
162
163
 
163
164
  The `ended` status includes a `reason`:
@@ -9,18 +9,10 @@ For the adapter contract itself, see [adapters](/docs/runtimes/concepts/adapters
9
9
 
10
10
  ## How it works
11
11
 
12
- ```
13
- composer add ──► POST /api/upload (presign) ──► PUT to object storage
14
- │
15
- ▼
16
- PendingAttachment with the public URL
17
- │
18
- composer send ◄────────────────────────────────────┘
19
- │
20
- └─► send() emits a content part with the URL
21
- │
22
- ▼
23
- AI SDK passes URL to the model
12
+ ```mermaid
13
+ flowchart LR
14
+ add["composer add"] --> presign["POST /api/upload<br/>(presign)"] --> put["PUT to object storage"] --> pending["PendingAttachment<br/>with the public URL"]
15
+ pending --> send["composer send"] --> part["send() emits a<br/>content part with the URL"] --> model["AI SDK passes<br/>URL to the model"]
24
16
  ```
25
17
 
26
18
  Three ideas to internalize before reading the code:
@@ -9,16 +9,13 @@ This page covers the non-cloud path. AssistantCloud users should see [cloud auth
9
9
 
10
10
  ## How it works
11
11
 
12
- ```
13
- browser ──► /api/auth/[...all] ──► better-auth handlers
14
- /api/chat │
15
- /api/threads/* │
16
- │ │
17
- ▼ ▼
18
- auth.api.getSession({ headers }) ──► session.user.id
19
- │
20
- ▼
21
- scope queries by userId
12
+ ```mermaid
13
+ flowchart LR
14
+ browser["browser"] --> routes["/api/auth/[...all]<br/>/api/chat<br/>/api/threads/*"]
15
+ routes --> handlers["better-auth handlers"]
16
+ routes --> session["auth.api.getSession({ headers })"]
17
+ handlers --> session
18
+ session --> uid["session.user.id"] --> scope["scope queries by userId"]
22
19
  ```
23
20
 
24
21
  Three integration points:
@@ -9,15 +9,9 @@ If you use AssistantCloud, see [cloud authorization](/docs/cloud/authorization)
9
9
 
10
10
  ## How it works
11
11
 
12
- ```
13
- browser ──► clerkMiddleware (proxy.ts) ──► /api/chat
14
- /api/threads/*
15
- │
16
- ▼
17
- auth() returns { userId }
18
- │
19
- ▼
20
- scope queries by userId
12
+ ```mermaid
13
+ flowchart LR
14
+ browser["browser"] --> mw["clerkMiddleware<br/>(proxy.ts)"] --> routes["/api/chat<br/>/api/threads/*"] --> auth["auth() returns { userId }"] --> scope["scope queries by userId"]
21
15
  ```
22
16
 
23
17
  Three places Clerk touches the integration:
@@ -9,14 +9,9 @@ If you use AssistantCloud, see [cloud authorization](/docs/cloud) instead; cloud
9
9
 
10
10
  ## How it works
11
11
 
12
- ```
13
- browser ──► /api/chat ──► auth() returns session
14
- /api/threads/* │
15
- ▼
16
- scope queries by session.user.id
17
- │
18
- ▼
19
- database
12
+ ```mermaid
13
+ flowchart LR
14
+ browser["browser"] --> routes["/api/chat<br/>/api/threads/*"] --> auth["auth() returns session"] --> scope["scope queries by<br/>session.user.id"] --> db["database"]
20
15
  ```
21
16
 
22
17
  Three places auth touches the integration:
@@ -5,7 +5,7 @@ description: Wire the Vercel AI SDK into a React chat UI with assistant-ui — u
5
5
 
6
6
  import { VercelIcon } from "@/components/icons/vercel";
7
7
 
8
- [Vercel AI SDK](https://ai-sdk.dev/) is the most common framework people pair with assistant-ui. The full setup, attachments, persistence, tool-call patterns, and version notes are documented under [runtimes/ai-sdk](/docs/runtimes/ai-sdk); this page is the entry point in the integrations tree for discoverability and architecture context.
8
+ [Vercel AI SDK](https://ai-sdk.dev/) is the most common framework people pair with assistant-ui. The full setup, attachments, persistence, tool-call patterns, and version notes are documented under [runtimes/ai-sdk](/docs/runtimes/ai-sdk/overview); this page is the entry point in the integrations tree for discoverability and architecture context.
9
9
 
10
10
  <Callout type="info">
11
11
  If you arrived here looking to wire up your first chat: jump to [AI SDK v6 quickstart](/docs/runtimes/ai-sdk/v6). This page is a high-level pointer.
@@ -13,11 +13,14 @@ If you arrived here looking to wire up your first chat: jump to [AI SDK v6 quick
13
13
 
14
14
  ## Where it slots in
15
15
 
16
- ```
17
- client ──► useChatRuntime (react-ai-sdk) ──► /api/chat (streamText) ──► provider
18
- │
19
- └─ thread state, tool calls, attachments,
20
- speech / dictation / feedback adapters
16
+ ```mermaid
17
+ flowchart LR
18
+ client["client"]
19
+ runtime["useChatRuntime (react-ai-sdk)<br/>(thread state · tool calls · attachments<br/>speech · dictation · feedback adapters)"]
20
+ api["/api/chat<br/>(streamText)"]
21
+ provider["provider"]
22
+
23
+ client --> runtime --> api --> provider
21
24
  ```
22
25
 
23
26
  `@assistant-ui/react-ai-sdk` wraps the AI SDK's `useChat` hook and exposes it as an assistant-ui runtime. The runtime owns conversation state on the client; your `/api/chat` route returns a UI message stream from `streamText`. Everything else (tools, attachments, observability, gateways, custom persistence) layers on top of this base.
@@ -55,7 +58,7 @@ AI SDK is the default choice for new projects on Next.js, Remix, or any framewor
55
58
  - You will compose with a framework like [Mastra](/docs/integrations/frameworks/mastra/overview), an [observability tool](/docs/integrations/observability/helicone), an [LLM gateway](/docs/integrations/gateways), or [tools through MCP](/docs/tools/mcp), all of which assume an AI SDK route.
56
59
  - You want first-party `frontendTools`, attachments, multi-step tool calls, token-usage metadata, and persisted history via `withFormat`.
57
60
 
58
- If you need streaming agent state (subgraph events, generative UI messages), look at [LangGraph](/docs/runtimes/langgraph) instead. If you have a different protocol-shaped backend (A2A, AG-UI, OpenCode), see [pick a runtime](/docs/runtimes/pick-a-runtime).
61
+ If you need streaming agent state (subgraph events, generative UI messages), look at [LangGraph](/docs/runtimes/langgraph/overview) instead. If you have a different protocol-shaped backend (A2A, AG-UI, OpenCode), see [pick a runtime](/docs/runtimes/pick-a-runtime).
59
62
 
60
63
  ## Related
61
64
 
@@ -64,7 +67,7 @@ If you need streaming agent state (subgraph events, generative UI messages), loo
64
67
  icon={<VercelIcon width={20} height={20} />}
65
68
  title="AI SDK runtime overview"
66
69
  description="The full runtime documentation and version selector."
67
- href="/docs/runtimes/ai-sdk"
70
+ href="/docs/runtimes/ai-sdk/overview"
68
71
  />
69
72
  <Card
70
73
  title="Pick a runtime"
@@ -15,18 +15,11 @@ Integrations are wiring guides for using third-party services with assistant-ui,
15
15
 
16
16
  ## Where integrations slot in
17
17
 
18
- ```
19
- client ──► your API route ──► LLM provider
20
- │ ▲
21
- │ │
22
- agent │ │ observability
23
- frameworks │ │ proxies
24
- (e.g. │ │ (e.g. Helicone,
25
- Mastra) │ │ Langfuse)
26
- ▼ │
27
- run on the server, │
28
- then forward calls ──────┘
29
- to the provider
18
+ ```mermaid
19
+ flowchart LR
20
+ client["client"] --> route["your API route"] --> provider["LLM provider"]
21
+ route -->|"agent frameworks (e.g. Mastra)"| server["run on the server,<br/>then forward calls"]
22
+ server -->|"observability proxies (e.g. Helicone, Langfuse)"| provider
30
23
  ```
31
24
 
32
25
  Integrations live on the server. **Agent frameworks** like Mastra take over the API route. **Gateways** swap the upstream provider URL. **Observability** logs or traces every call. **Auth** gates the route and scopes per-user data. **Persistence** and **attachments** are adapter recipes for storing chat data outside the default in-memory path.
@@ -165,7 +158,7 @@ Upload chat attachments to object storage instead of inlining as data URLs.
165
158
  assistant-ui doesn't ship a guide for every tool, but most fit one of two patterns:
166
159
 
167
160
  - **Routes through your AI SDK handler** (agent frameworks, observability proxies, gateways): adapt the [Mastra full-stack](/docs/integrations/frameworks/mastra/full-stack) or [Helicone proxy](/docs/integrations/observability/helicone) pattern using the service's own SDK.
168
- - **Replaces the runtime entirely** (custom backends): see [custom backend](/docs/runtimes/custom).
161
+ - **Replaces the runtime entirely** (custom backends): see [custom backend](/docs/runtimes/custom/overview).
169
162
 
170
163
  If you build something useful, [open an issue](https://github.com/assistant-ui/assistant-ui/issues) or post in [Discord](https://discord.gg/S9dwgCNEFs); the docs are open to contributions.
171
164
 
@@ -11,10 +11,9 @@ Helicone is independent of which assistant-ui runtime you use. It slots in at th
11
11
 
12
12
  ## How it works
13
13
 
14
- ```
15
- your server ──► Helicone proxy ──► OpenAI / Anthropic / etc.
16
- │
17
- └─ logs request, response, tokens, cost
14
+ ```mermaid
15
+ flowchart LR
16
+ server["your server"] --> proxy["Helicone proxy<br/>(logs request, response, tokens, cost)"] --> provider["OpenAI / Anthropic / etc."]
18
17
  ```
19
18
 
20
19
  Calls pass through Helicone's edge before reaching the upstream provider. The proxy is transparent: response shape and streaming behavior are unchanged, you just gain a dashboard of every call.
@@ -13,11 +13,9 @@ Pick Langfuse when you want to see the agent's full call tree inside a single tu
13
13
 
14
14
  ## How it works
15
15
 
16
- ```
17
- your route ──► AI SDK streamText (with experimental_telemetry)
18
- │
19
- ▼
20
- OpenTelemetry SDK ──► LangfuseSpanProcessor ──► Langfuse
16
+ ```mermaid
17
+ flowchart LR
18
+ route["your route"] --> stream["AI SDK streamText<br/>(experimental_telemetry)"] --> otel["OpenTelemetry SDK"] --> proc["LangfuseSpanProcessor"] --> langfuse["Langfuse"]
21
19
  ```
22
20
 
23
21
  Langfuse subscribes to OpenTelemetry spans the AI SDK already emits when telemetry is enabled. No proxy, no wrapping; the SDK ships spans and Langfuse renders them.
@@ -9,14 +9,15 @@ import { VercelIcon } from "@/components/icons/vercel";
9
9
 
10
10
  [LangSmith](https://www.langchain.com/langsmith) is LangChain's observability and eval platform. If you are already in the LangChain or LangGraph ecosystem, LangSmith is the natural pairing: traces, datasets, prompt versioning, and LLM-as-judge evals share state with the rest of the LangChain stack.
11
11
 
12
- This page covers the **AI SDK** path. If you use [`@assistant-ui/react-langgraph`](/docs/runtimes/langgraph), tracing flows through LangGraph Cloud automatically; you only need this guide when your route handler talks to AI SDK directly.
12
+ This page covers the **AI SDK** path. If you use [`@assistant-ui/react-langgraph`](/docs/runtimes/langgraph/overview), tracing flows through LangGraph Cloud automatically; you only need this guide when your route handler talks to AI SDK directly.
13
13
 
14
14
  ## How it works
15
15
 
16
16
  LangSmith provides a wrapper around the `ai` namespace. You call `wrapAISDK(ai)`, get back the same exports (`generateText`, `streamText`, `generateObject`, `streamObject`), and use those in place of the originals. Every call is then traced.
17
17
 
18
- ```
19
- your route ──► wrapped streamText ──► LangSmith client ──► LangSmith
18
+ ```mermaid
19
+ flowchart LR
20
+ route["your route"] --> stream["wrapped streamText"] --> client["LangSmith client"] --> langsmith["LangSmith"]
20
21
  ```
21
22
 
22
23
  ## Setup
@@ -133,7 +134,7 @@ Send a message. The trace should appear in your LangSmith project within seconds
133
134
  icon={<LangGraphIcon width={20} height={20} className="text-[#1C3C3C] dark:text-[#5b9595]" />}
134
135
  title="LangGraph runtime"
135
136
  description="If your backend is LangGraph, tracing flows through LangGraph Cloud automatically."
136
- href="/docs/runtimes/langgraph"
137
+ href="/docs/runtimes/langgraph/overview"
137
138
  />
138
139
  <Card
139
140
  icon={<LangfuseIcon width={20} height={20} />}
@@ -18,9 +18,10 @@ If you're rolling your own auth, replace `auth()` calls with whatever your stack
18
18
 
19
19
  ## How it works
20
20
 
21
- ```
22
- client ──► /api/threads/* (RemoteThreadListAdapter) ──► threads table
23
- /api/messages/* (ThreadHistoryAdapter) ──► messages table
21
+ ```mermaid
22
+ flowchart LR
23
+ client["client"] --> threads["/api/threads/*<br/>(RemoteThreadListAdapter)"] --> tt["threads table"]
24
+ client --> messages["/api/messages/*<br/>(ThreadHistoryAdapter)"] --> mt["messages table"]
24
25
  ```
25
26
 
26
27
  Two adapters, two tables:
@@ -323,6 +323,6 @@ export function Provider({ children }) {
323
323
  ## Need Help?
324
324
 
325
325
  If you encounter issues during migration:
326
- 1. Check the updated [LangGraph documentation](/docs/runtimes/langgraph)
326
+ 1. Check the updated [LangGraph documentation](/docs/runtimes/langgraph/overview)
327
327
  2. Review the [example implementation](https://github.com/assistant-ui/assistant-ui/tree/main/examples/with-langgraph)
328
- 3. Report issues on [GitHub](https://github.com/assistant-ui/assistant-ui/issues)
328
+ 3. Report issues on [GitHub](https://github.com/assistant-ui/assistant-ui/issues)
@@ -103,6 +103,14 @@ import { ComposerPrimitive } from "@assistant-ui/react";
103
103
 
104
104
  The primitive's behavior (keyboard handling, disabled state, form submission) is merged onto your element. Your styles, your component, primitive wiring.
105
105
 
106
+ <Callout type="info">
107
+ Own the input DOM entirely, such as a `contentEditable` surface or editor
108
+ library that cannot be expressed through `asChild` or `render`? See
109
+ [Headless Composer Input](/docs/guides/headless-composer-input) for the
110
+ unstable hook that supplies composer text and send gating without
111
+ `ComposerPrimitive.Input`.
112
+ </Callout>
113
+
106
114
  ### Unstable Trigger Popovers
107
115
 
108
116
  Composer includes an unstable **trigger popover** system for character-triggered popovers (e.g. `@` for mentions, `/` for slash commands). Multiple triggers coexist under a single `TriggerPopoverRoot`.
@@ -38,12 +38,12 @@ function MyThreadList() {
38
38
 
39
39
  function ThreadListItem() {
40
40
  return (
41
- <ThreadListItemPrimitive.Root className="group flex h-9 items-center rounded-lg hover:bg-muted data-active:bg-muted">
42
- <ThreadListItemPrimitive.Trigger className="flex-1 truncate px-3 text-sm">
41
+ <ThreadListItemPrimitive.Root className="group relative flex h-9 items-center rounded-lg hover:bg-muted data-active:bg-muted has-focus-visible:bg-muted">
42
+ <ThreadListItemPrimitive.Trigger className="min-w-0 flex-1 truncate px-3 text-sm outline-none">
43
43
  <ThreadListItemPrimitive.Title fallback="New Chat" />
44
44
  </ThreadListItemPrimitive.Trigger>
45
- <ThreadListItemMorePrimitive.Root>
46
- <ThreadListItemMorePrimitive.Trigger className="mr-2 size-7 rounded-md opacity-0 group-hover:opacity-100">
45
+ <ThreadListItemMorePrimitive.Root sharedFocusGroup>
46
+ <ThreadListItemMorePrimitive.Trigger className="absolute end-1.5 top-1/2 size-7 -translate-y-1/2 rounded-md opacity-0 group-hover:opacity-100 group-has-focus-visible:opacity-100 data-[state=open]:opacity-100">
47
47
  <MoreHorizontalIcon className="size-4" />
48
48
  </ThreadListItemMorePrimitive.Trigger>
49
49
  <ThreadListItemMorePrimitive.Content className="rounded-md border bg-popover p-1 shadow-md">
@@ -118,6 +118,21 @@ Both `ThreadListPrimitive.New` and `ThreadListItemPrimitive.Root` get a `data-ac
118
118
 
119
119
  The `New` button gets `data-active` when the user is on a fresh, unsaved thread.
120
120
 
121
+ ### Keyboard Navigation
122
+
123
+ Every item stays a native Tab stop, and arrow keys are layered on top. Up/Down move between items and Right focuses an item's `More` button (Left/Right are mirrored in RTL). This is built into the primitives — no prop is required.
124
+
125
+ To fold the `More` menu into the same navigation, opt it in with `sharedFocusGroup`:
126
+
127
+ ```tsx
128
+ <ThreadListItemMorePrimitive.Root sharedFocusGroup>
129
+ {/* Right opens the menu from the trigger; Left/Escape close it and
130
+ return focus to the trigger, keeping the highlight continuous. */}
131
+ </ThreadListItemMorePrimitive.Root>
132
+ ```
133
+
134
+ `sharedFocusGroup` joins the menu to the list's focus group, so its trigger and the open menu act as one keyboard-navigable unit: Right opens it and Left/Escape close it, synchronously restoring focus to the trigger so a `group-has-focus-visible` highlight never flickers. Enabling it forces the menu non-modal, because a focus trap and a shared focus group are mutually exclusive — the trap keeps focus inside the menu, the shared group lets it move out across the boundary. Without `sharedFocusGroup` the menu keeps its standard Radix `DropdownMenu` keyboard behavior (modal by default), so menus you already render are unchanged. Our assistant-ui [thread-list](/docs/ui/thread-list) registry component sets `sharedFocusGroup` for you.
135
+
121
136
  ### Items Iterator
122
137
 
123
138
  `ThreadListPrimitive.Items` now prefers a children render function, similar to `ThreadPrimitive.Messages`:
@@ -168,6 +183,15 @@ The canonical pattern composes `ThreadListItemPrimitive.Archive asChild` with `T
168
183
 
169
184
  `Archive` provides the click handler and disabled logic. `Item` provides the menu item behavior and styling. `asChild` merges them into a single element.
170
185
 
186
+ ## Accessibility
187
+
188
+ The thread list leans on native button semantics and Radix's `DropdownMenu`; the arrow-key navigation is layered on top as a convenience, not a requirement.
189
+
190
+ - Each `ThreadListItemPrimitive.Trigger` is a native `<button>` and its own Tab stop, so the whole list is reachable with <Kbd>Tab</Kbd> alone. The arrow keys (see [Keyboard Navigation](#keyboard-navigation)) speed traversal up, but nothing depends on them.
191
+ - The item representing the current thread sets `aria-current="true"` (alongside `data-active`) on `ThreadListItemPrimitive.Root`, so assistive tech announces which conversation is open.
192
+ - The `More` trigger exposes `aria-haspopup="menu"`, and its panel is a Radix `role="menu"` with `role="menuitem"` children, so screen readers announce it as a menu and the arrow keys cycle its items.
193
+ - With `sharedFocusGroup`, the `More` menu is non-modal and joins the list's focus group: opening and closing it move focus synchronously between the trigger and the menu, so keyboard focus is never dropped and the `group-has-focus-visible` highlight never flickers. Without it, the menu keeps Radix's default modal focus trap.
194
+
171
195
  ## Parts
172
196
 
173
197
  ### ThreadListPrimitive
@@ -319,10 +343,10 @@ Button that deletes the current thread item. Renders a `<button>` element unless
319
343
 
320
344
  #### Root
321
345
 
322
- Root container for the overflow menu primitives.
346
+ Root container for the overflow menu primitives. Pass `sharedFocusGroup` to fold the menu into the list's arrow-key navigation (see [Keyboard Navigation](#keyboard-navigation)); doing so forces it non-modal. Without it the menu honors the `modal` prop, defaulting to modal like Radix's `DropdownMenu`.
323
347
 
324
348
  ```tsx
325
- <ThreadListItemMorePrimitive.Root>
349
+ <ThreadListItemMorePrimitive.Root sharedFocusGroup>
326
350
  <ThreadListItemMorePrimitive.Trigger>More</ThreadListItemMorePrimitive.Trigger>
327
351
  </ThreadListItemMorePrimitive.Root>
328
352
  ```
@@ -319,6 +319,30 @@ Renders a single message at a specific index in the thread.
319
319
 
320
320
  <PrimitivesTypeTable type="ThreadPrimitiveMessageByIndexProps" parameters={ThreadPrimitiveDocs.MessageByIndex.props} />
321
321
 
322
+ ### Unstable_MessageById
323
+
324
+ Renders a single message by id with the same `components` surface as
325
+ `MessageByIndex`. Pair it with `unstable_useThreadMessageIds` for virtualized or
326
+ custom message lists that should stay attached to messages across reordering and
327
+ windowing. Unknown ids render `null`.
328
+
329
+ ```tsx
330
+ const messageIds = unstable_useThreadMessageIds();
331
+
332
+ messageIds.map((messageId) => (
333
+ <ThreadPrimitive.Unstable_MessageById
334
+ key={messageId}
335
+ messageId={messageId}
336
+ components={{ Message: MyMessage }}
337
+ />
338
+ ));
339
+ ```
340
+
341
+ <Callout type="warn">
342
+ `unstable_useThreadMessageIds` and `ThreadPrimitive.Unstable_MessageById` are
343
+ experimental and may change in any release.
344
+ </Callout>
345
+
322
346
  ### ScrollToBottom
323
347
 
324
348
  Scrolls the viewport to the bottom. Automatically disabled when already at the bottom. Renders a `<button>` element unless `asChild` is set.
@@ -3,7 +3,7 @@ title: Quickstart
3
3
  description: Minimal runtime and Thread setup against an A2A server.
4
4
  ---
5
5
 
6
- Three steps to a working chat against an A2A server. Assumes you have already installed the package and have an A2A v1.0 server reachable; if not, start at [overview](/docs/runtimes/a2a).
6
+ Three steps to a working chat against an A2A server. Assumes you have already installed the package and have an A2A v1.0 server reachable; if not, start at [overview](/docs/runtimes/a2a/overview).
7
7
 
8
8
  <Steps>
9
9
  <Step>
@@ -3,7 +3,7 @@ title: Quickstart
3
3
  description: Minimal HttpAgent + useAgUiRuntime setup against an AG-UI server.
4
4
  ---
5
5
 
6
- Three steps to a working chat against an AG-UI agent. Assumes you have already installed the package and have an AG-UI server reachable; if not, start at [overview](/docs/runtimes/ag-ui).
6
+ Three steps to a working chat against an AG-UI agent. Assumes you have already installed the package and have an AG-UI server reachable; if not, start at [overview](/docs/runtimes/ag-ui/overview).
7
7
 
8
8
  <Steps>
9
9
  <Step>