@assistant-ui/core 0.3.13 → 0.3.15

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 (463) 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 +38 -15
  8. package/dist/adapters/voice.js.map +1 -1
  9. package/dist/index.d.ts +2 -2
  10. package/dist/index.js +1 -1
  11. package/dist/index.js.map +1 -1
  12. package/dist/internal.d.ts +3 -2
  13. package/dist/internal.js +8 -7
  14. package/dist/model-context/frame/host.d.ts +1 -1
  15. package/dist/model-context/frame/host.d.ts.map +1 -1
  16. package/dist/model-context/frame/host.js +19 -4
  17. package/dist/model-context/frame/host.js.map +1 -1
  18. package/dist/model-context/frame/provider.d.ts +4 -0
  19. package/dist/model-context/frame/provider.d.ts.map +1 -1
  20. package/dist/model-context/frame/provider.js +50 -3
  21. package/dist/model-context/frame/provider.js.map +1 -1
  22. package/dist/model-context/frame/types.d.ts +4 -1
  23. package/dist/model-context/frame/types.d.ts.map +1 -1
  24. package/dist/model-context/frame/types.js.map +1 -1
  25. package/dist/model-context/registry.d.ts.map +1 -1
  26. package/dist/model-context/registry.js +16 -4
  27. package/dist/model-context/registry.js.map +1 -1
  28. package/dist/react/RuntimeAdapter.d.ts +8 -1
  29. package/dist/react/RuntimeAdapter.d.ts.map +1 -1
  30. package/dist/react/RuntimeAdapter.js +23 -18
  31. package/dist/react/RuntimeAdapter.js.map +1 -1
  32. package/dist/react/adapters/LocalStorageThreadListAdapter.d.ts.map +1 -1
  33. package/dist/react/adapters/LocalStorageThreadListAdapter.js +28 -12
  34. package/dist/react/adapters/LocalStorageThreadListAdapter.js.map +1 -1
  35. package/dist/react/client/AdaptedRemoteThread.d.ts +11 -0
  36. package/dist/react/client/AdaptedRemoteThread.d.ts.map +1 -0
  37. package/dist/react/client/AdaptedRemoteThread.js +27 -0
  38. package/dist/react/client/AdaptedRemoteThread.js.map +1 -0
  39. package/dist/react/client/InMemoryThreadList.d.ts +19 -0
  40. package/dist/react/client/InMemoryThreadList.d.ts.map +1 -0
  41. package/dist/react/client/InMemoryThreadList.js +418 -0
  42. package/dist/react/client/InMemoryThreadList.js.map +1 -0
  43. package/dist/react/client/Interactables.d.ts.map +1 -1
  44. package/dist/react/client/Interactables.js +159 -102
  45. package/dist/react/client/Interactables.js.map +1 -1
  46. package/dist/react/client/RemoteThreadList.d.ts +34 -0
  47. package/dist/react/client/RemoteThreadList.d.ts.map +1 -0
  48. package/dist/react/client/RemoteThreadList.js +898 -0
  49. package/dist/react/client/RemoteThreadList.js.map +1 -0
  50. package/dist/react/client/Tools.d.ts.map +1 -1
  51. package/dist/react/client/Tools.js +38 -27
  52. package/dist/react/client/Tools.js.map +1 -1
  53. package/dist/react/index.d.ts +7 -2
  54. package/dist/react/index.js +5 -2
  55. package/dist/react/interactables-legacy/Interactables.d.ts.map +1 -1
  56. package/dist/react/interactables-legacy/Interactables.js +145 -110
  57. package/dist/react/interactables-legacy/Interactables.js.map +1 -1
  58. package/dist/react/primitive-hooks/useActionBarCopy.d.ts.map +1 -1
  59. package/dist/react/primitive-hooks/useActionBarCopy.js +20 -19
  60. package/dist/react/primitive-hooks/useActionBarCopy.js.map +1 -1
  61. package/dist/react/primitive-hooks/useActionBarEdit.d.ts.map +1 -1
  62. package/dist/react/primitive-hooks/useActionBarEdit.js +2 -4
  63. package/dist/react/primitive-hooks/useActionBarEdit.js.map +1 -1
  64. package/dist/react/primitive-hooks/useActionBarReload.d.ts.map +1 -1
  65. package/dist/react/primitive-hooks/useActionBarReload.js +2 -4
  66. package/dist/react/primitive-hooks/useActionBarReload.js.map +1 -1
  67. package/dist/react/primitive-hooks/useBranchPickerNext.d.ts.map +1 -1
  68. package/dist/react/primitive-hooks/useBranchPickerNext.js +2 -6
  69. package/dist/react/primitive-hooks/useBranchPickerNext.js.map +1 -1
  70. package/dist/react/primitive-hooks/useBranchPickerPrevious.d.ts.map +1 -1
  71. package/dist/react/primitive-hooks/useBranchPickerPrevious.js +2 -6
  72. package/dist/react/primitive-hooks/useBranchPickerPrevious.js.map +1 -1
  73. package/dist/react/primitive-hooks/useComposerCancel.d.ts.map +1 -1
  74. package/dist/react/primitive-hooks/useComposerCancel.js +2 -4
  75. package/dist/react/primitive-hooks/useComposerCancel.js.map +1 -1
  76. package/dist/react/primitive-hooks/useComposerSend.d.ts.map +1 -1
  77. package/dist/react/primitive-hooks/useComposerSend.js +2 -4
  78. package/dist/react/primitive-hooks/useComposerSend.js.map +1 -1
  79. package/dist/react/primitive-hooks/useSuggestionTrigger.d.ts.map +1 -1
  80. package/dist/react/primitive-hooks/useSuggestionTrigger.js +6 -2
  81. package/dist/react/primitive-hooks/useSuggestionTrigger.js.map +1 -1
  82. package/dist/react/primitive-hooks/useThreadIsEmpty.d.ts +3 -0
  83. package/dist/react/primitive-hooks/useThreadIsEmpty.d.ts.map +1 -1
  84. package/dist/react/primitive-hooks/useThreadIsEmpty.js +3 -0
  85. package/dist/react/primitive-hooks/useThreadIsEmpty.js.map +1 -1
  86. package/dist/react/primitive-hooks/useThreadIsRunning.d.ts +3 -0
  87. package/dist/react/primitive-hooks/useThreadIsRunning.d.ts.map +1 -1
  88. package/dist/react/primitive-hooks/useThreadIsRunning.js +3 -0
  89. package/dist/react/primitive-hooks/useThreadIsRunning.js.map +1 -1
  90. package/dist/react/primitives/message/MessageParts.d.ts +13 -0
  91. package/dist/react/primitives/message/MessageParts.d.ts.map +1 -1
  92. package/dist/react/primitives/message/MessageParts.js.map +1 -1
  93. package/dist/react/primitives/thread/ThreadMessages.d.ts.map +1 -1
  94. package/dist/react/primitives/thread/ThreadMessages.js +1 -1
  95. package/dist/react/primitives/thread/ThreadMessages.js.map +1 -1
  96. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.d.ts +24 -10
  97. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.d.ts.map +1 -1
  98. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js +115 -71
  99. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js.map +1 -1
  100. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts +14 -2
  101. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts.map +1 -1
  102. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js +232 -63
  103. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js.map +1 -1
  104. package/dist/react/runtimes/RemoteThreadResource.d.ts +30 -0
  105. package/dist/react/runtimes/RemoteThreadResource.d.ts.map +1 -0
  106. package/dist/react/runtimes/RemoteThreadResource.js +136 -0
  107. package/dist/react/runtimes/RemoteThreadResource.js.map +1 -0
  108. package/dist/react/runtimes/RuntimeAdapterProvider.d.ts +3 -10
  109. package/dist/react/runtimes/RuntimeAdapterProvider.d.ts.map +1 -1
  110. package/dist/react/runtimes/RuntimeAdapterProvider.js +3 -6
  111. package/dist/react/runtimes/RuntimeAdapterProvider.js.map +1 -1
  112. package/dist/react/runtimes/assistant-transport/commandQueue.d.ts +17 -0
  113. package/dist/react/runtimes/assistant-transport/commandQueue.d.ts.map +1 -0
  114. package/dist/react/runtimes/assistant-transport/commandQueue.js +55 -0
  115. package/dist/react/runtimes/assistant-transport/commandQueue.js.map +1 -0
  116. package/dist/react/runtimes/assistant-transport/replayBoundaryStream.d.ts +11 -0
  117. package/dist/react/runtimes/assistant-transport/replayBoundaryStream.d.ts.map +1 -0
  118. package/dist/react/runtimes/assistant-transport/replayBoundaryStream.js +165 -0
  119. package/dist/react/runtimes/assistant-transport/replayBoundaryStream.js.map +1 -0
  120. package/dist/react/runtimes/assistant-transport/runManager.d.ts +15 -0
  121. package/dist/react/runtimes/assistant-transport/runManager.d.ts.map +1 -0
  122. package/dist/react/runtimes/assistant-transport/runManager.js +86 -0
  123. package/dist/react/runtimes/assistant-transport/runManager.js.map +1 -0
  124. package/dist/react/runtimes/assistant-transport/types.d.ts +130 -0
  125. package/dist/react/runtimes/assistant-transport/types.d.ts.map +1 -0
  126. package/dist/react/runtimes/assistant-transport/types.js +0 -0
  127. package/dist/react/runtimes/assistant-transport/useAssistantTransportRuntime.d.ts +14 -0
  128. package/dist/react/runtimes/assistant-transport/useAssistantTransportRuntime.d.ts.map +1 -0
  129. package/dist/react/runtimes/assistant-transport/useAssistantTransportRuntime.js +296 -0
  130. package/dist/react/runtimes/assistant-transport/useAssistantTransportRuntime.js.map +1 -0
  131. package/dist/react/runtimes/assistant-transport/useConvertedState.d.ts +7 -0
  132. package/dist/react/runtimes/assistant-transport/useConvertedState.d.ts.map +1 -0
  133. package/dist/react/runtimes/assistant-transport/useConvertedState.js +25 -0
  134. package/dist/react/runtimes/assistant-transport/useConvertedState.js.map +1 -0
  135. package/dist/react/runtimes/assistant-transport/useLatestRef.d.ts +7 -0
  136. package/dist/react/runtimes/assistant-transport/useLatestRef.d.ts.map +1 -0
  137. package/dist/react/runtimes/assistant-transport/useLatestRef.js +27 -0
  138. package/dist/react/runtimes/assistant-transport/useLatestRef.js.map +1 -0
  139. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.d.ts.map +1 -1
  140. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js +39 -13
  141. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js.map +1 -1
  142. package/dist/react/runtimes/cloud/CloudFileAttachmentAdapter.d.ts +2 -1
  143. package/dist/react/runtimes/cloud/CloudFileAttachmentAdapter.d.ts.map +1 -1
  144. package/dist/react/runtimes/cloud/CloudFileAttachmentAdapter.js +3 -3
  145. package/dist/react/runtimes/cloud/CloudFileAttachmentAdapter.js.map +1 -1
  146. package/dist/react/runtimes/cloud/auiV0.d.ts +4 -0
  147. package/dist/react/runtimes/cloud/auiV0.d.ts.map +1 -1
  148. package/dist/react/runtimes/cloud/auiV0.js +7 -0
  149. package/dist/react/runtimes/cloud/auiV0.js.map +1 -1
  150. package/dist/react/runtimes/cloud/useCloudThreadListAdapter.d.ts.map +1 -1
  151. package/dist/react/runtimes/cloud/useCloudThreadListAdapter.js +60 -17
  152. package/dist/react/runtimes/cloud/useCloudThreadListAdapter.js.map +1 -1
  153. package/dist/react/runtimes/cloud/useCloudThreadListRuntime.d.ts +16 -0
  154. package/dist/react/runtimes/cloud/useCloudThreadListRuntime.d.ts.map +1 -0
  155. package/dist/react/runtimes/cloud/useCloudThreadListRuntime.js +36 -0
  156. package/dist/react/runtimes/cloud/useCloudThreadListRuntime.js.map +1 -0
  157. package/dist/react/runtimes/external-message-converter.js +14 -14
  158. package/dist/react/runtimes/external-message-converter.js.map +1 -1
  159. package/dist/react/runtimes/useExternalStoreRuntime.d.ts.map +1 -1
  160. package/dist/react/runtimes/useExternalStoreRuntime.js +44 -28
  161. package/dist/react/runtimes/useExternalStoreRuntime.js.map +1 -1
  162. package/dist/react/runtimes/useExternalStoreSharedOptions.js.map +1 -1
  163. package/dist/react/runtimes/useLocalRuntime.js +1 -1
  164. package/dist/react/runtimes/useRuntimeAdapters.d.ts +9 -0
  165. package/dist/react/runtimes/useRuntimeAdapters.d.ts.map +1 -0
  166. package/dist/react/runtimes/useRuntimeAdapters.js +20 -0
  167. package/dist/react/runtimes/useRuntimeAdapters.js.map +1 -0
  168. package/dist/runtime/api/composer-runtime.d.ts +5 -0
  169. package/dist/runtime/api/composer-runtime.d.ts.map +1 -1
  170. package/dist/runtime/api/composer-runtime.js.map +1 -1
  171. package/dist/runtime/api/message-runtime.js +1 -1
  172. package/dist/runtime/api/thread-list-runtime.d.ts +1 -0
  173. package/dist/runtime/api/thread-list-runtime.d.ts.map +1 -1
  174. package/dist/runtime/api/thread-list-runtime.js +4 -2
  175. package/dist/runtime/api/thread-list-runtime.js.map +1 -1
  176. package/dist/runtime/api/thread-runtime.d.ts +12 -2
  177. package/dist/runtime/api/thread-runtime.d.ts.map +1 -1
  178. package/dist/runtime/api/thread-runtime.js +8 -2
  179. package/dist/runtime/api/thread-runtime.js.map +1 -1
  180. package/dist/runtime/base/base-composer-runtime-core.d.ts.map +1 -1
  181. package/dist/runtime/base/base-composer-runtime-core.js +8 -7
  182. package/dist/runtime/base/base-composer-runtime-core.js.map +1 -1
  183. package/dist/runtime/base/base-thread-runtime-core.d.ts +1 -0
  184. package/dist/runtime/base/base-thread-runtime-core.d.ts.map +1 -1
  185. package/dist/runtime/base/base-thread-runtime-core.js +3 -3
  186. package/dist/runtime/base/base-thread-runtime-core.js.map +1 -1
  187. package/dist/runtime/base/default-thread-composer-runtime-core.d.ts +0 -1
  188. package/dist/runtime/base/default-thread-composer-runtime-core.d.ts.map +1 -1
  189. package/dist/runtime/base/default-thread-composer-runtime-core.js +10 -4
  190. package/dist/runtime/base/default-thread-composer-runtime-core.js.map +1 -1
  191. package/dist/runtime/interfaces/thread-runtime-core.d.ts +6 -0
  192. package/dist/runtime/interfaces/thread-runtime-core.d.ts.map +1 -1
  193. package/dist/runtime/internal.js +5 -5
  194. package/dist/runtime/queue/message-queue.d.ts.map +1 -1
  195. package/dist/runtime/queue/message-queue.js +2 -1
  196. package/dist/runtime/queue/message-queue.js.map +1 -1
  197. package/dist/runtime/utils/message-repository.d.ts.map +1 -1
  198. package/dist/runtime/utils/message-repository.js +27 -18
  199. package/dist/runtime/utils/message-repository.js.map +1 -1
  200. package/dist/runtime/utils/thread-runtime-lifecycle.d.ts +8 -0
  201. package/dist/runtime/utils/thread-runtime-lifecycle.d.ts.map +1 -0
  202. package/dist/runtime/utils/thread-runtime-lifecycle.js +11 -0
  203. package/dist/runtime/utils/thread-runtime-lifecycle.js.map +1 -0
  204. package/dist/runtimes/external-store/external-store-shared-options.js.map +1 -1
  205. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.d.ts.map +1 -1
  206. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.js +4 -1
  207. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.js.map +1 -1
  208. package/dist/runtimes/external-store/external-store-thread-runtime-core.d.ts +8 -0
  209. package/dist/runtimes/external-store/external-store-thread-runtime-core.d.ts.map +1 -1
  210. package/dist/runtimes/external-store/external-store-thread-runtime-core.js +37 -13
  211. package/dist/runtimes/external-store/external-store-thread-runtime-core.js.map +1 -1
  212. package/dist/runtimes/inert/InertThreadRuntimeCore.d.ts +102 -0
  213. package/dist/runtimes/inert/InertThreadRuntimeCore.d.ts.map +1 -0
  214. package/dist/runtimes/inert/InertThreadRuntimeCore.js +156 -0
  215. package/dist/runtimes/inert/InertThreadRuntimeCore.js.map +1 -0
  216. package/dist/runtimes/internal.d.ts +1 -1
  217. package/dist/runtimes/local/local-thread-runtime-core.d.ts +3 -0
  218. package/dist/runtimes/local/local-thread-runtime-core.d.ts.map +1 -1
  219. package/dist/runtimes/local/local-thread-runtime-core.js +78 -19
  220. package/dist/runtimes/local/local-thread-runtime-core.js.map +1 -1
  221. package/dist/runtimes/readonly/ReadonlyThreadRuntimeCore.d.ts +12 -59
  222. package/dist/runtimes/readonly/ReadonlyThreadRuntimeCore.d.ts.map +1 -1
  223. package/dist/runtimes/readonly/ReadonlyThreadRuntimeCore.js +10 -142
  224. package/dist/runtimes/readonly/ReadonlyThreadRuntimeCore.js.map +1 -1
  225. package/dist/runtimes/remote-thread-list/adapter-changed.d.ts +9 -0
  226. package/dist/runtimes/remote-thread-list/adapter-changed.d.ts.map +1 -0
  227. package/dist/runtimes/remote-thread-list/adapter-changed.js +13 -0
  228. package/dist/runtimes/remote-thread-list/adapter-changed.js.map +1 -0
  229. package/dist/runtimes/remote-thread-list/empty-thread-core.d.ts.map +1 -1
  230. package/dist/runtimes/remote-thread-list/empty-thread-core.js +16 -152
  231. package/dist/runtimes/remote-thread-list/empty-thread-core.js.map +1 -1
  232. package/dist/runtimes/remote-thread-list/optimistic-state.d.ts +2 -0
  233. package/dist/runtimes/remote-thread-list/optimistic-state.d.ts.map +1 -1
  234. package/dist/runtimes/remote-thread-list/optimistic-state.js +11 -0
  235. package/dist/runtimes/remote-thread-list/optimistic-state.js.map +1 -1
  236. package/dist/runtimes/remote-thread-list/types.d.ts +32 -1
  237. package/dist/runtimes/remote-thread-list/types.d.ts.map +1 -1
  238. package/dist/runtimes/tool-invocations/ToolInvocationTracker.d.ts +1 -12
  239. package/dist/runtimes/tool-invocations/ToolInvocationTracker.d.ts.map +1 -1
  240. package/dist/runtimes/tool-invocations/ToolInvocationTracker.js +67 -38
  241. package/dist/runtimes/tool-invocations/ToolInvocationTracker.js.map +1 -1
  242. package/dist/store/clients/external-thread.js +1 -1
  243. package/dist/store/clients/external-thread.js.map +1 -1
  244. package/dist/store/clients/suggestions.js +2 -2
  245. package/dist/store/clients/suggestions.js.map +1 -1
  246. package/dist/store/clients/thread-selection-events.d.ts +10 -0
  247. package/dist/store/clients/thread-selection-events.d.ts.map +1 -0
  248. package/dist/store/clients/thread-selection-events.js +40 -0
  249. package/dist/store/clients/thread-selection-events.js.map +1 -0
  250. package/dist/store/env.d.ts +5 -0
  251. package/dist/store/env.d.ts.map +1 -0
  252. package/dist/store/env.js +12 -0
  253. package/dist/store/env.js.map +1 -0
  254. package/dist/store/index.d.ts +5 -3
  255. package/dist/store/index.js +4 -2
  256. package/dist/store/internal.d.ts +4 -1
  257. package/dist/store/internal.js +4 -1
  258. package/dist/store/primitive-predicates.d.ts +19 -0
  259. package/dist/store/primitive-predicates.d.ts.map +1 -0
  260. package/dist/store/primitive-predicates.js +19 -0
  261. package/dist/store/primitive-predicates.js.map +1 -0
  262. package/dist/store/runtime-clients/attachment-runtime-client.d.ts.map +1 -1
  263. package/dist/store/runtime-clients/attachment-runtime-client.js +18 -11
  264. package/dist/store/runtime-clients/attachment-runtime-client.js.map +1 -1
  265. package/dist/store/runtime-clients/handle-runtime-action.d.ts +5 -0
  266. package/dist/store/runtime-clients/handle-runtime-action.d.ts.map +1 -0
  267. package/dist/store/runtime-clients/handle-runtime-action.js +14 -0
  268. package/dist/store/runtime-clients/handle-runtime-action.js.map +1 -0
  269. package/dist/store/runtime-clients/handle-thread-list-action.d.ts +5 -0
  270. package/dist/store/runtime-clients/handle-thread-list-action.d.ts.map +1 -0
  271. package/dist/store/runtime-clients/handle-thread-list-action.js +7 -0
  272. package/dist/store/runtime-clients/handle-thread-list-action.js.map +1 -0
  273. package/dist/store/runtime-clients/thread-list-item-runtime-client.d.ts.map +1 -1
  274. package/dist/store/runtime-clients/thread-list-item-runtime-client.js +62 -29
  275. package/dist/store/runtime-clients/thread-list-item-runtime-client.js.map +1 -1
  276. package/dist/store/runtime-clients/thread-list-runtime-client.d.ts.map +1 -1
  277. package/dist/store/runtime-clients/thread-list-runtime-client.js +5 -6
  278. package/dist/store/runtime-clients/thread-list-runtime-client.js.map +1 -1
  279. package/dist/store/scopes/composer.d.ts +9 -4
  280. package/dist/store/scopes/composer.d.ts.map +1 -1
  281. package/dist/store/scopes/thread-list-item.d.ts +7 -6
  282. package/dist/store/scopes/thread-list-item.d.ts.map +1 -1
  283. package/dist/store/scopes/thread.d.ts +8 -7
  284. package/dist/store/scopes/thread.d.ts.map +1 -1
  285. package/dist/store/scopes/threads.d.ts +17 -1
  286. package/dist/store/scopes/threads.d.ts.map +1 -1
  287. package/dist/subscribable/subscribable.d.ts +2 -1
  288. package/dist/subscribable/subscribable.d.ts.map +1 -1
  289. package/dist/subscribable/subscribable.js +23 -18
  290. package/dist/subscribable/subscribable.js.map +1 -1
  291. package/dist/types/message.d.ts +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/dist/utils/silent-runtime-action.d.ts +6 -0
  302. package/dist/utils/silent-runtime-action.d.ts.map +1 -0
  303. package/dist/utils/silent-runtime-action.js +7 -0
  304. package/dist/utils/silent-runtime-action.js.map +1 -0
  305. package/package.json +8 -8
  306. package/src/adapters/speech.test.ts +118 -1
  307. package/src/adapters/speech.ts +21 -22
  308. package/src/adapters/thread-history.ts +2 -0
  309. package/src/adapters/voice.test.ts +134 -0
  310. package/src/adapters/voice.ts +35 -27
  311. package/src/index.ts +1 -0
  312. package/src/internal.ts +1 -0
  313. package/src/model-context/frame/host.test.ts +87 -5
  314. package/src/model-context/frame/host.ts +18 -4
  315. package/src/model-context/frame/provider.test.ts +347 -20
  316. package/src/model-context/frame/provider.ts +58 -2
  317. package/src/model-context/frame/types.ts +5 -0
  318. package/src/model-context/registry.test.ts +131 -0
  319. package/src/model-context/registry.ts +16 -6
  320. package/src/react/RuntimeAdapter.test.ts +2 -0
  321. package/src/react/RuntimeAdapter.ts +20 -8
  322. package/src/react/adapters/LocalStorageThreadListAdapter.tsx +34 -17
  323. package/src/react/client/AdaptedRemoteThread.ts +41 -0
  324. package/src/react/client/InMemoryThreadList.test.tsx +148 -0
  325. package/src/react/client/InMemoryThreadList.ts +290 -0
  326. package/src/react/client/Interactables.test.ts +326 -2
  327. package/src/react/client/Interactables.ts +227 -110
  328. package/src/react/client/RemoteThreadList.test.ts +1222 -0
  329. package/src/react/client/RemoteThreadList.ts +1136 -0
  330. package/src/react/client/Tools.ts +36 -29
  331. package/src/react/index.ts +13 -0
  332. package/src/react/interactables-legacy/Interactables.test.ts +317 -0
  333. package/src/react/interactables-legacy/Interactables.ts +204 -128
  334. package/src/react/primitive-hooks/useActionBarCopy.test.ts +15 -0
  335. package/src/react/primitive-hooks/useActionBarCopy.ts +15 -10
  336. package/src/react/primitive-hooks/useActionBarEdit.ts +2 -1
  337. package/src/react/primitive-hooks/useActionBarReload.ts +2 -6
  338. package/src/react/primitive-hooks/useBranchPickerNext.ts +2 -7
  339. package/src/react/primitive-hooks/useBranchPickerPrevious.ts +2 -7
  340. package/src/react/primitive-hooks/useComposerCancel.ts +2 -1
  341. package/src/react/primitive-hooks/useComposerSend.ts +2 -5
  342. package/src/react/primitive-hooks/useSuggestionTrigger.test.ts +11 -0
  343. package/src/react/primitive-hooks/useSuggestionTrigger.ts +4 -5
  344. package/src/react/primitive-hooks/useThreadIsEmpty.ts +3 -0
  345. package/src/react/primitive-hooks/useThreadIsRunning.ts +3 -0
  346. package/src/react/primitives/message/MessageParts.tsx +0 -1
  347. package/src/react/primitives/thread/ThreadMessages.tsx +2 -1
  348. package/src/react/runtimes/RemoteThreadListHookInstanceManager.retry.test.tsx +116 -0
  349. package/src/react/runtimes/RemoteThreadListHookInstanceManager.running.test.tsx +1 -1
  350. package/src/react/runtimes/RemoteThreadListHookInstanceManager.test.tsx +57 -6
  351. package/src/react/runtimes/RemoteThreadListHookInstanceManager.title.test.tsx +272 -0
  352. package/src/react/runtimes/RemoteThreadListHookInstanceManager.tsx +171 -124
  353. package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.republish.test.ts +87 -0
  354. package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.tsx +327 -83
  355. package/src/react/runtimes/RemoteThreadResource.test.ts +141 -0
  356. package/src/react/runtimes/RemoteThreadResource.ts +233 -0
  357. package/src/react/runtimes/RuntimeAdapterProvider.tsx +8 -20
  358. package/src/react/runtimes/assistant-transport/commandQueue.ts +68 -0
  359. package/src/react/runtimes/assistant-transport/replayBoundaryStream.test.ts +455 -0
  360. package/src/react/runtimes/assistant-transport/replayBoundaryStream.ts +172 -0
  361. package/src/react/runtimes/assistant-transport/runManager.ts +124 -0
  362. package/src/react/runtimes/assistant-transport/transport-delivery.test.tsx +79 -0
  363. package/src/react/runtimes/assistant-transport/transport-parentid.test.tsx +97 -0
  364. package/src/react/runtimes/assistant-transport/transport-scheduling.test.ts +414 -0
  365. package/src/react/runtimes/assistant-transport/types.ts +170 -0
  366. package/src/react/runtimes/assistant-transport/useAssistantTransport.spec.md +141 -0
  367. package/src/react/runtimes/assistant-transport/useAssistantTransportRuntime.test.tsx +683 -0
  368. package/src/react/runtimes/assistant-transport/useAssistantTransportRuntime.ts +483 -0
  369. package/src/react/runtimes/assistant-transport/useConvertedState.ts +20 -0
  370. package/src/react/runtimes/assistant-transport/useLatestRef.ts +9 -0
  371. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.test.tsx +131 -6
  372. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.ts +61 -15
  373. package/src/react/runtimes/cloud/CloudFileAttachmentAdapter.ts +6 -4
  374. package/src/react/runtimes/cloud/auiV0.ts +17 -1
  375. package/src/react/runtimes/cloud/useCloudThreadListAdapter.test.tsx +98 -3
  376. package/src/react/runtimes/cloud/useCloudThreadListAdapter.tsx +104 -27
  377. package/src/react/runtimes/cloud/useCloudThreadListRuntime.test.tsx +56 -0
  378. package/src/react/runtimes/cloud/useCloudThreadListRuntime.ts +31 -0
  379. package/src/react/runtimes/external-message-converter.test.ts +143 -0
  380. package/src/react/runtimes/external-message-converter.ts +41 -41
  381. package/src/react/runtimes/remote-thread-hook-on-tap.test.ts +58 -0
  382. package/src/react/runtimes/useExternalStoreRuntime.lifecycle.test.tsx +83 -0
  383. package/src/react/runtimes/useExternalStoreRuntime.suggestions.test.tsx +13 -3
  384. package/src/react/runtimes/useExternalStoreRuntime.ts +7 -0
  385. package/src/react/runtimes/useExternalStoreSharedOptions.ts +3 -1
  386. package/src/react/runtimes/useLocalRuntime.test.tsx +134 -8
  387. package/src/react/runtimes/useRuntimeAdapters.test.tsx +62 -0
  388. package/src/react/runtimes/useRuntimeAdapters.ts +28 -0
  389. package/src/runtime/api/composer-runtime.ts +5 -0
  390. package/src/runtime/api/thread-list-runtime.ts +3 -1
  391. package/src/runtime/api/thread-runtime.ts +20 -2
  392. package/src/runtime/base/base-composer-runtime-core.ts +6 -5
  393. package/src/runtime/base/base-thread-runtime-core.test.ts +191 -0
  394. package/src/runtime/base/base-thread-runtime-core.ts +11 -2
  395. package/src/runtime/base/default-thread-composer-runtime-core.ts +12 -4
  396. package/src/runtime/interfaces/thread-runtime-core.ts +6 -0
  397. package/src/runtime/queue/message-queue.ts +2 -1
  398. package/src/runtime/utils/message-repository.ts +26 -18
  399. package/src/runtime/utils/thread-runtime-lifecycle.ts +18 -0
  400. package/src/runtimes/external-store/external-store-shared-options.ts +3 -1
  401. package/src/runtimes/external-store/external-store-thread-list-runtime-core.ts +4 -1
  402. package/src/runtimes/external-store/external-store-thread-runtime-core.ts +103 -34
  403. package/src/runtimes/inert/InertThreadRuntimeCore.test.ts +253 -0
  404. package/src/runtimes/inert/InertThreadRuntimeCore.ts +232 -0
  405. package/src/runtimes/local/local-thread-runtime-core.test.ts +692 -4
  406. package/src/runtimes/local/local-thread-runtime-core.ts +117 -22
  407. package/src/runtimes/readonly/ReadonlyThreadRuntimeCore.ts +17 -205
  408. package/src/runtimes/remote-thread-list/adapter-changed.ts +10 -0
  409. package/src/runtimes/remote-thread-list/empty-thread-core.ts +28 -217
  410. package/src/runtimes/remote-thread-list/optimistic-state.test.ts +18 -0
  411. package/src/runtimes/remote-thread-list/optimistic-state.ts +13 -0
  412. package/src/runtimes/remote-thread-list/types.ts +34 -0
  413. package/src/runtimes/tool-invocations/EDGE_CASES.md +17 -6
  414. package/src/runtimes/tool-invocations/ToolInvocationTracker.test.ts +243 -0
  415. package/src/runtimes/tool-invocations/ToolInvocationTracker.ts +127 -61
  416. package/src/store/clients/external-thread.ts +1 -1
  417. package/src/store/clients/model-context-client.test.ts +16 -8
  418. package/src/store/clients/suggestions.ts +2 -2
  419. package/src/store/clients/thread-selection-events.ts +21 -0
  420. package/src/store/env.ts +11 -0
  421. package/src/store/index.ts +14 -1
  422. package/src/store/internal.ts +13 -0
  423. package/src/store/primitive-predicates.test.ts +184 -0
  424. package/src/store/primitive-predicates.ts +43 -0
  425. package/src/store/runtime-clients/attachment-runtime-client.test.ts +41 -0
  426. package/src/store/runtime-clients/attachment-runtime-client.ts +2 -1
  427. package/src/store/runtime-clients/handle-runtime-action.test.ts +40 -0
  428. package/src/store/runtime-clients/handle-runtime-action.ts +14 -0
  429. package/src/store/runtime-clients/handle-thread-list-action.test.ts +69 -0
  430. package/src/store/runtime-clients/handle-thread-list-action.ts +6 -0
  431. package/src/store/runtime-clients/thread-list-item-runtime-client.ts +15 -7
  432. package/src/store/runtime-clients/thread-list-runtime-client.ts +9 -6
  433. package/src/store/scopes/composer.ts +9 -4
  434. package/src/store/scopes/thread-list-item.ts +7 -6
  435. package/src/store/scopes/thread.ts +8 -7
  436. package/src/store/scopes/threads.ts +14 -0
  437. package/src/subscribable/subscribable.test.ts +77 -1
  438. package/src/subscribable/subscribable.ts +38 -23
  439. package/src/tests/MessageRepository.test.ts +48 -0
  440. package/src/tests/RemoteThreadListThreadListRuntimeCore-errors.test.ts +2 -2
  441. package/src/tests/RemoteThreadListThreadListRuntimeCore-loadMore.test.ts +8 -6
  442. package/src/tests/RemoteThreadListThreadListRuntimeCore-switchToThread-unarchive.test.ts +25 -0
  443. package/src/tests/auiV0Encode.test.ts +66 -0
  444. package/src/tests/base-composer-runtime-core-send.test.ts +30 -0
  445. package/src/tests/base-composer-runtime-core.test.ts +28 -0
  446. package/src/tests/composer-can-cancel.test.ts +101 -0
  447. package/src/tests/empty-thread-core.test.ts +6 -0
  448. package/src/tests/external-store-thread-list-runtime-core.test.ts +101 -0
  449. package/src/tests/external-store-thread-runtime-core-adapter.test.ts +211 -1
  450. package/src/tests/external-store-thread-runtime-core.test.ts +124 -0
  451. package/src/tests/external-thread-isEmpty.test.tsx +100 -0
  452. package/src/tests/interactables-toolui-migration.test.tsx +66 -0
  453. package/src/tests/message-queue.test.ts +28 -0
  454. package/src/tests/remote-thread-list-adapter-switch.test.ts +383 -0
  455. package/src/tests/session-reset-notification.test.ts +145 -0
  456. package/src/tests/thread-switch-events.test.tsx +99 -1
  457. package/src/tests/tools-scope-migration.test.tsx +58 -0
  458. package/src/tests/useRemoteThreadListRuntime-controlled.test.tsx +67 -0
  459. package/src/types/message.ts +1 -1
  460. package/src/utils/composite-context-provider.test.ts +139 -0
  461. package/src/utils/composite-context-provider.ts +26 -8
  462. package/src/utils/notify-event-listeners.ts +10 -5
  463. package/src/utils/silent-runtime-action.ts +6 -0
@@ -8,6 +8,8 @@ import type {
8
8
  import type { AppendMessage } from "../../types/message";
9
9
  import type { LocalRuntimeOptionsBase } from "./local-runtime-options";
10
10
  import type { ExportedMessageRepositoryItem } from "../../runtime/utils/message-repository";
11
+ import type { ThreadSuggestion } from "../../runtime/interfaces/thread-runtime-core";
12
+ import { isMessageNotSentError } from "../../types/error";
11
13
 
12
14
  const flush = () => new Promise((resolve) => setTimeout(resolve, 10));
13
15
 
@@ -143,6 +145,180 @@ describe("LocalThreadRuntimeCore events", () => {
143
145
  });
144
146
  });
145
147
 
148
+ describe("LocalThreadRuntimeCore history persistence", () => {
149
+ it("surfaces failed user persistence without abandoning the run", async () => {
150
+ const persistenceError = new Error("history unavailable");
151
+ const run = vi.fn(async () => ({
152
+ content: [{ type: "text" as const, text: "done" }],
153
+ }));
154
+ const append = vi.fn(async (item: ExportedMessageRepositoryItem) => {
155
+ if (item.message.role === "user") throw persistenceError;
156
+ });
157
+ const thread = createThread(
158
+ { run },
159
+ {
160
+ history: {
161
+ async load() {
162
+ return { messages: [] };
163
+ },
164
+ append,
165
+ },
166
+ },
167
+ );
168
+
169
+ await expect(thread.append(userMessage("hello"))).rejects.toBe(
170
+ persistenceError,
171
+ );
172
+
173
+ expect(append).toHaveBeenCalledTimes(2);
174
+ expect(run).toHaveBeenCalledOnce();
175
+ });
176
+
177
+ it("surfaces failed persistence for messages that do not start a run", async () => {
178
+ const persistenceError = new Error("history unavailable");
179
+ const run = vi.fn();
180
+ const thread = createThread(
181
+ { run },
182
+ {
183
+ history: {
184
+ async load() {
185
+ return { messages: [] };
186
+ },
187
+ async append() {
188
+ throw persistenceError;
189
+ },
190
+ },
191
+ },
192
+ );
193
+
194
+ await expect(
195
+ thread.append({ ...userMessage("hello"), startRun: false }),
196
+ ).rejects.toBe(persistenceError);
197
+
198
+ expect(run).not.toHaveBeenCalled();
199
+ });
200
+
201
+ it("handles failed persistence before notifying no-run subscribers", async () => {
202
+ const persistenceError = new Error("history unavailable");
203
+ const listenerError = new Error("listener unavailable");
204
+ const thread = createThread(
205
+ { run: vi.fn() },
206
+ {
207
+ history: {
208
+ async load() {
209
+ return { messages: [] };
210
+ },
211
+ async append() {
212
+ throw persistenceError;
213
+ },
214
+ },
215
+ },
216
+ );
217
+ thread.subscribe(() => {
218
+ throw listenerError;
219
+ });
220
+
221
+ await expect(
222
+ thread.append({ ...userMessage("hello"), startRun: false }),
223
+ ).rejects.toBe(listenerError);
224
+ await flush();
225
+ });
226
+ });
227
+
228
+ describe("LocalThreadRuntimeCore - detach", () => {
229
+ it("drops a pending append when detached", async () => {
230
+ let resolveInitialization!: () => void;
231
+ const initialization = new Promise<void>((resolve) => {
232
+ resolveInitialization = resolve;
233
+ });
234
+ const run = vi.fn(async () => ({
235
+ content: [{ type: "text" as const, text: "done" }],
236
+ }));
237
+ const thread = createThread({ run });
238
+ thread.__internal_setGetInitializePromise(() => initialization);
239
+
240
+ const appendPromise = thread.append(userMessage("hello"));
241
+ await Promise.resolve();
242
+ thread.detach();
243
+ resolveInitialization();
244
+
245
+ await appendPromise;
246
+ expect(run).not.toHaveBeenCalled();
247
+ expect(thread.messages).toEqual([]);
248
+ });
249
+ });
250
+
251
+ describe("LocalThreadRuntimeCore optimistic append", () => {
252
+ it("paints the appended message before initialization resolves", async () => {
253
+ let resolveInitialization!: () => void;
254
+ const initialization = new Promise<void>((resolve) => {
255
+ resolveInitialization = resolve;
256
+ });
257
+ const run = vi.fn(async () => ({
258
+ content: [{ type: "text" as const, text: "done" }],
259
+ }));
260
+ const thread = createThread({ run });
261
+ thread.__internal_setGetInitializePromise(() => initialization);
262
+ const onUpdate = vi.fn();
263
+ thread.subscribe(onUpdate);
264
+
265
+ const appendPromise = thread.append(userMessage("hello"));
266
+ await Promise.resolve();
267
+
268
+ expect(thread.messages).toHaveLength(1);
269
+ expect(thread.messages[0]?.role).toBe("user");
270
+ expect(onUpdate).toHaveBeenCalled();
271
+ expect(run).not.toHaveBeenCalled();
272
+
273
+ resolveInitialization();
274
+ await appendPromise;
275
+ expect(run).toHaveBeenCalledTimes(1);
276
+ });
277
+
278
+ it("silently drops a detached append even when initialization rejects", async () => {
279
+ let rejectInitialization!: (error: unknown) => void;
280
+ const initialization = new Promise<void>((_, reject) => {
281
+ rejectInitialization = reject;
282
+ });
283
+ const run = vi.fn(async () => ({
284
+ content: [{ type: "text" as const, text: "done" }],
285
+ }));
286
+ const thread = createThread({ run });
287
+ thread.__internal_setGetInitializePromise(() => initialization);
288
+
289
+ const appendPromise = thread.append(userMessage("hello"));
290
+ await Promise.resolve();
291
+ thread.detach();
292
+ rejectInitialization(new Error("initialization failed"));
293
+
294
+ await expect(appendPromise).resolves.toBeUndefined();
295
+ expect(thread.messages).toEqual([]);
296
+ expect(run).not.toHaveBeenCalled();
297
+ });
298
+
299
+ it("rolls the optimistic message back when initialization rejects", async () => {
300
+ const initializationError = new Error("initialization failed");
301
+ const run = vi.fn(async () => ({
302
+ content: [{ type: "text" as const, text: "done" }],
303
+ }));
304
+ const thread = createThread({ run });
305
+ thread.__internal_setGetInitializePromise(() =>
306
+ Promise.reject(initializationError),
307
+ );
308
+
309
+ const error = await thread.append(userMessage("hello")).then(
310
+ () => {
311
+ throw new Error("expected the append to reject");
312
+ },
313
+ (e: unknown) => e,
314
+ );
315
+ expect(isMessageNotSentError(error)).toBe(true);
316
+ expect((error as Error).cause).toBe(initializationError);
317
+ expect(thread.messages).toEqual([]);
318
+ expect(run).not.toHaveBeenCalled();
319
+ });
320
+ });
321
+
146
322
  describe("LocalThreadRuntimeCore human-in-the-loop tools", () => {
147
323
  it("pauses on requires-action while a listed tool call has no result", async () => {
148
324
  const { thread, runs } = createApprovalThread(toolCallResult("send_email"));
@@ -497,6 +673,325 @@ describe("LocalThreadRuntimeCore tool approvals", () => {
497
673
  });
498
674
 
499
675
  describe("LocalThreadRuntimeCore cancellation", () => {
676
+ it("settles a superseded tool-call result without aborting its replacement", async () => {
677
+ let resolveFirst!: (result: ChatModelRunResult) => void;
678
+ let resolveSecond!: (result: ChatModelRunResult) => void;
679
+ const firstResult = new Promise<ChatModelRunResult>((resolve) => {
680
+ resolveFirst = resolve;
681
+ });
682
+ const secondResult = new Promise<ChatModelRunResult>((resolve) => {
683
+ resolveSecond = resolve;
684
+ });
685
+ const runs: ChatModelRunOptions[] = [];
686
+ const thread = createThread({
687
+ run(options) {
688
+ runs.push(options);
689
+ return runs.length === 1 ? firstResult : secondResult;
690
+ },
691
+ });
692
+
693
+ const firstAppend = thread.append(userMessage("first"));
694
+ await flush();
695
+ const supersededMessageId = thread.messages.at(-1)!.id;
696
+ const secondAppend = thread.append({
697
+ ...userMessage("second"),
698
+ parentId: supersededMessageId,
699
+ });
700
+ await flush();
701
+
702
+ expect(runs).toHaveLength(2);
703
+ expect(runs[0]?.abortSignal.aborted).toBe(true);
704
+ expect(runs[1]?.abortSignal.aborted).toBe(false);
705
+
706
+ resolveFirst(toolCallResult("lookup_weather"));
707
+ await firstAppend;
708
+
709
+ expect(runs).toHaveLength(2);
710
+ expect(runs[1]?.abortSignal.aborted).toBe(false);
711
+ expect(
712
+ thread.messages.find((item) => item.id === supersededMessageId)?.status,
713
+ ).toEqual({
714
+ type: "incomplete",
715
+ reason: "cancelled",
716
+ });
717
+
718
+ resolveSecond({ content: [{ type: "text", text: "replacement" }] });
719
+ await secondAppend;
720
+ });
721
+
722
+ it("cancels a superseded approval pause", async () => {
723
+ let resolveFirst!: (result: ChatModelRunResult) => void;
724
+ let resolveSecond!: (result: ChatModelRunResult) => void;
725
+ const firstResult = new Promise<ChatModelRunResult>((resolve) => {
726
+ resolveFirst = resolve;
727
+ });
728
+ const secondResult = new Promise<ChatModelRunResult>((resolve) => {
729
+ resolveSecond = resolve;
730
+ });
731
+ const runs: ChatModelRunOptions[] = [];
732
+ const thread = createThread({
733
+ run(options) {
734
+ runs.push(options);
735
+ return runs.length === 1 ? firstResult : secondResult;
736
+ },
737
+ });
738
+
739
+ const firstAppend = thread.append(userMessage("first"));
740
+ await flush();
741
+ const supersededMessageId = thread.messages.at(-1)!.id;
742
+ const secondAppend = thread.append({
743
+ ...userMessage("second"),
744
+ parentId: supersededMessageId,
745
+ });
746
+ await flush();
747
+
748
+ resolveFirst(toolCallResult("deploy", { id: "approval-1" }));
749
+ await firstAppend;
750
+
751
+ expect(
752
+ thread.messages.find((item) => item.id === supersededMessageId)?.status,
753
+ ).toEqual({
754
+ type: "incomplete",
755
+ reason: "cancelled",
756
+ });
757
+
758
+ resolveSecond({ content: [{ type: "text", text: "replacement" }] });
759
+ await secondAppend;
760
+ });
761
+
762
+ it("ignores a stale result after addToolResult replaces the same message", async () => {
763
+ let releaseFirst!: () => void;
764
+ let resolveSecond!: (result: ChatModelRunResult) => void;
765
+ const firstTeardown = new Promise<void>((resolve) => {
766
+ releaseFirst = resolve;
767
+ });
768
+ const secondResult = new Promise<ChatModelRunResult>((resolve) => {
769
+ resolveSecond = resolve;
770
+ });
771
+ const runs: ChatModelRunOptions[] = [];
772
+ const thread = createThread({
773
+ run(options) {
774
+ runs.push(options);
775
+ if (runs.length === 1) {
776
+ return (async function* () {
777
+ yield toolCallResult("send_email");
778
+ await firstTeardown;
779
+ yield toolCallResult("stale_tool");
780
+ })();
781
+ }
782
+ return secondResult;
783
+ },
784
+ });
785
+
786
+ const firstAppend = thread.append(userMessage("send an email"));
787
+ await flush();
788
+ const messageId = thread.messages.at(-1)!.id;
789
+
790
+ thread.addToolResult({
791
+ messageId,
792
+ toolCallId: "call-send_email",
793
+ toolName: "send_email",
794
+ result: { approved: true },
795
+ isError: false,
796
+ });
797
+ await flush();
798
+
799
+ expect(runs).toHaveLength(2);
800
+ expect(runs[1]?.abortSignal.aborted).toBe(false);
801
+
802
+ releaseFirst();
803
+ await firstAppend;
804
+
805
+ const toolCall = thread.messages
806
+ .find((item) => item.id === messageId)
807
+ ?.content.find(
808
+ (part) =>
809
+ part.type === "tool-call" && part.toolCallId === "call-send_email",
810
+ );
811
+ expect(toolCall?.result).toEqual({ approved: true });
812
+ expect(runs[1]?.abortSignal.aborted).toBe(false);
813
+
814
+ resolveSecond({ content: [{ type: "text", text: "replacement" }] });
815
+ await vi.waitFor(() => {
816
+ expect(
817
+ thread.messages.find((item) => item.id === messageId)?.status.type,
818
+ ).toBe("complete");
819
+ });
820
+ });
821
+
822
+ it("ignores stale chunks after a partial tool result updates the active message", async () => {
823
+ let releaseTeardown!: () => void;
824
+ const teardown = new Promise<void>((resolve) => {
825
+ releaseTeardown = resolve;
826
+ });
827
+ const runs: ChatModelRunOptions[] = [];
828
+ const thread = createThread({
829
+ run(options) {
830
+ runs.push(options);
831
+ return (async function* () {
832
+ yield {
833
+ content: [
834
+ toolCallPart("send_email"),
835
+ toolCallPart("deploy", { id: "approval-1" }),
836
+ ],
837
+ status: {
838
+ type: "requires-action",
839
+ reason: "tool-calls",
840
+ },
841
+ } satisfies ChatModelRunResult;
842
+ await teardown;
843
+ yield { content: [{ type: "text", text: "stale" }] };
844
+ })();
845
+ },
846
+ });
847
+
848
+ const appendPromise = thread.append(userMessage("send and deploy"));
849
+ await flush();
850
+ const messageId = thread.messages.at(-1)!.id;
851
+
852
+ thread.addToolResult({
853
+ messageId,
854
+ toolCallId: "call-send_email",
855
+ toolName: "send_email",
856
+ result: { approved: true },
857
+ isError: false,
858
+ });
859
+ await flush();
860
+
861
+ expect(runs).toHaveLength(1);
862
+
863
+ releaseTeardown();
864
+ await appendPromise;
865
+
866
+ const message = thread.messages.find((item) => item.id === messageId);
867
+ const sendEmail = message?.content.find(
868
+ (part) =>
869
+ part.type === "tool-call" && part.toolCallId === "call-send_email",
870
+ );
871
+ const deploy = message?.content.find(
872
+ (part) => part.type === "tool-call" && part.toolCallId === "call-deploy",
873
+ );
874
+ expect(sendEmail?.result).toEqual({ approved: true });
875
+ expect(deploy?.approval).toEqual({ id: "approval-1" });
876
+ expect(message?.status.type).toBe("requires-action");
877
+ });
878
+
879
+ it("ignores a superseded result after its message is removed", async () => {
880
+ let resolveFirst!: (result: ChatModelRunResult) => void;
881
+ const firstResult = new Promise<ChatModelRunResult>((resolve) => {
882
+ resolveFirst = resolve;
883
+ });
884
+ let runCount = 0;
885
+ const thread = createThread({
886
+ run() {
887
+ runCount++;
888
+ if (runCount === 1) return firstResult;
889
+ return Promise.resolve({
890
+ content: [{ type: "text", text: "replacement" }],
891
+ });
892
+ },
893
+ });
894
+
895
+ const firstAppend = thread.append(userMessage("first"));
896
+ await flush();
897
+ await thread.append(userMessage("second"));
898
+ thread.reset();
899
+
900
+ resolveFirst(toolCallResult("lookup_weather"));
901
+
902
+ await expect(firstAppend).resolves.toBeUndefined();
903
+ expect(thread.messages).toHaveLength(0);
904
+ });
905
+
906
+ it("does not continue after cancelRun when a delayed tool call resolves", async () => {
907
+ let resolveFirst!: (result: ChatModelRunResult) => void;
908
+ const firstResult = new Promise<ChatModelRunResult>((resolve) => {
909
+ resolveFirst = resolve;
910
+ });
911
+ const runs: ChatModelRunOptions[] = [];
912
+ const thread = createThread({
913
+ run(options) {
914
+ runs.push(options);
915
+ if (runs.length === 1) return firstResult;
916
+ return Promise.resolve({
917
+ content: [{ type: "text", text: "unexpected continuation" }],
918
+ status: { type: "complete", reason: "stop" },
919
+ });
920
+ },
921
+ });
922
+
923
+ const appendPromise = thread.append(userMessage("first"));
924
+ await flush();
925
+
926
+ thread.cancelRun();
927
+ resolveFirst(toolCallResult("lookup_weather"));
928
+ await appendPromise;
929
+
930
+ expect(runs).toHaveLength(1);
931
+ expect(thread.messages.at(-1)?.status).toEqual({
932
+ type: "incomplete",
933
+ reason: "cancelled",
934
+ });
935
+ });
936
+
937
+ it("preserves a terminal adapter status when cancelled during teardown", async () => {
938
+ let releaseTeardown!: () => void;
939
+ const teardown = new Promise<void>((resolve) => {
940
+ releaseTeardown = resolve;
941
+ });
942
+ const thread = createThread({
943
+ async *run() {
944
+ yield {
945
+ content: [{ type: "text", text: "done" }],
946
+ status: { type: "complete", reason: "stop" },
947
+ };
948
+ await teardown;
949
+ },
950
+ });
951
+
952
+ const appendPromise = thread.append(userMessage("first"));
953
+ await flush();
954
+
955
+ expect(thread.messages.at(-1)?.status).toEqual({
956
+ type: "complete",
957
+ reason: "stop",
958
+ });
959
+
960
+ thread.cancelRun();
961
+ releaseTeardown();
962
+ await appendPromise;
963
+
964
+ expect(thread.messages.at(-1)?.status).toEqual({
965
+ type: "complete",
966
+ reason: "stop",
967
+ });
968
+ });
969
+
970
+ it("preserves an approval pause when the adapter yields after cancellation", async () => {
971
+ let releaseTeardown!: () => void;
972
+ const teardown = new Promise<void>((resolve) => {
973
+ releaseTeardown = resolve;
974
+ });
975
+ const thread = createThread({
976
+ async *run() {
977
+ yield toolCallResult("deploy", { id: "approval-1" });
978
+ await teardown;
979
+ yield { content: [{ type: "text", text: "late" }] };
980
+ },
981
+ });
982
+
983
+ const appendPromise = thread.append(userMessage("deploy"));
984
+ await flush();
985
+
986
+ expect(thread.messages.at(-1)?.status?.type).toBe("requires-action");
987
+
988
+ thread.cancelRun();
989
+ releaseTeardown();
990
+ await appendPromise;
991
+
992
+ expect(thread.messages.at(-1)?.status?.type).toBe("requires-action");
993
+ });
994
+
500
995
  it("keeps a replacement run cancellable after the previous run settles", async () => {
501
996
  let releaseFirst!: () => void;
502
997
  let releaseSecond!: () => void;
@@ -603,6 +1098,48 @@ describe("LocalThreadRuntimeCore cancellation", () => {
603
1098
  });
604
1099
 
605
1100
  describe("LocalThreadRuntimeCore suggestions", () => {
1101
+ it("ignores suggestion generation from a superseded run", async () => {
1102
+ let releaseFirst!: () => void;
1103
+ let releaseSecond!: () => void;
1104
+ const firstGate = new Promise<void>((resolve) => {
1105
+ releaseFirst = resolve;
1106
+ });
1107
+ const secondGate = new Promise<void>((resolve) => {
1108
+ releaseSecond = resolve;
1109
+ });
1110
+ let runCount = 0;
1111
+ const generate = vi.fn().mockResolvedValue([{ prompt: "follow up" }]);
1112
+ const thread = createThread(
1113
+ {
1114
+ async run() {
1115
+ runCount += 1;
1116
+ await (runCount === 1 ? firstGate : secondGate);
1117
+ return { content: [{ type: "text", text: "done" }] };
1118
+ },
1119
+ },
1120
+ { suggestion: { generate } },
1121
+ );
1122
+
1123
+ const firstAppend = thread.append(userMessage("first"));
1124
+ await flush();
1125
+ const secondAppend = thread.append(userMessage("second"));
1126
+ await flush();
1127
+
1128
+ releaseFirst();
1129
+ await firstAppend;
1130
+ await flush();
1131
+
1132
+ expect(generate).not.toHaveBeenCalled();
1133
+ expect(thread.suggestions).toEqual([]);
1134
+
1135
+ releaseSecond();
1136
+ await secondAppend;
1137
+ await flush();
1138
+
1139
+ expect(generate).toHaveBeenCalledOnce();
1140
+ expect(thread.suggestions).toEqual([{ prompt: "follow up" }]);
1141
+ });
1142
+
606
1143
  it("cancelRun aborts pending suggestion generation", async () => {
607
1144
  const generate = vi.fn().mockImplementation(
608
1145
  ({ signal }: { signal?: AbortSignal }) =>
@@ -656,8 +1193,8 @@ describe("LocalThreadRuntimeCore suggestions", () => {
656
1193
  });
657
1194
 
658
1195
  it("resolves append before suggestion generation completes", async () => {
659
- let resolveSuggestions!: (value: readonly { prompt: string }[]) => void;
660
- const suggestionsDeferred = new Promise<readonly { prompt: string }[]>(
1196
+ let resolveSuggestions!: (value: readonly ThreadSuggestion[]) => void;
1197
+ const suggestionsDeferred = new Promise<readonly ThreadSuggestion[]>(
661
1198
  (resolve) => {
662
1199
  resolveSuggestions = resolve;
663
1200
  },
@@ -681,9 +1218,15 @@ describe("LocalThreadRuntimeCore suggestions", () => {
681
1218
  expect(generate).toHaveBeenCalledTimes(1);
682
1219
  expect(thread.suggestions).toEqual([]);
683
1220
 
684
- resolveSuggestions([{ prompt: "follow up" }]);
1221
+ resolveSuggestions([
1222
+ { title: "Weather", label: "in SF", prompt: "What's the weather?" },
1223
+ { prompt: "follow up" },
1224
+ ]);
685
1225
  await new Promise((r) => setTimeout(r, 0));
686
- expect(thread.suggestions).toEqual([{ prompt: "follow up" }]);
1226
+ expect(thread.suggestions).toEqual([
1227
+ { title: "Weather", label: "in SF", prompt: "What's the weather?" },
1228
+ { prompt: "follow up" },
1229
+ ]);
687
1230
  });
688
1231
  });
689
1232
 
@@ -1313,4 +1856,149 @@ describe("LocalThreadRuntimeCore runs", () => {
1313
1856
  expect(thread.capabilities.attachments).toBe(false);
1314
1857
  expect(thread.capabilities.feedback).toBe(false);
1315
1858
  });
1859
+
1860
+ it("loads history when the adapter arrives after the first load", async () => {
1861
+ const adapter: ChatModelAdapter = {
1862
+ run: async () => ({ content: [] }),
1863
+ };
1864
+ const thread = createPlainThread(adapter);
1865
+ const load = vi.fn(async () => ({
1866
+ headId: "restored",
1867
+ messages: [
1868
+ {
1869
+ parentId: null,
1870
+ message: {
1871
+ id: "restored",
1872
+ role: "user" as const,
1873
+ content: [{ type: "text" as const, text: "hello" }],
1874
+ createdAt: new Date(0),
1875
+ metadata: { custom: {} },
1876
+ },
1877
+ },
1878
+ ],
1879
+ }));
1880
+
1881
+ thread.__internal_load();
1882
+ expect(load).not.toHaveBeenCalled();
1883
+ expect(thread.messages).toHaveLength(0);
1884
+
1885
+ thread.__internal_setOptions({
1886
+ adapters: {
1887
+ chatModel: adapter,
1888
+ history: { load, append: async () => {} },
1889
+ },
1890
+ });
1891
+ await flush();
1892
+
1893
+ expect(load).toHaveBeenCalledOnce();
1894
+ expect(thread.messages.map((message) => message.id)).toEqual(["restored"]);
1895
+ });
1896
+
1897
+ it("does not load late history over a thread that already has messages", async () => {
1898
+ const adapter: ChatModelAdapter = {
1899
+ run: async () => ({ content: [] }),
1900
+ };
1901
+ const thread = createPlainThread(adapter);
1902
+ const load = vi.fn(async () => ({
1903
+ headId: "restored",
1904
+ messages: [
1905
+ {
1906
+ parentId: null,
1907
+ message: {
1908
+ id: "restored",
1909
+ role: "user" as const,
1910
+ content: [{ type: "text" as const, text: "hello" }],
1911
+ createdAt: new Date(0),
1912
+ metadata: { custom: {} },
1913
+ },
1914
+ },
1915
+ ],
1916
+ }));
1917
+
1918
+ thread.__internal_load();
1919
+ await thread.append({ ...userMessage("typed"), startRun: false });
1920
+ expect(thread.messages).toHaveLength(1);
1921
+
1922
+ thread.__internal_setOptions({
1923
+ adapters: {
1924
+ chatModel: adapter,
1925
+ history: { load, append: async () => {} },
1926
+ },
1927
+ });
1928
+ await flush();
1929
+
1930
+ expect(load).not.toHaveBeenCalled();
1931
+ expect(thread.messages.map((message) => message.content)).toEqual([
1932
+ [{ type: "text", text: "typed" }],
1933
+ ]);
1934
+ });
1935
+
1936
+ it("logs a rejected late history load", async () => {
1937
+ const adapter: ChatModelAdapter = {
1938
+ run: async () => ({ content: [] }),
1939
+ };
1940
+ const thread = createPlainThread(adapter);
1941
+ const error = new Error("history unavailable");
1942
+ const consoleError = vi
1943
+ .spyOn(console, "error")
1944
+ .mockImplementation(() => {});
1945
+
1946
+ thread.__internal_load();
1947
+ thread.__internal_setOptions({
1948
+ adapters: {
1949
+ chatModel: adapter,
1950
+ history: {
1951
+ load: async () => {
1952
+ throw error;
1953
+ },
1954
+ append: async () => {},
1955
+ },
1956
+ },
1957
+ });
1958
+ await flush();
1959
+
1960
+ expect(consoleError).toHaveBeenCalledWith(
1961
+ "[assistant-ui] local thread history load failed:",
1962
+ error,
1963
+ );
1964
+ });
1965
+ });
1966
+
1967
+ describe("LocalRuntimeCore composer.canCancel", () => {
1968
+ it("is true after append when the thread was created with initial messages", async () => {
1969
+ const core = new LocalRuntimeCore(
1970
+ {
1971
+ adapters: {
1972
+ chatModel: {
1973
+ async *run({ abortSignal }) {
1974
+ await new Promise<void>((resolve) => {
1975
+ if (abortSignal.aborted) {
1976
+ resolve();
1977
+ return;
1978
+ }
1979
+ abortSignal.addEventListener("abort", () => resolve(), {
1980
+ once: true,
1981
+ });
1982
+ });
1983
+ },
1984
+ },
1985
+ },
1986
+ },
1987
+ [
1988
+ {
1989
+ role: "assistant",
1990
+ content: [{ type: "text", text: "Hello" }],
1991
+ status: { type: "complete", reason: "stop" },
1992
+ },
1993
+ ],
1994
+ );
1995
+ const thread = core.threads.getMainThreadRuntimeCore();
1996
+ expect(thread.composer.canCancel).toBe(false);
1997
+
1998
+ void thread.append(userMessage("Run"));
1999
+ await flush();
2000
+
2001
+ expect(thread.composer.canCancel).toBe(true);
2002
+ thread.cancelRun();
2003
+ });
1316
2004
  });