@assistant-ui/mcp-docs-server 0.1.30 → 0.1.32

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 (341) hide show
  1. package/.docs/organized/code-examples/waterfall.md +8 -8
  2. package/.docs/organized/code-examples/with-a2a.md +10 -10
  3. package/.docs/organized/code-examples/with-ag-ui.md +12 -12
  4. package/.docs/organized/code-examples/with-ai-sdk-v6.md +14 -14
  5. package/.docs/organized/code-examples/with-artifacts.md +14 -14
  6. package/.docs/organized/code-examples/with-assistant-transport.md +11 -11
  7. package/.docs/organized/code-examples/with-browser-extension.md +345 -0
  8. package/.docs/organized/code-examples/with-chain-of-thought.md +129 -63
  9. package/.docs/organized/code-examples/with-cloud-standalone.md +13 -13
  10. package/.docs/organized/code-examples/with-cloud.md +13 -13
  11. package/.docs/organized/code-examples/with-custom-thread-list.md +65 -20
  12. package/.docs/organized/code-examples/with-elevenlabs-conversational.md +18 -17
  13. package/.docs/organized/code-examples/with-elevenlabs-scribe.md +18 -17
  14. package/.docs/organized/code-examples/with-expo.md +25 -25
  15. package/.docs/organized/code-examples/with-external-store.md +10 -10
  16. package/.docs/organized/code-examples/with-ffmpeg.md +14 -14
  17. package/.docs/organized/code-examples/with-generative-ui.md +211 -14
  18. package/.docs/organized/code-examples/with-google-adk.md +12 -12
  19. package/.docs/organized/code-examples/with-heat-graph.md +8 -8
  20. package/.docs/organized/code-examples/with-image-generation.md +454 -0
  21. package/.docs/organized/code-examples/with-interactables.md +14 -14
  22. package/.docs/organized/code-examples/with-langchain.md +12 -12
  23. package/.docs/organized/code-examples/with-langgraph.md +15 -12
  24. package/.docs/organized/code-examples/with-livekit.md +19 -18
  25. package/.docs/organized/code-examples/with-mcp.md +748 -0
  26. package/.docs/organized/code-examples/with-opencode.md +107 -62
  27. package/.docs/organized/code-examples/with-parent-id-grouping.md +12 -12
  28. package/.docs/organized/code-examples/with-react-hook-form.md +14 -14
  29. package/.docs/organized/code-examples/with-react-ink.md +4 -4
  30. package/.docs/organized/code-examples/with-react-router.md +16 -16
  31. package/.docs/organized/code-examples/with-resumable-stream.md +660 -0
  32. package/.docs/organized/code-examples/with-store.md +8 -8
  33. package/.docs/organized/code-examples/with-tanstack.md +14 -14
  34. package/.docs/organized/code-examples/with-tap-runtime.md +11 -10
  35. package/.docs/raw/docs/(docs)/cli.mdx +3 -1
  36. package/.docs/raw/docs/(docs)/copilots/assistant-frame.mdx +1 -0
  37. package/.docs/raw/docs/(docs)/copilots/make-assistant-tool-ui.mdx +10 -3
  38. package/.docs/raw/docs/(docs)/copilots/make-assistant-tool.mdx +8 -3
  39. package/.docs/raw/docs/(docs)/copilots/make-assistant-visible.mdx +1 -0
  40. package/.docs/raw/docs/(docs)/copilots/model-context.mdx +1 -0
  41. package/.docs/raw/docs/(docs)/copilots/motivation.mdx +1 -0
  42. package/.docs/raw/docs/(docs)/copilots/use-assistant-instructions.mdx +1 -0
  43. package/.docs/raw/docs/(docs)/devtools.mdx +1 -0
  44. package/.docs/raw/docs/(docs)/index.mdx +3 -2
  45. package/.docs/raw/docs/(docs)/installation.mdx +2 -1
  46. package/.docs/raw/docs/(docs)/rtl.mdx +1 -0
  47. package/.docs/raw/docs/(reference)/api-reference/adapters/attachments.mdx +36 -0
  48. package/.docs/raw/docs/(reference)/api-reference/adapters/feedback.mdx +20 -0
  49. package/.docs/raw/docs/(reference)/api-reference/adapters/index.mdx +34 -0
  50. package/.docs/raw/docs/(reference)/api-reference/adapters/model.mdx +44 -0
  51. package/.docs/raw/docs/(reference)/api-reference/adapters/persistence.mdx +55 -0
  52. package/.docs/raw/docs/(reference)/api-reference/adapters/runtime.mdx +20 -0
  53. package/.docs/raw/docs/(reference)/api-reference/adapters/suggestions.mdx +20 -0
  54. package/.docs/raw/docs/(reference)/api-reference/context-providers/assistant-runtime-provider.mdx +37 -8
  55. package/.docs/raw/docs/(reference)/api-reference/context-providers/index.mdx +22 -0
  56. package/.docs/raw/docs/(reference)/api-reference/context-providers/scoped-providers.mdx +64 -0
  57. package/.docs/raw/docs/(reference)/api-reference/external-store/index.mdx +22 -0
  58. package/.docs/raw/docs/(reference)/api-reference/external-store/message-conversion.mdx +52 -0
  59. package/.docs/raw/docs/(reference)/api-reference/external-store/runtime.mdx +36 -0
  60. package/.docs/raw/docs/(reference)/api-reference/hooks/composer-triggers.mdx +98 -0
  61. package/.docs/raw/docs/(reference)/api-reference/hooks/index.mdx +31 -0
  62. package/.docs/raw/docs/(reference)/api-reference/hooks/model-context.mdx +33 -0
  63. package/.docs/raw/docs/(reference)/api-reference/hooks/primitives.mdx +640 -0
  64. package/.docs/raw/docs/(reference)/api-reference/hooks/runtimes.mdx +28 -0
  65. package/.docs/raw/docs/(reference)/api-reference/hooks/state.mdx +94 -0
  66. package/.docs/raw/docs/(reference)/api-reference/hooks/utilities.mdx +464 -0
  67. package/.docs/raw/docs/(reference)/api-reference/integrations/cloud-ai-sdk.mdx +24 -0
  68. package/.docs/raw/docs/(reference)/api-reference/integrations/index.mdx +22 -0
  69. package/.docs/raw/docs/(reference)/api-reference/integrations/react-ai-sdk.mdx +79 -0
  70. package/.docs/raw/docs/(reference)/api-reference/model-context/context.mdx +52 -0
  71. package/.docs/raw/docs/(reference)/api-reference/model-context/index.mdx +22 -0
  72. package/.docs/raw/docs/(reference)/api-reference/model-context/registry.mdx +20 -0
  73. package/.docs/raw/docs/(reference)/api-reference/overview.mdx +125 -125
  74. package/.docs/raw/docs/(reference)/api-reference/primitives/action-bar-more.mdx +78 -221
  75. package/.docs/raw/docs/(reference)/api-reference/primitives/action-bar.mdx +127 -242
  76. package/.docs/raw/docs/(reference)/api-reference/primitives/assistant-if.mdx +46 -20
  77. package/.docs/raw/docs/(reference)/api-reference/primitives/assistant-modal.mdx +66 -87
  78. package/.docs/raw/docs/(reference)/api-reference/primitives/attachment.mdx +50 -58
  79. package/.docs/raw/docs/(reference)/api-reference/primitives/branch-picker.mdx +80 -48
  80. package/.docs/raw/docs/(reference)/api-reference/primitives/chain-of-thought.mdx +67 -0
  81. package/.docs/raw/docs/(reference)/api-reference/primitives/composer.mdx +323 -461
  82. package/.docs/raw/docs/(reference)/api-reference/primitives/error.mdx +36 -43
  83. package/.docs/raw/docs/(reference)/api-reference/primitives/index.mdx +70 -0
  84. package/.docs/raw/docs/(reference)/api-reference/primitives/message-part.mdx +63 -245
  85. package/.docs/raw/docs/(reference)/api-reference/primitives/message.mdx +182 -554
  86. package/.docs/raw/docs/(reference)/api-reference/primitives/queue-item.mdx +65 -0
  87. package/.docs/raw/docs/(reference)/api-reference/primitives/selection-toolbar.mdx +35 -22
  88. package/.docs/raw/docs/(reference)/api-reference/primitives/suggestion.mdx +57 -140
  89. package/.docs/raw/docs/(reference)/api-reference/primitives/thread-list-item-more.mdx +70 -161
  90. package/.docs/raw/docs/(reference)/api-reference/primitives/thread-list-item.mdx +84 -108
  91. package/.docs/raw/docs/(reference)/api-reference/primitives/thread-list.mdx +82 -86
  92. package/.docs/raw/docs/(reference)/api-reference/primitives/thread.mdx +173 -314
  93. package/.docs/raw/docs/(reference)/api-reference/runtimes/assistant-runtime.mdx +9 -21
  94. package/.docs/raw/docs/(reference)/api-reference/runtimes/attachment-runtime.mdx +10 -21
  95. package/.docs/raw/docs/(reference)/api-reference/runtimes/composer-runtime.mdx +15 -70
  96. package/.docs/raw/docs/(reference)/api-reference/runtimes/index.mdx +43 -0
  97. package/.docs/raw/docs/(reference)/api-reference/runtimes/message-part-runtime.mdx +25 -28
  98. package/.docs/raw/docs/(reference)/api-reference/runtimes/message-runtime.mdx +11 -63
  99. package/.docs/raw/docs/(reference)/api-reference/runtimes/queue-state.mdx +20 -0
  100. package/.docs/raw/docs/(reference)/api-reference/runtimes/thread-list-item-runtime.mdx +11 -48
  101. package/.docs/raw/docs/(reference)/api-reference/runtimes/thread-list-runtime.mdx +10 -42
  102. package/.docs/raw/docs/(reference)/api-reference/runtimes/thread-runtime.mdx +18 -30
  103. package/.docs/raw/docs/(reference)/api-reference/tools/component-tools.mdx +68 -0
  104. package/.docs/raw/docs/(reference)/api-reference/tools/index.mdx +39 -0
  105. package/.docs/raw/docs/(reference)/api-reference/tools/rendering.mdx +79 -0
  106. package/.docs/raw/docs/(reference)/api-reference/tools/status.mdx +42 -0
  107. package/.docs/raw/docs/(reference)/api-reference/tools/toolkits.mdx +92 -0
  108. package/.docs/raw/docs/(reference)/api-reference/transport/assistant-transport.mdx +48 -0
  109. package/.docs/raw/docs/(reference)/api-reference/transport/frame.mdx +62 -0
  110. package/.docs/raw/docs/(reference)/api-reference/transport/index.mdx +22 -0
  111. package/.docs/raw/docs/(reference)/api-reference/utilities/index.mdx +19 -0
  112. package/.docs/raw/docs/(reference)/api-reference/utilities/miscellaneous.mdx +131 -0
  113. package/.docs/raw/docs/(reference)/api-reference/voice/index.mdx +22 -0
  114. package/.docs/raw/docs/(reference)/api-reference/voice/session.mdx +54 -0
  115. package/.docs/raw/docs/(reference)/api-reference/voice/speech-dictation.mdx +36 -0
  116. package/.docs/raw/docs/cloud/ai-sdk-assistant-ui.mdx +230 -2
  117. package/.docs/raw/docs/cloud/ai-sdk.mdx +222 -4
  118. package/.docs/raw/docs/cloud/index.mdx +2 -2
  119. package/.docs/raw/docs/cloud/langgraph.mdx +274 -2
  120. package/.docs/raw/docs/{(docs)/guides → guides}/attachments.mdx +45 -40
  121. package/.docs/raw/docs/guides/branching.mdx +76 -0
  122. package/.docs/raw/docs/guides/chain-of-thought.mdx +166 -0
  123. package/.docs/raw/docs/{(docs)/guides → guides}/context-api.mdx +54 -26
  124. package/.docs/raw/docs/{(docs)/guides → guides}/dictation.mdx +3 -1
  125. package/.docs/raw/docs/guides/editing.mdx +102 -0
  126. package/.docs/raw/docs/guides/generative-ui.mdx +142 -0
  127. package/.docs/raw/docs/guides/image-generation.mdx +74 -0
  128. package/.docs/raw/docs/guides/index.mdx +103 -0
  129. package/.docs/raw/docs/{(docs)/guides → guides}/interactables.mdx +51 -2
  130. package/.docs/raw/docs/{(docs)/guides → guides}/latex.mdx +53 -10
  131. package/.docs/raw/docs/guides/mcp-apps.mdx +231 -0
  132. package/.docs/raw/docs/{(docs)/guides → guides}/mentions.mdx +63 -88
  133. package/.docs/raw/docs/{(docs)/guides → guides}/message-timing.mdx +45 -6
  134. package/.docs/raw/docs/{(docs)/guides → guides}/multi-agent.mdx +66 -6
  135. package/.docs/raw/docs/{(docs)/guides → guides}/quoting.mdx +11 -18
  136. package/.docs/raw/docs/guides/resumable-stream-deployment.mdx +212 -0
  137. package/.docs/raw/docs/guides/resumable-stream-stores.mdx +152 -0
  138. package/.docs/raw/docs/guides/resumable-streams.mdx +210 -0
  139. package/.docs/raw/docs/{(docs)/guides → guides}/slash-commands.mdx +104 -38
  140. package/.docs/raw/docs/guides/speech.mdx +156 -0
  141. package/.docs/raw/docs/{(docs)/guides → guides}/suggestions.mdx +91 -69
  142. package/.docs/raw/docs/{(docs)/guides → guides}/tool-ui.mdx +110 -38
  143. package/.docs/raw/docs/{(docs)/guides → guides}/tools.mdx +197 -40
  144. package/.docs/raw/docs/{(docs)/guides → guides}/voice.mdx +41 -2
  145. package/.docs/raw/docs/ink/adapters.mdx +37 -1
  146. package/.docs/raw/docs/ink/custom-backend.mdx +59 -8
  147. package/.docs/raw/docs/ink/index.mdx +11 -14
  148. package/.docs/raw/docs/ink/migration.mdx +1 -3
  149. package/.docs/raw/docs/ink/primitives.mdx +386 -9
  150. package/.docs/raw/docs/integrations/attachments/custom-adapter.mdx +520 -0
  151. package/.docs/raw/docs/integrations/auth/better-auth.mdx +191 -0
  152. package/.docs/raw/docs/integrations/auth/clerk.mdx +172 -0
  153. package/.docs/raw/docs/integrations/auth/next-auth.mdx +196 -0
  154. package/.docs/raw/docs/integrations/frameworks/ai-sdk.mdx +79 -0
  155. package/.docs/raw/docs/integrations/frameworks/cloudflare-agents/overview.mdx +282 -0
  156. package/.docs/raw/docs/integrations/frameworks/mastra/full-stack.mdx +188 -0
  157. package/.docs/raw/docs/integrations/frameworks/mastra/overview.mdx +57 -0
  158. package/.docs/raw/docs/integrations/frameworks/mastra/separate-server.mdx +201 -0
  159. package/.docs/raw/docs/integrations/gateways/index.mdx +162 -0
  160. package/.docs/raw/docs/integrations/index.mdx +185 -0
  161. package/.docs/raw/docs/integrations/observability/helicone.mdx +130 -0
  162. package/.docs/raw/docs/integrations/observability/langfuse.mdx +163 -0
  163. package/.docs/raw/docs/integrations/observability/langsmith.mdx +150 -0
  164. package/.docs/raw/docs/integrations/persistence/custom-adapter.mdx +712 -0
  165. package/.docs/raw/docs/integrations/tools/mcp.mdx +267 -0
  166. package/.docs/raw/docs/integrations/tools/react-mcp.mdx +337 -0
  167. package/.docs/raw/docs/migrations/v0-14.mdx +297 -0
  168. package/.docs/raw/docs/primitives/action-bar.mdx +1 -0
  169. package/.docs/raw/docs/primitives/assistant-modal.mdx +1 -0
  170. package/.docs/raw/docs/primitives/attachment.mdx +1 -0
  171. package/.docs/raw/docs/primitives/branch-picker.mdx +1 -0
  172. package/.docs/raw/docs/primitives/chain-of-thought.mdx +90 -85
  173. package/.docs/raw/docs/primitives/composer.mdx +55 -1
  174. package/.docs/raw/docs/primitives/error.mdx +1 -0
  175. package/.docs/raw/docs/primitives/index.mdx +4 -3
  176. package/.docs/raw/docs/primitives/message.mdx +68 -5
  177. package/.docs/raw/docs/primitives/selection-toolbar.mdx +1 -0
  178. package/.docs/raw/docs/primitives/suggestion.mdx +10 -0
  179. package/.docs/raw/docs/primitives/thread-list.mdx +39 -0
  180. package/.docs/raw/docs/primitives/thread.mdx +16 -13
  181. package/.docs/raw/docs/react-native/hooks.mdx +2 -2
  182. package/.docs/raw/docs/react-native/index.mdx +5 -7
  183. package/.docs/raw/docs/react-native/migration.mdx +1 -3
  184. package/.docs/raw/docs/runtimes/a2a/client-and-hooks.mdx +396 -0
  185. package/.docs/raw/docs/runtimes/a2a/overview.mdx +60 -0
  186. package/.docs/raw/docs/runtimes/a2a/quickstart.mdx +216 -0
  187. package/.docs/raw/docs/runtimes/ag-ui/overview.mdx +70 -0
  188. package/.docs/raw/docs/runtimes/ag-ui/quickstart.mdx +243 -0
  189. package/.docs/raw/docs/runtimes/ag-ui/runtime-options.mdx +154 -0
  190. package/.docs/raw/docs/runtimes/ai-sdk/overview.mdx +52 -0
  191. package/.docs/raw/docs/runtimes/ai-sdk/v4-legacy.mdx +73 -128
  192. package/.docs/raw/docs/runtimes/ai-sdk/v5-legacy.mdx +70 -64
  193. package/.docs/raw/docs/runtimes/ai-sdk/v6.mdx +337 -123
  194. package/.docs/raw/docs/runtimes/concepts/adapters.mdx +265 -0
  195. package/.docs/raw/docs/runtimes/concepts/architecture.mdx +125 -0
  196. package/.docs/raw/docs/runtimes/concepts/stability.mdx +67 -0
  197. package/.docs/raw/docs/runtimes/concepts/threads.mdx +428 -0
  198. package/.docs/raw/docs/runtimes/custom/assistant-transport.mdx +703 -0
  199. package/.docs/raw/docs/runtimes/custom/data-stream.mdx +348 -0
  200. package/.docs/raw/docs/runtimes/custom/external-store.mdx +261 -1236
  201. package/.docs/raw/docs/runtimes/custom/local-runtime.mdx +746 -0
  202. package/.docs/raw/docs/runtimes/custom/overview.mdx +71 -0
  203. package/.docs/raw/docs/runtimes/google-adk/api.mdx +256 -0
  204. package/.docs/raw/docs/runtimes/google-adk/hooks.mdx +717 -0
  205. package/.docs/raw/docs/runtimes/google-adk/overview.mdx +69 -0
  206. package/.docs/raw/docs/runtimes/google-adk/quickstart.mdx +229 -0
  207. package/.docs/raw/docs/runtimes/langchain.mdx +533 -0
  208. package/.docs/raw/docs/runtimes/langgraph/generative-ui.mdx +305 -0
  209. package/.docs/raw/docs/runtimes/langgraph/interrupts.mdx +104 -0
  210. package/.docs/raw/docs/runtimes/langgraph/overview.mdx +84 -0
  211. package/.docs/raw/docs/runtimes/langgraph/quickstart.mdx +496 -0
  212. package/.docs/raw/docs/runtimes/langgraph/streaming.mdx +127 -0
  213. package/.docs/raw/docs/runtimes/langgraph/threads.mdx +113 -0
  214. package/.docs/raw/docs/runtimes/langgraph/tutorial/introduction.mdx +3 -3
  215. package/.docs/raw/docs/runtimes/langgraph/tutorial/part-1.mdx +0 -23
  216. package/.docs/raw/docs/runtimes/langgraph/tutorial/part-2.mdx +1 -1
  217. package/.docs/raw/docs/runtimes/opencode/hooks.mdx +191 -0
  218. package/.docs/raw/docs/runtimes/opencode/overview.mdx +48 -0
  219. package/.docs/raw/docs/runtimes/opencode/quickstart.mdx +119 -0
  220. package/.docs/raw/docs/runtimes/pick-a-runtime.mdx +78 -203
  221. package/.docs/raw/docs/ui/accordion.mdx +1 -0
  222. package/.docs/raw/docs/ui/assistant-modal.mdx +1 -0
  223. package/.docs/raw/docs/ui/assistant-sidebar.mdx +1 -0
  224. package/.docs/raw/docs/ui/attachment.mdx +1 -0
  225. package/.docs/raw/docs/ui/badge.mdx +1 -0
  226. package/.docs/raw/docs/ui/composer-trigger-popover.mdx +12 -1
  227. package/.docs/raw/docs/ui/context-display.mdx +1 -0
  228. package/.docs/raw/docs/ui/diff-viewer.mdx +1 -0
  229. package/.docs/raw/docs/ui/directive-text.mdx +1 -0
  230. package/.docs/raw/docs/ui/file.mdx +1 -0
  231. package/.docs/raw/docs/ui/image.mdx +1 -0
  232. package/.docs/raw/docs/ui/markdown.mdx +2 -14
  233. package/.docs/raw/docs/ui/mcp-config.mdx +102 -0
  234. package/.docs/raw/docs/ui/mermaid.mdx +1 -0
  235. package/.docs/raw/docs/ui/message-timing.mdx +3 -2
  236. package/.docs/raw/docs/ui/model-selector.mdx +9 -8
  237. package/.docs/raw/docs/ui/part-grouping.mdx +325 -313
  238. package/.docs/raw/docs/ui/quote.mdx +1 -0
  239. package/.docs/raw/docs/ui/reasoning.mdx +66 -33
  240. package/.docs/raw/docs/ui/scrollbar.mdx +1 -0
  241. package/.docs/raw/docs/ui/select.mdx +1 -0
  242. package/.docs/raw/docs/ui/sources.mdx +18 -0
  243. package/.docs/raw/docs/ui/streamdown.mdx +35 -2
  244. package/.docs/raw/docs/ui/syntax-highlighting.mdx +1 -0
  245. package/.docs/raw/docs/ui/tabs.mdx +1 -0
  246. package/.docs/raw/docs/ui/thread-list.mdx +19 -2
  247. package/.docs/raw/docs/ui/thread.mdx +58 -3
  248. package/.docs/raw/docs/ui/tool-fallback.mdx +1 -0
  249. package/.docs/raw/docs/ui/tool-group.mdx +39 -11
  250. package/.docs/raw/docs/ui/voice.mdx +1 -0
  251. package/.docs/raw/docs/utilities/heat-graph.mdx +1 -0
  252. package/.docs/raw/docs/utilities/react-o11y.mdx +278 -0
  253. package/.docs/raw/docs/utilities/tw-shimmer.mdx +1 -0
  254. package/README.md +14 -72
  255. package/dist/constants.d.ts +12 -9
  256. package/dist/constants.d.ts.map +1 -1
  257. package/dist/constants.js +13 -9
  258. package/dist/constants.js.map +1 -1
  259. package/dist/index.d.ts +7 -3
  260. package/dist/index.d.ts.map +1 -1
  261. package/dist/index.js +25 -24
  262. package/dist/index.js.map +1 -1
  263. package/dist/prepare-docs/code-examples.d.ts +4 -1
  264. package/dist/prepare-docs/code-examples.d.ts.map +1 -1
  265. package/dist/prepare-docs/code-examples.js +109 -121
  266. package/dist/prepare-docs/code-examples.js.map +1 -1
  267. package/dist/prepare-docs/copy-raw.d.ts +4 -1
  268. package/dist/prepare-docs/copy-raw.d.ts.map +1 -1
  269. package/dist/prepare-docs/copy-raw.js +45 -42
  270. package/dist/prepare-docs/copy-raw.js.map +1 -1
  271. package/dist/prepare-docs/prepare.d.ts +1 -2
  272. package/dist/prepare-docs/prepare.js +17 -17
  273. package/dist/prepare-docs/prepare.js.map +1 -1
  274. package/dist/stdio.d.ts +1 -3
  275. package/dist/stdio.js +6 -3
  276. package/dist/stdio.js.map +1 -1
  277. package/dist/tools/docs.d.ts +20 -15
  278. package/dist/tools/docs.d.ts.map +1 -1
  279. package/dist/tools/docs.js +140 -161
  280. package/dist/tools/docs.js.map +1 -1
  281. package/dist/tools/examples.d.ts +20 -15
  282. package/dist/tools/examples.d.ts.map +1 -1
  283. package/dist/tools/examples.js +74 -86
  284. package/dist/tools/examples.js.map +1 -1
  285. package/dist/tools/tests/test-setup.d.ts +5 -2
  286. package/dist/tools/tests/test-setup.d.ts.map +1 -1
  287. package/dist/tools/tests/test-setup.js +21 -28
  288. package/dist/tools/tests/test-setup.js.map +1 -1
  289. package/dist/utils/logger.d.ts +8 -5
  290. package/dist/utils/logger.d.ts.map +1 -1
  291. package/dist/utils/logger.js +17 -17
  292. package/dist/utils/logger.js.map +1 -1
  293. package/dist/utils/mcp-format.d.ts +8 -5
  294. package/dist/utils/mcp-format.d.ts.map +1 -1
  295. package/dist/utils/mcp-format.js +9 -9
  296. package/dist/utils/mcp-format.js.map +1 -1
  297. package/dist/utils/mdx.d.ts +8 -6
  298. package/dist/utils/mdx.d.ts.map +1 -1
  299. package/dist/utils/mdx.js +22 -22
  300. package/dist/utils/mdx.js.map +1 -1
  301. package/dist/utils/paths.d.ts +9 -6
  302. package/dist/utils/paths.d.ts.map +1 -1
  303. package/dist/utils/paths.js +66 -76
  304. package/dist/utils/paths.js.map +1 -1
  305. package/dist/utils/security.d.ts +4 -1
  306. package/dist/utils/security.d.ts.map +1 -1
  307. package/dist/utils/security.js +19 -40
  308. package/dist/utils/security.js.map +1 -1
  309. package/package.json +6 -6
  310. package/src/tools/tests/path-traversal.test.ts +1 -1
  311. package/.docs/raw/docs/(docs)/guides/branching.mdx +0 -65
  312. package/.docs/raw/docs/(docs)/guides/chain-of-thought.mdx +0 -164
  313. package/.docs/raw/docs/(docs)/guides/editing.mdx +0 -66
  314. package/.docs/raw/docs/(docs)/guides/speech.mdx +0 -38
  315. package/.docs/raw/docs/(reference)/api-reference/context-providers/text-message-part-provider.mdx +0 -40
  316. package/.docs/raw/docs/(reference)/api-reference/integrations/react-data-stream.mdx +0 -260
  317. package/.docs/raw/docs/(reference)/api-reference/integrations/react-hook-form.mdx +0 -103
  318. package/.docs/raw/docs/(reference)/api-reference/integrations/vercel-ai-sdk.mdx +0 -254
  319. package/.docs/raw/docs/(reference)/migrations/v0-14.mdx +0 -159
  320. package/.docs/raw/docs/runtimes/a2a/index.mdx +0 -298
  321. package/.docs/raw/docs/runtimes/assistant-transport.mdx +0 -1033
  322. package/.docs/raw/docs/runtimes/custom/custom-thread-list.mdx +0 -314
  323. package/.docs/raw/docs/runtimes/custom/local.mdx +0 -1464
  324. package/.docs/raw/docs/runtimes/data-stream.mdx +0 -422
  325. package/.docs/raw/docs/runtimes/google-adk/index.mdx +0 -686
  326. package/.docs/raw/docs/runtimes/helicone.mdx +0 -61
  327. package/.docs/raw/docs/runtimes/langchain/comparison.mdx +0 -60
  328. package/.docs/raw/docs/runtimes/langchain/index.mdx +0 -210
  329. package/.docs/raw/docs/runtimes/langgraph/index.mdx +0 -699
  330. package/.docs/raw/docs/runtimes/langgraph/tutorial/index.mdx +0 -12
  331. package/.docs/raw/docs/runtimes/langserve.mdx +0 -116
  332. package/.docs/raw/docs/runtimes/mastra/full-stack-integration.mdx +0 -218
  333. package/.docs/raw/docs/runtimes/mastra/overview.mdx +0 -18
  334. package/.docs/raw/docs/runtimes/mastra/separate-server-integration.mdx +0 -217
  335. package/dist/prepare-docs/prepare.d.ts.map +0 -1
  336. package/dist/stdio.d.ts.map +0 -1
  337. /package/.docs/raw/docs/{(reference)/migrations → migrations}/deprecation-policy.mdx +0 -0
  338. /package/.docs/raw/docs/{(reference) → migrations}/react-compatibility.mdx +0 -0
  339. /package/.docs/raw/docs/{(reference)/migrations → migrations}/react-langgraph-v0-7.mdx +0 -0
  340. /package/.docs/raw/docs/{(reference)/migrations → migrations}/v0-11.mdx +0 -0
  341. /package/.docs/raw/docs/{(reference)/migrations → migrations}/v0-12.mdx +0 -0
@@ -1,36 +1,50 @@
1
1
  ---
2
2
  title: ExternalStoreRuntime
3
- description: Bring your own Redux, Zustand, or state manager.
3
+ description: Bring your own redux, zustand, or state manager.
4
4
  ---
5
5
 
6
+ `ExternalStoreRuntime` bridges your existing state management with assistant-ui. You provide messages and callbacks; the runtime renders whatever you give it. UI features turn on based on which callbacks are present.
6
7
 
7
- ## Overview
8
+ ## When to use it
8
9
 
9
- `ExternalStoreRuntime` bridges your existing state management with assistant-ui components. It requires an `ExternalStoreAdapter<TMessage>` that handles communication between your state and the UI.
10
+ Pick `ExternalStoreRuntime` when:
10
11
 
11
- **Key differences from `LocalRuntime`:**
12
+ - You already keep messages in redux, zustand, tanstack-query, or another store, and want to keep them there.
13
+ - You want full control over message state, persistence, and synchronization.
14
+ - You have a custom message format and need automatic conversion to assistant-ui's format.
12
15
 
13
- - **You own the state** - Full control over message state, thread management, and persistence logic
14
- - **Bring your own state management** - Works with Redux, Zustand, TanStack Query, or any React state library
15
- - **Custom message formats** - Use your backend's message structure with automatic conversion
16
+ If you do not have an existing store, use [`LocalRuntime`](/docs/runtimes/custom/local-runtime) instead; it is lower-friction.
16
17
 
17
- <Callout type="warn">
18
- `ExternalStoreRuntime` gives you total control over state (persist, sync,
19
- share), but you must wire up every callback.
20
- </Callout>
18
+ ## Architecture
21
19
 
22
- ## Example Implementation
20
+ ```mermaid
21
+ graph TD
22
+ A[Your state] -->|messages| B[ExternalStoreAdapter]
23
+ B --> C[ExternalStoreRuntime]
24
+ C --> D[assistant-ui components]
25
+ D -->|user actions| B
26
+ B -->|state updates| A
27
+ ```
23
28
 
24
- ```tsx twoslash title="app/MyRuntimeProvider.tsx"
25
- type MyMessage = {
26
- role: "user" | "assistant";
27
- content: string;
28
- };
29
- const backendApi = async (input: string): Promise<MyMessage> => {
30
- return { role: "assistant", content: "Hello, world!" };
31
- };
29
+ Key idea: you own the state, the adapter translates between your format and assistant-ui's. UI features are capability-based; if you provide `setMessages`, branching turns on; if you provide `onEdit`, editing turns on; etc.
30
+
31
+ ## Quickstart
32
+
33
+ <Steps>
34
+ <Step>
35
+
36
+ ### Install
37
+
38
+ <InstallCommand npm={["@assistant-ui/react"]} />
39
+
40
+ </Step>
41
+ <Step>
42
+
43
+ ### Create the runtime provider
44
+
45
+ ```tsx title="app/MyRuntimeProvider.tsx"
46
+ "use client";
32
47
 
33
- // ---cut---
34
48
  import { useState, ReactNode } from "react";
35
49
  import {
36
50
  useExternalStoreRuntime,
@@ -39,37 +53,33 @@ import {
39
53
  AssistantRuntimeProvider,
40
54
  } from "@assistant-ui/react";
41
55
 
42
- const convertMessage = (message: MyMessage): ThreadMessageLike => {
43
- return {
44
- role: message.role,
45
- content: [{ type: "text", text: message.content }],
46
- };
56
+ type MyMessage = { role: "user" | "assistant"; content: string };
57
+
58
+ const convertMessage = (message: MyMessage): ThreadMessageLike => ({
59
+ role: message.role,
60
+ content: [{ type: "text", text: message.content }],
61
+ });
62
+
63
+ const backendApi = async (input: string): Promise<MyMessage> => {
64
+ return { role: "assistant", content: "Hello, world!" };
47
65
  };
48
66
 
49
67
  export function MyRuntimeProvider({
50
68
  children,
51
- }: Readonly<{
52
- children: ReactNode;
53
- }>) {
69
+ }: Readonly<{ children: ReactNode }>) {
54
70
  const [isRunning, setIsRunning] = useState(false);
55
71
  const [messages, setMessages] = useState<MyMessage[]>([]);
56
72
 
57
73
  const onNew = async (message: AppendMessage) => {
58
- if (message.content[0]?.type !== "text")
74
+ if (message.content[0]?.type !== "text") {
59
75
  throw new Error("Only text messages are supported");
60
-
76
+ }
61
77
  const input = message.content[0].text;
62
- setMessages((currentConversation) => [
63
- ...currentConversation,
64
- { role: "user", content: input },
65
- ]);
78
+ setMessages((prev) => [...prev, { role: "user", content: input }]);
66
79
 
67
80
  setIsRunning(true);
68
- const assistantMessage = await backendApi(input);
69
- setMessages((currentConversation) => [
70
- ...currentConversation,
71
- assistantMessage,
72
- ]);
81
+ const assistant = await backendApi(input);
82
+ setMessages((prev) => [...prev, assistant]);
73
83
  setIsRunning(false);
74
84
  };
75
85
 
@@ -88,159 +98,32 @@ export function MyRuntimeProvider({
88
98
  }
89
99
  ```
90
100
 
91
- ## When to Use
92
-
93
- Use `ExternalStoreRuntime` if you need:
94
-
95
- - **Full control over message state** - Manage messages with Redux, Zustand, TanStack Query, or any React state management library
96
- - **Custom multi-thread implementation** - Build your own thread management system with custom storage
97
- - **Integration with existing state** - Keep chat state in your existing state management solution
98
- - **Custom message formats** - Use your backend's message structure with automatic conversion
99
- - **Complex synchronization** - Sync messages with external data sources, databases, or multiple clients
100
- - **Custom persistence logic** - Implement your own storage patterns and caching strategies
101
-
102
- ## Key Features
103
-
104
- <Cards>
105
- <Card
106
- title="State Management Integration"
107
- description="Works seamlessly with Redux, Zustand, TanStack Query, and more"
108
- />
109
- <Card
110
- title="Message Conversion"
111
- description="Automatic conversion between your message format and assistant-ui's format"
112
- />
113
- <Card
114
- title="Real-time Streaming"
115
- description="Built-in support for streaming responses and progressive updates"
116
- />
117
- <Card
118
- title="Thread Management"
119
- description="Multi-conversation support with archiving and thread switching"
120
- />
121
- </Cards>
122
-
123
- ## Architecture
101
+ </Step>
102
+ <Step>
124
103
 
125
- ### How It Works
104
+ ### Use in your app
126
105
 
127
- `ExternalStoreRuntime` acts as a bridge between your state management and assistant-ui:
106
+ ```tsx title="app/page.tsx"
107
+ import { Thread } from "@/components/assistant-ui/thread";
108
+ import { MyRuntimeProvider } from "./MyRuntimeProvider";
128
109
 
129
- ```mermaid
130
- graph TD
131
- A[Your State Management] -->|messages| B[ExternalStoreAdapter]
132
- B --> C[ExternalStoreRuntime]
133
- C --> D[assistant-ui Components]
134
- D -->|user actions| B
135
- B -->|state updates| A
110
+ export default function Page() {
111
+ return (
112
+ <MyRuntimeProvider>
113
+ <Thread />
114
+ </MyRuntimeProvider>
115
+ );
116
+ }
136
117
  ```
137
118
 
138
- ### Key Concepts
139
-
140
- 1. **State Ownership** - You own and control all message state
141
- 2. **Adapter Pattern** - The adapter translates between your state and assistant-ui
142
- 3. **Capability-Based Features** - UI features are enabled based on which handlers you provide
143
- 4. **Message Conversion** - Automatic conversion between your message format and assistant-ui's format
144
- 5. **Optimistic Updates** - Built-in handling for streaming and loading states
145
-
146
- ## Getting Started
147
-
148
- <Steps>
149
- <Step>
150
- ### Install Dependencies
151
-
152
- <InstallCommand npm={["@assistant-ui/react"]} />
153
-
154
- </Step>
155
-
156
- <Step>
157
- ### Create Runtime Provider
158
-
159
- ```tsx title="app/MyRuntimeProvider.tsx"
160
- "use client";
161
-
162
- import { ThreadMessageLike } from "@assistant-ui/react";
163
- import { AppendMessage } from "@assistant-ui/react";
164
- import {
165
- AssistantRuntimeProvider,
166
- useExternalStoreRuntime,
167
- } from "@assistant-ui/react";
168
- import { useState } from "react";
169
-
170
- const convertMessage = (message: ThreadMessageLike, idx: number) => {
171
- return message;
172
- };
173
-
174
- export function MyRuntimeProvider({
175
- children,
176
- }: Readonly<{
177
- children: React.ReactNode;
178
- }>) {
179
- const [messages, setMessages] = useState<readonly ThreadMessageLike[]>([]);
180
-
181
- const onNew = async (message: AppendMessage) => {
182
- if (message.content.length !== 1 || message.content[0]?.type !== "text")
183
- throw new Error("Only text content is supported");
184
-
185
- const userMessage: ThreadMessageLike = {
186
- role: "user",
187
- content: [{ type: "text", text: message.content[0].text }],
188
- };
189
- setMessages((currentMessages) => [...currentMessages, userMessage]);
190
-
191
- // normally you would perform an API call here to get the assistant response
192
- await new Promise((resolve) => setTimeout(resolve, 1000));
193
-
194
- const assistantMessage: ThreadMessageLike = {
195
- role: "assistant",
196
- content: [{ type: "text", text: "Hello, world!" }],
197
- };
198
- setMessages((currentMessages) => [...currentMessages, assistantMessage]);
199
- };
200
-
201
- const runtime = useExternalStoreRuntime<ThreadMessageLike>({
202
- messages,
203
- setMessages,
204
- onNew,
205
- convertMessage,
206
- });
207
-
208
- return (
209
- <AssistantRuntimeProvider runtime={runtime}>
210
- {children}
211
- </AssistantRuntimeProvider>
212
- );
213
- }
214
- ```
215
-
216
- </Step>
217
-
218
- <Step>
219
- ### Use in Your App
220
-
221
- ```tsx title="app/page.tsx"
222
- import { Thread } from "@/components/assistant-ui/thread";
223
- import { MyRuntimeProvider } from "./MyRuntimeProvider";
224
-
225
- export default function Page() {
226
- return (
227
- <MyRuntimeProvider>
228
- <Thread />
229
- </MyRuntimeProvider>
230
- );
231
- }
232
- ```
233
-
234
- </Step>
119
+ </Step>
235
120
  </Steps>
236
121
 
237
- ## Implementation Patterns
122
+ ## Message conversion
238
123
 
239
- ### Message Conversion
124
+ Two approaches.
240
125
 
241
- Two approaches for converting your message format:
242
-
243
- #### 1. Simple Conversion (Recommended)
126
+ ### Inline `convertMessage`
244
127
 
245
128
  ```tsx
246
129
  const convertMessage = (message: MyMessage): ThreadMessageLike => ({
@@ -257,9 +140,9 @@ const runtime = useExternalStoreRuntime({
257
140
  });
258
141
  ```
259
142
 
260
- #### 2. Advanced Conversion with `useExternalMessageConverter`
143
+ ### `useExternalMessageConverter` (with join strategy)
261
144
 
262
- For complex scenarios with performance optimization:
145
+ For performance optimization or when you need to merge adjacent assistant messages:
263
146
 
264
147
  ```tsx
265
148
  import { useExternalMessageConverter } from "@assistant-ui/react";
@@ -269,98 +152,53 @@ const convertedMessages = useExternalMessageConverter({
269
152
  role: message.role,
270
153
  content: [{ type: "text", text: message.text }],
271
154
  id: message.id,
272
- createdAt: new Date(message.timestamp),
273
155
  }),
274
156
  messages,
275
157
  isRunning: false,
276
- joinStrategy: "concat-content", // Merge adjacent assistant messages
158
+ joinStrategy: "concat-content", // merges adjacent assistant messages
277
159
  });
278
160
 
279
161
  const runtime = useExternalStoreRuntime({
280
162
  messages: convertedMessages,
281
163
  onNew,
282
- // No convertMessage needed - already converted
283
164
  });
284
165
  ```
285
166
 
286
- ### Join Strategy
287
-
288
- Controls how adjacent assistant messages are combined:
289
-
290
- - **`concat-content`** (default): Merges adjacent assistant messages into one
291
- - **`none`**: Keeps all messages separate
292
-
293
- This is useful when your backend sends multiple message chunks that should appear as a single message in the UI.
294
-
295
- <Callout type="info">
296
- `useExternalMessageConverter` provides performance optimization for complex
297
- message conversion scenarios. For simpler cases, consider using the basic
298
- `convertMessage` approach shown above.
299
- </Callout>
300
-
301
- ### Essential Handlers
302
-
303
- #### Basic Chat (onNew only)
304
-
305
- ```tsx
306
- const runtime = useExternalStoreRuntime({
307
- messages,
308
- onNew: async (message) => {
309
- // Add user message to state
310
- const userMsg = { role: "user", content: message.content };
311
- setMessages([...messages, userMsg]);
312
-
313
- // Get AI response
314
- const response = await callAI(message);
315
- setMessages([...messages, userMsg, response]);
316
- },
317
- });
318
- ```
167
+ `joinStrategy` controls how adjacent assistant messages combine: `concat-content` (default) merges them into one; `none` keeps them separate.
319
168
 
320
- #### Full-Featured Chat
169
+ ## Handler matrix
321
170
 
322
- ```tsx
323
- const runtime = useExternalStoreRuntime({
324
- messages,
325
- setMessages, // Enables branch switching
326
- onNew, // Required
327
- onEdit, // Enables message editing
328
- onReload, // Enables regeneration
329
- onCancel, // Enables cancellation
330
- });
331
- ```
171
+ Each handler enables a specific UI feature.
332
172
 
333
- <Callout type="info">
334
- Each handler you provide enables specific UI features: - `setMessages` →
335
- Branch switching - `onEdit` → Message editing - `onReload` → Regenerate button
336
- - `onCancel` → Cancel button during generation
337
- </Callout>
173
+ | Handler | Enables |
174
+ | --- | --- |
175
+ | `onNew` | Sending new user messages (required) |
176
+ | `setMessages` | Branch switching |
177
+ | `onEdit` | Message edit button |
178
+ | `onReload` | Regenerate button |
179
+ | `onCancel` | Cancel button while generating |
180
+ | `onAddToolResult` | Client-side tool result handoff |
338
181
 
339
- ### Streaming Responses
182
+ ## Streaming responses
340
183
 
341
- Implement real-time streaming with progressive updates:
184
+ Stream by mutating the assistant message in place:
342
185
 
343
186
  ```tsx
344
187
  const onNew = async (message: AppendMessage) => {
345
- // Add user message
346
- const userMessage: ThreadMessageLike = {
188
+ const userMsg: ThreadMessageLike = {
347
189
  role: "user",
348
190
  content: message.content,
349
191
  id: generateId(),
350
192
  };
351
- setMessages((prev) => [...prev, userMessage]);
193
+ setMessages((prev) => [...prev, userMsg]);
352
194
 
353
- // Create placeholder for assistant message
354
195
  setIsRunning(true);
355
196
  const assistantId = generateId();
356
- const assistantMessage: ThreadMessageLike = {
357
- role: "assistant",
358
- content: [{ type: "text", text: "" }],
359
- id: assistantId,
360
- };
361
- setMessages((prev) => [...prev, assistantMessage]);
197
+ setMessages((prev) => [
198
+ ...prev,
199
+ { role: "assistant", content: [{ type: "text", text: "" }], id: assistantId },
200
+ ]);
362
201
 
363
- // Stream response
364
202
  const stream = await api.streamChat(message);
365
203
  for await (const chunk of stream) {
366
204
  setMessages((prev) =>
@@ -369,10 +207,7 @@ const onNew = async (message: AppendMessage) => {
369
207
  ? {
370
208
  ...m,
371
209
  content: [
372
- {
373
- type: "text",
374
- text: (m.content[0] as any).text + chunk,
375
- },
210
+ { type: "text", text: (m.content[0] as any).text + chunk },
376
211
  ],
377
212
  }
378
213
  : m,
@@ -383,52 +218,30 @@ const onNew = async (message: AppendMessage) => {
383
218
  };
384
219
  ```
385
220
 
386
- ### Message Editing
387
-
388
- Enable message editing by implementing the `onEdit` handler:
389
-
390
- <Callout type="info">
391
- You can implement `onEdit(editedMessage)` to handle user-initiated edits in
392
- your external store. This enables features like "edit and re-run" on your
393
- backend.
394
- </Callout>
221
+ ## Message editing
395
222
 
396
223
  ```tsx
397
224
  const onEdit = async (message: AppendMessage) => {
398
- // Find the index where to insert the edited message
399
225
  const index = messages.findIndex((m) => m.id === message.parentId) + 1;
400
-
401
- // Keep messages up to the parent
402
226
  const newMessages = [...messages.slice(0, index)];
403
-
404
- // Add the edited message
405
- const editedMessage: ThreadMessageLike = {
227
+ newMessages.push({
406
228
  role: "user",
407
229
  content: message.content,
408
- id: message.id || generateId(),
409
- };
410
- newMessages.push(editedMessage);
411
-
230
+ id: message.id ?? generateId(),
231
+ });
412
232
  setMessages(newMessages);
413
233
 
414
- // Generate new response
415
234
  setIsRunning(true);
416
235
  const response = await api.chat(message);
417
- newMessages.push({
418
- role: "assistant",
419
- content: response.content,
420
- id: generateId(),
421
- });
236
+ newMessages.push({ role: "assistant", content: response.content, id: generateId() });
422
237
  setMessages(newMessages);
423
238
  setIsRunning(false);
424
239
  };
425
240
  ```
426
241
 
427
- ### Branching Support
242
+ ## Branching
428
243
 
429
- The `messages` array path assumes a linear conversation — each message's parent is the previous message. To support branching (e.g. regenerating responses creates alternative branches), use `ExportedMessageRepository.fromBranchableArray()` combined with `thread.import()`.
430
-
431
- Each message must have an explicit `id` and `parentId`. Messages with the same `parentId` create branches:
244
+ The linear `messages` array assumes each message's parent is the previous one. For branching (e.g. multiple regenerations), use `ExportedMessageRepository.fromBranchableArray()` and import via `thread.import()`:
432
245
 
433
246
  ```tsx
434
247
  import {
@@ -436,60 +249,45 @@ import {
436
249
  useExternalStoreRuntime,
437
250
  } from "@assistant-ui/react";
438
251
 
439
- // Your messages from the backend, each with an id and parentId
440
252
  const backendMessages = [
441
253
  { id: "user-1", role: "user", content: "Hello", parentId: null },
442
254
  { id: "asst-1", role: "assistant", content: "Hi!", parentId: "user-1" },
443
- // A second response to the same user message = a branch
444
- { id: "asst-2", role: "assistant", content: "Hey!", parentId: "user-1" },
255
+ { id: "asst-2", role: "assistant", content: "Hey!", parentId: "user-1" }, // branch
445
256
  ];
446
257
 
447
- // Convert to ExportedMessageRepository
448
258
  const repo = ExportedMessageRepository.fromBranchableArray(
449
259
  backendMessages.map((m) => ({
450
260
  message: { id: m.id, role: m.role, content: m.content },
451
261
  parentId: m.parentId,
452
262
  })),
453
- { headId: "asst-1" }, // which branch to display initially
263
+ { headId: "asst-1" },
454
264
  );
455
265
 
456
- // Import into the runtime
457
266
  runtime.thread.import(repo);
458
267
  ```
459
268
 
460
- <Callout type="warn">
461
- Messages in the array must be ordered so that parents appear before their
462
- children. Each message **must** have an `id` field set.
463
- </Callout>
269
+ Each message must have an explicit `id` and `parentId`; messages with the same `parentId` create branches. Parents must appear before children in the array.
464
270
 
465
- ### Tool Calling
271
+ ## Tool calling
466
272
 
467
- Support tool calls with proper result handling:
273
+ Handle tool results by updating the matching tool-call entry:
468
274
 
469
275
  ```tsx
470
276
  const onAddToolResult = (options: AddToolResultOptions) => {
471
277
  setMessages((prev) =>
472
- prev.map((message) => {
473
- if (message.id === options.messageId) {
474
- // Update the specific tool call with its result
475
- return {
476
- ...message,
477
- content: message.content.map((part) => {
478
- if (
278
+ prev.map((message) =>
279
+ message.id === options.messageId
280
+ ? {
281
+ ...message,
282
+ content: message.content.map((part) =>
479
283
  part.type === "tool-call" &&
480
284
  part.toolCallId === options.toolCallId
481
- ) {
482
- return {
483
- ...part,
484
- result: options.result,
485
- };
486
- }
487
- return part;
488
- }),
489
- };
490
- }
491
- return message;
492
- }),
285
+ ? { ...part, result: options.result }
286
+ : part,
287
+ ),
288
+ }
289
+ : message,
290
+ ),
493
291
  );
494
292
  };
495
293
 
@@ -497,336 +295,38 @@ const runtime = useExternalStoreRuntime({
497
295
  messages,
498
296
  onNew,
499
297
  onAddToolResult,
500
- // ... other props
501
298
  });
502
299
  ```
503
300
 
504
- #### Automatic Tool Result Matching
505
-
506
- The runtime automatically matches tool results with their corresponding tool calls. When messages are converted and joined:
507
-
508
- 1. **Tool Call Tracking** - The runtime tracks tool calls by their `toolCallId`
509
- 2. **Result Association** - Tool results are automatically associated with their corresponding calls
510
- 3. **Message Grouping** - Related tool messages are intelligently grouped together
301
+ The runtime automatically matches tool results to their tool calls by `toolCallId` and groups related messages for display.
511
302
 
512
- ```tsx
513
- // Example: Tool call and result in separate messages
514
- const messages = [
515
- {
516
- role: "assistant",
517
- content: [
518
- {
519
- type: "tool-call",
520
- toolCallId: "call_123",
521
- toolName: "get_weather",
522
- args: { location: "San Francisco" },
523
- },
524
- ],
525
- },
526
- {
527
- role: "tool",
528
- content: [
529
- {
530
- type: "tool-result",
531
- toolCallId: "call_123",
532
- result: { temperature: 72, condition: "sunny" },
533
- },
534
- ],
535
- },
536
- ];
537
-
538
- // These are automatically matched and grouped by the runtime
539
- ```
540
-
541
- ### File Attachments
303
+ ## Attachments
542
304
 
543
- Enable file uploads with the attachment adapter:
305
+ Attachments use the standard adapter contract, see [adapters](/docs/runtimes/concepts/adapters#attachment-adapter):
544
306
 
545
307
  ```tsx
546
- const attachmentAdapter: AttachmentAdapter = {
547
- accept: "image/*,application/pdf,.txt,.md",
548
- async add({ file }) {
549
- // Upload file to your server
550
- const formData = new FormData();
551
- formData.append("file", file);
552
-
553
- const response = await fetch("/api/upload", {
554
- method: "POST",
555
- body: formData,
556
- });
557
-
558
- const { id } = await response.json();
559
- return {
560
- id,
561
- type: "document",
562
- name: file.name,
563
- file,
564
- status: { type: "requires-action", reason: "composer-send" },
565
- };
566
- },
567
- async remove(attachment) {
568
- // Remove file from server
569
- await fetch(`/api/upload/${attachment.id}`, {
570
- method: "DELETE",
571
- });
572
- },
573
- async send(attachment) {
574
- // Convert pending attachment to complete attachment when message is sent
575
- return {
576
- ...attachment,
577
- status: { type: "complete" },
578
- content: [{ type: "text", text: `File: ${attachment.name}` }],
579
- };
580
- },
581
- };
582
-
583
308
  const runtime = useExternalStoreRuntime({
584
309
  messages,
585
310
  onNew,
586
- adapters: {
587
- attachments: attachmentAdapter,
588
- },
589
- });
590
- ```
591
-
592
- ### Thread Management
593
-
594
- #### Managing Thread Context
595
-
596
- When implementing multi-thread support with `ExternalStoreRuntime`, you need to carefully manage thread context across your application. Here's a comprehensive approach:
597
-
598
- ```tsx
599
- // Create a context for thread management
600
- const ThreadContext = createContext<{
601
- currentThreadId: string;
602
- setCurrentThreadId: (id: string) => void;
603
- threads: Map<string, ThreadMessageLike[]>;
604
- setThreads: React.Dispatch<
605
- React.SetStateAction<Map<string, ThreadMessageLike[]>>
606
- >;
607
- }>({
608
- currentThreadId: "default",
609
- setCurrentThreadId: () => {},
610
- threads: new Map(),
611
- setThreads: () => {},
311
+ adapters: { attachments: myAttachmentAdapter },
612
312
  });
613
-
614
- // Thread provider component
615
- export function ThreadProvider({ children }: { children: ReactNode }) {
616
- const [threads, setThreads] = useState<Map<string, ThreadMessageLike[]>>(
617
- new Map([["default", []]]),
618
- );
619
- const [currentThreadId, setCurrentThreadId] = useState("default");
620
-
621
- return (
622
- <ThreadContext.Provider
623
- value={{ currentThreadId, setCurrentThreadId, threads, setThreads }}
624
- >
625
- {children}
626
- </ThreadContext.Provider>
627
- );
628
- }
629
-
630
- // Hook for accessing thread context
631
- export function useThreadContext() {
632
- const context = useContext(ThreadContext);
633
- if (!context) {
634
- throw new Error("useThreadContext must be used within ThreadProvider");
635
- }
636
- return context;
637
- }
638
- ```
639
-
640
- #### Complete Thread Implementation
641
-
642
- Here's a full implementation with proper context management:
643
-
644
- ```tsx
645
- function ChatWithThreads() {
646
- const { currentThreadId, setCurrentThreadId, threads, setThreads } =
647
- useThreadContext();
648
- const [threadList, setThreadList] = useState<ExternalStoreThreadData[]>([
649
- { id: "default", status: "regular", title: "New Chat" },
650
- ]);
651
-
652
- // Get messages for current thread
653
- const currentMessages = threads.get(currentThreadId) || [];
654
-
655
- const threadListAdapter: ExternalStoreThreadListAdapter = {
656
- threadId: currentThreadId,
657
- threads: threadList.filter((t) => t.status === "regular"),
658
- archivedThreads: threadList.filter((t) => t.status === "archived"),
659
-
660
- onSwitchToNewThread: () => {
661
- const newId = `thread-${Date.now()}`;
662
- setThreadList((prev) => [
663
- ...prev,
664
- {
665
- id: newId,
666
- status: "regular",
667
- title: "New Chat",
668
- },
669
- ]);
670
- setThreads((prev) => new Map(prev).set(newId, []));
671
- setCurrentThreadId(newId);
672
- },
673
-
674
- onSwitchToThread: (threadId) => {
675
- setCurrentThreadId(threadId);
676
- },
677
-
678
- onRename: (threadId, newTitle) => {
679
- setThreadList((prev) =>
680
- prev.map((t) =>
681
- t.id === threadId ? { ...t, title: newTitle } : t,
682
- ),
683
- );
684
- },
685
-
686
- onArchive: (threadId) => {
687
- setThreadList((prev) =>
688
- prev.map((t) =>
689
- t.id === threadId ? { ...t, status: "archived" } : t,
690
- ),
691
- );
692
- },
693
-
694
- onDelete: (threadId) => {
695
- setThreadList((prev) => prev.filter((t) => t.id !== threadId));
696
- setThreads((prev) => {
697
- const next = new Map(prev);
698
- next.delete(threadId);
699
- return next;
700
- });
701
- if (currentThreadId === threadId) {
702
- setCurrentThreadId("default");
703
- }
704
- },
705
- };
706
-
707
- const runtime = useExternalStoreRuntime({
708
- messages: currentMessages,
709
- setMessages: (messages) => {
710
- setThreads((prev) => new Map(prev).set(currentThreadId, messages));
711
- },
712
- onNew: async (message) => {
713
- // Handle new message for current thread
714
- // Your implementation here
715
- },
716
- adapters: {
717
- threadList: threadListAdapter,
718
- },
719
- });
720
-
721
- return (
722
- <AssistantRuntimeProvider runtime={runtime}>
723
- <ThreadList />
724
- <Thread />
725
- </AssistantRuntimeProvider>
726
- );
727
- }
728
-
729
- // App component with proper context wrapping
730
- export function App() {
731
- return (
732
- <ThreadProvider>
733
- <ChatWithThreads />
734
- </ThreadProvider>
735
- );
736
- }
737
- ```
738
-
739
- #### Thread Context Best Practices
740
-
741
- <Callout type="info">
742
- **Critical**: When using `ExternalStoreRuntime` with threads, the
743
- `currentThreadId` must be consistent across all components and handlers.
744
- Mismatched thread IDs will cause messages to appear in wrong threads or
745
- disappear entirely.
746
- </Callout>
747
-
748
- 1. **Centralize Thread State**: Always use a context or global state management solution to ensure thread ID consistency:
749
-
750
- ```tsx
751
- // ❌ Bad: Local state in multiple components
752
- function ThreadList() {
753
- const [currentThreadId, setCurrentThreadId] = useState("default");
754
- // This won't sync with the runtime!
755
- }
756
-
757
- // ✅ Good: Shared context
758
- function ThreadList() {
759
- const { currentThreadId, setCurrentThreadId } = useThreadContext();
760
- // Thread ID is synchronized everywhere
761
- }
762
- ```
763
-
764
- 2. **Sync Thread Changes**: Ensure all thread-related operations update both the thread ID and messages:
765
-
766
- ```tsx
767
- // ❌ Bad: Only updating thread ID
768
- onSwitchToThread: (threadId) => {
769
- setCurrentThreadId(threadId);
770
- // Messages won't update!
771
- };
772
-
773
- // ✅ Good: Complete state update
774
- onSwitchToThread: (threadId) => {
775
- setCurrentThreadId(threadId);
776
- // Messages automatically update via currentMessages = threads.get(currentThreadId)
777
- };
778
- ```
779
-
780
- 3. **Handle Edge Cases**: Always provide fallbacks for missing threads:
781
-
782
- ```tsx
783
- // Ensure thread always exists
784
- const currentMessages = threads.get(currentThreadId) || [];
785
-
786
- // Initialize new threads properly
787
- const initializeThread = (threadId: string) => {
788
- if (!threads.has(threadId)) {
789
- setThreads((prev) => new Map(prev).set(threadId, []));
790
- }
791
- };
792
313
  ```
793
314
 
794
- 4. **Persist Thread State**: For production apps, sync thread state with your backend:
315
+ ## Multi-thread
795
316
 
796
- ```tsx
797
- // Save thread state to backend
798
- useEffect(() => {
799
- const saveThread = async () => {
800
- await api.saveThread(currentThreadId, threads.get(currentThreadId) || []);
801
- };
802
-
803
- const debounced = debounce(saveThread, 1000);
804
- debounced();
805
-
806
- return () => debounced.cancel();
807
- }, [currentThreadId, threads]);
808
- ```
317
+ `ExternalStoreRuntime` uses `ExternalStoreThreadListAdapter` (synchronous, inline). See [threads](/docs/runtimes/concepts/threads#externalstorethreadlistadapter) for the contract and best practices on keeping `currentThreadId` in sync with your store.
809
318
 
810
- ## Integration Examples
319
+ ## Integration examples
811
320
 
812
- ### Redux Integration
321
+ ### Redux
813
322
 
814
323
  ```tsx title="app/chatSlice.ts"
815
- // Using Redux Toolkit (recommended)
816
324
  import { createSlice, PayloadAction } from "@reduxjs/toolkit";
817
325
  import { ThreadMessageLike } from "@assistant-ui/react";
818
326
 
819
- interface ChatState {
820
- messages: ThreadMessageLike[];
821
- isRunning: boolean;
822
- }
823
-
824
327
  const chatSlice = createSlice({
825
328
  name: "chat",
826
- initialState: {
827
- messages: [] as ThreadMessageLike[],
828
- isRunning: false,
829
- },
329
+ initialState: { messages: [] as ThreadMessageLike[], isRunning: false },
830
330
  reducers: {
831
331
  setMessages: (state, action: PayloadAction<ThreadMessageLike[]>) => {
832
332
  state.messages = action.payload;
@@ -841,23 +341,15 @@ const chatSlice = createSlice({
841
341
  });
842
342
 
843
343
  export const { setMessages, addMessage, setIsRunning } = chatSlice.actions;
844
- export const selectMessages = (state: RootState) => state.chat.messages;
845
- export const selectIsRunning = (state: RootState) => state.chat.isRunning;
846
- export default chatSlice.reducer;
344
+ ```
847
345
 
848
- // ReduxRuntimeProvider.tsx
346
+ ```tsx title="app/ReduxRuntimeProvider.tsx"
849
347
  import { useSelector, useDispatch } from "react-redux";
850
- import {
851
- selectMessages,
852
- selectIsRunning,
853
- addMessage,
854
- setMessages,
855
- setIsRunning,
856
- } from "./chatSlice";
348
+ import { useExternalStoreRuntime, AssistantRuntimeProvider } from "@assistant-ui/react";
857
349
 
858
350
  export function ReduxRuntimeProvider({ children }) {
859
- const messages = useSelector(selectMessages);
860
- const isRunning = useSelector(selectIsRunning);
351
+ const messages = useSelector((s: RootState) => s.chat.messages);
352
+ const isRunning = useSelector((s: RootState) => s.chat.isRunning);
861
353
  const dispatch = useDispatch();
862
354
 
863
355
  const runtime = useExternalStoreRuntime({
@@ -865,7 +357,6 @@ export function ReduxRuntimeProvider({ children }) {
865
357
  isRunning,
866
358
  setMessages: (messages) => dispatch(setMessages(messages)),
867
359
  onNew: async (message) => {
868
- // Add user message
869
360
  dispatch(
870
361
  addMessage({
871
362
  role: "user",
@@ -874,8 +365,6 @@ export function ReduxRuntimeProvider({ children }) {
874
365
  createdAt: new Date(),
875
366
  }),
876
367
  );
877
-
878
- // Generate response
879
368
  dispatch(setIsRunning(true));
880
369
  const response = await api.chat(message);
881
370
  dispatch(
@@ -898,10 +387,9 @@ export function ReduxRuntimeProvider({ children }) {
898
387
  }
899
388
  ```
900
389
 
901
- ### Zustand Integration (v5)
390
+ ### Zustand
902
391
 
903
392
  ```tsx title="app/chatStore.ts"
904
- // Using Zustand v5 with TypeScript
905
393
  import { create } from "zustand";
906
394
  import { immer } from "zustand/middleware/immer";
907
395
  import { ThreadMessageLike } from "@assistant-ui/react";
@@ -912,53 +400,32 @@ interface ChatState {
912
400
  addMessage: (message: ThreadMessageLike) => void;
913
401
  setMessages: (messages: ThreadMessageLike[]) => void;
914
402
  setIsRunning: (isRunning: boolean) => void;
915
- updateMessage: (id: string, updates: Partial<ThreadMessageLike>) => void;
916
403
  }
917
404
 
918
- // Zustand v5 requires the extra parentheses for TypeScript
919
- const useChatStore = create<ChatState>()(
405
+ export const useChatStore = create<ChatState>()(
920
406
  immer((set) => ({
921
407
  messages: [],
922
408
  isRunning: false,
923
-
924
- addMessage: (message) =>
925
- set((state) => {
926
- state.messages.push(message);
927
- }),
928
-
929
- setMessages: (messages) =>
930
- set((state) => {
931
- state.messages = messages;
932
- }),
933
-
934
- setIsRunning: (isRunning) =>
935
- set((state) => {
936
- state.isRunning = isRunning;
937
- }),
938
-
939
- updateMessage: (id, updates) =>
940
- set((state) => {
941
- const index = state.messages.findIndex((m) => m.id === id);
942
- if (index !== -1) {
943
- Object.assign(state.messages[index], updates);
944
- }
945
- }),
409
+ addMessage: (message) => set((s) => { s.messages.push(message); }),
410
+ setMessages: (messages) => set((s) => { s.messages = messages; }),
411
+ setIsRunning: (isRunning) => set((s) => { s.isRunning = isRunning; }),
946
412
  })),
947
413
  );
414
+ ```
948
415
 
949
- // ZustandRuntimeProvider.tsx
416
+ ```tsx title="app/ZustandRuntimeProvider.tsx"
950
417
  import { useShallow } from "zustand/shallow";
418
+ import { useExternalStoreRuntime, AssistantRuntimeProvider } from "@assistant-ui/react";
951
419
 
952
420
  export function ZustandRuntimeProvider({ children }) {
953
- // Use useShallow to prevent unnecessary re-renders
954
421
  const { messages, isRunning, addMessage, setMessages, setIsRunning } =
955
422
  useChatStore(
956
- useShallow((state) => ({
957
- messages: state.messages,
958
- isRunning: state.isRunning,
959
- addMessage: state.addMessage,
960
- setMessages: state.setMessages,
961
- setIsRunning: state.setIsRunning,
423
+ useShallow((s) => ({
424
+ messages: s.messages,
425
+ isRunning: s.isRunning,
426
+ addMessage: s.addMessage,
427
+ setMessages: s.setMessages,
428
+ setIsRunning: s.setIsRunning,
962
429
  })),
963
430
  );
964
431
 
@@ -967,21 +434,18 @@ export function ZustandRuntimeProvider({ children }) {
967
434
  isRunning,
968
435
  setMessages,
969
436
  onNew: async (message) => {
970
- // Add user message
971
437
  addMessage({
972
438
  role: "user",
973
439
  content: message.content,
974
440
  id: `msg-${Date.now()}`,
975
441
  createdAt: new Date(),
976
442
  });
977
-
978
- // Generate response
979
443
  setIsRunning(true);
980
444
  const response = await api.chat(message);
981
445
  addMessage({
982
446
  role: "assistant",
983
447
  content: response.content,
984
- id: `msg-${Date.now()}-assistant`,
448
+ id: `msg-${Date.now()}-a`,
985
449
  createdAt: new Date(),
986
450
  });
987
451
  setIsRunning(false);
@@ -996,98 +460,56 @@ export function ZustandRuntimeProvider({ children }) {
996
460
  }
997
461
  ```
998
462
 
999
- ### TanStack Query Integration
463
+ ### TanStack Query
1000
464
 
1001
- ```tsx title="app/chatQueries.ts"
1002
- // Using TanStack Query v5 with TypeScript
465
+ ```tsx
1003
466
  import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
1004
- import { ThreadMessageLike, AppendMessage } from "@assistant-ui/react";
467
+ import { useExternalStoreRuntime } from "@assistant-ui/react";
1005
468
 
1006
- // Query key factory pattern
1007
- export const messageKeys = {
469
+ const messageKeys = {
1008
470
  all: ["messages"] as const,
1009
471
  thread: (threadId: string) => [...messageKeys.all, threadId] as const,
1010
472
  };
1011
473
 
1012
- // TanStackQueryRuntimeProvider.tsx
1013
474
  export function TanStackQueryRuntimeProvider({ children }) {
1014
475
  const queryClient = useQueryClient();
1015
- const threadId = "main"; // Or from context/props
476
+ const threadId = "main";
1016
477
 
1017
478
  const { data: messages = [] } = useQuery({
1018
479
  queryKey: messageKeys.thread(threadId),
1019
480
  queryFn: () => fetchMessages(threadId),
1020
- staleTime: 1000 * 60 * 5, // Consider data fresh for 5 minutes
1021
481
  });
1022
482
 
1023
483
  const sendMessage = useMutation({
1024
484
  mutationFn: api.chat,
1025
-
1026
- // Optimistic updates with proper TypeScript types
1027
485
  onMutate: async (message: AppendMessage) => {
1028
- // Cancel any outgoing refetches
1029
486
  await queryClient.cancelQueries({
1030
487
  queryKey: messageKeys.thread(threadId),
1031
488
  });
1032
-
1033
- // Snapshot the previous value
1034
- const previousMessages = queryClient.getQueryData<ThreadMessageLike[]>(
1035
- messageKeys.thread(threadId),
1036
- );
1037
-
1038
- // Optimistically update with typed data
1039
- const optimisticMessage: ThreadMessageLike = {
1040
- role: "user",
1041
- content: message.content,
1042
- id: `temp-${Date.now()}`,
1043
- createdAt: new Date(),
1044
- };
1045
-
1046
- queryClient.setQueryData<ThreadMessageLike[]>(
489
+ const previous = queryClient.getQueryData<ThreadMessageLike[]>(
1047
490
  messageKeys.thread(threadId),
1048
- (old = []) => [...old, optimisticMessage],
1049
491
  );
1050
-
1051
- return { previousMessages, tempId: optimisticMessage.id };
1052
- },
1053
-
1054
- onSuccess: (response, variables, context) => {
1055
- // Replace optimistic message with real data
1056
492
  queryClient.setQueryData<ThreadMessageLike[]>(
1057
493
  messageKeys.thread(threadId),
1058
- (old = []) => {
1059
- // Remove temp message and add real ones
1060
- return old
1061
- .filter((m) => m.id !== context?.tempId)
1062
- .concat([
1063
- {
1064
- role: "user",
1065
- content: variables.content,
1066
- id: `user-${Date.now()}`,
1067
- createdAt: new Date(),
1068
- },
1069
- response,
1070
- ]);
1071
- },
494
+ (old = []) => [
495
+ ...old,
496
+ {
497
+ role: "user",
498
+ content: message.content,
499
+ id: `temp-${Date.now()}`,
500
+ createdAt: new Date(),
501
+ },
502
+ ],
1072
503
  );
504
+ return { previous };
1073
505
  },
1074
-
1075
- onError: (error, variables, context) => {
1076
- // Rollback to previous messages on error
1077
- if (context?.previousMessages) {
1078
- queryClient.setQueryData(
1079
- messageKeys.thread(threadId),
1080
- context.previousMessages,
1081
- );
506
+ onError: (_err, _msg, context) => {
507
+ if (context?.previous) {
508
+ queryClient.setQueryData(messageKeys.thread(threadId), context.previous);
1082
509
  }
1083
510
  },
1084
-
1085
- onSettled: () => {
1086
- // Always refetch after error or success
1087
- queryClient.invalidateQueries({
1088
- queryKey: messageKeys.thread(threadId),
1089
- });
1090
- },
511
+ onSettled: () =>
512
+ queryClient.invalidateQueries({ queryKey: messageKeys.thread(threadId) }),
1091
513
  });
1092
514
 
1093
515
  const runtime = useExternalStoreRuntime({
@@ -1096,7 +518,6 @@ export function TanStackQueryRuntimeProvider({ children }) {
1096
518
  onNew: async (message) => {
1097
519
  await sendMessage.mutateAsync(message);
1098
520
  },
1099
- // Enable message editing
1100
521
  setMessages: (newMessages) => {
1101
522
  queryClient.setQueryData(messageKeys.thread(threadId), newMessages);
1102
523
  },
@@ -1110,103 +531,28 @@ export function TanStackQueryRuntimeProvider({ children }) {
1110
531
  }
1111
532
  ```
1112
533
 
1113
- ## Key Features
1114
-
1115
- ### Automatic Optimistic Updates
1116
-
1117
- When `isRunning` becomes true, the runtime automatically shows an optimistic assistant message:
1118
-
1119
- ```tsx
1120
- // Your code
1121
- setIsRunning(true);
1122
-
1123
- // Runtime automatically:
1124
- // 1. Shows empty assistant message with { type: "running" } status
1125
- // 2. Displays typing indicator
1126
- // 3. Updates status to { type: "complete", reason: "unknown" } when isRunning becomes false
1127
- ```
1128
-
1129
- ### Message Status Management
1130
-
1131
- Assistant messages get automatic status updates:
1132
-
1133
- - `{ type: "running" }` - When `isRunning` is true
1134
- - `{ type: "complete", reason: "unknown" }` - When `isRunning` becomes false
1135
- - `{ type: "incomplete", reason: "cancelled" }` - When cancelled via `onCancel`
1136
-
1137
- ### Tool Result Matching
1138
-
1139
- The runtime automatically matches tool results with their calls:
1140
-
1141
- ```tsx
1142
- // Tool call and result can be in separate messages
1143
- const messages = [
1144
- {
1145
- role: "assistant",
1146
- content: [
1147
- {
1148
- type: "tool-call",
1149
- toolCallId: "call_123",
1150
- toolName: "get_weather",
1151
- args: { location: "SF" },
1152
- },
1153
- ],
1154
- },
1155
- {
1156
- role: "tool",
1157
- content: [
1158
- {
1159
- type: "tool-result",
1160
- toolCallId: "call_123",
1161
- result: { temp: 72 },
1162
- },
1163
- ],
1164
- },
1165
- ];
1166
- // Runtime automatically associates these
1167
- ```
1168
-
1169
- ## Working with External Messages
534
+ ## Working with external messages
1170
535
 
1171
- ### Converting Back to Your Format
536
+ ### `getExternalStoreMessages`
1172
537
 
1173
- Use `getExternalStoreMessages` to access your original messages:
538
+ Retrieve your original message format from any assistant-ui state:
1174
539
 
1175
540
  ```tsx
1176
- import { getExternalStoreMessages } from "@assistant-ui/react";
541
+ import { getExternalStoreMessages, useAuiState } from "@assistant-ui/react";
1177
542
 
1178
- const MyComponent = () => {
543
+ function MyComponent() {
1179
544
  const originalMessages = useAuiState((s) => getExternalStoreMessages(s.message));
1180
545
  // originalMessages is MyMessage[] (your original type)
1181
- };
546
+ }
1182
547
  ```
1183
548
 
1184
- <Callout type="info">
1185
- After the chat finishes, use `getExternalStoreMessages(runtime)` to convert
1186
- back to your domain model. Refer to the API reference for return structures
1187
- and edge-case behaviors.
549
+ <Callout type="warn">
550
+ `getExternalStoreMessages` may return multiple messages for a single UI message; assistant-ui merges adjacent assistant and tool messages for display.
1188
551
  </Callout>
1189
552
 
1190
- <Callout type="warning">
1191
- `getExternalStoreMessages` may return multiple messages for a single UI
1192
- message. This happens because assistant-ui merges adjacent assistant and tool
1193
- messages for display.
1194
- </Callout>
553
+ ### `bindExternalStoreMessage`
1195
554
 
1196
- ### Message part Access
1197
-
1198
- ```tsx
1199
- const ToolUI = makeAssistantToolUI({
1200
- render: () => {
1201
- const originalMessages = useAuiState((s) => getExternalStoreMessages(s.part));
1202
- // Access original message data for this message part
1203
- },
1204
- });
1205
- ```
1206
-
1207
- ### Binding External Messages Manually
1208
-
1209
- Use `bindExternalStoreMessage` to attach your original message to a `ThreadMessage` or message part object. This is useful when you construct `ThreadMessage` objects yourself (outside of the built-in message converter) and want `getExternalStoreMessages` to work with them.
555
+ Attach your original message to a `ThreadMessage` you constructed manually (outside the built-in converter):
1210
556
 
1211
557
  ```tsx
1212
558
  import {
@@ -1214,574 +560,253 @@ import {
1214
560
  getExternalStoreMessages,
1215
561
  } from "@assistant-ui/react";
1216
562
 
1217
- // Attach your original message to a ThreadMessage
1218
563
  bindExternalStoreMessage(threadMessage, originalMessage);
1219
-
1220
- // Later, retrieve it
1221
564
  const original = getExternalStoreMessages(threadMessage);
1222
565
  ```
1223
566
 
567
+ `bindExternalStoreMessage` is a no-op if the target already has a bound message. It mutates the target in place.
568
+
1224
569
  <Callout type="warn">
1225
- This API is experimental and may change without notice.
570
+ This API is experimental and may change without notice.
1226
571
  </Callout>
1227
572
 
1228
- `bindExternalStoreMessage` is a no-op if the target already has a bound message. It mutates the target object in place.
1229
-
1230
- ## Debugging
1231
-
1232
- ### Common Debugging Scenarios
1233
-
1234
- ```tsx
1235
- // Debug message conversion
1236
- const convertMessage = (message: MyMessage): ThreadMessageLike => {
1237
- console.log("Converting message:", message);
1238
- const converted = {
1239
- role: message.role,
1240
- content: [{ type: "text", text: message.content }],
1241
- };
1242
- console.log("Converted to:", converted);
1243
- return converted;
1244
- };
1245
-
1246
- // Debug adapter calls
1247
- const onNew = async (message: AppendMessage) => {
1248
- console.log("onNew called with:", message);
1249
- // ... implementation
1250
- };
1251
-
1252
- // Enable verbose logging
1253
- const runtime = useExternalStoreRuntime({
1254
- messages,
1255
- onNew: (...args) => {
1256
- console.log("Runtime onNew:", args);
1257
- return onNew(...args);
1258
- },
1259
- // ... other props
1260
- });
1261
- ```
1262
-
1263
- ## Best Practices
1264
-
1265
- ### 1. Immutable Updates
1266
-
1267
- Always create new arrays when updating messages:
573
+ ## Best practices
1268
574
 
1269
- ```tsx
1270
- // ❌ Wrong - mutating array
1271
- messages.push(newMessage);
1272
- setMessages(messages);
1273
-
1274
- // ✅ Correct - new array
1275
- setMessages([...messages, newMessage]);
1276
- ```
575
+ 1. **Immutable updates.** Always create new arrays:
576
+ ```tsx
577
+ setMessages([...messages, newMessage]); // not messages.push(newMessage)
578
+ ```
579
+ 2. **Stable handler references.** Memoize `onNew`, `onEdit`, etc. with `useCallback` to avoid recreating the runtime.
580
+ 3. **Use `useShallow`** with zustand to prevent unnecessary re-renders.
1277
581
 
1278
- ### 2. Stable Handler References
582
+ ## Common pitfalls
1279
583
 
1280
- Memoize handlers to prevent runtime recreation:
584
+ **Edit / regenerate / cancel buttons missing.** Each requires its handler:
1281
585
 
1282
586
  ```tsx
1283
- const onNew = useCallback(
1284
- async (message: AppendMessage) => {
1285
- // Handle new message
1286
- },
1287
- [
1288
- /* dependencies */
1289
- ],
1290
- );
1291
-
1292
- const runtime = useExternalStoreRuntime({
587
+ useExternalStoreRuntime({
1293
588
  messages,
1294
- onNew, // Stable reference
589
+ onNew, // required
590
+ setMessages, // branch switching
591
+ onEdit, // edit
592
+ onReload, // regenerate
593
+ onCancel, // cancel
1295
594
  });
1296
595
  ```
1297
596
 
1298
- ### 3. Performance Optimization
1299
-
1300
- ```tsx
1301
- // For large message lists
1302
- const recentMessages = useMemo(
1303
- () => messages.slice(-50), // Show last 50 messages
1304
- [messages],
1305
- );
1306
-
1307
- // For expensive conversions
1308
- const convertMessage = useCallback((msg) => {
1309
- // Conversion logic
1310
- }, []);
1311
- ```
1312
-
1313
- ## `LocalRuntime` vs `ExternalStoreRuntime`
1314
-
1315
- ### When to Choose Which
1316
-
1317
- | Scenario | Recommendation |
1318
- | -------------------------------- | ------------------------------------------------------------ |
1319
- | Quick prototype | `LocalRuntime` |
1320
- | Using Redux/Zustand | `ExternalStoreRuntime` |
1321
- | Need Assistant Cloud integration | `LocalRuntime` |
1322
- | Custom thread storage | Both (`LocalRuntime` with adapter or `ExternalStoreRuntime`) |
1323
- | Simple single thread | `LocalRuntime` |
1324
- | Complex state logic | `ExternalStoreRuntime` |
1325
-
1326
- ### Feature Comparison
1327
-
1328
- | Feature | `LocalRuntime` | `ExternalStoreRuntime` |
1329
- | ---------------- | --------------------------- | ---------------------- |
1330
- | State Management | Built-in | You provide |
1331
- | Multi-thread | Via Cloud or custom adapter | Via adapter |
1332
- | Message Format | ThreadMessage | Any (with conversion) |
1333
- | Setup Complexity | Low | Medium |
1334
- | Flexibility | Medium | High |
1335
-
1336
- ## Common Pitfalls
1337
-
1338
- <Callout type="error">
1339
- **Features not appearing**: Each UI feature requires its corresponding handler:
1340
-
1341
- ```tsx
1342
- // ❌ No edit button
1343
- const runtime = useExternalStoreRuntime({ messages, onNew });
1344
-
1345
- // ✅ Edit button appears
1346
- const runtime = useExternalStoreRuntime({ messages, onNew, onEdit });
1347
- ```
1348
-
1349
- </Callout>
1350
-
1351
- <Callout type="warning">
1352
-
1353
- **State not updating**: Common causes:
1354
-
1355
- 1. Mutating arrays instead of creating new ones
1356
- 2. Missing `setMessages` for branch switching
1357
- 3. Not handling async operations properly
1358
- 4. Incorrect message format conversion
1359
-
1360
- </Callout>
1361
-
1362
- ### Debugging Checklist
597
+ **State not updating.** check for: array mutation instead of new arrays, missing `setMessages`, broken async handling, or invalid `convertMessage` output.
1363
598
 
1364
- - Are you creating new arrays when updating messages?
1365
- - Did you provide all required handlers for desired features?
1366
- - Is your `convertMessage` returning valid `ThreadMessageLike`?
1367
- - Are you properly handling `isRunning` state?
1368
- - For threads: Is your thread list adapter complete?
599
+ **Messages going to the wrong thread.** the runtime's `currentThreadId` and your store's selected thread must stay in sync. Centralize thread id in a context, never in component-local state. See [threads](/docs/runtimes/concepts/threads#externalstorethreadlistadapter).
1369
600
 
1370
- ### Thread-Specific Debugging
1371
-
1372
- Common thread context issues and solutions:
1373
-
1374
- **Messages disappearing when switching threads:**
1375
-
1376
- ```tsx
1377
- // Check 1: Ensure currentThreadId is consistent
1378
- console.log("Runtime threadId:", threadListAdapter.threadId);
1379
- console.log("Current threadId:", currentThreadId);
1380
- console.log("Messages for thread:", threads.get(currentThreadId));
1381
-
1382
- // Check 2: Verify setMessages uses correct thread
1383
- setMessages: (messages) => {
1384
- console.log("Setting messages for thread:", currentThreadId);
1385
- setThreads((prev) => new Map(prev).set(currentThreadId, messages));
1386
- };
1387
- ```
1388
-
1389
- **Thread list not updating:**
1390
-
1391
- ```tsx
1392
- // Ensure threadList state is properly managed
1393
- onSwitchToNewThread: () => {
1394
- const newId = `thread-${Date.now()}`;
1395
- console.log("Creating new thread:", newId);
1396
-
1397
- // All three updates must happen together
1398
- setThreadList((prev) => [...prev, newThreadData]);
1399
- setThreads((prev) => new Map(prev).set(newId, []));
1400
- setCurrentThreadId(newId);
1401
- };
1402
- ```
1403
-
1404
- **Messages going to wrong thread:**
1405
-
1406
- ```tsx
1407
- // Add validation to prevent race conditions
1408
- const validateThreadContext = () => {
1409
- const runtimeThread = threadListAdapter.threadId;
1410
- const contextThread = currentThreadId;
1411
-
1412
- if (runtimeThread !== contextThread) {
1413
- console.error("Thread mismatch!", { runtimeThread, contextThread });
1414
- throw new Error("Thread context mismatch");
1415
- }
1416
- };
1417
-
1418
- // Call before any message operation
1419
- onNew: async (message) => {
1420
- validateThreadContext();
1421
- // ... handle message
1422
- };
1423
- ```
1424
-
1425
- ## API Reference
601
+ ## API reference
1426
602
 
1427
603
  ### `ExternalStoreAdapter`
1428
604
 
1429
- The main interface for connecting your state to assistant-ui.
1430
-
1431
605
  <ParametersTable
1432
606
  type="ExternalStoreAdapter<T>"
1433
607
  parameters={[
1434
608
  {
1435
609
  name: "messages",
1436
610
  type: "readonly T[]",
1437
- description: "Array of messages from your state",
611
+ description: "Array of messages from your state.",
1438
612
  required: true,
1439
613
  },
1440
614
  {
1441
615
  name: "onNew",
1442
616
  type: "(message: AppendMessage) => Promise<void>",
1443
- description: "Handler for new messages from the user",
617
+ description: "Handler for new messages from the user.",
1444
618
  required: true,
1445
619
  },
1446
620
  {
1447
621
  name: "isRunning",
1448
622
  type: "boolean",
1449
623
  description:
1450
- "Whether the assistant is currently generating a response. When true, shows an optimistic assistant message and flows directly to `thread.isRunning`, so the thread stays in a running state even after the last assistant message has completed (e.g. while suggestions or metadata chunks are still arriving). When omitted, `thread.isRunning` falls back to the last-message-status heuristic.",
624
+ "Whether the assistant is currently generating a response. When true, shows an optimistic assistant message and flows directly to thread.isRunning.",
1451
625
  default: "false",
1452
626
  },
1453
627
  {
1454
628
  name: "isDisabled",
1455
629
  type: "boolean",
1456
- description: "Whether the chat input should be disabled",
630
+ description:
631
+ "Disables the entire composer, including the text input. For a narrower gate that keeps the input usable but blocks only sending, use isSendDisabled.",
1457
632
  default: "false",
1458
633
  },
634
+ {
635
+ name: "isSendDisabled",
636
+ type: "boolean",
637
+ description:
638
+ "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).",
639
+ default: "false",
640
+ },
641
+ {
642
+ name: "isLoading",
643
+ type: "boolean",
644
+ description:
645
+ "Whether the adapter is in a loading state. Displays a loading indicator instead of the composer.",
646
+ },
1459
647
  {
1460
648
  name: "suggestions",
1461
649
  type: "readonly ThreadSuggestion[]",
1462
- description: "Suggested prompts to display",
650
+ description: "Suggested prompts to display.",
1463
651
  },
1464
652
  {
1465
653
  name: "extras",
1466
654
  type: "unknown",
1467
- description: "Additional data accessible via runtime.extras",
655
+ description: "Additional data accessible via runtime.extras.",
1468
656
  },
1469
657
  {
1470
658
  name: "setMessages",
1471
659
  type: "(messages: readonly T[]) => void",
1472
- description: "Update messages (required for branch switching)",
660
+ description: "Update messages (required for branch switching).",
1473
661
  },
1474
662
  {
1475
663
  name: "onEdit",
1476
664
  type: "(message: AppendMessage) => Promise<void>",
1477
- description: "Handler for message edits (required for edit feature)",
665
+ description: "Handler for message edits (required for edit feature).",
1478
666
  },
1479
667
  {
1480
668
  name: "onReload",
1481
- type: "(parentId: string | null, config: StartRunConfig) => Promise<void>",
669
+ type: "(parentId: string | Null, config: StartRunConfig) => Promise<void>",
1482
670
  description:
1483
- "Handler for regenerating messages (required for reload feature)",
671
+ "Handler for regenerating messages (required for reload feature).",
1484
672
  },
1485
673
  {
1486
674
  name: "onCancel",
1487
675
  type: "() => Promise<void>",
1488
- description: "Handler for cancelling the current generation",
676
+ description: "Handler for cancelling the current generation.",
1489
677
  },
1490
678
  {
1491
679
  name: "onAddToolResult",
1492
- type: "(options: AddToolResultOptions) => Promise<void> | void",
1493
- description: "Handler for adding tool call results",
680
+ type: "(options: AddToolResultOptions) => Promise<void> | Void",
681
+ description: "Handler for adding tool call results.",
1494
682
  },
1495
683
  {
1496
684
  name: "onResume",
1497
685
  type: "(config: ResumeRunConfig) => Promise<void>",
1498
686
  description:
1499
- "Handler for resuming an interrupted run (e.g. after a page reload mid-generation)",
687
+ "Handler for resuming an interrupted run (e.g. after a page reload mid-generation).",
1500
688
  },
1501
689
  {
1502
690
  name: "onResumeToolCall",
1503
691
  type: "(options: { toolCallId: string; payload: unknown }) => void",
1504
692
  description:
1505
- "Handler for resuming a suspended tool call (used with human-in-the-loop tool execution)",
1506
- },
1507
- {
1508
- name: "isLoading",
1509
- type: "boolean",
1510
- description:
1511
- "Whether the adapter is in a loading state (e.g. initial data fetch). Displays a loading indicator instead of the composer",
693
+ "Handler for resuming a suspended tool call (used with human-in-the-loop tool execution).",
1512
694
  },
1513
695
  {
1514
696
  name: "messageRepository",
1515
697
  type: "ExportedMessageRepository",
1516
698
  description:
1517
- "Pre-built message repository with branching history. Use instead of `messages` when you need to restore branch state",
699
+ "Pre-built message repository with branching history. Use instead of messages when you need to restore branch state.",
1518
700
  },
1519
701
  {
1520
702
  name: "state",
1521
703
  type: "ReadonlyJSONValue",
1522
704
  description:
1523
- "Opaque serializable state passed to `onLoadExternalState` during thread import",
705
+ "Opaque serializable state passed to onLoadExternalState during thread import.",
1524
706
  },
1525
707
  {
1526
708
  name: "onImport",
1527
709
  type: "(messages: readonly ThreadMessage[]) => void",
1528
710
  description:
1529
- "Called when the runtime imports messages into the external store (e.g. on thread switch)",
711
+ "Called when the runtime imports messages into the external store (e.g. on thread switch).",
1530
712
  },
1531
713
  {
1532
714
  name: "onExportExternalState",
1533
715
  type: "() => any",
1534
716
  description:
1535
- "Called to retrieve external state when the runtime exports a thread snapshot",
717
+ "Called to retrieve external state when the runtime exports a thread snapshot.",
1536
718
  },
1537
719
  {
1538
720
  name: "onLoadExternalState",
1539
721
  type: "(state: any) => void",
1540
722
  description:
1541
- "Called with previously exported external state when restoring a thread snapshot",
723
+ "Called with previously exported external state when restoring a thread snapshot.",
1542
724
  },
1543
725
  {
1544
726
  name: "convertMessage",
1545
727
  type: "(message: T, index: number) => ThreadMessageLike",
1546
728
  description:
1547
- "Convert your message format to assistant-ui format. Not needed if using ThreadMessage type",
729
+ "Convert your message format to assistant-ui format. Not needed if using ThreadMessage type.",
1548
730
  },
1549
731
  {
1550
732
  name: "adapters",
1551
733
  type: "object",
1552
- description: "Feature adapters (same as LocalRuntime)",
1553
- children: [
1554
- {
1555
- type: "adapters",
1556
- parameters: [
1557
- {
1558
- name: "attachments",
1559
- type: "AttachmentAdapter",
1560
- description: "Enable file attachments",
1561
- },
1562
- {
1563
- name: "speech",
1564
- type: "SpeechSynthesisAdapter",
1565
- description: "Enable text-to-speech",
1566
- },
1567
- {
1568
- name: "dictation",
1569
- type: "DictationAdapter",
1570
- description: "Enable speech-to-text dictation",
1571
- },
1572
- {
1573
- name: "feedback",
1574
- type: "FeedbackAdapter",
1575
- description: "Enable message feedback",
1576
- },
1577
- {
1578
- name: "threadList",
1579
- type: "ExternalStoreThreadListAdapter",
1580
- description: "Enable multi-thread management",
1581
- },
1582
- ],
1583
- },
1584
- ],
734
+ description:
735
+ "Capability adapters: attachments, speech, dictation, feedback, threadList. See /docs/runtimes/concepts/adapters.",
1585
736
  },
1586
737
  {
1587
738
  name: "unstable_capabilities",
1588
739
  type: "object",
1589
- description: "Configure runtime capabilities",
1590
- children: [
1591
- {
1592
- type: "unstable_capabilities",
1593
- parameters: [
1594
- {
1595
- name: "copy",
1596
- type: "boolean",
1597
- description: "Enable message copy feature",
1598
- default: "true",
1599
- },
1600
- ],
1601
- },
1602
- ],
740
+ description:
741
+ "Configure runtime capabilities (e.g. copy). Unstable, may change.",
1603
742
  },
1604
743
  ]}
1605
744
  />
1606
745
 
1607
746
  ### `ThreadMessageLike`
1608
747
 
1609
- A flexible message format that can be converted to assistant-ui's internal format.
1610
-
1611
748
  <ParametersTable
1612
749
  type="ThreadMessageLike"
1613
750
  parameters={[
1614
751
  {
1615
752
  name: "role",
1616
753
  type: '"assistant" | "user" | "system"',
1617
- description: "The role of the message sender",
754
+ description: "The role of the message sender.",
1618
755
  required: true,
1619
756
  },
1620
757
  {
1621
758
  name: "content",
1622
- type: "string | readonly MessagePart[]",
1623
- description: "Message content as string or structured message parts. Supports `data-*` prefixed types (e.g. `{ type: \"data-workflow\", data: {...} }`) which are automatically converted to DataMessagePart.",
759
+ type: "string | Readonly MessagePart[]",
760
+ description:
761
+ "Message content as string or structured message parts. Supports data-* prefixed types (e.g. { type: \"data-workflow\", data: {...} }) which are automatically converted to DataMessagePart.",
1624
762
  required: true,
1625
763
  },
1626
764
  {
1627
765
  name: "id",
1628
766
  type: "string",
1629
- description: "Unique identifier for the message",
767
+ description: "Unique identifier for the message.",
1630
768
  },
1631
769
  {
1632
770
  name: "createdAt",
1633
771
  type: "Date",
1634
- description: "Timestamp when the message was created",
772
+ description: "Timestamp when the message was created.",
1635
773
  },
1636
774
  {
1637
775
  name: "status",
1638
776
  type: "MessageStatus",
1639
777
  description:
1640
- "Status of assistant messages ({ type: \"running\" }, { type: \"complete\" }, { type: \"incomplete\" })",
778
+ 'Status of assistant messages ({ type: "running" }, { type: "complete" }, { type: "incomplete" }).',
1641
779
  },
1642
780
  {
1643
781
  name: "attachments",
1644
782
  type: "readonly CompleteAttachment[]",
1645
- description: "File attachments (user messages only). Attachment `type` accepts custom strings beyond \"image\" | \"document\" | \"file\", and `contentType` is optional.",
783
+ description:
784
+ 'File attachments (user messages only). Type accepts custom strings beyond "image" | "document" | "file"; contentType is optional.',
1646
785
  },
1647
786
  {
1648
787
  name: "metadata",
1649
788
  type: "object",
1650
- description: "Additional message metadata",
1651
- children: [
1652
- {
1653
- type: "metadata",
1654
- parameters: [
1655
- {
1656
- name: "steps",
1657
- type: "readonly ThreadStep[]",
1658
- description: "Tool call steps for assistant messages",
1659
- },
1660
- {
1661
- name: "custom",
1662
- type: "Record<string, unknown>",
1663
- description: "Custom metadata for your application",
1664
- },
1665
- ],
1666
- },
1667
- ],
1668
- },
1669
- ]}
1670
- />
1671
-
1672
- ### `ExternalStoreThreadListAdapter`
1673
-
1674
- Enable multi-thread support with custom thread management.
1675
-
1676
- <ParametersTable
1677
- type="ExternalStoreThreadListAdapter"
1678
- parameters={[
1679
- {
1680
- name: "threadId",
1681
- type: "string",
1682
- description:
1683
- "ID of the current active thread. **Deprecated** — this API is still under active development and might change without notice.",
1684
- },
1685
- {
1686
- name: "isLoading",
1687
- type: "boolean",
1688
- description: "Whether the thread list is currently loading",
1689
- },
1690
- {
1691
- name: "threads",
1692
- type: "readonly ExternalStoreThreadData<\"regular\">[]",
1693
- description: "Array of active threads. Each entry is an `ExternalStoreThreadData` object.",
1694
- },
1695
- {
1696
- name: "archivedThreads",
1697
- type: "readonly ExternalStoreThreadData<\"archived\">[]",
1698
- description: "Array of archived threads. Each entry is an `ExternalStoreThreadData` object.",
1699
- },
1700
- {
1701
- name: "onSwitchToNewThread",
1702
- type: "() => Promise<void> | void",
1703
- description:
1704
- "Handler for creating a new thread. **Deprecated** — this API is still under active development and might change without notice.",
1705
- },
1706
- {
1707
- name: "onSwitchToThread",
1708
- type: "(threadId: string) => Promise<void> | void",
1709
- description:
1710
- "Handler for switching to an existing thread. **Deprecated** — this API is still under active development and might change without notice.",
1711
- },
1712
- {
1713
- name: "onRename",
1714
- type: "(threadId: string, newTitle: string) => Promise<void> | void",
1715
- description: "Handler for renaming a thread",
1716
- },
1717
- {
1718
- name: "onArchive",
1719
- type: "(threadId: string) => Promise<void> | void",
1720
- description: "Handler for archiving a thread",
1721
- },
1722
- {
1723
- name: "onUnarchive",
1724
- type: "(threadId: string) => Promise<void> | void",
1725
- description: "Handler for unarchiving a thread",
1726
- },
1727
- {
1728
- name: "onDelete",
1729
- type: "(threadId: string) => Promise<void> | void",
1730
- description: "Handler for deleting a thread",
1731
- },
1732
- ]}
1733
- />
1734
-
1735
- <Callout type="info">
1736
- The thread list adapter enables multi-thread support. Without it, the runtime
1737
- only manages the current conversation.
1738
- </Callout>
1739
-
1740
- ### `ExternalStoreThreadData`
1741
-
1742
- Represents a single thread entry in the thread list.
1743
-
1744
- <ParametersTable
1745
- type="ExternalStoreThreadData<TState>"
1746
- parameters={[
1747
- {
1748
- name: "id",
1749
- type: "string",
1750
- description: "Unique local identifier for the thread",
1751
- required: true,
1752
- },
1753
- {
1754
- name: "status",
1755
- type: '"regular" | "archived"',
1756
- description: "Whether the thread is active or archived",
1757
- required: true,
1758
- },
1759
- {
1760
- name: "title",
1761
- type: "string",
1762
- description: "Display title for the thread",
1763
- },
1764
- {
1765
- name: "remoteId",
1766
- type: "string",
1767
- description: "Remote/server-side identifier for the thread (used for persistence)",
1768
- },
1769
- {
1770
- name: "externalId",
1771
- type: "string",
1772
- description: "External system identifier for the thread (e.g. from a third-party service)",
789
+ description: "Additional message metadata (steps, custom fields).",
1773
790
  },
1774
791
  ]}
1775
792
  />
1776
793
 
1777
- ### Related Runtime APIs
794
+ ## Related
1778
795
 
1779
- - [AssistantRuntime API](/docs/api-reference/runtimes/assistant-runtime) - Core runtime interface and methods
1780
- - [ThreadRuntime API](/docs/api-reference/runtimes/thread-runtime) - Thread-specific operations and state management
1781
- - [Runtime Providers](/docs/api-reference/context-providers/assistant-runtime-provider) - Context providers for runtime integration
1782
-
1783
- ## Related Resources
1784
-
1785
- - [Pick a Runtime Guide](/docs/runtimes/pick-a-runtime)
1786
- - [`LocalRuntime` Documentation](/docs/runtimes/custom/local)
1787
- - [Examples Repository](https://github.com/assistant-ui/assistant-ui/tree/main/examples/with-external-store)
796
+ <Cards>
797
+ <Card
798
+ title="LocalRuntime"
799
+ description="Simpler core runtime when you do not have your own state store."
800
+ href="/docs/runtimes/custom/local-runtime"
801
+ />
802
+ <Card
803
+ title="Adapters"
804
+ description="Attachments, speech, feedback, history, suggestions."
805
+ href="/docs/runtimes/concepts/adapters"
806
+ />
807
+ <Card
808
+ title="Threads"
809
+ description="ExternalStoreThreadListAdapter for multi-thread."
810
+ href="/docs/runtimes/concepts/threads"
811
+ />
812
+ </Cards>