@assistant-ui/core 0.3.12 → 0.3.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (452) hide show
  1. package/dist/adapters/speech.d.ts.map +1 -1
  2. package/dist/adapters/speech.js +9 -15
  3. package/dist/adapters/speech.js.map +1 -1
  4. package/dist/adapters/thread-history.d.ts +2 -0
  5. package/dist/adapters/thread-history.d.ts.map +1 -1
  6. package/dist/adapters/voice.d.ts.map +1 -1
  7. package/dist/adapters/voice.js +27 -15
  8. package/dist/adapters/voice.js.map +1 -1
  9. package/dist/index.d.ts +3 -3
  10. package/dist/index.js +2 -2
  11. package/dist/index.js.map +1 -1
  12. package/dist/internal.d.ts +1 -1
  13. package/dist/internal.js +6 -6
  14. package/dist/model-context/frame/host.d.ts.map +1 -1
  15. package/dist/model-context/frame/host.js +2 -1
  16. package/dist/model-context/frame/host.js.map +1 -1
  17. package/dist/model-context/registry.d.ts.map +1 -1
  18. package/dist/model-context/registry.js +16 -4
  19. package/dist/model-context/registry.js.map +1 -1
  20. package/dist/react/RuntimeAdapter.d.ts +8 -1
  21. package/dist/react/RuntimeAdapter.d.ts.map +1 -1
  22. package/dist/react/RuntimeAdapter.js +23 -18
  23. package/dist/react/RuntimeAdapter.js.map +1 -1
  24. package/dist/react/adapters/LocalStorageThreadListAdapter.d.ts.map +1 -1
  25. package/dist/react/adapters/LocalStorageThreadListAdapter.js +28 -12
  26. package/dist/react/adapters/LocalStorageThreadListAdapter.js.map +1 -1
  27. package/dist/react/client/AdaptedRemoteThread.d.ts +11 -0
  28. package/dist/react/client/AdaptedRemoteThread.d.ts.map +1 -0
  29. package/dist/react/client/AdaptedRemoteThread.js +27 -0
  30. package/dist/react/client/AdaptedRemoteThread.js.map +1 -0
  31. package/dist/react/client/InMemoryThreadList.d.ts +19 -0
  32. package/dist/react/client/InMemoryThreadList.d.ts.map +1 -0
  33. package/dist/react/client/InMemoryThreadList.js +418 -0
  34. package/dist/react/client/InMemoryThreadList.js.map +1 -0
  35. package/dist/react/client/Interactables.d.ts.map +1 -1
  36. package/dist/react/client/Interactables.js +159 -102
  37. package/dist/react/client/Interactables.js.map +1 -1
  38. package/dist/react/client/RemoteThreadList.d.ts +34 -0
  39. package/dist/react/client/RemoteThreadList.d.ts.map +1 -0
  40. package/dist/react/client/RemoteThreadList.js +826 -0
  41. package/dist/react/client/RemoteThreadList.js.map +1 -0
  42. package/dist/react/client/Tools.d.ts.map +1 -1
  43. package/dist/react/client/Tools.js +38 -27
  44. package/dist/react/client/Tools.js.map +1 -1
  45. package/dist/react/client/interactable-model-context.d.ts.map +1 -1
  46. package/dist/react/client/interactable-model-context.js +5 -1
  47. package/dist/react/client/interactable-model-context.js.map +1 -1
  48. package/dist/react/index.d.ts +7 -2
  49. package/dist/react/index.js +5 -2
  50. package/dist/react/interactables-legacy/Interactables.d.ts.map +1 -1
  51. package/dist/react/interactables-legacy/Interactables.js +145 -110
  52. package/dist/react/interactables-legacy/Interactables.js.map +1 -1
  53. package/dist/react/primitive-hooks/useActionBarCopy.d.ts.map +1 -1
  54. package/dist/react/primitive-hooks/useActionBarCopy.js +13 -18
  55. package/dist/react/primitive-hooks/useActionBarCopy.js.map +1 -1
  56. package/dist/react/primitive-hooks/useActionBarEdit.d.ts.map +1 -1
  57. package/dist/react/primitive-hooks/useActionBarEdit.js +2 -4
  58. package/dist/react/primitive-hooks/useActionBarEdit.js.map +1 -1
  59. package/dist/react/primitive-hooks/useActionBarReload.d.ts.map +1 -1
  60. package/dist/react/primitive-hooks/useActionBarReload.js +2 -4
  61. package/dist/react/primitive-hooks/useActionBarReload.js.map +1 -1
  62. package/dist/react/primitive-hooks/useBranchPickerNext.d.ts.map +1 -1
  63. package/dist/react/primitive-hooks/useBranchPickerNext.js +2 -6
  64. package/dist/react/primitive-hooks/useBranchPickerNext.js.map +1 -1
  65. package/dist/react/primitive-hooks/useBranchPickerPrevious.d.ts.map +1 -1
  66. package/dist/react/primitive-hooks/useBranchPickerPrevious.js +2 -6
  67. package/dist/react/primitive-hooks/useBranchPickerPrevious.js.map +1 -1
  68. package/dist/react/primitive-hooks/useComposerCancel.d.ts.map +1 -1
  69. package/dist/react/primitive-hooks/useComposerCancel.js +2 -4
  70. package/dist/react/primitive-hooks/useComposerCancel.js.map +1 -1
  71. package/dist/react/primitive-hooks/useComposerSend.d.ts.map +1 -1
  72. package/dist/react/primitive-hooks/useComposerSend.js +2 -4
  73. package/dist/react/primitive-hooks/useComposerSend.js.map +1 -1
  74. package/dist/react/primitive-hooks/useSuggestionTrigger.d.ts.map +1 -1
  75. package/dist/react/primitive-hooks/useSuggestionTrigger.js +6 -2
  76. package/dist/react/primitive-hooks/useSuggestionTrigger.js.map +1 -1
  77. package/dist/react/primitives/message/MessageParts.d.ts +13 -0
  78. package/dist/react/primitives/message/MessageParts.d.ts.map +1 -1
  79. package/dist/react/primitives/message/MessageParts.js.map +1 -1
  80. package/dist/react/primitives/thread/ThreadMessages.d.ts.map +1 -1
  81. package/dist/react/primitives/thread/ThreadMessages.js +1 -1
  82. package/dist/react/primitives/thread/ThreadMessages.js.map +1 -1
  83. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.d.ts +22 -10
  84. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.d.ts.map +1 -1
  85. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js +109 -71
  86. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js.map +1 -1
  87. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts +8 -2
  88. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts.map +1 -1
  89. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js +40 -13
  90. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js.map +1 -1
  91. package/dist/react/runtimes/RemoteThreadResource.d.ts +30 -0
  92. package/dist/react/runtimes/RemoteThreadResource.d.ts.map +1 -0
  93. package/dist/react/runtimes/RemoteThreadResource.js +134 -0
  94. package/dist/react/runtimes/RemoteThreadResource.js.map +1 -0
  95. package/dist/react/runtimes/RuntimeAdapterProvider.d.ts +3 -10
  96. package/dist/react/runtimes/RuntimeAdapterProvider.d.ts.map +1 -1
  97. package/dist/react/runtimes/RuntimeAdapterProvider.js +3 -6
  98. package/dist/react/runtimes/RuntimeAdapterProvider.js.map +1 -1
  99. package/dist/react/runtimes/assistant-transport/commandQueue.d.ts +17 -0
  100. package/dist/react/runtimes/assistant-transport/commandQueue.d.ts.map +1 -0
  101. package/dist/react/runtimes/assistant-transport/commandQueue.js +55 -0
  102. package/dist/react/runtimes/assistant-transport/commandQueue.js.map +1 -0
  103. package/dist/react/runtimes/assistant-transport/replayBoundaryStream.d.ts +11 -0
  104. package/dist/react/runtimes/assistant-transport/replayBoundaryStream.d.ts.map +1 -0
  105. package/dist/react/runtimes/assistant-transport/replayBoundaryStream.js +165 -0
  106. package/dist/react/runtimes/assistant-transport/replayBoundaryStream.js.map +1 -0
  107. package/dist/react/runtimes/assistant-transport/runManager.d.ts +15 -0
  108. package/dist/react/runtimes/assistant-transport/runManager.d.ts.map +1 -0
  109. package/dist/react/runtimes/assistant-transport/runManager.js +86 -0
  110. package/dist/react/runtimes/assistant-transport/runManager.js.map +1 -0
  111. package/dist/react/runtimes/assistant-transport/types.d.ts +130 -0
  112. package/dist/react/runtimes/assistant-transport/types.d.ts.map +1 -0
  113. package/dist/react/runtimes/assistant-transport/types.js +0 -0
  114. package/dist/react/runtimes/assistant-transport/useAssistantTransportRuntime.d.ts +14 -0
  115. package/dist/react/runtimes/assistant-transport/useAssistantTransportRuntime.d.ts.map +1 -0
  116. package/dist/react/runtimes/assistant-transport/useAssistantTransportRuntime.js +296 -0
  117. package/dist/react/runtimes/assistant-transport/useAssistantTransportRuntime.js.map +1 -0
  118. package/dist/react/runtimes/assistant-transport/useConvertedState.d.ts +7 -0
  119. package/dist/react/runtimes/assistant-transport/useConvertedState.d.ts.map +1 -0
  120. package/dist/react/runtimes/assistant-transport/useConvertedState.js +25 -0
  121. package/dist/react/runtimes/assistant-transport/useConvertedState.js.map +1 -0
  122. package/dist/react/runtimes/assistant-transport/useLatestRef.d.ts +7 -0
  123. package/dist/react/runtimes/assistant-transport/useLatestRef.d.ts.map +1 -0
  124. package/dist/react/runtimes/assistant-transport/useLatestRef.js +27 -0
  125. package/dist/react/runtimes/assistant-transport/useLatestRef.js.map +1 -0
  126. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.d.ts.map +1 -1
  127. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js +39 -13
  128. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js.map +1 -1
  129. package/dist/react/runtimes/cloud/CloudFileAttachmentAdapter.d.ts +2 -1
  130. package/dist/react/runtimes/cloud/CloudFileAttachmentAdapter.d.ts.map +1 -1
  131. package/dist/react/runtimes/cloud/CloudFileAttachmentAdapter.js +3 -3
  132. package/dist/react/runtimes/cloud/CloudFileAttachmentAdapter.js.map +1 -1
  133. package/dist/react/runtimes/cloud/auiV0.d.ts +4 -0
  134. package/dist/react/runtimes/cloud/auiV0.d.ts.map +1 -1
  135. package/dist/react/runtimes/cloud/auiV0.js +7 -0
  136. package/dist/react/runtimes/cloud/auiV0.js.map +1 -1
  137. package/dist/react/runtimes/cloud/useCloudThreadListAdapter.d.ts.map +1 -1
  138. package/dist/react/runtimes/cloud/useCloudThreadListAdapter.js +22 -12
  139. package/dist/react/runtimes/cloud/useCloudThreadListAdapter.js.map +1 -1
  140. package/dist/react/runtimes/cloud/useCloudThreadListRuntime.d.ts +16 -0
  141. package/dist/react/runtimes/cloud/useCloudThreadListRuntime.d.ts.map +1 -0
  142. package/dist/react/runtimes/cloud/useCloudThreadListRuntime.js +36 -0
  143. package/dist/react/runtimes/cloud/useCloudThreadListRuntime.js.map +1 -0
  144. package/dist/react/runtimes/external-message-converter.js +14 -14
  145. package/dist/react/runtimes/external-message-converter.js.map +1 -1
  146. package/dist/react/runtimes/useExternalStoreRuntime.d.ts.map +1 -1
  147. package/dist/react/runtimes/useExternalStoreRuntime.js +44 -28
  148. package/dist/react/runtimes/useExternalStoreRuntime.js.map +1 -1
  149. package/dist/react/runtimes/useExternalStoreSharedOptions.js.map +1 -1
  150. package/dist/react/runtimes/useLocalRuntime.js +1 -1
  151. package/dist/react/runtimes/useRuntimeAdapters.d.ts +9 -0
  152. package/dist/react/runtimes/useRuntimeAdapters.d.ts.map +1 -0
  153. package/dist/react/runtimes/useRuntimeAdapters.js +20 -0
  154. package/dist/react/runtimes/useRuntimeAdapters.js.map +1 -0
  155. package/dist/runtime/api/composer-runtime.d.ts +5 -0
  156. package/dist/runtime/api/composer-runtime.d.ts.map +1 -1
  157. package/dist/runtime/api/composer-runtime.js.map +1 -1
  158. package/dist/runtime/api/message-runtime.js +1 -1
  159. package/dist/runtime/api/thread-list-runtime.d.ts +1 -0
  160. package/dist/runtime/api/thread-list-runtime.d.ts.map +1 -1
  161. package/dist/runtime/api/thread-list-runtime.js +4 -2
  162. package/dist/runtime/api/thread-list-runtime.js.map +1 -1
  163. package/dist/runtime/api/thread-runtime.d.ts +12 -2
  164. package/dist/runtime/api/thread-runtime.d.ts.map +1 -1
  165. package/dist/runtime/api/thread-runtime.js +13 -3
  166. package/dist/runtime/api/thread-runtime.js.map +1 -1
  167. package/dist/runtime/base/base-composer-runtime-core.d.ts +25 -0
  168. package/dist/runtime/base/base-composer-runtime-core.d.ts.map +1 -1
  169. package/dist/runtime/base/base-composer-runtime-core.js +72 -18
  170. package/dist/runtime/base/base-composer-runtime-core.js.map +1 -1
  171. package/dist/runtime/base/base-thread-runtime-core.d.ts +15 -0
  172. package/dist/runtime/base/base-thread-runtime-core.d.ts.map +1 -1
  173. package/dist/runtime/base/base-thread-runtime-core.js +34 -3
  174. package/dist/runtime/base/base-thread-runtime-core.js.map +1 -1
  175. package/dist/runtime/base/default-edit-composer-runtime-core.d.ts.map +1 -1
  176. package/dist/runtime/base/default-edit-composer-runtime-core.js +1 -6
  177. package/dist/runtime/base/default-edit-composer-runtime-core.js.map +1 -1
  178. package/dist/runtime/base/default-thread-composer-runtime-core.d.ts +0 -1
  179. package/dist/runtime/base/default-thread-composer-runtime-core.d.ts.map +1 -1
  180. package/dist/runtime/base/default-thread-composer-runtime-core.js +11 -8
  181. package/dist/runtime/base/default-thread-composer-runtime-core.js.map +1 -1
  182. package/dist/runtime/interfaces/thread-runtime-core.d.ts +6 -0
  183. package/dist/runtime/interfaces/thread-runtime-core.d.ts.map +1 -1
  184. package/dist/runtime/internal.js +5 -5
  185. package/dist/runtime/queue/external-thread-queue-adapter.d.ts +15 -0
  186. package/dist/runtime/queue/external-thread-queue-adapter.d.ts.map +1 -1
  187. package/dist/runtime/queue/message-queue.d.ts.map +1 -1
  188. package/dist/runtime/queue/message-queue.js +25 -11
  189. package/dist/runtime/queue/message-queue.js.map +1 -1
  190. package/dist/runtime/utils/thread-runtime-lifecycle.d.ts +8 -0
  191. package/dist/runtime/utils/thread-runtime-lifecycle.d.ts.map +1 -0
  192. package/dist/runtime/utils/thread-runtime-lifecycle.js +11 -0
  193. package/dist/runtime/utils/thread-runtime-lifecycle.js.map +1 -0
  194. package/dist/runtimes/external-store/external-store-adapter.d.ts +7 -0
  195. package/dist/runtimes/external-store/external-store-adapter.d.ts.map +1 -1
  196. package/dist/runtimes/external-store/external-store-shared-options.js.map +1 -1
  197. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.d.ts.map +1 -1
  198. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.js +4 -1
  199. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.js.map +1 -1
  200. package/dist/runtimes/external-store/external-store-thread-runtime-core.d.ts +11 -1
  201. package/dist/runtimes/external-store/external-store-thread-runtime-core.d.ts.map +1 -1
  202. package/dist/runtimes/external-store/external-store-thread-runtime-core.js +78 -20
  203. package/dist/runtimes/external-store/external-store-thread-runtime-core.js.map +1 -1
  204. package/dist/runtimes/internal.d.ts +1 -1
  205. package/dist/runtimes/local/local-thread-runtime-core.d.ts +3 -0
  206. package/dist/runtimes/local/local-thread-runtime-core.d.ts.map +1 -1
  207. package/dist/runtimes/local/local-thread-runtime-core.js +55 -14
  208. package/dist/runtimes/local/local-thread-runtime-core.js.map +1 -1
  209. package/dist/runtimes/readonly/ReadonlyThreadRuntimeCore.d.ts +1 -0
  210. package/dist/runtimes/readonly/ReadonlyThreadRuntimeCore.d.ts.map +1 -1
  211. package/dist/runtimes/readonly/ReadonlyThreadRuntimeCore.js +3 -0
  212. package/dist/runtimes/readonly/ReadonlyThreadRuntimeCore.js.map +1 -1
  213. package/dist/runtimes/remote-thread-list/empty-thread-core.js +3 -0
  214. package/dist/runtimes/remote-thread-list/empty-thread-core.js.map +1 -1
  215. package/dist/runtimes/remote-thread-list/types.d.ts +29 -1
  216. package/dist/runtimes/remote-thread-list/types.d.ts.map +1 -1
  217. package/dist/runtimes/tool-invocations/ToolInvocationTracker.d.ts +1 -12
  218. package/dist/runtimes/tool-invocations/ToolInvocationTracker.d.ts.map +1 -1
  219. package/dist/runtimes/tool-invocations/ToolInvocationTracker.js +67 -38
  220. package/dist/runtimes/tool-invocations/ToolInvocationTracker.js.map +1 -1
  221. package/dist/store/clients/external-thread.d.ts +7 -0
  222. package/dist/store/clients/external-thread.d.ts.map +1 -1
  223. package/dist/store/clients/external-thread.js +460 -369
  224. package/dist/store/clients/external-thread.js.map +1 -1
  225. package/dist/store/clients/runtime-adapter.d.ts.map +1 -1
  226. package/dist/store/clients/runtime-adapter.js +5 -2
  227. package/dist/store/clients/runtime-adapter.js.map +1 -1
  228. package/dist/store/clients/single-thread-list.d.ts.map +1 -1
  229. package/dist/store/clients/single-thread-list.js +31 -27
  230. package/dist/store/clients/single-thread-list.js.map +1 -1
  231. package/dist/store/clients/suggestions.d.ts +3 -1
  232. package/dist/store/clients/suggestions.d.ts.map +1 -1
  233. package/dist/store/clients/suggestions.js +65 -37
  234. package/dist/store/clients/suggestions.js.map +1 -1
  235. package/dist/store/clients/thread-selection-events.d.ts +10 -0
  236. package/dist/store/clients/thread-selection-events.d.ts.map +1 -0
  237. package/dist/store/clients/thread-selection-events.js +40 -0
  238. package/dist/store/clients/thread-selection-events.js.map +1 -0
  239. package/dist/store/env.d.ts +5 -0
  240. package/dist/store/env.d.ts.map +1 -0
  241. package/dist/store/env.js +12 -0
  242. package/dist/store/env.js.map +1 -0
  243. package/dist/store/index.d.ts +6 -4
  244. package/dist/store/index.js +5 -3
  245. package/dist/store/internal.d.ts +4 -1
  246. package/dist/store/internal.js +4 -1
  247. package/dist/store/primitive-predicates.d.ts +19 -0
  248. package/dist/store/primitive-predicates.d.ts.map +1 -0
  249. package/dist/store/primitive-predicates.js +19 -0
  250. package/dist/store/primitive-predicates.js.map +1 -0
  251. package/dist/store/runtime-clients/attachment-runtime-client.d.ts.map +1 -1
  252. package/dist/store/runtime-clients/attachment-runtime-client.js +18 -11
  253. package/dist/store/runtime-clients/attachment-runtime-client.js.map +1 -1
  254. package/dist/store/runtime-clients/handle-runtime-action.d.ts +5 -0
  255. package/dist/store/runtime-clients/handle-runtime-action.d.ts.map +1 -0
  256. package/dist/store/runtime-clients/handle-runtime-action.js +12 -0
  257. package/dist/store/runtime-clients/handle-runtime-action.js.map +1 -0
  258. package/dist/store/runtime-clients/handle-thread-list-action.d.ts +5 -0
  259. package/dist/store/runtime-clients/handle-thread-list-action.d.ts.map +1 -0
  260. package/dist/store/runtime-clients/handle-thread-list-action.js +7 -0
  261. package/dist/store/runtime-clients/handle-thread-list-action.js.map +1 -0
  262. package/dist/store/runtime-clients/thread-list-item-runtime-client.d.ts.map +1 -1
  263. package/dist/store/runtime-clients/thread-list-item-runtime-client.js +104 -57
  264. package/dist/store/runtime-clients/thread-list-item-runtime-client.js.map +1 -1
  265. package/dist/store/runtime-clients/thread-list-runtime-client.d.ts.map +1 -1
  266. package/dist/store/runtime-clients/thread-list-runtime-client.js +5 -6
  267. package/dist/store/runtime-clients/thread-list-runtime-client.js.map +1 -1
  268. package/dist/store/runtime-clients/thread-runtime-client.d.ts.map +1 -1
  269. package/dist/store/runtime-clients/thread-runtime-client.js +98 -82
  270. package/dist/store/runtime-clients/thread-runtime-client.js.map +1 -1
  271. package/dist/store/scope-registration.d.ts +2 -2
  272. package/dist/store/scopes/composer.d.ts +9 -4
  273. package/dist/store/scopes/composer.d.ts.map +1 -1
  274. package/dist/store/scopes/suggestions.d.ts +6 -1
  275. package/dist/store/scopes/suggestions.d.ts.map +1 -1
  276. package/dist/store/scopes/thread-list-item.d.ts +7 -6
  277. package/dist/store/scopes/thread-list-item.d.ts.map +1 -1
  278. package/dist/store/scopes/thread.d.ts +25 -7
  279. package/dist/store/scopes/thread.d.ts.map +1 -1
  280. package/dist/store/scopes/threads.d.ts +17 -1
  281. package/dist/store/scopes/threads.d.ts.map +1 -1
  282. package/dist/subscribable/subscribable.d.ts +2 -1
  283. package/dist/subscribable/subscribable.d.ts.map +1 -1
  284. package/dist/subscribable/subscribable.js +23 -18
  285. package/dist/subscribable/subscribable.js.map +1 -1
  286. package/dist/types/error.d.ts +15 -1
  287. package/dist/types/error.d.ts.map +1 -1
  288. package/dist/types/error.js +18 -1
  289. package/dist/types/error.js.map +1 -1
  290. package/dist/types/message.d.ts +2 -0
  291. package/dist/types/message.d.ts.map +1 -1
  292. package/dist/types/message.js.map +1 -1
  293. package/dist/utils/composite-context-provider.d.ts +1 -0
  294. package/dist/utils/composite-context-provider.d.ts.map +1 -1
  295. package/dist/utils/composite-context-provider.js +25 -9
  296. package/dist/utils/composite-context-provider.js.map +1 -1
  297. package/dist/utils/notify-event-listeners.d.ts +2 -2
  298. package/dist/utils/notify-event-listeners.d.ts.map +1 -1
  299. package/dist/utils/notify-event-listeners.js +2 -2
  300. package/dist/utils/notify-event-listeners.js.map +1 -1
  301. package/package.json +7 -7
  302. package/src/adapters/speech.test.ts +118 -1
  303. package/src/adapters/speech.ts +21 -22
  304. package/src/adapters/thread-history.ts +2 -0
  305. package/src/adapters/voice.test.ts +69 -0
  306. package/src/adapters/voice.ts +27 -27
  307. package/src/index.ts +2 -0
  308. package/src/model-context/frame/host.test.ts +23 -0
  309. package/src/model-context/frame/host.ts +2 -1
  310. package/src/model-context/registry.test.ts +131 -0
  311. package/src/model-context/registry.ts +16 -6
  312. package/src/react/RuntimeAdapter.test.ts +2 -0
  313. package/src/react/RuntimeAdapter.ts +20 -8
  314. package/src/react/adapters/LocalStorageThreadListAdapter.tsx +34 -17
  315. package/src/react/client/AdaptedRemoteThread.ts +41 -0
  316. package/src/react/client/InMemoryThreadList.test.tsx +148 -0
  317. package/src/react/client/InMemoryThreadList.ts +290 -0
  318. package/src/react/client/Interactables.test.ts +326 -2
  319. package/src/react/client/Interactables.ts +227 -110
  320. package/src/react/client/RemoteThreadList.test.ts +1026 -0
  321. package/src/react/client/RemoteThreadList.ts +1067 -0
  322. package/src/react/client/Tools.ts +36 -29
  323. package/src/react/client/interactable-model-context.ts +6 -1
  324. package/src/react/index.ts +13 -0
  325. package/src/react/interactables-legacy/Interactables.test.ts +317 -0
  326. package/src/react/interactables-legacy/Interactables.ts +204 -128
  327. package/src/react/primitive-hooks/useActionBarCopy.ts +2 -7
  328. package/src/react/primitive-hooks/useActionBarEdit.ts +2 -1
  329. package/src/react/primitive-hooks/useActionBarReload.ts +2 -6
  330. package/src/react/primitive-hooks/useBranchPickerNext.ts +2 -7
  331. package/src/react/primitive-hooks/useBranchPickerPrevious.ts +2 -7
  332. package/src/react/primitive-hooks/useComposerCancel.ts +2 -1
  333. package/src/react/primitive-hooks/useComposerSend.ts +2 -5
  334. package/src/react/primitive-hooks/useSuggestionTrigger.test.ts +11 -0
  335. package/src/react/primitive-hooks/useSuggestionTrigger.ts +4 -5
  336. package/src/react/primitives/message/MessageParts.tsx +0 -1
  337. package/src/react/primitives/thread/ThreadMessages.tsx +2 -1
  338. package/src/react/runtimes/RemoteThreadListHookInstanceManager.retry.test.tsx +116 -0
  339. package/src/react/runtimes/RemoteThreadListHookInstanceManager.running.test.tsx +1 -1
  340. package/src/react/runtimes/RemoteThreadListHookInstanceManager.test.tsx +57 -6
  341. package/src/react/runtimes/RemoteThreadListHookInstanceManager.title.test.tsx +272 -0
  342. package/src/react/runtimes/RemoteThreadListHookInstanceManager.tsx +159 -124
  343. package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.tsx +59 -20
  344. package/src/react/runtimes/RemoteThreadResource.test.ts +141 -0
  345. package/src/react/runtimes/RemoteThreadResource.ts +231 -0
  346. package/src/react/runtimes/RuntimeAdapterProvider.tsx +8 -20
  347. package/src/react/runtimes/assistant-transport/commandQueue.ts +68 -0
  348. package/src/react/runtimes/assistant-transport/replayBoundaryStream.test.ts +455 -0
  349. package/src/react/runtimes/assistant-transport/replayBoundaryStream.ts +172 -0
  350. package/src/react/runtimes/assistant-transport/runManager.ts +124 -0
  351. package/src/react/runtimes/assistant-transport/transport-delivery.test.tsx +79 -0
  352. package/src/react/runtimes/assistant-transport/transport-parentid.test.tsx +97 -0
  353. package/src/react/runtimes/assistant-transport/transport-scheduling.test.ts +414 -0
  354. package/src/react/runtimes/assistant-transport/types.ts +170 -0
  355. package/src/react/runtimes/assistant-transport/useAssistantTransport.spec.md +141 -0
  356. package/src/react/runtimes/assistant-transport/useAssistantTransportRuntime.test.tsx +625 -0
  357. package/src/react/runtimes/assistant-transport/useAssistantTransportRuntime.ts +481 -0
  358. package/src/react/runtimes/assistant-transport/useConvertedState.ts +20 -0
  359. package/src/react/runtimes/assistant-transport/useLatestRef.ts +9 -0
  360. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.test.tsx +131 -6
  361. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.ts +61 -15
  362. package/src/react/runtimes/cloud/CloudFileAttachmentAdapter.ts +6 -4
  363. package/src/react/runtimes/cloud/auiV0.ts +17 -1
  364. package/src/react/runtimes/cloud/useCloudThreadListAdapter.tsx +36 -21
  365. package/src/react/runtimes/cloud/useCloudThreadListRuntime.test.tsx +56 -0
  366. package/src/react/runtimes/cloud/useCloudThreadListRuntime.ts +31 -0
  367. package/src/react/runtimes/external-message-converter.test.ts +143 -0
  368. package/src/react/runtimes/external-message-converter.ts +41 -41
  369. package/src/react/runtimes/remote-thread-hook-on-tap.test.ts +58 -0
  370. package/src/react/runtimes/useExternalStoreRuntime.lifecycle.test.tsx +83 -0
  371. package/src/react/runtimes/useExternalStoreRuntime.suggestions.test.tsx +124 -0
  372. package/src/react/runtimes/useExternalStoreRuntime.ts +7 -0
  373. package/src/react/runtimes/useExternalStoreSharedOptions.ts +3 -1
  374. package/src/react/runtimes/useLocalRuntime.test.tsx +130 -4
  375. package/src/react/runtimes/useRuntimeAdapters.test.tsx +62 -0
  376. package/src/react/runtimes/useRuntimeAdapters.ts +28 -0
  377. package/src/runtime/api/composer-runtime.ts +5 -0
  378. package/src/runtime/api/thread-list-runtime.ts +3 -1
  379. package/src/runtime/api/thread-runtime.ts +28 -3
  380. package/src/runtime/base/base-composer-runtime-core.ts +106 -13
  381. package/src/runtime/base/base-thread-runtime-core.test.ts +191 -0
  382. package/src/runtime/base/base-thread-runtime-core.ts +47 -2
  383. package/src/runtime/base/default-edit-composer-runtime-core.ts +1 -15
  384. package/src/runtime/base/default-thread-composer-runtime-core.ts +13 -15
  385. package/src/runtime/interfaces/thread-runtime-core.ts +6 -0
  386. package/src/runtime/queue/external-thread-queue-adapter.ts +17 -0
  387. package/src/runtime/queue/message-queue.ts +28 -10
  388. package/src/runtime/utils/thread-runtime-lifecycle.ts +18 -0
  389. package/src/runtimes/external-store/external-store-adapter.ts +7 -0
  390. package/src/runtimes/external-store/external-store-shared-options.ts +3 -1
  391. package/src/runtimes/external-store/external-store-thread-list-runtime-core.ts +4 -1
  392. package/src/runtimes/external-store/external-store-thread-runtime-core.ts +187 -48
  393. package/src/runtimes/local/local-thread-runtime-core.test.ts +373 -4
  394. package/src/runtimes/local/local-thread-runtime-core.ts +83 -15
  395. package/src/runtimes/readonly/ReadonlyThreadRuntimeCore.ts +4 -0
  396. package/src/runtimes/remote-thread-list/empty-thread-core.ts +4 -0
  397. package/src/runtimes/remote-thread-list/types.ts +30 -0
  398. package/src/runtimes/tool-invocations/EDGE_CASES.md +17 -6
  399. package/src/runtimes/tool-invocations/ToolInvocationTracker.test.ts +243 -0
  400. package/src/runtimes/tool-invocations/ToolInvocationTracker.ts +127 -61
  401. package/src/store/clients/external-thread.ts +119 -16
  402. package/src/store/clients/model-context-client.test.ts +16 -8
  403. package/src/store/clients/runtime-adapter.ts +5 -2
  404. package/src/store/clients/single-thread-list.ts +2 -1
  405. package/src/store/clients/suggestions.ts +39 -13
  406. package/src/store/clients/thread-selection-events.ts +21 -0
  407. package/src/store/env.ts +11 -0
  408. package/src/store/index.ts +14 -1
  409. package/src/store/internal.ts +13 -0
  410. package/src/store/primitive-predicates.test.ts +184 -0
  411. package/src/store/primitive-predicates.ts +43 -0
  412. package/src/store/runtime-clients/attachment-runtime-client.test.ts +41 -0
  413. package/src/store/runtime-clients/attachment-runtime-client.ts +2 -1
  414. package/src/store/runtime-clients/handle-runtime-action.ts +11 -0
  415. package/src/store/runtime-clients/handle-thread-list-action.test.ts +69 -0
  416. package/src/store/runtime-clients/handle-thread-list-action.ts +6 -0
  417. package/src/store/runtime-clients/thread-list-item-runtime-client.ts +28 -35
  418. package/src/store/runtime-clients/thread-list-runtime-client.ts +9 -6
  419. package/src/store/runtime-clients/thread-runtime-client.ts +5 -0
  420. package/src/store/scopes/composer.ts +9 -4
  421. package/src/store/scopes/suggestions.ts +6 -0
  422. package/src/store/scopes/thread-list-item.ts +7 -6
  423. package/src/store/scopes/thread.ts +25 -7
  424. package/src/store/scopes/threads.ts +14 -0
  425. package/src/subscribable/subscribable.test.ts +77 -1
  426. package/src/subscribable/subscribable.ts +38 -23
  427. package/src/tests/RemoteThreadListThreadListRuntimeCore-switchToThread-unarchive.test.ts +25 -0
  428. package/src/tests/append-interactable-snapshots.test.ts +350 -0
  429. package/src/tests/auiV0Encode.test.ts +66 -0
  430. package/src/tests/base-composer-runtime-core-send.test.ts +156 -0
  431. package/src/tests/base-composer-runtime-core.test.ts +136 -1
  432. package/src/tests/composer-can-cancel.test.ts +101 -0
  433. package/src/tests/default-edit-composer-runtime-core.test.ts +0 -68
  434. package/src/tests/external-store-thread-list-runtime-core.test.ts +101 -0
  435. package/src/tests/external-store-thread-runtime-core-adapter.test.ts +491 -1
  436. package/src/tests/external-store-thread-runtime-core.test.ts +124 -0
  437. package/src/tests/external-thread-attachments.test.tsx +214 -0
  438. package/src/tests/external-thread-parity.test.tsx +54 -0
  439. package/src/tests/external-thread-refetch.test.tsx +117 -0
  440. package/src/tests/external-thread-suggestions.test.tsx +59 -0
  441. package/src/tests/interactables-toolui-migration.test.tsx +66 -0
  442. package/src/tests/message-queue.test.ts +54 -0
  443. package/src/tests/session-reset-notification.test.ts +145 -0
  444. package/src/tests/thread-message-like.test.ts +39 -0
  445. package/src/tests/thread-switch-events.test.tsx +234 -0
  446. package/src/tests/tools-scope-migration.test.tsx +58 -0
  447. package/src/types/error.ts +24 -0
  448. package/src/types/message.ts +2 -0
  449. package/src/utils/composite-context-provider.test.ts +139 -0
  450. package/src/utils/composite-context-provider.ts +26 -8
  451. package/src/utils/notify-event-listeners.ts +10 -5
  452. package/src/tests/default-thread-composer-runtime-core.test.ts +0 -82
@@ -0,0 +1,83 @@
1
+ // @vitest-environment jsdom
2
+
3
+ import { StrictMode } from "react";
4
+ import { act, render } from "@testing-library/react";
5
+ import { describe, expect, it, vi } from "vitest";
6
+ import { useExternalStoreRuntime } from "./useExternalStoreRuntime";
7
+ import type { AssistantRuntime } from "../../runtime/api/assistant-runtime";
8
+
9
+ describe("useExternalStoreRuntime lifecycle", () => {
10
+ it("keeps dispatching appends after StrictMode's simulated remount", async () => {
11
+ const onNew = vi.fn(async () => {});
12
+ const capture: { runtime: AssistantRuntime | null } = { runtime: null };
13
+ const App = () => {
14
+ const runtime = useExternalStoreRuntime({ messages: [], onNew });
15
+ capture.runtime = runtime;
16
+ return null;
17
+ };
18
+ render(
19
+ <StrictMode>
20
+ <App />
21
+ </StrictMode>,
22
+ );
23
+ expect(capture.runtime).not.toBeNull();
24
+
25
+ await act(async () => {
26
+ await capture.runtime!.thread.append({
27
+ role: "user",
28
+ content: [{ type: "text", text: "hello" }],
29
+ });
30
+ });
31
+
32
+ expect(onNew).toHaveBeenCalledTimes(1);
33
+ });
34
+
35
+ it("dispatches an append before unmount", async () => {
36
+ let resolveInitialization!: () => void;
37
+ const initialization = new Promise<void>((resolve) => {
38
+ resolveInitialization = resolve;
39
+ });
40
+ const onNew = vi.fn(async () => {});
41
+ const capture: { runtime: AssistantRuntime | null } = { runtime: null };
42
+ const App = () => {
43
+ const runtime = useExternalStoreRuntime({ messages: [], onNew });
44
+ capture.runtime = runtime;
45
+ return null;
46
+ };
47
+ const view = render(<App />);
48
+ expect(capture.runtime).not.toBeNull();
49
+ const core = (
50
+ capture.runtime!.thread as unknown as {
51
+ __internal_threadBinding: {
52
+ getState(): {
53
+ __internal_setGetInitializePromise(
54
+ getPromise: () => Promise<unknown> | undefined,
55
+ ): void;
56
+ append(message: unknown): Promise<void>;
57
+ };
58
+ };
59
+ }
60
+ ).__internal_threadBinding.getState();
61
+ core.__internal_setGetInitializePromise(() => initialization);
62
+
63
+ const appendPromise = core.append({
64
+ parentId: null,
65
+ sourceId: null,
66
+ runConfig: {},
67
+ role: "user",
68
+ content: [{ type: "text", text: "hello" }],
69
+ attachments: [],
70
+ metadata: { custom: {} },
71
+ createdAt: new Date(0),
72
+ });
73
+ await Promise.resolve();
74
+
75
+ expect(onNew).toHaveBeenCalledTimes(1);
76
+
77
+ view.unmount();
78
+ resolveInitialization();
79
+
80
+ await appendPromise;
81
+ expect(onNew).toHaveBeenCalledTimes(1);
82
+ });
83
+ });
@@ -0,0 +1,124 @@
1
+ // @vitest-environment jsdom
2
+
3
+ import { StrictMode } from "react";
4
+ import { cleanup, render, waitFor } from "@testing-library/react";
5
+ import { afterEach, describe, expect, it } from "vitest";
6
+ import {
7
+ AuiConfig,
8
+ useAui,
9
+ useAuiState,
10
+ type AssistantClient,
11
+ } from "@assistant-ui/store";
12
+ import type { ThreadSuggestion } from "../../runtime/interfaces/thread-runtime-core";
13
+ import { Suggestions } from "../../store/clients/suggestions";
14
+ import { AssistantRuntimeProvider } from "../AssistantRuntimeProvider";
15
+ import { useExternalStoreRuntime } from "./useExternalStoreRuntime";
16
+
17
+ let aui!: AssistantClient;
18
+ let scopeSuggestions!: readonly { prompt: string }[];
19
+
20
+ const Consumer = () => {
21
+ aui = useAui();
22
+ scopeSuggestions = useAuiState((s) => s.suggestions.suggestions);
23
+ return null;
24
+ };
25
+
26
+ const App = ({
27
+ suggestions,
28
+ config,
29
+ strict,
30
+ }: {
31
+ suggestions?: ThreadSuggestion[];
32
+ config?: AuiConfig;
33
+ strict?: boolean;
34
+ }) => {
35
+ const runtime = useExternalStoreRuntime({
36
+ messages: [],
37
+ onNew: async () => {},
38
+ ...(suggestions && { suggestions }),
39
+ });
40
+ const tree = (
41
+ <AssistantRuntimeProvider runtime={runtime} config={config}>
42
+ <Consumer />
43
+ </AssistantRuntimeProvider>
44
+ );
45
+ return strict ? <StrictMode>{tree}</StrictMode> : tree;
46
+ };
47
+
48
+ afterEach(() => {
49
+ cleanup();
50
+ });
51
+
52
+ describe("useExternalStoreRuntime suggestions scope", () => {
53
+ it("exposes runtime suggestions on the suggestions scope", () => {
54
+ render(
55
+ <App
56
+ suggestions={[
57
+ { title: "Weather", label: "in SF", prompt: "What's the weather?" },
58
+ { prompt: "Help" },
59
+ ]}
60
+ />,
61
+ );
62
+
63
+ expect(scopeSuggestions).toEqual([
64
+ { title: "Weather", label: "in SF", prompt: "What's the weather?" },
65
+ { title: "Help", label: "", prompt: "Help" },
66
+ ]);
67
+ expect(aui.suggestions.suggestion({ index: 0 }).getState()).toEqual({
68
+ title: "Weather",
69
+ label: "in SF",
70
+ prompt: "What's the weather?",
71
+ });
72
+ expect(aui.suggestions.suggestion({ index: 1 }).getState()).toEqual({
73
+ title: "Help",
74
+ label: "",
75
+ prompt: "Help",
76
+ });
77
+ expect(aui.thread.getState().suggestions).toEqual([
78
+ { title: "Weather", label: "in SF", prompt: "What's the weather?" },
79
+ { prompt: "Help" },
80
+ ]);
81
+ });
82
+
83
+ it("exposes runtime suggestions under StrictMode", () => {
84
+ render(<App strict suggestions={[{ prompt: "Tell me a joke" }]} />);
85
+
86
+ expect(scopeSuggestions).toEqual([
87
+ { title: "Tell me a joke", label: "", prompt: "Tell me a joke" },
88
+ ]);
89
+ });
90
+
91
+ it("reports an empty scope without runtime suggestions", () => {
92
+ render(<App />);
93
+
94
+ expect(scopeSuggestions).toEqual([]);
95
+ });
96
+
97
+ it("follows runtime suggestion updates", async () => {
98
+ const view = render(<App suggestions={[{ prompt: "First" }]} />);
99
+ expect(scopeSuggestions).toEqual([
100
+ { title: "First", label: "", prompt: "First" },
101
+ ]);
102
+
103
+ view.rerender(<App suggestions={[{ prompt: "Second" }]} />);
104
+ await waitFor(() => {
105
+ expect(scopeSuggestions).toEqual([
106
+ { title: "Second", label: "", prompt: "Second" },
107
+ ]);
108
+ });
109
+ });
110
+
111
+ it("prefers config-provided suggestions over runtime suggestions", () => {
112
+ render(
113
+ <App
114
+ suggestions={[{ prompt: "Runtime" }]}
115
+ config={AuiConfig({ suggestions: Suggestions(["Static"]) })}
116
+ />,
117
+ );
118
+
119
+ expect(scopeSuggestions).toEqual([
120
+ { title: "Static", label: "", prompt: "Static" },
121
+ ]);
122
+ expect(aui.thread.getState().suggestions).toEqual([{ prompt: "Runtime" }]);
123
+ });
124
+ });
@@ -5,6 +5,7 @@ import { ExternalStoreRuntimeCore } from "../../runtimes/internal";
5
5
  import type { ExternalStoreAdapter } from "../../runtimes/external-store/external-store-adapter";
6
6
  import type { AssistantRuntime } from "../../runtime/api/assistant-runtime";
7
7
  import { AssistantRuntimeImpl } from "../../runtime/internal";
8
+ import { invalidateThreadRuntime } from "../../runtime/utils/thread-runtime-lifecycle";
8
9
  import { useRuntimeAdapters } from "./RuntimeAdapterProvider";
9
10
 
10
11
  export const useExternalStoreRuntime = <T>(
@@ -12,6 +13,12 @@ export const useExternalStoreRuntime = <T>(
12
13
  ): AssistantRuntime => {
13
14
  const [runtime] = useState(() => new ExternalStoreRuntimeCore(store));
14
15
 
16
+ useEffect(() => {
17
+ return () => {
18
+ invalidateThreadRuntime(runtime.threads.getMainThreadRuntimeCore());
19
+ };
20
+ }, [runtime]);
21
+
15
22
  useEffect(() => {
16
23
  runtime.setAdapter(store);
17
24
  });
@@ -16,7 +16,9 @@ export const useExternalStoreSharedOptions = (
16
16
  unstable_capabilities,
17
17
  suggestions,
18
18
  }) satisfies {
19
- [K in keyof Required<ExternalStoreSharedOptions>]: ExternalStoreSharedOptions[K];
19
+ [
20
+ K in keyof Required<ExternalStoreSharedOptions>
21
+ ]: ExternalStoreSharedOptions[K];
20
22
  },
21
23
  [isDisabled, isSendDisabled, unstable_capabilities, suggestions],
22
24
  );
@@ -1,9 +1,10 @@
1
1
  // @vitest-environment jsdom
2
2
 
3
- import { render, waitFor } from "@testing-library/react";
4
- import { StrictMode } from "react";
3
+ import { act, render, waitFor } from "@testing-library/react";
4
+ import { type FC, StrictMode } from "react";
5
5
  import { afterEach, describe, expect, it, vi } from "vitest";
6
6
  import type { AssistantCloud } from "assistant-cloud";
7
+ import { useAui, useAuiState } from "@assistant-ui/store";
7
8
  import type { ChatModelAdapter } from "../../runtime/utils/chat-model-adapter";
8
9
  import { AssistantRuntimeProvider } from "../AssistantRuntimeProvider";
9
10
  import { useLocalRuntime } from "./useLocalRuntime";
@@ -17,13 +18,49 @@ const makeCloud = () =>
17
18
  threads: {
18
19
  list: vi.fn().mockResolvedValue({ threads: [] }),
19
20
  },
21
+ files: {
22
+ generatePresignedUploadUrl: vi.fn().mockResolvedValue({
23
+ signedUrl: "https://storage.example/upload",
24
+ publicUrl: "https://cdn.example/file.txt",
25
+ }),
26
+ },
20
27
  }) as unknown as AssistantCloud;
21
28
 
22
29
  afterEach(() => {
30
+ vi.unstubAllGlobals();
23
31
  vi.restoreAllMocks();
24
32
  });
25
33
 
26
34
  describe("useLocalRuntime", () => {
35
+ it("surfaces the live thread after mount without user input", async () => {
36
+ const auiRef: { current: ReturnType<typeof useAui> | null } = {
37
+ current: null,
38
+ };
39
+ const loadingRef = { current: true };
40
+ const Capture: FC = () => {
41
+ auiRef.current = useAui();
42
+ loadingRef.current = useAuiState((s) => s.thread.isLoading);
43
+ return null;
44
+ };
45
+
46
+ const App = () => {
47
+ const runtime = useLocalRuntime(chatModel, {
48
+ unstable_enableMessageQueue: true,
49
+ });
50
+ return (
51
+ <AssistantRuntimeProvider runtime={runtime}>
52
+ <Capture />
53
+ </AssistantRuntimeProvider>
54
+ );
55
+ };
56
+
57
+ render(<App />);
58
+ await waitFor(() => {
59
+ expect(loadingRef.current).toBe(false);
60
+ expect(auiRef.current!.thread.getState().capabilities.queue).toBe(true);
61
+ });
62
+ });
63
+
27
64
  it("keeps the Cloud thread list loaded across unrelated rerenders", async () => {
28
65
  const firstCloud = makeCloud();
29
66
  const secondCloud = makeCloud();
@@ -59,6 +96,47 @@ describe("useLocalRuntime", () => {
59
96
  expect(firstCloud.threads.list).toHaveBeenCalledOnce();
60
97
  });
61
98
 
99
+ it("uses the current Cloud client for attachment uploads", async () => {
100
+ vi.stubGlobal("fetch", vi.fn().mockResolvedValue({ ok: true }));
101
+ const firstCloud = makeCloud();
102
+ const secondCloud = makeCloud();
103
+ let addAttachment: ((file: File) => Promise<void>) | undefined;
104
+ let getAttachmentStatus: (() => unknown) | undefined;
105
+
106
+ const App = ({ cloud }: { cloud: AssistantCloud }) => {
107
+ const runtime = useLocalRuntime(chatModel, { cloud });
108
+ addAttachment = (file) => runtime.thread.composer.addAttachment(file);
109
+ getAttachmentStatus = () =>
110
+ runtime.thread.composer.getState().attachments[0]?.status;
111
+ return (
112
+ <AssistantRuntimeProvider runtime={runtime}>
113
+ <div />
114
+ </AssistantRuntimeProvider>
115
+ );
116
+ };
117
+
118
+ const { rerender } = render(<App cloud={firstCloud} />);
119
+
120
+ await waitFor(() => {
121
+ expect(firstCloud.threads.list).toHaveBeenCalledOnce();
122
+ });
123
+
124
+ rerender(<App cloud={secondCloud} />);
125
+
126
+ await act(async () => {
127
+ await addAttachment!(
128
+ new File(["hello"], "notes.txt", { type: "text/plain" }),
129
+ );
130
+ });
131
+
132
+ expect(firstCloud.files.generatePresignedUploadUrl).not.toHaveBeenCalled();
133
+ expect(secondCloud.files.generatePresignedUploadUrl).toHaveBeenCalledOnce();
134
+ expect(getAttachmentStatus!()).toEqual({
135
+ type: "requires-action",
136
+ reason: "composer-send",
137
+ });
138
+ });
139
+
62
140
  it("handles rejected history loads", async () => {
63
141
  const error = new Error("history unavailable");
64
142
  const consoleError = vi
@@ -92,8 +170,56 @@ describe("useLocalRuntime", () => {
92
170
  );
93
171
  });
94
172
 
173
+ const loadsAfterMount = history.load.mock.calls.length;
174
+ const errorsAfterMount = consoleError.mock.calls.length;
175
+ expect(loadsAfterMount).toBeGreaterThan(0);
95
176
  rerender(renderApp());
96
- expect(history.load).toHaveBeenCalledTimes(1);
97
- expect(consoleError).toHaveBeenCalledTimes(1);
177
+ expect(history.load).toHaveBeenCalledTimes(loadsAfterMount);
178
+ expect(consoleError).toHaveBeenCalledTimes(errorsAfterMount);
179
+ });
180
+
181
+ it("exposes composer.canCancel while a run started after initial messages is in flight", async () => {
182
+ const hangingModel: ChatModelAdapter = {
183
+ async *run({ abortSignal }) {
184
+ await new Promise<void>((resolve) => {
185
+ if (abortSignal.aborted) {
186
+ resolve();
187
+ return;
188
+ }
189
+ abortSignal.addEventListener("abort", () => resolve(), {
190
+ once: true,
191
+ });
192
+ });
193
+ },
194
+ };
195
+ let runtime: ReturnType<typeof useLocalRuntime> | null = null;
196
+ const App = () => {
197
+ runtime = useLocalRuntime(hangingModel, {
198
+ initialMessages: [
199
+ {
200
+ role: "assistant",
201
+ content: [{ type: "text", text: "Hello" }],
202
+ status: { type: "complete", reason: "stop" },
203
+ },
204
+ ],
205
+ });
206
+ return (
207
+ <AssistantRuntimeProvider runtime={runtime}>
208
+ <div />
209
+ </AssistantRuntimeProvider>
210
+ );
211
+ };
212
+
213
+ render(<App />);
214
+ act(() => {
215
+ runtime!.thread.append("Run");
216
+ });
217
+ await waitFor(() => {
218
+ expect(runtime!.thread.getState().isRunning).toBe(true);
219
+ expect(runtime!.thread.composer.getState().canCancel).toBe(true);
220
+ });
221
+ act(() => {
222
+ runtime!.thread.cancelRun();
223
+ });
98
224
  });
99
225
  });
@@ -0,0 +1,62 @@
1
+ // @vitest-environment jsdom
2
+
3
+ import { renderHook } from "@testing-library/react";
4
+ import { describe, expect, it } from "vitest";
5
+ import { useStableRuntimeAdapters } from "./useRuntimeAdapters";
6
+ import type { RuntimeAdapters } from "../../runtimes/remote-thread-list/types";
7
+ import type { ThreadHistoryAdapter } from "../../adapters/thread-history";
8
+
9
+ const makeHistory = (): ThreadHistoryAdapter => ({
10
+ load: async () => ({ messages: [] }),
11
+ append: async () => {},
12
+ });
13
+
14
+ describe("useStableRuntimeAdapters", () => {
15
+ it("keeps the previous bag when a fresh literal is shallow-equal", () => {
16
+ const history = makeHistory();
17
+ const { result, rerender } = renderHook(
18
+ ({ adapters }: { adapters: RuntimeAdapters | null }) =>
19
+ useStableRuntimeAdapters(adapters),
20
+ { initialProps: { adapters: { history } } },
21
+ );
22
+ const first = result.current;
23
+
24
+ rerender({ adapters: { history } });
25
+
26
+ expect(result.current).toBe(first);
27
+ });
28
+
29
+ it("adopts the new bag when a field changes", () => {
30
+ const first = makeHistory();
31
+ const second = makeHistory();
32
+ const { result, rerender } = renderHook(
33
+ ({ adapters }: { adapters: RuntimeAdapters | null }) =>
34
+ useStableRuntimeAdapters(adapters),
35
+ { initialProps: { adapters: { history: first } } },
36
+ );
37
+
38
+ rerender({ adapters: { history: second } });
39
+
40
+ expect(result.current?.history).toBe(second);
41
+ });
42
+
43
+ it("moves between null and a bag", () => {
44
+ const history = makeHistory();
45
+ const { result, rerender } = renderHook(
46
+ ({ adapters }: { adapters: RuntimeAdapters | null }) =>
47
+ useStableRuntimeAdapters(adapters),
48
+ { initialProps: { adapters: null as RuntimeAdapters | null } },
49
+ );
50
+ expect(result.current).toBeNull();
51
+
52
+ rerender({ adapters: { history } });
53
+ expect(result.current?.history).toBe(history);
54
+
55
+ rerender({ adapters: null });
56
+ expect(result.current).toBeNull();
57
+
58
+ rerender({ adapters: {} });
59
+ expect(result.current).not.toBeNull();
60
+ expect(result.current).toEqual({});
61
+ });
62
+ });
@@ -0,0 +1,28 @@
1
+ import { createContext, useContext } from "react";
2
+ import { useContextProvider } from "@assistant-ui/tap";
3
+ import { useShallowStable } from "@assistant-ui/store/internal";
4
+ import type { RuntimeAdapters } from "../../runtimes/remote-thread-list/types";
5
+
6
+ const RuntimeAdaptersContext = createContext<RuntimeAdapters | null>(null);
7
+
8
+ export const useRuntimeAdaptersProvider = <T>(
9
+ adapters: RuntimeAdapters | null,
10
+ fn: () => T,
11
+ ): T => useContextProvider(RuntimeAdaptersContext, adapters, fn);
12
+
13
+ export const useRuntimeAdapters = () => {
14
+ return useContext(RuntimeAdaptersContext);
15
+ };
16
+
17
+ const NO_ADAPTERS: RuntimeAdapters = {};
18
+
19
+ // Both adapter faces absorb a fresh but shallow-equal bag from
20
+ // `unstable_useAdapters`, so a per-render literal does not churn the context.
21
+ export const useStableRuntimeAdapters = (
22
+ adapters: RuntimeAdapters | null | undefined,
23
+ ): RuntimeAdapters | null => {
24
+ const stable = useShallowStable(adapters ?? NO_ADAPTERS);
25
+ return adapters == null ? null : stable;
26
+ };
27
+
28
+ export { RuntimeAdaptersContext };
@@ -42,6 +42,11 @@ export type {
42
42
  };
43
43
 
44
44
  type BaseComposerState = {
45
+ /**
46
+ * Whether the composer can cancel the current run. `true` when the runtime
47
+ * supports cancel and a run is in flight, not merely when cancel is a
48
+ * capability.
49
+ */
45
50
  readonly canCancel: boolean;
46
51
  readonly canSend: boolean;
47
52
  readonly isEditing: boolean;
@@ -114,6 +114,7 @@ export type ThreadListRuntimeCoreBinding = ThreadListRuntimeCore;
114
114
 
115
115
  export class ThreadListRuntimeImpl implements ThreadListRuntime {
116
116
  private _getState;
117
+ private _stateBinding: LazyMemoizeSubject<ThreadListState, object>;
117
118
  private _core: ThreadListRuntimeCoreBinding;
118
119
  private _runtimeFactory: new (
119
120
  binding: ThreadRuntimeCoreBinding,
@@ -136,6 +137,7 @@ export class ThreadListRuntimeImpl implements ThreadListRuntime {
136
137
  });
137
138
 
138
139
  this._getState = stateBinding.getState.bind(stateBinding);
140
+ this._stateBinding = stateBinding;
139
141
 
140
142
  this._mainThreadListItemRuntime = new ThreadListItemRuntimeImpl(
141
143
  new ShallowMemoizeSubject({
@@ -213,7 +215,7 @@ export class ThreadListRuntimeImpl implements ThreadListRuntime {
213
215
  }
214
216
 
215
217
  public subscribe(callback: () => void): Unsubscribe {
216
- return this._core.subscribe(callback);
218
+ return this._stateBinding.subscribe(callback);
217
219
  }
218
220
 
219
221
  private _mainThreadListItemRuntime;
@@ -34,6 +34,7 @@ import type {
34
34
  import type { ThreadListItemState } from "./bindings";
35
35
  import type { AppendMessage, ThreadMessage } from "../../types/message";
36
36
  import type { Unsubscribe } from "../../types/unsubscribe";
37
+ import { isMessageNotSentError } from "../../types/error";
37
38
  import type { RunConfig } from "../../types/message";
38
39
  import { EventSubscriptionSubject } from "../../subscribable/subscribable";
39
40
  import { symbolInnerMessage } from "../utils/external-store-message";
@@ -198,7 +199,7 @@ export type ThreadState = {
198
199
  * reports it directly; the rest fall back to the trailing assistant message.
199
200
  */
200
201
  export const getThreadRuntimeCoreIsRunning = (
201
- runtime: ThreadRuntimeCore,
202
+ runtime: Pick<ThreadRuntimeCore, "isRunning" | "messages">,
202
203
  ): boolean => {
203
204
  if (runtime.isRunning !== undefined) return runtime.isRunning;
204
205
  const lastMessage = runtime.messages.at(-1);
@@ -293,6 +294,13 @@ export type ThreadRuntime = {
293
294
 
294
295
  subscribe(callback: () => void): Unsubscribe;
295
296
  cancelRun(): void;
297
+ /**
298
+ * Notifies the runtime that the adapter discarded its backing session.
299
+ * Clears session-scoped tool-invocation state without run-cancel side
300
+ * effects such as composer draft restoration. Internal API for
301
+ * external-store adapter authors.
302
+ */
303
+ unstable_notifySessionReset(): void;
296
304
  getModelContext(): ModelContext;
297
305
 
298
306
  export(): ExportedMessageRepository;
@@ -338,6 +346,10 @@ export class ThreadRuntimeImpl implements ThreadRuntime {
338
346
  private readonly _threadBinding: ThreadRuntimeCoreBinding & {
339
347
  getStateState(): ThreadState;
340
348
  };
349
+ private readonly _stateBinding: ShallowMemoizeSubject<
350
+ ThreadState,
351
+ ThreadRuntimePath
352
+ >;
341
353
 
342
354
  constructor(
343
355
  threadBinding: ThreadRuntimeCoreBinding,
@@ -360,6 +372,7 @@ export class ThreadRuntimeImpl implements ThreadRuntime {
360
372
  },
361
373
  });
362
374
 
375
+ this._stateBinding = stateBinding;
363
376
  this._threadBinding = {
364
377
  path: threadBinding.path,
365
378
  getState: () => threadBinding.getState(),
@@ -391,6 +404,8 @@ export class ThreadRuntimeImpl implements ThreadRuntime {
391
404
  this.exportExternalState = this.exportExternalState.bind(this);
392
405
  this.startRun = this.startRun.bind(this);
393
406
  this.cancelRun = this.cancelRun.bind(this);
407
+ this.unstable_notifySessionReset =
408
+ this.unstable_notifySessionReset.bind(this);
394
409
  this.stopSpeaking = this.stopSpeaking.bind(this);
395
410
  this.connectVoice = this.connectVoice.bind(this);
396
411
  this.disconnectVoice = this.disconnectVoice.bind(this);
@@ -416,11 +431,17 @@ export class ThreadRuntimeImpl implements ThreadRuntime {
416
431
  }
417
432
 
418
433
  public append(message: CreateAppendMessage) {
419
- this._threadBinding
434
+ const task = this._threadBinding
420
435
  .getState()
421
436
  .append(
422
437
  toAppendMessage(this._threadBinding.getState().messages, message),
423
438
  );
439
+ // An undispatched send is reported to the composer, so it is a control
440
+ // signal rather than a failure to surface; every other rejection keeps
441
+ // reaching the host untouched.
442
+ void Promise.resolve(task).catch((error) => {
443
+ if (!isMessageNotSentError(error)) throw error;
444
+ });
424
445
  }
425
446
 
426
447
  public deleteMessage(messageId: string) {
@@ -428,7 +449,7 @@ export class ThreadRuntimeImpl implements ThreadRuntime {
428
449
  }
429
450
 
430
451
  public subscribe(callback: () => void) {
431
- return this._threadBinding.subscribe(callback);
452
+ return this._stateBinding.subscribe(callback);
432
453
  }
433
454
 
434
455
  public getModelContext() {
@@ -455,6 +476,10 @@ export class ThreadRuntimeImpl implements ThreadRuntime {
455
476
  this._threadBinding.getState().cancelRun();
456
477
  }
457
478
 
479
+ public unstable_notifySessionReset() {
480
+ this._threadBinding.getState().unstable_notifySessionReset();
481
+ }
482
+
458
483
  public stopSpeaking() {
459
484
  return this._threadBinding.getState().stopSpeaking();
460
485
  }