@assistant-ui/core 0.3.12 → 0.3.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (452) hide show
  1. package/dist/adapters/speech.d.ts.map +1 -1
  2. package/dist/adapters/speech.js +9 -15
  3. package/dist/adapters/speech.js.map +1 -1
  4. package/dist/adapters/thread-history.d.ts +2 -0
  5. package/dist/adapters/thread-history.d.ts.map +1 -1
  6. package/dist/adapters/voice.d.ts.map +1 -1
  7. package/dist/adapters/voice.js +27 -15
  8. package/dist/adapters/voice.js.map +1 -1
  9. package/dist/index.d.ts +3 -3
  10. package/dist/index.js +2 -2
  11. package/dist/index.js.map +1 -1
  12. package/dist/internal.d.ts +1 -1
  13. package/dist/internal.js +6 -6
  14. package/dist/model-context/frame/host.d.ts.map +1 -1
  15. package/dist/model-context/frame/host.js +2 -1
  16. package/dist/model-context/frame/host.js.map +1 -1
  17. package/dist/model-context/registry.d.ts.map +1 -1
  18. package/dist/model-context/registry.js +16 -4
  19. package/dist/model-context/registry.js.map +1 -1
  20. package/dist/react/RuntimeAdapter.d.ts +8 -1
  21. package/dist/react/RuntimeAdapter.d.ts.map +1 -1
  22. package/dist/react/RuntimeAdapter.js +23 -18
  23. package/dist/react/RuntimeAdapter.js.map +1 -1
  24. package/dist/react/adapters/LocalStorageThreadListAdapter.d.ts.map +1 -1
  25. package/dist/react/adapters/LocalStorageThreadListAdapter.js +28 -12
  26. package/dist/react/adapters/LocalStorageThreadListAdapter.js.map +1 -1
  27. package/dist/react/client/AdaptedRemoteThread.d.ts +11 -0
  28. package/dist/react/client/AdaptedRemoteThread.d.ts.map +1 -0
  29. package/dist/react/client/AdaptedRemoteThread.js +27 -0
  30. package/dist/react/client/AdaptedRemoteThread.js.map +1 -0
  31. package/dist/react/client/InMemoryThreadList.d.ts +19 -0
  32. package/dist/react/client/InMemoryThreadList.d.ts.map +1 -0
  33. package/dist/react/client/InMemoryThreadList.js +418 -0
  34. package/dist/react/client/InMemoryThreadList.js.map +1 -0
  35. package/dist/react/client/Interactables.d.ts.map +1 -1
  36. package/dist/react/client/Interactables.js +159 -102
  37. package/dist/react/client/Interactables.js.map +1 -1
  38. package/dist/react/client/RemoteThreadList.d.ts +34 -0
  39. package/dist/react/client/RemoteThreadList.d.ts.map +1 -0
  40. package/dist/react/client/RemoteThreadList.js +826 -0
  41. package/dist/react/client/RemoteThreadList.js.map +1 -0
  42. package/dist/react/client/Tools.d.ts.map +1 -1
  43. package/dist/react/client/Tools.js +38 -27
  44. package/dist/react/client/Tools.js.map +1 -1
  45. package/dist/react/client/interactable-model-context.d.ts.map +1 -1
  46. package/dist/react/client/interactable-model-context.js +5 -1
  47. package/dist/react/client/interactable-model-context.js.map +1 -1
  48. package/dist/react/index.d.ts +7 -2
  49. package/dist/react/index.js +5 -2
  50. package/dist/react/interactables-legacy/Interactables.d.ts.map +1 -1
  51. package/dist/react/interactables-legacy/Interactables.js +145 -110
  52. package/dist/react/interactables-legacy/Interactables.js.map +1 -1
  53. package/dist/react/primitive-hooks/useActionBarCopy.d.ts.map +1 -1
  54. package/dist/react/primitive-hooks/useActionBarCopy.js +13 -18
  55. package/dist/react/primitive-hooks/useActionBarCopy.js.map +1 -1
  56. package/dist/react/primitive-hooks/useActionBarEdit.d.ts.map +1 -1
  57. package/dist/react/primitive-hooks/useActionBarEdit.js +2 -4
  58. package/dist/react/primitive-hooks/useActionBarEdit.js.map +1 -1
  59. package/dist/react/primitive-hooks/useActionBarReload.d.ts.map +1 -1
  60. package/dist/react/primitive-hooks/useActionBarReload.js +2 -4
  61. package/dist/react/primitive-hooks/useActionBarReload.js.map +1 -1
  62. package/dist/react/primitive-hooks/useBranchPickerNext.d.ts.map +1 -1
  63. package/dist/react/primitive-hooks/useBranchPickerNext.js +2 -6
  64. package/dist/react/primitive-hooks/useBranchPickerNext.js.map +1 -1
  65. package/dist/react/primitive-hooks/useBranchPickerPrevious.d.ts.map +1 -1
  66. package/dist/react/primitive-hooks/useBranchPickerPrevious.js +2 -6
  67. package/dist/react/primitive-hooks/useBranchPickerPrevious.js.map +1 -1
  68. package/dist/react/primitive-hooks/useComposerCancel.d.ts.map +1 -1
  69. package/dist/react/primitive-hooks/useComposerCancel.js +2 -4
  70. package/dist/react/primitive-hooks/useComposerCancel.js.map +1 -1
  71. package/dist/react/primitive-hooks/useComposerSend.d.ts.map +1 -1
  72. package/dist/react/primitive-hooks/useComposerSend.js +2 -4
  73. package/dist/react/primitive-hooks/useComposerSend.js.map +1 -1
  74. package/dist/react/primitive-hooks/useSuggestionTrigger.d.ts.map +1 -1
  75. package/dist/react/primitive-hooks/useSuggestionTrigger.js +6 -2
  76. package/dist/react/primitive-hooks/useSuggestionTrigger.js.map +1 -1
  77. package/dist/react/primitives/message/MessageParts.d.ts +13 -0
  78. package/dist/react/primitives/message/MessageParts.d.ts.map +1 -1
  79. package/dist/react/primitives/message/MessageParts.js.map +1 -1
  80. package/dist/react/primitives/thread/ThreadMessages.d.ts.map +1 -1
  81. package/dist/react/primitives/thread/ThreadMessages.js +1 -1
  82. package/dist/react/primitives/thread/ThreadMessages.js.map +1 -1
  83. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.d.ts +22 -10
  84. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.d.ts.map +1 -1
  85. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js +109 -71
  86. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js.map +1 -1
  87. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts +8 -2
  88. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts.map +1 -1
  89. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js +40 -13
  90. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js.map +1 -1
  91. package/dist/react/runtimes/RemoteThreadResource.d.ts +30 -0
  92. package/dist/react/runtimes/RemoteThreadResource.d.ts.map +1 -0
  93. package/dist/react/runtimes/RemoteThreadResource.js +134 -0
  94. package/dist/react/runtimes/RemoteThreadResource.js.map +1 -0
  95. package/dist/react/runtimes/RuntimeAdapterProvider.d.ts +3 -10
  96. package/dist/react/runtimes/RuntimeAdapterProvider.d.ts.map +1 -1
  97. package/dist/react/runtimes/RuntimeAdapterProvider.js +3 -6
  98. package/dist/react/runtimes/RuntimeAdapterProvider.js.map +1 -1
  99. package/dist/react/runtimes/assistant-transport/commandQueue.d.ts +17 -0
  100. package/dist/react/runtimes/assistant-transport/commandQueue.d.ts.map +1 -0
  101. package/dist/react/runtimes/assistant-transport/commandQueue.js +55 -0
  102. package/dist/react/runtimes/assistant-transport/commandQueue.js.map +1 -0
  103. package/dist/react/runtimes/assistant-transport/replayBoundaryStream.d.ts +11 -0
  104. package/dist/react/runtimes/assistant-transport/replayBoundaryStream.d.ts.map +1 -0
  105. package/dist/react/runtimes/assistant-transport/replayBoundaryStream.js +165 -0
  106. package/dist/react/runtimes/assistant-transport/replayBoundaryStream.js.map +1 -0
  107. package/dist/react/runtimes/assistant-transport/runManager.d.ts +15 -0
  108. package/dist/react/runtimes/assistant-transport/runManager.d.ts.map +1 -0
  109. package/dist/react/runtimes/assistant-transport/runManager.js +86 -0
  110. package/dist/react/runtimes/assistant-transport/runManager.js.map +1 -0
  111. package/dist/react/runtimes/assistant-transport/types.d.ts +130 -0
  112. package/dist/react/runtimes/assistant-transport/types.d.ts.map +1 -0
  113. package/dist/react/runtimes/assistant-transport/types.js +0 -0
  114. package/dist/react/runtimes/assistant-transport/useAssistantTransportRuntime.d.ts +14 -0
  115. package/dist/react/runtimes/assistant-transport/useAssistantTransportRuntime.d.ts.map +1 -0
  116. package/dist/react/runtimes/assistant-transport/useAssistantTransportRuntime.js +296 -0
  117. package/dist/react/runtimes/assistant-transport/useAssistantTransportRuntime.js.map +1 -0
  118. package/dist/react/runtimes/assistant-transport/useConvertedState.d.ts +7 -0
  119. package/dist/react/runtimes/assistant-transport/useConvertedState.d.ts.map +1 -0
  120. package/dist/react/runtimes/assistant-transport/useConvertedState.js +25 -0
  121. package/dist/react/runtimes/assistant-transport/useConvertedState.js.map +1 -0
  122. package/dist/react/runtimes/assistant-transport/useLatestRef.d.ts +7 -0
  123. package/dist/react/runtimes/assistant-transport/useLatestRef.d.ts.map +1 -0
  124. package/dist/react/runtimes/assistant-transport/useLatestRef.js +27 -0
  125. package/dist/react/runtimes/assistant-transport/useLatestRef.js.map +1 -0
  126. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.d.ts.map +1 -1
  127. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js +39 -13
  128. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js.map +1 -1
  129. package/dist/react/runtimes/cloud/CloudFileAttachmentAdapter.d.ts +2 -1
  130. package/dist/react/runtimes/cloud/CloudFileAttachmentAdapter.d.ts.map +1 -1
  131. package/dist/react/runtimes/cloud/CloudFileAttachmentAdapter.js +3 -3
  132. package/dist/react/runtimes/cloud/CloudFileAttachmentAdapter.js.map +1 -1
  133. package/dist/react/runtimes/cloud/auiV0.d.ts +4 -0
  134. package/dist/react/runtimes/cloud/auiV0.d.ts.map +1 -1
  135. package/dist/react/runtimes/cloud/auiV0.js +7 -0
  136. package/dist/react/runtimes/cloud/auiV0.js.map +1 -1
  137. package/dist/react/runtimes/cloud/useCloudThreadListAdapter.d.ts.map +1 -1
  138. package/dist/react/runtimes/cloud/useCloudThreadListAdapter.js +22 -12
  139. package/dist/react/runtimes/cloud/useCloudThreadListAdapter.js.map +1 -1
  140. package/dist/react/runtimes/cloud/useCloudThreadListRuntime.d.ts +16 -0
  141. package/dist/react/runtimes/cloud/useCloudThreadListRuntime.d.ts.map +1 -0
  142. package/dist/react/runtimes/cloud/useCloudThreadListRuntime.js +36 -0
  143. package/dist/react/runtimes/cloud/useCloudThreadListRuntime.js.map +1 -0
  144. package/dist/react/runtimes/external-message-converter.js +14 -14
  145. package/dist/react/runtimes/external-message-converter.js.map +1 -1
  146. package/dist/react/runtimes/useExternalStoreRuntime.d.ts.map +1 -1
  147. package/dist/react/runtimes/useExternalStoreRuntime.js +44 -28
  148. package/dist/react/runtimes/useExternalStoreRuntime.js.map +1 -1
  149. package/dist/react/runtimes/useExternalStoreSharedOptions.js.map +1 -1
  150. package/dist/react/runtimes/useLocalRuntime.js +1 -1
  151. package/dist/react/runtimes/useRuntimeAdapters.d.ts +9 -0
  152. package/dist/react/runtimes/useRuntimeAdapters.d.ts.map +1 -0
  153. package/dist/react/runtimes/useRuntimeAdapters.js +20 -0
  154. package/dist/react/runtimes/useRuntimeAdapters.js.map +1 -0
  155. package/dist/runtime/api/composer-runtime.d.ts +5 -0
  156. package/dist/runtime/api/composer-runtime.d.ts.map +1 -1
  157. package/dist/runtime/api/composer-runtime.js.map +1 -1
  158. package/dist/runtime/api/message-runtime.js +1 -1
  159. package/dist/runtime/api/thread-list-runtime.d.ts +1 -0
  160. package/dist/runtime/api/thread-list-runtime.d.ts.map +1 -1
  161. package/dist/runtime/api/thread-list-runtime.js +4 -2
  162. package/dist/runtime/api/thread-list-runtime.js.map +1 -1
  163. package/dist/runtime/api/thread-runtime.d.ts +12 -2
  164. package/dist/runtime/api/thread-runtime.d.ts.map +1 -1
  165. package/dist/runtime/api/thread-runtime.js +13 -3
  166. package/dist/runtime/api/thread-runtime.js.map +1 -1
  167. package/dist/runtime/base/base-composer-runtime-core.d.ts +25 -0
  168. package/dist/runtime/base/base-composer-runtime-core.d.ts.map +1 -1
  169. package/dist/runtime/base/base-composer-runtime-core.js +72 -18
  170. package/dist/runtime/base/base-composer-runtime-core.js.map +1 -1
  171. package/dist/runtime/base/base-thread-runtime-core.d.ts +15 -0
  172. package/dist/runtime/base/base-thread-runtime-core.d.ts.map +1 -1
  173. package/dist/runtime/base/base-thread-runtime-core.js +34 -3
  174. package/dist/runtime/base/base-thread-runtime-core.js.map +1 -1
  175. package/dist/runtime/base/default-edit-composer-runtime-core.d.ts.map +1 -1
  176. package/dist/runtime/base/default-edit-composer-runtime-core.js +1 -6
  177. package/dist/runtime/base/default-edit-composer-runtime-core.js.map +1 -1
  178. package/dist/runtime/base/default-thread-composer-runtime-core.d.ts +0 -1
  179. package/dist/runtime/base/default-thread-composer-runtime-core.d.ts.map +1 -1
  180. package/dist/runtime/base/default-thread-composer-runtime-core.js +11 -8
  181. package/dist/runtime/base/default-thread-composer-runtime-core.js.map +1 -1
  182. package/dist/runtime/interfaces/thread-runtime-core.d.ts +6 -0
  183. package/dist/runtime/interfaces/thread-runtime-core.d.ts.map +1 -1
  184. package/dist/runtime/internal.js +5 -5
  185. package/dist/runtime/queue/external-thread-queue-adapter.d.ts +15 -0
  186. package/dist/runtime/queue/external-thread-queue-adapter.d.ts.map +1 -1
  187. package/dist/runtime/queue/message-queue.d.ts.map +1 -1
  188. package/dist/runtime/queue/message-queue.js +25 -11
  189. package/dist/runtime/queue/message-queue.js.map +1 -1
  190. package/dist/runtime/utils/thread-runtime-lifecycle.d.ts +8 -0
  191. package/dist/runtime/utils/thread-runtime-lifecycle.d.ts.map +1 -0
  192. package/dist/runtime/utils/thread-runtime-lifecycle.js +11 -0
  193. package/dist/runtime/utils/thread-runtime-lifecycle.js.map +1 -0
  194. package/dist/runtimes/external-store/external-store-adapter.d.ts +7 -0
  195. package/dist/runtimes/external-store/external-store-adapter.d.ts.map +1 -1
  196. package/dist/runtimes/external-store/external-store-shared-options.js.map +1 -1
  197. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.d.ts.map +1 -1
  198. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.js +4 -1
  199. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.js.map +1 -1
  200. package/dist/runtimes/external-store/external-store-thread-runtime-core.d.ts +11 -1
  201. package/dist/runtimes/external-store/external-store-thread-runtime-core.d.ts.map +1 -1
  202. package/dist/runtimes/external-store/external-store-thread-runtime-core.js +78 -20
  203. package/dist/runtimes/external-store/external-store-thread-runtime-core.js.map +1 -1
  204. package/dist/runtimes/internal.d.ts +1 -1
  205. package/dist/runtimes/local/local-thread-runtime-core.d.ts +3 -0
  206. package/dist/runtimes/local/local-thread-runtime-core.d.ts.map +1 -1
  207. package/dist/runtimes/local/local-thread-runtime-core.js +55 -14
  208. package/dist/runtimes/local/local-thread-runtime-core.js.map +1 -1
  209. package/dist/runtimes/readonly/ReadonlyThreadRuntimeCore.d.ts +1 -0
  210. package/dist/runtimes/readonly/ReadonlyThreadRuntimeCore.d.ts.map +1 -1
  211. package/dist/runtimes/readonly/ReadonlyThreadRuntimeCore.js +3 -0
  212. package/dist/runtimes/readonly/ReadonlyThreadRuntimeCore.js.map +1 -1
  213. package/dist/runtimes/remote-thread-list/empty-thread-core.js +3 -0
  214. package/dist/runtimes/remote-thread-list/empty-thread-core.js.map +1 -1
  215. package/dist/runtimes/remote-thread-list/types.d.ts +29 -1
  216. package/dist/runtimes/remote-thread-list/types.d.ts.map +1 -1
  217. package/dist/runtimes/tool-invocations/ToolInvocationTracker.d.ts +1 -12
  218. package/dist/runtimes/tool-invocations/ToolInvocationTracker.d.ts.map +1 -1
  219. package/dist/runtimes/tool-invocations/ToolInvocationTracker.js +67 -38
  220. package/dist/runtimes/tool-invocations/ToolInvocationTracker.js.map +1 -1
  221. package/dist/store/clients/external-thread.d.ts +7 -0
  222. package/dist/store/clients/external-thread.d.ts.map +1 -1
  223. package/dist/store/clients/external-thread.js +460 -369
  224. package/dist/store/clients/external-thread.js.map +1 -1
  225. package/dist/store/clients/runtime-adapter.d.ts.map +1 -1
  226. package/dist/store/clients/runtime-adapter.js +5 -2
  227. package/dist/store/clients/runtime-adapter.js.map +1 -1
  228. package/dist/store/clients/single-thread-list.d.ts.map +1 -1
  229. package/dist/store/clients/single-thread-list.js +31 -27
  230. package/dist/store/clients/single-thread-list.js.map +1 -1
  231. package/dist/store/clients/suggestions.d.ts +3 -1
  232. package/dist/store/clients/suggestions.d.ts.map +1 -1
  233. package/dist/store/clients/suggestions.js +65 -37
  234. package/dist/store/clients/suggestions.js.map +1 -1
  235. package/dist/store/clients/thread-selection-events.d.ts +10 -0
  236. package/dist/store/clients/thread-selection-events.d.ts.map +1 -0
  237. package/dist/store/clients/thread-selection-events.js +40 -0
  238. package/dist/store/clients/thread-selection-events.js.map +1 -0
  239. package/dist/store/env.d.ts +5 -0
  240. package/dist/store/env.d.ts.map +1 -0
  241. package/dist/store/env.js +12 -0
  242. package/dist/store/env.js.map +1 -0
  243. package/dist/store/index.d.ts +6 -4
  244. package/dist/store/index.js +5 -3
  245. package/dist/store/internal.d.ts +4 -1
  246. package/dist/store/internal.js +4 -1
  247. package/dist/store/primitive-predicates.d.ts +19 -0
  248. package/dist/store/primitive-predicates.d.ts.map +1 -0
  249. package/dist/store/primitive-predicates.js +19 -0
  250. package/dist/store/primitive-predicates.js.map +1 -0
  251. package/dist/store/runtime-clients/attachment-runtime-client.d.ts.map +1 -1
  252. package/dist/store/runtime-clients/attachment-runtime-client.js +18 -11
  253. package/dist/store/runtime-clients/attachment-runtime-client.js.map +1 -1
  254. package/dist/store/runtime-clients/handle-runtime-action.d.ts +5 -0
  255. package/dist/store/runtime-clients/handle-runtime-action.d.ts.map +1 -0
  256. package/dist/store/runtime-clients/handle-runtime-action.js +12 -0
  257. package/dist/store/runtime-clients/handle-runtime-action.js.map +1 -0
  258. package/dist/store/runtime-clients/handle-thread-list-action.d.ts +5 -0
  259. package/dist/store/runtime-clients/handle-thread-list-action.d.ts.map +1 -0
  260. package/dist/store/runtime-clients/handle-thread-list-action.js +7 -0
  261. package/dist/store/runtime-clients/handle-thread-list-action.js.map +1 -0
  262. package/dist/store/runtime-clients/thread-list-item-runtime-client.d.ts.map +1 -1
  263. package/dist/store/runtime-clients/thread-list-item-runtime-client.js +104 -57
  264. package/dist/store/runtime-clients/thread-list-item-runtime-client.js.map +1 -1
  265. package/dist/store/runtime-clients/thread-list-runtime-client.d.ts.map +1 -1
  266. package/dist/store/runtime-clients/thread-list-runtime-client.js +5 -6
  267. package/dist/store/runtime-clients/thread-list-runtime-client.js.map +1 -1
  268. package/dist/store/runtime-clients/thread-runtime-client.d.ts.map +1 -1
  269. package/dist/store/runtime-clients/thread-runtime-client.js +98 -82
  270. package/dist/store/runtime-clients/thread-runtime-client.js.map +1 -1
  271. package/dist/store/scope-registration.d.ts +2 -2
  272. package/dist/store/scopes/composer.d.ts +9 -4
  273. package/dist/store/scopes/composer.d.ts.map +1 -1
  274. package/dist/store/scopes/suggestions.d.ts +6 -1
  275. package/dist/store/scopes/suggestions.d.ts.map +1 -1
  276. package/dist/store/scopes/thread-list-item.d.ts +7 -6
  277. package/dist/store/scopes/thread-list-item.d.ts.map +1 -1
  278. package/dist/store/scopes/thread.d.ts +25 -7
  279. package/dist/store/scopes/thread.d.ts.map +1 -1
  280. package/dist/store/scopes/threads.d.ts +17 -1
  281. package/dist/store/scopes/threads.d.ts.map +1 -1
  282. package/dist/subscribable/subscribable.d.ts +2 -1
  283. package/dist/subscribable/subscribable.d.ts.map +1 -1
  284. package/dist/subscribable/subscribable.js +23 -18
  285. package/dist/subscribable/subscribable.js.map +1 -1
  286. package/dist/types/error.d.ts +15 -1
  287. package/dist/types/error.d.ts.map +1 -1
  288. package/dist/types/error.js +18 -1
  289. package/dist/types/error.js.map +1 -1
  290. package/dist/types/message.d.ts +2 -0
  291. package/dist/types/message.d.ts.map +1 -1
  292. package/dist/types/message.js.map +1 -1
  293. package/dist/utils/composite-context-provider.d.ts +1 -0
  294. package/dist/utils/composite-context-provider.d.ts.map +1 -1
  295. package/dist/utils/composite-context-provider.js +25 -9
  296. package/dist/utils/composite-context-provider.js.map +1 -1
  297. package/dist/utils/notify-event-listeners.d.ts +2 -2
  298. package/dist/utils/notify-event-listeners.d.ts.map +1 -1
  299. package/dist/utils/notify-event-listeners.js +2 -2
  300. package/dist/utils/notify-event-listeners.js.map +1 -1
  301. package/package.json +7 -7
  302. package/src/adapters/speech.test.ts +118 -1
  303. package/src/adapters/speech.ts +21 -22
  304. package/src/adapters/thread-history.ts +2 -0
  305. package/src/adapters/voice.test.ts +69 -0
  306. package/src/adapters/voice.ts +27 -27
  307. package/src/index.ts +2 -0
  308. package/src/model-context/frame/host.test.ts +23 -0
  309. package/src/model-context/frame/host.ts +2 -1
  310. package/src/model-context/registry.test.ts +131 -0
  311. package/src/model-context/registry.ts +16 -6
  312. package/src/react/RuntimeAdapter.test.ts +2 -0
  313. package/src/react/RuntimeAdapter.ts +20 -8
  314. package/src/react/adapters/LocalStorageThreadListAdapter.tsx +34 -17
  315. package/src/react/client/AdaptedRemoteThread.ts +41 -0
  316. package/src/react/client/InMemoryThreadList.test.tsx +148 -0
  317. package/src/react/client/InMemoryThreadList.ts +290 -0
  318. package/src/react/client/Interactables.test.ts +326 -2
  319. package/src/react/client/Interactables.ts +227 -110
  320. package/src/react/client/RemoteThreadList.test.ts +1026 -0
  321. package/src/react/client/RemoteThreadList.ts +1067 -0
  322. package/src/react/client/Tools.ts +36 -29
  323. package/src/react/client/interactable-model-context.ts +6 -1
  324. package/src/react/index.ts +13 -0
  325. package/src/react/interactables-legacy/Interactables.test.ts +317 -0
  326. package/src/react/interactables-legacy/Interactables.ts +204 -128
  327. package/src/react/primitive-hooks/useActionBarCopy.ts +2 -7
  328. package/src/react/primitive-hooks/useActionBarEdit.ts +2 -1
  329. package/src/react/primitive-hooks/useActionBarReload.ts +2 -6
  330. package/src/react/primitive-hooks/useBranchPickerNext.ts +2 -7
  331. package/src/react/primitive-hooks/useBranchPickerPrevious.ts +2 -7
  332. package/src/react/primitive-hooks/useComposerCancel.ts +2 -1
  333. package/src/react/primitive-hooks/useComposerSend.ts +2 -5
  334. package/src/react/primitive-hooks/useSuggestionTrigger.test.ts +11 -0
  335. package/src/react/primitive-hooks/useSuggestionTrigger.ts +4 -5
  336. package/src/react/primitives/message/MessageParts.tsx +0 -1
  337. package/src/react/primitives/thread/ThreadMessages.tsx +2 -1
  338. package/src/react/runtimes/RemoteThreadListHookInstanceManager.retry.test.tsx +116 -0
  339. package/src/react/runtimes/RemoteThreadListHookInstanceManager.running.test.tsx +1 -1
  340. package/src/react/runtimes/RemoteThreadListHookInstanceManager.test.tsx +57 -6
  341. package/src/react/runtimes/RemoteThreadListHookInstanceManager.title.test.tsx +272 -0
  342. package/src/react/runtimes/RemoteThreadListHookInstanceManager.tsx +159 -124
  343. package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.tsx +59 -20
  344. package/src/react/runtimes/RemoteThreadResource.test.ts +141 -0
  345. package/src/react/runtimes/RemoteThreadResource.ts +231 -0
  346. package/src/react/runtimes/RuntimeAdapterProvider.tsx +8 -20
  347. package/src/react/runtimes/assistant-transport/commandQueue.ts +68 -0
  348. package/src/react/runtimes/assistant-transport/replayBoundaryStream.test.ts +455 -0
  349. package/src/react/runtimes/assistant-transport/replayBoundaryStream.ts +172 -0
  350. package/src/react/runtimes/assistant-transport/runManager.ts +124 -0
  351. package/src/react/runtimes/assistant-transport/transport-delivery.test.tsx +79 -0
  352. package/src/react/runtimes/assistant-transport/transport-parentid.test.tsx +97 -0
  353. package/src/react/runtimes/assistant-transport/transport-scheduling.test.ts +414 -0
  354. package/src/react/runtimes/assistant-transport/types.ts +170 -0
  355. package/src/react/runtimes/assistant-transport/useAssistantTransport.spec.md +141 -0
  356. package/src/react/runtimes/assistant-transport/useAssistantTransportRuntime.test.tsx +625 -0
  357. package/src/react/runtimes/assistant-transport/useAssistantTransportRuntime.ts +481 -0
  358. package/src/react/runtimes/assistant-transport/useConvertedState.ts +20 -0
  359. package/src/react/runtimes/assistant-transport/useLatestRef.ts +9 -0
  360. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.test.tsx +131 -6
  361. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.ts +61 -15
  362. package/src/react/runtimes/cloud/CloudFileAttachmentAdapter.ts +6 -4
  363. package/src/react/runtimes/cloud/auiV0.ts +17 -1
  364. package/src/react/runtimes/cloud/useCloudThreadListAdapter.tsx +36 -21
  365. package/src/react/runtimes/cloud/useCloudThreadListRuntime.test.tsx +56 -0
  366. package/src/react/runtimes/cloud/useCloudThreadListRuntime.ts +31 -0
  367. package/src/react/runtimes/external-message-converter.test.ts +143 -0
  368. package/src/react/runtimes/external-message-converter.ts +41 -41
  369. package/src/react/runtimes/remote-thread-hook-on-tap.test.ts +58 -0
  370. package/src/react/runtimes/useExternalStoreRuntime.lifecycle.test.tsx +83 -0
  371. package/src/react/runtimes/useExternalStoreRuntime.suggestions.test.tsx +124 -0
  372. package/src/react/runtimes/useExternalStoreRuntime.ts +7 -0
  373. package/src/react/runtimes/useExternalStoreSharedOptions.ts +3 -1
  374. package/src/react/runtimes/useLocalRuntime.test.tsx +130 -4
  375. package/src/react/runtimes/useRuntimeAdapters.test.tsx +62 -0
  376. package/src/react/runtimes/useRuntimeAdapters.ts +28 -0
  377. package/src/runtime/api/composer-runtime.ts +5 -0
  378. package/src/runtime/api/thread-list-runtime.ts +3 -1
  379. package/src/runtime/api/thread-runtime.ts +28 -3
  380. package/src/runtime/base/base-composer-runtime-core.ts +106 -13
  381. package/src/runtime/base/base-thread-runtime-core.test.ts +191 -0
  382. package/src/runtime/base/base-thread-runtime-core.ts +47 -2
  383. package/src/runtime/base/default-edit-composer-runtime-core.ts +1 -15
  384. package/src/runtime/base/default-thread-composer-runtime-core.ts +13 -15
  385. package/src/runtime/interfaces/thread-runtime-core.ts +6 -0
  386. package/src/runtime/queue/external-thread-queue-adapter.ts +17 -0
  387. package/src/runtime/queue/message-queue.ts +28 -10
  388. package/src/runtime/utils/thread-runtime-lifecycle.ts +18 -0
  389. package/src/runtimes/external-store/external-store-adapter.ts +7 -0
  390. package/src/runtimes/external-store/external-store-shared-options.ts +3 -1
  391. package/src/runtimes/external-store/external-store-thread-list-runtime-core.ts +4 -1
  392. package/src/runtimes/external-store/external-store-thread-runtime-core.ts +187 -48
  393. package/src/runtimes/local/local-thread-runtime-core.test.ts +373 -4
  394. package/src/runtimes/local/local-thread-runtime-core.ts +83 -15
  395. package/src/runtimes/readonly/ReadonlyThreadRuntimeCore.ts +4 -0
  396. package/src/runtimes/remote-thread-list/empty-thread-core.ts +4 -0
  397. package/src/runtimes/remote-thread-list/types.ts +30 -0
  398. package/src/runtimes/tool-invocations/EDGE_CASES.md +17 -6
  399. package/src/runtimes/tool-invocations/ToolInvocationTracker.test.ts +243 -0
  400. package/src/runtimes/tool-invocations/ToolInvocationTracker.ts +127 -61
  401. package/src/store/clients/external-thread.ts +119 -16
  402. package/src/store/clients/model-context-client.test.ts +16 -8
  403. package/src/store/clients/runtime-adapter.ts +5 -2
  404. package/src/store/clients/single-thread-list.ts +2 -1
  405. package/src/store/clients/suggestions.ts +39 -13
  406. package/src/store/clients/thread-selection-events.ts +21 -0
  407. package/src/store/env.ts +11 -0
  408. package/src/store/index.ts +14 -1
  409. package/src/store/internal.ts +13 -0
  410. package/src/store/primitive-predicates.test.ts +184 -0
  411. package/src/store/primitive-predicates.ts +43 -0
  412. package/src/store/runtime-clients/attachment-runtime-client.test.ts +41 -0
  413. package/src/store/runtime-clients/attachment-runtime-client.ts +2 -1
  414. package/src/store/runtime-clients/handle-runtime-action.ts +11 -0
  415. package/src/store/runtime-clients/handle-thread-list-action.test.ts +69 -0
  416. package/src/store/runtime-clients/handle-thread-list-action.ts +6 -0
  417. package/src/store/runtime-clients/thread-list-item-runtime-client.ts +28 -35
  418. package/src/store/runtime-clients/thread-list-runtime-client.ts +9 -6
  419. package/src/store/runtime-clients/thread-runtime-client.ts +5 -0
  420. package/src/store/scopes/composer.ts +9 -4
  421. package/src/store/scopes/suggestions.ts +6 -0
  422. package/src/store/scopes/thread-list-item.ts +7 -6
  423. package/src/store/scopes/thread.ts +25 -7
  424. package/src/store/scopes/threads.ts +14 -0
  425. package/src/subscribable/subscribable.test.ts +77 -1
  426. package/src/subscribable/subscribable.ts +38 -23
  427. package/src/tests/RemoteThreadListThreadListRuntimeCore-switchToThread-unarchive.test.ts +25 -0
  428. package/src/tests/append-interactable-snapshots.test.ts +350 -0
  429. package/src/tests/auiV0Encode.test.ts +66 -0
  430. package/src/tests/base-composer-runtime-core-send.test.ts +156 -0
  431. package/src/tests/base-composer-runtime-core.test.ts +136 -1
  432. package/src/tests/composer-can-cancel.test.ts +101 -0
  433. package/src/tests/default-edit-composer-runtime-core.test.ts +0 -68
  434. package/src/tests/external-store-thread-list-runtime-core.test.ts +101 -0
  435. package/src/tests/external-store-thread-runtime-core-adapter.test.ts +491 -1
  436. package/src/tests/external-store-thread-runtime-core.test.ts +124 -0
  437. package/src/tests/external-thread-attachments.test.tsx +214 -0
  438. package/src/tests/external-thread-parity.test.tsx +54 -0
  439. package/src/tests/external-thread-refetch.test.tsx +117 -0
  440. package/src/tests/external-thread-suggestions.test.tsx +59 -0
  441. package/src/tests/interactables-toolui-migration.test.tsx +66 -0
  442. package/src/tests/message-queue.test.ts +54 -0
  443. package/src/tests/session-reset-notification.test.ts +145 -0
  444. package/src/tests/thread-message-like.test.ts +39 -0
  445. package/src/tests/thread-switch-events.test.tsx +234 -0
  446. package/src/tests/tools-scope-migration.test.tsx +58 -0
  447. package/src/types/error.ts +24 -0
  448. package/src/types/message.ts +2 -0
  449. package/src/utils/composite-context-provider.test.ts +139 -0
  450. package/src/utils/composite-context-provider.ts +26 -8
  451. package/src/utils/notify-event-listeners.ts +10 -5
  452. package/src/tests/default-thread-composer-runtime-core.test.ts +0 -82
@@ -1,4 +1,4 @@
1
- import { describe, it, expect, beforeEach, vi } from "vitest";
1
+ import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
2
2
  import {
3
3
  ExternalStoreThreadRuntimeCore,
4
4
  hasUpcomingMessage,
@@ -6,6 +6,8 @@ import {
6
6
  import type { ExternalStoreAdapter } from "../runtimes/external-store/external-store-adapter";
7
7
  import type { ModelContextProvider } from "../model-context/types";
8
8
  import type { AppendMessage, ThreadMessage } from "../types/message";
9
+ import { createMessageQueue } from "../runtime/queue/message-queue";
10
+ import { getThreadMessageText } from "../utils/text";
9
11
 
10
12
  const createContextProvider = (): ModelContextProvider => ({
11
13
  getModelContext: () => ({}),
@@ -47,6 +49,23 @@ const createBaseAdapter = (
47
49
  ...overrides,
48
50
  });
49
51
 
52
+ const captureUnhandledRejections = async (
53
+ run: () => Promise<void>,
54
+ ): Promise<unknown[]> => {
55
+ const rejections: unknown[] = [];
56
+ const onUnhandledRejection = (reason: unknown) => {
57
+ rejections.push(reason);
58
+ };
59
+ process.on("unhandledRejection", onUnhandledRejection);
60
+ try {
61
+ await run();
62
+ await new Promise((resolve) => setImmediate(resolve));
63
+ } finally {
64
+ process.removeListener("unhandledRejection", onUnhandledRejection);
65
+ }
66
+ return rejections;
67
+ };
68
+
50
69
  describe("ExternalStoreThreadRuntimeCore adapter contract", () => {
51
70
  let contextProvider: ModelContextProvider;
52
71
 
@@ -54,6 +73,10 @@ describe("ExternalStoreThreadRuntimeCore adapter contract", () => {
54
73
  contextProvider = createContextProvider();
55
74
  });
56
75
 
76
+ afterEach(() => {
77
+ vi.restoreAllMocks();
78
+ });
79
+
57
80
  it("hasUpcomingMessage is true only while running without an assistant tail", () => {
58
81
  expect(hasUpcomingMessage(true, [createUserMessage("u1")])).toBe(true);
59
82
  expect(hasUpcomingMessage(true, [createAssistantMessage("a1")])).toBe(
@@ -235,6 +258,175 @@ describe("ExternalStoreThreadRuntimeCore adapter contract", () => {
235
258
  expect(lastCall.map((m) => m.id)).toContain("server-msg");
236
259
  });
237
260
 
261
+ it("does not revert a store update that lands before the resync flushes", async () => {
262
+ const optimisticAssistant = {
263
+ ...createAssistantMessage("server-msg", "partial answer"),
264
+ status: { type: "running" as const },
265
+ metadata: {
266
+ unstable_state: null,
267
+ unstable_annotations: [],
268
+ unstable_data: [],
269
+ steps: [],
270
+ custom: {},
271
+ isOptimistic: true,
272
+ },
273
+ } as ThreadMessage;
274
+ const setMessages = vi.fn();
275
+ const adapter = createBaseAdapter({
276
+ messages: [createUserMessage("u1"), optimisticAssistant],
277
+ isRunning: true,
278
+ onCancel: vi.fn(),
279
+ setMessages,
280
+ });
281
+ const core = new ExternalStoreThreadRuntimeCore(contextProvider, adapter);
282
+
283
+ core.cancelRun();
284
+
285
+ // The store settles the cancelled turn and re-supplies it in the same
286
+ // tick as the cancel.
287
+ core.__internal_setAdapter(
288
+ createBaseAdapter({
289
+ messages: [
290
+ createUserMessage("u1"),
291
+ createAssistantMessage("server-msg", "partial answer (stopped)"),
292
+ ],
293
+ isRunning: false,
294
+ onCancel: vi.fn(),
295
+ setMessages,
296
+ }),
297
+ );
298
+
299
+ await new Promise((resolve) => setTimeout(resolve, 0));
300
+ expect(setMessages).toHaveBeenCalled();
301
+ const lastCall = setMessages.mock.lastCall?.[0] as ThreadMessage[];
302
+ const texts = lastCall.map(getThreadMessageText);
303
+ expect(texts).toContain("partial answer (stopped)");
304
+ expect(texts).not.toContain("partial answer");
305
+ });
306
+
307
+ it("re-applies the user leaf rollback when the store updates before the flush", async () => {
308
+ const messages = [createUserMessage("u1", "cancel me")];
309
+ const setMessages = vi.fn();
310
+ const adapter = createBaseAdapter({
311
+ messages,
312
+ isRunning: true,
313
+ onCancel: vi.fn(),
314
+ setMessages,
315
+ });
316
+ const core = new ExternalStoreThreadRuntimeCore(contextProvider, adapter);
317
+
318
+ core.cancelRun();
319
+
320
+ // The host flips isRunning after onCancel; the store still holds the
321
+ // rolled-back message because only the deferred resync removes it.
322
+ core.__internal_setAdapter(
323
+ createBaseAdapter({
324
+ messages,
325
+ isRunning: false,
326
+ onCancel: vi.fn(),
327
+ setMessages,
328
+ }),
329
+ );
330
+
331
+ await new Promise((resolve) => setTimeout(resolve, 0));
332
+ expect(core.composer.text).toBe("cancel me");
333
+ expect(setMessages).toHaveBeenCalled();
334
+ const lastCall = setMessages.mock.lastCall?.[0] as ThreadMessage[];
335
+ expect(lastCall.map((m) => m.id)).not.toContain("u1");
336
+ expect(core.export().messages.map((m) => m.message.id)).not.toContain(
337
+ "u1",
338
+ );
339
+ });
340
+
341
+ it("keeps a rolled-back user leaf the store answered in the gap and takes the draft back", async () => {
342
+ const setMessages = vi.fn();
343
+ const adapter = createBaseAdapter({
344
+ messages: [createUserMessage("u1", "cancel me")],
345
+ isRunning: true,
346
+ onCancel: vi.fn(),
347
+ setMessages,
348
+ });
349
+ const core = new ExternalStoreThreadRuntimeCore(contextProvider, adapter);
350
+
351
+ core.cancelRun();
352
+ expect(core.composer.text).toBe("cancel me");
353
+
354
+ core.__internal_setAdapter(
355
+ createBaseAdapter({
356
+ messages: [
357
+ createUserMessage("u1", "cancel me"),
358
+ createAssistantMessage("a1", "answered anyway"),
359
+ ],
360
+ isRunning: false,
361
+ onCancel: vi.fn(),
362
+ setMessages,
363
+ }),
364
+ );
365
+
366
+ await new Promise((resolve) => setTimeout(resolve, 0));
367
+ expect(core.composer.text).toBe("");
368
+ const lastCall = setMessages.mock.lastCall?.[0] as ThreadMessage[];
369
+ expect(lastCall.map((m) => m.id)).toEqual(["u1", "a1"]);
370
+ });
371
+
372
+ it("leaves an edited draft alone when the store answered in the gap", async () => {
373
+ const setMessages = vi.fn();
374
+ const adapter = createBaseAdapter({
375
+ messages: [createUserMessage("u1", "cancel me")],
376
+ isRunning: true,
377
+ onCancel: vi.fn(),
378
+ setMessages,
379
+ });
380
+ const core = new ExternalStoreThreadRuntimeCore(contextProvider, adapter);
381
+
382
+ core.cancelRun();
383
+ core.composer.setText("edited");
384
+
385
+ core.__internal_setAdapter(
386
+ createBaseAdapter({
387
+ messages: [
388
+ createUserMessage("u1", "cancel me"),
389
+ createAssistantMessage("a1", "answered anyway"),
390
+ ],
391
+ isRunning: false,
392
+ onCancel: vi.fn(),
393
+ setMessages,
394
+ }),
395
+ );
396
+
397
+ await new Promise((resolve) => setTimeout(resolve, 0));
398
+ expect(core.composer.text).toBe("edited");
399
+ const lastCall = setMessages.mock.lastCall?.[0] as ThreadMessage[];
400
+ expect(lastCall.map((m) => m.id)).toEqual(["u1", "a1"]);
401
+ });
402
+
403
+ it("drops a placeholder regenerated between cancel and flush", async () => {
404
+ const setMessages = vi.fn();
405
+ const adapter = createBaseAdapter({
406
+ messages: [createUserMessage("u1", "cancel me")],
407
+ isRunning: true,
408
+ onCancel: vi.fn(),
409
+ setMessages,
410
+ });
411
+ const core = new ExternalStoreThreadRuntimeCore(contextProvider, adapter);
412
+
413
+ core.cancelRun();
414
+
415
+ core.__internal_setAdapter(
416
+ createBaseAdapter({
417
+ messages: [createUserMessage("u1", "cancel me")],
418
+ isRunning: true,
419
+ onCancel: vi.fn(),
420
+ setMessages,
421
+ }),
422
+ );
423
+
424
+ await new Promise((resolve) => setTimeout(resolve, 0));
425
+ expect(core.composer.text).toBe("cancel me");
426
+ const lastCall = setMessages.mock.lastCall?.[0] as ThreadMessage[];
427
+ expect(lastCall).toEqual([]);
428
+ });
429
+
238
430
  it("evicts an empty optimistic head on cancel", async () => {
239
431
  const optimisticAssistant = {
240
432
  ...createAssistantMessage("server-msg", ""),
@@ -264,6 +456,135 @@ describe("ExternalStoreThreadRuntimeCore adapter contract", () => {
264
456
  const lastCall = setMessages.mock.lastCall?.[0] as ThreadMessage[];
265
457
  expect(lastCall.map((m) => m.id)).not.toContain("server-msg");
266
458
  });
459
+
460
+ it("pauses a queue instead of dispatching the next message", async () => {
461
+ const dispatched: string[] = [];
462
+ const queue = createMessageQueue({
463
+ run: (message) => {
464
+ dispatched.push(getThreadMessageText(message));
465
+ },
466
+ });
467
+ const adapter = createBaseAdapter({
468
+ isRunning: true,
469
+ onCancel: vi.fn(),
470
+ queue: queue.adapter,
471
+ });
472
+ const core = new ExternalStoreThreadRuntimeCore(contextProvider, adapter);
473
+ const appendAtTail = (text: string) =>
474
+ core.append({
475
+ role: "user",
476
+ content: [{ type: "text", text }],
477
+ attachments: [],
478
+ createdAt: new Date(),
479
+ parentId: core.messages.at(-1)?.id ?? null,
480
+ sourceId: null,
481
+ runConfig: undefined,
482
+ metadata: { custom: {} },
483
+ } as AppendMessage);
484
+
485
+ await appendAtTail("first");
486
+ await appendAtTail("queued");
487
+
488
+ core.cancelRun();
489
+ // the aborted run settles; a queue host reports that the same way it
490
+ // reports any other run ending
491
+ queue.notifyIdle();
492
+
493
+ expect(dispatched).toEqual(["first"]);
494
+ expect(queue.adapter.steerItems.map((item) => item.prompt)).toEqual([
495
+ "queued",
496
+ ]);
497
+ });
498
+
499
+ it("keeps a trailing user message out of the composer without setMessages", async () => {
500
+ const userMessage = createUserMessage("u1", "Do not duplicate me");
501
+ const adapter = createBaseAdapter({
502
+ messages: [userMessage],
503
+ isRunning: true,
504
+ onCancel: vi.fn(),
505
+ });
506
+ const core = new ExternalStoreThreadRuntimeCore(contextProvider, adapter);
507
+
508
+ core.cancelRun();
509
+
510
+ await new Promise((resolve) => setTimeout(resolve, 0));
511
+ expect(core.composer.text).toBe("");
512
+ expect(core.export().messages.map((m) => m.message.id)).toContain("u1");
513
+ });
514
+
515
+ it("keeps the message in the thread when the composer is busy", async () => {
516
+ const adapter = createBaseAdapter({
517
+ messages: [createUserMessage("u1", "cancel me")],
518
+ isRunning: true,
519
+ onCancel: vi.fn(),
520
+ setMessages: vi.fn(),
521
+ });
522
+ const core = new ExternalStoreThreadRuntimeCore(contextProvider, adapter);
523
+ core.composer.setText("something else");
524
+
525
+ core.cancelRun();
526
+
527
+ await new Promise((resolve) => setTimeout(resolve, 0));
528
+ expect(core.composer.text).toBe("something else");
529
+ expect(core.export().messages.map((m) => m.message.id)).toContain("u1");
530
+ });
531
+
532
+ it("keeps a message with non-text content in the thread", async () => {
533
+ const userMessage = {
534
+ ...createUserMessage("u1", "look at this"),
535
+ content: [
536
+ { type: "text" as const, text: "look at this" },
537
+ { type: "image" as const, image: "https://example.com/cat.png" },
538
+ ],
539
+ } as ThreadMessage;
540
+ const adapter = createBaseAdapter({
541
+ messages: [userMessage],
542
+ isRunning: true,
543
+ onCancel: vi.fn(),
544
+ setMessages: vi.fn(),
545
+ });
546
+ const core = new ExternalStoreThreadRuntimeCore(contextProvider, adapter);
547
+
548
+ core.cancelRun();
549
+
550
+ await new Promise((resolve) => setTimeout(resolve, 0));
551
+ expect(core.composer.text).toBe("");
552
+ expect(core.export().messages.map((m) => m.message.id)).toContain("u1");
553
+ });
554
+
555
+ it("restores the message whole when the composer is free", async () => {
556
+ const attachment = {
557
+ id: "a1",
558
+ type: "file" as const,
559
+ name: "spec.pdf",
560
+ contentType: "application/pdf",
561
+ status: { type: "complete" as const },
562
+ content: [],
563
+ };
564
+ const quote = { text: "quoted", messageId: "m-1" };
565
+ const userMessage = {
566
+ ...createUserMessage("u1", "cancel me"),
567
+ attachments: [attachment],
568
+ metadata: { custom: { quote } },
569
+ } as ThreadMessage;
570
+ const adapter = createBaseAdapter({
571
+ messages: [userMessage],
572
+ isRunning: true,
573
+ onCancel: vi.fn(),
574
+ setMessages: vi.fn(),
575
+ });
576
+ const core = new ExternalStoreThreadRuntimeCore(contextProvider, adapter);
577
+
578
+ core.cancelRun();
579
+
580
+ await new Promise((resolve) => setTimeout(resolve, 0));
581
+ expect(core.composer.text).toBe("cancel me");
582
+ expect(core.composer.attachments).toEqual([attachment]);
583
+ expect(core.composer.quote).toEqual(quote);
584
+ expect(core.export().messages.map((m) => m.message.id)).not.toContain(
585
+ "u1",
586
+ );
587
+ });
267
588
  });
268
589
 
269
590
  describe("optimistic assistant message", () => {
@@ -460,6 +781,175 @@ describe("ExternalStoreThreadRuntimeCore adapter contract", () => {
460
781
  });
461
782
  });
462
783
 
784
+ describe("tool callbacks", () => {
785
+ it("handles rejected automatic tool result callbacks", async () => {
786
+ const error = new Error("tool result failed");
787
+ const consoleError = vi
788
+ .spyOn(console, "error")
789
+ .mockImplementation(() => {});
790
+ const execute = vi.fn(async () => ({ forecast: "sunny" }));
791
+ let callbackCalls = 0;
792
+ const onAddToolResult = () => {
793
+ callbackCalls += 1;
794
+ return Promise.reject(error);
795
+ };
796
+ const core = new ExternalStoreThreadRuntimeCore(
797
+ {
798
+ getModelContext: () => ({
799
+ tools: {
800
+ weatherSearch: {
801
+ parameters: { type: "object", properties: {} },
802
+ execute,
803
+ },
804
+ },
805
+ }),
806
+ },
807
+ createBaseAdapter({
808
+ unstable_enableToolInvocations: true,
809
+ onAddToolResult,
810
+ }),
811
+ );
812
+
813
+ const rejections = await captureUnhandledRejections(async () => {
814
+ core.__internal_setAdapter(
815
+ createBaseAdapter({
816
+ unstable_enableToolInvocations: true,
817
+ isRunning: true,
818
+ messages: [
819
+ {
820
+ ...createAssistantMessage("a1"),
821
+ status: { type: "requires-action", reason: "tool-calls" },
822
+ content: [
823
+ {
824
+ type: "tool-call",
825
+ toolCallId: "tc1",
826
+ toolName: "weatherSearch",
827
+ args: { city: "London" },
828
+ argsText: '{"city":"London"}',
829
+ },
830
+ ],
831
+ },
832
+ ],
833
+ onAddToolResult,
834
+ }),
835
+ );
836
+
837
+ await vi.waitFor(() =>
838
+ expect(consoleError).toHaveBeenCalledWith(
839
+ "[ExternalStoreThreadRuntimeCore] onAddToolResult callback rejected",
840
+ error,
841
+ ),
842
+ );
843
+ });
844
+
845
+ expect(execute).toHaveBeenCalledOnce();
846
+ expect(callbackCalls).toBe(1);
847
+ expect(rejections).toEqual([]);
848
+ });
849
+
850
+ it("handles rejected direct tool result callbacks", async () => {
851
+ const error = new Error("tool result failed");
852
+ const consoleError = vi
853
+ .spyOn(console, "error")
854
+ .mockImplementation(() => {});
855
+ let callbackCalls = 0;
856
+ const core = new ExternalStoreThreadRuntimeCore(
857
+ contextProvider,
858
+ createBaseAdapter({
859
+ onAddToolResult: () => {
860
+ callbackCalls += 1;
861
+ return Promise.reject(error);
862
+ },
863
+ }),
864
+ );
865
+
866
+ const rejections = await captureUnhandledRejections(async () => {
867
+ core.addToolResult({
868
+ messageId: "m1",
869
+ toolName: "weatherSearch",
870
+ toolCallId: "tc1",
871
+ result: { forecast: "sunny" },
872
+ isError: false,
873
+ });
874
+
875
+ await vi.waitFor(() =>
876
+ expect(consoleError).toHaveBeenCalledWith(
877
+ "[ExternalStoreThreadRuntimeCore] onAddToolResult callback rejected",
878
+ error,
879
+ ),
880
+ );
881
+ });
882
+
883
+ expect(callbackCalls).toBe(1);
884
+ expect(rejections).toEqual([]);
885
+ });
886
+
887
+ it("handles rejected onRespondToToolApproval callbacks", async () => {
888
+ const error = new Error("approval failed");
889
+ const consoleError = vi
890
+ .spyOn(console, "error")
891
+ .mockImplementation(() => {});
892
+ let callbackCalls = 0;
893
+ const core = new ExternalStoreThreadRuntimeCore(
894
+ contextProvider,
895
+ createBaseAdapter({
896
+ onRespondToToolApproval: () => {
897
+ callbackCalls += 1;
898
+ return Promise.reject(error);
899
+ },
900
+ }),
901
+ );
902
+
903
+ const rejections = await captureUnhandledRejections(async () => {
904
+ core.respondToToolApproval({
905
+ approvalId: "approval-1",
906
+ approved: true,
907
+ });
908
+
909
+ await vi.waitFor(() =>
910
+ expect(consoleError).toHaveBeenCalledWith(
911
+ "[ExternalStoreThreadRuntimeCore] onRespondToToolApproval callback rejected",
912
+ error,
913
+ ),
914
+ );
915
+ });
916
+
917
+ expect(callbackCalls).toBe(1);
918
+ expect(rejections).toEqual([]);
919
+ });
920
+
921
+ it("handles rejected onCancel callbacks", async () => {
922
+ const error = new Error("cancel failed");
923
+ const consoleError = vi
924
+ .spyOn(console, "error")
925
+ .mockImplementation(() => {});
926
+ let callbackCalls = 0;
927
+ const core = new ExternalStoreThreadRuntimeCore(
928
+ contextProvider,
929
+ createBaseAdapter({
930
+ onCancel: () => {
931
+ callbackCalls += 1;
932
+ return Promise.reject(error);
933
+ },
934
+ }),
935
+ );
936
+
937
+ const rejections = await captureUnhandledRejections(async () => {
938
+ core.cancelRun();
939
+
940
+ await vi.waitFor(() =>
941
+ expect(consoleError).toHaveBeenCalledWith(
942
+ "[ExternalStoreThreadRuntimeCore] onCancel callback rejected",
943
+ error,
944
+ ),
945
+ );
946
+ });
947
+
948
+ expect(callbackCalls).toBe(1);
949
+ expect(rejections).toEqual([]);
950
+ });
951
+ });
952
+
463
953
  describe("messageRepository incremental sync", () => {
464
954
  const u1 = createUserMessage("u1");
465
955
  const a1 = createAssistantMessage("a1");
@@ -4,6 +4,7 @@ import type { ExternalStoreAdapter } from "../runtimes/external-store/external-s
4
4
  import type { ModelContextProvider } from "../model-context/types";
5
5
  import type { ThreadMessageLike } from "../runtime/utils/thread-message-like";
6
6
  import type { AppendMessage } from "../types/message";
7
+ import { invalidateThreadRuntime } from "../runtime/utils/thread-runtime-lifecycle";
7
8
 
8
9
  const mockContextProvider: ModelContextProvider = {
9
10
  getModelContext: () => ({}),
@@ -868,6 +869,129 @@ describe("ExternalStoreThreadRuntimeCore - message queue", () => {
868
869
  expect(withoutQueue.capabilities.queue).toBe(false);
869
870
  });
870
871
 
872
+ it("waits for thread initialization before enqueueing into the queue adapter", async () => {
873
+ let resolveInitialization!: () => void;
874
+ const initialization = new Promise<void>((resolve) => {
875
+ resolveInitialization = resolve;
876
+ });
877
+ const queue = makeQueue();
878
+ const onNew = vi.fn(async () => {});
879
+ const runtime = new ExternalStoreThreadRuntimeCore(
880
+ mockContextProvider,
881
+ makeStore({ onNew, queue }),
882
+ );
883
+ runtime.__internal_setGetInitializePromise(() => initialization);
884
+
885
+ const appendPromise = runtime.append(appendMessage());
886
+ await Promise.resolve();
887
+
888
+ expect(queue.enqueue).not.toHaveBeenCalled();
889
+
890
+ resolveInitialization();
891
+ await appendPromise;
892
+
893
+ expect(queue.enqueue).toHaveBeenCalledTimes(1);
894
+ expect(onNew).not.toHaveBeenCalled();
895
+ });
896
+
897
+ it("does not enqueue when the thread is invalidated during initialization", async () => {
898
+ let resolveInitialization!: () => void;
899
+ const initialization = new Promise<void>((resolve) => {
900
+ resolveInitialization = resolve;
901
+ });
902
+ const queue = makeQueue();
903
+ const runtime = new ExternalStoreThreadRuntimeCore(
904
+ mockContextProvider,
905
+ makeStore({ onNew: vi.fn(), queue }),
906
+ );
907
+ runtime.__internal_setGetInitializePromise(() => initialization);
908
+
909
+ const appendPromise = runtime.append(appendMessage());
910
+ await Promise.resolve();
911
+ invalidateThreadRuntime(runtime);
912
+ resolveInitialization();
913
+
914
+ await appendPromise;
915
+ expect(queue.enqueue).not.toHaveBeenCalled();
916
+ expect(queue.steer).not.toHaveBeenCalled();
917
+ });
918
+
919
+ it("dispatches an append without waiting for thread initialization", async () => {
920
+ const initialization = new Promise<void>(() => {});
921
+ const getInitializePromise = vi.fn(() => initialization);
922
+ const onNew = vi.fn(async () => {});
923
+ const runtime = new ExternalStoreThreadRuntimeCore(
924
+ mockContextProvider,
925
+ makeStore({ onNew }),
926
+ );
927
+ runtime.__internal_setGetInitializePromise(getInitializePromise);
928
+
929
+ await runtime.append(appendMessage());
930
+
931
+ expect(onNew).toHaveBeenCalledTimes(1);
932
+ expect(getInitializePromise).toHaveBeenCalledTimes(1);
933
+ });
934
+
935
+ it("dispatches concurrent appends without waiting for initialization", async () => {
936
+ const initialization = new Promise<void>(() => {});
937
+ const onNew = vi.fn(async () => {});
938
+ const runtime = new ExternalStoreThreadRuntimeCore(
939
+ mockContextProvider,
940
+ makeStore({ onNew }),
941
+ );
942
+ runtime.__internal_setGetInitializePromise(() => initialization);
943
+
944
+ await Promise.all([
945
+ runtime.append(appendMessage()),
946
+ runtime.append(appendMessage()),
947
+ ]);
948
+
949
+ expect(onNew).toHaveBeenCalledTimes(2);
950
+ });
951
+
952
+ it("does not fail the append when thread initialization rejects", async () => {
953
+ const initialization = Promise.reject(new Error("initialization failed"));
954
+ const onNew = vi.fn(async () => {});
955
+ const runtime = new ExternalStoreThreadRuntimeCore(
956
+ mockContextProvider,
957
+ makeStore({ onNew }),
958
+ );
959
+ runtime.__internal_setGetInitializePromise(() => initialization);
960
+
961
+ await runtime.append(appendMessage());
962
+
963
+ expect(onNew).toHaveBeenCalledTimes(1);
964
+ });
965
+
966
+ it("drops an append disposed while aborting client-side tools", async () => {
967
+ let resolveAbort!: () => void;
968
+ const abortPromise = new Promise<void>((resolve) => {
969
+ resolveAbort = resolve;
970
+ });
971
+ const onNew = vi.fn(async () => {});
972
+ const runtime = new ExternalStoreThreadRuntimeCore(
973
+ mockContextProvider,
974
+ makeStore({ onNew }),
975
+ );
976
+ runtime.__internal_setGetInitializePromise(() => Promise.resolve());
977
+ const abort = vi.fn(() => abortPromise);
978
+ (
979
+ runtime as unknown as { _toolInvocations: { abort: typeof abort } }
980
+ )._toolInvocations = {
981
+ abort,
982
+ };
983
+
984
+ const appendPromise = runtime.append(appendMessage());
985
+ await Promise.resolve();
986
+ expect(abort).toHaveBeenCalledOnce();
987
+
988
+ invalidateThreadRuntime(runtime);
989
+ resolveAbort();
990
+
991
+ await appendPromise;
992
+ expect(onNew).not.toHaveBeenCalled();
993
+ });
994
+
871
995
  it("routes a tail append through the queue adapter instead of onNew", async () => {
872
996
  const queue = makeQueue();
873
997
  const onNew = vi.fn();