@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
@@ -0,0 +1,79 @@
1
+ import { describe, it, expect, vi } from "vitest";
2
+ import { ExternalStoreThreadRuntimeCore } from "../runtimes/external-store/external-store-thread-runtime-core";
3
+ import type { ExternalStoreAdapter } from "../runtimes/external-store/external-store-adapter";
4
+ import type { ModelContextProvider } from "../model-context/types";
5
+ import type { ThreadMessage } from "../types/message";
6
+
7
+ const createContextProvider = (): ModelContextProvider => ({
8
+ getModelContext: () => ({}),
9
+ });
10
+
11
+ const createUserMessage = (id: string, text = "Hello"): ThreadMessage =>
12
+ ({
13
+ id,
14
+ role: "user" as const,
15
+ createdAt: new Date(),
16
+ content: [{ type: "text" as const, text }],
17
+ attachments: [],
18
+ metadata: {
19
+ custom: {},
20
+ },
21
+ }) as ThreadMessage;
22
+
23
+ const createAssistantMessage = (id: string, text = "Hi there"): ThreadMessage =>
24
+ ({
25
+ id,
26
+ role: "assistant" as const,
27
+ createdAt: new Date(),
28
+ content: [{ type: "text" as const, text }],
29
+ status: { type: "complete" as const, reason: "stop" as const },
30
+ metadata: {
31
+ unstable_state: null,
32
+ unstable_annotations: [],
33
+ unstable_data: [],
34
+ steps: [],
35
+ custom: {},
36
+ },
37
+ }) as ThreadMessage;
38
+
39
+ const createAdapter = (
40
+ messages: readonly ThreadMessage[],
41
+ isRunning: boolean,
42
+ ): ExternalStoreAdapter<ThreadMessage> => ({
43
+ messages,
44
+ isRunning,
45
+ onNew: vi.fn(async () => {}),
46
+ });
47
+
48
+ describe("ExternalStoreThreadRuntimeCore optimistic placeholder id", () => {
49
+ it("keeps one placeholder id per pending response and issues a fresh one per episode", () => {
50
+ const u1 = createUserMessage("u1");
51
+ const core = new ExternalStoreThreadRuntimeCore(
52
+ createContextProvider(),
53
+ createAdapter([u1], true),
54
+ );
55
+ const first = core.messages.at(-1)!;
56
+ expect(first.role).toBe("assistant");
57
+ expect(first.content).toHaveLength(0);
58
+
59
+ core.__internal_setAdapter(createAdapter([u1], true));
60
+ core.__internal_setAdapter(createAdapter([u1], true));
61
+ expect(core.messages.at(-1)!.id).toBe(first.id);
62
+
63
+ const u2 = createUserMessage("u2", "follow-up");
64
+ core.__internal_setAdapter(createAdapter([u1, u2], true));
65
+ const second = core.messages.at(-1)!;
66
+ expect(second.role).toBe("assistant");
67
+ expect(second.id).not.toBe(first.id);
68
+
69
+ const a1 = createAssistantMessage("a1");
70
+ core.__internal_setAdapter(createAdapter([u1, u2, a1], false));
71
+ expect(core.messages.at(-1)!.id).toBe("a1");
72
+
73
+ const u3 = createUserMessage("u3", "again");
74
+ core.__internal_setAdapter(createAdapter([u1, u2, a1, u3], true));
75
+ const third = core.messages.at(-1)!;
76
+ expect(third.role).toBe("assistant");
77
+ expect(third.id).not.toBe(second.id);
78
+ });
79
+ });
@@ -0,0 +1,166 @@
1
+ import { describe, it, expect, vi } from "vitest";
2
+ import { ExternalStoreThreadRuntimeCore } from "../runtimes/external-store/external-store-thread-runtime-core";
3
+ import type { ExternalStoreAdapter } from "../runtimes/external-store/external-store-adapter";
4
+ import type { ModelContextProvider } from "../model-context/types";
5
+ import type { ThreadMessage } from "../types/message";
6
+ import { MessageRepository } from "../runtime/utils/message-repository";
7
+
8
+ const createContextProvider = (): ModelContextProvider => ({
9
+ getModelContext: () => ({}),
10
+ });
11
+
12
+ const createUserMessage = (id: string, text = "Hello"): ThreadMessage =>
13
+ ({
14
+ id,
15
+ role: "user" as const,
16
+ createdAt: new Date(),
17
+ content: [{ type: "text" as const, text }],
18
+ attachments: [],
19
+ metadata: {
20
+ custom: {},
21
+ },
22
+ }) as ThreadMessage;
23
+
24
+ const createAssistantMessage = (id: string, text = "Hi there"): ThreadMessage =>
25
+ ({
26
+ id,
27
+ role: "assistant" as const,
28
+ createdAt: new Date(),
29
+ content: [{ type: "text" as const, text }],
30
+ status: { type: "complete" as const, reason: "stop" as const },
31
+ metadata: {
32
+ unstable_state: null,
33
+ unstable_annotations: [],
34
+ unstable_data: [],
35
+ steps: [],
36
+ custom: {},
37
+ },
38
+ }) as ThreadMessage;
39
+
40
+ const createAdapter = (
41
+ messages: readonly ThreadMessage[],
42
+ repository: MessageRepository,
43
+ ): ExternalStoreAdapter<ThreadMessage> => ({
44
+ messages,
45
+ onNew: vi.fn(async () => {}),
46
+ unstable_messageRepositoryInstance: repository,
47
+ });
48
+
49
+ describe("ExternalStoreThreadRuntimeCore repository instance swap", () => {
50
+ it("keeps each conversation's history in its own repository across swaps", () => {
51
+ const repoA = new MessageRepository();
52
+ const repoB = new MessageRepository();
53
+ const threadA = [
54
+ createUserMessage("a-u1", "thread a question"),
55
+ createAssistantMessage("a-m1", "thread a answer"),
56
+ ];
57
+ const threadB = [
58
+ createUserMessage("b-u1", "thread b question"),
59
+ createAssistantMessage("b-m1", "thread b answer"),
60
+ ];
61
+
62
+ const core = new ExternalStoreThreadRuntimeCore(
63
+ createContextProvider(),
64
+ createAdapter(threadA, repoA),
65
+ );
66
+ expect(core.messages.map((m) => m.id)).toEqual(["a-u1", "a-m1"]);
67
+
68
+ core.__internal_setAdapter(createAdapter(threadB, repoB));
69
+ expect(core.messages.map((m) => m.id)).toEqual(["b-u1", "b-m1"]);
70
+ expect(core.getBranches("b-u1")).toEqual(["b-u1"]);
71
+
72
+ core.__internal_setAdapter(createAdapter(threadA, repoA));
73
+ expect(core.messages.map((m) => m.id)).toEqual(["a-u1", "a-m1"]);
74
+ expect(core.getBranches("a-u1")).toEqual(["a-u1"]);
75
+ expect(repoB.getMessages().map((m) => m.id)).toEqual(["b-u1", "b-m1"]);
76
+ });
77
+
78
+ it("preserves intra-thread branches across a swap through an empty conversation", () => {
79
+ const repoA = new MessageRepository();
80
+ const repoB = new MessageRepository();
81
+ const original = [
82
+ createUserMessage("a-u1"),
83
+ createAssistantMessage("a-m1"),
84
+ ];
85
+ const edited = [
86
+ createUserMessage("a-u2", "edited question"),
87
+ createAssistantMessage("a-m2", "edited answer"),
88
+ ];
89
+
90
+ const core = new ExternalStoreThreadRuntimeCore(
91
+ createContextProvider(),
92
+ createAdapter(original, repoA),
93
+ );
94
+ repoA.addOrUpdateMessage(null, edited[0]!);
95
+ repoA.addOrUpdateMessage("a-u2", edited[1]!);
96
+ core.__internal_setAdapter(createAdapter(edited, repoA));
97
+ expect(core.getBranches("a-u2")).toEqual(["a-u1", "a-u2"]);
98
+
99
+ core.__internal_setAdapter(createAdapter([], repoB));
100
+ expect(core.messages).toHaveLength(0);
101
+
102
+ core.__internal_setAdapter(createAdapter(edited, repoA));
103
+ expect(core.messages.map((m) => m.id)).toEqual(["a-u2", "a-m2"]);
104
+ expect(core.getBranches("a-u2")).toEqual(["a-u1", "a-u2"]);
105
+ });
106
+
107
+ it("does not adopt anything when the instance is omitted", () => {
108
+ const messages = [createUserMessage("u1")];
109
+ const core = new ExternalStoreThreadRuntimeCore(createContextProvider(), {
110
+ messages,
111
+ onNew: vi.fn(async () => {}),
112
+ });
113
+ expect(core.messages.map((m) => m.id)).toEqual(["u1"]);
114
+
115
+ const next = [createUserMessage("u1"), createAssistantMessage("m1")];
116
+ core.__internal_setAdapter({
117
+ messages: next,
118
+ onNew: vi.fn(async () => {}),
119
+ });
120
+ expect(core.messages.map((m) => m.id)).toEqual(["u1", "m1"]);
121
+ });
122
+
123
+ it("reconciles a same-reference messages array when only the repository swapped", () => {
124
+ const repoA = new MessageRepository();
125
+ const repoB = new MessageRepository();
126
+ const shared = [createUserMessage("s-u1")];
127
+
128
+ const core = new ExternalStoreThreadRuntimeCore(
129
+ createContextProvider(),
130
+ createAdapter(shared, repoA),
131
+ );
132
+ expect(repoA.getMessages().map((m) => m.id)).toEqual(["s-u1"]);
133
+ expect(repoB.getMessages()).toHaveLength(0);
134
+
135
+ core.__internal_setAdapter(createAdapter(shared, repoB));
136
+ expect(repoB.getMessages().map((m) => m.id)).toEqual(["s-u1"]);
137
+ expect(core.messages.map((m) => m.id)).toEqual(["s-u1"]);
138
+ });
139
+
140
+ it("re-imports a same-reference repository snapshot when the instance swapped", () => {
141
+ const repoA = new MessageRepository();
142
+ const repoB = new MessageRepository();
143
+ const snapshot = {
144
+ headId: "s-m1",
145
+ messages: [
146
+ { parentId: null, message: createUserMessage("s-u1") },
147
+ { parentId: "s-u1", message: createAssistantMessage("s-m1") },
148
+ ],
149
+ };
150
+
151
+ const core = new ExternalStoreThreadRuntimeCore(createContextProvider(), {
152
+ messageRepository: snapshot,
153
+ onNew: vi.fn(async () => {}),
154
+ unstable_messageRepositoryInstance: repoA,
155
+ });
156
+ expect(core.messages.map((m) => m.id)).toEqual(["s-u1", "s-m1"]);
157
+
158
+ core.__internal_setAdapter({
159
+ messageRepository: snapshot,
160
+ onNew: vi.fn(async () => {}),
161
+ unstable_messageRepositoryInstance: repoB,
162
+ });
163
+ expect(core.messages.map((m) => m.id)).toEqual(["s-u1", "s-m1"]);
164
+ expect(repoB.getMessages().map((m) => m.id)).toEqual(["s-u1", "s-m1"]);
165
+ });
166
+ });
@@ -998,7 +998,7 @@ describe("ExternalStoreThreadRuntimeCore adapter contract", () => {
998
998
  core.__internal_setAdapter(
999
999
  createBaseAdapter({
1000
1000
  unstable_enableToolInvocations: true,
1001
- isRunning: true,
1001
+ isRunning: false,
1002
1002
  messages: [
1003
1003
  {
1004
1004
  ...createAssistantMessage("a1"),
@@ -1068,11 +1068,8 @@ describe("ExternalStoreThreadRuntimeCore adapter contract", () => {
1068
1068
  expect(rejections).toEqual([]);
1069
1069
  });
1070
1070
 
1071
- it("handles rejected onRespondToToolApproval callbacks", async () => {
1071
+ it("hands a rejected onRespondToToolApproval back to the caller", async () => {
1072
1072
  const error = new Error("approval failed");
1073
- const consoleError = vi
1074
- .spyOn(console, "error")
1075
- .mockImplementation(() => {});
1076
1073
  let callbackCalls = 0;
1077
1074
  const core = new ExternalStoreThreadRuntimeCore(
1078
1075
  contextProvider,
@@ -1085,23 +1082,32 @@ describe("ExternalStoreThreadRuntimeCore adapter contract", () => {
1085
1082
  );
1086
1083
 
1087
1084
  const rejections = await captureUnhandledRejections(async () => {
1088
- core.respondToToolApproval({
1089
- approvalId: "approval-1",
1090
- approved: true,
1091
- });
1092
-
1093
- await vi.waitFor(() =>
1094
- expect(consoleError).toHaveBeenCalledWith(
1095
- "[ExternalStoreThreadRuntimeCore] onRespondToToolApproval callback rejected",
1096
- error,
1097
- ),
1098
- );
1085
+ await expect(
1086
+ core.respondToToolApproval({
1087
+ approvalId: "approval-1",
1088
+ approved: true,
1089
+ }),
1090
+ ).rejects.toBe(error);
1099
1091
  });
1100
1092
 
1101
1093
  expect(callbackCalls).toBe(1);
1102
1094
  expect(rejections).toEqual([]);
1103
1095
  });
1104
1096
 
1097
+ it("resolves when a synchronous onRespondToToolApproval accepts", async () => {
1098
+ const core = new ExternalStoreThreadRuntimeCore(
1099
+ contextProvider,
1100
+ createBaseAdapter({ onRespondToToolApproval: () => {} }),
1101
+ );
1102
+
1103
+ await expect(
1104
+ core.respondToToolApproval({
1105
+ approvalId: "approval-1",
1106
+ approved: true,
1107
+ }),
1108
+ ).resolves.toBeUndefined();
1109
+ });
1110
+
1105
1111
  it("handles rejected onCancel callbacks", async () => {
1106
1112
  const error = new Error("cancel failed");
1107
1113
  const consoleError = vi
@@ -2,7 +2,7 @@
2
2
 
3
3
  import { act, render, waitFor } from "@testing-library/react";
4
4
  import type { FC } from "react";
5
- import { afterEach, describe, expect, it, vi } from "vitest";
5
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
6
6
  import { AuiProvider, useAui } from "@assistant-ui/store";
7
7
  import type {
8
8
  ExternalThreadMessage,
@@ -14,6 +14,24 @@ import type {
14
14
  PendingAttachment,
15
15
  } from "../types/attachment";
16
16
 
17
+ const { mockGenerateId, realGenerateId } = vi.hoisted(() => {
18
+ const realGenerateId = { current: (): string => "" };
19
+ return {
20
+ realGenerateId,
21
+ mockGenerateId: vi.fn(() => realGenerateId.current()),
22
+ };
23
+ });
24
+
25
+ vi.mock("../utils/id", async (importOriginal) => {
26
+ const actual = await importOriginal<typeof import("../utils/id")>();
27
+ realGenerateId.current = actual.generateId;
28
+ return { ...actual, generateId: mockGenerateId };
29
+ });
30
+
31
+ beforeEach(() => {
32
+ mockGenerateId.mockReset();
33
+ });
34
+
17
35
  const renderThreadWithProps = (props: Partial<ExternalThreadProps>) => {
18
36
  const captured: { aui?: ReturnType<typeof useAui> } = {};
19
37
  const Capture: FC = () => {
@@ -57,6 +75,72 @@ const renderThread = () => {
57
75
  };
58
76
 
59
77
  describe("ExternalThread attachments", () => {
78
+ it("uses generated IDs unless a prepared attachment supplies one", async () => {
79
+ const aui = renderThread();
80
+ mockGenerateId
81
+ .mockReturnValueOnce("generated-file-id")
82
+ .mockReturnValueOnce("generated-prepared-id");
83
+
84
+ await act(async () => {
85
+ await aui()
86
+ .thread()
87
+ .composer()
88
+ .addAttachment(new File(["data"], "photo.png", { type: "image/png" }));
89
+ await aui()
90
+ .thread()
91
+ .composer()
92
+ .addAttachment({
93
+ name: "notes.txt",
94
+ contentType: "text/plain",
95
+ content: [{ type: "text", text: "hello" }],
96
+ });
97
+ await aui().thread().composer().addAttachment({
98
+ id: "provided-id",
99
+ name: "report.pdf",
100
+ contentType: "application/pdf",
101
+ content: [],
102
+ });
103
+ });
104
+
105
+ await waitFor(() => {
106
+ expect(
107
+ aui()
108
+ .thread()
109
+ .composer()
110
+ .getState()
111
+ .attachments.map((attachment) => attachment.id),
112
+ ).toEqual(["generated-file-id", "generated-prepared-id", "provided-id"]);
113
+ });
114
+ expect(mockGenerateId).toHaveBeenCalledTimes(2);
115
+ });
116
+
117
+ it("generates distinct IDs for attachments without one", async () => {
118
+ const aui = renderThread();
119
+
120
+ await act(async () => {
121
+ await aui()
122
+ .thread.composer()
123
+ .addAttachment(new File(["data"], "photo.png", { type: "image/png" }));
124
+ await aui()
125
+ .thread.composer()
126
+ .addAttachment({
127
+ name: "notes.txt",
128
+ contentType: "text/plain",
129
+ content: [{ type: "text", text: "hello" }],
130
+ });
131
+ });
132
+
133
+ await waitFor(() =>
134
+ expect(aui().thread.composer().getState().attachments).toHaveLength(2),
135
+ );
136
+ const ids = aui()
137
+ .thread.composer()
138
+ .getState()
139
+ .attachments.map((attachment) => attachment.id);
140
+ expect(ids.every((id) => id.length > 0)).toBe(true);
141
+ expect(new Set(ids).size).toBe(2);
142
+ });
143
+
60
144
  it("adds prepared attachments when File is unavailable", async () => {
61
145
  const aui = renderThread();
62
146
  const fileConstructor = globalThis.File;
@@ -51,6 +51,27 @@ describe("RemoteThreadList adapter changes", () => {
51
51
  expect(adapterB.rename).not.toHaveBeenCalled();
52
52
  });
53
53
 
54
+ it("clears loading when the replaced adapter's list never settles", async () => {
55
+ const adapterA = makeAdapter({
56
+ list: vi.fn(() => new Promise<never>(() => {})),
57
+ });
58
+ const adapterB = makeAdapter({
59
+ list: vi.fn(async () => ({ threads: [thread("thread-b")] })),
60
+ });
61
+ const core = createCore(adapterA);
62
+
63
+ void core.getLoadThreadsPromise();
64
+
65
+ core.__internal_setOptions({
66
+ adapter: adapterB,
67
+ runtimeHook: () => ({}) as never,
68
+ });
69
+ await core.getLoadThreadsPromise();
70
+
71
+ expect(core.threadIds).toEqual(["thread-b"]);
72
+ expect(core.isLoading).toBe(false);
73
+ });
74
+
54
75
  it("does not resume an old adapter mutation through the new adapter", async () => {
55
76
  const adapterA = makeAdapter({
56
77
  list: async () => ({ threads: [thread("thread-a")] }),
@@ -378,6 +399,50 @@ describe("RemoteThreadList adapter changes", () => {
378
399
 
379
400
  expect(core.getItemById(draftId!)?.remoteId).toBe("created-on-b");
380
401
  expect(core.mainThreadId).toBe(draftId);
381
- expect(core.threadIds).toContain("created-on-b");
402
+ expect(core.threadIds).toContain(draftId);
403
+ expect(core.getItemById("created-on-b")?.id).toBe(draftId);
404
+ });
405
+
406
+ it("keeps one slot when the replacement list already carries the initialized thread", async () => {
407
+ const adapterA = makeAdapter({
408
+ list: async () => ({ threads: [thread("thread-a")] }),
409
+ });
410
+ const listB = deferred<{ threads: ReturnType<typeof thread>[] }>();
411
+ const adapterB = makeAdapter({
412
+ list: vi.fn(() => listB.promise),
413
+ initialize: vi.fn(async () => ({
414
+ remoteId: "created-on-b",
415
+ externalId: "created-on-b",
416
+ })),
417
+ });
418
+ const core = createCore(adapterA);
419
+
420
+ await core.getLoadThreadsPromise();
421
+ const draftId = core.newThreadId;
422
+
423
+ core.__internal_setOptions({
424
+ adapter: adapterB,
425
+ runtimeHook: () => ({}) as never,
426
+ });
427
+ await core.initialize(draftId!);
428
+
429
+ listB.resolve({
430
+ threads: [thread("created-on-b"), thread("thread-b")],
431
+ });
432
+ await core.getLoadThreadsPromise();
433
+
434
+ expect(
435
+ Object.values(core.threadItems).filter(
436
+ (item) => item.remoteId === "created-on-b",
437
+ ),
438
+ ).toHaveLength(1);
439
+ expect(core.threadIds).toEqual([draftId, "thread-b"]);
440
+ expect(core.getItemById("created-on-b")?.id).toBe(draftId);
441
+
442
+ await core.delete("created-on-b");
443
+
444
+ expect(core.getItemById("created-on-b")).toBeUndefined();
445
+ expect(core.getItemById(draftId!)).toBeUndefined();
446
+ expect(core.threadIds).toEqual(["thread-b"]);
382
447
  });
383
448
  });