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

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 (258) hide show
  1. package/.docs/organized/code-examples/waterfall.md +11 -12
  2. package/.docs/organized/code-examples/with-a2a.md +18 -13
  3. package/.docs/organized/code-examples/with-ag-ui.md +19 -14
  4. package/.docs/organized/code-examples/{with-ai-sdk-v6.md → with-ai-sdk-v7.md} +34 -23
  5. package/.docs/organized/code-examples/with-artifacts.md +473 -141
  6. package/.docs/organized/code-examples/with-assistant-transport.md +17 -10
  7. package/.docs/organized/code-examples/with-browser-extension.md +17 -10
  8. package/.docs/organized/code-examples/with-chain-of-thought.md +21 -14
  9. package/.docs/organized/code-examples/with-cloud-standalone.md +12 -11
  10. package/.docs/organized/code-examples/with-cloud.md +20 -15
  11. package/.docs/organized/code-examples/with-custom-thread-list.md +19 -12
  12. package/.docs/organized/code-examples/with-elevenlabs-conversational.md +22 -15
  13. package/.docs/organized/code-examples/with-elevenlabs-scribe.md +22 -15
  14. package/.docs/organized/code-examples/with-eve.md +18 -11
  15. package/.docs/organized/code-examples/with-expo.md +35 -52
  16. package/.docs/organized/code-examples/with-external-store.md +18 -13
  17. package/.docs/organized/code-examples/with-ffmpeg.md +20 -15
  18. package/.docs/organized/code-examples/with-generative-ui.md +22 -17
  19. package/.docs/organized/code-examples/with-google-adk.md +18 -11
  20. package/.docs/organized/code-examples/with-heat-graph.md +10 -11
  21. package/.docs/organized/code-examples/with-image-generation.md +19 -12
  22. package/.docs/organized/code-examples/with-interactables.md +21 -17
  23. package/.docs/organized/code-examples/with-langchain.md +19 -12
  24. package/.docs/organized/code-examples/with-langgraph.md +19 -12
  25. package/.docs/organized/code-examples/with-livekit.md +23 -16
  26. package/.docs/organized/code-examples/with-mcp.md +46 -28
  27. package/.docs/organized/code-examples/with-opencode.md +25 -23
  28. package/.docs/organized/code-examples/with-pi.md +54 -19
  29. package/.docs/organized/code-examples/with-react-hook-form.md +21 -16
  30. package/.docs/organized/code-examples/with-react-ink-web.md +9 -9
  31. package/.docs/organized/code-examples/with-react-ink.md +4 -4
  32. package/.docs/organized/code-examples/with-react-router.md +22 -17
  33. package/.docs/organized/code-examples/with-resumable-stream.md +21 -14
  34. package/.docs/organized/code-examples/with-store.md +10 -11
  35. package/.docs/organized/code-examples/with-tanstack.md +19 -13
  36. package/.docs/organized/code-examples/with-tap-runtime.md +18 -13
  37. package/.docs/organized/code-examples/with-virtualized-thread.md +19 -14
  38. package/.docs/raw/docs/(docs)/base-ui.mdx +39 -0
  39. package/.docs/raw/docs/(docs)/cli.mdx +19 -1
  40. package/.docs/raw/docs/(docs)/devtools.mdx +7 -2
  41. package/.docs/raw/docs/(docs)/installation.mdx +15 -1
  42. package/.docs/raw/docs/(docs)/rtl.mdx +2 -4
  43. package/.docs/raw/docs/(reference)/api-reference/generative-ui/a2ui.mdx +40 -0
  44. package/.docs/raw/docs/(reference)/api-reference/generative-ui/actions.mdx +56 -0
  45. package/.docs/raw/docs/(reference)/api-reference/generative-ui/components.mdx +86 -0
  46. package/.docs/raw/docs/(reference)/api-reference/generative-ui/index.mdx +22 -1
  47. package/.docs/raw/docs/(reference)/api-reference/generative-ui/json-generative-ui.mdx +42 -0
  48. package/.docs/raw/docs/(reference)/api-reference/generative-ui/rendering.mdx +53 -2
  49. package/.docs/raw/docs/(reference)/api-reference/generative-ui/slack.mdx +81 -0
  50. package/.docs/raw/docs/(reference)/api-reference/generative-ui/teams.mdx +86 -0
  51. package/.docs/raw/docs/(reference)/api-reference/generative-ui/tokens.mdx +62 -0
  52. package/.docs/raw/docs/(reference)/api-reference/hooks/primitives.mdx +19 -420
  53. package/.docs/raw/docs/(reference)/api-reference/hooks/state.mdx +4 -1
  54. package/.docs/raw/docs/(reference)/api-reference/integrations/index.mdx +4 -1
  55. package/.docs/raw/docs/(reference)/api-reference/integrations/react-ai-sdk.mdx +25 -2
  56. package/.docs/raw/docs/(reference)/api-reference/integrations/react-data-stream.mdx +37 -0
  57. package/.docs/raw/docs/(reference)/api-reference/model-context/context.mdx +1 -9
  58. package/.docs/raw/docs/(reference)/api-reference/overview.mdx +1 -0
  59. package/.docs/raw/docs/(reference)/api-reference/primitives/assistant-if.mdx +14 -31
  60. package/.docs/raw/docs/(reference)/api-reference/primitives/composition.mdx +1 -0
  61. package/.docs/raw/docs/(reference)/api-reference/primitives/index.mdx +3 -0
  62. package/.docs/raw/docs/(reference)/api-reference/primitives/selection-toolbar.mdx +2 -0
  63. package/.docs/raw/docs/(reference)/api-reference/tools/status.mdx +0 -2
  64. package/.docs/raw/docs/(reference)/api-reference/tools/toolkits.mdx +12 -9
  65. package/.docs/raw/docs/(reference)/api-reference/utilities/miscellaneous.mdx +4 -0
  66. package/.docs/raw/docs/cloud/ai-sdk-assistant-ui.mdx +7 -1
  67. package/.docs/raw/docs/cloud/langgraph.mdx +4 -2
  68. package/.docs/raw/docs/copilots/model-context.mdx +1 -1
  69. package/.docs/raw/docs/copilots/motivation.mdx +1 -1
  70. package/.docs/raw/docs/guides/attachments.mdx +3 -3
  71. package/.docs/raw/docs/guides/branching.mdx +2 -2
  72. package/.docs/raw/docs/guides/chatgpt-subscription.mdx +108 -0
  73. package/.docs/raw/docs/guides/context-api.mdx +89 -111
  74. package/.docs/raw/docs/guides/dictation.mdx +185 -257
  75. package/.docs/raw/docs/guides/editing.mdx +5 -5
  76. package/.docs/raw/docs/guides/electron.mdx +369 -0
  77. package/.docs/raw/docs/guides/index.mdx +20 -0
  78. package/.docs/raw/docs/guides/mentions.mdx +31 -3
  79. package/.docs/raw/docs/guides/quoting.mdx +3 -3
  80. package/.docs/raw/docs/guides/resumable-stream-deployment.mdx +2 -2
  81. package/.docs/raw/docs/guides/resumable-streams.mdx +12 -1
  82. package/.docs/raw/docs/guides/speech.mdx +47 -29
  83. package/.docs/raw/docs/guides/suggestions.mdx +70 -1
  84. package/.docs/raw/docs/guides/voice.mdx +197 -267
  85. package/.docs/raw/docs/ink/hooks.mdx +3 -3
  86. package/.docs/raw/docs/ink/primitives.mdx +49 -8
  87. package/.docs/raw/docs/integrations/auth/better-auth.mdx +2 -2
  88. package/.docs/raw/docs/integrations/auth/clerk.mdx +2 -2
  89. package/.docs/raw/docs/integrations/auth/next-auth.mdx +3 -3
  90. package/.docs/raw/docs/integrations/frameworks/ai-sdk.mdx +10 -4
  91. package/.docs/raw/docs/integrations/frameworks/cloudflare-agents/overview.mdx +3 -3
  92. package/.docs/raw/docs/integrations/frameworks/mastra/full-stack.mdx +1 -1
  93. package/.docs/raw/docs/integrations/frameworks/mastra/overview.mdx +3 -3
  94. package/.docs/raw/docs/integrations/frameworks/mastra/separate-server.mdx +1 -1
  95. package/.docs/raw/docs/integrations/gateways/index.mdx +2 -2
  96. package/.docs/raw/docs/integrations/observability/helicone.mdx +1 -1
  97. package/.docs/raw/docs/integrations/observability/langfuse.mdx +1 -1
  98. package/.docs/raw/docs/integrations/observability/langsmith.mdx +2 -2
  99. package/.docs/raw/docs/integrations/persistence/custom-adapter.mdx +36 -9
  100. package/.docs/raw/docs/migrations/index.mdx +50 -0
  101. package/.docs/raw/docs/migrations/toolkit-tools.mdx +4 -2
  102. package/.docs/raw/docs/migrations/v0-15.mdx +156 -0
  103. package/.docs/raw/docs/primitives/chain-of-thought.mdx +6 -1
  104. package/.docs/raw/docs/primitives/composer.mdx +17 -1
  105. package/.docs/raw/docs/primitives/selection-toolbar.mdx +25 -0
  106. package/.docs/raw/docs/primitives/thread-list.mdx +2 -2
  107. package/.docs/raw/docs/react-native/hooks.mdx +3 -3
  108. package/.docs/raw/docs/react-native/index.mdx +2 -2
  109. package/.docs/raw/docs/react-native/primitives.mdx +62 -5
  110. package/.docs/raw/docs/runtimes/ag-ui/agent-state.mdx +124 -0
  111. package/.docs/raw/docs/runtimes/ag-ui/runtime-options.mdx +10 -1
  112. package/.docs/raw/docs/runtimes/ai-sdk/overview.mdx +14 -5
  113. package/.docs/raw/docs/runtimes/ai-sdk/v4-legacy.mdx +15 -15
  114. package/.docs/raw/docs/runtimes/ai-sdk/v5-legacy.mdx +8 -8
  115. package/.docs/raw/docs/runtimes/ai-sdk/{v6.mdx → v6-legacy.mdx} +11 -9
  116. package/.docs/raw/docs/runtimes/ai-sdk/v7.mdx +717 -0
  117. package/.docs/raw/docs/runtimes/concepts/adapters.mdx +1 -1
  118. package/.docs/raw/docs/runtimes/concepts/architecture.mdx +1 -1
  119. package/.docs/raw/docs/runtimes/concepts/threads.mdx +10 -10
  120. package/.docs/raw/docs/runtimes/custom/assistant-transport.mdx +2 -2
  121. package/.docs/raw/docs/runtimes/custom/data-stream.mdx +10 -19
  122. package/.docs/raw/docs/runtimes/custom/external-store.mdx +2 -2
  123. package/.docs/raw/docs/runtimes/custom/local-runtime.mdx +5 -3
  124. package/.docs/raw/docs/runtimes/eve/overview.mdx +1 -1
  125. package/.docs/raw/docs/runtimes/langchain.mdx +2 -2
  126. package/.docs/raw/docs/runtimes/langgraph/agent-state.mdx +181 -0
  127. package/.docs/raw/docs/runtimes/langgraph/overview.mdx +1 -1
  128. package/.docs/raw/docs/runtimes/opencode/hooks.mdx +3 -1
  129. package/.docs/raw/docs/tools/a2ui.mdx +107 -0
  130. package/.docs/raw/docs/tools/backend.mdx +6 -3
  131. package/.docs/raw/docs/tools/defining-tools.mdx +7 -1
  132. package/.docs/raw/docs/tools/generative-ui.mdx +60 -2
  133. package/.docs/raw/docs/tools/interactables-legacy.mdx +4 -4
  134. package/.docs/raw/docs/tools/interactables.mdx +3 -3
  135. package/.docs/raw/docs/tools/mcp-apps.mdx +90 -13
  136. package/.docs/raw/docs/tools/mcp.mdx +100 -3
  137. package/.docs/raw/docs/tools/tool-ui.mdx +6 -4
  138. package/.docs/raw/docs/tools/user-managed-mcp.mdx +77 -9
  139. package/.docs/raw/docs/ui/accordion.mdx +16 -10
  140. package/.docs/raw/docs/ui/assistant-modal.mdx +8 -4
  141. package/.docs/raw/docs/ui/attachment.mdx +5 -1
  142. package/.docs/raw/docs/ui/badge.mdx +23 -12
  143. package/.docs/raw/docs/ui/follow-up-suggestions.mdx +4 -2
  144. package/.docs/raw/docs/ui/model-selector.mdx +33 -3
  145. package/.docs/raw/docs/ui/part-grouping.mdx +0 -4
  146. package/.docs/raw/docs/ui/reasoning.mdx +1 -1
  147. package/.docs/raw/docs/ui/select.mdx +22 -14
  148. package/.docs/raw/docs/ui/sources.mdx +1 -1
  149. package/.docs/raw/docs/ui/tabs.mdx +25 -14
  150. package/.docs/raw/docs/utilities/heat-graph.mdx +2 -2
  151. package/dist/constants.d.ts.map +1 -1
  152. package/dist/index.d.ts +1 -2
  153. package/dist/index.d.ts.map +1 -1
  154. package/dist/index.js +41 -2
  155. package/dist/index.js.map +1 -1
  156. package/dist/prepare-docs/code-examples.d.ts.map +1 -1
  157. package/dist/prepare-docs/code-examples.js.map +1 -1
  158. package/dist/prepare-docs/copy-raw.d.ts.map +1 -1
  159. package/dist/prepare-docs/prepare.d.ts +1 -1
  160. package/dist/prompts/xulux-playground.d.ts +12 -0
  161. package/dist/prompts/xulux-playground.d.ts.map +1 -0
  162. package/dist/prompts/xulux-playground.js +33 -0
  163. package/dist/prompts/xulux-playground.js.map +1 -0
  164. package/dist/stdio.d.ts +1 -1
  165. package/dist/tools/docs.d.ts +8 -14
  166. package/dist/tools/docs.d.ts.map +1 -1
  167. package/dist/tools/docs.js +26 -10
  168. package/dist/tools/docs.js.map +1 -1
  169. package/dist/tools/examples.d.ts +6 -12
  170. package/dist/tools/examples.d.ts.map +1 -1
  171. package/dist/tools/examples.js +11 -8
  172. package/dist/tools/examples.js.map +1 -1
  173. package/dist/tools/resources.d.ts +1 -2
  174. package/dist/tools/resources.d.ts.map +1 -1
  175. package/dist/tools/resources.js +1 -1
  176. package/dist/tools/resources.js.map +1 -1
  177. package/dist/tools/search.d.ts +6 -15
  178. package/dist/tools/search.d.ts.map +1 -1
  179. package/dist/tools/search.js +2 -2
  180. package/dist/tools/search.js.map +1 -1
  181. package/dist/tools/tests/mcp-test-client.d.ts +15 -0
  182. package/dist/tools/tests/mcp-test-client.d.ts.map +1 -0
  183. package/dist/tools/tests/mcp-test-client.js +68 -0
  184. package/dist/tools/tests/mcp-test-client.js.map +1 -0
  185. package/dist/tools/tests/test-setup.d.ts.map +1 -1
  186. package/dist/tools/tests/test-setup.js +5 -1
  187. package/dist/tools/tests/test-setup.js.map +1 -1
  188. package/dist/tools/xulux-templates.d.ts +58 -0
  189. package/dist/tools/xulux-templates.d.ts.map +1 -0
  190. package/dist/tools/xulux-templates.js +82 -0
  191. package/dist/tools/xulux-templates.js.map +1 -0
  192. package/dist/utils/cache.d.ts +5 -0
  193. package/dist/utils/cache.d.ts.map +1 -0
  194. package/dist/utils/cache.js +18 -0
  195. package/dist/utils/cache.js.map +1 -0
  196. package/dist/utils/logger.d.ts.map +1 -1
  197. package/dist/utils/mcp-format.d.ts +1 -0
  198. package/dist/utils/mcp-format.d.ts.map +1 -1
  199. package/dist/utils/mcp-format.js +7 -4
  200. package/dist/utils/mcp-format.js.map +1 -1
  201. package/dist/utils/mdx.d.ts.map +1 -1
  202. package/dist/utils/paths.d.ts +1 -1
  203. package/dist/utils/paths.d.ts.map +1 -1
  204. package/dist/utils/paths.js +3 -1
  205. package/dist/utils/paths.js.map +1 -1
  206. package/dist/utils/search.d.ts.map +1 -1
  207. package/dist/utils/security.d.ts.map +1 -1
  208. package/dist/utils/security.js.map +1 -1
  209. package/dist/xulux/catalog-client.d.ts +14 -0
  210. package/dist/xulux/catalog-client.d.ts.map +1 -0
  211. package/dist/xulux/catalog-client.js +67 -0
  212. package/dist/xulux/catalog-client.js.map +1 -0
  213. package/dist/xulux/fallback-catalog.d.ts +7 -0
  214. package/dist/xulux/fallback-catalog.d.ts.map +1 -0
  215. package/dist/xulux/fallback-catalog.js +47 -0
  216. package/dist/xulux/fallback-catalog.js.map +1 -0
  217. package/dist/xulux/fetch-sandbox.d.ts +5 -0
  218. package/dist/xulux/fetch-sandbox.d.ts.map +1 -0
  219. package/dist/xulux/fetch-sandbox.js +40 -0
  220. package/dist/xulux/fetch-sandbox.js.map +1 -0
  221. package/dist/xulux/template-service.d.ts +84 -0
  222. package/dist/xulux/template-service.d.ts.map +1 -0
  223. package/dist/xulux/template-service.js +223 -0
  224. package/dist/xulux/template-service.js.map +1 -0
  225. package/dist/xulux/types.d.ts +55 -0
  226. package/dist/xulux/types.d.ts.map +1 -0
  227. package/dist/xulux/types.js +6 -0
  228. package/dist/xulux/types.js.map +1 -0
  229. package/package.json +7 -6
  230. package/src/index.ts +55 -2
  231. package/src/prompts/xulux-playground.ts +36 -0
  232. package/src/tools/docs.ts +27 -5
  233. package/src/tools/examples.ts +17 -12
  234. package/src/tools/resources.ts +1 -4
  235. package/src/tools/search.ts +2 -2
  236. package/src/tools/tests/completions.test.ts +40 -26
  237. package/src/tools/tests/docs.test.ts +20 -0
  238. package/src/tools/tests/examples.test.ts +5 -5
  239. package/src/tools/tests/integration.test.ts +3 -4
  240. package/src/tools/tests/listings-cache.test.ts +19 -0
  241. package/src/tools/tests/mcp-protocol.test.ts +173 -108
  242. package/src/tools/tests/mcp-test-client.ts +111 -0
  243. package/src/tools/tests/resources.test.ts +97 -66
  244. package/src/tools/tests/test-setup.ts +8 -0
  245. package/src/tools/tests/xulux-templates.test.ts +262 -0
  246. package/src/tools/xulux-templates.ts +141 -0
  247. package/src/utils/cache.ts +20 -0
  248. package/src/utils/mcp-format.ts +8 -6
  249. package/src/utils/paths.ts +4 -1
  250. package/src/utils/tests/cache.test.ts +51 -0
  251. package/src/utils/tests/mcp-format.test.ts +22 -0
  252. package/src/utils/tests/security.test.ts +1 -1
  253. package/src/xulux/catalog-client.ts +105 -0
  254. package/src/xulux/fallback-catalog.ts +63 -0
  255. package/src/xulux/fetch-sandbox.ts +56 -0
  256. package/src/xulux/template-service.ts +406 -0
  257. package/src/xulux/types.ts +60 -0
  258. package/.docs/raw/docs/(reference)/api-reference/hooks/utilities.mdx +0 -464
@@ -196,7 +196,7 @@ type ThreadHistoryAdapter = {
196
196
  `load` runs when a thread opens. `append` runs after each message completes.
197
197
 
198
198
  <Callout type="info">
199
- `react-ai-sdk` requires `withFormat` so messages round-trip as AI SDK `UIMessage` objects. An adapter without `withFormat` throws at runtime in the AI SDK path. See the [AI SDK history docs](/docs/runtimes/ai-sdk/v6) for the full pattern.
199
+ `react-ai-sdk` requires `withFormat` so messages round-trip as AI SDK `UIMessage` objects. An adapter without `withFormat` throws at runtime in the AI SDK path. See the [AI SDK history docs](/docs/runtimes/ai-sdk/v7) for the full pattern.
200
200
  </Callout>
201
201
 
202
202
  ## Suggestion adapter
@@ -118,7 +118,7 @@ The fastest path. Each adapter wraps one of the core or protocol layers and adds
118
118
 
119
119
  | Adapter | Layered on | Targets |
120
120
  | --- | --- | --- |
121
- | `react-ai-sdk` | `ExternalStoreRuntime` | Vercel AI SDK v6 (`useChat`) |
121
+ | `react-ai-sdk` | `ExternalStoreRuntime` | Vercel AI SDK v7 (`useChat`) |
122
122
  | `react-langgraph` | `ExternalStoreRuntime` | LangGraph Cloud via `@langchain/langgraph-sdk` |
123
123
  | `react-langchain` | `ExternalStoreRuntime` | LangGraph Cloud via `@langchain/react`'s `useStream` |
124
124
  | `react-google-adk` | `ExternalStoreRuntime` | Google ADK JS or Python agents |
@@ -149,7 +149,7 @@ const adapterWithHistory: RemoteThreadListAdapter = {
149
149
  const history = useMemo<ThreadHistoryAdapter>(
150
150
  () => ({
151
151
  async load() {
152
- const { remoteId } = aui.threadListItem().getState();
152
+ const { remoteId } = aui.threadListItem.getState();
153
153
  if (!remoteId) return { messages: [] };
154
154
  const rows = await fetch(
155
155
  `/api/threads/${remoteId}/messages`,
@@ -157,7 +157,7 @@ const adapterWithHistory: RemoteThreadListAdapter = {
157
157
  return { messages: rows.map(toThreadMessage) };
158
158
  },
159
159
  async append({ message, parentId }) {
160
- const { remoteId } = await aui.threadListItem().initialize();
160
+ const { remoteId } = await aui.threadListItem.initialize();
161
161
  await fetch(`/api/threads/${remoteId}/messages`, {
162
162
  method: "POST",
163
163
  body: JSON.stringify({ message, parentId }),
@@ -181,11 +181,11 @@ const adapterWithHistory: RemoteThreadListAdapter = {
181
181
 
182
182
  ### Avoiding the first-message race
183
183
 
184
- `append` may be called before the thread record exists in your backend. Always await `aui.threadListItem().initialize()` before writing:
184
+ `append` may be called before the thread record exists in your backend. Always await `aui.threadListItem.initialize()` before writing:
185
185
 
186
186
  ```ts
187
187
  async append({ message, parentId }) {
188
- const { remoteId } = await aui.threadListItem().initialize();
188
+ const { remoteId } = await aui.threadListItem.initialize();
189
189
  await saveMessage(remoteId, parentId, message);
190
190
  }
191
191
  ```
@@ -194,14 +194,14 @@ async append({ message, parentId }) {
194
194
 
195
195
  ### Reloading after async authentication
196
196
 
197
- If your adapter depends on a user that resolves asynchronously (oidc, `next-auth`, `better-auth`), the initial `list()` may run before the user is available. Call `aui.threads().reload()` after auth completes:
197
+ If your adapter depends on a user that resolves asynchronously (oidc, `next-auth`, `better-auth`), the initial `list()` may run before the user is available. Call `aui.threads.reload()` after auth completes:
198
198
 
199
199
  ```tsx
200
200
  function ReloadOnAuth() {
201
201
  const aui = useAui();
202
202
  const { isLoading, user } = useAuth();
203
203
  useEffect(() => {
204
- if (!isLoading && user) aui.threads().reload();
204
+ if (!isLoading && user) aui.threads.reload();
205
205
  }, [isLoading, user?.id]);
206
206
  return null;
207
207
  }
@@ -211,7 +211,7 @@ function ReloadOnAuth() {
211
211
 
212
212
  ### Paginating the thread list
213
213
 
214
- If your backend returns thread pages, return a `nextCursor` from `list()` and consume `aui.threads().hasMore` plus `aui.threads().loadMore()` in the UI. The runtime threads `params.after` back through `list()` on every `loadMore()`; the initial call passes no `params`, so treat a missing `after` as "first page". `reload()` resets the cursor so the next load starts from page 1 again.
214
+ If your backend returns thread pages, return a `nextCursor` from `list()` and consume `aui.threads.hasMore` plus `aui.threads.loadMore()` in the UI. The runtime threads `params.after` back through `list()` on every `loadMore()`; the initial call passes no `params`, so treat a missing `after` as "first page". `reload()` resets the cursor so the next load starts from page 1 again.
215
215
 
216
216
  ```ts title="threadListAdapter.ts (excerpt)"
217
217
  async list({ after } = {}) {
@@ -271,7 +271,7 @@ A few invariants worth knowing when wiring a custom UI on top of `loadMore()`:
271
271
  name: "list",
272
272
  type: "(params?: { after?: string }) => Promise<{ threads: RemoteThreadMetadata[]; nextCursor?: string }>",
273
273
  description:
274
- "Hydrate threads on mount. Each thread must include status and remoteId; title, externalId, and custom are optional. Return a `nextCursor` to enable `aui.threads().loadMore()`; the runtime will pass it back as `params.after` on the next call.",
274
+ "Hydrate threads on mount. Each thread must include status and remoteId; title, externalId, and custom are optional. Return a `nextCursor` to enable `aui.threads.loadMore()`; the runtime will pass it back as `params.after` on the next call.",
275
275
  required: true,
276
276
  },
277
277
  {
@@ -291,7 +291,7 @@ A few invariants worth knowing when wiring a custom UI on top of `loadMore()`:
291
291
  name: "updateCustom",
292
292
  type: "(remoteId: string, custom: Record<string, unknown> | undefined) => Promise<void>",
293
293
  description:
294
- "Optional. Persist replacement custom metadata from `aui.threadListItem().updateCustom(custom)`.",
294
+ "Optional. Persist replacement custom metadata from `aui.threadListItem.updateCustom(custom)`.",
295
295
  },
296
296
  {
297
297
  name: "archive",
@@ -361,7 +361,7 @@ function ThreadListItemMeta() {
361
361
  }
362
362
  ```
363
363
 
364
- `custom` is preserved across `rename`, `archive`, `unarchive`, and `generateTitle`. To replace it from your UI, implement `RemoteThreadListAdapter.updateCustom` and call `aui.threadListItem().updateCustom(custom)`. The cloud adapter persists this through `cloud.threads.update(threadId, { metadata })`. If your adapter mutates thread metadata through a separate application path, return the updated values from `fetch()` or call `aui.threads().reload()` to re-run `list()`.
364
+ `custom` is preserved across `rename`, `archive`, `unarchive`, and `generateTitle`. To replace it from your UI, implement `RemoteThreadListAdapter.updateCustom` and call `aui.threadListItem.updateCustom(custom)`. The cloud adapter persists this through `cloud.threads.update(threadId, { metadata })`. If your adapter mutates thread metadata through a separate application path, return the updated values from `fetch()` or call `aui.threads.reload()` to re-run `list()`.
365
365
 
366
366
  ## ExternalStoreThreadListAdapter
367
367
 
@@ -536,8 +536,8 @@ function useResumeOnMount(threadId: string) {
536
536
  r.json(),
537
537
  );
538
538
  if (status.isRunning) {
539
- const parentId = aui.thread().getState().messages.at(-1)?.id ?? null;
540
- aui.thread().resumeRun({ parentId });
539
+ const parentId = aui.thread.getState().messages.at(-1)?.id ?? null;
540
+ aui.thread.resumeRun({ parentId });
541
541
  }
542
542
  })();
543
543
  }, [aui, threadId]);
@@ -19,14 +19,17 @@ If your backend exposes a richer state surface, consider [`AssistantTransport`](
19
19
 
20
20
  ## Wire protocols
21
21
 
22
- `useDataStreamRuntime` accepts two wire formats via its `protocol` option:
22
+ `useDataStreamRuntime` accepts two wire formats. When `protocol` is omitted, it
23
+ detects known Vercel AI response markers before falling back to UI message
24
+ stream for compatibility.
23
25
 
24
- | Protocol | Default | Matching backend |
26
+ | Protocol | Detection | Matching backend |
25
27
  | --- | --- | --- |
26
- | `"ui-message-stream"` | yes | AI SDK v6's `result.toUIMessageStreamResponse()` (SSE) |
27
- | `"data-stream"` | no | `createAssistantStreamResponse` from `assistant-stream`, or AI SDK v4's `toDataStreamResponse()` (Vercel data stream v1) |
28
+ | `"ui-message-stream"` | `x-vercel-ai-ui-message-stream: v1`, otherwise fallback | AI SDK v5+'s `result.toUIMessageStreamResponse()` (SSE) |
29
+ | `"data-stream"` | `x-vercel-ai-data-stream: v1` | `createAssistantStreamResponse` from `assistant-stream`, or AI SDK v4's `toDataStreamResponse()` (Vercel data stream v1) |
28
30
 
29
- Pairing the default decoder with a data stream backend throws `Stream ended abruptly without receiving [DONE] marker` at flush; pass `protocol: "data-stream"` to switch decoders.
31
+ Set `protocol` explicitly only for custom endpoints that do not preserve or
32
+ expose the response marker.
30
33
 
31
34
  ## Install
32
35
 
@@ -66,10 +69,7 @@ import { AssistantRuntimeProvider } from "@assistant-ui/react";
66
69
  import { Thread } from "@/components/assistant-ui/thread";
67
70
 
68
71
  export default function ChatPage() {
69
- const runtime = useDataStreamRuntime({
70
- api: "/api/chat",
71
- protocol: "data-stream",
72
- });
72
+ const runtime = useDataStreamRuntime({ api: "/api/chat" });
73
73
  return (
74
74
  <AssistantRuntimeProvider runtime={runtime}>
75
75
  <Thread />
@@ -90,10 +90,7 @@ import { Thread } from "@/components/assistant-ui/thread";
90
90
  const API_URL = process.env.EXPO_PUBLIC_API_URL ?? "http://localhost:3000";
91
91
 
92
92
  export default function ChatPage() {
93
- const runtime = useDataStreamRuntime({
94
- api: `${API_URL}/api/chat`,
95
- protocol: "data-stream",
96
- });
93
+ const runtime = useDataStreamRuntime({ api: `${API_URL}/api/chat` });
97
94
  return (
98
95
  <AssistantRuntimeProvider runtime={runtime}>
99
96
  <View style={{ flex: 1 }}>
@@ -116,7 +113,6 @@ import { Thread } from "./components/thread.js";
116
113
  export function App() {
117
114
  const runtime = useDataStreamRuntime({
118
115
  api: "http://localhost:3000/api/chat",
119
- protocol: "data-stream",
120
116
  });
121
117
  return (
122
118
  <AssistantRuntimeProvider runtime={runtime}>
@@ -167,7 +163,6 @@ The request body includes `messages`, `tools`, `system` (if configured), and `th
167
163
  ```tsx
168
164
  const runtime = useDataStreamRuntime({
169
165
  api: "/api/chat",
170
- protocol: "data-stream",
171
166
  headers: { Authorization: `Bearer ${token}`, "X-Custom-Header": "value" },
172
167
  credentials: "include",
173
168
  });
@@ -178,7 +173,6 @@ Evaluate per-request:
178
173
  ```tsx
179
174
  const runtime = useDataStreamRuntime({
180
175
  api: "/api/chat",
181
- protocol: "data-stream",
182
176
  headers: async () => ({
183
177
  Authorization: `Bearer ${await getAuthToken()}`,
184
178
  }),
@@ -195,7 +189,6 @@ const runtime = useDataStreamRuntime({
195
189
  ```tsx
196
190
  const runtime = useDataStreamRuntime({
197
191
  api: "/api/chat",
198
- protocol: "data-stream",
199
192
  onResponse: (response) => console.log("status:", response.status),
200
193
  onFinish: (message) => console.log("done:", message),
201
194
  onError: (error) => console.error(error),
@@ -230,7 +223,6 @@ const myTools = {
230
223
 
231
224
  const runtime = useDataStreamRuntime({
232
225
  api: "/api/chat",
233
- protocol: "data-stream",
234
226
  body: { tools: toToolsJSONSchema(myTools) },
235
227
  });
236
228
  ```
@@ -291,7 +283,6 @@ const runtime = useCloudRuntime({
291
283
  ```tsx
292
284
  const runtime = useDataStreamRuntime({
293
285
  api: "/api/chat",
294
- protocol: "data-stream",
295
286
  initialMessages: [
296
287
  { role: "user", content: [{ type: "text", text: "Hello" }] },
297
288
  { role: "assistant", content: [{ type: "text", text: "Hi!" }] },
@@ -741,7 +741,7 @@ useExternalStoreRuntime({
741
741
  name: "isSendDisabled",
742
742
  type: "boolean",
743
743
  description:
744
- "Blocks new-message sending while leaving the input usable. When true, the thread composer's canSend becomes false, the Send button is disabled, Enter and the steer hotkey are no-ops, and aui.composer().send() short-circuits. Edit composers (saving message edits) ignore this flag. Use this to gate sending on external React state (e.g. while tools or auth are still loading).",
744
+ "Blocks new-message sending while leaving the input usable. When true, the thread composer's canSend becomes false, the Send button is disabled, Enter and the steer hotkey are no-ops, and aui.composer.send() short-circuits. Edit composers (saving message edits) ignore this flag. Use this to gate sending on external React state (e.g. while tools or auth are still loading).",
745
745
  default: "false",
746
746
  },
747
747
  {
@@ -796,7 +796,7 @@ useExternalStoreRuntime({
796
796
  name: "onResume",
797
797
  type: "(config: ResumeRunConfig) => Promise<void>",
798
798
  description:
799
- "Handler for resuming an interrupted run (e.g. after a page reload mid-generation).",
799
+ "Handler for resuming an interrupted run (e.g. after a page reload mid-generation). For AI SDK reload-safe streaming, see the [Resumable Streams](/docs/guides/resumable-streams) guide.",
800
800
  },
801
801
  {
802
802
  name: "onResumeToolCall",
@@ -595,7 +595,7 @@ async function* createCustomStream(): AsyncGenerator<ChatModelRunResult> {
595
595
  };
596
596
  }
597
597
 
598
- aui.thread().resumeRun({
598
+ aui.thread.resumeRun({
599
599
  parentId: "message-id",
600
600
  stream: createCustomStream,
601
601
  });
@@ -617,8 +617,8 @@ function useStreamReconnect(threadId: string) {
617
617
  r.json(),
618
618
  );
619
619
  if (status.isRunning) {
620
- const parentId = aui.thread().getState().messages.at(-1)?.id ?? null;
621
- aui.thread().resumeRun({ parentId });
620
+ const parentId = aui.thread.getState().messages.at(-1)?.id ?? null;
621
+ aui.thread.resumeRun({ parentId });
622
622
  }
623
623
  })();
624
624
  }, [aui, threadId]);
@@ -698,6 +698,8 @@ const OpenAIAdapter: ChatModelAdapter = {
698
698
  };
699
699
  ```
700
700
 
701
+ For local development on a ChatGPT Plus or Pro plan, the same client can run without a real API key by pointing `baseURL` at a local OAuth proxy and passing a placeholder `apiKey`; see [ChatGPT Subscription](/docs/guides/chatgpt-subscription#openai-compatible-proxy).
702
+
701
703
  ### Custom REST API
702
704
 
703
705
  ```tsx
@@ -56,7 +56,7 @@ export default function Home() {
56
56
  - An Eve app mounted with `eve/next`.
57
57
  - A model credential for the model configured in `agent/agent.ts`.
58
58
 
59
- Eve's default gateway model ids route through the [Vercel AI Gateway](https://vercel.com/docs/ai-gateway). Use `AI_GATEWAY_API_KEY` or Vercel OIDC, or configure a direct provider `LanguageModel`.
59
+ Eve's default gateway model ids route through the [Vercel AI Gateway](https://vercel.com/docs/ai-gateway). Use `AI_GATEWAY_API_KEY` or Vercel OIDC, or configure a direct provider `LanguageModel`. For local development, a direct `LanguageModel` can also run on a ChatGPT Plus or Pro plan without any API key; see [ChatGPT Subscription](/docs/guides/chatgpt-subscription).
60
60
 
61
61
  ## Install
62
62
 
@@ -5,7 +5,7 @@ description: Use LangChain's useStream hook with a React chat UI through assista
5
5
 
6
6
  import { LangGraphIcon } from "@/components/icons/langgraph";
7
7
 
8
- `@assistant-ui/react-langchain` wraps [`useStream`](https://docs.langchain.com/oss/javascript/langgraph-sdk/react-stream) from `@langchain/react` and exposes it as an assistant-ui runtime. It targets the same backend as [`@assistant-ui/react-langgraph`](/docs/runtimes/langgraph/overview) (LangGraph Cloud) but at a higher level, delegating stream plumbing to the upstream hook.
8
+ `@assistant-ui/react-langchain` wraps [`useStream`](https://reference.langchain.com/javascript/langchain-react/use-stream) from `@langchain/react` and exposes it as an assistant-ui runtime. It targets the same backend as [`@assistant-ui/react-langgraph`](/docs/runtimes/langgraph/overview) (LangGraph Cloud) but at a higher level, delegating stream plumbing to the upstream hook.
9
9
 
10
10
  ## When to use it
11
11
 
@@ -32,7 +32,7 @@ Shared adapters (attachments, speech, feedback) work the same way described in [
32
32
 
33
33
  ## Requirements
34
34
 
35
- - A LangGraph Cloud API server (locally via [LangGraph Studio](https://github.com/langchain-ai/langgraph-studio) or hosted via [LangSmith](https://www.langchain.com/langsmith)).
35
+ - A LangGraph Cloud API server (locally via [LangGraph Studio](https://docs.langchain.com/langsmith/quick-start-studio) or hosted via [LangSmith](https://www.langchain.com/langsmith)).
36
36
  - The graph state must include a `messages` key with LangChain-alike messages, or pass a custom `messagesKey`.
37
37
 
38
38
  ## Quickstart
@@ -0,0 +1,181 @@
1
+ ---
2
+ title: Agent state
3
+ description: Read and optimistically update graph state with useLangGraphState and useLangGraphSetState in LangGraph.
4
+ ---
5
+
6
+ `useLangGraphState` mirrors the graph's values object that LangGraph streams to the client. It updates live while the agent runs (when the `values` stream mode is enabled). `useLangGraphSetState` lets you apply optimistic local updates that ride the next send.
7
+
8
+ ## Enable the `values` stream mode
9
+
10
+ <Callout type="warn">
11
+ The default stream setup does **not** include the `values` stream mode. Without it, `useLangGraphState` never receives live graph state.
12
+ </Callout>
13
+
14
+ When you call `client.runs.stream` yourself, request `values` alongside the modes you already use:
15
+
16
+ ```ts
17
+ client.runs.stream(threadId, assistantId, {
18
+ input,
19
+ streamMode: ["messages", "updates", "custom", "values"],
20
+ });
21
+ ```
22
+
23
+ If you use `unstable_createLangGraphStream`, its default stream modes do **not** include `values` either. Pass the option explicitly:
24
+
25
+ ```ts
26
+ const stream = unstable_createLangGraphStream({
27
+ client,
28
+ assistantId: ASSISTANT_ID,
29
+ streamMode: ["messages", "updates", "custom", "values"],
30
+ });
31
+ ```
32
+
33
+ ## Basic usage
34
+
35
+ ```tsx
36
+ import {
37
+ useLangGraphState,
38
+ useLangGraphSetState,
39
+ } from "@assistant-ui/react-langgraph";
40
+ import { useAuiState } from "@assistant-ui/react";
41
+
42
+ type GraphState = {
43
+ messages: unknown[];
44
+ filters: { region: string; maxResults: number };
45
+ };
46
+
47
+ const state = useLangGraphState<GraphState>();
48
+ // state: GraphState | undefined (latest agent state; updates live while the agent runs)
49
+
50
+ const setState = useLangGraphSetState<GraphState>();
51
+ // setState(next | (prev) => next) (optimistic local update; sent with the NEXT run)
52
+
53
+ const isRunning = useAuiState((s) => s.thread.isRunning);
54
+ // isRunning: boolean (whether the thread is currently running)
55
+ ```
56
+
57
+ ## Example
58
+
59
+ Render graph state beside the chat and stage an optimistic filter update:
60
+
61
+ ```tsx
62
+ "use client";
63
+
64
+ import {
65
+ useLangGraphState,
66
+ useLangGraphSetState,
67
+ } from "@assistant-ui/react-langgraph";
68
+ import { useAuiState } from "@assistant-ui/react";
69
+ import { Thread } from "@/components/assistant-ui/thread";
70
+
71
+ type GraphState = {
72
+ filters: { region: string; maxResults: number };
73
+ lastQuery?: string;
74
+ };
75
+
76
+ export function CatalogAssistant() {
77
+ const state = useLangGraphState<GraphState>();
78
+ const setState = useLangGraphSetState<GraphState>();
79
+ const isRunning = useAuiState((s) => s.thread.isRunning);
80
+
81
+ return (
82
+ <div className="flex h-full">
83
+ <aside className="w-72 border-r p-4">
84
+ <h2>Graph state</h2>
85
+ {state ? (
86
+ <ul>
87
+ <li>Region: {state.filters.region}</li>
88
+ <li>Max results: {state.filters.maxResults}</li>
89
+ {state.lastQuery && <li>Last query: {state.lastQuery}</li>}
90
+ </ul>
91
+ ) : (
92
+ <p>No graph state yet. Ensure streamMode includes "values".</p>
93
+ )}
94
+ <button
95
+ type="button"
96
+ disabled={isRunning}
97
+ onClick={() =>
98
+ setState((prev) => ({
99
+ ...prev,
100
+ filters: {
101
+ region: "eu",
102
+ maxResults: prev?.filters.maxResults ?? 10,
103
+ },
104
+ }))
105
+ }
106
+ >
107
+ Prefer EU region
108
+ </button>
109
+ {isRunning && <p>Agent is running…</p>}
110
+ </aside>
111
+ <main className="flex-1">
112
+ <Thread />
113
+ </main>
114
+ </div>
115
+ );
116
+ }
117
+ ```
118
+
119
+ The panel tracks the latest `values` events from the graph. The button updates the local overlay immediately; that update object is merged into the run `input` on the next send so LangGraph applies it through the graph's state reducers and input schema.
120
+
121
+ ## How state is synced
122
+
123
+ LangGraph state is the graph's values object. When `streamMode` includes `"values"`, each `values` event updates the client-side snapshot that `useLangGraphState` exposes.
124
+
125
+ The setter from `useLangGraphSetState` overlays that snapshot locally. On the next send, the runtime merges the update object into the run `input`, so LangGraph reduces it through the graph's state reducers and input schema.
126
+
127
+ If you supply a custom `stream` callback, the staged update is available as `config.state`. Forward it into your run input yourself:
128
+
129
+ ```ts
130
+ const runtime = useLangGraphRuntime({
131
+ stream: async (messages, { initialize, ...config }) => {
132
+ const { externalId } = await initialize();
133
+ if (!externalId) throw new Error("Thread not found");
134
+
135
+ const client = createClient();
136
+ return client.runs.stream(externalId, ASSISTANT_ID, {
137
+ input: {
138
+ ...(config.state ?? {}),
139
+ messages,
140
+ },
141
+ streamMode: ["messages", "updates", "custom", "values"],
142
+ });
143
+ },
144
+ });
145
+ ```
146
+
147
+ The built-in path that uses the package helpers performs this merge for you. Custom `stream` callbacks must do it explicitly.
148
+
149
+ ## Write-back timing
150
+
151
+ `useLangGraphSetState` is optimistic and local first. The value reaches the agent only when the next run starts. It is not a live channel into a run that is already in progress. Stage filters, preferences, or other graph fields that the next turn should apply through reducers; do not expect an in-flight run to observe mid-run setter calls.
152
+
153
+ ## Relationship to other state
154
+
155
+ Keep the three state layers distinct:
156
+
157
+ - **Your app state** stays yours (React state, URL, a store). assistant-ui does not own it.
158
+ - **`useAuiState`** reads assistant-ui's client state (messages, composer, thread status).
159
+ - **`useLangGraphState` / `useLangGraphSetState`** mirror state the **agent** (your LangGraph graph) owns, synced over the wire via `values` events.
160
+
161
+ Use `useLangGraphState` and `useLangGraphSetState` for fields that live in the graph state schema. Use `useAuiState` for UI that depends on the chat thread itself. Use your own state for everything else.
162
+
163
+ ## Next
164
+
165
+ <Cards>
166
+ <Card
167
+ title="Streaming"
168
+ description="Event handlers, message metadata, generative UI."
169
+ href="/docs/runtimes/langgraph/streaming"
170
+ />
171
+ <Card
172
+ title="Quickstart"
173
+ description="From-template and manual setup paths."
174
+ href="/docs/runtimes/langgraph/quickstart"
175
+ />
176
+ <Card
177
+ title="Generative UI"
178
+ description="Structured UI components emitted by your graph."
179
+ href="/docs/runtimes/langgraph/generative-ui"
180
+ />
181
+ </Cards>
@@ -13,7 +13,7 @@ description: Build a chat UI for LangGraph agents in React with assistant-ui —
13
13
 
14
14
  Pick the LangGraph runtime when:
15
15
 
16
- - You have (or want) a LangGraph Cloud server, locally via [LangGraph Studio](https://github.com/langchain-ai/langgraph-studio) or hosted via [LangSmith](https://www.langchain.com/langsmith).
16
+ - You have (or want) a LangGraph Cloud server, locally via [LangGraph Studio](https://docs.langchain.com/langsmith/quick-start-studio) or hosted via [LangSmith](https://www.langchain.com/langsmith).
17
17
  - Your graph state has a `messages` key with LangChain-alike messages.
18
18
  - You want generative UI (`ui_message`), per-message metadata, subgraph events, or checkpoint-based message editing.
19
19
 
@@ -9,7 +9,9 @@ OpenCode-specific React hooks for interacting with the running session. All hook
9
9
 
10
10
  ## Permissions
11
11
 
12
- OpenCode pauses tool execution to ask the user for permission (e.g. running shell commands, writing files). `useOpenCodePermissions` returns the pending permission requests and a reply function:
12
+ OpenCode pauses tool execution to ask the user for permission (e.g. running shell commands, writing files). Permissions linked to a tool call are projected into assistant-ui's standard tool approval contract, so the default `ToolFallback` renders working **Allow**, **Always allow**, and **Deny** actions without a custom permission component. The always option only appears when OpenCode offers patterns to persist.
13
+
14
+ `useOpenCodePermissions` remains available for custom tool renderers and permission requests that are not linked to a tool call. It returns the pending permission requests and a reply function:
13
15
 
14
16
  ```tsx
15
17
  import { useOpenCodePermissions } from "@assistant-ui/react-opencode";
@@ -0,0 +1,107 @@
1
+ ---
2
+ title: A2UI over AG-UI
3
+ description: Render A2UI surfaces from AG-UI activity snapshots as generative UI, using the community a2ui-surface convention.
4
+ platforms: ["react"]
5
+ ---
6
+
7
+ [A2UI](https://a2ui.org/) is a declarative generative UI protocol: the agent streams surface operations (create a surface, upsert components, update a data model) and the host renders them from a pre-approved component catalog, with no code over the wire. The AG-UI ecosystem carries A2UI as `ACTIVITY_SNAPSHOT` events with `activityType: "a2ui-surface"` and the operations under `content.a2ui_operations`; this is the convention emitted by [`@ag-ui/a2ui-middleware`](https://github.com/ag-ui-protocol/ag-ui/tree/main/middlewares/a2ui-middleware).
8
+
9
+ `useAgUiRuntime` consumes these snapshots natively. Each surface becomes one tool-call part with `toolCallId` `a2ui:<surfaceId>` and `toolName` `"present"`, whose args are the converted generative UI spec, so surfaces render through the same path as the [`present` frontend tool](/docs/tools/generative-ui). Snapshots with any other `activityType` are ignored, and the MCP Apps activity path is unaffected.
10
+
11
+ ## Wire contract
12
+
13
+ A backend paints or updates a surface by emitting an activity snapshot on the AG-UI stream:
14
+
15
+ ```json
16
+ {
17
+ "type": "ACTIVITY_SNAPSHOT",
18
+ "messageId": "a2ui-surface-call_1",
19
+ "activityType": "a2ui-surface",
20
+ "replace": true,
21
+ "content": {
22
+ "a2ui_operations": [
23
+ { "version": "v0.9", "createSurface": { "surfaceId": "s1" } },
24
+ {
25
+ "version": "v0.9",
26
+ "updateComponents": {
27
+ "surfaceId": "s1",
28
+ "components": [
29
+ { "id": "root", "component": "Card", "title": "Order", "children": ["total"] },
30
+ { "id": "total", "component": "Text", "text": { "path": "/total" } }
31
+ ]
32
+ }
33
+ },
34
+ {
35
+ "version": "v0.9",
36
+ "updateDataModel": { "surfaceId": "s1", "path": "/", "contents": { "total": "$42" } }
37
+ }
38
+ ]
39
+ }
40
+ }
41
+ ```
42
+
43
+ - A snapshot with `replace: true` (the schema default) rebuilds that `messageId`'s surface state from the operations it carries. `replace: false` means the snapshot is ignored when that `messageId` has already been seen, matching the AG-UI event spec. The middleware always emits `replace: true`.
44
+ - Surfaces are keyed by the `surfaceId` inside the operations, not by `messageId`. Multiple snapshots that share a `messageId` update their surfaces in place, and the synthesized part keeps its `toolCallId`, so the rendered surface updates without duplicating parts.
45
+ - Component trees use the A2UI adjacency-list model: nodes reference children by id, the root node has id `root`, and props of shape `{ "path": "/x/y" }` are JSON Pointer bindings resolved against the surface data model. Both v0.9 and v1.0 operation payloads are accepted, including the v1.0 inline `components` and `dataModel` on `createSurface`.
46
+ - Lifecycle snapshots that carry a `status` (such as `"building"`) but no `a2ui_operations` are tolerated and produce no part until operations arrive. A `deleteSurface` operation removes the surface's part.
47
+ - The `a2ui:` tool-call id prefix is reserved for synthesized surface parts: they are client-side render artifacts and are excluded from the history sent back to the agent, so genuine agent tool calls must not use ids starting with `a2ui:`.
48
+
49
+ ## Quick start
50
+
51
+ Register the `present` frontend tool from `@assistant-ui/react-generative-ui`; incoming surfaces then render with the default vocabulary:
52
+
53
+ ```tsx
54
+ import {
55
+ JSONGenerativeUI,
56
+ defaultGenerativeUILibrary,
57
+ } from "@assistant-ui/react-generative-ui";
58
+
59
+ const generative = new JSONGenerativeUI({
60
+ library: defaultGenerativeUILibrary,
61
+ });
62
+
63
+ const toolkit = {
64
+ present: generative.present({ display: "standalone" }),
65
+ };
66
+ ```
67
+
68
+ Pass the toolkit to your assistant as on the [Generative UI](/docs/tools/generative-ui) page; no other configuration is needed on `useAgUiRuntime`.
69
+
70
+ ## Actions
71
+
72
+ `Button` nodes dispatch `$action` objects with type `"a2ui:action"` through the action registry. Wire the registry to `useAgUiSendA2uiAction` inside a component; the hook returns a stable function, so the toolkit can be memoized on it:
73
+
74
+ ```tsx
75
+ import { useMemo } from "react";
76
+ import { useAgUiSendA2uiAction } from "@assistant-ui/react-ag-ui";
77
+ import {
78
+ JSONGenerativeUI,
79
+ createActionRegistry,
80
+ defaultGenerativeUILibrary,
81
+ } from "@assistant-ui/react-generative-ui";
82
+
83
+ function useA2uiToolkit() {
84
+ const sendA2uiAction = useAgUiSendA2uiAction();
85
+ return useMemo(() => {
86
+ const generative = new JSONGenerativeUI({
87
+ library: defaultGenerativeUILibrary,
88
+ actions: createActionRegistry({
89
+ "a2ui:action": ({ payload }) => sendA2uiAction(payload),
90
+ }),
91
+ });
92
+ return { present: generative.present({ display: "standalone" }) };
93
+ }, [sendA2uiAction]);
94
+ }
95
+ ```
96
+
97
+ Sending an action triggers a run with no new user message, and the agent receives it as `forwardedProps.a2uiAction.userAction`, the convention `@ag-ui/a2ui-middleware` consumes. The action rides exactly one run; the `type` field is stripped and a `timestamp` is added when absent.
98
+
99
+ The middleware turns the action into a synthetic `log_a2ui_event` tool-call pair that is never declared in `input.tools`; a backend that validates tool calls against the declared tool list will reject it.
100
+
101
+ ## Component mapping
102
+
103
+ The converter maps the A2UI basic catalog onto the default generative UI vocabulary: `Text` becomes `Markdown` (`h1` to `h6` variants become `Header`, `caption` becomes `Caption`), `Column` becomes `Col`, `TextField` becomes `Input` (the control name is derived from the last segment of its binding path), `CheckBox` becomes `Checkbox`, and `Image`, `Row`, `Card`, `Divider`, `Button` map one to one. A node with template children expands into a `ListView` over the bound list. `Button` actions become `$action` objects with type `"a2ui:action"` carrying the action name, `surfaceId`, and `sourceComponentId`, which is how your action registry receives them. Unknown components and operations are skipped with a debug warning, and conversion is bounded (depth 32, 100 template items, 5000 nodes) so a malformed stream cannot hang the client.
104
+
105
+ ## Limitations
106
+
107
+ - The upstream middleware currently emits v0.9 operation payloads; the renderer accepts v0.9 and v1.0 shapes.
@@ -13,6 +13,8 @@ For authoring tools, see [Defining Tools](/docs/tools/defining-tools). For MCP s
13
13
  `@assistant-ui/react-ai-sdk` posts `{ messages, system, tools }` to your route. `tools` is the map of **frontend** tools the client serialized for this request (the model needs their schemas to call them, even though they run in the browser):
14
14
 
15
15
  ```ts
16
+ import type { FrontendTools } from "@assistant-ui/react-ai-sdk";
17
+
16
18
  const {
17
19
  messages,
18
20
  system,
@@ -20,7 +22,7 @@ const {
20
22
  }: {
21
23
  messages: UIMessage[];
22
24
  system?: string;
23
- tools?: Record<string, { description?: string; parameters: JSONSchema7 }>;
25
+ tools?: FrontendTools;
24
26
  } = await req.json();
25
27
  ```
26
28
 
@@ -136,9 +138,10 @@ To let the model see a frontend tool's result and continue, configure the runtim
136
138
  import { lastAssistantMessageIsCompleteWithToolCalls } from "ai";
137
139
 
138
140
  const runtime = useChatRuntime({
139
- api: "/api/chat",
140
141
  sendAutomaticallyWhen: lastAssistantMessageIsCompleteWithToolCalls,
141
142
  });
142
143
  ```
143
144
 
144
- For the full AI SDK v6 backend setup — history persistence, reasoning, server-side approvals — see the [AI SDK v6 guide](/docs/runtimes/ai-sdk/v6).
145
+ `useChatRuntime()` targets `/api/chat` by default. To point at a different endpoint or customize requests, see [Custom transport](/docs/runtimes/ai-sdk/v7#custom-transport).
146
+
147
+ For the full AI SDK v7 backend setup — history persistence, reasoning, server-side approvals — see the [AI SDK v7 guide](/docs/runtimes/ai-sdk/v7).