@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
@@ -6,6 +6,7 @@ import {
6
6
  type PendingAttachment,
7
7
  } from "../../types/attachment";
8
8
  import type { MessageRole, AppendMessage } from "../../types/message";
9
+ import { isMessageNotSentError } from "../../types/error";
9
10
  import type { QuoteInfo } from "../../types/quote";
10
11
  import type { Unsubscribe } from "../../types/unsubscribe";
11
12
  import type { RunConfig } from "../../types/message";
@@ -118,15 +119,22 @@ export abstract class BaseComposerRuntimeCore
118
119
  if (this._text === value) return;
119
120
 
120
121
  this._text = value;
121
- if (this._dictation) {
122
- this._dictationBaseText = value;
123
- this._currentInterimText = "";
124
- const { status, inputDisabled } = this._dictation;
125
- this._dictation = inputDisabled ? { status, inputDisabled } : { status };
126
- }
122
+ this._rebaseDictation(value);
127
123
  this._notifySubscribers();
128
124
  }
129
125
 
126
+ // A live dictation session appends to the text it last saw, so any write
127
+ // that bypasses `setText` has to move that baseline or the next transcript
128
+ // overwrites what was just written.
129
+ private _rebaseDictation(value: string) {
130
+ if (!this._dictation) return;
131
+
132
+ this._dictationBaseText = value;
133
+ this._currentInterimText = "";
134
+ const { status, inputDisabled } = this._dictation;
135
+ this._dictation = inputDisabled ? { status, inputDisabled } : { status };
136
+ }
137
+
130
138
  public setRole(role: MessageRole) {
131
139
  if (this._role === role) return;
132
140
 
@@ -232,6 +240,8 @@ export abstract class BaseComposerRuntimeCore
232
240
  const originalAttachments = this.attachments;
233
241
  const text = this.text;
234
242
  const quote = this._quote;
243
+ const role = this.role;
244
+ const runConfig = this.runConfig;
235
245
  this._quote = undefined;
236
246
  this._text = "";
237
247
  this._isSending = true;
@@ -245,6 +255,7 @@ export abstract class BaseComposerRuntimeCore
245
255
  if (generation === this._sendGeneration) {
246
256
  if (!this.text.trim() && this._quote === undefined) {
247
257
  this._text = text;
258
+ this._rebaseDictation(text);
248
259
  this._quote = quote;
249
260
  this._notifySubscribers();
250
261
  }
@@ -282,18 +293,101 @@ export abstract class BaseComposerRuntimeCore
282
293
 
283
294
  const message: Omit<AppendMessage, "parentId" | "sourceId"> = {
284
295
  createdAt: new Date(),
285
- role: this.role,
296
+ role,
286
297
  content: text ? [{ type: "text", text }] : [],
287
298
  attachments: finalAttachments,
288
- runConfig: this.runConfig,
299
+ runConfig,
289
300
  metadata: { custom: { ...(quote ? { quote } : {}) } },
290
301
  };
291
302
 
292
- const sendTask = this.handleSend(message, options);
293
- if (sendTask) void sendTask.catch(() => {});
303
+ const draft = { text, quote, attachments: finalAttachments };
304
+ let sendTask: void | Promise<void>;
305
+ try {
306
+ sendTask = this.handleSend(message, options);
307
+ } catch (error) {
308
+ this._restoreUnsentDraft(error, generation, draft);
309
+ throw error;
310
+ }
311
+ if (sendTask)
312
+ void sendTask.catch((error) => {
313
+ this._restoreUnsentDraft(error, generation, draft);
314
+ });
294
315
  this._notifyEventSubscribers("send", {});
295
316
  }
296
317
 
318
+ /**
319
+ * Take a message back into the composer when it has nowhere else to live:
320
+ * a send the runtime never dispatched, or a message a cancelled run is
321
+ * removing from the thread. Reports whether the composer accepted it, so a
322
+ * caller that is also removing the message can keep it instead of dropping
323
+ * it. Refused, and left untouched, while the composer holds anything of its
324
+ * own.
325
+ */
326
+ public restoreDraft(draft: {
327
+ text: string;
328
+ quote?: QuoteInfo | undefined;
329
+ attachments?: readonly Attachment[] | undefined;
330
+ }): boolean {
331
+ if (
332
+ this._text.trim() ||
333
+ this._quote !== undefined ||
334
+ this._attachments.length > 0
335
+ )
336
+ return false;
337
+
338
+ this._text = draft.text;
339
+ this._rebaseDictation(draft.text);
340
+ this._quote = draft.quote;
341
+ this._attachments = draft.attachments ?? [];
342
+ this._notifySubscribers();
343
+ return true;
344
+ }
345
+
346
+ /**
347
+ * Inverse of `restoreDraft`: clears the composer while it still holds
348
+ * exactly the given draft. A draft the user has edited since is left
349
+ * untouched.
350
+ */
351
+ public retractDraft(draft: {
352
+ text: string;
353
+ quote?: QuoteInfo | undefined;
354
+ attachments?: readonly Attachment[] | undefined;
355
+ }): void {
356
+ const attachmentsUntouched =
357
+ draft.attachments !== undefined
358
+ ? this._attachments === draft.attachments
359
+ : this._attachments.length === 0;
360
+ if (
361
+ this._text !== draft.text ||
362
+ this._quote !== draft.quote ||
363
+ !attachmentsUntouched
364
+ )
365
+ return;
366
+
367
+ this._text = "";
368
+ this._rebaseDictation("");
369
+ this._quote = undefined;
370
+ this._attachments = [];
371
+ this._notifySubscribers();
372
+ }
373
+
374
+ // The generation check is what a reset and a later send use to invalidate a
375
+ // draft, so of several queued drafts only the most recent one is still
376
+ // restorable.
377
+ private _restoreUnsentDraft(
378
+ error: unknown,
379
+ generation: number,
380
+ draft: {
381
+ text: string;
382
+ quote: QuoteInfo | undefined;
383
+ attachments: readonly CompleteAttachment[];
384
+ },
385
+ ) {
386
+ if (!isMessageNotSentError(error)) return;
387
+ if (generation !== this._sendGeneration) return;
388
+ this.restoreDraft(draft);
389
+ }
390
+
297
391
  public cancel() {
298
392
  this.handleCancel();
299
393
  }
@@ -626,9 +720,8 @@ export abstract class BaseComposerRuntimeCore
626
720
 
627
721
  const session = this._dictationSession;
628
722
  const sessionId = this._activeDictationSessionId;
629
- session.stop().finally(() => {
630
- this._cleanupDictation({ sessionId });
631
- });
723
+ const cleanup = () => this._cleanupDictation({ sessionId });
724
+ void session.stop().then(cleanup, cleanup);
632
725
  }
633
726
 
634
727
  private _cleanupDictation(options?: { sessionId: number | undefined }): void {
@@ -0,0 +1,191 @@
1
+ import { afterEach, describe, expect, it, vi } from "vitest";
2
+ import type { RealtimeVoiceAdapter } from "../../adapters/voice";
3
+ import type { AppendMessage } from "../../types/message";
4
+ import type {
5
+ AddToolResultOptions,
6
+ ResumeRunConfig,
7
+ ResumeToolCallOptions,
8
+ RespondToToolApprovalOptions,
9
+ RuntimeCapabilities,
10
+ StartRunConfig,
11
+ ThreadSuggestion,
12
+ } from "../interfaces/thread-runtime-core";
13
+ import { BaseThreadRuntimeCore } from "./base-thread-runtime-core";
14
+
15
+ const createVoiceAdapter = () => {
16
+ let volumeCallback: ((volume: number) => void) | undefined;
17
+ const session: RealtimeVoiceAdapter.Session = {
18
+ status: { type: "running" },
19
+ isMuted: false,
20
+ disconnect: vi.fn(),
21
+ mute: vi.fn(),
22
+ unmute: vi.fn(),
23
+ onStatusChange: () => () => {},
24
+ onTranscript: () => () => {},
25
+ onModeChange: () => () => {},
26
+ onVolumeChange: (callback) => {
27
+ volumeCallback = callback;
28
+ return () => {
29
+ volumeCallback = undefined;
30
+ };
31
+ },
32
+ };
33
+
34
+ return {
35
+ adapter: { connect: () => session },
36
+ emitVolume: (volume: number) => volumeCallback?.(volume),
37
+ session,
38
+ } satisfies {
39
+ adapter: RealtimeVoiceAdapter;
40
+ emitVolume: (volume: number) => void;
41
+ session: RealtimeVoiceAdapter.Session;
42
+ };
43
+ };
44
+
45
+ class TestRuntime extends BaseThreadRuntimeCore {
46
+ private readonly voiceAdapter: ReturnType<typeof createVoiceAdapter>;
47
+
48
+ constructor(voiceAdapter: ReturnType<typeof createVoiceAdapter>) {
49
+ super({ getModelContext: () => ({}) });
50
+ this.voiceAdapter = voiceAdapter;
51
+ }
52
+
53
+ get adapters() {
54
+ return { voice: this.voiceAdapter.adapter };
55
+ }
56
+
57
+ get isDisabled() {
58
+ return false;
59
+ }
60
+
61
+ get isSendDisabled() {
62
+ return false;
63
+ }
64
+
65
+ get isLoading() {
66
+ return false;
67
+ }
68
+
69
+ get suggestions(): readonly ThreadSuggestion[] {
70
+ return [];
71
+ }
72
+
73
+ get extras() {
74
+ return undefined;
75
+ }
76
+
77
+ get capabilities(): RuntimeCapabilities {
78
+ return {
79
+ switchToBranch: false,
80
+ switchBranchDuringRun: false,
81
+ edit: false,
82
+ delete: false,
83
+ reload: false,
84
+ refetchThread: false,
85
+ cancel: false,
86
+ unstable_copy: false,
87
+ speech: false,
88
+ dictation: false,
89
+ voice: true,
90
+ attachments: false,
91
+ feedback: false,
92
+ queue: false,
93
+ };
94
+ }
95
+
96
+ append(_message: AppendMessage) {}
97
+ deleteMessage(_messageId: string) {}
98
+ startRun(_config: StartRunConfig) {}
99
+ resumeRun(_config: ResumeRunConfig) {}
100
+ addToolResult(_options: AddToolResultOptions) {}
101
+ resumeToolCall(_options: ResumeToolCallOptions) {}
102
+ respondToToolApproval(_options: RespondToToolApprovalOptions) {}
103
+ cancelRun() {}
104
+ exportExternalState() {
105
+ return {};
106
+ }
107
+ importExternalState(_state: unknown) {}
108
+ unstable_notifySessionReset() {}
109
+ }
110
+
111
+ afterEach(() => {
112
+ vi.restoreAllMocks();
113
+ });
114
+
115
+ describe("BaseThreadRuntimeCore voice volume subscriptions", () => {
116
+ it("continues notifying subscribers when one throws", () => {
117
+ const consoleError = vi
118
+ .spyOn(console, "error")
119
+ .mockImplementation(() => {});
120
+ const voice = createVoiceAdapter();
121
+ const runtime = new TestRuntime(voice);
122
+ runtime.connectVoice();
123
+
124
+ const listenerError = new Error("listener failed");
125
+ const laterListener = vi.fn();
126
+ runtime.subscribeVoiceVolume(() => {
127
+ throw listenerError;
128
+ });
129
+ runtime.subscribeVoiceVolume(laterListener);
130
+
131
+ expect(() => voice.emitVolume(0.5)).not.toThrow();
132
+ expect(laterListener).toHaveBeenCalledOnce();
133
+ expect(runtime.getVoiceVolume()).toBe(0.5);
134
+ expect(consoleError).toHaveBeenCalledWith(
135
+ "[assistant-ui] Voice volume listener threw an error",
136
+ listenerError,
137
+ );
138
+ });
139
+
140
+ it("continues disconnect cleanup when a subscriber throws", () => {
141
+ const consoleError = vi
142
+ .spyOn(console, "error")
143
+ .mockImplementation(() => {});
144
+ const voice = createVoiceAdapter();
145
+ const runtime = new TestRuntime(voice);
146
+ runtime.connectVoice();
147
+
148
+ const listenerError = new Error("disconnect listener failed");
149
+ const laterListener = vi.fn();
150
+ runtime.subscribeVoiceVolume(() => {
151
+ throw listenerError;
152
+ });
153
+ runtime.subscribeVoiceVolume(laterListener);
154
+
155
+ expect(() => runtime.disconnectVoice()).not.toThrow();
156
+ expect(laterListener).toHaveBeenCalledOnce();
157
+ expect(runtime.getVoiceVolume()).toBe(0);
158
+ expect(runtime.voice).toBeUndefined();
159
+ expect(voice.session.disconnect).toHaveBeenCalledOnce();
160
+ expect(consoleError).toHaveBeenCalledWith(
161
+ "[assistant-ui] Voice volume listener threw an error",
162
+ listenerError,
163
+ );
164
+ });
165
+
166
+ it("reports rejected subscriber thenables", async () => {
167
+ const consoleError = vi
168
+ .spyOn(console, "error")
169
+ .mockImplementation(() => {});
170
+ const voice = createVoiceAdapter();
171
+ const runtime = new TestRuntime(voice);
172
+ runtime.connectVoice();
173
+
174
+ const listenerError = new Error("async listener failed");
175
+ const laterListener = vi.fn();
176
+ runtime.subscribeVoiceVolume(async () => {
177
+ throw listenerError;
178
+ });
179
+ runtime.subscribeVoiceVolume(laterListener);
180
+
181
+ voice.emitVolume(0.5);
182
+
183
+ await vi.waitFor(() => {
184
+ expect(laterListener).toHaveBeenCalledOnce();
185
+ expect(consoleError).toHaveBeenCalledWith(
186
+ "[assistant-ui] Voice volume listener threw an error",
187
+ listenerError,
188
+ );
189
+ });
190
+ });
191
+ });
@@ -35,6 +35,7 @@ import type { AttachmentAdapter } from "../../adapters/attachment";
35
35
  import type { RealtimeVoiceAdapter } from "../../adapters/voice";
36
36
  import type { ThreadMessageLike } from "../utils/thread-message-like";
37
37
  import { notifyEventListeners } from "../../utils/notify-event-listeners";
38
+ import { gateInteractableComposerMetadata } from "../../model-context/interactable-composer-metadata";
38
39
 
39
40
  type BaseThreadAdapters = {
40
41
  speech?: SpeechSynthesisAdapter | undefined;
@@ -68,6 +69,7 @@ export abstract class BaseThreadRuntimeCore implements ThreadRuntimeCore {
68
69
  public abstract cancelRun(): void;
69
70
  public abstract exportExternalState(): any;
70
71
  public abstract importExternalState(state: any): void;
72
+ public abstract unstable_notifySessionReset(): void;
71
73
 
72
74
  protected _voiceMessages: ThreadMessage[] = [];
73
75
  protected _voiceGeneration = 0;
@@ -123,6 +125,41 @@ export abstract class BaseThreadRuntimeCore implements ThreadRuntimeCore {
123
125
  return this._contextProvider.getModelContext();
124
126
  }
125
127
 
128
+ /**
129
+ * Stamps provider-contributed composer metadata onto an outgoing message.
130
+ * Called at dispatch rather than in the composer, so programmatic sends are
131
+ * covered too, and exactly once per message: a queued send is stamped when
132
+ * it leaves the lane, never when it enters.
133
+ *
134
+ * Only user messages are stamped, matching the readers: both the version
135
+ * fold and the model injection skip every other role.
136
+ *
137
+ * @param anchorId Message the gated branch prefix ends at. A queued send
138
+ * passes the current tail, having waited through a run that grew the prefix
139
+ * past the parent it was created with.
140
+ */
141
+ protected enrichAppendMetadata(
142
+ message: AppendMessage,
143
+ anchorId: string | null = message.parentId,
144
+ ): AppendMessage {
145
+ if (message.role !== "user") return message;
146
+ const messages = this.messages;
147
+ const parentIndex =
148
+ anchorId === null ? -1 : messages.findIndex((m) => m.id === anchorId);
149
+ const composerMetadata = gateInteractableComposerMetadata(
150
+ this.getModelContext().unstable_composerMetadata,
151
+ messages.slice(0, parentIndex + 1),
152
+ );
153
+ if (!composerMetadata) return message;
154
+ return {
155
+ ...message,
156
+ metadata: {
157
+ ...message.metadata,
158
+ custom: { ...message.metadata?.custom, ...composerMetadata },
159
+ },
160
+ };
161
+ }
162
+
126
163
  private _editComposers = new Map<string, DefaultEditComposerRuntimeCore>();
127
164
  public getEditComposer(messageId: string) {
128
165
  return this._editComposers.get(messageId);
@@ -319,7 +356,11 @@ export abstract class BaseThreadRuntimeCore implements ThreadRuntimeCore {
319
356
  unsubs.push(
320
357
  session.onVolumeChange((volume) => {
321
358
  this._voiceVolume = volume;
322
- for (const cb of this._voiceVolumeSubscribers) cb();
359
+ notifyEventListeners(
360
+ this._voiceVolumeSubscribers,
361
+ undefined,
362
+ "Voice volume",
363
+ );
323
364
  }),
324
365
  );
325
366
 
@@ -418,7 +459,11 @@ export abstract class BaseThreadRuntimeCore implements ThreadRuntimeCore {
418
459
  this._voiceSession = undefined;
419
460
  this.voice = undefined;
420
461
  this._voiceVolume = 0;
421
- for (const cb of this._voiceVolumeSubscribers) cb();
462
+ notifyEventListeners(
463
+ this._voiceVolumeSubscribers,
464
+ undefined,
465
+ "Voice volume",
466
+ );
422
467
  this._voiceMessages = [];
423
468
  this._markVoiceMessagesDirty();
424
469
  this._notifySubscribers();
@@ -7,7 +7,6 @@ import type { DictationAdapter } from "../../adapters/speech";
7
7
  import type { SendOptions } from "../interfaces/composer-runtime-core";
8
8
  import type { ThreadRuntimeCore } from "../interfaces/thread-runtime-core";
9
9
  import { BaseComposerRuntimeCore } from "./base-composer-runtime-core";
10
- import { gateInteractableComposerMetadata } from "../../model-context/interactable-composer-metadata";
11
10
 
12
11
  export class DefaultEditComposerRuntimeCore extends BaseComposerRuntimeCore {
13
12
  public get canCancel() {
@@ -97,21 +96,8 @@ export class DefaultEditComposerRuntimeCore extends BaseComposerRuntimeCore {
97
96
  ...this._nonTextPassthrough,
98
97
  ] as AppendMessage["content"])
99
98
  : message.content;
100
- // Gate live state against the new branch's prefix (messages up to the
101
- // parent): an unchanged interactable re-stamps the prior baseline, an
102
- // interactable edited since the original message stamps its newest state.
103
- const messages = this.runtime.messages;
104
- const parentIndex =
105
- this._parentId === null
106
- ? -1
107
- : messages.findIndex((m) => m.id === this._parentId);
108
- const composerMetadata = gateInteractableComposerMetadata(
109
- this.runtime.getModelContext().unstable_composerMetadata,
110
- messages.slice(0, parentIndex + 1),
111
- );
112
- const enriched = this.enrichWithComposerMetadata(message, composerMetadata);
113
99
  const appendTask = this.runtime.append({
114
- ...enriched,
100
+ ...message,
115
101
  content,
116
102
  parentId: this._parentId,
117
103
  sourceId: this._sourceId,
@@ -6,21 +6,26 @@ import type {
6
6
  ThreadComposerRuntimeCore,
7
7
  } from "../interfaces/composer-runtime-core";
8
8
  import type { ThreadRuntimeCore } from "../interfaces/thread-runtime-core";
9
+ import { getThreadRuntimeCoreIsRunning } from "../api/thread-runtime";
9
10
  import type { QueuePlacement } from "../queue/external-thread-queue-adapter";
10
11
  import {
11
12
  EMPTY_QUEUE_ITEMS,
12
13
  type QueueItemState,
13
14
  } from "../../store/scopes/queue-item";
14
15
  import { BaseComposerRuntimeCore } from "./base-composer-runtime-core";
15
- import { gateInteractableComposerMetadata } from "../../model-context/interactable-composer-metadata";
16
+
17
+ const isCancelable = (runtime: Omit<ThreadRuntimeCore, "composer">) => {
18
+ // capabilities is a subclass field assigned after this composer is constructed
19
+ if (!runtime.capabilities?.cancel) return false;
20
+ return getThreadRuntimeCoreIsRunning(runtime);
21
+ };
16
22
 
17
23
  export class DefaultThreadComposerRuntimeCore
18
24
  extends BaseComposerRuntimeCore
19
25
  implements ThreadComposerRuntimeCore
20
26
  {
21
- private _canCancel = false;
22
27
  public get canCancel() {
23
- return this._canCancel;
28
+ return isCancelable(this.runtime);
24
29
  }
25
30
 
26
31
  public get canSend() {
@@ -95,12 +100,14 @@ export class DefaultThreadComposerRuntimeCore
95
100
  }
96
101
 
97
102
  public connect() {
103
+ let lastCanCancel = false;
98
104
  let lastIsSendDisabled = this.runtime.isSendDisabled;
99
105
  let lastQueue = this.queue;
100
106
  return this.runtime.subscribe(() => {
101
107
  let changed = false;
102
- if (this.canCancel !== this.runtime.capabilities.cancel) {
103
- this._canCancel = this.runtime.capabilities.cancel;
108
+ const nextCanCancel = this.canCancel;
109
+ if (lastCanCancel !== nextCanCancel) {
110
+ lastCanCancel = nextCanCancel;
104
111
  changed = true;
105
112
  }
106
113
  if (lastIsSendDisabled !== this.runtime.isSendDisabled) {
@@ -119,17 +126,8 @@ export class DefaultThreadComposerRuntimeCore
119
126
  message: Omit<AppendMessage, "parentId" | "sourceId">,
120
127
  options?: SendOptions,
121
128
  ) {
122
- // Merge provider-contributed metadata onto the outgoing user message
123
- // (same metadata.custom append path quotes ride). The interactables gate
124
- // runs here because it needs thread history, unavailable to the provider.
125
- const composerMetadata = gateInteractableComposerMetadata(
126
- this.runtime.getModelContext().unstable_composerMetadata,
127
- this.runtime.messages,
128
- );
129
- const enriched = this.enrichWithComposerMetadata(message, composerMetadata);
130
-
131
129
  return this.runtime.append({
132
- ...(enriched as AppendMessage),
130
+ ...(message as AppendMessage),
133
131
  parentId: this.runtime.messages.at(-1)?.id ?? null,
134
132
  sourceId: null,
135
133
  startRun: options?.startRun,
@@ -72,6 +72,11 @@ export type SubmitFeedbackOptions = {
72
72
  };
73
73
 
74
74
  export type ThreadSuggestion = {
75
+ /** Display heading for the suggestion. Falls back to the prompt when absent. */
76
+ title?: string;
77
+ /** Secondary display text shown alongside the title. */
78
+ label?: string;
79
+ /** The message text sent when the suggestion is selected. */
75
80
  prompt: string;
76
81
  };
77
82
 
@@ -156,6 +161,7 @@ export type ThreadRuntimeCore = Readonly<{
156
161
  startRun: (config: StartRunConfig) => void;
157
162
  resumeRun: (config: ResumeRunConfig) => void;
158
163
  cancelRun: () => void;
164
+ unstable_notifySessionReset: () => void;
159
165
 
160
166
  addToolResult: (options: AddToolResultOptions) => void;
161
167
  resumeToolCall: (options: ResumeToolCallOptions) => void;
@@ -27,4 +27,21 @@ export type ExternalThreadQueueAdapter = {
27
27
  move: (queueItemId: string, placement: QueuePlacement) => void;
28
28
  edit: (queueItemId: string, message: AppendMessage) => void;
29
29
  remove: (queueItemId: string) => void;
30
+ /**
31
+ * Installed by the owning runtime, applied to a message as it leaves a lane.
32
+ * A message can wait through an entire run, so state the runtime attaches at
33
+ * send time is recomputed against the thread it actually lands on. Adapters
34
+ * from `createMessageQueue` provide this; a hand-rolled adapter that omits
35
+ * it dispatches whatever was enqueued.
36
+ */
37
+ __internal_setDispatchTransform?:
38
+ | ((transform: (message: AppendMessage) => AppendMessage) => void)
39
+ | undefined;
40
+ /**
41
+ * Called by the owning runtime when the user cancels the run, before the run
42
+ * is aborted. Pauses the queue so the cancelled run's settle keeps the
43
+ * pending items instead of dispatching the next one; the next send re-arms
44
+ * draining. A hand-rolled adapter that omits it drains on cancel.
45
+ */
46
+ __internal_notifyCancelled?: (() => void) | undefined;
30
47
  };
@@ -8,6 +8,7 @@ import {
8
8
  type QueueItemState,
9
9
  } from "../../store/scopes/queue-item";
10
10
  import { generateId } from "../../utils/id";
11
+ import { notifyEventListeners } from "../../utils/notify-event-listeners";
11
12
  import { getThreadMessageText } from "../../utils/text";
12
13
  import type {
13
14
  ExternalThreadQueueAdapter,
@@ -79,13 +80,15 @@ export const createMessageQueue = (
79
80
 
80
81
  let running = false;
81
82
  let paused = false;
83
+ let dispatchTransform: (message: AppendMessage) => AppendMessage = (m) => m;
82
84
  // swallow the cancelled run's settle when steering so it does not double-advance
83
85
  let suppressIdle = 0;
84
86
  // settles from cancelled runs that must drop `running` without advancing
85
87
  let cancelSettles = 0;
88
+ let interrupting = false;
86
89
 
87
90
  const notify = () => {
88
- for (const callback of subscribers) callback();
91
+ notifyEventListeners(subscribers, undefined, "Message queue");
89
92
  };
90
93
 
91
94
  const setLanes = (next: Record<Lane, readonly QueueItemState[]>) => {
@@ -117,7 +120,7 @@ export const createMessageQueue = (
117
120
  setLanes({ ...lanes, [lane]: lanes[lane].slice(1) });
118
121
  if (!message) return;
119
122
  running = true;
120
- driver.run(message, { steer: false });
123
+ driver.run(dispatchTransform(message), { steer: false });
121
124
  };
122
125
 
123
126
  const interrupt = (message: AppendMessage) => {
@@ -126,9 +129,17 @@ export const createMessageQueue = (
126
129
  // already cancel-notified
127
130
  suppressIdle += Math.max(cancelSettles, 1);
128
131
  cancelSettles = 0;
129
- driver.cancel!();
132
+ // a driver whose cancel routes through the runtime notifies this queue
133
+ // back; the interrupt already accounted for that settle and is dispatching
134
+ // in its place
135
+ interrupting = true;
136
+ try {
137
+ driver.cancel!();
138
+ } finally {
139
+ interrupting = false;
140
+ }
130
141
  running = true;
131
- driver.run(message, { steer: true });
142
+ driver.run(dispatchTransform(message), { steer: true });
132
143
  };
133
144
 
134
145
  const push = (lane: Lane, message: AppendMessage) => {
@@ -241,6 +252,14 @@ export const createMessageQueue = (
241
252
  });
242
253
  };
243
254
 
255
+ const notifyCancelled = () => {
256
+ if (interrupting) return;
257
+ if (running && cancelSettles === 0) {
258
+ paused = true;
259
+ cancelSettles = 1;
260
+ }
261
+ };
262
+
244
263
  const adapter: ExternalThreadQueueAdapter = {
245
264
  items: lanes.queue,
246
265
  steerItems: lanes.steer,
@@ -249,6 +268,10 @@ export const createMessageQueue = (
249
268
  move,
250
269
  edit,
251
270
  remove,
271
+ __internal_setDispatchTransform: (transform) => {
272
+ dispatchTransform = transform;
273
+ },
274
+ __internal_notifyCancelled: notifyCancelled,
252
275
  };
253
276
 
254
277
  return {
@@ -270,12 +293,7 @@ export const createMessageQueue = (
270
293
  running = false;
271
294
  advance();
272
295
  },
273
- notifyCancelled: () => {
274
- if (running && cancelSettles === 0) {
275
- paused = true;
276
- cancelSettles = 1;
277
- }
278
- },
296
+ notifyCancelled,
279
297
  clear: () => {
280
298
  messages.clear();
281
299
  setLanes({ queue: EMPTY_QUEUE_ITEMS, steer: EMPTY_QUEUE_ITEMS });