@assistant-ui/core 0.3.13 → 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 (396) 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 +2 -2
  10. package/dist/index.js.map +1 -1
  11. package/dist/internal.d.ts +1 -1
  12. package/dist/internal.js +6 -6
  13. package/dist/model-context/frame/host.d.ts.map +1 -1
  14. package/dist/model-context/frame/host.js +2 -1
  15. package/dist/model-context/frame/host.js.map +1 -1
  16. package/dist/model-context/registry.d.ts.map +1 -1
  17. package/dist/model-context/registry.js +16 -4
  18. package/dist/model-context/registry.js.map +1 -1
  19. package/dist/react/RuntimeAdapter.d.ts +8 -1
  20. package/dist/react/RuntimeAdapter.d.ts.map +1 -1
  21. package/dist/react/RuntimeAdapter.js +23 -18
  22. package/dist/react/RuntimeAdapter.js.map +1 -1
  23. package/dist/react/adapters/LocalStorageThreadListAdapter.d.ts.map +1 -1
  24. package/dist/react/adapters/LocalStorageThreadListAdapter.js +28 -12
  25. package/dist/react/adapters/LocalStorageThreadListAdapter.js.map +1 -1
  26. package/dist/react/client/AdaptedRemoteThread.d.ts +11 -0
  27. package/dist/react/client/AdaptedRemoteThread.d.ts.map +1 -0
  28. package/dist/react/client/AdaptedRemoteThread.js +27 -0
  29. package/dist/react/client/AdaptedRemoteThread.js.map +1 -0
  30. package/dist/react/client/InMemoryThreadList.d.ts +19 -0
  31. package/dist/react/client/InMemoryThreadList.d.ts.map +1 -0
  32. package/dist/react/client/InMemoryThreadList.js +418 -0
  33. package/dist/react/client/InMemoryThreadList.js.map +1 -0
  34. package/dist/react/client/Interactables.d.ts.map +1 -1
  35. package/dist/react/client/Interactables.js +159 -102
  36. package/dist/react/client/Interactables.js.map +1 -1
  37. package/dist/react/client/RemoteThreadList.d.ts +34 -0
  38. package/dist/react/client/RemoteThreadList.d.ts.map +1 -0
  39. package/dist/react/client/RemoteThreadList.js +826 -0
  40. package/dist/react/client/RemoteThreadList.js.map +1 -0
  41. package/dist/react/client/Tools.d.ts.map +1 -1
  42. package/dist/react/client/Tools.js +38 -27
  43. package/dist/react/client/Tools.js.map +1 -1
  44. package/dist/react/index.d.ts +7 -2
  45. package/dist/react/index.js +5 -2
  46. package/dist/react/interactables-legacy/Interactables.d.ts.map +1 -1
  47. package/dist/react/interactables-legacy/Interactables.js +145 -110
  48. package/dist/react/interactables-legacy/Interactables.js.map +1 -1
  49. package/dist/react/primitive-hooks/useActionBarCopy.d.ts.map +1 -1
  50. package/dist/react/primitive-hooks/useActionBarCopy.js +13 -18
  51. package/dist/react/primitive-hooks/useActionBarCopy.js.map +1 -1
  52. package/dist/react/primitive-hooks/useActionBarEdit.d.ts.map +1 -1
  53. package/dist/react/primitive-hooks/useActionBarEdit.js +2 -4
  54. package/dist/react/primitive-hooks/useActionBarEdit.js.map +1 -1
  55. package/dist/react/primitive-hooks/useActionBarReload.d.ts.map +1 -1
  56. package/dist/react/primitive-hooks/useActionBarReload.js +2 -4
  57. package/dist/react/primitive-hooks/useActionBarReload.js.map +1 -1
  58. package/dist/react/primitive-hooks/useBranchPickerNext.d.ts.map +1 -1
  59. package/dist/react/primitive-hooks/useBranchPickerNext.js +2 -6
  60. package/dist/react/primitive-hooks/useBranchPickerNext.js.map +1 -1
  61. package/dist/react/primitive-hooks/useBranchPickerPrevious.d.ts.map +1 -1
  62. package/dist/react/primitive-hooks/useBranchPickerPrevious.js +2 -6
  63. package/dist/react/primitive-hooks/useBranchPickerPrevious.js.map +1 -1
  64. package/dist/react/primitive-hooks/useComposerCancel.d.ts.map +1 -1
  65. package/dist/react/primitive-hooks/useComposerCancel.js +2 -4
  66. package/dist/react/primitive-hooks/useComposerCancel.js.map +1 -1
  67. package/dist/react/primitive-hooks/useComposerSend.d.ts.map +1 -1
  68. package/dist/react/primitive-hooks/useComposerSend.js +2 -4
  69. package/dist/react/primitive-hooks/useComposerSend.js.map +1 -1
  70. package/dist/react/primitive-hooks/useSuggestionTrigger.d.ts.map +1 -1
  71. package/dist/react/primitive-hooks/useSuggestionTrigger.js +6 -2
  72. package/dist/react/primitive-hooks/useSuggestionTrigger.js.map +1 -1
  73. package/dist/react/primitives/message/MessageParts.d.ts +13 -0
  74. package/dist/react/primitives/message/MessageParts.d.ts.map +1 -1
  75. package/dist/react/primitives/message/MessageParts.js.map +1 -1
  76. package/dist/react/primitives/thread/ThreadMessages.d.ts.map +1 -1
  77. package/dist/react/primitives/thread/ThreadMessages.js +1 -1
  78. package/dist/react/primitives/thread/ThreadMessages.js.map +1 -1
  79. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.d.ts +22 -10
  80. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.d.ts.map +1 -1
  81. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js +109 -71
  82. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js.map +1 -1
  83. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts +8 -2
  84. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts.map +1 -1
  85. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js +40 -13
  86. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js.map +1 -1
  87. package/dist/react/runtimes/RemoteThreadResource.d.ts +30 -0
  88. package/dist/react/runtimes/RemoteThreadResource.d.ts.map +1 -0
  89. package/dist/react/runtimes/RemoteThreadResource.js +134 -0
  90. package/dist/react/runtimes/RemoteThreadResource.js.map +1 -0
  91. package/dist/react/runtimes/RuntimeAdapterProvider.d.ts +3 -10
  92. package/dist/react/runtimes/RuntimeAdapterProvider.d.ts.map +1 -1
  93. package/dist/react/runtimes/RuntimeAdapterProvider.js +3 -6
  94. package/dist/react/runtimes/RuntimeAdapterProvider.js.map +1 -1
  95. package/dist/react/runtimes/assistant-transport/commandQueue.d.ts +17 -0
  96. package/dist/react/runtimes/assistant-transport/commandQueue.d.ts.map +1 -0
  97. package/dist/react/runtimes/assistant-transport/commandQueue.js +55 -0
  98. package/dist/react/runtimes/assistant-transport/commandQueue.js.map +1 -0
  99. package/dist/react/runtimes/assistant-transport/replayBoundaryStream.d.ts +11 -0
  100. package/dist/react/runtimes/assistant-transport/replayBoundaryStream.d.ts.map +1 -0
  101. package/dist/react/runtimes/assistant-transport/replayBoundaryStream.js +165 -0
  102. package/dist/react/runtimes/assistant-transport/replayBoundaryStream.js.map +1 -0
  103. package/dist/react/runtimes/assistant-transport/runManager.d.ts +15 -0
  104. package/dist/react/runtimes/assistant-transport/runManager.d.ts.map +1 -0
  105. package/dist/react/runtimes/assistant-transport/runManager.js +86 -0
  106. package/dist/react/runtimes/assistant-transport/runManager.js.map +1 -0
  107. package/dist/react/runtimes/assistant-transport/types.d.ts +130 -0
  108. package/dist/react/runtimes/assistant-transport/types.d.ts.map +1 -0
  109. package/dist/react/runtimes/assistant-transport/types.js +0 -0
  110. package/dist/react/runtimes/assistant-transport/useAssistantTransportRuntime.d.ts +14 -0
  111. package/dist/react/runtimes/assistant-transport/useAssistantTransportRuntime.d.ts.map +1 -0
  112. package/dist/react/runtimes/assistant-transport/useAssistantTransportRuntime.js +296 -0
  113. package/dist/react/runtimes/assistant-transport/useAssistantTransportRuntime.js.map +1 -0
  114. package/dist/react/runtimes/assistant-transport/useConvertedState.d.ts +7 -0
  115. package/dist/react/runtimes/assistant-transport/useConvertedState.d.ts.map +1 -0
  116. package/dist/react/runtimes/assistant-transport/useConvertedState.js +25 -0
  117. package/dist/react/runtimes/assistant-transport/useConvertedState.js.map +1 -0
  118. package/dist/react/runtimes/assistant-transport/useLatestRef.d.ts +7 -0
  119. package/dist/react/runtimes/assistant-transport/useLatestRef.d.ts.map +1 -0
  120. package/dist/react/runtimes/assistant-transport/useLatestRef.js +27 -0
  121. package/dist/react/runtimes/assistant-transport/useLatestRef.js.map +1 -0
  122. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.d.ts.map +1 -1
  123. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js +39 -13
  124. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js.map +1 -1
  125. package/dist/react/runtimes/cloud/CloudFileAttachmentAdapter.d.ts +2 -1
  126. package/dist/react/runtimes/cloud/CloudFileAttachmentAdapter.d.ts.map +1 -1
  127. package/dist/react/runtimes/cloud/CloudFileAttachmentAdapter.js +3 -3
  128. package/dist/react/runtimes/cloud/CloudFileAttachmentAdapter.js.map +1 -1
  129. package/dist/react/runtimes/cloud/auiV0.d.ts +4 -0
  130. package/dist/react/runtimes/cloud/auiV0.d.ts.map +1 -1
  131. package/dist/react/runtimes/cloud/auiV0.js +7 -0
  132. package/dist/react/runtimes/cloud/auiV0.js.map +1 -1
  133. package/dist/react/runtimes/cloud/useCloudThreadListAdapter.d.ts.map +1 -1
  134. package/dist/react/runtimes/cloud/useCloudThreadListAdapter.js +22 -12
  135. package/dist/react/runtimes/cloud/useCloudThreadListAdapter.js.map +1 -1
  136. package/dist/react/runtimes/cloud/useCloudThreadListRuntime.d.ts +16 -0
  137. package/dist/react/runtimes/cloud/useCloudThreadListRuntime.d.ts.map +1 -0
  138. package/dist/react/runtimes/cloud/useCloudThreadListRuntime.js +36 -0
  139. package/dist/react/runtimes/cloud/useCloudThreadListRuntime.js.map +1 -0
  140. package/dist/react/runtimes/external-message-converter.js +14 -14
  141. package/dist/react/runtimes/external-message-converter.js.map +1 -1
  142. package/dist/react/runtimes/useExternalStoreRuntime.d.ts.map +1 -1
  143. package/dist/react/runtimes/useExternalStoreRuntime.js +44 -28
  144. package/dist/react/runtimes/useExternalStoreRuntime.js.map +1 -1
  145. package/dist/react/runtimes/useExternalStoreSharedOptions.js.map +1 -1
  146. package/dist/react/runtimes/useLocalRuntime.js +1 -1
  147. package/dist/react/runtimes/useRuntimeAdapters.d.ts +9 -0
  148. package/dist/react/runtimes/useRuntimeAdapters.d.ts.map +1 -0
  149. package/dist/react/runtimes/useRuntimeAdapters.js +20 -0
  150. package/dist/react/runtimes/useRuntimeAdapters.js.map +1 -0
  151. package/dist/runtime/api/composer-runtime.d.ts +5 -0
  152. package/dist/runtime/api/composer-runtime.d.ts.map +1 -1
  153. package/dist/runtime/api/composer-runtime.js.map +1 -1
  154. package/dist/runtime/api/message-runtime.js +1 -1
  155. package/dist/runtime/api/thread-list-runtime.d.ts +1 -0
  156. package/dist/runtime/api/thread-list-runtime.d.ts.map +1 -1
  157. package/dist/runtime/api/thread-list-runtime.js +4 -2
  158. package/dist/runtime/api/thread-list-runtime.js.map +1 -1
  159. package/dist/runtime/api/thread-runtime.d.ts +12 -2
  160. package/dist/runtime/api/thread-runtime.d.ts.map +1 -1
  161. package/dist/runtime/api/thread-runtime.js +8 -2
  162. package/dist/runtime/api/thread-runtime.js.map +1 -1
  163. package/dist/runtime/base/base-composer-runtime-core.d.ts.map +1 -1
  164. package/dist/runtime/base/base-composer-runtime-core.js +8 -7
  165. package/dist/runtime/base/base-composer-runtime-core.js.map +1 -1
  166. package/dist/runtime/base/base-thread-runtime-core.d.ts +1 -0
  167. package/dist/runtime/base/base-thread-runtime-core.d.ts.map +1 -1
  168. package/dist/runtime/base/base-thread-runtime-core.js +3 -3
  169. package/dist/runtime/base/base-thread-runtime-core.js.map +1 -1
  170. package/dist/runtime/base/default-thread-composer-runtime-core.d.ts +0 -1
  171. package/dist/runtime/base/default-thread-composer-runtime-core.d.ts.map +1 -1
  172. package/dist/runtime/base/default-thread-composer-runtime-core.js +10 -4
  173. package/dist/runtime/base/default-thread-composer-runtime-core.js.map +1 -1
  174. package/dist/runtime/interfaces/thread-runtime-core.d.ts +6 -0
  175. package/dist/runtime/interfaces/thread-runtime-core.d.ts.map +1 -1
  176. package/dist/runtime/internal.js +5 -5
  177. package/dist/runtime/queue/message-queue.d.ts.map +1 -1
  178. package/dist/runtime/queue/message-queue.js +2 -1
  179. package/dist/runtime/queue/message-queue.js.map +1 -1
  180. package/dist/runtime/utils/thread-runtime-lifecycle.d.ts +8 -0
  181. package/dist/runtime/utils/thread-runtime-lifecycle.d.ts.map +1 -0
  182. package/dist/runtime/utils/thread-runtime-lifecycle.js +11 -0
  183. package/dist/runtime/utils/thread-runtime-lifecycle.js.map +1 -0
  184. package/dist/runtimes/external-store/external-store-shared-options.js.map +1 -1
  185. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.d.ts.map +1 -1
  186. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.js +4 -1
  187. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.js.map +1 -1
  188. package/dist/runtimes/external-store/external-store-thread-runtime-core.d.ts +8 -0
  189. package/dist/runtimes/external-store/external-store-thread-runtime-core.d.ts.map +1 -1
  190. package/dist/runtimes/external-store/external-store-thread-runtime-core.js +35 -13
  191. package/dist/runtimes/external-store/external-store-thread-runtime-core.js.map +1 -1
  192. package/dist/runtimes/internal.d.ts +1 -1
  193. package/dist/runtimes/local/local-thread-runtime-core.d.ts +3 -0
  194. package/dist/runtimes/local/local-thread-runtime-core.d.ts.map +1 -1
  195. package/dist/runtimes/local/local-thread-runtime-core.js +53 -13
  196. package/dist/runtimes/local/local-thread-runtime-core.js.map +1 -1
  197. package/dist/runtimes/readonly/ReadonlyThreadRuntimeCore.d.ts +1 -0
  198. package/dist/runtimes/readonly/ReadonlyThreadRuntimeCore.d.ts.map +1 -1
  199. package/dist/runtimes/readonly/ReadonlyThreadRuntimeCore.js +3 -0
  200. package/dist/runtimes/readonly/ReadonlyThreadRuntimeCore.js.map +1 -1
  201. package/dist/runtimes/remote-thread-list/empty-thread-core.js +3 -0
  202. package/dist/runtimes/remote-thread-list/empty-thread-core.js.map +1 -1
  203. package/dist/runtimes/remote-thread-list/types.d.ts +29 -1
  204. package/dist/runtimes/remote-thread-list/types.d.ts.map +1 -1
  205. package/dist/runtimes/tool-invocations/ToolInvocationTracker.d.ts +1 -12
  206. package/dist/runtimes/tool-invocations/ToolInvocationTracker.d.ts.map +1 -1
  207. package/dist/runtimes/tool-invocations/ToolInvocationTracker.js +67 -38
  208. package/dist/runtimes/tool-invocations/ToolInvocationTracker.js.map +1 -1
  209. package/dist/store/clients/suggestions.js +2 -2
  210. package/dist/store/clients/suggestions.js.map +1 -1
  211. package/dist/store/clients/thread-selection-events.d.ts +10 -0
  212. package/dist/store/clients/thread-selection-events.d.ts.map +1 -0
  213. package/dist/store/clients/thread-selection-events.js +40 -0
  214. package/dist/store/clients/thread-selection-events.js.map +1 -0
  215. package/dist/store/env.d.ts +5 -0
  216. package/dist/store/env.d.ts.map +1 -0
  217. package/dist/store/env.js +12 -0
  218. package/dist/store/env.js.map +1 -0
  219. package/dist/store/index.d.ts +5 -3
  220. package/dist/store/index.js +4 -2
  221. package/dist/store/internal.d.ts +4 -1
  222. package/dist/store/internal.js +4 -1
  223. package/dist/store/primitive-predicates.d.ts +19 -0
  224. package/dist/store/primitive-predicates.d.ts.map +1 -0
  225. package/dist/store/primitive-predicates.js +19 -0
  226. package/dist/store/primitive-predicates.js.map +1 -0
  227. package/dist/store/runtime-clients/attachment-runtime-client.d.ts.map +1 -1
  228. package/dist/store/runtime-clients/attachment-runtime-client.js +18 -11
  229. package/dist/store/runtime-clients/attachment-runtime-client.js.map +1 -1
  230. package/dist/store/runtime-clients/handle-runtime-action.d.ts +5 -0
  231. package/dist/store/runtime-clients/handle-runtime-action.d.ts.map +1 -0
  232. package/dist/store/runtime-clients/handle-runtime-action.js +12 -0
  233. package/dist/store/runtime-clients/handle-runtime-action.js.map +1 -0
  234. package/dist/store/runtime-clients/handle-thread-list-action.d.ts +5 -0
  235. package/dist/store/runtime-clients/handle-thread-list-action.d.ts.map +1 -0
  236. package/dist/store/runtime-clients/handle-thread-list-action.js +7 -0
  237. package/dist/store/runtime-clients/handle-thread-list-action.js.map +1 -0
  238. package/dist/store/runtime-clients/thread-list-item-runtime-client.d.ts.map +1 -1
  239. package/dist/store/runtime-clients/thread-list-item-runtime-client.js +62 -29
  240. package/dist/store/runtime-clients/thread-list-item-runtime-client.js.map +1 -1
  241. package/dist/store/runtime-clients/thread-list-runtime-client.d.ts.map +1 -1
  242. package/dist/store/runtime-clients/thread-list-runtime-client.js +5 -6
  243. package/dist/store/runtime-clients/thread-list-runtime-client.js.map +1 -1
  244. package/dist/store/scopes/composer.d.ts +9 -4
  245. package/dist/store/scopes/composer.d.ts.map +1 -1
  246. package/dist/store/scopes/thread-list-item.d.ts +7 -6
  247. package/dist/store/scopes/thread-list-item.d.ts.map +1 -1
  248. package/dist/store/scopes/thread.d.ts +8 -7
  249. package/dist/store/scopes/thread.d.ts.map +1 -1
  250. package/dist/store/scopes/threads.d.ts +17 -1
  251. package/dist/store/scopes/threads.d.ts.map +1 -1
  252. package/dist/subscribable/subscribable.d.ts +2 -1
  253. package/dist/subscribable/subscribable.d.ts.map +1 -1
  254. package/dist/subscribable/subscribable.js +23 -18
  255. package/dist/subscribable/subscribable.js.map +1 -1
  256. package/dist/utils/composite-context-provider.d.ts +1 -0
  257. package/dist/utils/composite-context-provider.d.ts.map +1 -1
  258. package/dist/utils/composite-context-provider.js +25 -9
  259. package/dist/utils/composite-context-provider.js.map +1 -1
  260. package/dist/utils/notify-event-listeners.d.ts +2 -2
  261. package/dist/utils/notify-event-listeners.d.ts.map +1 -1
  262. package/dist/utils/notify-event-listeners.js +2 -2
  263. package/dist/utils/notify-event-listeners.js.map +1 -1
  264. package/package.json +7 -7
  265. package/src/adapters/speech.test.ts +118 -1
  266. package/src/adapters/speech.ts +21 -22
  267. package/src/adapters/thread-history.ts +2 -0
  268. package/src/adapters/voice.test.ts +69 -0
  269. package/src/adapters/voice.ts +27 -27
  270. package/src/index.ts +1 -0
  271. package/src/model-context/frame/host.test.ts +23 -0
  272. package/src/model-context/frame/host.ts +2 -1
  273. package/src/model-context/registry.test.ts +131 -0
  274. package/src/model-context/registry.ts +16 -6
  275. package/src/react/RuntimeAdapter.test.ts +2 -0
  276. package/src/react/RuntimeAdapter.ts +20 -8
  277. package/src/react/adapters/LocalStorageThreadListAdapter.tsx +34 -17
  278. package/src/react/client/AdaptedRemoteThread.ts +41 -0
  279. package/src/react/client/InMemoryThreadList.test.tsx +148 -0
  280. package/src/react/client/InMemoryThreadList.ts +290 -0
  281. package/src/react/client/Interactables.test.ts +326 -2
  282. package/src/react/client/Interactables.ts +227 -110
  283. package/src/react/client/RemoteThreadList.test.ts +1026 -0
  284. package/src/react/client/RemoteThreadList.ts +1067 -0
  285. package/src/react/client/Tools.ts +36 -29
  286. package/src/react/index.ts +13 -0
  287. package/src/react/interactables-legacy/Interactables.test.ts +317 -0
  288. package/src/react/interactables-legacy/Interactables.ts +204 -128
  289. package/src/react/primitive-hooks/useActionBarCopy.ts +2 -7
  290. package/src/react/primitive-hooks/useActionBarEdit.ts +2 -1
  291. package/src/react/primitive-hooks/useActionBarReload.ts +2 -6
  292. package/src/react/primitive-hooks/useBranchPickerNext.ts +2 -7
  293. package/src/react/primitive-hooks/useBranchPickerPrevious.ts +2 -7
  294. package/src/react/primitive-hooks/useComposerCancel.ts +2 -1
  295. package/src/react/primitive-hooks/useComposerSend.ts +2 -5
  296. package/src/react/primitive-hooks/useSuggestionTrigger.test.ts +11 -0
  297. package/src/react/primitive-hooks/useSuggestionTrigger.ts +4 -5
  298. package/src/react/primitives/message/MessageParts.tsx +0 -1
  299. package/src/react/primitives/thread/ThreadMessages.tsx +2 -1
  300. package/src/react/runtimes/RemoteThreadListHookInstanceManager.retry.test.tsx +116 -0
  301. package/src/react/runtimes/RemoteThreadListHookInstanceManager.running.test.tsx +1 -1
  302. package/src/react/runtimes/RemoteThreadListHookInstanceManager.test.tsx +57 -6
  303. package/src/react/runtimes/RemoteThreadListHookInstanceManager.title.test.tsx +272 -0
  304. package/src/react/runtimes/RemoteThreadListHookInstanceManager.tsx +159 -124
  305. package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.tsx +59 -20
  306. package/src/react/runtimes/RemoteThreadResource.test.ts +141 -0
  307. package/src/react/runtimes/RemoteThreadResource.ts +231 -0
  308. package/src/react/runtimes/RuntimeAdapterProvider.tsx +8 -20
  309. package/src/react/runtimes/assistant-transport/commandQueue.ts +68 -0
  310. package/src/react/runtimes/assistant-transport/replayBoundaryStream.test.ts +455 -0
  311. package/src/react/runtimes/assistant-transport/replayBoundaryStream.ts +172 -0
  312. package/src/react/runtimes/assistant-transport/runManager.ts +124 -0
  313. package/src/react/runtimes/assistant-transport/transport-delivery.test.tsx +79 -0
  314. package/src/react/runtimes/assistant-transport/transport-parentid.test.tsx +97 -0
  315. package/src/react/runtimes/assistant-transport/transport-scheduling.test.ts +414 -0
  316. package/src/react/runtimes/assistant-transport/types.ts +170 -0
  317. package/src/react/runtimes/assistant-transport/useAssistantTransport.spec.md +141 -0
  318. package/src/react/runtimes/assistant-transport/useAssistantTransportRuntime.test.tsx +625 -0
  319. package/src/react/runtimes/assistant-transport/useAssistantTransportRuntime.ts +481 -0
  320. package/src/react/runtimes/assistant-transport/useConvertedState.ts +20 -0
  321. package/src/react/runtimes/assistant-transport/useLatestRef.ts +9 -0
  322. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.test.tsx +131 -6
  323. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.ts +61 -15
  324. package/src/react/runtimes/cloud/CloudFileAttachmentAdapter.ts +6 -4
  325. package/src/react/runtimes/cloud/auiV0.ts +17 -1
  326. package/src/react/runtimes/cloud/useCloudThreadListAdapter.tsx +36 -21
  327. package/src/react/runtimes/cloud/useCloudThreadListRuntime.test.tsx +56 -0
  328. package/src/react/runtimes/cloud/useCloudThreadListRuntime.ts +31 -0
  329. package/src/react/runtimes/external-message-converter.test.ts +143 -0
  330. package/src/react/runtimes/external-message-converter.ts +41 -41
  331. package/src/react/runtimes/remote-thread-hook-on-tap.test.ts +58 -0
  332. package/src/react/runtimes/useExternalStoreRuntime.lifecycle.test.tsx +83 -0
  333. package/src/react/runtimes/useExternalStoreRuntime.suggestions.test.tsx +13 -3
  334. package/src/react/runtimes/useExternalStoreRuntime.ts +7 -0
  335. package/src/react/runtimes/useExternalStoreSharedOptions.ts +3 -1
  336. package/src/react/runtimes/useLocalRuntime.test.tsx +130 -4
  337. package/src/react/runtimes/useRuntimeAdapters.test.tsx +62 -0
  338. package/src/react/runtimes/useRuntimeAdapters.ts +28 -0
  339. package/src/runtime/api/composer-runtime.ts +5 -0
  340. package/src/runtime/api/thread-list-runtime.ts +3 -1
  341. package/src/runtime/api/thread-runtime.ts +20 -2
  342. package/src/runtime/base/base-composer-runtime-core.ts +6 -5
  343. package/src/runtime/base/base-thread-runtime-core.test.ts +191 -0
  344. package/src/runtime/base/base-thread-runtime-core.ts +11 -2
  345. package/src/runtime/base/default-thread-composer-runtime-core.ts +12 -4
  346. package/src/runtime/interfaces/thread-runtime-core.ts +6 -0
  347. package/src/runtime/queue/message-queue.ts +2 -1
  348. package/src/runtime/utils/thread-runtime-lifecycle.ts +18 -0
  349. package/src/runtimes/external-store/external-store-shared-options.ts +3 -1
  350. package/src/runtimes/external-store/external-store-thread-list-runtime-core.ts +4 -1
  351. package/src/runtimes/external-store/external-store-thread-runtime-core.ts +99 -34
  352. package/src/runtimes/local/local-thread-runtime-core.test.ts +373 -4
  353. package/src/runtimes/local/local-thread-runtime-core.ts +79 -14
  354. package/src/runtimes/readonly/ReadonlyThreadRuntimeCore.ts +4 -0
  355. package/src/runtimes/remote-thread-list/empty-thread-core.ts +4 -0
  356. package/src/runtimes/remote-thread-list/types.ts +30 -0
  357. package/src/runtimes/tool-invocations/EDGE_CASES.md +17 -6
  358. package/src/runtimes/tool-invocations/ToolInvocationTracker.test.ts +243 -0
  359. package/src/runtimes/tool-invocations/ToolInvocationTracker.ts +127 -61
  360. package/src/store/clients/model-context-client.test.ts +16 -8
  361. package/src/store/clients/suggestions.ts +2 -2
  362. package/src/store/clients/thread-selection-events.ts +21 -0
  363. package/src/store/env.ts +11 -0
  364. package/src/store/index.ts +14 -1
  365. package/src/store/internal.ts +13 -0
  366. package/src/store/primitive-predicates.test.ts +184 -0
  367. package/src/store/primitive-predicates.ts +43 -0
  368. package/src/store/runtime-clients/attachment-runtime-client.test.ts +41 -0
  369. package/src/store/runtime-clients/attachment-runtime-client.ts +2 -1
  370. package/src/store/runtime-clients/handle-runtime-action.ts +11 -0
  371. package/src/store/runtime-clients/handle-thread-list-action.test.ts +69 -0
  372. package/src/store/runtime-clients/handle-thread-list-action.ts +6 -0
  373. package/src/store/runtime-clients/thread-list-item-runtime-client.ts +15 -7
  374. package/src/store/runtime-clients/thread-list-runtime-client.ts +9 -6
  375. package/src/store/scopes/composer.ts +9 -4
  376. package/src/store/scopes/thread-list-item.ts +7 -6
  377. package/src/store/scopes/thread.ts +8 -7
  378. package/src/store/scopes/threads.ts +14 -0
  379. package/src/subscribable/subscribable.test.ts +77 -1
  380. package/src/subscribable/subscribable.ts +38 -23
  381. package/src/tests/RemoteThreadListThreadListRuntimeCore-switchToThread-unarchive.test.ts +25 -0
  382. package/src/tests/auiV0Encode.test.ts +66 -0
  383. package/src/tests/base-composer-runtime-core-send.test.ts +30 -0
  384. package/src/tests/base-composer-runtime-core.test.ts +28 -0
  385. package/src/tests/composer-can-cancel.test.ts +101 -0
  386. package/src/tests/external-store-thread-list-runtime-core.test.ts +101 -0
  387. package/src/tests/external-store-thread-runtime-core-adapter.test.ts +191 -1
  388. package/src/tests/external-store-thread-runtime-core.test.ts +124 -0
  389. package/src/tests/interactables-toolui-migration.test.tsx +66 -0
  390. package/src/tests/message-queue.test.ts +28 -0
  391. package/src/tests/session-reset-notification.test.ts +145 -0
  392. package/src/tests/thread-switch-events.test.tsx +99 -1
  393. package/src/tests/tools-scope-migration.test.tsx +58 -0
  394. package/src/utils/composite-context-provider.test.ts +139 -0
  395. package/src/utils/composite-context-provider.ts +26 -8
  396. package/src/utils/notify-event-listeners.ts +10 -5
@@ -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"));
@@ -603,6 +779,48 @@ describe("LocalThreadRuntimeCore cancellation", () => {
603
779
  });
604
780
 
605
781
  describe("LocalThreadRuntimeCore suggestions", () => {
782
+ it("ignores suggestion generation from a superseded run", async () => {
783
+ let releaseFirst!: () => void;
784
+ let releaseSecond!: () => void;
785
+ const firstGate = new Promise<void>((resolve) => {
786
+ releaseFirst = resolve;
787
+ });
788
+ const secondGate = new Promise<void>((resolve) => {
789
+ releaseSecond = resolve;
790
+ });
791
+ let runCount = 0;
792
+ const generate = vi.fn().mockResolvedValue([{ prompt: "follow up" }]);
793
+ const thread = createThread(
794
+ {
795
+ async run() {
796
+ runCount += 1;
797
+ await (runCount === 1 ? firstGate : secondGate);
798
+ return { content: [{ type: "text", text: "done" }] };
799
+ },
800
+ },
801
+ { suggestion: { generate } },
802
+ );
803
+
804
+ const firstAppend = thread.append(userMessage("first"));
805
+ await flush();
806
+ const secondAppend = thread.append(userMessage("second"));
807
+ await flush();
808
+
809
+ releaseFirst();
810
+ await firstAppend;
811
+ await flush();
812
+
813
+ expect(generate).not.toHaveBeenCalled();
814
+ expect(thread.suggestions).toEqual([]);
815
+
816
+ releaseSecond();
817
+ await secondAppend;
818
+ await flush();
819
+
820
+ expect(generate).toHaveBeenCalledOnce();
821
+ expect(thread.suggestions).toEqual([{ prompt: "follow up" }]);
822
+ });
823
+
606
824
  it("cancelRun aborts pending suggestion generation", async () => {
607
825
  const generate = vi.fn().mockImplementation(
608
826
  ({ signal }: { signal?: AbortSignal }) =>
@@ -656,8 +874,8 @@ describe("LocalThreadRuntimeCore suggestions", () => {
656
874
  });
657
875
 
658
876
  it("resolves append before suggestion generation completes", async () => {
659
- let resolveSuggestions!: (value: readonly { prompt: string }[]) => void;
660
- const suggestionsDeferred = new Promise<readonly { prompt: string }[]>(
877
+ let resolveSuggestions!: (value: readonly ThreadSuggestion[]) => void;
878
+ const suggestionsDeferred = new Promise<readonly ThreadSuggestion[]>(
661
879
  (resolve) => {
662
880
  resolveSuggestions = resolve;
663
881
  },
@@ -681,9 +899,15 @@ describe("LocalThreadRuntimeCore suggestions", () => {
681
899
  expect(generate).toHaveBeenCalledTimes(1);
682
900
  expect(thread.suggestions).toEqual([]);
683
901
 
684
- resolveSuggestions([{ prompt: "follow up" }]);
902
+ resolveSuggestions([
903
+ { title: "Weather", label: "in SF", prompt: "What's the weather?" },
904
+ { prompt: "follow up" },
905
+ ]);
685
906
  await new Promise((r) => setTimeout(r, 0));
686
- expect(thread.suggestions).toEqual([{ prompt: "follow up" }]);
907
+ expect(thread.suggestions).toEqual([
908
+ { title: "Weather", label: "in SF", prompt: "What's the weather?" },
909
+ { prompt: "follow up" },
910
+ ]);
687
911
  });
688
912
  });
689
913
 
@@ -1313,4 +1537,149 @@ describe("LocalThreadRuntimeCore runs", () => {
1313
1537
  expect(thread.capabilities.attachments).toBe(false);
1314
1538
  expect(thread.capabilities.feedback).toBe(false);
1315
1539
  });
1540
+
1541
+ it("loads history when the adapter arrives after the first load", async () => {
1542
+ const adapter: ChatModelAdapter = {
1543
+ run: async () => ({ content: [] }),
1544
+ };
1545
+ const thread = createPlainThread(adapter);
1546
+ const load = vi.fn(async () => ({
1547
+ headId: "restored",
1548
+ messages: [
1549
+ {
1550
+ parentId: null,
1551
+ message: {
1552
+ id: "restored",
1553
+ role: "user" as const,
1554
+ content: [{ type: "text" as const, text: "hello" }],
1555
+ createdAt: new Date(0),
1556
+ metadata: { custom: {} },
1557
+ },
1558
+ },
1559
+ ],
1560
+ }));
1561
+
1562
+ thread.__internal_load();
1563
+ expect(load).not.toHaveBeenCalled();
1564
+ expect(thread.messages).toHaveLength(0);
1565
+
1566
+ thread.__internal_setOptions({
1567
+ adapters: {
1568
+ chatModel: adapter,
1569
+ history: { load, append: async () => {} },
1570
+ },
1571
+ });
1572
+ await flush();
1573
+
1574
+ expect(load).toHaveBeenCalledOnce();
1575
+ expect(thread.messages.map((message) => message.id)).toEqual(["restored"]);
1576
+ });
1577
+
1578
+ it("does not load late history over a thread that already has messages", async () => {
1579
+ const adapter: ChatModelAdapter = {
1580
+ run: async () => ({ content: [] }),
1581
+ };
1582
+ const thread = createPlainThread(adapter);
1583
+ const load = vi.fn(async () => ({
1584
+ headId: "restored",
1585
+ messages: [
1586
+ {
1587
+ parentId: null,
1588
+ message: {
1589
+ id: "restored",
1590
+ role: "user" as const,
1591
+ content: [{ type: "text" as const, text: "hello" }],
1592
+ createdAt: new Date(0),
1593
+ metadata: { custom: {} },
1594
+ },
1595
+ },
1596
+ ],
1597
+ }));
1598
+
1599
+ thread.__internal_load();
1600
+ await thread.append({ ...userMessage("typed"), startRun: false });
1601
+ expect(thread.messages).toHaveLength(1);
1602
+
1603
+ thread.__internal_setOptions({
1604
+ adapters: {
1605
+ chatModel: adapter,
1606
+ history: { load, append: async () => {} },
1607
+ },
1608
+ });
1609
+ await flush();
1610
+
1611
+ expect(load).not.toHaveBeenCalled();
1612
+ expect(thread.messages.map((message) => message.content)).toEqual([
1613
+ [{ type: "text", text: "typed" }],
1614
+ ]);
1615
+ });
1616
+
1617
+ it("logs a rejected late history load", async () => {
1618
+ const adapter: ChatModelAdapter = {
1619
+ run: async () => ({ content: [] }),
1620
+ };
1621
+ const thread = createPlainThread(adapter);
1622
+ const error = new Error("history unavailable");
1623
+ const consoleError = vi
1624
+ .spyOn(console, "error")
1625
+ .mockImplementation(() => {});
1626
+
1627
+ thread.__internal_load();
1628
+ thread.__internal_setOptions({
1629
+ adapters: {
1630
+ chatModel: adapter,
1631
+ history: {
1632
+ load: async () => {
1633
+ throw error;
1634
+ },
1635
+ append: async () => {},
1636
+ },
1637
+ },
1638
+ });
1639
+ await flush();
1640
+
1641
+ expect(consoleError).toHaveBeenCalledWith(
1642
+ "[assistant-ui] local thread history load failed:",
1643
+ error,
1644
+ );
1645
+ });
1646
+ });
1647
+
1648
+ describe("LocalRuntimeCore composer.canCancel", () => {
1649
+ it("is true after append when the thread was created with initial messages", async () => {
1650
+ const core = new LocalRuntimeCore(
1651
+ {
1652
+ adapters: {
1653
+ chatModel: {
1654
+ async *run({ abortSignal }) {
1655
+ await new Promise<void>((resolve) => {
1656
+ if (abortSignal.aborted) {
1657
+ resolve();
1658
+ return;
1659
+ }
1660
+ abortSignal.addEventListener("abort", () => resolve(), {
1661
+ once: true,
1662
+ });
1663
+ });
1664
+ },
1665
+ },
1666
+ },
1667
+ },
1668
+ [
1669
+ {
1670
+ role: "assistant",
1671
+ content: [{ type: "text", text: "Hello" }],
1672
+ status: { type: "complete", reason: "stop" },
1673
+ },
1674
+ ],
1675
+ );
1676
+ const thread = core.threads.getMainThreadRuntimeCore();
1677
+ expect(thread.composer.canCancel).toBe(false);
1678
+
1679
+ void thread.append(userMessage("Run"));
1680
+ await flush();
1681
+
1682
+ expect(thread.composer.canCancel).toBe(true);
1683
+ thread.cancelRun();
1684
+ });
1316
1685
  });
@@ -22,7 +22,7 @@ import type {
22
22
  ThreadAssistantMessage,
23
23
  } from "../../types/message";
24
24
  import type { RunConfig } from "../../types/message";
25
- import { toAssistantError } from "../../types/error";
25
+ import { MessageNotSentError, toAssistantError } from "../../types/error";
26
26
  import type { ModelContextProvider } from "../../model-context/types";
27
27
  import {
28
28
  createMessageQueue,
@@ -33,6 +33,11 @@ import {
33
33
  EMPTY_QUEUE_ITEMS,
34
34
  type QueueItemState,
35
35
  } from "../../store/scopes/queue-item";
36
+ import {
37
+ captureThreadRuntimeGeneration,
38
+ invalidateThreadRuntime,
39
+ isThreadRuntimeGenerationCurrent,
40
+ } from "../../runtime/utils/thread-runtime-lifecycle";
36
41
 
37
42
  class AbortError extends Error {
38
43
  override name = "AbortError";
@@ -162,6 +167,7 @@ export class LocalThreadRuntimeCore
162
167
  public __internal_setOptions(options: LocalRuntimeOptionsBase) {
163
168
  if (this._options === options) return;
164
169
 
170
+ const previousHistory = this._options?.adapters.history;
165
171
  this._options = options;
166
172
 
167
173
  let hasUpdates = false;
@@ -234,13 +240,31 @@ export class LocalThreadRuntimeCore
234
240
  }
235
241
 
236
242
  if (hasUpdates) this._notifySubscribers();
243
+
244
+ if (
245
+ this._loadRequested &&
246
+ !this._loadPromise &&
247
+ !previousHistory &&
248
+ options.adapters.history &&
249
+ this.messages.length === 0
250
+ ) {
251
+ void this.__internal_load().catch((error: unknown) => {
252
+ console.error(
253
+ "[assistant-ui] local thread history load failed:",
254
+ error,
255
+ );
256
+ });
257
+ }
237
258
  }
238
259
 
239
260
  private _loadPromise: Promise<void> | undefined;
261
+ private _loadRequested = false;
240
262
  public __internal_load() {
263
+ this._loadRequested = true;
241
264
  if (this._loadPromise) return this._loadPromise;
265
+ if (!this.adapters.history) return Promise.resolve();
242
266
 
243
- const promise = this.adapters.history?.load() ?? Promise.resolve(null);
267
+ const promise = this.adapters.history.load();
244
268
 
245
269
  this._isLoading = true;
246
270
  this._notifySubscribers();
@@ -312,38 +336,72 @@ export class LocalThreadRuntimeCore
312
336
  this._queue?.adapter.remove(queueItemId);
313
337
  }
314
338
 
339
+ private _rollbackAppend(messageId: string) {
340
+ try {
341
+ this.repository.deleteMessage(messageId);
342
+ } catch {
343
+ return;
344
+ }
345
+ this._notifySubscribers();
346
+ }
347
+
315
348
  private async _runAppend(rawMessage: AppendMessage): Promise<void> {
316
349
  // Stamped here rather than in `append` so a queued message is gated after
317
350
  // the flush re-pointed its parentId at the current tail.
351
+ const generation = captureThreadRuntimeGeneration(this);
318
352
  const message = this.enrichAppendMetadata(rawMessage);
319
353
  this.ensureInitialized();
320
354
 
321
- const initPromise = this._getInitializePromise?.();
322
- if (initPromise) {
323
- await initPromise;
324
- }
325
-
326
355
  const newMessage = fromThreadMessageLike(message, generateId(), {
327
356
  type: "complete",
328
357
  reason: "unknown",
329
358
  });
330
359
  this.repository.addOrUpdateMessage(message.parentId, newMessage);
331
- this._options.adapters.history?.append({
360
+ this._notifySubscribers();
361
+
362
+ // Initialization only gates the history write and the run; the message
363
+ // is already on screen. A failed barrier rolls the optimistic message
364
+ // back and rejects as an unsent message so the composer restores the
365
+ // draft; a thread invalidated mid-wait rolls back silently.
366
+ try {
367
+ const initPromise = this._getInitializePromise?.();
368
+ if (initPromise) {
369
+ await initPromise;
370
+ }
371
+ } catch (error) {
372
+ this._rollbackAppend(newMessage.id);
373
+ if (!isThreadRuntimeGenerationCurrent(this, generation)) return;
374
+ const notSent = new MessageNotSentError();
375
+ notSent.cause = error;
376
+ throw notSent;
377
+ }
378
+ if (!isThreadRuntimeGenerationCurrent(this, generation)) {
379
+ this._rollbackAppend(newMessage.id);
380
+ return;
381
+ }
382
+ const historyWrite = this._options.adapters.history?.append({
332
383
  parentId: message.parentId,
333
384
  message: newMessage,
334
385
  ...(message.runConfig !== undefined && { runConfig: message.runConfig }),
335
386
  });
387
+ void historyWrite?.catch(() => {});
336
388
 
337
389
  const startRun = message.startRun ?? message.role === "user";
338
390
  if (startRun) {
339
- await this.startRun({
340
- parentId: newMessage.id,
341
- sourceId: message.sourceId,
342
- runConfig: message.runConfig ?? {},
343
- });
391
+ const [runResult, historyResult] = await Promise.allSettled([
392
+ this.startRun({
393
+ parentId: newMessage.id,
394
+ sourceId: message.sourceId,
395
+ runConfig: message.runConfig ?? {},
396
+ }),
397
+ historyWrite,
398
+ ]);
399
+ if (runResult.status === "rejected") throw runResult.reason;
400
+ if (historyResult.status === "rejected") throw historyResult.reason;
344
401
  } else {
345
402
  this.repository.resetHead(newMessage.id);
346
403
  this._notifySubscribers();
404
+ await historyWrite;
347
405
  }
348
406
  }
349
407
 
@@ -380,6 +438,10 @@ export class LocalThreadRuntimeCore
380
438
  throw new Error("Runtime does not support importing external states.");
381
439
  }
382
440
 
441
+ public unstable_notifySessionReset(): void {
442
+ throw new Error("Runtime does not support resetting sessions.");
443
+ }
444
+
383
445
  public async startRun(
384
446
  { parentId, runConfig }: StartRunConfig,
385
447
  runCallback?: ChatModelAdapter["run"],
@@ -424,6 +486,7 @@ export class LocalThreadRuntimeCore
424
486
  this._activeRun = run;
425
487
  this._runGeneration++;
426
488
 
489
+ let active = false;
427
490
  try {
428
491
  // mark busy for runs not started through the queue (regenerate, resume)
429
492
  this._queue?.notifyBusy();
@@ -447,7 +510,7 @@ export class LocalThreadRuntimeCore
447
510
  // the settle belongs to this run only while it is still the active run
448
511
  // or was cancelled (the engine expects a cancelled run's settle); a run
449
512
  // superseded by a newer one stays silent
450
- const active = this._activeRun === run;
513
+ active = this._activeRun === run;
451
514
  if (active) this._activeRun = null;
452
515
  if (active || run.cancelled) {
453
516
  queueMicrotask(() => this._queue?.notifyIdle());
@@ -455,6 +518,7 @@ export class LocalThreadRuntimeCore
455
518
  }
456
519
 
457
520
  if (
521
+ active &&
458
522
  this.adapters.suggestion &&
459
523
  message.status?.type !== "requires-action"
460
524
  ) {
@@ -674,6 +738,7 @@ export class LocalThreadRuntimeCore
674
738
  }
675
739
 
676
740
  public detach() {
741
+ invalidateThreadRuntime(this);
677
742
  // drop the queue so pending items cannot dispatch on a detached thread
678
743
  this._queue = null;
679
744
  const error = new AbortError(true);
@@ -112,6 +112,10 @@ export class ReadonlyThreadRuntimeCore
112
112
  throw READONLY_THREAD_ERROR;
113
113
  }
114
114
 
115
+ unstable_notifySessionReset(): void {
116
+ throw READONLY_THREAD_ERROR;
117
+ }
118
+
115
119
  composer = {
116
120
  attachments: [] as never[],
117
121
  attachmentAccept: "*",
@@ -91,6 +91,10 @@ export const EMPTY_THREAD_CORE: ThreadRuntimeCore = {
91
91
  throw EMPTY_THREAD_ERROR;
92
92
  },
93
93
 
94
+ unstable_notifySessionReset() {
95
+ throw EMPTY_THREAD_ERROR;
96
+ },
97
+
94
98
  composer: {
95
99
  attachments: [],
96
100
  attachmentAccept: "*",
@@ -1,6 +1,9 @@
1
1
  import type { ThreadMessage } from "../../types/message";
2
2
  import type { AssistantRuntime } from "../../runtime/api/assistant-runtime";
3
3
  import type { AssistantStream } from "assistant-stream";
4
+ import type { ThreadHistoryAdapter } from "../../adapters/thread-history";
5
+ import type { AttachmentAdapter } from "../../adapters/attachment";
6
+ import type { ModelContextProvider } from "../../model-context/types";
4
7
 
5
8
  /* oxlint-disable typescript/no-explicit-any -- structural stand-in for ComponentType without depending on react types */
6
9
  type RemoteThreadListProviderProps = { children?: any };
@@ -33,6 +36,12 @@ export type RemoteThreadListPageOptions = {
33
36
  after?: string | undefined;
34
37
  };
35
38
 
39
+ export type RuntimeAdapters = {
40
+ modelContext?: ModelContextProvider | undefined;
41
+ history?: ThreadHistoryAdapter | undefined;
42
+ attachments?: AttachmentAdapter | undefined;
43
+ };
44
+
36
45
  export type RemoteThreadListAdapter = {
37
46
  list(params?: RemoteThreadListPageOptions): Promise<RemoteThreadListResponse>;
38
47
 
@@ -56,12 +65,33 @@ export type RemoteThreadListAdapter = {
56
65
  * inject per-thread context such as a history or attachments adapter (see
57
66
  * `useCloudThreadListAdapter` for the canonical shape).
58
67
  *
68
+ * `useRemoteThreadListRuntime` renders this component when present. If it
69
+ * is omitted, that host synthesizes a `RuntimeAdapterProvider` from
70
+ * `unstable_useAdapters`. The `RemoteThreadList` store entry ignores it;
71
+ * expose `unstable_useAdapters` for that host.
72
+ *
59
73
  * The Provider must render `children` on its first commit; deferring them
60
74
  * behind a loading state, a Suspense boundary, or a `useEffect`-gated render
61
75
  * is unsupported and leaves thread context unavailable to downstream
62
76
  * consumers. Load data inside an always-mounted child instead.
63
77
  */
64
78
  unstable_Provider?: RemoteThreadListProviderComponent | undefined;
79
+
80
+ /**
81
+ * Hook the `RemoteThreadList` store entry calls once for the main-thread
82
+ * slot, then provides to the `thread` factory. This is not mounted per
83
+ * listed thread. `useRemoteThreadListRuntime` also calls it when
84
+ * `unstable_Provider` is omitted. Resolve `threadListItem` lazily on each
85
+ * adapter call; do not capture it at hook mount. The hook must keep a
86
+ * stable hook count across adapter swaps; a different count throws.
87
+ * Memoize the returned object.
88
+ *
89
+ * Per-thread history also requires the `thread` factory to be keyed by
90
+ * thread id (`withKey(id, thread(...))`). History loaders such as
91
+ * `useExternalHistory` run once per mount; an unkeyed factory keeps one
92
+ * instance across switches and the next thread's messages never load.
93
+ */
94
+ unstable_useAdapters?: (() => RuntimeAdapters | null | undefined) | undefined;
65
95
  };
66
96
 
67
97
  export type RemoteThreadListOptions = {