@assistant-ui/mcp-docs-server 0.2.3 → 0.3.1

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 (452) hide show
  1. package/README.md +16 -43
  2. package/dist/index.d.ts +2 -3
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +3 -91
  5. package/dist/index.js.map +1 -1
  6. package/dist/proxy.d.ts +9 -0
  7. package/dist/proxy.d.ts.map +1 -0
  8. package/dist/proxy.js +91 -0
  9. package/dist/proxy.js.map +1 -0
  10. package/package.json +8 -12
  11. package/src/index.ts +3 -115
  12. package/src/proxy.test.ts +385 -0
  13. package/src/proxy.ts +126 -0
  14. package/.docs/organized/code-examples/waterfall.md +0 -807
  15. package/.docs/organized/code-examples/with-a2a.md +0 -675
  16. package/.docs/organized/code-examples/with-ag-ui.md +0 -553
  17. package/.docs/organized/code-examples/with-ai-sdk-v7.md +0 -466
  18. package/.docs/organized/code-examples/with-artifacts.md +0 -815
  19. package/.docs/organized/code-examples/with-assistant-transport.md +0 -576
  20. package/.docs/organized/code-examples/with-browser-extension.md +0 -371
  21. package/.docs/organized/code-examples/with-chain-of-thought.md +0 -962
  22. package/.docs/organized/code-examples/with-cloud-standalone.md +0 -681
  23. package/.docs/organized/code-examples/with-cloud.md +0 -439
  24. package/.docs/organized/code-examples/with-custom-thread-list.md +0 -569
  25. package/.docs/organized/code-examples/with-elevenlabs-conversational.md +0 -527
  26. package/.docs/organized/code-examples/with-elevenlabs-scribe.md +0 -697
  27. package/.docs/organized/code-examples/with-eve.md +0 -405
  28. package/.docs/organized/code-examples/with-expo.md +0 -2246
  29. package/.docs/organized/code-examples/with-external-store.md +0 -421
  30. package/.docs/organized/code-examples/with-ffmpeg.md +0 -829
  31. package/.docs/organized/code-examples/with-generative-ui.md +0 -1810
  32. package/.docs/organized/code-examples/with-google-adk.md +0 -368
  33. package/.docs/organized/code-examples/with-heat-graph.md +0 -304
  34. package/.docs/organized/code-examples/with-image-generation.md +0 -463
  35. package/.docs/organized/code-examples/with-interactables.md +0 -733
  36. package/.docs/organized/code-examples/with-langchain.md +0 -446
  37. package/.docs/organized/code-examples/with-langgraph.md +0 -855
  38. package/.docs/organized/code-examples/with-livekit.md +0 -643
  39. package/.docs/organized/code-examples/with-mcp.md +0 -782
  40. package/.docs/organized/code-examples/with-nuxt.md +0 -2428
  41. package/.docs/organized/code-examples/with-opencode.md +0 -1974
  42. package/.docs/organized/code-examples/with-openui.md +0 -450
  43. package/.docs/organized/code-examples/with-pi.md +0 -2084
  44. package/.docs/organized/code-examples/with-react-hook-form.md +0 -727
  45. package/.docs/organized/code-examples/with-react-ink-web.md +0 -740
  46. package/.docs/organized/code-examples/with-react-ink.md +0 -473
  47. package/.docs/organized/code-examples/with-react-router.md +0 -936
  48. package/.docs/organized/code-examples/with-resumable-stream.md +0 -668
  49. package/.docs/organized/code-examples/with-store.md +0 -679
  50. package/.docs/organized/code-examples/with-svelte.md +0 -415
  51. package/.docs/organized/code-examples/with-sveltekit.md +0 -1061
  52. package/.docs/organized/code-examples/with-tanstack.md +0 -812
  53. package/.docs/organized/code-examples/with-tap-runtime.md +0 -813
  54. package/.docs/organized/code-examples/with-virtualized-thread.md +0 -681
  55. package/.docs/organized/code-examples/with-vue.md +0 -408
  56. package/.docs/raw/docs/(getting-started)/architecture.mdx +0 -146
  57. package/.docs/raw/docs/(getting-started)/base-ui.mdx +0 -39
  58. package/.docs/raw/docs/(getting-started)/cli.mdx +0 -542
  59. package/.docs/raw/docs/(getting-started)/devtools.mdx +0 -79
  60. package/.docs/raw/docs/(getting-started)/index.mdx +0 -23
  61. package/.docs/raw/docs/(getting-started)/installation.mdx +0 -483
  62. package/.docs/raw/docs/(getting-started)/llm.mdx +0 -211
  63. package/.docs/raw/docs/(getting-started)/rtl.mdx +0 -78
  64. package/.docs/raw/docs/(reference)/api-reference/adapters/attachments.mdx +0 -36
  65. package/.docs/raw/docs/(reference)/api-reference/adapters/feedback.mdx +0 -20
  66. package/.docs/raw/docs/(reference)/api-reference/adapters/index.mdx +0 -34
  67. package/.docs/raw/docs/(reference)/api-reference/adapters/model.mdx +0 -44
  68. package/.docs/raw/docs/(reference)/api-reference/adapters/persistence.mdx +0 -59
  69. package/.docs/raw/docs/(reference)/api-reference/adapters/runtime.mdx +0 -20
  70. package/.docs/raw/docs/(reference)/api-reference/adapters/suggestions.mdx +0 -26
  71. package/.docs/raw/docs/(reference)/api-reference/context-providers/assistant-runtime-provider.mdx +0 -84
  72. package/.docs/raw/docs/(reference)/api-reference/context-providers/index.mdx +0 -22
  73. package/.docs/raw/docs/(reference)/api-reference/context-providers/scoped-providers.mdx +0 -64
  74. package/.docs/raw/docs/(reference)/api-reference/external-store/index.mdx +0 -22
  75. package/.docs/raw/docs/(reference)/api-reference/external-store/message-conversion.mdx +0 -57
  76. package/.docs/raw/docs/(reference)/api-reference/external-store/runtime.mdx +0 -49
  77. package/.docs/raw/docs/(reference)/api-reference/generative-ui/a2ui.mdx +0 -40
  78. package/.docs/raw/docs/(reference)/api-reference/generative-ui/actions.mdx +0 -56
  79. package/.docs/raw/docs/(reference)/api-reference/generative-ui/components.mdx +0 -86
  80. package/.docs/raw/docs/(reference)/api-reference/generative-ui/index.mdx +0 -39
  81. package/.docs/raw/docs/(reference)/api-reference/generative-ui/json-generative-ui.mdx +0 -42
  82. package/.docs/raw/docs/(reference)/api-reference/generative-ui/rendering.mdx +0 -84
  83. package/.docs/raw/docs/(reference)/api-reference/generative-ui/slack.mdx +0 -85
  84. package/.docs/raw/docs/(reference)/api-reference/generative-ui/spec.mdx +0 -45
  85. package/.docs/raw/docs/(reference)/api-reference/generative-ui/teams.mdx +0 -93
  86. package/.docs/raw/docs/(reference)/api-reference/generative-ui/tokens.mdx +0 -62
  87. package/.docs/raw/docs/(reference)/api-reference/hooks/composer-triggers.mdx +0 -129
  88. package/.docs/raw/docs/(reference)/api-reference/hooks/index.mdx +0 -31
  89. package/.docs/raw/docs/(reference)/api-reference/hooks/model-context.mdx +0 -34
  90. package/.docs/raw/docs/(reference)/api-reference/hooks/primitives.mdx +0 -350
  91. package/.docs/raw/docs/(reference)/api-reference/hooks/runtimes.mdx +0 -28
  92. package/.docs/raw/docs/(reference)/api-reference/hooks/state.mdx +0 -97
  93. package/.docs/raw/docs/(reference)/api-reference/integrations/ai-sdk.mdx +0 -200
  94. package/.docs/raw/docs/(reference)/api-reference/integrations/cloud-ai-sdk.mdx +0 -24
  95. package/.docs/raw/docs/(reference)/api-reference/integrations/eve.mdx +0 -118
  96. package/.docs/raw/docs/(reference)/api-reference/integrations/index.mdx +0 -28
  97. package/.docs/raw/docs/(reference)/api-reference/integrations/react-data-stream.mdx +0 -37
  98. package/.docs/raw/docs/(reference)/api-reference/model-context/context.mdx +0 -50
  99. package/.docs/raw/docs/(reference)/api-reference/model-context/index.mdx +0 -22
  100. package/.docs/raw/docs/(reference)/api-reference/model-context/registry.mdx +0 -20
  101. package/.docs/raw/docs/(reference)/api-reference/overview.mdx +0 -566
  102. package/.docs/raw/docs/(reference)/api-reference/primitives/action-bar-more.mdx +0 -152
  103. package/.docs/raw/docs/(reference)/api-reference/primitives/action-bar.mdx +0 -239
  104. package/.docs/raw/docs/(reference)/api-reference/primitives/assistant-if.mdx +0 -208
  105. package/.docs/raw/docs/(reference)/api-reference/primitives/assistant-modal.mdx +0 -108
  106. package/.docs/raw/docs/(reference)/api-reference/primitives/attachment.mdx +0 -99
  107. package/.docs/raw/docs/(reference)/api-reference/primitives/branch-picker.mdx +0 -145
  108. package/.docs/raw/docs/(reference)/api-reference/primitives/chain-of-thought.mdx +0 -82
  109. package/.docs/raw/docs/(reference)/api-reference/primitives/composer.mdx +0 -637
  110. package/.docs/raw/docs/(reference)/api-reference/primitives/composition.mdx +0 -22
  111. package/.docs/raw/docs/(reference)/api-reference/primitives/error.mdx +0 -65
  112. package/.docs/raw/docs/(reference)/api-reference/primitives/index.mdx +0 -73
  113. package/.docs/raw/docs/(reference)/api-reference/primitives/message-part.mdx +0 -117
  114. package/.docs/raw/docs/(reference)/api-reference/primitives/message.mdx +0 -339
  115. package/.docs/raw/docs/(reference)/api-reference/primitives/queue-item.mdx +0 -77
  116. package/.docs/raw/docs/(reference)/api-reference/primitives/selection-toolbar.mdx +0 -88
  117. package/.docs/raw/docs/(reference)/api-reference/primitives/suggestion.mdx +0 -81
  118. package/.docs/raw/docs/(reference)/api-reference/primitives/thread-list-item-more.mdx +0 -129
  119. package/.docs/raw/docs/(reference)/api-reference/primitives/thread-list-item.mdx +0 -138
  120. package/.docs/raw/docs/(reference)/api-reference/primitives/thread-list.mdx +0 -122
  121. package/.docs/raw/docs/(reference)/api-reference/primitives/thread.mdx +0 -289
  122. package/.docs/raw/docs/(reference)/api-reference/runtimes/assistant-runtime.mdx +0 -20
  123. package/.docs/raw/docs/(reference)/api-reference/runtimes/attachment-runtime.mdx +0 -24
  124. package/.docs/raw/docs/(reference)/api-reference/runtimes/composer-runtime.mdx +0 -32
  125. package/.docs/raw/docs/(reference)/api-reference/runtimes/index.mdx +0 -43
  126. package/.docs/raw/docs/(reference)/api-reference/runtimes/message-part-runtime.mdx +0 -43
  127. package/.docs/raw/docs/(reference)/api-reference/runtimes/message-runtime.mdx +0 -24
  128. package/.docs/raw/docs/(reference)/api-reference/runtimes/queue-state.mdx +0 -20
  129. package/.docs/raw/docs/(reference)/api-reference/runtimes/thread-list-item-runtime.mdx +0 -24
  130. package/.docs/raw/docs/(reference)/api-reference/runtimes/thread-list-runtime.mdx +0 -24
  131. package/.docs/raw/docs/(reference)/api-reference/runtimes/thread-runtime.mdx +0 -36
  132. package/.docs/raw/docs/(reference)/api-reference/tools/component-tools.mdx +0 -85
  133. package/.docs/raw/docs/(reference)/api-reference/tools/index.mdx +0 -45
  134. package/.docs/raw/docs/(reference)/api-reference/tools/interactables-legacy.mdx +0 -55
  135. package/.docs/raw/docs/(reference)/api-reference/tools/interactables.mdx +0 -151
  136. package/.docs/raw/docs/(reference)/api-reference/tools/rendering.mdx +0 -106
  137. package/.docs/raw/docs/(reference)/api-reference/tools/status.mdx +0 -62
  138. package/.docs/raw/docs/(reference)/api-reference/tools/toolkits.mdx +0 -178
  139. package/.docs/raw/docs/(reference)/api-reference/transport/assistant-transport.mdx +0 -48
  140. package/.docs/raw/docs/(reference)/api-reference/transport/frame.mdx +0 -62
  141. package/.docs/raw/docs/(reference)/api-reference/transport/index.mdx +0 -22
  142. package/.docs/raw/docs/(reference)/api-reference/utilities/index.mdx +0 -19
  143. package/.docs/raw/docs/(reference)/api-reference/utilities/miscellaneous.mdx +0 -152
  144. package/.docs/raw/docs/(reference)/api-reference/voice/index.mdx +0 -22
  145. package/.docs/raw/docs/(reference)/api-reference/voice/session.mdx +0 -54
  146. package/.docs/raw/docs/(reference)/api-reference/voice/speech-dictation.mdx +0 -36
  147. package/.docs/raw/docs/cloud/ai-sdk-assistant-ui.mdx +0 -527
  148. package/.docs/raw/docs/cloud/ai-sdk.mdx +0 -604
  149. package/.docs/raw/docs/cloud/authorization.mdx +0 -252
  150. package/.docs/raw/docs/cloud/index.mdx +0 -46
  151. package/.docs/raw/docs/cloud/langgraph.mdx +0 -554
  152. package/.docs/raw/docs/copilots/assistant-frame.mdx +0 -411
  153. package/.docs/raw/docs/copilots/make-assistant-visible.mdx +0 -82
  154. package/.docs/raw/docs/copilots/model-context.mdx +0 -156
  155. package/.docs/raw/docs/copilots/motivation.mdx +0 -202
  156. package/.docs/raw/docs/copilots/use-assistant-instructions.mdx +0 -64
  157. package/.docs/raw/docs/guides/attachments.mdx +0 -619
  158. package/.docs/raw/docs/guides/branching.mdx +0 -76
  159. package/.docs/raw/docs/guides/chain-of-thought.mdx +0 -164
  160. package/.docs/raw/docs/guides/chatgpt-subscription.mdx +0 -108
  161. package/.docs/raw/docs/guides/context-api.mdx +0 -619
  162. package/.docs/raw/docs/guides/dictation.mdx +0 -298
  163. package/.docs/raw/docs/guides/editing.mdx +0 -102
  164. package/.docs/raw/docs/guides/electron.mdx +0 -369
  165. package/.docs/raw/docs/guides/headless-composer-input.mdx +0 -113
  166. package/.docs/raw/docs/guides/image-generation.mdx +0 -74
  167. package/.docs/raw/docs/guides/index.mdx +0 -117
  168. package/.docs/raw/docs/guides/input-history.mdx +0 -55
  169. package/.docs/raw/docs/guides/latex.mdx +0 -158
  170. package/.docs/raw/docs/guides/mentions.mdx +0 -575
  171. package/.docs/raw/docs/guides/message-timing.mdx +0 -215
  172. package/.docs/raw/docs/guides/quoting.mdx +0 -169
  173. package/.docs/raw/docs/guides/resumable-stream-deployment.mdx +0 -212
  174. package/.docs/raw/docs/guides/resumable-stream-stores.mdx +0 -152
  175. package/.docs/raw/docs/guides/resumable-streams.mdx +0 -292
  176. package/.docs/raw/docs/guides/slash-commands.mdx +0 -358
  177. package/.docs/raw/docs/guides/speech.mdx +0 -172
  178. package/.docs/raw/docs/guides/suggestions.mdx +0 -384
  179. package/.docs/raw/docs/guides/virtualization.mdx +0 -133
  180. package/.docs/raw/docs/guides/voice.mdx +0 -310
  181. package/.docs/raw/docs/ink/adapters.mdx +0 -99
  182. package/.docs/raw/docs/ink/custom-backend.mdx +0 -254
  183. package/.docs/raw/docs/ink/hooks.mdx +0 -469
  184. package/.docs/raw/docs/ink/index.mdx +0 -237
  185. package/.docs/raw/docs/ink/migration.mdx +0 -138
  186. package/.docs/raw/docs/ink/primitives.mdx +0 -1259
  187. package/.docs/raw/docs/integrations/attachments/custom-adapter.mdx +0 -528
  188. package/.docs/raw/docs/integrations/auth/better-auth.mdx +0 -188
  189. package/.docs/raw/docs/integrations/auth/clerk.mdx +0 -166
  190. package/.docs/raw/docs/integrations/auth/next-auth.mdx +0 -191
  191. package/.docs/raw/docs/integrations/frameworks/ai-sdk.mdx +0 -88
  192. package/.docs/raw/docs/integrations/frameworks/cloudflare-agents.mdx +0 -283
  193. package/.docs/raw/docs/integrations/frameworks/mastra/full-stack.mdx +0 -188
  194. package/.docs/raw/docs/integrations/frameworks/mastra/overview.mdx +0 -57
  195. package/.docs/raw/docs/integrations/frameworks/mastra/separate-server.mdx +0 -201
  196. package/.docs/raw/docs/integrations/gateways/index.mdx +0 -162
  197. package/.docs/raw/docs/integrations/index.mdx +0 -178
  198. package/.docs/raw/docs/integrations/observability/helicone.mdx +0 -129
  199. package/.docs/raw/docs/integrations/observability/langfuse.mdx +0 -161
  200. package/.docs/raw/docs/integrations/observability/langsmith.mdx +0 -151
  201. package/.docs/raw/docs/integrations/persistence/custom-adapter.mdx +0 -773
  202. package/.docs/raw/docs/migrations/deprecation-policy.mdx +0 -42
  203. package/.docs/raw/docs/migrations/index.mdx +0 -50
  204. package/.docs/raw/docs/migrations/react-compatibility.mdx +0 -74
  205. package/.docs/raw/docs/migrations/react-langgraph-v0-7.mdx +0 -328
  206. package/.docs/raw/docs/migrations/toolkit-tools.mdx +0 -236
  207. package/.docs/raw/docs/migrations/v0-11.mdx +0 -172
  208. package/.docs/raw/docs/migrations/v0-12.mdx +0 -302
  209. package/.docs/raw/docs/migrations/v0-14.mdx +0 -297
  210. package/.docs/raw/docs/migrations/v0-15.mdx +0 -270
  211. package/.docs/raw/docs/primitives/action-bar.mdx +0 -352
  212. package/.docs/raw/docs/primitives/assistant-modal.mdx +0 -216
  213. package/.docs/raw/docs/primitives/attachment.mdx +0 -217
  214. package/.docs/raw/docs/primitives/branch-picker.mdx +0 -222
  215. package/.docs/raw/docs/primitives/chain-of-thought.mdx +0 -315
  216. package/.docs/raw/docs/primitives/composer.mdx +0 -659
  217. package/.docs/raw/docs/primitives/error.mdx +0 -142
  218. package/.docs/raw/docs/primitives/index.mdx +0 -99
  219. package/.docs/raw/docs/primitives/message.mdx +0 -618
  220. package/.docs/raw/docs/primitives/selection-toolbar.mdx +0 -191
  221. package/.docs/raw/docs/primitives/suggestion.mdx +0 -254
  222. package/.docs/raw/docs/primitives/thread-list.mdx +0 -467
  223. package/.docs/raw/docs/primitives/thread.mdx +0 -509
  224. package/.docs/raw/docs/react-native/adapters.mdx +0 -94
  225. package/.docs/raw/docs/react-native/custom-backend.mdx +0 -207
  226. package/.docs/raw/docs/react-native/hooks.mdx +0 -339
  227. package/.docs/raw/docs/react-native/index.mdx +0 -289
  228. package/.docs/raw/docs/react-native/migration.mdx +0 -142
  229. package/.docs/raw/docs/react-native/primitives.mdx +0 -976
  230. package/.docs/raw/docs/runtimes/a2a/client-and-hooks.mdx +0 -399
  231. package/.docs/raw/docs/runtimes/a2a/overview.mdx +0 -60
  232. package/.docs/raw/docs/runtimes/a2a/quickstart.mdx +0 -216
  233. package/.docs/raw/docs/runtimes/ag-ui/agent-state.mdx +0 -124
  234. package/.docs/raw/docs/runtimes/ag-ui/overview.mdx +0 -70
  235. package/.docs/raw/docs/runtimes/ag-ui/quickstart.mdx +0 -243
  236. package/.docs/raw/docs/runtimes/ag-ui/runtime-options.mdx +0 -267
  237. package/.docs/raw/docs/runtimes/ai-sdk/overview.mdx +0 -61
  238. package/.docs/raw/docs/runtimes/ai-sdk/v4-legacy.mdx +0 -122
  239. package/.docs/raw/docs/runtimes/ai-sdk/v5-legacy.mdx +0 -135
  240. package/.docs/raw/docs/runtimes/ai-sdk/v6-legacy.mdx +0 -636
  241. package/.docs/raw/docs/runtimes/ai-sdk/v7.mdx +0 -731
  242. package/.docs/raw/docs/runtimes/claude-managed-agents.mdx +0 -118
  243. package/.docs/raw/docs/runtimes/concepts/adapters.mdx +0 -265
  244. package/.docs/raw/docs/runtimes/concepts/architecture.mdx +0 -167
  245. package/.docs/raw/docs/runtimes/concepts/stability.mdx +0 -68
  246. package/.docs/raw/docs/runtimes/concepts/threads.mdx +0 -513
  247. package/.docs/raw/docs/runtimes/custom/assistant-transport.mdx +0 -763
  248. package/.docs/raw/docs/runtimes/custom/data-stream.mdx +0 -339
  249. package/.docs/raw/docs/runtimes/custom/external-store.mdx +0 -957
  250. package/.docs/raw/docs/runtimes/custom/local-runtime.mdx +0 -902
  251. package/.docs/raw/docs/runtimes/custom/overview.mdx +0 -71
  252. package/.docs/raw/docs/runtimes/eve/overview.mdx +0 -151
  253. package/.docs/raw/docs/runtimes/eve/quickstart.mdx +0 -192
  254. package/.docs/raw/docs/runtimes/google-adk/api.mdx +0 -256
  255. package/.docs/raw/docs/runtimes/google-adk/hooks.mdx +0 -784
  256. package/.docs/raw/docs/runtimes/google-adk/overview.mdx +0 -69
  257. package/.docs/raw/docs/runtimes/google-adk/quickstart.mdx +0 -229
  258. package/.docs/raw/docs/runtimes/langchain.mdx +0 -901
  259. package/.docs/raw/docs/runtimes/langgraph/agent-state.mdx +0 -181
  260. package/.docs/raw/docs/runtimes/langgraph/generative-ui.mdx +0 -305
  261. package/.docs/raw/docs/runtimes/langgraph/interrupts.mdx +0 -104
  262. package/.docs/raw/docs/runtimes/langgraph/overview.mdx +0 -84
  263. package/.docs/raw/docs/runtimes/langgraph/quickstart.mdx +0 -496
  264. package/.docs/raw/docs/runtimes/langgraph/streaming.mdx +0 -144
  265. package/.docs/raw/docs/runtimes/langgraph/threads.mdx +0 -113
  266. package/.docs/raw/docs/runtimes/langgraph/tutorial/introduction.mdx +0 -29
  267. package/.docs/raw/docs/runtimes/langgraph/tutorial/part-1.mdx +0 -93
  268. package/.docs/raw/docs/runtimes/langgraph/tutorial/part-2.mdx +0 -347
  269. package/.docs/raw/docs/runtimes/langgraph/tutorial/part-3.mdx +0 -393
  270. package/.docs/raw/docs/runtimes/opencode/hooks.mdx +0 -194
  271. package/.docs/raw/docs/runtimes/opencode/overview.mdx +0 -58
  272. package/.docs/raw/docs/runtimes/opencode/quickstart.mdx +0 -119
  273. package/.docs/raw/docs/runtimes/pick-a-runtime.mdx +0 -133
  274. package/.docs/raw/docs/tools/a2ui.mdx +0 -107
  275. package/.docs/raw/docs/tools/backend.mdx +0 -147
  276. package/.docs/raw/docs/tools/defining-tools.mdx +0 -574
  277. package/.docs/raw/docs/tools/dynamic-tools.mdx +0 -112
  278. package/.docs/raw/docs/tools/generative-ui-primitive.mdx +0 -180
  279. package/.docs/raw/docs/tools/generative-ui-slack.mdx +0 -167
  280. package/.docs/raw/docs/tools/generative-ui-teams.mdx +0 -160
  281. package/.docs/raw/docs/tools/generative-ui.mdx +0 -322
  282. package/.docs/raw/docs/tools/index.mdx +0 -72
  283. package/.docs/raw/docs/tools/interactables.mdx +0 -1084
  284. package/.docs/raw/docs/tools/mcp-apps.mdx +0 -363
  285. package/.docs/raw/docs/tools/mcp.mdx +0 -466
  286. package/.docs/raw/docs/tools/multi-agent.mdx +0 -237
  287. package/.docs/raw/docs/tools/openui.mdx +0 -175
  288. package/.docs/raw/docs/tools/tool-ui.mdx +0 -1020
  289. package/.docs/raw/docs/tools/user-managed-mcp.mdx +0 -428
  290. package/.docs/raw/docs/ui/assistant-modal.mdx +0 -166
  291. package/.docs/raw/docs/ui/assistant-sidebar.mdx +0 -89
  292. package/.docs/raw/docs/ui/attachment.mdx +0 -279
  293. package/.docs/raw/docs/ui/composer-trigger-popover.mdx +0 -247
  294. package/.docs/raw/docs/ui/context-display.mdx +0 -148
  295. package/.docs/raw/docs/ui/directive-text.mdx +0 -114
  296. package/.docs/raw/docs/ui/file.mdx +0 -159
  297. package/.docs/raw/docs/ui/follow-up-suggestions.mdx +0 -82
  298. package/.docs/raw/docs/ui/image.mdx +0 -102
  299. package/.docs/raw/docs/ui/markdown.mdx +0 -103
  300. package/.docs/raw/docs/ui/mcp-config.mdx +0 -107
  301. package/.docs/raw/docs/ui/mermaid.mdx +0 -92
  302. package/.docs/raw/docs/ui/message-timing.mdx +0 -93
  303. package/.docs/raw/docs/ui/model-selector.mdx +0 -455
  304. package/.docs/raw/docs/ui/part-grouping.mdx +0 -614
  305. package/.docs/raw/docs/ui/quote.mdx +0 -211
  306. package/.docs/raw/docs/ui/reasoning.mdx +0 -214
  307. package/.docs/raw/docs/ui/scrollbar.mdx +0 -77
  308. package/.docs/raw/docs/ui/sources.mdx +0 -186
  309. package/.docs/raw/docs/ui/streamdown.mdx +0 -392
  310. package/.docs/raw/docs/ui/syntax-highlighting.mdx +0 -205
  311. package/.docs/raw/docs/ui/thread-list.mdx +0 -411
  312. package/.docs/raw/docs/ui/thread.mdx +0 -559
  313. package/.docs/raw/docs/ui/tool-fallback.mdx +0 -141
  314. package/.docs/raw/docs/ui/tool-group.mdx +0 -240
  315. package/.docs/raw/docs/ui/voice.mdx +0 -173
  316. package/.docs/raw/docs/utilities/heat-graph.mdx +0 -237
  317. package/.docs/raw/docs/utilities/react-o11y.mdx +0 -362
  318. package/.docs/raw/docs/utilities/tw-shimmer.mdx +0 -212
  319. package/dist/constants.d.ts +0 -14
  320. package/dist/constants.d.ts.map +0 -1
  321. package/dist/constants.js +0 -19
  322. package/dist/constants.js.map +0 -1
  323. package/dist/prepare-docs/code-examples.d.ts +0 -5
  324. package/dist/prepare-docs/code-examples.d.ts.map +0 -1
  325. package/dist/prepare-docs/code-examples.js +0 -117
  326. package/dist/prepare-docs/code-examples.js.map +0 -1
  327. package/dist/prepare-docs/copy-raw.d.ts +0 -5
  328. package/dist/prepare-docs/copy-raw.d.ts.map +0 -1
  329. package/dist/prepare-docs/copy-raw.js +0 -49
  330. package/dist/prepare-docs/copy-raw.js.map +0 -1
  331. package/dist/prepare-docs/prepare.d.ts +0 -1
  332. package/dist/prepare-docs/prepare.js +0 -22
  333. package/dist/prepare-docs/prepare.js.map +0 -1
  334. package/dist/prompts/xulux-playground.d.ts +0 -12
  335. package/dist/prompts/xulux-playground.d.ts.map +0 -1
  336. package/dist/prompts/xulux-playground.js +0 -33
  337. package/dist/prompts/xulux-playground.js.map +0 -1
  338. package/dist/tools/docs.d.ts +0 -22
  339. package/dist/tools/docs.d.ts.map +0 -1
  340. package/dist/tools/docs.js +0 -177
  341. package/dist/tools/docs.js.map +0 -1
  342. package/dist/tools/examples.d.ts +0 -24
  343. package/dist/tools/examples.d.ts.map +0 -1
  344. package/dist/tools/examples.js +0 -87
  345. package/dist/tools/examples.js.map +0 -1
  346. package/dist/tools/resources.d.ts +0 -6
  347. package/dist/tools/resources.d.ts.map +0 -1
  348. package/dist/tools/resources.js +0 -75
  349. package/dist/tools/resources.js.map +0 -1
  350. package/dist/tools/search.d.ts +0 -24
  351. package/dist/tools/search.d.ts.map +0 -1
  352. package/dist/tools/search.js +0 -39
  353. package/dist/tools/search.js.map +0 -1
  354. package/dist/tools/tests/mcp-test-client.d.ts +0 -15
  355. package/dist/tools/tests/mcp-test-client.d.ts.map +0 -1
  356. package/dist/tools/tests/mcp-test-client.js +0 -68
  357. package/dist/tools/tests/mcp-test-client.js.map +0 -1
  358. package/dist/tools/tests/test-setup.d.ts +0 -7
  359. package/dist/tools/tests/test-setup.d.ts.map +0 -1
  360. package/dist/tools/tests/test-setup.js +0 -36
  361. package/dist/tools/tests/test-setup.js.map +0 -1
  362. package/dist/tools/xulux-templates.d.ts +0 -58
  363. package/dist/tools/xulux-templates.d.ts.map +0 -1
  364. package/dist/tools/xulux-templates.js +0 -84
  365. package/dist/tools/xulux-templates.js.map +0 -1
  366. package/dist/utils/cache.d.ts +0 -5
  367. package/dist/utils/cache.d.ts.map +0 -1
  368. package/dist/utils/cache.js +0 -18
  369. package/dist/utils/cache.js.map +0 -1
  370. package/dist/utils/logger.d.ts +0 -10
  371. package/dist/utils/logger.d.ts.map +0 -1
  372. package/dist/utils/logger.js +0 -20
  373. package/dist/utils/logger.js.map +0 -1
  374. package/dist/utils/mcp-format.d.ts +0 -11
  375. package/dist/utils/mcp-format.d.ts.map +0 -1
  376. package/dist/utils/mcp-format.js +0 -14
  377. package/dist/utils/mcp-format.js.map +0 -1
  378. package/dist/utils/mdx.d.ts +0 -12
  379. package/dist/utils/mdx.d.ts.map +0 -1
  380. package/dist/utils/mdx.js +0 -45
  381. package/dist/utils/mdx.js.map +0 -1
  382. package/dist/utils/paths.d.ts +0 -12
  383. package/dist/utils/paths.d.ts.map +0 -1
  384. package/dist/utils/paths.js +0 -93
  385. package/dist/utils/paths.js.map +0 -1
  386. package/dist/utils/search.d.ts +0 -10
  387. package/dist/utils/search.d.ts.map +0 -1
  388. package/dist/utils/search.js +0 -97
  389. package/dist/utils/search.js.map +0 -1
  390. package/dist/utils/security.d.ts +0 -5
  391. package/dist/utils/security.d.ts.map +0 -1
  392. package/dist/utils/security.js +0 -22
  393. package/dist/utils/security.js.map +0 -1
  394. package/dist/xulux/catalog-client.d.ts +0 -14
  395. package/dist/xulux/catalog-client.d.ts.map +0 -1
  396. package/dist/xulux/catalog-client.js +0 -112
  397. package/dist/xulux/catalog-client.js.map +0 -1
  398. package/dist/xulux/fallback-catalog.d.ts +0 -7
  399. package/dist/xulux/fallback-catalog.d.ts.map +0 -1
  400. package/dist/xulux/fallback-catalog.js +0 -47
  401. package/dist/xulux/fallback-catalog.js.map +0 -1
  402. package/dist/xulux/fetch-sandbox.d.ts +0 -5
  403. package/dist/xulux/fetch-sandbox.d.ts.map +0 -1
  404. package/dist/xulux/fetch-sandbox.js +0 -40
  405. package/dist/xulux/fetch-sandbox.js.map +0 -1
  406. package/dist/xulux/template-service.d.ts +0 -84
  407. package/dist/xulux/template-service.d.ts.map +0 -1
  408. package/dist/xulux/template-service.js +0 -223
  409. package/dist/xulux/template-service.js.map +0 -1
  410. package/dist/xulux/types.d.ts +0 -55
  411. package/dist/xulux/types.d.ts.map +0 -1
  412. package/dist/xulux/types.js +0 -6
  413. package/dist/xulux/types.js.map +0 -1
  414. package/src/constants.ts +0 -24
  415. package/src/prepare-docs/code-examples.ts +0 -158
  416. package/src/prepare-docs/copy-raw.ts +0 -50
  417. package/src/prepare-docs/prepare.ts +0 -24
  418. package/src/prompts/xulux-playground.ts +0 -36
  419. package/src/tools/docs.ts +0 -255
  420. package/src/tools/examples.ts +0 -114
  421. package/src/tools/resources.ts +0 -111
  422. package/src/tools/search.ts +0 -46
  423. package/src/tools/tests/completions.test.ts +0 -60
  424. package/src/tools/tests/directory-size-cap.test.ts +0 -50
  425. package/src/tools/tests/docs.test.ts +0 -147
  426. package/src/tools/tests/examples.test.ts +0 -94
  427. package/src/tools/tests/integration.test.ts +0 -45
  428. package/src/tools/tests/json-parsing.test.ts +0 -23
  429. package/src/tools/tests/listings-cache.test.ts +0 -19
  430. package/src/tools/tests/mcp-protocol.test.ts +0 -216
  431. package/src/tools/tests/mcp-test-client.ts +0 -111
  432. package/src/tools/tests/path-traversal.test.ts +0 -84
  433. package/src/tools/tests/resources.test.ts +0 -133
  434. package/src/tools/tests/search.test.ts +0 -37
  435. package/src/tools/tests/test-setup.ts +0 -50
  436. package/src/tools/tests/xulux-templates.test.ts +0 -325
  437. package/src/tools/xulux-templates.ts +0 -141
  438. package/src/utils/cache.ts +0 -20
  439. package/src/utils/logger.ts +0 -20
  440. package/src/utils/mcp-format.ts +0 -14
  441. package/src/utils/mdx.ts +0 -59
  442. package/src/utils/paths.ts +0 -139
  443. package/src/utils/search.ts +0 -131
  444. package/src/utils/security.ts +0 -52
  445. package/src/utils/tests/cache.test.ts +0 -51
  446. package/src/utils/tests/mcp-format.test.ts +0 -22
  447. package/src/utils/tests/security.test.ts +0 -119
  448. package/src/xulux/catalog-client.ts +0 -150
  449. package/src/xulux/fallback-catalog.ts +0 -63
  450. package/src/xulux/fetch-sandbox.ts +0 -56
  451. package/src/xulux/template-service.ts +0 -406
  452. package/src/xulux/types.ts +0 -64
@@ -1,1084 +0,0 @@
1
- ---
2
- title: Interactable Tool UIs
3
- description: Build stateful components and tool UIs that both the user and the model can read and edit. Render them beside the thread, or inside messages as versioned, editable surfaces like notepads and artifacts.
4
- platforms: ["react"]
5
- ---
6
-
7
- import { InteractableSample } from "@/components/pages/docs/samples/interactable";
8
-
9
- Interactables allow both agents and users to read and edit tool UIs and components. They can be in-thread tool UIs like an email composer, or app-scoped components, like artifacts, task boards, or settings panels.
10
-
11
- <InteractableSample />
12
-
13
- ## Overview
14
-
15
- ### Types of Interactables
16
-
17
- - **App-scoped:** a component you mount yourself with `unstable_useInteractable`, anywhere in your app (a sidebar, a panel, wherever). The model automatically gets an `update_{name}` tool to read and edit it, and its state can persist across threads.
18
- - **Thread-scoped:** an interactable tool UI the model can call in-thread. You define it with `unstable_interactableTool` inside `defineToolkit`, and it renders inline when called.
19
-
20
- ### Features
21
-
22
- - **Shared, editable state**: the user (via React) and the model (via the auto-generated `update_{name}` tool) both write to the same state, and each sees the other's edits.
23
- - **Streaming and partial updates**: updates are streamed to the interactable, and the model only needs to update the fields it wants to change.
24
- - **Versioning and history**: each user edit and model `update_*` is recorded as a version you can display, list, and `restore()` ([Versions](#versions))
25
- - **Persistent**: state outlives the tool call and the turn, and can survive a reload (thread-scoped via thread history; app-scoped with a persistence adapter).
26
- - **Auto-generated update tools:** based on the interactable's state schema, an `update_{name}` tool is generated for the model to update and edit it.
27
-
28
- ### Use Cases
29
-
30
- - Settings panels or dashboards the agent can edit and interact with
31
- - Collaborative task lists, sticky notes, document editors
32
- - Making artifacts editable and versioned
33
- - Anything else, **any React component can be an interactable!**
34
-
35
- ## Quick Start
36
-
37
- <Steps>
38
- <Step>
39
-
40
- ### Register the interactables scope
41
-
42
- ```tsx
43
- import {
44
- AuiConfig,
45
- unstable_Interactables,
46
- AssistantRuntimeProvider,
47
- Tools,
48
- } from "@assistant-ui/react";
49
- import { useChatRuntime } from "@assistant-ui/ai-sdk";
50
-
51
- function MyRuntimeProvider({ children }: { children: React.ReactNode }) {
52
- const runtime = useChatRuntime();
53
- const config = AuiConfig({
54
- unstable_interactables: unstable_Interactables(), // [!code ++]
55
- });
56
-
57
- return (
58
- <AssistantRuntimeProvider
59
- runtime={runtime}
60
- config={config}
61
- >
62
- {children}
63
- </AssistantRuntimeProvider>
64
- );
65
- }
66
- ```
67
-
68
- <Callout type="idea">
69
- The deprecated [`interactables:
70
- Interactables()`](/docs/api-reference/tools/interactables-legacy) scope and the
71
- new `unstable_interactables: unstable_Interactables()` scope are mutually
72
- exclusive. Mount only one interactables API in a single provider config.
73
- </Callout>
74
-
75
- This scope is needed for both kinds of interactable. Thread-scoped interactables also live in a toolkit, which you register with `Tools` (shown below).
76
-
77
- </Step>
78
- <Step>
79
-
80
- ### Define an interactable
81
-
82
- Pick the path that matches who creates it.
83
-
84
- <Tabs items={["App-scoped", "Thread-scoped"]}>
85
- <Tab>
86
-
87
- A component you mount yourself. Define it with `unstable_useInteractable` where you render it (a sidebar, a panel, wherever).
88
-
89
- ```tsx
90
- import { unstable_useInteractable } from "@assistant-ui/react";
91
- import { z } from "zod";
92
-
93
- const taskBoardSchema = z.object({
94
- tasks: z.array(
95
- z.object({ id: z.string(), title: z.string(), done: z.boolean() }),
96
- ),
97
- });
98
-
99
- function TaskBoard() {
100
- const [state, { setState }] = unstable_useInteractable("taskBoard", {
101
- description:
102
- "A task board panel that lists tasks. Use update_taskBoard with tasks.add/update/remove/clear to manage tasks. New tasks need a title and done=false.",
103
- stateSchema: taskBoardSchema,
104
- initialState: { tasks: [] },
105
- });
106
-
107
- return (
108
- <ul>
109
- {state.tasks.map((task) => (
110
- <li key={task.id}>
111
- <input
112
- type="checkbox"
113
- checked={task.done}
114
- onChange={() =>
115
- setState((prev) => ({
116
- tasks: prev.tasks.map((t) =>
117
- t.id === task.id ? { ...t, done: !t.done } : t,
118
- ),
119
- }))
120
- }
121
- />
122
- {task.title}
123
- </li>
124
- ))}
125
- </ul>
126
- );
127
- }
128
- ```
129
-
130
- That's all you need, `update_taskBoard` is generated automatically from your `stateSchema` once the `TaskBoard` component is mounted in your app.
131
-
132
- <Callout type="tip">
133
- App-scoped state is shared across every thread and can be persisted by
134
- defining a [persistence adapter](#persistence).
135
- </Callout>
136
-
137
- </Tab>
138
- <Tab>
139
-
140
- An interactable tool UI the model can call in-thread. Define it with `unstable_interactableTool` inside `defineToolkit`; it renders inline when the model calls it, and its `update_{name}` tool is generated automatically from your `stateSchema` once the tool is called by the model.
141
-
142
- ```tsx
143
- "use generative";
144
-
145
- import { defineToolkit, unstable_interactableTool } from "@assistant-ui/react";
146
- import { z } from "zod";
147
-
148
- const notepadSchema = z.object({
149
- title: z.string(),
150
- content: z.string(),
151
- });
152
-
153
- const toolkit = defineToolkit({
154
- notepad: unstable_interactableTool({
155
- description: "A notepad with drafted text the user can read and edit.",
156
- stateSchema: notepadSchema,
157
- render: ({ state, setState, version, streaming }) => (
158
- <Notepad
159
- value={state}
160
- onChange={setState}
161
- busy={streaming}
162
- readOnly={version ? !version.isLatest : false}
163
- />
164
- ),
165
- }),
166
- });
167
- ```
168
-
169
- Register the toolkit alongside the `unstable_Interactables` scope:
170
-
171
- ```tsx
172
- AuiConfig({
173
- unstable_interactables: unstable_Interactables(),
174
- tools: Tools({ toolkit }), // [!code ++]
175
- });
176
- ```
177
-
178
- <Callout type="info">
179
- Thread-scoped state rides the thread's history, so it survives reloads with
180
- nothing extra to persist. `render` is run when the interactable is created,
181
- and whenever it's updated (via `update_{name}`).
182
- </Callout>
183
-
184
- </Tab>
185
- </Tabs>
186
-
187
- </Step>
188
- <Step>
189
-
190
- ### Surface state to the model in your route
191
-
192
- <Tabs items={["AI-SDK", "Other Backends"]}>
193
- <Tab>
194
- Each user message carries its state snapshots in its metadata, but the AI SDK's `convertToModelMessages` ignores metadata. Use `unstable_injectInteractableContext` to pass interactable state to the model:
195
-
196
- ```ts title="app/api/chat/route.ts"
197
- import { openai } from "@ai-sdk/openai";
198
- import { convertToModelMessages, streamText } from "ai";
199
- import { unstable_injectInteractableContext as injectInteractableContext } from "@assistant-ui/ai-sdk";
200
-
201
- export async function POST(req: Request) {
202
- const { messages } = await req.json();
203
-
204
- const result = streamText({
205
- model: openai("gpt-5.6-luna"),
206
- messages: await convertToModelMessages(injectInteractableContext(messages)), // [!code ++]
207
- });
208
-
209
- return result.toUIMessageStreamResponse();
210
- }
211
- ```
212
-
213
- <Callout type="info">
214
- The format of the snapshot sent to the model can be customized, see [State
215
- snapshots](#state-snapshots) for more details.
216
- </Callout>
217
-
218
- </Tab>
219
- <Tab>
220
-
221
- Each user message carries its state snapshots at `metadata.custom.interactables`. For other backends (LangGraph, Mastra, a custom runtime), build the equivalent injection with the two helpers exported from `@assistant-ui/react`:
222
-
223
- ```ts
224
- import {
225
- unstable_getInteractableSnapshots, // (message) => snapshot entries | undefined
226
- unstable_formatInteractableSnapshot, // (entry) => the model-facing formatting of the snapshot injection
227
- } from "@assistant-ui/react";
228
-
229
- for (const message of messages) {
230
- if (message.role !== "user") continue;
231
- const items = unstable_getInteractableSnapshots(message);
232
- if (!items?.length) continue;
233
- const text = items.map(unstable_formatInteractableSnapshot).join("\n");
234
- // prepend `text` to the message content in whatever shape your backend expects
235
- }
236
- ```
237
-
238
- <Callout type="info">
239
- For more details, and how to customize the snapshot format for other backends,
240
- see [State snapshots](#customizing-the-format).
241
- </Callout>
242
-
243
- </Tab>
244
- </Tabs>
245
-
246
- </Step>
247
- </Steps>
248
-
249
- ## State snapshots
250
-
251
- Outgoing user messages can carry the interactable's state to the model as a snapshot, stamped when the state has changed since the model last saw it. When only some fields change, a partial snapshot is created containing a shallow diff of only the changed fields and the id.
252
-
253
- Default full snapshot formatting:
254
-
255
- ```ts
256
- `[Current state of "note" (id: "n1"): {"title":"Q3 launch","body":"Ship the beta by Friday."}]`;
257
- ```
258
-
259
- Default partial snapshot formatting:
260
-
261
- ```ts
262
- `[State of "note" (id: "n1") changed — updated fields: {"title":"Q4 launch"}; fields not listed are unchanged]`;
263
- ```
264
-
265
- ### Customizing the format
266
-
267
- A formatter receives one snapshot `entry` and returns the line the model sees:
268
-
269
- - `name` and `id` identify the instance. Keep the `id` visible so the model knows what the state belongs to.
270
- - `state` is the snapshot payload.
271
- - `partial` is `true` when `state` carries a shallow diff of only the fields that changed since the model's last known state. Handle it.
272
-
273
- Write a custom formatter:
274
-
275
- ```ts
276
- import { type Unstable_InteractableSnapshotEntry } from "@assistant-ui/react";
277
-
278
- const formatSnapshot = (entry: Unstable_InteractableSnapshotEntry) =>
279
- entry.partial
280
- ? `State of "${entry.name}" (id: "${entry.id}") has been updated, the following fields have changed: ${JSON.stringify(entry.state)}`
281
- : `Current state of "${entry.name}" (id: "${entry.id}"): ${JSON.stringify(entry.state)}`;
282
- ```
283
-
284
- <Callout type="info">
285
- When customizing format, remember to:
286
- - Handle `partial: true` entries, whose `state` carries a shallow diff of only the fields that changed.
287
- - Keep each instance's `id` visible so the model knows what the state belongs to.
288
- </Callout>
289
-
290
- Then wire it to your backend:
291
-
292
- - **AI SDK:** pass it as the second argument, `unstable_injectInteractableContext(messages, formatSnapshot)`.
293
- - **Other backends:** use it in place of `unstable_formatInteractableSnapshot` in your helper (see [Step 3](#surface-state-to-the-model-in-your-route)'s "Other Backends" tab).
294
-
295
- ```ts title="app/api/chat/route.ts"
296
- messages: await convertToModelMessages(
297
- unstable_injectInteractableContext(messages, formatSnapshot),
298
- ),
299
- ```
300
-
301
- ## Artifacts
302
-
303
- An artifact combines two pieces that point at the same interactable:
304
-
305
- - a thread tool (`unstable_interactableTool`) the model calls to create the artifact inline (a button or small preview in the message), and
306
- - a panel you mount with `unstable_useInteractable` that opens that same instance at full size.
307
-
308
- Both use the same `id`, so they are one interactable, not two: the model creates it in the conversation, and the panel is just a larger view of the very same state. Because the thread holds the creating call, the artifact is **thread-scoped**: it persists with the thread's history (no [persistence adapter](#persistence) needed), and each message's trigger can open that message's version (see [Versions](#versions)).
309
-
310
- ```
311
- in the thread (model-created) your layout (mounted once)
312
- ────────────────────────────── ──────────────────────────────
313
- unstable_interactableTool("document") ArtifactPanel()
314
- render: a button / inline preview unstable_useInteractable("document", { id })
315
- onClick → openArtifact(id) ────┐ → live, editable, full height
316
- │ │
317
- └───── same id ────┘
318
- one thread-scoped instance
319
- ```
320
-
321
- ```tsx
322
- const toolkit = defineToolkit({
323
- document: unstable_interactableTool({
324
- description: "A document the user can open and edit.",
325
- stateSchema: documentSchema,
326
- render: ({ state, version, id }) => (
327
- <ArtifactButton
328
- title={(version?.state ?? state).title}
329
- onClick={() => openArtifact(id)} // your own state: which artifact is open
330
- />
331
- ),
332
- }),
333
- });
334
-
335
- function ArtifactPanel({ id }: { id: string }) {
336
- const [state, { setState }] = unstable_useInteractable("document", {
337
- id,
338
- description: "A document the user can open and edit.",
339
- stateSchema: documentSchema,
340
- initialState: emptyDocument, // fallback only; existing state comes from the thread
341
- });
342
- const versions = unstable_useInteractableVersions<Document>(id, "document");
343
-
344
- return (
345
- <aside>
346
- <VersionMenu>
347
- {versions.map((v, i) => (
348
- <DropdownItem key={i} onSelect={v.restore}>
349
- v{i + 1}: {v.origin === "user-edit" ? "you" : "assistant"}
350
- </DropdownItem>
351
- ))}
352
- </VersionMenu>
353
- <Editor value={state} onChange={setState} />
354
- </aside>
355
- );
356
- }
357
- ```
358
-
359
- You don't mount anything per artifact: the model renders the inline part on its own, and `openArtifact(id)` is your own state setter for which artifact the panel currently shows. Outside message parts the hook always returns the live state (`version` is `undefined`), so the panel is plainly editable. The panel and the inline tool UIs register the same `id`, so they share one instance; registration is reference-counted, and the instance stays alive until the last one unmounts.
360
-
361
- <Callout type="warn">
362
- Keep one mount of the artifact on screen (hidden is fine) whenever its instance
363
- should stay available. If the panel is closed and every inline button has
364
- scrolled out of a virtualized thread, the instance and its `update_{name}` tool
365
- unregister and the tool list churns. The state itself is safe, since it rides
366
- thread history.
367
- </Callout>
368
-
369
- ## Companion tools
370
-
371
- The generated `update_{name}` tool covers everything that lives in the state: editing fields, and adding, updating, removing, or clearing items in a list. Reach for a separate frontend tool only for what state can't express: a side effect like sending, exporting, or saving.
372
-
373
- Take an email composer the user and assistant co-edit. `update_email` keeps the draft in sync from both sides, but actually **sending** it is a side effect that no amount of state editing can perform. That is what a companion tool is for: `send_email` acts on the current draft and fires it.
374
-
375
- How you wire one depends on what its executor touches.
376
-
377
- **Closes over React state** (here, the live draft): declare the contract with `stubTool()` in your `"use generative"` toolkit, and supply the executor with `useAuiToolOverrides` in the component that owns the state. See [Dynamic Tools](/docs/tools/dynamic-tools).
378
-
379
- ```tsx title="app/email-toolkit.tsx"
380
- "use generative";
381
-
382
- import { defineToolkit, stubTool } from "@assistant-ui/react";
383
- import { z } from "zod";
384
-
385
- export default defineToolkit({
386
- send_email: {
387
- description:
388
- "Send the email currently shown in the composer. Call this only once the draft is ready.",
389
- parameters: z.object({}),
390
- execute: stubTool(),
391
- renderText: { running: "Sending...", complete: "Email sent" },
392
- },
393
- });
394
- ```
395
-
396
- ```tsx title="app/EmailComposer.tsx"
397
- import {
398
- unstable_useInteractable,
399
- useAuiToolOverrides,
400
- } from "@assistant-ui/react";
401
-
402
- function EmailComposer() {
403
- const [draft, { setState }] = unstable_useInteractable("email", {
404
- description: "An email draft the user and assistant can read and edit.",
405
- stateSchema: emailSchema,
406
- initialState: { to: "", subject: "", body: "" },
407
- });
408
-
409
- return (
410
- <>
411
- <SendEmailTool draft={draft} />
412
- {/* inputs bound to draft + setState */}
413
- </>
414
- );
415
- }
416
-
417
- // A null-returning child supplies the executor, closing over the live draft.
418
- function SendEmailTool({ draft }: { draft: Email }) {
419
- useAuiToolOverrides({
420
- send_email: {
421
- execute: async () => {
422
- await fetch("/api/send-email", {
423
- method: "POST",
424
- body: JSON.stringify(draft),
425
- });
426
- return { success: true };
427
- },
428
- },
429
- });
430
- return null;
431
- }
432
- ```
433
-
434
- The split is the whole point: `update_email` edits the draft, `send_email` does the thing the draft can't describe. The executor reads the live `draft`, so it always sends what is currently on screen.
435
-
436
- **Self-contained** (needs nothing from React, only its args and browser APIs): put a real `execute` with an inner `"use client"` directly in the toolkit. A `copy_share_link({ id })` that builds a URL and writes it to the clipboard is a good fit. See [Defining Tools](/docs/tools/defining-tools#frontend-tools).
437
-
438
- ## Custom Update Rendering
439
-
440
- `render` draws the create call; `updateRender` draws each `update_{name}` call. `unstable_interactableTool` reuses your one `render` for both, so edits look like the creation. Supply your own `updateRender` to render edits differently.
441
-
442
- <Callout type="info">
443
- To vary by this message's version, or render older messages differently from
444
- the newest, you don't need `updateRender`. The render already receives
445
- `version`; see [Versions](#versions).
446
- </Callout>
447
-
448
- ### Render edits differently from the create
449
-
450
- A thread tool locks the create and edit renders together. Drop to `unstable_useInteractable` to split them, here showing the full notepad on the create call and a one-line summary on every edit:
451
-
452
- ```tsx
453
- // Hoisted so its identity is stable; an inline updateRender re-registers the
454
- // tool UI on every render.
455
- const EditSummaryRender: ToolCallMessagePartComponent = ({ args }) => (
456
- <EditSummary changed={args} />
457
- );
458
-
459
- const NotepadToolUI: ToolCallMessagePartComponent<NotepadArgs> = ({
460
- toolCallId,
461
- args,
462
- result,
463
- }) => {
464
- if (!result) return <NotepadDraft args={args} />;
465
- return <Notepad id={toolCallId} initial={args} />;
466
- };
467
-
468
- function Notepad({ id, initial }: { id: string; initial: NotepadArgs }) {
469
- const [state, { setState }] = unstable_useInteractable("notepad", {
470
- id,
471
- description: "A notepad the user can read and edit.",
472
- stateSchema: notepadSchema,
473
- initialState: initial,
474
- updateRender: EditSummaryRender,
475
- });
476
- return <NotepadEditor value={state} onChange={setState} />;
477
- }
478
-
479
- // NotepadToolUI is the create call's render; updateRender handles the edits.
480
- const toolkit = defineToolkit({
481
- notepad: {
482
- type: "frontend",
483
- description: "A notepad the user can read and edit.",
484
- parameters: notepadSchema,
485
- display: "standalone",
486
- execute: async () => ({ success: true as const }),
487
- render: NotepadToolUI,
488
- },
489
- });
490
- ```
491
-
492
- Pass the create call's `toolCallId` as the `id`: that one convention ties both renders to a single instance and restores its state from thread history after a reload.
493
-
494
- ### Mark edits on an app-scoped surface
495
-
496
- An app-scoped interactable lives in your layout (a sidebar, a panel), so it has no inline presence in the thread. Pass `updateRender` and each `update_{name}` call gains one: an inline marker of what the model just did, while the live component keeps updating in place.
497
-
498
- ```tsx
499
- const EditMarkerRender: ToolCallMessagePartComponent = ({ args }) => (
500
- <EditMarker changed={args} />
501
- );
502
-
503
- function DocumentPanel() {
504
- const [state, { setState }] = unstable_useInteractable("document", {
505
- description: "A document the user can read and edit.",
506
- stateSchema: documentSchema,
507
- initialState: emptyDocument,
508
- updateRender: EditMarkerRender,
509
- });
510
- return <Editor value={state} onChange={setState} />;
511
- }
512
- ```
513
-
514
- ## Versions
515
-
516
- A thread is an append-only log, so an instance accumulates **versions**: each user edit, each `update_*` call, and (thread-scoped only) the creating call. For thread-scoped interactables these are computed from the thread record, so history survives reloads with nothing extra to persist. There are two ways to reach them.
517
-
518
- **This message's version.** Inside a thread-scoped `render`, `state` / `setState` are the **live** instance (there's exactly one, shared by every message), while `version` is **this message's** snapshot: `{ state, isLatest, restore }`. `version.state` is the interactable as it was at that point in the conversation; `restore()` sets the live state back to it.
519
-
520
- Those three fields are all you need, and two independent choices decide how history behaves:
521
-
522
- - **Editable or read-only?** Render the live `state` / `setState` to let any message edit the shared instance, or `version.state` read-only to freeze it.
523
- - **Restorable?** Offer `version.restore()` to roll an old version back to live, or leave it out.
524
-
525
- | You want | Render |
526
- | -------------------- | --------------------------------------- |
527
- | Frozen history | `version.state` read-only on old |
528
- | Live-editable | `state` / `setState` everywhere |
529
- | Read-only + rollback | `version.state` read-only plus `restore` |
530
-
531
- ```tsx
532
- // Read-only history with rollback:
533
- // old messages are frozen but can roll their version back to live
534
- render: ({ state, setState, version }) =>
535
- version && !version.isLatest ? (
536
- <Notepad value={version.state} readOnly onRestore={version.restore} />
537
- ) : (
538
- <Notepad value={state} onChange={setState} />
539
- );
540
- ```
541
-
542
- ```tsx
543
- // Live-editable:
544
- // every message edits the shared instance; restore reverts to this point
545
- render: ({ state, setState, version }) => (
546
- <Notepad value={state} onChange={setState} onRestore={version?.restore} />
547
- );
548
- ```
549
-
550
- **Every version at once.** `unstable_useInteractableVersions(id, name)` returns them oldest-first, each with the full `state` and a `restore()`. Use it for a history dropdown; it works for both app- and thread-scoped interactables:
551
-
552
- ```tsx
553
- function VersionDropdown({ id, name }: { id: string; name: string }) {
554
- const versions = unstable_useInteractableVersions(id, name);
555
- if (versions.length < 2) return null;
556
-
557
- return (
558
- <select onChange={(e) => versions[+e.target.value]!.restore()}>
559
- {versions.map((v, i) => (
560
- <option key={i} value={i}>
561
- v{i + 1}: {v.origin === "user-edit" ? "you" : "assistant"}
562
- </option>
563
- ))}
564
- </select>
565
- );
566
- }
567
- ```
568
-
569
- **Thread-scoped:**
570
-
571
- ```tsx
572
- const Notepad = ({
573
- id,
574
- state,
575
- setState,
576
- }: Unstable_InteractableToolRenderProps<NotepadArgs>) => (
577
- <div>
578
- <VersionDropdown id={id} name="notepad" />
579
- {/* ...editor... */}
580
- </div>
581
- );
582
- ```
583
-
584
- **App-scoped:**
585
-
586
- ```tsx
587
- const [state, { id }] = unstable_useInteractable("taskBoard", config);
588
- return <VersionDropdown id={id} name="taskBoard" />;
589
- ```
590
-
591
- An app-scoped item's history covers the current conversation, not its full cross-thread lifetime.
592
-
593
- ## Persistence
594
-
595
- By default, app-scoped interactable state is in-memory and lost on page refresh. You can add persistence by passing an adapter to `unstable_Interactables`:
596
-
597
- ```tsx
598
- import {
599
- AuiConfig,
600
- AuiProvider,
601
- useAui,
602
- unstable_Interactables,
603
- } from "@assistant-ui/react";
604
-
605
- // Module-level (or memoized) so the adapter identity is stable across renders.
606
- const persistenceAdapter = {
607
- load: () => {
608
- const saved = localStorage.getItem("interactables");
609
- return saved ? JSON.parse(saved) : undefined;
610
- },
611
- save: (state) => {
612
- localStorage.setItem("interactables", JSON.stringify(state));
613
- },
614
- };
615
-
616
- function MyRuntimeProvider({ children }) {
617
- const aui = useAui();
618
- const config = AuiConfig({
619
- unstable_interactables: unstable_Interactables({
620
- persistence: persistenceAdapter,
621
- }),
622
- });
623
- return (
624
- <AuiProvider extends={aui} config={config}>
625
- {children}
626
- </AuiProvider>
627
- );
628
- }
629
- ```
630
-
631
- `load` is called when the adapter is attached and may be async. Loaded state seeds interactables as they register; a local edit made while a slow `load` is still in flight wins over the loaded value. Thread-scoped interactables are not touched by the adapter; they persist via thread history.
632
-
633
- For dynamic setups (an adapter that depends on auth), call `aui.unstable_interactables.setPersistenceAdapter(adapter)` imperatively instead. Replacing or removing an adapter flushes queued changes through the outgoing adapter before the new persistence context is attached.
634
-
635
- ### Sync Status
636
-
637
- When a persistence adapter is set, interactable hooks expose sync metadata:
638
-
639
- ```tsx
640
- const [state, { setState, isPending, error, flush }] =
641
- unstable_useInteractableState<TState>(id);
642
-
643
- // isPending: true while a save is in-flight
644
- // error: the error from the last failed save, if any
645
- // flush(): force an immediate save (useful before navigation)
646
- ```
647
-
648
- State changes are automatically debounced (500ms) before saving. When the owning component unmounts, any pending save is flushed immediately.
649
-
650
- ### Export / Import
651
-
652
- For custom persistence strategies, use `exportState` and `importState` directly:
653
-
654
- ```tsx
655
- const snapshot = aui.interactables.exportState();
656
- // => { "note-1": { name: "note", state: { title: "Hello" } }, ... }
657
-
658
- aui.interactables.importState(snapshot);
659
- // Imported state is picked up when components next register
660
- ```
661
-
662
- ### Schema Evolution
663
-
664
- <Callout type="warn">
665
- If you change a Zod schema after state has been persisted, the loaded snapshot
666
- may silently mis-match the new shape. The adapter does a shallow merge, so
667
- extra fields are preserved and missing fields keep their initial values, but
668
- type mismatches are not caught at runtime. To avoid silent corruption, version
669
- your schema key (e.g. `"taskBoard_v2"`) or namespace it by schema hash
670
- whenever you make breaking changes. Alternatively, add a migration step in
671
- your `load` or `importState` call.
672
- </Callout>
673
-
674
- ## Streaming Updates
675
-
676
- The same partial merge runs token by token as the model generates an `update_{name}` call, so the interactable fills in live: a field the model is writing updates character by character, and only the fields and array items the call touches change. Everything it doesn't mention stays exactly as it was.
677
-
678
- That stability is concrete, not just visual. While one array item streams in, the items the model isn't editing keep their exact object identity for the whole stream, so a memoized row for them never re-renders:
679
-
680
- ```tsx
681
- import { memo } from "react";
682
-
683
- const TaskRow = memo(function TaskRow({ task }: { task: Task }) {
684
- return <li>{task.title}</li>;
685
- });
686
-
687
- function TaskBoard() {
688
- const [state] = unstable_useInteractable("taskBoard", config);
689
-
690
- return (
691
- <ul>
692
- {state.tasks.map((task) => (
693
- <TaskRow key={task.id} task={task} />
694
- ))}
695
- </ul>
696
- );
697
- }
698
- ```
699
-
700
- As `update_taskBoard` streams an edit to one task, that row re-renders as its fields arrive while every other `TaskRow` stays put, no flicker and no work. You get a live-updating list for free; you only reach for the partial state when you want to show something extra during the stream.
701
-
702
- One case worth handling: at the very start of a fresh create the model may not have produced any items yet. Use the thread's `isRunning` to tell "still streaming, nothing yet" apart from "the model returned an empty result", and show a skeleton only for that gap:
703
-
704
- ```tsx
705
- import { useAuiState } from "@assistant-ui/react";
706
-
707
- const isRunning = useAuiState((s) => s.thread.isRunning);
708
- const isLoading = isRunning && state.tasks.length === 0;
709
- ```
710
-
711
- Inside a thread-scoped `render`, `streaming: true` carries the same live state: at the creating call `state` is the partial draft (fields may be missing); at an `update_{name}` call it's the live state filling in as the edit streams. Render a preview; edits made during a create stream are dropped.
712
-
713
- ## Multiple Instances
714
-
715
- A `name` can have many live instances at once. They all share one `update_{name}` tool: the model addresses an instance with the tool's `id` parameter, which it reads from the state snapshots in the conversation. The tool's name, schema, and description never change as instances mount and unmount, so the model's tool list (and provider prompt caches) stay stable. The schema marks `id` as required, so the model is expected to send it on every call; the runtime still resolves an id-less call while exactly one instance exists. A call with an unknown `id` returns an error listing the valid ids, so the model can recover.
716
-
717
- How instances come into being differs by scope.
718
-
719
- ### Thread-scoped: instances for free
720
-
721
- A thread-scoped interactable gets a fresh instance every time the model calls its tool. The instance `id` is the creating call's `toolCallId`, so two calls are two instances with no extra code on your side: the same `render` and the same `update_{name}` tool serve all of them. This is how a model spins up several artifacts or notepads in one conversation, each addressed by its own `toolCallId`.
722
-
723
- ### App-scoped: one instance per mount
724
-
725
- For app-scoped interactables you decide how many instances exist by mounting `unstable_useInteractable` in more than one place, one instance per mount. Give each mount a distinct `id`:
726
-
727
- ```tsx
728
- import { unstable_useInteractable } from "@assistant-ui/react";
729
- import { z } from "zod";
730
-
731
- const noteSchema = z.object({
732
- title: z.string(),
733
- content: z.string(),
734
- color: z.enum(["yellow", "blue", "green", "pink"]),
735
- });
736
-
737
- const noteInitialState = {
738
- title: "New Note",
739
- content: "",
740
- color: "yellow" as const,
741
- };
742
-
743
- function NoteCard({ noteId }: { noteId: string }) {
744
- const [state] = unstable_useInteractable("note", {
745
- id: noteId,
746
- description: "A sticky note",
747
- stateSchema: noteSchema,
748
- initialState: noteInitialState,
749
- });
750
-
751
- return <div>{state.title}</div>;
752
- }
753
-
754
- function App() {
755
- return (
756
- <>
757
- <NoteCard noteId="note-1" />
758
- <NoteCard noteId="note-2" />
759
- {/* one update_note tool: update_note({ id: "note-2", color: "blue" }) */}
760
- </>
761
- );
762
- }
763
- ```
764
-
765
- Pass an explicit `id` whenever you need to reach a specific instance: to read or write it from another component with `unstable_useInteractableState(id)`, to share it between an [artifact](#artifacts) panel and its inline trigger, or to keep [persisted](#persistence) state attached across reloads. Omitting `id` also works, and each mount then gets its own auto-generated id, but those ids are positional and shift as a dynamic list adds and removes items, so persisted state keyed by an old id would not reattach. Leave `id` off only when the component is the sole reader and its state is not persisted.
766
-
767
- <Callout type="info">
768
- A top-level `id` field in your `stateSchema` is reserved: the update tool uses
769
- it for instance addressing, so the model cannot write a state field named
770
- `id`. Nest it or name it differently (e.g. `noteId`).
771
- </Callout>
772
-
773
- `update_note` edits a note that is already mounted: its `title`, `content`, `color`, and any other schema fields. It cannot mount a new `NoteCard` or unmount one; which components exist is your app's state. If you want the model to add and remove notes, model them as a single interactable holding an array (one `notes` field), and `update_notes` then adds, updates, removes, and clears entries directly, the way the [Quick Start](#quick-start) task board does. Use separate mounted instances only when each note is genuinely its own component; mounting and unmounting those is app work you can expose as a [companion tool](#companion-tools).
774
-
775
- ## Partial Updates
776
-
777
- Auto-generated tools use a partial schema in which all fields are optional. The AI only sends the fields it wants to change; omitted fields keep their current values.
778
-
779
- ```tsx
780
- // If the state is { title: "My Note", content: "Hello", color: "yellow" }
781
- // The AI can call: update_note({ color: "blue" })
782
- // Result: { title: "My Note", content: "Hello", color: "blue" }
783
- ```
784
-
785
- This is especially useful for large state objects where regenerating the entire state would be expensive and error-prone.
786
-
787
- For array fields whose items carry an `id`, the AI doesn't send a replacement array, it sends operations (`add`, `update`, `remove`, `clear`) and the framework applies them to the current list. Added items get their `id` from the framework; existing items are addressed by the `id` you gave them.
788
-
789
- <Callout type="info">
790
- Merge is shallow (one level deep). If the AI sends a nested object, it
791
- replaces that entire field rather than deep-merging into it.
792
- </Callout>
793
-
794
- ## How It Works
795
-
796
- 1. **Register**: the interactable joins the `interactables` scope with its name, description, schema, and initial state.
797
- 2. **Generate the tool**: one `update_{name}` tool per name, with a partial schema (every field optional) plus a required `id`. The tool list stays stable as instances mount and unmount, so provider prompt caches stay warm.
798
- 3. **Snapshot**: each sent user message carries the current state in `metadata.custom`, but only when the model doesn't already know it. A user edit stamps a snapshot; the model's own `update_*` calls and an in-message instance's create args don't. When the change fits a shallow merge it stamps only the changed fields (`partial: true`). Your route turns snapshots into model-visible text (see [State snapshots](#state-snapshots)).
799
- 4. **Stream**: state updates field-by-field as the model generates the tool arguments, so the UI fills in live.
800
- 5. **Merge**: only the fields the model sends are applied; the rest stay. Array fields keyed by `id` take operations (add/update/remove/clear) instead of a replacement array, and the framework mints ids for added items.
801
- 6. **Both directions**: a model `update_*` updates state and re-renders; a user `setState` rides the next message as a fresh snapshot.
802
-
803
- ## Unmount Behavior
804
-
805
- When a component that called `unstable_useInteractable` unmounts, the interactable is unregistered, but its state is preserved in the `unstable_Interactables` scope. When the component mounts again with the same name and id, the scope restores the preserved state rather than resetting to `initialState`. This means transient unmounts (such as React Strict Mode double-mounts or tab switches) do not lose state.
806
-
807
- An instance registered from several places (its creating tool call, `update_*` calls, an artifact panel) stays registered until the last one unmounts, so scrolling one out of a virtualized thread doesn't tear the instance down while another is visible.
808
-
809
- ## API Reference
810
-
811
- ### `unstable_useInteractable`
812
-
813
- Registers an interactable with the AI assistant and returns its state. It behaves like `useState`, except the model can also read and update the value. Call it once per instance.
814
-
815
- ```tsx
816
- const [state, { id, setState, isPending, error, flush }] =
817
- unstable_useInteractable(name, config);
818
- ```
819
-
820
- **Parameters:**
821
-
822
- <PrimitivesTypeTable
823
- type="unstable_useInteractable-params"
824
- parameters={[
825
- {
826
- name: "name",
827
- type: "string",
828
- required: true,
829
- description: (
830
- <>
831
- Name for the interactable (determines the{" "}
832
- <code>update_{"{name}"}</code> tool).
833
- </>
834
- ),
835
- },
836
- {
837
- name: "config",
838
- type: "Unstable_InteractableConfig<TSchema>",
839
- required: true,
840
- description: "Configuration for the interactable.",
841
- children: [
842
- {
843
- parameters: [
844
- {
845
- name: "description",
846
- type: "string",
847
- required: true,
848
- description: "Description shown to the AI.",
849
- },
850
- {
851
- name: "stateSchema",
852
- type: "StandardSchemaV1 | JSONSchema7",
853
- required: true,
854
- description: "Schema for the state (e.g., a Zod schema).",
855
- },
856
- {
857
- name: "initialState",
858
- type: "TState",
859
- required: true,
860
- description: (
861
- <>
862
- Initial state value. The type is inferred from{" "}
863
- <code>stateSchema</code>.
864
- </>
865
- ),
866
- },
867
- {
868
- name: "id",
869
- type: "string",
870
- required: false,
871
- description:
872
- "Unique instance ID, used to address this instance when multiple interactables share a name. Auto-generated if omitted.",
873
- },
874
- {
875
- name: "updateRender",
876
- type: "ToolCallMessagePartComponent",
877
- required: false,
878
- description: (
879
- <>
880
- Renders the model's <code>update_{"{name}"}</code> tool calls
881
- yourself; installed once per name. See{" "}
882
- <a href="#custom-update-rendering">Custom Update Rendering</a>.
883
- </>
884
- ),
885
- },
886
- ],
887
- },
888
- ],
889
- },
890
- ]}
891
- />
892
-
893
- **Returns:** `[state, methods]`
894
-
895
- <PrimitivesTypeTable
896
- type="unstable_useInteractable-returns"
897
- parameters={[
898
- {
899
- name: "state",
900
- type: "TState",
901
- required: true,
902
- description: (
903
- <>
904
- Current state. Inferred from <code>stateSchema</code>.
905
- </>
906
- ),
907
- },
908
- {
909
- name: "id",
910
- type: "string",
911
- required: true,
912
- description: (
913
- <>
914
- The instance id; pass it to <code>unstable_useInteractableState</code>{" "}
915
- in other components.
916
- </>
917
- ),
918
- },
919
- {
920
- name: "setState",
921
- type: "(updater: TState | ((prev: TState) => TState)) => void",
922
- required: true,
923
- description: (
924
- <>
925
- State setter, like <code>useState</code>.
926
- </>
927
- ),
928
- },
929
- {
930
- name: "version",
931
- type: "{ state: TState; isLatest: boolean; restore: () => void } | undefined",
932
- required: true,
933
- description: (
934
- <>
935
- This message's version of the instance, when rendered inside a
936
- tool-call part; see <a href="#versions">Versions</a>.{" "}
937
- <code>undefined</code> outside messages and for app scope.
938
- </>
939
- ),
940
- },
941
- {
942
- name: "isPending",
943
- type: "boolean",
944
- required: true,
945
- description: "Whether a persistence save is in-flight.",
946
- },
947
- {
948
- name: "error",
949
- type: "unknown",
950
- required: true,
951
- description: "Error from the last failed save.",
952
- },
953
- {
954
- name: "flush",
955
- type: "() => Promise<void>",
956
- required: true,
957
- description: "Force an immediate persistence save.",
958
- },
959
- ]}
960
- />
961
-
962
- <Callout type="info">
963
- Selection is not a built-in field. To tell the AI which interactable is
964
- focused, add a selection field such as `selectedId` to your own state; see
965
- [Selection](#selection).
966
- </Callout>
967
-
968
- ### `unstable_useInteractableState`
969
-
970
- Reads and writes the state of an interactable registered elsewhere, by id. Use this from secondary readers (children, siblings of the owning component).
971
-
972
- ```tsx
973
- const [state, { setState, isPending, error, flush }] =
974
- unstable_useInteractableState<TState>(id);
975
- ```
976
-
977
- **Parameters:**
978
-
979
- <PrimitivesTypeTable
980
- type="unstable_useInteractableState-params"
981
- parameters={[
982
- {
983
- name: "id",
984
- type: "string",
985
- required: true,
986
- description: (
987
- <>
988
- The interactable instance id (from{" "}
989
- <code>unstable_useInteractable</code>).
990
- </>
991
- ),
992
- },
993
- ]}
994
- />
995
-
996
- **Returns:** `[state, methods]`, the same shape as `unstable_useInteractable` without `id`. `state` is `TState | undefined` until the owning `unstable_useInteractable` has registered.
997
-
998
- ### `unstable_interactableTool`
999
-
1000
- ```tsx
1001
- notepad: unstable_interactableTool({ description, stateSchema, render }),
1002
- ```
1003
-
1004
- Returns a complete toolkit tool entry (a frontend tool with standalone display); the entry key is the interactable name, and the tool's arguments are its initial state. The same `render` then appears at every message that creates or updates the instance. `render` receives:
1005
-
1006
- <PrimitivesTypeTable
1007
- type="unstable_interactableTool-render-props"
1008
- parameters={[
1009
- {
1010
- name: "state",
1011
- type: "TState",
1012
- required: true,
1013
- description:
1014
- "The live state. While streaming, fields the model has not finished generating may be missing.",
1015
- },
1016
- {
1017
- name: "setState",
1018
- type: "(updater: TState | ((prev: TState) => TState)) => void",
1019
- required: true,
1020
- description: "Updates the live state.",
1021
- },
1022
- {
1023
- name: "version",
1024
- type: "{ state: TState; isLatest: boolean; restore: () => void } | undefined",
1025
- required: true,
1026
- description:
1027
- "This message's version of the instance; undefined while streaming.",
1028
- },
1029
- {
1030
- name: "id",
1031
- type: "string",
1032
- required: true,
1033
- description: "The instance id (the creating call's toolCallId).",
1034
- },
1035
- {
1036
- name: "streaming",
1037
- type: "boolean",
1038
- required: true,
1039
- description: "True while the tool call's arguments are still streaming.",
1040
- },
1041
- ]}
1042
- />
1043
-
1044
- ### `unstable_useInteractableVersions`
1045
-
1046
- ```tsx
1047
- const versions = unstable_useInteractableVersions<TState>(id, name);
1048
- // → [{ state, origin: "create" | "update" | "user-edit", toolCallId?, restore }, ...]
1049
- ```
1050
-
1051
- Every version of an interactable recorded in the current thread, oldest first. Each entry carries the full `state` and a `restore()` that sets the live instance back to it. Works for both scopes; see [Versions](#versions) for usage. The non-React equivalent for backends is `unstable_getInteractableVersions(messages, id, name)`, exported from `@assistant-ui/react`.
1052
-
1053
- ### `unstable_Interactables`
1054
-
1055
- The scope resource that manages all interactables. Register it via your provider's `config`, optionally with a [persistence adapter](#persistence):
1056
-
1057
- ```tsx
1058
- AuiConfig({
1059
- unstable_interactables: unstable_Interactables({ persistence: myAdapter }),
1060
- });
1061
- ```
1062
-
1063
- ## Migrating from the Previous API
1064
-
1065
- If you used an earlier version of the interactables API:
1066
-
1067
- - `useAssistantInteractable` and `useInteractableState` have been merged into a single [`unstable_useInteractable`](#unstable_useinteractable) hook that registers and returns state. `unstable_useInteractableState` remains for secondary readers.
1068
- - Per-instance tools (`update_note_note-1`) are gone. Each name has one stable `update_{name}` tool with a required `id` parameter.
1069
- - The top-level `selected` prop and `setSelected` method have been removed. Represent selection as ordinary state; see [Selection](#selection).
1070
-
1071
- ## Full Example
1072
-
1073
- See the complete [with-interactables example](https://github.com/assistant-ui/assistant-ui/tree/main/examples/with-interactables) for a working implementation featuring:
1074
-
1075
- - **Task Board**: one interactable whose `tasks` array the AI edits with `update_taskBoard` (add/update/remove/clear)
1076
- - **Sticky Notes**: one `notes` interactable with a `selectedId` field, where the AI adds, edits, removes, and selects notes through `update_notes`
1077
- - **localStorage persistence**: state survives page refresh via a `load`/`save` persistence adapter
1078
- - **Sync indicator**: spinning icon while a save is in-flight (`isPending`)
1079
-
1080
- ## Related
1081
-
1082
- - [Dynamic Tools](/docs/tools/dynamic-tools): Frontend tools whose executors close over React state
1083
- - [Tool UI](/docs/tools/tool-ui): Inline tool call UIs rendered inside messages
1084
- - [LangGraph Generative UI](/docs/runtimes/langgraph/generative-ui): Structured UI components emitted by a LangGraph graph alongside messages