@assistant-ui/core 0.3.16 → 0.3.17

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 (305) hide show
  1. package/dist/index.d.ts +2 -2
  2. package/dist/index.js +2 -2
  3. package/dist/index.js.map +1 -1
  4. package/dist/internal.d.ts +2 -1
  5. package/dist/internal.js +2 -1
  6. package/dist/model-context/frame/provider.d.ts +3 -0
  7. package/dist/model-context/frame/provider.d.ts.map +1 -1
  8. package/dist/model-context/frame/provider.js +71 -21
  9. package/dist/model-context/frame/provider.js.map +1 -1
  10. package/dist/model-context/tool-call-text.d.ts +26 -0
  11. package/dist/model-context/tool-call-text.d.ts.map +1 -0
  12. package/dist/model-context/tool-call-text.js +18 -0
  13. package/dist/model-context/tool-call-text.js.map +1 -0
  14. package/dist/react/RuntimeAdapter.js +1 -1
  15. package/dist/react/adapters/LocalStorageThreadListAdapter.d.ts.map +1 -1
  16. package/dist/react/adapters/LocalStorageThreadListAdapter.js +1 -1
  17. package/dist/react/adapters/LocalStorageThreadListAdapter.js.map +1 -1
  18. package/dist/react/client/AdaptedRemoteThread.js +1 -1
  19. package/dist/react/client/InMemoryThreadList.js +1 -1
  20. package/dist/react/client/Interactables.js +1 -1
  21. package/dist/react/client/RemoteThreadList.d.ts +15 -0
  22. package/dist/react/client/RemoteThreadList.d.ts.map +1 -1
  23. package/dist/react/client/RemoteThreadList.js +240 -138
  24. package/dist/react/client/RemoteThreadList.js.map +1 -1
  25. package/dist/react/client/Tools.d.ts.map +1 -1
  26. package/dist/react/client/Tools.js +6 -2
  27. package/dist/react/client/Tools.js.map +1 -1
  28. package/dist/react/index.d.ts +2 -1
  29. package/dist/react/index.js +2 -1
  30. package/dist/react/interactables-legacy/Interactables.js +4 -4
  31. package/dist/react/interactables-legacy/Interactables.js.map +1 -1
  32. package/dist/react/model-context/toolbox.d.ts +2 -14
  33. package/dist/react/model-context/toolbox.d.ts.map +1 -1
  34. package/dist/react/model-context/toolbox.js +1 -13
  35. package/dist/react/model-context/toolbox.js.map +1 -1
  36. package/dist/react/model-context/useAssistantContext.js +37 -7
  37. package/dist/react/model-context/useAssistantContext.js.map +1 -1
  38. package/dist/react/model-context/useAuiToolOverrides.js +31 -5
  39. package/dist/react/model-context/useAuiToolOverrides.js.map +1 -1
  40. package/dist/react/model-context/useInlineRender.d.ts.map +1 -1
  41. package/dist/react/model-context/useInlineRender.js +10 -36
  42. package/dist/react/model-context/useInlineRender.js.map +1 -1
  43. package/dist/react/primitive-hooks/useMessageError.d.ts.map +1 -1
  44. package/dist/react/primitive-hooks/useMessageError.js +2 -8
  45. package/dist/react/primitive-hooks/useMessageError.js.map +1 -1
  46. package/dist/react/primitive-hooks/useThreadListLoadMore.d.ts.map +1 -1
  47. package/dist/react/primitive-hooks/useThreadListLoadMore.js +2 -4
  48. package/dist/react/primitive-hooks/useThreadListLoadMore.js.map +1 -1
  49. package/dist/react/primitives/composer/ComposerAttachments.d.ts.map +1 -1
  50. package/dist/react/primitives/composer/ComposerAttachments.js +4 -3
  51. package/dist/react/primitives/composer/ComposerAttachments.js.map +1 -1
  52. package/dist/react/primitives/composer/ComposerQueue.d.ts.map +1 -1
  53. package/dist/react/primitives/composer/ComposerQueue.js +4 -3
  54. package/dist/react/primitives/composer/ComposerQueue.js.map +1 -1
  55. package/dist/react/primitives/message/MessageAttachments.d.ts.map +1 -1
  56. package/dist/react/primitives/message/MessageAttachments.js +7 -6
  57. package/dist/react/primitives/message/MessageAttachments.js.map +1 -1
  58. package/dist/react/primitives/message/MessageGroupedParts.js +2 -2
  59. package/dist/react/primitives/message/MessageGroupedParts.js.map +1 -1
  60. package/dist/react/primitives/message/MessageParts.js +3 -3
  61. package/dist/react/primitives/message/MessageParts.js.map +1 -1
  62. package/dist/react/primitives/threadList/ThreadListItems.d.ts.map +1 -1
  63. package/dist/react/primitives/threadList/ThreadListItems.js +5 -4
  64. package/dist/react/primitives/threadList/ThreadListItems.js.map +1 -1
  65. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.d.ts +6 -4
  66. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.d.ts.map +1 -1
  67. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js +44 -22
  68. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js.map +1 -1
  69. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts +6 -5
  70. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts.map +1 -1
  71. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js +79 -62
  72. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js.map +1 -1
  73. package/dist/react/runtimes/RemoteThreadResource.d.ts +1 -6
  74. package/dist/react/runtimes/RemoteThreadResource.d.ts.map +1 -1
  75. package/dist/react/runtimes/RemoteThreadResource.js +3 -3
  76. package/dist/react/runtimes/RemoteThreadResource.js.map +1 -1
  77. package/dist/react/runtimes/assistant-transport/useLatestRef.js +2 -2
  78. package/dist/react/runtimes/assistant-transport/useLatestRef.js.map +1 -1
  79. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.d.ts.map +1 -1
  80. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js +23 -18
  81. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js.map +1 -1
  82. package/dist/react/runtimes/cloud/auiV0.d.ts +5 -1
  83. package/dist/react/runtimes/cloud/auiV0.d.ts.map +1 -1
  84. package/dist/react/runtimes/cloud/auiV0.js.map +1 -1
  85. package/dist/react/runtimes/cloud/createCloudThreadListAdapter.d.ts +29 -0
  86. package/dist/react/runtimes/cloud/createCloudThreadListAdapter.d.ts.map +1 -0
  87. package/dist/react/runtimes/cloud/createCloudThreadListAdapter.js +159 -0
  88. package/dist/react/runtimes/cloud/createCloudThreadListAdapter.js.map +1 -0
  89. package/dist/react/runtimes/cloud/useCloudThreadListAdapter.d.ts +1 -9
  90. package/dist/react/runtimes/cloud/useCloudThreadListAdapter.d.ts.map +1 -1
  91. package/dist/react/runtimes/cloud/useCloudThreadListAdapter.js +16 -153
  92. package/dist/react/runtimes/cloud/useCloudThreadListAdapter.js.map +1 -1
  93. package/dist/react/runtimes/external-message-converter.js +1 -1
  94. package/dist/react/runtimes/external-message-converter.js.map +1 -1
  95. package/dist/react/runtimes/useRemoteThreadListRuntime.d.ts.map +1 -1
  96. package/dist/react/runtimes/useRemoteThreadListRuntime.js +14 -6
  97. package/dist/react/runtimes/useRemoteThreadListRuntime.js.map +1 -1
  98. package/dist/react/types/MessagePartComponentTypes.d.ts +7 -3
  99. package/dist/react/types/MessagePartComponentTypes.d.ts.map +1 -1
  100. package/dist/react/types/scopes/tools.d.ts +2 -0
  101. package/dist/react/types/scopes/tools.d.ts.map +1 -1
  102. package/dist/runtime/api/message-part-runtime.d.ts +2 -2
  103. package/dist/runtime/api/message-part-runtime.d.ts.map +1 -1
  104. package/dist/runtime/api/message-part-runtime.js +1 -1
  105. package/dist/runtime/api/message-part-runtime.js.map +1 -1
  106. package/dist/runtime/api/thread-list-runtime.d.ts +9 -1
  107. package/dist/runtime/api/thread-list-runtime.d.ts.map +1 -1
  108. package/dist/runtime/api/thread-list-runtime.js +5 -0
  109. package/dist/runtime/api/thread-list-runtime.js.map +1 -1
  110. package/dist/runtime/api/thread-runtime.d.ts +1 -1
  111. package/dist/runtime/base/base-thread-runtime-core.d.ts +2 -2
  112. package/dist/runtime/base/base-thread-runtime-core.d.ts.map +1 -1
  113. package/dist/runtime/base/base-thread-runtime-core.js +2 -2
  114. package/dist/runtime/base/base-thread-runtime-core.js.map +1 -1
  115. package/dist/runtime/interfaces/thread-list-runtime-core.d.ts +14 -2
  116. package/dist/runtime/interfaces/thread-list-runtime-core.d.ts.map +1 -1
  117. package/dist/runtime/interfaces/thread-runtime-core.d.ts +15 -1
  118. package/dist/runtime/interfaces/thread-runtime-core.d.ts.map +1 -1
  119. package/dist/runtime/utils/auto-status.d.ts +1 -1
  120. package/dist/runtime/utils/auto-status.d.ts.map +1 -1
  121. package/dist/runtime/utils/auto-status.js +6 -2
  122. package/dist/runtime/utils/auto-status.js.map +1 -1
  123. package/dist/runtime/utils/external-message-conversion.d.ts +3 -2
  124. package/dist/runtime/utils/external-message-conversion.d.ts.map +1 -1
  125. package/dist/runtime/utils/external-message-conversion.js +4 -3
  126. package/dist/runtime/utils/external-message-conversion.js.map +1 -1
  127. package/dist/runtime/utils/resolveToolApprovalResponse.d.ts +6 -3
  128. package/dist/runtime/utils/resolveToolApprovalResponse.d.ts.map +1 -1
  129. package/dist/runtime/utils/resolveToolApprovalResponse.js +12 -3
  130. package/dist/runtime/utils/resolveToolApprovalResponse.js.map +1 -1
  131. package/dist/runtime/utils/thread-message-like.d.ts +2 -10
  132. package/dist/runtime/utils/thread-message-like.d.ts.map +1 -1
  133. package/dist/runtime/utils/thread-message-like.js.map +1 -1
  134. package/dist/runtime/utils/tool-call-tree.d.ts +39 -0
  135. package/dist/runtime/utils/tool-call-tree.d.ts.map +1 -0
  136. package/dist/runtime/utils/tool-call-tree.js +85 -0
  137. package/dist/runtime/utils/tool-call-tree.js.map +1 -0
  138. package/dist/runtimes/external-store/external-store-adapter.d.ts +29 -2
  139. package/dist/runtimes/external-store/external-store-adapter.d.ts.map +1 -1
  140. package/dist/runtimes/external-store/external-store-thread-runtime-core.d.ts +2 -1
  141. package/dist/runtimes/external-store/external-store-thread-runtime-core.d.ts.map +1 -1
  142. package/dist/runtimes/external-store/external-store-thread-runtime-core.js +34 -28
  143. package/dist/runtimes/external-store/external-store-thread-runtime-core.js.map +1 -1
  144. package/dist/runtimes/inert/InertThreadRuntimeCore.d.ts +1 -1
  145. package/dist/runtimes/inert/InertThreadRuntimeCore.d.ts.map +1 -1
  146. package/dist/runtimes/inert/InertThreadRuntimeCore.js.map +1 -1
  147. package/dist/runtimes/local/local-thread-runtime-core.d.ts +1 -1
  148. package/dist/runtimes/local/local-thread-runtime-core.d.ts.map +1 -1
  149. package/dist/runtimes/local/local-thread-runtime-core.js +3 -1
  150. package/dist/runtimes/local/local-thread-runtime-core.js.map +1 -1
  151. package/dist/runtimes/remote-thread-list/remote-thread-state.d.ts +1 -1
  152. package/dist/runtimes/remote-thread-list/remote-thread-state.d.ts.map +1 -1
  153. package/dist/runtimes/remote-thread-list/remote-thread-state.js +30 -13
  154. package/dist/runtimes/remote-thread-list/remote-thread-state.js.map +1 -1
  155. package/dist/runtimes/remote-thread-list/title.d.ts +9 -0
  156. package/dist/runtimes/remote-thread-list/title.d.ts.map +1 -0
  157. package/dist/runtimes/remote-thread-list/title.js +6 -0
  158. package/dist/runtimes/remote-thread-list/title.js.map +1 -0
  159. package/dist/runtimes/remote-thread-list/types.d.ts +6 -5
  160. package/dist/runtimes/remote-thread-list/types.d.ts.map +1 -1
  161. package/dist/runtimes/tool-invocations/ToolInvocationTracker.d.ts +31 -8
  162. package/dist/runtimes/tool-invocations/ToolInvocationTracker.d.ts.map +1 -1
  163. package/dist/runtimes/tool-invocations/ToolInvocationTracker.js +97 -60
  164. package/dist/runtimes/tool-invocations/ToolInvocationTracker.js.map +1 -1
  165. package/dist/store/clients/external-thread.d.ts +1 -1
  166. package/dist/store/clients/external-thread.d.ts.map +1 -1
  167. package/dist/store/clients/external-thread.js +11 -4
  168. package/dist/store/clients/external-thread.js.map +1 -1
  169. package/dist/store/clients/single-thread-list.js +1 -1
  170. package/dist/store/clients/suggestions.d.ts.map +1 -1
  171. package/dist/store/clients/suggestions.js +18 -6
  172. package/dist/store/clients/suggestions.js.map +1 -1
  173. package/dist/store/clients/thread-message-client.js +1 -1
  174. package/dist/store/clients/thread-selection-events.js +1 -1
  175. package/dist/store/index.d.ts +2 -1
  176. package/dist/store/index.js +2 -1
  177. package/dist/store/internal.d.ts +2 -2
  178. package/dist/store/internal.js +2 -2
  179. package/dist/store/primitive-predicates.d.ts +4 -1
  180. package/dist/store/primitive-predicates.d.ts.map +1 -1
  181. package/dist/store/primitive-predicates.js +9 -1
  182. package/dist/store/primitive-predicates.js.map +1 -1
  183. package/dist/store/runtime-clients/composer-runtime-client.js +1 -1
  184. package/dist/store/runtime-clients/message-runtime-client.js +1 -1
  185. package/dist/store/runtime-clients/thread-list-item-runtime-client.js +1 -1
  186. package/dist/store/runtime-clients/thread-list-runtime-client.d.ts.map +1 -1
  187. package/dist/store/runtime-clients/thread-list-runtime-client.js +124 -105
  188. package/dist/store/runtime-clients/thread-list-runtime-client.js.map +1 -1
  189. package/dist/store/runtime-clients/thread-runtime-client.js +1 -1
  190. package/dist/store/runtime-clients/useSubscribable.d.ts +3 -1
  191. package/dist/store/runtime-clients/useSubscribable.d.ts.map +1 -1
  192. package/dist/store/runtime-clients/useSubscribable.js +1 -1
  193. package/dist/store/runtime-clients/useSubscribable.js.map +1 -1
  194. package/dist/store/scopes/part.d.ts +6 -3
  195. package/dist/store/scopes/part.d.ts.map +1 -1
  196. package/dist/subscribable/subscribable.d.ts +8 -1
  197. package/dist/subscribable/subscribable.d.ts.map +1 -1
  198. package/dist/subscribable/subscribable.js +24 -16
  199. package/dist/subscribable/subscribable.js.map +1 -1
  200. package/dist/types/message.d.ts +40 -1
  201. package/dist/types/message.d.ts.map +1 -1
  202. package/dist/types/message.js +7 -1
  203. package/dist/types/message.js.map +1 -1
  204. package/package.json +12 -17
  205. package/src/index.ts +6 -1
  206. package/src/internal.ts +8 -0
  207. package/src/model-context/frame/provider.test.ts +217 -1
  208. package/src/model-context/frame/provider.ts +111 -23
  209. package/src/model-context/tool-call-text.test.ts +67 -0
  210. package/src/model-context/tool-call-text.ts +67 -0
  211. package/src/react/AssistantRuntimeProvider.test.tsx +66 -2
  212. package/src/react/adapters/LocalStorageThreadListAdapter.tsx +1 -3
  213. package/src/react/client/RemoteThreadList.background.test.ts +384 -0
  214. package/src/react/client/RemoteThreadList.concurrent.test.tsx +274 -0
  215. package/src/react/client/RemoteThreadList.test.ts +273 -1
  216. package/src/react/client/RemoteThreadList.ts +293 -83
  217. package/src/react/client/Tools.ts +7 -1
  218. package/src/react/index.ts +4 -0
  219. package/src/react/model-context/modelContextHooks.test.tsx +131 -0
  220. package/src/react/model-context/toolbox.test.ts +9 -9
  221. package/src/react/model-context/toolbox.ts +9 -36
  222. package/src/react/model-context/useAssistantContext.ts +4 -2
  223. package/src/react/model-context/useAuiToolOverrides.ts +4 -2
  224. package/src/react/model-context/useInlineRender.test.tsx +48 -0
  225. package/src/react/model-context/useInlineRender.ts +14 -10
  226. package/src/react/primitive-hooks/useMessageError.ts +2 -19
  227. package/src/react/primitive-hooks/useThreadListLoadMore.ts +2 -3
  228. package/src/react/primitives/composer/ComposerAttachments.test.tsx +76 -0
  229. package/src/react/primitives/composer/ComposerAttachments.tsx +9 -4
  230. package/src/react/primitives/composer/ComposerQueue.test.tsx +72 -0
  231. package/src/react/primitives/composer/ComposerQueue.tsx +7 -4
  232. package/src/react/primitives/message/MessageAttachments.test.tsx +107 -37
  233. package/src/react/primitives/message/MessageAttachments.tsx +10 -7
  234. package/src/react/primitives/message/MessageGroupedParts.tsx +2 -2
  235. package/src/react/primitives/message/MessageParts.tsx +3 -3
  236. package/src/react/primitives/threadList/ThreadListItems.test.tsx +99 -0
  237. package/src/react/primitives/threadList/ThreadListItems.tsx +8 -5
  238. package/src/react/runtimes/RemoteThreadListHookInstanceManager.running.test.tsx +94 -1
  239. package/src/react/runtimes/RemoteThreadListHookInstanceManager.tsx +72 -31
  240. package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.switch-delete.test.ts +169 -0
  241. package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.tsx +114 -68
  242. package/src/react/runtimes/RemoteThreadResource.ts +1 -4
  243. package/src/react/runtimes/assistant-transport/transport-scheduling.test.ts +51 -2
  244. package/src/react/runtimes/assistant-transport/useLatestRef.ts +2 -2
  245. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.test.ts +45 -0
  246. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.test.tsx +65 -1
  247. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.ts +42 -35
  248. package/src/react/runtimes/cloud/auiV0.ts +5 -0
  249. package/src/react/runtimes/cloud/createCloudThreadListAdapter.test.ts +88 -0
  250. package/src/react/runtimes/cloud/createCloudThreadListAdapter.ts +236 -0
  251. package/src/react/runtimes/cloud/useCloudThreadListAdapter.test.tsx +90 -1
  252. package/src/react/runtimes/cloud/useCloudThreadListAdapter.tsx +33 -238
  253. package/src/react/runtimes/external-message-converter.ts +1 -0
  254. package/src/react/runtimes/useRemoteThreadListRuntime.concurrent.test.tsx +216 -0
  255. package/src/react/runtimes/useRemoteThreadListRuntime.ts +24 -6
  256. package/src/react/types/MessagePartComponentTypes.ts +7 -3
  257. package/src/react/types/scopes/tools.ts +2 -0
  258. package/src/runtime/api/message-part-runtime.ts +3 -3
  259. package/src/runtime/api/thread-list-runtime.ts +25 -1
  260. package/src/runtime/base/base-thread-runtime-core.test.ts +94 -3
  261. package/src/runtime/base/base-thread-runtime-core.ts +10 -4
  262. package/src/runtime/interfaces/thread-list-runtime-core.ts +20 -1
  263. package/src/runtime/interfaces/thread-runtime-core.ts +17 -1
  264. package/src/runtime/utils/auto-status.test.ts +57 -0
  265. package/src/runtime/utils/auto-status.ts +13 -1
  266. package/src/runtime/utils/external-message-conversion.test.ts +97 -0
  267. package/src/runtime/utils/external-message-conversion.ts +17 -1
  268. package/src/runtime/utils/resolveToolApprovalResponse.test.ts +73 -0
  269. package/src/runtime/utils/resolveToolApprovalResponse.ts +27 -6
  270. package/src/runtime/utils/thread-message-like.ts +2 -10
  271. package/src/runtime/utils/tool-call-tree.test.ts +191 -0
  272. package/src/runtime/utils/tool-call-tree.ts +97 -0
  273. package/src/runtimes/external-store/external-store-adapter.ts +38 -2
  274. package/src/runtimes/external-store/external-store-thread-runtime-core.ts +56 -33
  275. package/src/runtimes/inert/InertThreadRuntimeCore.ts +1 -1
  276. package/src/runtimes/local/local-thread-runtime-core.test.ts +29 -3
  277. package/src/runtimes/local/local-thread-runtime-core.ts +5 -1
  278. package/src/runtimes/remote-thread-list/remote-thread-state.test.ts +112 -0
  279. package/src/runtimes/remote-thread-list/remote-thread-state.ts +55 -16
  280. package/src/runtimes/remote-thread-list/title.ts +3 -0
  281. package/src/runtimes/remote-thread-list/types.ts +6 -5
  282. package/src/runtimes/tool-invocations/EDGE_CASES.md +110 -5
  283. package/src/runtimes/tool-invocations/ToolInvocationTracker.test.ts +750 -136
  284. package/src/runtimes/tool-invocations/ToolInvocationTracker.ts +144 -91
  285. package/src/store/clients/external-thread.ts +14 -8
  286. package/src/store/clients/suggestions.test.ts +155 -1
  287. package/src/store/clients/suggestions.ts +42 -34
  288. package/src/store/index.ts +4 -0
  289. package/src/store/internal.ts +2 -0
  290. package/src/store/primitive-predicates.ts +35 -0
  291. package/src/store/runtime-clients/thread-list-runtime-client.ts +16 -2
  292. package/src/store/runtime-clients/useSubscribable.ts +8 -2
  293. package/src/store/scopes/part.ts +6 -3
  294. package/src/subscribable/subscribable.test.ts +50 -0
  295. package/src/subscribable/subscribable.ts +33 -21
  296. package/src/tests/RemoteThreadListThreadListRuntimeCore-load-race.test.ts +111 -0
  297. package/src/tests/RemoteThreadListThreadListRuntimeCore-loadMore.test.ts +78 -0
  298. package/src/tests/RemoteThreadListThreadListRuntimeCore-reload.test.ts +52 -0
  299. package/src/tests/external-store-optimistic-id.test.ts +79 -0
  300. package/src/tests/external-store-repository-instance.test.ts +166 -0
  301. package/src/tests/external-store-thread-runtime-core-adapter.test.ts +22 -16
  302. package/src/tests/external-thread-attachments.test.tsx +85 -1
  303. package/src/tests/remote-thread-list-adapter-switch.test.ts +66 -1
  304. package/src/tests/thread-switch-events.test.tsx +397 -2
  305. package/src/types/message.ts +50 -2
@@ -38,7 +38,11 @@ export class AssistantFrameProvider {
38
38
  private _providerUnsubscribes = new Map<symbol, Unsubscribe | undefined>();
39
39
  private _activeToolCalls = new Map<
40
40
  string,
41
- { abortController: AbortController; event: MessageEvent }
41
+ {
42
+ abortController: AbortController;
43
+ event: MessageEvent;
44
+ provider: ModelContextProvider | undefined;
45
+ }
42
46
  >();
43
47
  private _targetOrigin: string;
44
48
  private _strictRegistrations = 0;
@@ -115,10 +119,15 @@ export class AssistantFrameProvider {
115
119
  message: Extract<FrameMessage, { type: "tool-call" }>,
116
120
  event: MessageEvent,
117
121
  ) {
118
- const tool = this.getModelContext().tools?.[message.toolName];
122
+ const resolvedTool = this.getTool(message.toolName);
123
+ const tool = resolvedTool?.tool;
119
124
  const abortController = new AbortController();
120
125
  this._activeToolCalls.get(message.id)?.abortController.abort();
121
- const activeCall = { abortController, event };
126
+ const activeCall = {
127
+ abortController,
128
+ event,
129
+ provider: resolvedTool?.provider,
130
+ };
122
131
  this._activeToolCalls.set(message.id, activeCall);
123
132
 
124
133
  let result: any;
@@ -161,6 +170,37 @@ export class AssistantFrameProvider {
161
170
  activeCall.abortController.abort();
162
171
  }
163
172
 
173
+ private cancelToolCallsForProvider(provider: ModelContextProvider) {
174
+ const matchingCalls = Array.from(this._activeToolCalls).filter(
175
+ ([, activeCall]) => activeCall.provider === provider,
176
+ );
177
+ for (const [id, activeCall] of matchingCalls) {
178
+ this._activeToolCalls.delete(id);
179
+ activeCall.abortController.abort();
180
+ }
181
+
182
+ let sendFailed = false;
183
+ let sendError: unknown;
184
+ for (const [id, activeCall] of matchingCalls) {
185
+ try {
186
+ this.sendMessage(activeCall.event, {
187
+ type: "tool-result",
188
+ id,
189
+ error: "AssistantFrame tool provider has been removed",
190
+ });
191
+ } catch (error) {
192
+ if (sendFailed) {
193
+ console.error(error);
194
+ } else {
195
+ sendFailed = true;
196
+ sendError = error;
197
+ }
198
+ }
199
+ }
200
+
201
+ if (sendFailed) throw sendError;
202
+ }
203
+
164
204
  private sendMessage(event: MessageEvent, message: FrameMessage) {
165
205
  event.source?.postMessage(
166
206
  { channel: FRAME_MESSAGE_CHANNEL, message },
@@ -168,9 +208,26 @@ export class AssistantFrameProvider {
168
208
  );
169
209
  }
170
210
 
211
+ private getProviders() {
212
+ return Array.from(new Set(this._providers.values()));
213
+ }
214
+
215
+ private getTool(toolName: string) {
216
+ let resolved:
217
+ | { provider: ModelContextProvider; tool: Tool<any, any> }
218
+ | undefined;
219
+
220
+ for (const provider of this.getProviders()) {
221
+ const tool = provider.getModelContext().tools?.[toolName];
222
+ if (tool) resolved = { provider, tool };
223
+ }
224
+
225
+ return resolved;
226
+ }
227
+
171
228
  private getModelContext(): ModelContext {
172
- const contexts = Array.from(new Set(this._providers.values())).map((p) =>
173
- p.getModelContext(),
229
+ const contexts = this.getProviders().map((provider) =>
230
+ provider.getModelContext(),
174
231
  );
175
232
 
176
233
  return contexts.reduce(
@@ -200,7 +257,14 @@ export class AssistantFrameProvider {
200
257
  }
201
258
  }
202
259
 
203
- private removeProvider(id: symbol, origin: string): Unsubscribe | undefined {
260
+ private removeProvider(
261
+ id: symbol,
262
+ origin: string,
263
+ ): {
264
+ unsubscribe: Unsubscribe | undefined;
265
+ removedProvider: ModelContextProvider | undefined;
266
+ } {
267
+ const provider = this._providers.get(id);
204
268
  this._providers.delete(id);
205
269
  const unsubscribe = this._providerUnsubscribes.get(id);
206
270
  this._providerUnsubscribes.delete(id);
@@ -219,7 +283,11 @@ export class AssistantFrameProvider {
219
283
  this._wildcardRegistrations > 0 ? "*" : getDefaultTargetOrigin();
220
284
  }
221
285
  }
222
- return unsubscribe;
286
+ const removedProvider =
287
+ provider && !this.getProviders().includes(provider)
288
+ ? provider
289
+ : undefined;
290
+ return { unsubscribe, removedProvider };
223
291
  }
224
292
 
225
293
  static addModelContextProvider(
@@ -246,8 +314,18 @@ export class AssistantFrameProvider {
246
314
 
247
315
  instance.broadcastUpdate();
248
316
  } catch (error) {
249
- const unsubscribe = instance.removeProvider(id, origin);
317
+ const { unsubscribe, removedProvider } = instance.removeProvider(
318
+ id,
319
+ origin,
320
+ );
250
321
  // Rollback failures must not replace the registration error.
322
+ try {
323
+ if (removedProvider) {
324
+ instance.cancelToolCallsForProvider(removedProvider);
325
+ }
326
+ } catch (cancelError) {
327
+ console.error(cancelError);
328
+ }
251
329
  try {
252
330
  unsubscribe?.();
253
331
  } catch (unsubscribeError) {
@@ -265,22 +343,32 @@ export class AssistantFrameProvider {
265
343
  return () => {
266
344
  if (released) return;
267
345
  released = true;
268
- const unsubscribe = instance.removeProvider(id, origin);
269
- let unsubscribeFailed = false;
270
- let unsubscribeError: unknown;
271
- try {
272
- unsubscribe?.();
273
- } catch (error) {
274
- unsubscribeFailed = true;
275
- unsubscribeError = error;
276
- }
277
- try {
278
- instance.broadcastUpdate();
279
- } catch (error) {
280
- if (!unsubscribeFailed) throw error;
281
- console.error(error);
346
+ const { unsubscribe, removedProvider } = instance.removeProvider(
347
+ id,
348
+ origin,
349
+ );
350
+ let cleanupFailed = false;
351
+ let cleanupError: unknown;
352
+ const runCleanup = (cleanup: () => void) => {
353
+ try {
354
+ cleanup();
355
+ } catch (error) {
356
+ if (cleanupFailed) {
357
+ console.error(error);
358
+ } else {
359
+ cleanupFailed = true;
360
+ cleanupError = error;
361
+ }
362
+ }
363
+ };
364
+
365
+ if (removedProvider) {
366
+ runCleanup(() => instance.cancelToolCallsForProvider(removedProvider));
282
367
  }
283
- if (unsubscribeFailed) throw unsubscribeError;
368
+ if (unsubscribe) runCleanup(unsubscribe);
369
+ runCleanup(() => instance.broadcastUpdate());
370
+
371
+ if (cleanupFailed) throw cleanupError;
284
372
  };
285
373
  }
286
374
 
@@ -0,0 +1,67 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { resolveToolCallText } from "./tool-call-text";
3
+
4
+ describe("resolveToolCallText", () => {
5
+ it("resolves literal text for running and complete tool calls", () => {
6
+ const text = {
7
+ running: "Searching...",
8
+ complete: "Done searching",
9
+ };
10
+
11
+ expect(
12
+ resolveToolCallText(text, {
13
+ args: {},
14
+ status: { type: "running" },
15
+ }),
16
+ ).toBe("Searching...");
17
+ expect(
18
+ resolveToolCallText(text, {
19
+ args: {},
20
+ status: { type: "complete" },
21
+ }),
22
+ ).toBe("Done searching");
23
+ });
24
+
25
+ it("passes args and results to text functions", () => {
26
+ const text = {
27
+ running: ({ args }: { args: { query: string } }) =>
28
+ `Searching ${args.query}...`,
29
+ complete: ({
30
+ args,
31
+ result,
32
+ }: {
33
+ args: { query: string };
34
+ result: number | undefined;
35
+ }) => `Found ${result ?? 0} results for ${args.query}`,
36
+ };
37
+
38
+ expect(
39
+ resolveToolCallText(text, {
40
+ args: { query: "docs" },
41
+ status: { type: "requires-action" },
42
+ }),
43
+ ).toBe("Searching docs...");
44
+ expect(
45
+ resolveToolCallText(text, {
46
+ args: { query: "docs" },
47
+ result: 3,
48
+ status: { type: "complete" },
49
+ }),
50
+ ).toBe("Found 3 results for docs");
51
+ });
52
+
53
+ it("returns null when the active status has no text", () => {
54
+ expect(
55
+ resolveToolCallText(
56
+ { running: "Searching..." },
57
+ { args: {}, status: { type: "complete" } },
58
+ ),
59
+ ).toBeNull();
60
+ expect(
61
+ resolveToolCallText(
62
+ { complete: "Done searching" },
63
+ { args: {}, status: { type: "running" } },
64
+ ),
65
+ ).toBeNull();
66
+ });
67
+ });
@@ -0,0 +1,67 @@
1
+ type ToolCallRunningText<TArgs extends Record<string, unknown>, TValue> =
2
+ | TValue
3
+ | undefined
4
+ | null
5
+ | ((options: { args: TArgs }) => TValue | undefined | null);
6
+
7
+ type ToolCallCompleteText<
8
+ TArgs extends Record<string, unknown>,
9
+ TResult,
10
+ TValue,
11
+ > =
12
+ | TValue
13
+ | undefined
14
+ | null
15
+ | ((options: {
16
+ args: TArgs;
17
+ result: TResult | undefined;
18
+ }) => TValue | undefined | null);
19
+
20
+ export type ToolCallText<
21
+ TArgs extends Record<string, unknown>,
22
+ TResult,
23
+ TValue = string,
24
+ > =
25
+ | {
26
+ running: ToolCallRunningText<TArgs, TValue>;
27
+ complete?: ToolCallCompleteText<TArgs, TResult, TValue> | undefined;
28
+ }
29
+ | {
30
+ running?: ToolCallRunningText<TArgs, TValue> | undefined;
31
+ complete: ToolCallCompleteText<TArgs, TResult, TValue>;
32
+ };
33
+
34
+ type ToolCallTextPart<TArgs extends Record<string, unknown>, TResult> = {
35
+ readonly args: TArgs;
36
+ readonly result?: TResult | undefined;
37
+ readonly status?: { readonly type?: string | undefined } | undefined;
38
+ };
39
+
40
+ export const resolveToolCallText = <
41
+ TArgs extends Record<string, unknown>,
42
+ TResult,
43
+ TValue,
44
+ >(
45
+ text: ToolCallText<TArgs, TResult, TValue>,
46
+ part: ToolCallTextPart<TArgs, TResult>,
47
+ ): TValue | undefined | null => {
48
+ const isRunning =
49
+ part.status?.type === "running" || part.status?.type === "requires-action";
50
+
51
+ if (!isRunning) {
52
+ const value = text.complete;
53
+ if (typeof value !== "function") return value ?? null;
54
+ return (
55
+ value as (options: {
56
+ args: TArgs;
57
+ result: TResult | undefined;
58
+ }) => TValue | undefined | null
59
+ )({ args: part.args, result: part.result });
60
+ }
61
+
62
+ const value = text.running;
63
+ if (typeof value !== "function") return value ?? null;
64
+ return (value as (options: { args: TArgs }) => TValue | undefined | null)({
65
+ args: part.args,
66
+ });
67
+ };
@@ -1,8 +1,8 @@
1
1
  // @vitest-environment jsdom
2
2
 
3
3
  import type { ReactNode } from "react";
4
- import { useEffect } from "react";
5
- import { cleanup, render } from "@testing-library/react";
4
+ import { useEffect, useState } from "react";
5
+ import { act, cleanup, render } from "@testing-library/react";
6
6
  import { afterEach, describe, expect, it } from "vitest";
7
7
  import { resource } from "@assistant-ui/tap";
8
8
  import {
@@ -12,8 +12,10 @@ import {
12
12
  type AssistantClient,
13
13
  } from "@assistant-ui/store";
14
14
  import type { ChatModelAdapter } from "../runtime/utils/chat-model-adapter";
15
+ import type { ThreadMessage } from "../types/message";
15
16
  import { AssistantRuntimeProvider } from "./AssistantRuntimeProvider";
16
17
  import { useLocalRuntime } from "./runtimes/useLocalRuntime";
18
+ import { useExternalStoreRuntime } from "./runtimes/useExternalStoreRuntime";
17
19
 
18
20
  type AnyClient = Record<string, any>;
19
21
 
@@ -21,6 +23,20 @@ const chatModel: ChatModelAdapter = {
21
23
  run: async () => ({ content: [] }),
22
24
  };
23
25
 
26
+ const hoistedConfig = AuiConfig({});
27
+
28
+ const message = (id: string, role: "user" | "assistant"): ThreadMessage =>
29
+ ({
30
+ id,
31
+ role,
32
+ content: [{ type: "text", text: `text of ${id}` }],
33
+ createdAt: new Date(1718000000000),
34
+ ...(role === "assistant"
35
+ ? { status: { type: "complete", reason: "stop" } }
36
+ : { attachments: [] }),
37
+ metadata: { custom: {} },
38
+ }) as ThreadMessage;
39
+
24
40
  const makeCounterClient = (log?: string[]) => {
25
41
  const useCounterClient = () => {
26
42
  useEffect(() => {
@@ -152,4 +168,52 @@ describe("AssistantRuntimeProvider aui composition", () => {
152
168
 
153
169
  expect(aui.threads.getState().main).toBeDefined();
154
170
  });
171
+
172
+ const countPublicationsOverThreeRerenders = async (
173
+ makeMessages: () => ThreadMessage[],
174
+ ) => {
175
+ let advance!: () => void;
176
+ let notifications = 0;
177
+
178
+ const Subscriber = () => {
179
+ const aui = useAui();
180
+ useEffect(() => aui.subscribe(() => notifications++), [aui]);
181
+ return null;
182
+ };
183
+
184
+ const App = () => {
185
+ const [, setTick] = useState(0);
186
+ advance = () => setTick((tick) => tick + 1);
187
+ const runtime = useExternalStoreRuntime<ThreadMessage>({
188
+ messages: makeMessages(),
189
+ onNew: async () => {},
190
+ });
191
+
192
+ return (
193
+ <AssistantRuntimeProvider runtime={runtime} config={hoistedConfig}>
194
+ <Subscriber />
195
+ </AssistantRuntimeProvider>
196
+ );
197
+ };
198
+
199
+ render(<App />);
200
+ notifications = 0;
201
+
202
+ await act(async () => advance());
203
+ await act(async () => advance());
204
+ await act(async () => advance());
205
+
206
+ return notifications;
207
+ };
208
+
209
+ it("publishes once when the external-store runtime owner rerenders", async () => {
210
+ expect(await countPublicationsOverThreeRerenders(() => [])).toBe(3);
211
+ });
212
+
213
+ it("publishes once per rerender when the thread already has messages", async () => {
214
+ const messages = [message("m1", "user"), message("m2", "assistant")];
215
+ expect(await countPublicationsOverThreeRerenders(() => [...messages])).toBe(
216
+ 3,
217
+ );
218
+ });
155
219
  });
@@ -21,6 +21,7 @@ import type {
21
21
  ExportedMessageRepository,
22
22
  ExportedMessageRepositoryItem,
23
23
  } from "../../internal";
24
+ import { isRecord } from "../../utils/json/is-json";
24
25
  import {
25
26
  RuntimeAdapterProvider,
26
27
  type RuntimeAdapters,
@@ -82,9 +83,6 @@ type StoredSystemMessage = Extract<ThreadMessage, { role: "system" }>;
82
83
  type StoredUserMessage = Extract<ThreadMessage, { role: "user" }>;
83
84
  type StoredAssistantMessage = Extract<ThreadMessage, { role: "assistant" }>;
84
85
 
85
- const isRecord = (value: unknown): value is Record<string, unknown> =>
86
- typeof value === "object" && value !== null && !Array.isArray(value);
87
-
88
86
  const parseJSON = (raw: string | null): unknown => {
89
87
  if (!raw) return undefined;
90
88
  try {