@assistant-ui/core 0.3.16 → 0.3.18

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 (474) hide show
  1. package/dist/adapters/attachment.d.ts.map +1 -1
  2. package/dist/adapters/attachment.js +5 -4
  3. package/dist/adapters/attachment.js.map +1 -1
  4. package/dist/index.d.ts +3 -3
  5. package/dist/index.js +2 -2
  6. package/dist/index.js.map +1 -1
  7. package/dist/internal.d.ts +4 -2
  8. package/dist/internal.js +4 -2
  9. package/dist/model-context/frame/host.d.ts.map +1 -1
  10. package/dist/model-context/frame/host.js +10 -4
  11. package/dist/model-context/frame/host.js.map +1 -1
  12. package/dist/model-context/frame/provider.d.ts +5 -0
  13. package/dist/model-context/frame/provider.d.ts.map +1 -1
  14. package/dist/model-context/frame/provider.js +101 -28
  15. package/dist/model-context/frame/provider.js.map +1 -1
  16. package/dist/model-context/interactable-composer-metadata.d.ts.map +1 -1
  17. package/dist/model-context/interactable-composer-metadata.js +16 -9
  18. package/dist/model-context/interactable-composer-metadata.js.map +1 -1
  19. package/dist/model-context/registry.d.ts.map +1 -1
  20. package/dist/model-context/registry.js +3 -5
  21. package/dist/model-context/registry.js.map +1 -1
  22. package/dist/model-context/tool-call-text.d.ts +26 -0
  23. package/dist/model-context/tool-call-text.d.ts.map +1 -0
  24. package/dist/model-context/tool-call-text.js +18 -0
  25. package/dist/model-context/tool-call-text.js.map +1 -0
  26. package/dist/model-context/types.d.ts.map +1 -1
  27. package/dist/model-context/types.js +5 -4
  28. package/dist/model-context/types.js.map +1 -1
  29. package/dist/react/RuntimeAdapter.js +1 -1
  30. package/dist/react/adapters/LocalStorageThreadListAdapter.d.ts.map +1 -1
  31. package/dist/react/adapters/LocalStorageThreadListAdapter.js +1 -1
  32. package/dist/react/adapters/LocalStorageThreadListAdapter.js.map +1 -1
  33. package/dist/react/client/AdaptedRemoteThread.js +1 -1
  34. package/dist/react/client/InMemoryThreadList.d.ts.map +1 -1
  35. package/dist/react/client/InMemoryThreadList.js +63 -57
  36. package/dist/react/client/InMemoryThreadList.js.map +1 -1
  37. package/dist/react/client/Interactables.d.ts.map +1 -1
  38. package/dist/react/client/Interactables.js +25 -28
  39. package/dist/react/client/Interactables.js.map +1 -1
  40. package/dist/react/client/RemoteThreadList.d.ts +16 -0
  41. package/dist/react/client/RemoteThreadList.d.ts.map +1 -1
  42. package/dist/react/client/RemoteThreadList.js +354 -242
  43. package/dist/react/client/RemoteThreadList.js.map +1 -1
  44. package/dist/react/client/Tools.d.ts.map +1 -1
  45. package/dist/react/client/Tools.js +24 -18
  46. package/dist/react/client/Tools.js.map +1 -1
  47. package/dist/react/index.d.ts +2 -1
  48. package/dist/react/index.js +2 -1
  49. package/dist/react/interactables-legacy/Interactables.js +246 -125
  50. package/dist/react/interactables-legacy/Interactables.js.map +1 -1
  51. package/dist/react/interactables-shared/useInteractablePersistenceQueue.d.ts.map +1 -1
  52. package/dist/react/interactables-shared/useInteractablePersistenceQueue.js +11 -9
  53. package/dist/react/interactables-shared/useInteractablePersistenceQueue.js.map +1 -1
  54. package/dist/react/model-context/makeAssistantTool.d.ts +2 -2
  55. package/dist/react/model-context/makeAssistantTool.js +1 -1
  56. package/dist/react/model-context/makeAssistantTool.js.map +1 -1
  57. package/dist/react/model-context/toolbox.d.ts +2 -14
  58. package/dist/react/model-context/toolbox.d.ts.map +1 -1
  59. package/dist/react/model-context/toolbox.js +1 -13
  60. package/dist/react/model-context/toolbox.js.map +1 -1
  61. package/dist/react/model-context/useAssistantContext.js +37 -7
  62. package/dist/react/model-context/useAssistantContext.js.map +1 -1
  63. package/dist/react/model-context/useAssistantTool.d.ts +2 -2
  64. package/dist/react/model-context/useAssistantTool.js +1 -1
  65. package/dist/react/model-context/useAssistantTool.js.map +1 -1
  66. package/dist/react/model-context/useAuiToolOverrides.js +31 -5
  67. package/dist/react/model-context/useAuiToolOverrides.js.map +1 -1
  68. package/dist/react/model-context/useInlineRender.d.ts.map +1 -1
  69. package/dist/react/model-context/useInlineRender.js +10 -36
  70. package/dist/react/model-context/useInlineRender.js.map +1 -1
  71. package/dist/react/model-context/useToolArgsStatus.d.ts.map +1 -1
  72. package/dist/react/model-context/useToolArgsStatus.js +2 -1
  73. package/dist/react/model-context/useToolArgsStatus.js.map +1 -1
  74. package/dist/react/primitive-hooks/useMessageError.d.ts.map +1 -1
  75. package/dist/react/primitive-hooks/useMessageError.js +2 -8
  76. package/dist/react/primitive-hooks/useMessageError.js.map +1 -1
  77. package/dist/react/primitive-hooks/useThreadListLoadMore.d.ts.map +1 -1
  78. package/dist/react/primitive-hooks/useThreadListLoadMore.js +2 -4
  79. package/dist/react/primitive-hooks/useThreadListLoadMore.js.map +1 -1
  80. package/dist/react/primitives/composer/ComposerAttachments.d.ts.map +1 -1
  81. package/dist/react/primitives/composer/ComposerAttachments.js +4 -3
  82. package/dist/react/primitives/composer/ComposerAttachments.js.map +1 -1
  83. package/dist/react/primitives/composer/ComposerQueue.d.ts.map +1 -1
  84. package/dist/react/primitives/composer/ComposerQueue.js +4 -3
  85. package/dist/react/primitives/composer/ComposerQueue.js.map +1 -1
  86. package/dist/react/primitives/message/MessageAttachments.d.ts.map +1 -1
  87. package/dist/react/primitives/message/MessageAttachments.js +7 -6
  88. package/dist/react/primitives/message/MessageAttachments.js.map +1 -1
  89. package/dist/react/primitives/message/MessageGroupedParts.js +2 -2
  90. package/dist/react/primitives/message/MessageGroupedParts.js.map +1 -1
  91. package/dist/react/primitives/message/MessageParts.js +3 -3
  92. package/dist/react/primitives/message/MessageParts.js.map +1 -1
  93. package/dist/react/primitives/threadList/ThreadListItems.d.ts.map +1 -1
  94. package/dist/react/primitives/threadList/ThreadListItems.js +5 -4
  95. package/dist/react/primitives/threadList/ThreadListItems.js.map +1 -1
  96. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.d.ts +6 -4
  97. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.d.ts.map +1 -1
  98. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js +59 -29
  99. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js.map +1 -1
  100. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts +11 -6
  101. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts.map +1 -1
  102. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js +160 -143
  103. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js.map +1 -1
  104. package/dist/react/runtimes/RemoteThreadResource.d.ts +2 -6
  105. package/dist/react/runtimes/RemoteThreadResource.d.ts.map +1 -1
  106. package/dist/react/runtimes/RemoteThreadResource.js +15 -13
  107. package/dist/react/runtimes/RemoteThreadResource.js.map +1 -1
  108. package/dist/react/runtimes/assistant-transport/useLatestRef.js +2 -2
  109. package/dist/react/runtimes/assistant-transport/useLatestRef.js.map +1 -1
  110. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.d.ts +4 -1
  111. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.d.ts.map +1 -1
  112. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js +424 -52
  113. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js.map +1 -1
  114. package/dist/react/runtimes/cloud/auiV0.d.ts +5 -1
  115. package/dist/react/runtimes/cloud/auiV0.d.ts.map +1 -1
  116. package/dist/react/runtimes/cloud/auiV0.js.map +1 -1
  117. package/dist/react/runtimes/cloud/createCloudThreadListAdapter.d.ts +29 -0
  118. package/dist/react/runtimes/cloud/createCloudThreadListAdapter.d.ts.map +1 -0
  119. package/dist/react/runtimes/cloud/createCloudThreadListAdapter.js +160 -0
  120. package/dist/react/runtimes/cloud/createCloudThreadListAdapter.js.map +1 -0
  121. package/dist/react/runtimes/cloud/useCloudThreadListAdapter.d.ts +1 -9
  122. package/dist/react/runtimes/cloud/useCloudThreadListAdapter.d.ts.map +1 -1
  123. package/dist/react/runtimes/cloud/useCloudThreadListAdapter.js +16 -153
  124. package/dist/react/runtimes/cloud/useCloudThreadListAdapter.js.map +1 -1
  125. package/dist/react/runtimes/external-message-converter.js +1 -1
  126. package/dist/react/runtimes/external-message-converter.js.map +1 -1
  127. package/dist/react/runtimes/useExternalStoreRuntime.js +75 -46
  128. package/dist/react/runtimes/useExternalStoreRuntime.js.map +1 -1
  129. package/dist/react/runtimes/useRemoteThreadListRuntime.d.ts.map +1 -1
  130. package/dist/react/runtimes/useRemoteThreadListRuntime.js +77 -29
  131. package/dist/react/runtimes/useRemoteThreadListRuntime.js.map +1 -1
  132. package/dist/react/types/MessagePartComponentTypes.d.ts +7 -3
  133. package/dist/react/types/MessagePartComponentTypes.d.ts.map +1 -1
  134. package/dist/react/types/scopes/tools.d.ts +2 -0
  135. package/dist/react/types/scopes/tools.d.ts.map +1 -1
  136. package/dist/runtime/api/message-part-runtime.d.ts +2 -2
  137. package/dist/runtime/api/message-part-runtime.d.ts.map +1 -1
  138. package/dist/runtime/api/message-part-runtime.js +1 -1
  139. package/dist/runtime/api/message-part-runtime.js.map +1 -1
  140. package/dist/runtime/api/thread-list-item-runtime.d.ts +7 -3
  141. package/dist/runtime/api/thread-list-item-runtime.d.ts.map +1 -1
  142. package/dist/runtime/api/thread-list-item-runtime.js +2 -2
  143. package/dist/runtime/api/thread-list-item-runtime.js.map +1 -1
  144. package/dist/runtime/api/thread-list-runtime.d.ts +12 -1
  145. package/dist/runtime/api/thread-list-runtime.d.ts.map +1 -1
  146. package/dist/runtime/api/thread-list-runtime.js +23 -14
  147. package/dist/runtime/api/thread-list-runtime.js.map +1 -1
  148. package/dist/runtime/api/thread-runtime.d.ts +5 -1
  149. package/dist/runtime/api/thread-runtime.d.ts.map +1 -1
  150. package/dist/runtime/api/thread-runtime.js +1 -1
  151. package/dist/runtime/api/thread-runtime.js.map +1 -1
  152. package/dist/runtime/base/base-composer-runtime-core.d.ts.map +1 -1
  153. package/dist/runtime/base/base-composer-runtime-core.js +16 -12
  154. package/dist/runtime/base/base-composer-runtime-core.js.map +1 -1
  155. package/dist/runtime/base/base-thread-runtime-core.d.ts +2 -2
  156. package/dist/runtime/base/base-thread-runtime-core.d.ts.map +1 -1
  157. package/dist/runtime/base/base-thread-runtime-core.js +8 -7
  158. package/dist/runtime/base/base-thread-runtime-core.js.map +1 -1
  159. package/dist/runtime/interfaces/composer-runtime-core.d.ts +12 -5
  160. package/dist/runtime/interfaces/composer-runtime-core.d.ts.map +1 -1
  161. package/dist/runtime/interfaces/thread-list-runtime-core.d.ts +18 -3
  162. package/dist/runtime/interfaces/thread-list-runtime-core.d.ts.map +1 -1
  163. package/dist/runtime/interfaces/thread-runtime-core.d.ts +15 -1
  164. package/dist/runtime/interfaces/thread-runtime-core.d.ts.map +1 -1
  165. package/dist/runtime/utils/auto-status.d.ts +1 -1
  166. package/dist/runtime/utils/auto-status.d.ts.map +1 -1
  167. package/dist/runtime/utils/auto-status.js +6 -2
  168. package/dist/runtime/utils/auto-status.js.map +1 -1
  169. package/dist/runtime/utils/external-message-conversion.d.ts +3 -2
  170. package/dist/runtime/utils/external-message-conversion.d.ts.map +1 -1
  171. package/dist/runtime/utils/external-message-conversion.js +5 -4
  172. package/dist/runtime/utils/external-message-conversion.js.map +1 -1
  173. package/dist/runtime/utils/message-repository.d.ts +1 -0
  174. package/dist/runtime/utils/message-repository.d.ts.map +1 -1
  175. package/dist/runtime/utils/message-repository.js +15 -6
  176. package/dist/runtime/utils/message-repository.js.map +1 -1
  177. package/dist/runtime/utils/resolveToolApprovalResponse.d.ts +6 -3
  178. package/dist/runtime/utils/resolveToolApprovalResponse.d.ts.map +1 -1
  179. package/dist/runtime/utils/resolveToolApprovalResponse.js +12 -3
  180. package/dist/runtime/utils/resolveToolApprovalResponse.js.map +1 -1
  181. package/dist/runtime/utils/thread-message-like.d.ts +2 -10
  182. package/dist/runtime/utils/thread-message-like.d.ts.map +1 -1
  183. package/dist/runtime/utils/thread-message-like.js +1 -1
  184. package/dist/runtime/utils/thread-message-like.js.map +1 -1
  185. package/dist/runtime/utils/tool-call-tree.d.ts +39 -0
  186. package/dist/runtime/utils/tool-call-tree.d.ts.map +1 -0
  187. package/dist/runtime/utils/tool-call-tree.js +85 -0
  188. package/dist/runtime/utils/tool-call-tree.js.map +1 -0
  189. package/dist/runtimes/external-store/external-store-adapter.d.ts +29 -2
  190. package/dist/runtimes/external-store/external-store-adapter.d.ts.map +1 -1
  191. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.d.ts.map +1 -1
  192. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.js +21 -27
  193. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.js.map +1 -1
  194. package/dist/runtimes/external-store/external-store-thread-runtime-core.d.ts +2 -1
  195. package/dist/runtimes/external-store/external-store-thread-runtime-core.d.ts.map +1 -1
  196. package/dist/runtimes/external-store/external-store-thread-runtime-core.js +34 -28
  197. package/dist/runtimes/external-store/external-store-thread-runtime-core.js.map +1 -1
  198. package/dist/runtimes/inert/InertThreadRuntimeCore.d.ts +1 -1
  199. package/dist/runtimes/inert/InertThreadRuntimeCore.d.ts.map +1 -1
  200. package/dist/runtimes/inert/InertThreadRuntimeCore.js.map +1 -1
  201. package/dist/runtimes/internal.d.ts +2 -2
  202. package/dist/runtimes/internal.js +2 -2
  203. package/dist/runtimes/local/local-thread-runtime-core.d.ts +1 -1
  204. package/dist/runtimes/local/local-thread-runtime-core.d.ts.map +1 -1
  205. package/dist/runtimes/local/local-thread-runtime-core.js +3 -1
  206. package/dist/runtimes/local/local-thread-runtime-core.js.map +1 -1
  207. package/dist/runtimes/remote-thread-list/remote-thread-state.d.ts +18 -1
  208. package/dist/runtimes/remote-thread-list/remote-thread-state.d.ts.map +1 -1
  209. package/dist/runtimes/remote-thread-list/remote-thread-state.js +148 -43
  210. package/dist/runtimes/remote-thread-list/remote-thread-state.js.map +1 -1
  211. package/dist/runtimes/remote-thread-list/title-generation.d.ts +49 -0
  212. package/dist/runtimes/remote-thread-list/title-generation.d.ts.map +1 -0
  213. package/dist/runtimes/remote-thread-list/title-generation.js +197 -0
  214. package/dist/runtimes/remote-thread-list/title-generation.js.map +1 -0
  215. package/dist/runtimes/remote-thread-list/title.d.ts +11 -0
  216. package/dist/runtimes/remote-thread-list/title.d.ts.map +1 -0
  217. package/dist/runtimes/remote-thread-list/title.js +14 -0
  218. package/dist/runtimes/remote-thread-list/title.js.map +1 -0
  219. package/dist/runtimes/remote-thread-list/types.d.ts +16 -5
  220. package/dist/runtimes/remote-thread-list/types.d.ts.map +1 -1
  221. package/dist/runtimes/tool-invocations/ToolInvocationTracker.d.ts +31 -8
  222. package/dist/runtimes/tool-invocations/ToolInvocationTracker.d.ts.map +1 -1
  223. package/dist/runtimes/tool-invocations/ToolInvocationTracker.js +97 -60
  224. package/dist/runtimes/tool-invocations/ToolInvocationTracker.js.map +1 -1
  225. package/dist/store/clients/external-thread.d.ts +1 -1
  226. package/dist/store/clients/external-thread.d.ts.map +1 -1
  227. package/dist/store/clients/external-thread.js +30 -7
  228. package/dist/store/clients/external-thread.js.map +1 -1
  229. package/dist/store/clients/model-context-client.d.ts.map +1 -1
  230. package/dist/store/clients/model-context-client.js +2 -1
  231. package/dist/store/clients/model-context-client.js.map +1 -1
  232. package/dist/store/clients/single-thread-list.d.ts.map +1 -1
  233. package/dist/store/clients/single-thread-list.js +2 -1
  234. package/dist/store/clients/single-thread-list.js.map +1 -1
  235. package/dist/store/clients/suggestions.d.ts.map +1 -1
  236. package/dist/store/clients/suggestions.js +19 -6
  237. package/dist/store/clients/suggestions.js.map +1 -1
  238. package/dist/store/clients/thread-message-client.js +1 -1
  239. package/dist/store/clients/thread-selection-events.js +1 -1
  240. package/dist/store/index.d.ts +2 -1
  241. package/dist/store/index.js +2 -1
  242. package/dist/store/internal.d.ts +2 -2
  243. package/dist/store/internal.js +2 -2
  244. package/dist/store/primitive-predicates.d.ts +4 -1
  245. package/dist/store/primitive-predicates.d.ts.map +1 -1
  246. package/dist/store/primitive-predicates.js +9 -1
  247. package/dist/store/primitive-predicates.js.map +1 -1
  248. package/dist/store/runtime-clients/composer-runtime-client.d.ts +1 -0
  249. package/dist/store/runtime-clients/composer-runtime-client.d.ts.map +1 -1
  250. package/dist/store/runtime-clients/composer-runtime-client.js +98 -62
  251. package/dist/store/runtime-clients/composer-runtime-client.js.map +1 -1
  252. package/dist/store/runtime-clients/message-runtime-client.d.ts +1 -0
  253. package/dist/store/runtime-clients/message-runtime-client.d.ts.map +1 -1
  254. package/dist/store/runtime-clients/message-runtime-client.js +221 -144
  255. package/dist/store/runtime-clients/message-runtime-client.js.map +1 -1
  256. package/dist/store/runtime-clients/thread-list-item-runtime-client.d.ts.map +1 -1
  257. package/dist/store/runtime-clients/thread-list-item-runtime-client.js +2 -2
  258. package/dist/store/runtime-clients/thread-list-item-runtime-client.js.map +1 -1
  259. package/dist/store/runtime-clients/thread-list-runtime-client.d.ts.map +1 -1
  260. package/dist/store/runtime-clients/thread-list-runtime-client.js +128 -107
  261. package/dist/store/runtime-clients/thread-list-runtime-client.js.map +1 -1
  262. package/dist/store/runtime-clients/thread-runtime-client.d.ts.map +1 -1
  263. package/dist/store/runtime-clients/thread-runtime-client.js +194 -122
  264. package/dist/store/runtime-clients/thread-runtime-client.js.map +1 -1
  265. package/dist/store/runtime-clients/useSubscribable.d.ts +3 -1
  266. package/dist/store/runtime-clients/useSubscribable.d.ts.map +1 -1
  267. package/dist/store/runtime-clients/useSubscribable.js +1 -1
  268. package/dist/store/runtime-clients/useSubscribable.js.map +1 -1
  269. package/dist/store/scopes/composer.d.ts +10 -2
  270. package/dist/store/scopes/composer.d.ts.map +1 -1
  271. package/dist/store/scopes/message.d.ts +30 -1
  272. package/dist/store/scopes/message.d.ts.map +1 -1
  273. package/dist/store/scopes/part.d.ts +6 -3
  274. package/dist/store/scopes/part.d.ts.map +1 -1
  275. package/dist/store/scopes/thread-list-item.d.ts +2 -2
  276. package/dist/store/scopes/thread-list-item.d.ts.map +1 -1
  277. package/dist/store/scopes/thread.d.ts +8 -0
  278. package/dist/store/scopes/thread.d.ts.map +1 -1
  279. package/dist/store/scopes/threads.d.ts +1 -0
  280. package/dist/store/scopes/threads.d.ts.map +1 -1
  281. package/dist/subscribable/subscribable.d.ts +8 -1
  282. package/dist/subscribable/subscribable.d.ts.map +1 -1
  283. package/dist/subscribable/subscribable.js +24 -16
  284. package/dist/subscribable/subscribable.js.map +1 -1
  285. package/dist/types/attachment.d.ts +2 -1
  286. package/dist/types/attachment.d.ts.map +1 -1
  287. package/dist/types/attachment.js +2 -1
  288. package/dist/types/attachment.js.map +1 -1
  289. package/dist/types/message.d.ts +40 -1
  290. package/dist/types/message.d.ts.map +1 -1
  291. package/dist/types/message.js +7 -1
  292. package/dist/types/message.js.map +1 -1
  293. package/dist/utils/abortable-iterable.d.ts +19 -0
  294. package/dist/utils/abortable-iterable.d.ts.map +1 -0
  295. package/dist/utils/abortable-iterable.js +72 -0
  296. package/dist/utils/abortable-iterable.js.map +1 -0
  297. package/dist/utils/composite-context-provider.d.ts.map +1 -1
  298. package/dist/utils/composite-context-provider.js +20 -2
  299. package/dist/utils/composite-context-provider.js.map +1 -1
  300. package/dist/utils/record.d.ts +11 -0
  301. package/dist/utils/record.d.ts.map +1 -0
  302. package/dist/utils/record.js +12 -0
  303. package/dist/utils/record.js.map +1 -0
  304. package/package.json +12 -17
  305. package/src/adapters/attachment.test.ts +50 -1
  306. package/src/adapters/attachment.ts +7 -4
  307. package/src/index.ts +7 -1
  308. package/src/internal.ts +12 -0
  309. package/src/model-context/frame/host.test.ts +24 -0
  310. package/src/model-context/frame/host.ts +10 -4
  311. package/src/model-context/frame/provider.test.ts +288 -1
  312. package/src/model-context/frame/provider.ts +148 -30
  313. package/src/model-context/interactable-composer-metadata.test.ts +28 -0
  314. package/src/model-context/interactable-composer-metadata.ts +9 -3
  315. package/src/model-context/registry.test.ts +12 -0
  316. package/src/model-context/registry.ts +3 -2
  317. package/src/model-context/tool-call-text.test.ts +67 -0
  318. package/src/model-context/tool-call-text.ts +67 -0
  319. package/src/model-context/types.test.ts +25 -0
  320. package/src/model-context/types.ts +9 -4
  321. package/src/react/AssistantRuntimeProvider.test.tsx +66 -2
  322. package/src/react/RuntimeAdapter.test.ts +34 -0
  323. package/src/react/adapters/LocalStorageThreadListAdapter.tsx +1 -3
  324. package/src/react/client/InMemoryThreadList.ts +1 -0
  325. package/src/react/client/Interactables.test.ts +28 -0
  326. package/src/react/client/Interactables.ts +15 -13
  327. package/src/react/client/RemoteThreadList.background.test.ts +526 -0
  328. package/src/react/client/RemoteThreadList.concurrent.test.tsx +315 -0
  329. package/src/react/client/RemoteThreadList.test.ts +617 -3
  330. package/src/react/client/RemoteThreadList.ts +433 -167
  331. package/src/react/client/Tools.ts +29 -12
  332. package/src/react/index.ts +4 -0
  333. package/src/react/interactables-legacy/Interactables.ts +16 -16
  334. package/src/react/interactables-shared/useInteractablePersistenceQueue.ts +9 -10
  335. package/src/react/model-context/makeAssistantTool.ts +2 -2
  336. package/src/react/model-context/modelContextHooks.test.tsx +131 -0
  337. package/src/react/model-context/toolbox.test.ts +9 -9
  338. package/src/react/model-context/toolbox.ts +9 -36
  339. package/src/react/model-context/useAssistantContext.ts +4 -2
  340. package/src/react/model-context/useAssistantTool.ts +2 -2
  341. package/src/react/model-context/useAuiToolOverrides.ts +4 -2
  342. package/src/react/model-context/useInlineRender.test.tsx +48 -0
  343. package/src/react/model-context/useInlineRender.ts +14 -10
  344. package/src/react/model-context/useToolArgsStatus.test.tsx +30 -0
  345. package/src/react/model-context/useToolArgsStatus.ts +2 -1
  346. package/src/react/primitive-hooks/useMessageError.ts +2 -19
  347. package/src/react/primitive-hooks/useThreadListLoadMore.ts +2 -3
  348. package/src/react/primitives/composer/ComposerAttachments.test.tsx +76 -0
  349. package/src/react/primitives/composer/ComposerAttachments.tsx +9 -4
  350. package/src/react/primitives/composer/ComposerQueue.test.tsx +72 -0
  351. package/src/react/primitives/composer/ComposerQueue.tsx +7 -4
  352. package/src/react/primitives/message/MessageAttachments.test.tsx +107 -37
  353. package/src/react/primitives/message/MessageAttachments.tsx +10 -7
  354. package/src/react/primitives/message/MessageGroupedParts.tsx +2 -2
  355. package/src/react/primitives/message/MessageParts.tsx +3 -3
  356. package/src/react/primitives/threadList/ThreadListItems.test.tsx +99 -0
  357. package/src/react/primitives/threadList/ThreadListItems.tsx +8 -5
  358. package/src/react/runtimes/RemoteThreadListHookInstanceManager.destroy.test.tsx +172 -0
  359. package/src/react/runtimes/RemoteThreadListHookInstanceManager.running.test.tsx +127 -1
  360. package/src/react/runtimes/RemoteThreadListHookInstanceManager.test.tsx +7 -1
  361. package/src/react/runtimes/RemoteThreadListHookInstanceManager.tsx +99 -35
  362. package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.load-error.test.ts +132 -0
  363. package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.switch-delete.test.ts +274 -0
  364. package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.tsx +223 -152
  365. package/src/react/runtimes/RemoteThreadResource.test.ts +9 -0
  366. package/src/react/runtimes/RemoteThreadResource.ts +20 -16
  367. package/src/react/runtimes/assistant-transport/transport-scheduling.test.ts +51 -2
  368. package/src/react/runtimes/assistant-transport/useLatestRef.ts +2 -2
  369. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.test.ts +45 -0
  370. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.test.tsx +546 -5
  371. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.ts +546 -103
  372. package/src/react/runtimes/cloud/auiV0.ts +5 -0
  373. package/src/react/runtimes/cloud/createCloudThreadListAdapter.test.ts +103 -0
  374. package/src/react/runtimes/cloud/createCloudThreadListAdapter.ts +237 -0
  375. package/src/react/runtimes/cloud/useCloudThreadListAdapter.test.tsx +90 -1
  376. package/src/react/runtimes/cloud/useCloudThreadListAdapter.tsx +33 -238
  377. package/src/react/runtimes/external-message-converter.ts +1 -0
  378. package/src/react/runtimes/useExternalStoreRuntime.lifecycle.test.tsx +41 -0
  379. package/src/react/runtimes/useExternalStoreRuntime.ts +10 -4
  380. package/src/react/runtimes/useLocalRuntime.test.tsx +20 -0
  381. package/src/react/runtimes/useRemoteThreadListRuntime.concurrent.test.tsx +216 -0
  382. package/src/react/runtimes/useRemoteThreadListRuntime.load-error.test.tsx +108 -0
  383. package/src/react/runtimes/useRemoteThreadListRuntime.ts +57 -6
  384. package/src/react/types/MessagePartComponentTypes.ts +7 -3
  385. package/src/react/types/scopes/tools.ts +2 -0
  386. package/src/runtime/api/message-part-runtime.ts +3 -3
  387. package/src/runtime/api/message-runtime.test.ts +75 -0
  388. package/src/runtime/api/thread-list-item-runtime.ts +10 -3
  389. package/src/runtime/api/thread-list-runtime.ts +46 -12
  390. package/src/runtime/api/thread-runtime.test.ts +78 -0
  391. package/src/runtime/api/thread-runtime.ts +8 -1
  392. package/src/runtime/base/base-composer-runtime-core.ts +36 -8
  393. package/src/runtime/base/base-thread-runtime-core.test.ts +149 -4
  394. package/src/runtime/base/base-thread-runtime-core.ts +16 -8
  395. package/src/runtime/interfaces/composer-runtime-core.ts +10 -5
  396. package/src/runtime/interfaces/thread-list-runtime-core.ts +25 -2
  397. package/src/runtime/interfaces/thread-runtime-core.ts +17 -1
  398. package/src/runtime/utils/auto-status.test.ts +57 -0
  399. package/src/runtime/utils/auto-status.ts +13 -1
  400. package/src/runtime/utils/external-message-conversion.test.ts +165 -0
  401. package/src/runtime/utils/external-message-conversion.ts +18 -2
  402. package/src/runtime/utils/message-repository.ts +29 -17
  403. package/src/runtime/utils/resolveToolApprovalResponse.test.ts +73 -0
  404. package/src/runtime/utils/resolveToolApprovalResponse.ts +27 -6
  405. package/src/runtime/utils/thread-message-like.ts +3 -11
  406. package/src/runtime/utils/tool-call-tree.test.ts +191 -0
  407. package/src/runtime/utils/tool-call-tree.ts +97 -0
  408. package/src/runtimes/external-store/external-store-adapter.ts +38 -2
  409. package/src/runtimes/external-store/external-store-thread-list-runtime-core.ts +18 -13
  410. package/src/runtimes/external-store/external-store-thread-runtime-core.ts +56 -33
  411. package/src/runtimes/inert/InertThreadRuntimeCore.ts +1 -1
  412. package/src/runtimes/internal.ts +1 -0
  413. package/src/runtimes/local/local-thread-runtime-core.test.ts +29 -3
  414. package/src/runtimes/local/local-thread-runtime-core.ts +5 -1
  415. package/src/runtimes/remote-thread-list/remote-thread-state.test.ts +238 -0
  416. package/src/runtimes/remote-thread-list/remote-thread-state.ts +223 -44
  417. package/src/runtimes/remote-thread-list/title-generation.test.ts +602 -0
  418. package/src/runtimes/remote-thread-list/title-generation.ts +329 -0
  419. package/src/runtimes/remote-thread-list/title.test.ts +62 -0
  420. package/src/runtimes/remote-thread-list/title.ts +16 -0
  421. package/src/runtimes/remote-thread-list/types.ts +16 -5
  422. package/src/runtimes/tool-invocations/EDGE_CASES.md +110 -5
  423. package/src/runtimes/tool-invocations/ToolInvocationTracker.test.ts +750 -136
  424. package/src/runtimes/tool-invocations/ToolInvocationTracker.ts +144 -91
  425. package/src/store/clients/external-thread.ts +62 -11
  426. package/src/store/clients/model-context-client.ts +5 -1
  427. package/src/store/clients/single-thread-list.ts +1 -0
  428. package/src/store/clients/suggestions.test.ts +195 -1
  429. package/src/store/clients/suggestions.ts +45 -34
  430. package/src/store/index.ts +4 -0
  431. package/src/store/internal.ts +2 -0
  432. package/src/store/primitive-predicates.ts +35 -0
  433. package/src/store/runtime-clients/composer-runtime-client.ts +42 -9
  434. package/src/store/runtime-clients/message-runtime-client.ts +54 -6
  435. package/src/store/runtime-clients/thread-list-item-runtime-client.ts +4 -2
  436. package/src/store/runtime-clients/thread-list-runtime-client.ts +17 -2
  437. package/src/store/runtime-clients/thread-runtime-client.ts +58 -10
  438. package/src/store/runtime-clients/useSubscribable.ts +8 -2
  439. package/src/store/scopes/composer.ts +16 -4
  440. package/src/store/scopes/message.ts +14 -0
  441. package/src/store/scopes/part.ts +6 -3
  442. package/src/store/scopes/thread-list-item.ts +5 -2
  443. package/src/store/scopes/thread.ts +4 -0
  444. package/src/store/scopes/threads.ts +1 -0
  445. package/src/subscribable/subscribable.test.ts +50 -0
  446. package/src/subscribable/subscribable.ts +33 -21
  447. package/src/tests/MessageRepository.test.ts +116 -0
  448. package/src/tests/OptimisticState-list-race.test.ts +1 -0
  449. package/src/tests/RemoteThreadListThreadListRuntimeCore-initialize-adapter-change.test.ts +38 -0
  450. package/src/tests/RemoteThreadListThreadListRuntimeCore-load-race.test.ts +158 -0
  451. package/src/tests/RemoteThreadListThreadListRuntimeCore-loadMore.test.ts +78 -0
  452. package/src/tests/RemoteThreadListThreadListRuntimeCore-reload.test.ts +52 -0
  453. package/src/tests/RemoteThreadListThreadListRuntimeCore-title-race.test.ts +89 -0
  454. package/src/tests/attachment-adapters.test.ts +14 -0
  455. package/src/tests/base-composer-runtime-core.test.ts +39 -0
  456. package/src/tests/external-store-optimistic-id.test.ts +79 -0
  457. package/src/tests/external-store-repository-instance.test.ts +166 -0
  458. package/src/tests/external-store-thread-list-runtime-core.test.ts +83 -0
  459. package/src/tests/external-store-thread-runtime-core-adapter.test.ts +49 -34
  460. package/src/tests/external-thread-attachments.test.tsx +224 -1
  461. package/src/tests/external-thread-parity.test.tsx +23 -0
  462. package/src/tests/remote-thread-list-adapter-switch.test.ts +66 -1
  463. package/src/tests/remote-thread-list-isLoading.test.ts +1 -0
  464. package/src/tests/thread-list-runtime-getLoadThreadsPromise.test.ts +1 -0
  465. package/src/tests/thread-switch-events.test.tsx +475 -2
  466. package/src/tests/tools-scope-migration.test.tsx +28 -0
  467. package/src/types/attachment.ts +4 -0
  468. package/src/types/message.ts +50 -2
  469. package/src/utils/abortable-iterable.test.ts +213 -0
  470. package/src/utils/abortable-iterable.ts +93 -0
  471. package/src/utils/composite-context-provider.test.ts +25 -0
  472. package/src/utils/composite-context-provider.ts +23 -2
  473. package/src/utils/record.test.ts +48 -0
  474. package/src/utils/record.ts +11 -0
@@ -16,10 +16,12 @@ import type { Tool } from "assistant-stream";
16
16
  import {
17
17
  isStandaloneToolDisplay,
18
18
  makeToolCallTextComponent,
19
+ type ToolCallText,
19
20
  type Toolkit,
20
21
  } from "../model-context/toolbox";
21
22
  import type { ToolCallMessagePartComponent } from "../types/MessagePartComponentTypes";
22
23
  import { ModelContext } from "../../store/clients/model-context-client";
24
+ import { nullProtoRecord } from "../../utils/record";
23
25
 
24
26
  export type { McpAppResourceOutput };
25
27
 
@@ -43,7 +45,9 @@ const useTools = ({
43
45
  const mcpAppOutputs = useResources(mcpApp ? [withKey("mcpApp", mcpApp)] : []);
44
46
  const mcpAppOutput = mcpAppOutputs[0];
45
47
 
46
- const [toolUIs, setToolUIs] = useState<ToolsState["toolUIs"]>(() => ({}));
48
+ const [toolUIs, setToolUIs] = useState<ToolsState["toolUIs"]>(() =>
49
+ nullProtoRecord(),
50
+ );
47
51
 
48
52
  const state = useMemo(
49
53
  (): ToolsState => ({
@@ -59,29 +63,38 @@ const useTools = ({
59
63
  (
60
64
  toolName: string,
61
65
  render: ToolCallMessagePartComponent,
62
- options?: { standalone?: boolean },
66
+ options?: {
67
+ standalone?: boolean;
68
+ renderText?: ToolCallText<any, any> | undefined;
69
+ },
63
70
  ) => {
64
71
  // One registration object per call; identity is the removal key, so
65
72
  // the per-name list stays correctly ref-counted across re-registers.
66
73
  const registration = {
67
74
  render,
75
+ renderText: options?.renderText,
68
76
  standalone: options?.standalone ?? false,
69
77
  };
70
78
 
71
- setToolUIs((prev) => ({
72
- ...prev,
73
- [toolName]: [...(prev[toolName] ?? []), registration],
74
- }));
79
+ setToolUIs((prev) => {
80
+ const next = nullProtoRecord(prev);
81
+ next[toolName] = [...(next[toolName] ?? []), registration];
82
+ return next;
83
+ });
75
84
 
76
85
  return () => {
77
86
  setToolUIs((prev) => {
78
- const next = prev[toolName]?.filter((r) => r !== registration) ?? [];
79
- if (next.length > 0) return { ...prev, [toolName]: next };
87
+ const registrations =
88
+ prev[toolName]?.filter((r) => r !== registration) ?? [];
89
+ const next = nullProtoRecord(prev);
90
+ if (registrations.length > 0) {
91
+ next[toolName] = registrations;
92
+ return next;
93
+ }
80
94
  // Drop the key entirely so repeatedly mounted/unmounted tools
81
95
  // don't leave empty arrays accumulating across a long session.
82
- const rest = { ...prev };
83
- delete rest[toolName];
84
- return rest;
96
+ delete next[toolName];
97
+ return next;
85
98
  });
86
99
  };
87
100
  },
@@ -103,8 +116,12 @@ const useTools = ({
103
116
  : undefined);
104
117
  if (render) {
105
118
  unsubscribes.push(
119
+ // Registration has to be undone on unmount, so the registry write and
120
+ // its unsubscribe belong to the same effect.
121
+ // eslint-disable-next-line react-hooks/set-state-in-effect
106
122
  setToolUI(toolName, render, {
107
123
  standalone: isStandaloneToolDisplay(tool),
124
+ renderText: toolRenderText,
108
125
  }),
109
126
  );
110
127
  }
@@ -135,7 +152,7 @@ const useTools = ({
135
152
  acc[name] = rest as Tool<any, any>;
136
153
  return acc;
137
154
  },
138
- {} as Record<string, Tool<any, any>>,
155
+ nullProtoRecord<Tool<any, any>>(),
139
156
  );
140
157
 
141
158
  const modelContextProvider = {
@@ -235,6 +235,10 @@ export { RemoteThreadListHookInstanceManager } from "./runtimes/RemoteThreadList
235
235
  export { RemoteThreadListThreadListRuntimeCore } from "./runtimes/RemoteThreadListThreadListRuntimeCore";
236
236
  export { useRemoteThreadListRuntime } from "./runtimes/useRemoteThreadListRuntime";
237
237
  export { useCloudThreadListAdapter } from "./runtimes/cloud/useCloudThreadListAdapter";
238
+ export {
239
+ createCloudThreadListAdapter,
240
+ type CloudThreadListAdapterOptions,
241
+ } from "./runtimes/cloud/createCloudThreadListAdapter";
238
242
  export { useCloudThreadListRuntime } from "./runtimes/cloud/useCloudThreadListRuntime";
239
243
  export {
240
244
  useAssistantTransportRuntime,
@@ -18,11 +18,12 @@ import { ModelContext } from "../../store";
18
18
  import { buildInteractableModelContext } from "./interactable-model-context";
19
19
  import { notifySubscribers as notifyStateSubscribers } from "../../subscribable/subscribable";
20
20
  import { useInteractablePersistenceQueue } from "../interactables-shared/useInteractablePersistenceQueue";
21
+ import { nullProtoRecord } from "../../utils/record";
21
22
 
22
23
  const useInteractables = (): ClientOutput<"interactables"> => {
23
24
  const [state, setState] = useState<InteractablesState>(() => ({
24
- definitions: {},
25
- persistence: {},
25
+ definitions: nullProtoRecord(),
26
+ persistence: nullProtoRecord(),
26
27
  }));
27
28
 
28
29
  const clientRef = useAssistantClientRef();
@@ -49,7 +50,7 @@ const useInteractables = (): ClientOutput<"interactables"> => {
49
50
  );
50
51
 
51
52
  const exportState = useCallback((): InteractablePersistedState => {
52
- const result: InteractablePersistedState = {};
53
+ const result = nullProtoRecord<InteractablePersistedState[string]>();
53
54
  for (const [id, def] of Object.entries(stateRef.current.definitions)) {
54
55
  result[id] = { name: def.name, state: def.state };
55
56
  }
@@ -86,7 +87,7 @@ const useInteractables = (): ClientOutput<"interactables"> => {
86
87
  }
87
88
  setStateAndRef((prev) => {
88
89
  let changed = false;
89
- const definitions = { ...prev.definitions };
90
+ const definitions = nullProtoRecord(prev.definitions);
90
91
  for (const [id, entry] of Object.entries(saved)) {
91
92
  if (definitions[id]) {
92
93
  definitions[id] = { ...definitions[id], state: entry.state };
@@ -114,10 +115,9 @@ const useInteractables = (): ClientOutput<"interactables"> => {
114
115
  if (!existing) return prev;
115
116
  return {
116
117
  ...prev,
117
- definitions: {
118
- ...prev.definitions,
118
+ definitions: nullProtoRecord(prev.definitions, {
119
119
  [id]: { ...existing, state: updater(existing.state) },
120
- },
120
+ }),
121
121
  };
122
122
  });
123
123
  if (stateRef.current.definitions[id]) schedulePersistence(id);
@@ -132,10 +132,9 @@ const useInteractables = (): ClientOutput<"interactables"> => {
132
132
  if (!existing) return prev;
133
133
  return {
134
134
  ...prev,
135
- definitions: {
136
- ...prev.definitions,
135
+ definitions: nullProtoRecord(prev.definitions, {
137
136
  [id]: { ...existing, selected },
138
- },
137
+ }),
139
138
  };
140
139
  });
141
140
  },
@@ -194,8 +193,7 @@ const useInteractables = (): ClientOutput<"interactables"> => {
194
193
 
195
194
  setStateAndRef((prev) => ({
196
195
  ...prev,
197
- definitions: {
198
- ...prev.definitions,
196
+ definitions: nullProtoRecord(prev.definitions, {
199
197
  [def.id]: {
200
198
  id: def.id,
201
199
  name: def.name,
@@ -205,7 +203,7 @@ const useInteractables = (): ClientOutput<"interactables"> => {
205
203
  prev.definitions[def.id]?.state ?? detached ?? def.initialState,
206
204
  selected: def.selected,
207
205
  },
208
- },
206
+ }),
209
207
  }));
210
208
 
211
209
  return () => {
@@ -216,9 +214,11 @@ const useInteractables = (): ClientOutput<"interactables"> => {
216
214
  detachedStateRef.current.set(def.id, existing.state);
217
215
  }
218
216
  partialSchemaCacheRef.current.delete(def.id);
219
- const { [def.id]: _, ...rest } = prev.definitions;
220
- const { [def.id]: __, ...restPersistence } = prev.persistence;
221
- return { ...prev, definitions: rest, persistence: restPersistence };
217
+ const definitions = nullProtoRecord(prev.definitions);
218
+ const persistence = nullProtoRecord(prev.persistence);
219
+ delete definitions[def.id];
220
+ delete persistence[def.id];
221
+ return { ...prev, definitions, persistence };
222
222
  });
223
223
  };
224
224
  },
@@ -1,4 +1,5 @@
1
1
  import { useCallback, useRef, type RefObject } from "react";
2
+ import { nullProtoRecord } from "../../utils/record";
2
3
 
3
4
  const PERSISTENCE_DEBOUNCE_MS = 500;
4
5
 
@@ -90,15 +91,13 @@ export const useInteractablePersistenceQueue = <State>({
90
91
  const { adapter, payload, dirtyIds, seq } = resolved;
91
92
  inFlightPersistenceRef.current += 1;
92
93
 
93
- updatePersistenceStatus((prev) => ({
94
- ...prev,
95
- ...Object.fromEntries(
96
- [...dirtyIds].map((id) => [
97
- id,
98
- { isPending: true, error: undefined },
99
- ]),
100
- ),
101
- }));
94
+ updatePersistenceStatus((prev) => {
95
+ const persistence = nullProtoRecord(prev);
96
+ for (const id of dirtyIds) {
97
+ persistence[id] = { isPending: true, error: undefined };
98
+ }
99
+ return persistence;
100
+ });
102
101
 
103
102
  const settleBatch = (status: PersistenceStatus | undefined) => {
104
103
  const settledIds: string[] = [];
@@ -114,7 +113,7 @@ export const useInteractablePersistenceQueue = <State>({
114
113
  if (settledIds.length === 0) return;
115
114
  updatePersistenceStatus((prev) => {
116
115
  let changed = false;
117
- const persistence = { ...prev };
116
+ const persistence = nullProtoRecord(prev);
118
117
  for (const id of settledIds) {
119
118
  if (prev[id] === undefined) continue;
120
119
  if (status === undefined) delete persistence[id];
@@ -8,7 +8,7 @@ import { type AssistantToolProps, useAssistantTool } from "./useAssistantTool";
8
8
  * subtree.
9
9
  *
10
10
  * @deprecated Use a toolkit with `Tools({ toolkit })` and register it via
11
- * `useAui({ tools: Tools({ toolkit }) })` instead. See
11
+ * `AuiConfig({ tools: Tools({ toolkit }) })` on the provider's `config` prop instead. See
12
12
  * https://assistant-ui.com/docs/migrations/toolkit-tools.
13
13
  */
14
14
  export type AssistantTool = FC & {
@@ -25,7 +25,7 @@ export type AssistantTool = FC & {
25
25
  * @param tool - Tool definition and name to register.
26
26
  *
27
27
  * @deprecated Use a toolkit with `Tools({ toolkit })` and register it via
28
- * `useAui({ tools: Tools({ toolkit }) })` instead. See
28
+ * `AuiConfig({ tools: Tools({ toolkit }) })` on the provider's `config` prop instead. See
29
29
  * https://assistant-ui.com/docs/migrations/toolkit-tools.
30
30
  */
31
31
  export const makeAssistantTool = <
@@ -0,0 +1,131 @@
1
+ // @vitest-environment jsdom
2
+
3
+ import { act, cleanup, render, waitFor } from "@testing-library/react";
4
+ import { startTransition, Suspense } from "react";
5
+ import { afterEach, describe, expect, it, vi } from "vitest";
6
+
7
+ const { aui, getProvider, resetProvider } = vi.hoisted(() => {
8
+ type TestProvider = {
9
+ getModelContext: () => {
10
+ system?: string | undefined;
11
+ tools?:
12
+ | Record<string, { execute?: (() => unknown) | undefined }>
13
+ | undefined;
14
+ };
15
+ };
16
+ let provider: TestProvider | undefined;
17
+
18
+ return {
19
+ aui: {
20
+ modelContext: {
21
+ register: vi.fn((nextProvider: TestProvider) => {
22
+ provider = nextProvider;
23
+ return () => {
24
+ if (provider === nextProvider) provider = undefined;
25
+ };
26
+ }),
27
+ },
28
+ },
29
+ getProvider: () => provider,
30
+ resetProvider: () => {
31
+ provider = undefined;
32
+ },
33
+ };
34
+ });
35
+
36
+ vi.mock("@assistant-ui/store", async (importOriginal) => ({
37
+ ...(await importOriginal()),
38
+ useAui: () => aui,
39
+ }));
40
+
41
+ import { useAssistantContext } from "./useAssistantContext";
42
+ import { useAuiToolOverrides } from "./useAuiToolOverrides";
43
+
44
+ afterEach(() => {
45
+ cleanup();
46
+ resetProvider();
47
+ });
48
+
49
+ describe("model context hooks", () => {
50
+ it("keeps assistant context scoped to the committed render", async () => {
51
+ const pending = new Promise<never>(() => {});
52
+ const renderB = vi.fn();
53
+ const Probe = ({ value, suspend }: { value: string; suspend: boolean }) => {
54
+ useAssistantContext({ getContext: () => value });
55
+ if (value === "workspace-b") renderB();
56
+ if (suspend) throw pending;
57
+ return null;
58
+ };
59
+
60
+ const view = render(
61
+ <Suspense fallback={null}>
62
+ <Probe value="workspace-a" suspend={false} />
63
+ </Suspense>,
64
+ );
65
+ await waitFor(() => expect(getProvider()).toBeDefined());
66
+
67
+ act(() => {
68
+ startTransition(() => {
69
+ view.rerender(
70
+ <Suspense fallback={null}>
71
+ <Probe value="workspace-b" suspend />
72
+ </Suspense>,
73
+ );
74
+ });
75
+ });
76
+
77
+ expect(renderB).toHaveBeenCalled();
78
+ expect(getProvider()?.getModelContext().system).toBe("workspace-a");
79
+
80
+ view.rerender(
81
+ <Suspense fallback={null}>
82
+ <Probe value="workspace-b" suspend={false} />
83
+ </Suspense>,
84
+ );
85
+
86
+ expect(getProvider()?.getModelContext().system).toBe("workspace-b");
87
+ });
88
+
89
+ it("keeps tool overrides scoped to the committed render", async () => {
90
+ const pending = new Promise<never>(() => {});
91
+ const renderB = vi.fn();
92
+ const Probe = ({ value, suspend }: { value: string; suspend: boolean }) => {
93
+ useAuiToolOverrides({ search: { execute: () => value } });
94
+ if (value === "workspace-b") renderB();
95
+ if (suspend) throw pending;
96
+ return null;
97
+ };
98
+
99
+ const view = render(
100
+ <Suspense fallback={null}>
101
+ <Probe value="workspace-a" suspend={false} />
102
+ </Suspense>,
103
+ );
104
+ await waitFor(() => expect(getProvider()).toBeDefined());
105
+
106
+ act(() => {
107
+ startTransition(() => {
108
+ view.rerender(
109
+ <Suspense fallback={null}>
110
+ <Probe value="workspace-b" suspend />
111
+ </Suspense>,
112
+ );
113
+ });
114
+ });
115
+
116
+ expect(renderB).toHaveBeenCalled();
117
+ expect(getProvider()?.getModelContext().tools?.search?.execute?.()).toBe(
118
+ "workspace-a",
119
+ );
120
+
121
+ view.rerender(
122
+ <Suspense fallback={null}>
123
+ <Probe value="workspace-b" suspend={false} />
124
+ </Suspense>,
125
+ );
126
+
127
+ expect(getProvider()?.getModelContext().tools?.search?.execute?.()).toBe(
128
+ "workspace-b",
129
+ );
130
+ });
131
+ });
@@ -19,7 +19,7 @@ describe("makeToolCallTextComponent", () => {
19
19
  status: { type: "running" },
20
20
  addResult: () => {},
21
21
  resume: () => {},
22
- respondToApproval: () => {},
22
+ respondToApproval: async () => {},
23
23
  }),
24
24
  ).toBe("Searching...");
25
25
 
@@ -34,7 +34,7 @@ describe("makeToolCallTextComponent", () => {
34
34
  status: { type: "complete" },
35
35
  addResult: () => {},
36
36
  resume: () => {},
37
- respondToApproval: () => {},
37
+ respondToApproval: async () => {},
38
38
  }),
39
39
  ).toBe("Done searching");
40
40
  });
@@ -59,7 +59,7 @@ describe("makeToolCallTextComponent", () => {
59
59
  status: { type: "running" },
60
60
  addResult: () => {},
61
61
  resume: () => {},
62
- respondToApproval: () => {},
62
+ respondToApproval: async () => {},
63
63
  }),
64
64
  ).toBe("Searching docs...");
65
65
 
@@ -74,7 +74,7 @@ describe("makeToolCallTextComponent", () => {
74
74
  status: { type: "complete" },
75
75
  addResult: () => {},
76
76
  resume: () => {},
77
- respondToApproval: () => {},
77
+ respondToApproval: async () => {},
78
78
  }),
79
79
  ).toBe("Found 3 results for docs");
80
80
  });
@@ -94,7 +94,7 @@ describe("makeToolCallTextComponent", () => {
94
94
  argsText: "{}",
95
95
  addResult: () => {},
96
96
  resume: () => {},
97
- respondToApproval: () => {},
97
+ respondToApproval: async () => {},
98
98
  } as unknown as ToolCallMessagePartProps),
99
99
  ).toBe("Complete");
100
100
  });
@@ -115,7 +115,7 @@ describe("makeToolCallTextComponent", () => {
115
115
  status: { type: "incomplete", reason: "error" },
116
116
  addResult: () => {},
117
117
  resume: () => {},
118
- respondToApproval: () => {},
118
+ respondToApproval: async () => {},
119
119
  }),
120
120
  ).toBe("Finished");
121
121
  });
@@ -136,7 +136,7 @@ describe("makeToolCallTextComponent", () => {
136
136
  status: { type: "requires-action", reason: "interrupt" },
137
137
  addResult: () => {},
138
138
  resume: () => {},
139
- respondToApproval: () => {},
139
+ respondToApproval: async () => {},
140
140
  }),
141
141
  ).toBe("Waiting for approval...");
142
142
  });
@@ -156,7 +156,7 @@ describe("makeToolCallTextComponent", () => {
156
156
  status: { type: "complete" },
157
157
  addResult: () => {},
158
158
  resume: () => {},
159
- respondToApproval: () => {},
159
+ respondToApproval: async () => {},
160
160
  }),
161
161
  ).toBeNull();
162
162
  });
@@ -176,7 +176,7 @@ describe("makeToolCallTextComponent", () => {
176
176
  status: { type: "running" },
177
177
  addResult: () => {},
178
178
  resume: () => {},
179
- respondToApproval: () => {},
179
+ respondToApproval: async () => {},
180
180
  }),
181
181
  ).toBeNull();
182
182
  });
@@ -9,6 +9,15 @@ import type {
9
9
  ToolCallMessagePartComponent,
10
10
  ToolCallMessagePartProps,
11
11
  } from "../types/MessagePartComponentTypes";
12
+ import {
13
+ resolveToolCallText,
14
+ type ToolCallText as NeutralToolCallText,
15
+ } from "../../model-context/tool-call-text";
16
+
17
+ export type ToolCallText<
18
+ TArgs extends Record<string, unknown>,
19
+ TResult,
20
+ > = NeutralToolCallText<TArgs, TResult, ReactNode>;
12
21
 
13
22
  /**
14
23
  * Resolves whether a tool's UI should be presented standalone (outside the
@@ -61,42 +70,6 @@ type ToolStreamCall<TArgs extends Record<string, unknown>, TResult> = (
61
70
  context: ToolExecuteContext,
62
71
  ) => void;
63
72
 
64
- type ToolCallRunningText<TArgs extends Record<string, unknown>> =
65
- | ReactNode
66
- | ((options: { args: TArgs }) => ReactNode);
67
-
68
- type ToolCallCompleteText<TArgs extends Record<string, unknown>, TResult> =
69
- | ReactNode
70
- | ((options: { args: TArgs; result: TResult | undefined }) => ReactNode);
71
-
72
- export type ToolCallText<TArgs extends Record<string, unknown>, TResult> =
73
- | {
74
- running: ToolCallRunningText<TArgs>;
75
- complete?: ToolCallCompleteText<TArgs, TResult> | undefined;
76
- }
77
- | {
78
- running?: ToolCallRunningText<TArgs> | undefined;
79
- complete: ToolCallCompleteText<TArgs, TResult>;
80
- };
81
-
82
- const resolveToolCallText = <TArgs extends Record<string, unknown>, TResult>(
83
- text: ToolCallText<TArgs, TResult>,
84
- part: ToolCallMessagePartProps<TArgs, TResult>,
85
- ): ReactNode => {
86
- const isRunning =
87
- part.status?.type === "running" || part.status?.type === "requires-action";
88
-
89
- if (!isRunning) {
90
- const value = text.complete;
91
- if (typeof value !== "function") return value ?? null;
92
- return value({ args: part.args, result: part.result });
93
- }
94
-
95
- const value = text.running;
96
- if (typeof value !== "function") return value ?? null;
97
- return value({ args: part.args });
98
- };
99
-
100
73
  export const makeToolCallTextComponent = <
101
74
  TArgs extends Record<string, unknown>,
102
75
  TResult,
@@ -1,4 +1,4 @@
1
- import { useEffect, useRef } from "react";
1
+ import { useEffect, useInsertionEffect, useRef } from "react";
2
2
  import { useAui } from "@assistant-ui/store";
3
3
  import type { AssistantContextConfig } from "../..";
4
4
 
@@ -8,7 +8,9 @@ export const useAssistantContext = (config: AssistantContextConfig) => {
8
8
  const { getContext, disabled = false } = config;
9
9
  const aui = useAui();
10
10
  const getContextRef = useRef(getContext);
11
- getContextRef.current = getContext;
11
+ useInsertionEffect(() => {
12
+ getContextRef.current = getContext;
13
+ }, [getContext]);
12
14
 
13
15
  useEffect(() => {
14
16
  if (disabled) return;
@@ -12,7 +12,7 @@ import {
12
12
  * Props used to register a tool from React.
13
13
  *
14
14
  * @deprecated Use a toolkit with `Tools({ toolkit })` and register it via
15
- * `useAui({ tools: Tools({ toolkit }) })` instead. See
15
+ * `AuiConfig({ tools: Tools({ toolkit }) })` on the provider's `config` prop instead. See
16
16
  * https://assistant-ui.com/docs/migrations/toolkit-tools.
17
17
  */
18
18
  export type AssistantToolProps<
@@ -39,7 +39,7 @@ export type AssistantToolProps<
39
39
  * @param tool - Tool definition and name to register.
40
40
  *
41
41
  * @deprecated Use a toolkit with `Tools({ toolkit })` and register it via
42
- * `useAui({ tools: Tools({ toolkit }) })` instead. See
42
+ * `AuiConfig({ tools: Tools({ toolkit }) })` on the provider's `config` prop instead. See
43
43
  * https://assistant-ui.com/docs/migrations/toolkit-tools.
44
44
  *
45
45
  * @example
@@ -1,4 +1,4 @@
1
- import { useEffect, useRef } from "react";
1
+ import { useEffect, useInsertionEffect, useRef } from "react";
2
2
  import { useAui } from "@assistant-ui/store";
3
3
  import type { Tool } from "assistant-stream";
4
4
 
@@ -25,7 +25,9 @@ type AuiToolOverrides = Record<string, AuiToolOverride<any, any>>;
25
25
  export function useAuiToolOverrides(overrides: AuiToolOverrides): void {
26
26
  const aui = useAui();
27
27
  const overridesRef = useRef(overrides);
28
- overridesRef.current = overrides;
28
+ useInsertionEffect(() => {
29
+ overridesRef.current = overrides;
30
+ }, [overrides]);
29
31
 
30
32
  useEffect(() => {
31
33
  return aui.modelContext.register({
@@ -0,0 +1,48 @@
1
+ // @vitest-environment jsdom
2
+ import { useState, type FC } from "react";
3
+ import { act, cleanup, render, screen } from "@testing-library/react";
4
+ import { afterEach, describe, expect, it } from "vitest";
5
+ import { useInlineRender } from "./useInlineRender";
6
+ import type { ToolCallMessagePartProps } from "../types/MessagePartComponentTypes";
7
+
8
+ afterEach(() => cleanup());
9
+
10
+ type Props = ToolCallMessagePartProps<any, any>;
11
+
12
+ // The point of useInlineRender is a component whose identity survives a
13
+ // changing toolUI, so inner state must not be lost when toolUI swaps.
14
+ const makeToolUI = (label: string): FC<Props> =>
15
+ function Inner() {
16
+ const [n, setN] = useState(0);
17
+ return (
18
+ <button onClick={() => setN((v) => v + 1)}>
19
+ {label}:{n}
20
+ </button>
21
+ );
22
+ };
23
+
24
+ describe("useInlineRender", () => {
25
+ it("follows toolUI changes without remounting the inner tree", async () => {
26
+ let swap!: (fc: FC<Props>) => void;
27
+ const identities = new Set<unknown>();
28
+
29
+ const Host = () => {
30
+ const [toolUI, setToolUI] = useState(() => makeToolUI("a"));
31
+ swap = (fc) => setToolUI(() => fc);
32
+ const ToolUI = useInlineRender(toolUI);
33
+ identities.add(ToolUI);
34
+ return <ToolUI {...({} as Props)} />;
35
+ };
36
+
37
+ render(<Host />);
38
+ expect(screen.getByRole("button").textContent).toBe("a:0");
39
+
40
+ await act(async () => screen.getByRole("button").click());
41
+ expect(screen.getByRole("button").textContent).toBe("a:1");
42
+
43
+ await act(async () => swap(makeToolUI("b")));
44
+ // label follows the new toolUI, counter survives => no remount
45
+ expect(screen.getByRole("button").textContent).toBe("b:1");
46
+ expect(identities.size).toBe(1);
47
+ });
48
+ });
@@ -1,25 +1,29 @@
1
1
  import { type FC, useCallback, useEffect, useState } from "react";
2
2
  import type { ToolCallMessagePartProps } from "../types/MessagePartComponentTypes";
3
- import { create } from "zustand";
3
+ import { WritableSubscribable } from "../../subscribable/subscribable";
4
+ import { useSubscribable } from "../../store/runtime-clients/useSubscribable";
4
5
 
5
6
  export const useInlineRender = <TArgs, TResult>(
6
7
  toolUI: FC<ToolCallMessagePartProps<TArgs, TResult>>,
7
8
  ): FC<ToolCallMessagePartProps<TArgs, TResult>> => {
8
- const [useToolUIStore] = useState(() =>
9
- create(() => ({
10
- toolUI,
11
- })),
9
+ const [toolUIStore] = useState(
10
+ () =>
11
+ new WritableSubscribable<FC<ToolCallMessagePartProps<TArgs, TResult>>>(
12
+ toolUI,
13
+ ),
12
14
  );
13
15
 
14
16
  useEffect(() => {
15
- useToolUIStore.setState({ toolUI });
16
- }, [toolUI, useToolUIStore]);
17
+ toolUIStore.setState(toolUI);
18
+ }, [toolUI, toolUIStore]);
17
19
 
18
20
  return useCallback(
19
21
  function ToolUI(args) {
20
- const store = useToolUIStore();
21
- return store.toolUI(args);
22
+ // Invoked as a plain function so its hooks belong to this component,
23
+ // which keeps its identity while `toolUI` changes underneath.
24
+ const currentToolUI = useSubscribable(toolUIStore);
25
+ return currentToolUI(args);
22
26
  },
23
- [useToolUIStore],
27
+ [toolUIStore],
24
28
  );
25
29
  };