@assistant-ui/core 0.3.13 → 0.3.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (396) hide show
  1. package/dist/adapters/speech.d.ts.map +1 -1
  2. package/dist/adapters/speech.js +9 -15
  3. package/dist/adapters/speech.js.map +1 -1
  4. package/dist/adapters/thread-history.d.ts +2 -0
  5. package/dist/adapters/thread-history.d.ts.map +1 -1
  6. package/dist/adapters/voice.d.ts.map +1 -1
  7. package/dist/adapters/voice.js +27 -15
  8. package/dist/adapters/voice.js.map +1 -1
  9. package/dist/index.d.ts +2 -2
  10. package/dist/index.js.map +1 -1
  11. package/dist/internal.d.ts +1 -1
  12. package/dist/internal.js +6 -6
  13. package/dist/model-context/frame/host.d.ts.map +1 -1
  14. package/dist/model-context/frame/host.js +2 -1
  15. package/dist/model-context/frame/host.js.map +1 -1
  16. package/dist/model-context/registry.d.ts.map +1 -1
  17. package/dist/model-context/registry.js +16 -4
  18. package/dist/model-context/registry.js.map +1 -1
  19. package/dist/react/RuntimeAdapter.d.ts +8 -1
  20. package/dist/react/RuntimeAdapter.d.ts.map +1 -1
  21. package/dist/react/RuntimeAdapter.js +23 -18
  22. package/dist/react/RuntimeAdapter.js.map +1 -1
  23. package/dist/react/adapters/LocalStorageThreadListAdapter.d.ts.map +1 -1
  24. package/dist/react/adapters/LocalStorageThreadListAdapter.js +28 -12
  25. package/dist/react/adapters/LocalStorageThreadListAdapter.js.map +1 -1
  26. package/dist/react/client/AdaptedRemoteThread.d.ts +11 -0
  27. package/dist/react/client/AdaptedRemoteThread.d.ts.map +1 -0
  28. package/dist/react/client/AdaptedRemoteThread.js +27 -0
  29. package/dist/react/client/AdaptedRemoteThread.js.map +1 -0
  30. package/dist/react/client/InMemoryThreadList.d.ts +19 -0
  31. package/dist/react/client/InMemoryThreadList.d.ts.map +1 -0
  32. package/dist/react/client/InMemoryThreadList.js +418 -0
  33. package/dist/react/client/InMemoryThreadList.js.map +1 -0
  34. package/dist/react/client/Interactables.d.ts.map +1 -1
  35. package/dist/react/client/Interactables.js +159 -102
  36. package/dist/react/client/Interactables.js.map +1 -1
  37. package/dist/react/client/RemoteThreadList.d.ts +34 -0
  38. package/dist/react/client/RemoteThreadList.d.ts.map +1 -0
  39. package/dist/react/client/RemoteThreadList.js +826 -0
  40. package/dist/react/client/RemoteThreadList.js.map +1 -0
  41. package/dist/react/client/Tools.d.ts.map +1 -1
  42. package/dist/react/client/Tools.js +38 -27
  43. package/dist/react/client/Tools.js.map +1 -1
  44. package/dist/react/index.d.ts +7 -2
  45. package/dist/react/index.js +5 -2
  46. package/dist/react/interactables-legacy/Interactables.d.ts.map +1 -1
  47. package/dist/react/interactables-legacy/Interactables.js +145 -110
  48. package/dist/react/interactables-legacy/Interactables.js.map +1 -1
  49. package/dist/react/primitive-hooks/useActionBarCopy.d.ts.map +1 -1
  50. package/dist/react/primitive-hooks/useActionBarCopy.js +13 -18
  51. package/dist/react/primitive-hooks/useActionBarCopy.js.map +1 -1
  52. package/dist/react/primitive-hooks/useActionBarEdit.d.ts.map +1 -1
  53. package/dist/react/primitive-hooks/useActionBarEdit.js +2 -4
  54. package/dist/react/primitive-hooks/useActionBarEdit.js.map +1 -1
  55. package/dist/react/primitive-hooks/useActionBarReload.d.ts.map +1 -1
  56. package/dist/react/primitive-hooks/useActionBarReload.js +2 -4
  57. package/dist/react/primitive-hooks/useActionBarReload.js.map +1 -1
  58. package/dist/react/primitive-hooks/useBranchPickerNext.d.ts.map +1 -1
  59. package/dist/react/primitive-hooks/useBranchPickerNext.js +2 -6
  60. package/dist/react/primitive-hooks/useBranchPickerNext.js.map +1 -1
  61. package/dist/react/primitive-hooks/useBranchPickerPrevious.d.ts.map +1 -1
  62. package/dist/react/primitive-hooks/useBranchPickerPrevious.js +2 -6
  63. package/dist/react/primitive-hooks/useBranchPickerPrevious.js.map +1 -1
  64. package/dist/react/primitive-hooks/useComposerCancel.d.ts.map +1 -1
  65. package/dist/react/primitive-hooks/useComposerCancel.js +2 -4
  66. package/dist/react/primitive-hooks/useComposerCancel.js.map +1 -1
  67. package/dist/react/primitive-hooks/useComposerSend.d.ts.map +1 -1
  68. package/dist/react/primitive-hooks/useComposerSend.js +2 -4
  69. package/dist/react/primitive-hooks/useComposerSend.js.map +1 -1
  70. package/dist/react/primitive-hooks/useSuggestionTrigger.d.ts.map +1 -1
  71. package/dist/react/primitive-hooks/useSuggestionTrigger.js +6 -2
  72. package/dist/react/primitive-hooks/useSuggestionTrigger.js.map +1 -1
  73. package/dist/react/primitives/message/MessageParts.d.ts +13 -0
  74. package/dist/react/primitives/message/MessageParts.d.ts.map +1 -1
  75. package/dist/react/primitives/message/MessageParts.js.map +1 -1
  76. package/dist/react/primitives/thread/ThreadMessages.d.ts.map +1 -1
  77. package/dist/react/primitives/thread/ThreadMessages.js +1 -1
  78. package/dist/react/primitives/thread/ThreadMessages.js.map +1 -1
  79. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.d.ts +22 -10
  80. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.d.ts.map +1 -1
  81. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js +109 -71
  82. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js.map +1 -1
  83. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts +8 -2
  84. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts.map +1 -1
  85. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js +40 -13
  86. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js.map +1 -1
  87. package/dist/react/runtimes/RemoteThreadResource.d.ts +30 -0
  88. package/dist/react/runtimes/RemoteThreadResource.d.ts.map +1 -0
  89. package/dist/react/runtimes/RemoteThreadResource.js +134 -0
  90. package/dist/react/runtimes/RemoteThreadResource.js.map +1 -0
  91. package/dist/react/runtimes/RuntimeAdapterProvider.d.ts +3 -10
  92. package/dist/react/runtimes/RuntimeAdapterProvider.d.ts.map +1 -1
  93. package/dist/react/runtimes/RuntimeAdapterProvider.js +3 -6
  94. package/dist/react/runtimes/RuntimeAdapterProvider.js.map +1 -1
  95. package/dist/react/runtimes/assistant-transport/commandQueue.d.ts +17 -0
  96. package/dist/react/runtimes/assistant-transport/commandQueue.d.ts.map +1 -0
  97. package/dist/react/runtimes/assistant-transport/commandQueue.js +55 -0
  98. package/dist/react/runtimes/assistant-transport/commandQueue.js.map +1 -0
  99. package/dist/react/runtimes/assistant-transport/replayBoundaryStream.d.ts +11 -0
  100. package/dist/react/runtimes/assistant-transport/replayBoundaryStream.d.ts.map +1 -0
  101. package/dist/react/runtimes/assistant-transport/replayBoundaryStream.js +165 -0
  102. package/dist/react/runtimes/assistant-transport/replayBoundaryStream.js.map +1 -0
  103. package/dist/react/runtimes/assistant-transport/runManager.d.ts +15 -0
  104. package/dist/react/runtimes/assistant-transport/runManager.d.ts.map +1 -0
  105. package/dist/react/runtimes/assistant-transport/runManager.js +86 -0
  106. package/dist/react/runtimes/assistant-transport/runManager.js.map +1 -0
  107. package/dist/react/runtimes/assistant-transport/types.d.ts +130 -0
  108. package/dist/react/runtimes/assistant-transport/types.d.ts.map +1 -0
  109. package/dist/react/runtimes/assistant-transport/types.js +0 -0
  110. package/dist/react/runtimes/assistant-transport/useAssistantTransportRuntime.d.ts +14 -0
  111. package/dist/react/runtimes/assistant-transport/useAssistantTransportRuntime.d.ts.map +1 -0
  112. package/dist/react/runtimes/assistant-transport/useAssistantTransportRuntime.js +296 -0
  113. package/dist/react/runtimes/assistant-transport/useAssistantTransportRuntime.js.map +1 -0
  114. package/dist/react/runtimes/assistant-transport/useConvertedState.d.ts +7 -0
  115. package/dist/react/runtimes/assistant-transport/useConvertedState.d.ts.map +1 -0
  116. package/dist/react/runtimes/assistant-transport/useConvertedState.js +25 -0
  117. package/dist/react/runtimes/assistant-transport/useConvertedState.js.map +1 -0
  118. package/dist/react/runtimes/assistant-transport/useLatestRef.d.ts +7 -0
  119. package/dist/react/runtimes/assistant-transport/useLatestRef.d.ts.map +1 -0
  120. package/dist/react/runtimes/assistant-transport/useLatestRef.js +27 -0
  121. package/dist/react/runtimes/assistant-transport/useLatestRef.js.map +1 -0
  122. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.d.ts.map +1 -1
  123. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js +39 -13
  124. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js.map +1 -1
  125. package/dist/react/runtimes/cloud/CloudFileAttachmentAdapter.d.ts +2 -1
  126. package/dist/react/runtimes/cloud/CloudFileAttachmentAdapter.d.ts.map +1 -1
  127. package/dist/react/runtimes/cloud/CloudFileAttachmentAdapter.js +3 -3
  128. package/dist/react/runtimes/cloud/CloudFileAttachmentAdapter.js.map +1 -1
  129. package/dist/react/runtimes/cloud/auiV0.d.ts +4 -0
  130. package/dist/react/runtimes/cloud/auiV0.d.ts.map +1 -1
  131. package/dist/react/runtimes/cloud/auiV0.js +7 -0
  132. package/dist/react/runtimes/cloud/auiV0.js.map +1 -1
  133. package/dist/react/runtimes/cloud/useCloudThreadListAdapter.d.ts.map +1 -1
  134. package/dist/react/runtimes/cloud/useCloudThreadListAdapter.js +22 -12
  135. package/dist/react/runtimes/cloud/useCloudThreadListAdapter.js.map +1 -1
  136. package/dist/react/runtimes/cloud/useCloudThreadListRuntime.d.ts +16 -0
  137. package/dist/react/runtimes/cloud/useCloudThreadListRuntime.d.ts.map +1 -0
  138. package/dist/react/runtimes/cloud/useCloudThreadListRuntime.js +36 -0
  139. package/dist/react/runtimes/cloud/useCloudThreadListRuntime.js.map +1 -0
  140. package/dist/react/runtimes/external-message-converter.js +14 -14
  141. package/dist/react/runtimes/external-message-converter.js.map +1 -1
  142. package/dist/react/runtimes/useExternalStoreRuntime.d.ts.map +1 -1
  143. package/dist/react/runtimes/useExternalStoreRuntime.js +44 -28
  144. package/dist/react/runtimes/useExternalStoreRuntime.js.map +1 -1
  145. package/dist/react/runtimes/useExternalStoreSharedOptions.js.map +1 -1
  146. package/dist/react/runtimes/useLocalRuntime.js +1 -1
  147. package/dist/react/runtimes/useRuntimeAdapters.d.ts +9 -0
  148. package/dist/react/runtimes/useRuntimeAdapters.d.ts.map +1 -0
  149. package/dist/react/runtimes/useRuntimeAdapters.js +20 -0
  150. package/dist/react/runtimes/useRuntimeAdapters.js.map +1 -0
  151. package/dist/runtime/api/composer-runtime.d.ts +5 -0
  152. package/dist/runtime/api/composer-runtime.d.ts.map +1 -1
  153. package/dist/runtime/api/composer-runtime.js.map +1 -1
  154. package/dist/runtime/api/message-runtime.js +1 -1
  155. package/dist/runtime/api/thread-list-runtime.d.ts +1 -0
  156. package/dist/runtime/api/thread-list-runtime.d.ts.map +1 -1
  157. package/dist/runtime/api/thread-list-runtime.js +4 -2
  158. package/dist/runtime/api/thread-list-runtime.js.map +1 -1
  159. package/dist/runtime/api/thread-runtime.d.ts +12 -2
  160. package/dist/runtime/api/thread-runtime.d.ts.map +1 -1
  161. package/dist/runtime/api/thread-runtime.js +8 -2
  162. package/dist/runtime/api/thread-runtime.js.map +1 -1
  163. package/dist/runtime/base/base-composer-runtime-core.d.ts.map +1 -1
  164. package/dist/runtime/base/base-composer-runtime-core.js +8 -7
  165. package/dist/runtime/base/base-composer-runtime-core.js.map +1 -1
  166. package/dist/runtime/base/base-thread-runtime-core.d.ts +1 -0
  167. package/dist/runtime/base/base-thread-runtime-core.d.ts.map +1 -1
  168. package/dist/runtime/base/base-thread-runtime-core.js +3 -3
  169. package/dist/runtime/base/base-thread-runtime-core.js.map +1 -1
  170. package/dist/runtime/base/default-thread-composer-runtime-core.d.ts +0 -1
  171. package/dist/runtime/base/default-thread-composer-runtime-core.d.ts.map +1 -1
  172. package/dist/runtime/base/default-thread-composer-runtime-core.js +10 -4
  173. package/dist/runtime/base/default-thread-composer-runtime-core.js.map +1 -1
  174. package/dist/runtime/interfaces/thread-runtime-core.d.ts +6 -0
  175. package/dist/runtime/interfaces/thread-runtime-core.d.ts.map +1 -1
  176. package/dist/runtime/internal.js +5 -5
  177. package/dist/runtime/queue/message-queue.d.ts.map +1 -1
  178. package/dist/runtime/queue/message-queue.js +2 -1
  179. package/dist/runtime/queue/message-queue.js.map +1 -1
  180. package/dist/runtime/utils/thread-runtime-lifecycle.d.ts +8 -0
  181. package/dist/runtime/utils/thread-runtime-lifecycle.d.ts.map +1 -0
  182. package/dist/runtime/utils/thread-runtime-lifecycle.js +11 -0
  183. package/dist/runtime/utils/thread-runtime-lifecycle.js.map +1 -0
  184. package/dist/runtimes/external-store/external-store-shared-options.js.map +1 -1
  185. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.d.ts.map +1 -1
  186. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.js +4 -1
  187. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.js.map +1 -1
  188. package/dist/runtimes/external-store/external-store-thread-runtime-core.d.ts +8 -0
  189. package/dist/runtimes/external-store/external-store-thread-runtime-core.d.ts.map +1 -1
  190. package/dist/runtimes/external-store/external-store-thread-runtime-core.js +35 -13
  191. package/dist/runtimes/external-store/external-store-thread-runtime-core.js.map +1 -1
  192. package/dist/runtimes/internal.d.ts +1 -1
  193. package/dist/runtimes/local/local-thread-runtime-core.d.ts +3 -0
  194. package/dist/runtimes/local/local-thread-runtime-core.d.ts.map +1 -1
  195. package/dist/runtimes/local/local-thread-runtime-core.js +53 -13
  196. package/dist/runtimes/local/local-thread-runtime-core.js.map +1 -1
  197. package/dist/runtimes/readonly/ReadonlyThreadRuntimeCore.d.ts +1 -0
  198. package/dist/runtimes/readonly/ReadonlyThreadRuntimeCore.d.ts.map +1 -1
  199. package/dist/runtimes/readonly/ReadonlyThreadRuntimeCore.js +3 -0
  200. package/dist/runtimes/readonly/ReadonlyThreadRuntimeCore.js.map +1 -1
  201. package/dist/runtimes/remote-thread-list/empty-thread-core.js +3 -0
  202. package/dist/runtimes/remote-thread-list/empty-thread-core.js.map +1 -1
  203. package/dist/runtimes/remote-thread-list/types.d.ts +29 -1
  204. package/dist/runtimes/remote-thread-list/types.d.ts.map +1 -1
  205. package/dist/runtimes/tool-invocations/ToolInvocationTracker.d.ts +1 -12
  206. package/dist/runtimes/tool-invocations/ToolInvocationTracker.d.ts.map +1 -1
  207. package/dist/runtimes/tool-invocations/ToolInvocationTracker.js +67 -38
  208. package/dist/runtimes/tool-invocations/ToolInvocationTracker.js.map +1 -1
  209. package/dist/store/clients/suggestions.js +2 -2
  210. package/dist/store/clients/suggestions.js.map +1 -1
  211. package/dist/store/clients/thread-selection-events.d.ts +10 -0
  212. package/dist/store/clients/thread-selection-events.d.ts.map +1 -0
  213. package/dist/store/clients/thread-selection-events.js +40 -0
  214. package/dist/store/clients/thread-selection-events.js.map +1 -0
  215. package/dist/store/env.d.ts +5 -0
  216. package/dist/store/env.d.ts.map +1 -0
  217. package/dist/store/env.js +12 -0
  218. package/dist/store/env.js.map +1 -0
  219. package/dist/store/index.d.ts +5 -3
  220. package/dist/store/index.js +4 -2
  221. package/dist/store/internal.d.ts +4 -1
  222. package/dist/store/internal.js +4 -1
  223. package/dist/store/primitive-predicates.d.ts +19 -0
  224. package/dist/store/primitive-predicates.d.ts.map +1 -0
  225. package/dist/store/primitive-predicates.js +19 -0
  226. package/dist/store/primitive-predicates.js.map +1 -0
  227. package/dist/store/runtime-clients/attachment-runtime-client.d.ts.map +1 -1
  228. package/dist/store/runtime-clients/attachment-runtime-client.js +18 -11
  229. package/dist/store/runtime-clients/attachment-runtime-client.js.map +1 -1
  230. package/dist/store/runtime-clients/handle-runtime-action.d.ts +5 -0
  231. package/dist/store/runtime-clients/handle-runtime-action.d.ts.map +1 -0
  232. package/dist/store/runtime-clients/handle-runtime-action.js +12 -0
  233. package/dist/store/runtime-clients/handle-runtime-action.js.map +1 -0
  234. package/dist/store/runtime-clients/handle-thread-list-action.d.ts +5 -0
  235. package/dist/store/runtime-clients/handle-thread-list-action.d.ts.map +1 -0
  236. package/dist/store/runtime-clients/handle-thread-list-action.js +7 -0
  237. package/dist/store/runtime-clients/handle-thread-list-action.js.map +1 -0
  238. package/dist/store/runtime-clients/thread-list-item-runtime-client.d.ts.map +1 -1
  239. package/dist/store/runtime-clients/thread-list-item-runtime-client.js +62 -29
  240. package/dist/store/runtime-clients/thread-list-item-runtime-client.js.map +1 -1
  241. package/dist/store/runtime-clients/thread-list-runtime-client.d.ts.map +1 -1
  242. package/dist/store/runtime-clients/thread-list-runtime-client.js +5 -6
  243. package/dist/store/runtime-clients/thread-list-runtime-client.js.map +1 -1
  244. package/dist/store/scopes/composer.d.ts +9 -4
  245. package/dist/store/scopes/composer.d.ts.map +1 -1
  246. package/dist/store/scopes/thread-list-item.d.ts +7 -6
  247. package/dist/store/scopes/thread-list-item.d.ts.map +1 -1
  248. package/dist/store/scopes/thread.d.ts +8 -7
  249. package/dist/store/scopes/thread.d.ts.map +1 -1
  250. package/dist/store/scopes/threads.d.ts +17 -1
  251. package/dist/store/scopes/threads.d.ts.map +1 -1
  252. package/dist/subscribable/subscribable.d.ts +2 -1
  253. package/dist/subscribable/subscribable.d.ts.map +1 -1
  254. package/dist/subscribable/subscribable.js +23 -18
  255. package/dist/subscribable/subscribable.js.map +1 -1
  256. package/dist/utils/composite-context-provider.d.ts +1 -0
  257. package/dist/utils/composite-context-provider.d.ts.map +1 -1
  258. package/dist/utils/composite-context-provider.js +25 -9
  259. package/dist/utils/composite-context-provider.js.map +1 -1
  260. package/dist/utils/notify-event-listeners.d.ts +2 -2
  261. package/dist/utils/notify-event-listeners.d.ts.map +1 -1
  262. package/dist/utils/notify-event-listeners.js +2 -2
  263. package/dist/utils/notify-event-listeners.js.map +1 -1
  264. package/package.json +7 -7
  265. package/src/adapters/speech.test.ts +118 -1
  266. package/src/adapters/speech.ts +21 -22
  267. package/src/adapters/thread-history.ts +2 -0
  268. package/src/adapters/voice.test.ts +69 -0
  269. package/src/adapters/voice.ts +27 -27
  270. package/src/index.ts +1 -0
  271. package/src/model-context/frame/host.test.ts +23 -0
  272. package/src/model-context/frame/host.ts +2 -1
  273. package/src/model-context/registry.test.ts +131 -0
  274. package/src/model-context/registry.ts +16 -6
  275. package/src/react/RuntimeAdapter.test.ts +2 -0
  276. package/src/react/RuntimeAdapter.ts +20 -8
  277. package/src/react/adapters/LocalStorageThreadListAdapter.tsx +34 -17
  278. package/src/react/client/AdaptedRemoteThread.ts +41 -0
  279. package/src/react/client/InMemoryThreadList.test.tsx +148 -0
  280. package/src/react/client/InMemoryThreadList.ts +290 -0
  281. package/src/react/client/Interactables.test.ts +326 -2
  282. package/src/react/client/Interactables.ts +227 -110
  283. package/src/react/client/RemoteThreadList.test.ts +1026 -0
  284. package/src/react/client/RemoteThreadList.ts +1067 -0
  285. package/src/react/client/Tools.ts +36 -29
  286. package/src/react/index.ts +13 -0
  287. package/src/react/interactables-legacy/Interactables.test.ts +317 -0
  288. package/src/react/interactables-legacy/Interactables.ts +204 -128
  289. package/src/react/primitive-hooks/useActionBarCopy.ts +2 -7
  290. package/src/react/primitive-hooks/useActionBarEdit.ts +2 -1
  291. package/src/react/primitive-hooks/useActionBarReload.ts +2 -6
  292. package/src/react/primitive-hooks/useBranchPickerNext.ts +2 -7
  293. package/src/react/primitive-hooks/useBranchPickerPrevious.ts +2 -7
  294. package/src/react/primitive-hooks/useComposerCancel.ts +2 -1
  295. package/src/react/primitive-hooks/useComposerSend.ts +2 -5
  296. package/src/react/primitive-hooks/useSuggestionTrigger.test.ts +11 -0
  297. package/src/react/primitive-hooks/useSuggestionTrigger.ts +4 -5
  298. package/src/react/primitives/message/MessageParts.tsx +0 -1
  299. package/src/react/primitives/thread/ThreadMessages.tsx +2 -1
  300. package/src/react/runtimes/RemoteThreadListHookInstanceManager.retry.test.tsx +116 -0
  301. package/src/react/runtimes/RemoteThreadListHookInstanceManager.running.test.tsx +1 -1
  302. package/src/react/runtimes/RemoteThreadListHookInstanceManager.test.tsx +57 -6
  303. package/src/react/runtimes/RemoteThreadListHookInstanceManager.title.test.tsx +272 -0
  304. package/src/react/runtimes/RemoteThreadListHookInstanceManager.tsx +159 -124
  305. package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.tsx +59 -20
  306. package/src/react/runtimes/RemoteThreadResource.test.ts +141 -0
  307. package/src/react/runtimes/RemoteThreadResource.ts +231 -0
  308. package/src/react/runtimes/RuntimeAdapterProvider.tsx +8 -20
  309. package/src/react/runtimes/assistant-transport/commandQueue.ts +68 -0
  310. package/src/react/runtimes/assistant-transport/replayBoundaryStream.test.ts +455 -0
  311. package/src/react/runtimes/assistant-transport/replayBoundaryStream.ts +172 -0
  312. package/src/react/runtimes/assistant-transport/runManager.ts +124 -0
  313. package/src/react/runtimes/assistant-transport/transport-delivery.test.tsx +79 -0
  314. package/src/react/runtimes/assistant-transport/transport-parentid.test.tsx +97 -0
  315. package/src/react/runtimes/assistant-transport/transport-scheduling.test.ts +414 -0
  316. package/src/react/runtimes/assistant-transport/types.ts +170 -0
  317. package/src/react/runtimes/assistant-transport/useAssistantTransport.spec.md +141 -0
  318. package/src/react/runtimes/assistant-transport/useAssistantTransportRuntime.test.tsx +625 -0
  319. package/src/react/runtimes/assistant-transport/useAssistantTransportRuntime.ts +481 -0
  320. package/src/react/runtimes/assistant-transport/useConvertedState.ts +20 -0
  321. package/src/react/runtimes/assistant-transport/useLatestRef.ts +9 -0
  322. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.test.tsx +131 -6
  323. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.ts +61 -15
  324. package/src/react/runtimes/cloud/CloudFileAttachmentAdapter.ts +6 -4
  325. package/src/react/runtimes/cloud/auiV0.ts +17 -1
  326. package/src/react/runtimes/cloud/useCloudThreadListAdapter.tsx +36 -21
  327. package/src/react/runtimes/cloud/useCloudThreadListRuntime.test.tsx +56 -0
  328. package/src/react/runtimes/cloud/useCloudThreadListRuntime.ts +31 -0
  329. package/src/react/runtimes/external-message-converter.test.ts +143 -0
  330. package/src/react/runtimes/external-message-converter.ts +41 -41
  331. package/src/react/runtimes/remote-thread-hook-on-tap.test.ts +58 -0
  332. package/src/react/runtimes/useExternalStoreRuntime.lifecycle.test.tsx +83 -0
  333. package/src/react/runtimes/useExternalStoreRuntime.suggestions.test.tsx +13 -3
  334. package/src/react/runtimes/useExternalStoreRuntime.ts +7 -0
  335. package/src/react/runtimes/useExternalStoreSharedOptions.ts +3 -1
  336. package/src/react/runtimes/useLocalRuntime.test.tsx +130 -4
  337. package/src/react/runtimes/useRuntimeAdapters.test.tsx +62 -0
  338. package/src/react/runtimes/useRuntimeAdapters.ts +28 -0
  339. package/src/runtime/api/composer-runtime.ts +5 -0
  340. package/src/runtime/api/thread-list-runtime.ts +3 -1
  341. package/src/runtime/api/thread-runtime.ts +20 -2
  342. package/src/runtime/base/base-composer-runtime-core.ts +6 -5
  343. package/src/runtime/base/base-thread-runtime-core.test.ts +191 -0
  344. package/src/runtime/base/base-thread-runtime-core.ts +11 -2
  345. package/src/runtime/base/default-thread-composer-runtime-core.ts +12 -4
  346. package/src/runtime/interfaces/thread-runtime-core.ts +6 -0
  347. package/src/runtime/queue/message-queue.ts +2 -1
  348. package/src/runtime/utils/thread-runtime-lifecycle.ts +18 -0
  349. package/src/runtimes/external-store/external-store-shared-options.ts +3 -1
  350. package/src/runtimes/external-store/external-store-thread-list-runtime-core.ts +4 -1
  351. package/src/runtimes/external-store/external-store-thread-runtime-core.ts +99 -34
  352. package/src/runtimes/local/local-thread-runtime-core.test.ts +373 -4
  353. package/src/runtimes/local/local-thread-runtime-core.ts +79 -14
  354. package/src/runtimes/readonly/ReadonlyThreadRuntimeCore.ts +4 -0
  355. package/src/runtimes/remote-thread-list/empty-thread-core.ts +4 -0
  356. package/src/runtimes/remote-thread-list/types.ts +30 -0
  357. package/src/runtimes/tool-invocations/EDGE_CASES.md +17 -6
  358. package/src/runtimes/tool-invocations/ToolInvocationTracker.test.ts +243 -0
  359. package/src/runtimes/tool-invocations/ToolInvocationTracker.ts +127 -61
  360. package/src/store/clients/model-context-client.test.ts +16 -8
  361. package/src/store/clients/suggestions.ts +2 -2
  362. package/src/store/clients/thread-selection-events.ts +21 -0
  363. package/src/store/env.ts +11 -0
  364. package/src/store/index.ts +14 -1
  365. package/src/store/internal.ts +13 -0
  366. package/src/store/primitive-predicates.test.ts +184 -0
  367. package/src/store/primitive-predicates.ts +43 -0
  368. package/src/store/runtime-clients/attachment-runtime-client.test.ts +41 -0
  369. package/src/store/runtime-clients/attachment-runtime-client.ts +2 -1
  370. package/src/store/runtime-clients/handle-runtime-action.ts +11 -0
  371. package/src/store/runtime-clients/handle-thread-list-action.test.ts +69 -0
  372. package/src/store/runtime-clients/handle-thread-list-action.ts +6 -0
  373. package/src/store/runtime-clients/thread-list-item-runtime-client.ts +15 -7
  374. package/src/store/runtime-clients/thread-list-runtime-client.ts +9 -6
  375. package/src/store/scopes/composer.ts +9 -4
  376. package/src/store/scopes/thread-list-item.ts +7 -6
  377. package/src/store/scopes/thread.ts +8 -7
  378. package/src/store/scopes/threads.ts +14 -0
  379. package/src/subscribable/subscribable.test.ts +77 -1
  380. package/src/subscribable/subscribable.ts +38 -23
  381. package/src/tests/RemoteThreadListThreadListRuntimeCore-switchToThread-unarchive.test.ts +25 -0
  382. package/src/tests/auiV0Encode.test.ts +66 -0
  383. package/src/tests/base-composer-runtime-core-send.test.ts +30 -0
  384. package/src/tests/base-composer-runtime-core.test.ts +28 -0
  385. package/src/tests/composer-can-cancel.test.ts +101 -0
  386. package/src/tests/external-store-thread-list-runtime-core.test.ts +101 -0
  387. package/src/tests/external-store-thread-runtime-core-adapter.test.ts +191 -1
  388. package/src/tests/external-store-thread-runtime-core.test.ts +124 -0
  389. package/src/tests/interactables-toolui-migration.test.tsx +66 -0
  390. package/src/tests/message-queue.test.ts +28 -0
  391. package/src/tests/session-reset-notification.test.ts +145 -0
  392. package/src/tests/thread-switch-events.test.tsx +99 -1
  393. package/src/tests/tools-scope-migration.test.tsx +58 -0
  394. package/src/utils/composite-context-provider.test.ts +139 -0
  395. package/src/utils/composite-context-provider.ts +26 -8
  396. package/src/utils/notify-event-listeners.ts +10 -5
@@ -1,6 +1,19 @@
1
1
  import { notifyEventListeners } from "../utils/notify-event-listeners.js";
2
2
  //#region src/subscribable/subscribable.ts
3
3
  const SKIP_UPDATE = Symbol("skip-update");
4
+ const notifySubscribers = (subscribers, ...args) => {
5
+ const errors = [];
6
+ for (const callback of subscribers) try {
7
+ callback(...args);
8
+ } catch (error) {
9
+ errors.push(error);
10
+ }
11
+ if (errors.length === 1) throw errors[0];
12
+ if (errors.length > 1) {
13
+ for (const error of errors) console.error(error);
14
+ throw new AggregateError(errors);
15
+ }
16
+ };
4
17
  function shallowEqual(objA, objB) {
5
18
  if (objA === void 0 && objB === void 0) return true;
6
19
  if (objA === void 0) return false;
@@ -29,19 +42,7 @@ var BaseSubscribable = class {
29
42
  });
30
43
  }
31
44
  _notifySubscribers() {
32
- const errors = [];
33
- for (const callback of this._subscribers) try {
34
- callback();
35
- } catch (error) {
36
- errors.push(error);
37
- }
38
- if (errors.length > 0) {
39
- if (errors.length === 1) throw errors[0];
40
- else {
41
- for (const error of errors) console.error(error);
42
- throw new AggregateError(errors);
43
- }
44
- }
45
+ notifySubscribers(this._subscribers);
45
46
  }
46
47
  };
47
48
  var BaseSubject = class {
@@ -55,7 +56,7 @@ var BaseSubject = class {
55
56
  notifyEventListeners(this._subscriptions, payload, errorContext);
56
57
  return;
57
58
  }
58
- for (const callback of this._subscriptions) callback(payload);
59
+ notifySubscribers(this._subscriptions, payload);
59
60
  }
60
61
  _updateConnection() {
61
62
  if (this._subscriptions.size > 0) {
@@ -103,7 +104,9 @@ var ShallowMemoizeSubject = class extends BaseSubject {
103
104
  const callback = () => {
104
105
  if (this._syncState()) this.notifySubscribers();
105
106
  };
106
- return this.binding.subscribe(callback);
107
+ const unsubscribe = this.binding.subscribe(callback);
108
+ this._syncState();
109
+ return unsubscribe;
107
110
  }
108
111
  };
109
112
  var LazyMemoizeSubject = class extends BaseSubject {
@@ -120,7 +123,7 @@ var LazyMemoizeSubject = class extends BaseSubject {
120
123
  getState = () => {
121
124
  if (!this.isConnected || this._previousStateDirty) {
122
125
  const newState = this.binding.getState();
123
- if (newState !== SKIP_UPDATE) this._previousState = newState;
126
+ if (newState !== SKIP_UPDATE && (this._previousState === void 0 || !shallowEqual(newState, this._previousState))) this._previousState = newState;
124
127
  this._previousStateDirty = false;
125
128
  }
126
129
  if (this._previousState === void 0) throw new Error("Entry not available in the store");
@@ -131,7 +134,9 @@ var LazyMemoizeSubject = class extends BaseSubject {
131
134
  this._previousStateDirty = true;
132
135
  this.notifySubscribers();
133
136
  };
134
- return this.binding.subscribe(callback);
137
+ const unsubscribe = this.binding.subscribe(callback);
138
+ this._previousStateDirty = true;
139
+ return unsubscribe;
135
140
  }
136
141
  };
137
142
  var NestedSubscriptionSubject = class extends BaseSubject {
@@ -204,6 +209,6 @@ var EventSubscriptionSubject = class extends BaseSubject {
204
209
  }
205
210
  };
206
211
  //#endregion
207
- export { BaseSubject, BaseSubscribable, EventSubscriptionSubject, LazyMemoizeSubject, NestedSubscriptionSubject, SKIP_UPDATE, ShallowMemoizeSubject };
212
+ export { BaseSubject, BaseSubscribable, EventSubscriptionSubject, LazyMemoizeSubject, NestedSubscriptionSubject, SKIP_UPDATE, ShallowMemoizeSubject, notifySubscribers };
208
213
 
209
214
  //# sourceMappingURL=subscribable.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"subscribable.js","names":["Unsubscribe","notifyEventListeners","SKIP_UPDATE","Symbol","Subscribable","subscribe","callback","SubscribableWithState","path","TPath","getState","TState","NestedSubscribable","EventSubscribable","event","TEvent","binding","unstable_on","payload","shallowEqual","objA","T","objB","undefined","keysA","Object","keys","length","key","valueA","valueB","is","BaseSubscribable","_subscribers","Set","add","delete","waitForUpdate","Promise","resolve","unsubscribe","_notifySubscribers","errors","error","push","console","AggregateError","BaseSubject","_subscriptions","_connection","isConnected","_connect","notifySubscribers","errorContext","_updateConnection","size","ShallowMemoizeSubject","constructor","state","Error","_previousState","_syncState","LazyMemoizeSubject","_previousStateDirty","newState","NestedSubscriptionSubject","outerSubscribe","lastState","innerUnsubscribe","onRuntimeUpdate","outerUnsubscribe","EventSubscriptionSubject","config"],"sources":["../../src/subscribable/subscribable.ts"],"sourcesContent":["import type { Unsubscribe } from \"../types/unsubscribe\";\nimport { notifyEventListeners } from \"../utils/notify-event-listeners\";\n\nexport const SKIP_UPDATE = Symbol(\"skip-update\");\nexport type SKIP_UPDATE = typeof SKIP_UPDATE;\n\nexport type Subscribable = {\n subscribe: (callback: () => void) => Unsubscribe;\n};\n\nexport type SubscribableWithState<TState, TPath> = Subscribable & {\n path: TPath;\n getState: () => TState;\n};\n\nexport type NestedSubscribable<\n TState extends Subscribable | undefined,\n TPath,\n> = SubscribableWithState<TState, TPath>;\n\nexport type EventSubscribable<TEvent extends string> = {\n event: TEvent;\n binding: SubscribableWithState<\n | {\n unstable_on: (\n event: TEvent,\n callback: (payload?: unknown) => void,\n ) => Unsubscribe;\n }\n | undefined,\n unknown\n >;\n};\n\nfunction shallowEqual<T extends object>(\n objA: T | undefined,\n objB: T | undefined,\n) {\n if (objA === undefined && objB === undefined) return true;\n if (objA === undefined) return false;\n if (objB === undefined) return false;\n\n const keysA = Object.keys(objA);\n if (keysA.length !== Object.keys(objB).length) return false;\n\n for (const key of keysA) {\n const valueA = objA[key as keyof T];\n const valueB = objB[key as keyof T];\n if (!Object.is(valueA, valueB)) return false;\n }\n\n return true;\n}\n\nexport class BaseSubscribable {\n private _subscribers = new Set<() => void>();\n\n public subscribe(callback: () => void): Unsubscribe {\n this._subscribers.add(callback);\n return () => this._subscribers.delete(callback);\n }\n\n public waitForUpdate() {\n return new Promise<void>((resolve) => {\n const unsubscribe = this.subscribe(() => {\n unsubscribe();\n resolve();\n });\n });\n }\n\n protected _notifySubscribers() {\n const errors = [];\n for (const callback of this._subscribers) {\n try {\n callback();\n } catch (error) {\n errors.push(error);\n }\n }\n\n if (errors.length > 0) {\n if (errors.length === 1) {\n throw errors[0];\n } else {\n for (const error of errors) {\n console.error(error);\n }\n throw new AggregateError(errors);\n }\n }\n }\n}\n\n// lazy connect/disconnect: only opens upstream subscription while it has subscribers\nexport abstract class BaseSubject {\n private _subscriptions = new Set<(payload?: unknown) => void>();\n private _connection: Unsubscribe | undefined;\n\n protected get isConnected() {\n return !!this._connection;\n }\n\n protected abstract _connect(): Unsubscribe;\n\n protected notifySubscribers(payload?: unknown, errorContext?: string) {\n if (errorContext) {\n notifyEventListeners(this._subscriptions, payload, errorContext);\n return;\n }\n\n for (const callback of this._subscriptions) callback(payload);\n }\n\n private _updateConnection() {\n if (this._subscriptions.size > 0) {\n if (this._connection) return;\n this._connection = this._connect();\n } else {\n this._connection?.();\n this._connection = undefined;\n }\n }\n\n public subscribe(callback: (payload?: unknown) => void) {\n this._subscriptions.add(callback);\n this._updateConnection();\n\n return () => {\n this._subscriptions.delete(callback);\n this._updateConnection();\n };\n }\n}\n\nexport class ShallowMemoizeSubject<TState extends object, TPath>\n extends BaseSubject\n implements SubscribableWithState<TState, TPath>\n{\n public get path() {\n return this.binding.path;\n }\n\n private binding: SubscribableWithState<TState | SKIP_UPDATE, TPath>;\n\n constructor(binding: SubscribableWithState<TState | SKIP_UPDATE, TPath>) {\n super();\n this.binding = binding;\n const state = binding.getState();\n if (state === SKIP_UPDATE)\n throw new Error(\"Entry not available in the store\");\n this._previousState = state;\n }\n\n private _previousState: TState;\n public getState = () => {\n if (!this.isConnected) this._syncState();\n return this._previousState;\n };\n\n private _syncState() {\n const state = this.binding.getState();\n if (state === SKIP_UPDATE) return false;\n if (shallowEqual(state, this._previousState)) return false;\n this._previousState = state;\n return true;\n }\n\n protected _connect() {\n const callback = () => {\n if (this._syncState()) {\n this.notifySubscribers();\n }\n };\n\n return this.binding.subscribe(callback);\n }\n}\n\nexport class LazyMemoizeSubject<TState extends object, TPath>\n extends BaseSubject\n implements SubscribableWithState<TState, TPath>\n{\n public get path() {\n return this.binding.path;\n }\n\n private binding: SubscribableWithState<TState | SKIP_UPDATE, TPath>;\n\n constructor(binding: SubscribableWithState<TState | SKIP_UPDATE, TPath>) {\n super();\n this.binding = binding;\n }\n\n private _previousStateDirty = true;\n private _previousState: TState | undefined;\n public getState = () => {\n if (!this.isConnected || this._previousStateDirty) {\n const newState = this.binding.getState();\n if (newState !== SKIP_UPDATE) {\n this._previousState = newState;\n }\n this._previousStateDirty = false;\n }\n if (this._previousState === undefined)\n throw new Error(\"Entry not available in the store\");\n return this._previousState;\n };\n\n protected _connect() {\n const callback = () => {\n this._previousStateDirty = true;\n this.notifySubscribers();\n };\n\n return this.binding.subscribe(callback);\n }\n}\n\nexport class NestedSubscriptionSubject<\n TState extends Subscribable | undefined,\n TPath,\n>\n extends BaseSubject\n implements\n SubscribableWithState<TState, TPath>,\n NestedSubscribable<TState, TPath>\n{\n public get path() {\n return this.binding.path;\n }\n\n private binding: NestedSubscribable<TState, TPath>;\n\n constructor(binding: NestedSubscribable<TState, TPath>) {\n super();\n this.binding = binding;\n }\n\n public getState() {\n return this.binding.getState();\n }\n\n public outerSubscribe(callback: () => void) {\n return this.binding.subscribe(callback);\n }\n\n protected _connect(): Unsubscribe {\n const callback = () => {\n this.notifySubscribers();\n };\n\n let lastState = this.binding.getState();\n let innerUnsubscribe = lastState?.subscribe(callback);\n const onRuntimeUpdate = () => {\n const newState = this.binding.getState();\n if (newState === lastState) return;\n lastState = newState;\n\n innerUnsubscribe?.();\n innerUnsubscribe = newState?.subscribe(callback);\n\n callback();\n };\n\n const outerUnsubscribe = this.outerSubscribe(onRuntimeUpdate);\n return () => {\n outerUnsubscribe?.();\n innerUnsubscribe?.();\n };\n }\n}\n\nexport class EventSubscriptionSubject<\n TEvent extends string,\n> extends BaseSubject {\n private config: EventSubscribable<TEvent>;\n\n constructor(config: EventSubscribable<TEvent>) {\n super();\n this.config = config;\n }\n\n public getState() {\n return this.config.binding.getState();\n }\n\n public outerSubscribe(callback: () => void) {\n return this.config.binding.subscribe(callback);\n }\n\n protected _connect(): Unsubscribe {\n const errorContext = `Runtime event \"${this.config.event}\"`;\n const callback = (payload?: unknown) => {\n this.notifySubscribers(payload, errorContext);\n };\n\n let lastState = this.config.binding.getState();\n let innerUnsubscribe = lastState?.unstable_on(this.config.event, callback);\n const onRuntimeUpdate = () => {\n const newState = this.config.binding.getState();\n if (newState === lastState) return;\n lastState = newState;\n\n innerUnsubscribe?.();\n innerUnsubscribe = newState?.unstable_on(this.config.event, callback);\n };\n\n const outerUnsubscribe = this.outerSubscribe(onRuntimeUpdate);\n return () => {\n outerUnsubscribe?.();\n innerUnsubscribe?.();\n };\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"subscribable.js","names":["Unsubscribe","notifyEventListeners","SKIP_UPDATE","Symbol","Subscribable","subscribe","callback","SubscribableWithState","path","TPath","getState","TState","NestedSubscribable","EventSubscribable","event","TEvent","binding","unstable_on","payload","notifySubscribers","subscribers","Iterable","args","TArgs","errors","error","push","length","console","AggregateError","shallowEqual","objA","T","objB","undefined","keysA","Object","keys","key","valueA","valueB","is","BaseSubscribable","_subscribers","Set","add","delete","waitForUpdate","Promise","resolve","unsubscribe","_notifySubscribers","BaseSubject","_subscriptions","_connection","isConnected","_connect","errorContext","_updateConnection","size","ShallowMemoizeSubject","constructor","state","Error","_previousState","_syncState","LazyMemoizeSubject","_previousStateDirty","newState","NestedSubscriptionSubject","outerSubscribe","lastState","innerUnsubscribe","onRuntimeUpdate","outerUnsubscribe","EventSubscriptionSubject","config"],"sources":["../../src/subscribable/subscribable.ts"],"sourcesContent":["import type { Unsubscribe } from \"../types/unsubscribe\";\nimport { notifyEventListeners } from \"../utils/notify-event-listeners\";\n\nexport const SKIP_UPDATE = Symbol(\"skip-update\");\nexport type SKIP_UPDATE = typeof SKIP_UPDATE;\n\nexport type Subscribable = {\n subscribe: (callback: () => void) => Unsubscribe;\n};\n\nexport type SubscribableWithState<TState, TPath> = Subscribable & {\n path: TPath;\n getState: () => TState;\n};\n\nexport type NestedSubscribable<\n TState extends Subscribable | undefined,\n TPath,\n> = SubscribableWithState<TState, TPath>;\n\nexport type EventSubscribable<TEvent extends string> = {\n event: TEvent;\n binding: SubscribableWithState<\n | {\n unstable_on: (\n event: TEvent,\n callback: (payload?: unknown) => void,\n ) => Unsubscribe;\n }\n | undefined,\n unknown\n >;\n};\n\nexport const notifySubscribers = <TArgs extends unknown[]>(\n subscribers: Iterable<(...args: TArgs) => void>,\n ...args: TArgs\n): void => {\n const errors: unknown[] = [];\n for (const callback of subscribers) {\n try {\n callback(...args);\n } catch (error) {\n errors.push(error);\n }\n }\n\n if (errors.length === 1) {\n throw errors[0];\n }\n\n if (errors.length > 1) {\n for (const error of errors) {\n console.error(error);\n }\n throw new AggregateError(errors);\n }\n};\n\nfunction shallowEqual<T extends object>(\n objA: T | undefined,\n objB: T | undefined,\n) {\n if (objA === undefined && objB === undefined) return true;\n if (objA === undefined) return false;\n if (objB === undefined) return false;\n\n const keysA = Object.keys(objA);\n if (keysA.length !== Object.keys(objB).length) return false;\n\n for (const key of keysA) {\n const valueA = objA[key as keyof T];\n const valueB = objB[key as keyof T];\n if (!Object.is(valueA, valueB)) return false;\n }\n\n return true;\n}\n\nexport class BaseSubscribable {\n private _subscribers = new Set<() => void>();\n\n public subscribe(callback: () => void): Unsubscribe {\n this._subscribers.add(callback);\n return () => this._subscribers.delete(callback);\n }\n\n public waitForUpdate() {\n return new Promise<void>((resolve) => {\n const unsubscribe = this.subscribe(() => {\n unsubscribe();\n resolve();\n });\n });\n }\n\n protected _notifySubscribers() {\n notifySubscribers(this._subscribers);\n }\n}\n\n// lazy connect/disconnect: only opens upstream subscription while it has subscribers\nexport abstract class BaseSubject {\n private _subscriptions = new Set<(payload?: unknown) => void>();\n private _connection: Unsubscribe | undefined;\n\n protected get isConnected() {\n return !!this._connection;\n }\n\n protected abstract _connect(): Unsubscribe;\n\n protected notifySubscribers(payload?: unknown, errorContext?: string) {\n if (errorContext) {\n notifyEventListeners(this._subscriptions, payload, errorContext);\n return;\n }\n\n notifySubscribers(this._subscriptions, payload);\n }\n\n private _updateConnection() {\n if (this._subscriptions.size > 0) {\n if (this._connection) return;\n this._connection = this._connect();\n } else {\n this._connection?.();\n this._connection = undefined;\n }\n }\n\n public subscribe(callback: (payload?: unknown) => void) {\n this._subscriptions.add(callback);\n this._updateConnection();\n\n return () => {\n this._subscriptions.delete(callback);\n this._updateConnection();\n };\n }\n}\n\nexport class ShallowMemoizeSubject<TState extends object, TPath>\n extends BaseSubject\n implements SubscribableWithState<TState, TPath>\n{\n public get path() {\n return this.binding.path;\n }\n\n private binding: SubscribableWithState<TState | SKIP_UPDATE, TPath>;\n\n constructor(binding: SubscribableWithState<TState | SKIP_UPDATE, TPath>) {\n super();\n this.binding = binding;\n const state = binding.getState();\n if (state === SKIP_UPDATE)\n throw new Error(\"Entry not available in the store\");\n this._previousState = state;\n }\n\n private _previousState: TState;\n public getState = () => {\n if (!this.isConnected) this._syncState();\n return this._previousState;\n };\n\n private _syncState() {\n const state = this.binding.getState();\n if (state === SKIP_UPDATE) return false;\n if (shallowEqual(state, this._previousState)) return false;\n this._previousState = state;\n return true;\n }\n\n protected _connect() {\n const callback = () => {\n if (this._syncState()) {\n this.notifySubscribers();\n }\n };\n\n const unsubscribe = this.binding.subscribe(callback);\n this._syncState();\n return unsubscribe;\n }\n}\n\nexport class LazyMemoizeSubject<TState extends object, TPath>\n extends BaseSubject\n implements SubscribableWithState<TState, TPath>\n{\n public get path() {\n return this.binding.path;\n }\n\n private binding: SubscribableWithState<TState | SKIP_UPDATE, TPath>;\n\n constructor(binding: SubscribableWithState<TState | SKIP_UPDATE, TPath>) {\n super();\n this.binding = binding;\n }\n\n private _previousStateDirty = true;\n private _previousState: TState | undefined;\n public getState = () => {\n if (!this.isConnected || this._previousStateDirty) {\n const newState = this.binding.getState();\n if (\n newState !== SKIP_UPDATE &&\n (this._previousState === undefined ||\n !shallowEqual(newState, this._previousState))\n ) {\n this._previousState = newState;\n }\n this._previousStateDirty = false;\n }\n if (this._previousState === undefined)\n throw new Error(\"Entry not available in the store\");\n return this._previousState;\n };\n\n protected _connect() {\n const callback = () => {\n this._previousStateDirty = true;\n this.notifySubscribers();\n };\n\n const unsubscribe = this.binding.subscribe(callback);\n this._previousStateDirty = true;\n return unsubscribe;\n }\n}\n\nexport class NestedSubscriptionSubject<\n TState extends Subscribable | undefined,\n TPath,\n>\n extends BaseSubject\n implements\n SubscribableWithState<TState, TPath>,\n NestedSubscribable<TState, TPath>\n{\n public get path() {\n return this.binding.path;\n }\n\n private binding: NestedSubscribable<TState, TPath>;\n\n constructor(binding: NestedSubscribable<TState, TPath>) {\n super();\n this.binding = binding;\n }\n\n public getState() {\n return this.binding.getState();\n }\n\n public outerSubscribe(callback: () => void) {\n return this.binding.subscribe(callback);\n }\n\n protected _connect(): Unsubscribe {\n const callback = () => {\n this.notifySubscribers();\n };\n\n let lastState = this.binding.getState();\n let innerUnsubscribe = lastState?.subscribe(callback);\n const onRuntimeUpdate = () => {\n const newState = this.binding.getState();\n if (newState === lastState) return;\n lastState = newState;\n\n innerUnsubscribe?.();\n innerUnsubscribe = newState?.subscribe(callback);\n\n callback();\n };\n\n const outerUnsubscribe = this.outerSubscribe(onRuntimeUpdate);\n return () => {\n outerUnsubscribe?.();\n innerUnsubscribe?.();\n };\n }\n}\n\nexport class EventSubscriptionSubject<\n TEvent extends string,\n> extends BaseSubject {\n private config: EventSubscribable<TEvent>;\n\n constructor(config: EventSubscribable<TEvent>) {\n super();\n this.config = config;\n }\n\n public getState() {\n return this.config.binding.getState();\n }\n\n public outerSubscribe(callback: () => void) {\n return this.config.binding.subscribe(callback);\n }\n\n protected _connect(): Unsubscribe {\n const errorContext = `Runtime event \"${this.config.event}\"`;\n const callback = (payload?: unknown) => {\n this.notifySubscribers(payload, errorContext);\n };\n\n let lastState = this.config.binding.getState();\n let innerUnsubscribe = lastState?.unstable_on(this.config.event, callback);\n const onRuntimeUpdate = () => {\n const newState = this.config.binding.getState();\n if (newState === lastState) return;\n lastState = newState;\n\n innerUnsubscribe?.();\n innerUnsubscribe = newState?.unstable_on(this.config.event, callback);\n };\n\n const outerUnsubscribe = this.outerSubscribe(onRuntimeUpdate);\n return () => {\n outerUnsubscribe?.();\n innerUnsubscribe?.();\n };\n }\n}\n"],"mappings":"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"}
@@ -3,6 +3,7 @@ import "../index.js";
3
3
  //#region src/utils/composite-context-provider.d.ts
4
4
  declare class CompositeContextProvider implements ModelContextProvider {
5
5
  private _providers;
6
+ private _providerUnsubscribes;
6
7
  getModelContext(): ModelContext;
7
8
  registerModelContextProvider(provider: ModelContextProvider): () => void;
8
9
  private _subscribers;
@@ -1 +1 @@
1
- {"version":3,"file":"composite-context-provider.d.ts","names":[],"sources":["../../src/utils/composite-context-provider.ts"],"mappings":";;;cAKa,oCAAoC;UACvC;EAER,mBAHW;EAOX,6BAA6B,UAAU;UAa/B;EAER;EAIA,UAAU"}
1
+ {"version":3,"file":"composite-context-provider.d.ts","names":[],"sources":["../../src/utils/composite-context-provider.ts"],"mappings":";;;cAOa,oCAAoC;UACvC;UACA;EAER,mBAJW;EAQX,6BAA6B,UAAU;UA4B/B;EAER;EAIA,UAAU"}
@@ -1,25 +1,41 @@
1
1
  import { mergeModelContexts } from "../model-context/types.js";
2
+ import { notifySubscribers } from "../subscribable/subscribable.js";
2
3
  //#region src/utils/composite-context-provider.ts
3
4
  var CompositeContextProvider = class {
4
- _providers = /* @__PURE__ */ new Set();
5
+ _providers = /* @__PURE__ */ new Map();
6
+ _providerUnsubscribes = /* @__PURE__ */ new Map();
5
7
  getModelContext() {
6
- return mergeModelContexts(this._providers);
8
+ return mergeModelContexts(new Set(this._providers.values()));
7
9
  }
8
10
  registerModelContextProvider(provider) {
9
- this._providers.add(provider);
10
- const unsubscribe = provider.subscribe?.(() => {
11
- this.notifySubscribers();
12
- });
11
+ const id = Symbol();
12
+ this._providers.set(id, provider);
13
+ let unsubscribe;
14
+ try {
15
+ unsubscribe = provider.subscribe?.(() => {
16
+ this.notifySubscribers();
17
+ });
18
+ } catch (error) {
19
+ this._providers.delete(id);
20
+ try {
21
+ this.notifySubscribers();
22
+ } catch (notifyError) {
23
+ console.error(notifyError);
24
+ }
25
+ throw error;
26
+ }
27
+ this._providerUnsubscribes.set(id, unsubscribe);
13
28
  this.notifySubscribers();
14
29
  return () => {
15
- this._providers.delete(provider);
16
- unsubscribe?.();
30
+ this._providers.delete(id);
31
+ this._providerUnsubscribes.get(id)?.();
32
+ this._providerUnsubscribes.delete(id);
17
33
  this.notifySubscribers();
18
34
  };
19
35
  }
20
36
  _subscribers = /* @__PURE__ */ new Set();
21
37
  notifySubscribers() {
22
- for (const callback of this._subscribers) callback();
38
+ notifySubscribers(this._subscribers);
23
39
  }
24
40
  subscribe(callback) {
25
41
  this._subscribers.add(callback);
@@ -1 +1 @@
1
- {"version":3,"file":"composite-context-provider.js","names":["ModelContextProvider","mergeModelContexts","CompositeContextProvider","_providers","Set","getModelContext","registerModelContextProvider","provider","add","unsubscribe","subscribe","notifySubscribers","delete","_subscribers","callback"],"sources":["../../src/utils/composite-context-provider.ts"],"sourcesContent":["import {\n type ModelContextProvider,\n mergeModelContexts,\n} from \"../model-context/types\";\n\nexport class CompositeContextProvider implements ModelContextProvider {\n private _providers = new Set<ModelContextProvider>();\n\n getModelContext() {\n return mergeModelContexts(this._providers);\n }\n\n registerModelContextProvider(provider: ModelContextProvider) {\n this._providers.add(provider);\n const unsubscribe = provider.subscribe?.(() => {\n this.notifySubscribers();\n });\n this.notifySubscribers();\n return () => {\n this._providers.delete(provider);\n unsubscribe?.();\n this.notifySubscribers();\n };\n }\n\n private _subscribers = new Set<() => void>();\n\n notifySubscribers() {\n for (const callback of this._subscribers) callback();\n }\n\n subscribe(callback: () => void) {\n this._subscribers.add(callback);\n return () => {\n this._subscribers.delete(callback);\n };\n }\n}\n"],"mappings":";;AAKA,IAAaE,2BAAb,MAAsE;CACpE,6BAAqB,IAAIE,IAA0B;CAEnDC,kBAAkB;EAChB,OAAOJ,mBAAmB,KAAKE,UAAU;CAC3C;CAEAG,6BAA6BC,UAAgC;EAC3D,KAAKJ,WAAWK,IAAID,QAAQ;EAC5B,MAAME,cAAcF,SAASG,kBAAkB;GAC7C,KAAKC,kBAAkB;EACzB,CAAC;EACD,KAAKA,kBAAkB;EACvB,aAAa;GACX,KAAKR,WAAWS,OAAOL,QAAQ;GAC/BE,cAAc;GACd,KAAKE,kBAAkB;EACzB;CACF;CAEA,+BAAuB,IAAIP,IAAgB;CAE3CO,oBAAoB;EAClB,KAAK,MAAMG,YAAY,KAAKD,cAAcC,SAAS;CACrD;CAEAJ,UAAUI,UAAsB;EAC9B,KAAKD,aAAaL,IAAIM,QAAQ;EAC9B,aAAa;GACX,KAAKD,aAAaD,OAAOE,QAAQ;EACnC;CACF;AACF"}
1
+ {"version":3,"file":"composite-context-provider.js","names":["ModelContextProvider","mergeModelContexts","notifySubscribers","notifyStateSubscribers","Unsubscribe","CompositeContextProvider","_providers","Map","_providerUnsubscribes","getModelContext","Set","values","registerModelContextProvider","provider","id","Symbol","set","unsubscribe","subscribe","error","delete","notifyError","console","get","_subscribers","callback","add"],"sources":["../../src/utils/composite-context-provider.ts"],"sourcesContent":["import {\n type ModelContextProvider,\n mergeModelContexts,\n} from \"../model-context/types\";\nimport { notifySubscribers as notifyStateSubscribers } from \"../subscribable/subscribable\";\nimport type { Unsubscribe } from \"../types/unsubscribe\";\n\nexport class CompositeContextProvider implements ModelContextProvider {\n private _providers = new Map<symbol, ModelContextProvider>();\n private _providerUnsubscribes = new Map<symbol, Unsubscribe | undefined>();\n\n getModelContext() {\n return mergeModelContexts(new Set(this._providers.values()));\n }\n\n registerModelContextProvider(provider: ModelContextProvider) {\n const id = Symbol();\n this._providers.set(id, provider);\n let unsubscribe: Unsubscribe | undefined;\n try {\n unsubscribe = provider.subscribe?.(() => {\n this.notifySubscribers();\n });\n } catch (error) {\n this._providers.delete(id);\n try {\n this.notifySubscribers();\n } catch (notifyError) {\n console.error(notifyError);\n }\n throw error;\n }\n this._providerUnsubscribes.set(id, unsubscribe);\n this.notifySubscribers();\n return () => {\n this._providers.delete(id);\n const unsubscribe = this._providerUnsubscribes.get(id);\n unsubscribe?.();\n this._providerUnsubscribes.delete(id);\n this.notifySubscribers();\n };\n }\n\n private _subscribers = new Set<() => void>();\n\n notifySubscribers() {\n notifyStateSubscribers(this._subscribers);\n }\n\n subscribe(callback: () => void) {\n this._subscribers.add(callback);\n return () => {\n this._subscribers.delete(callback);\n };\n }\n}\n"],"mappings":";;;AAOA,IAAaK,2BAAb,MAAsE;CACpE,6BAAqB,IAAIE,IAAkC;CAC3D,wCAAgC,IAAIA,IAAqC;CAEzEE,kBAAkB;EAChB,OAAOR,mBAAmB,IAAIS,IAAI,KAAKJ,WAAWK,OAAO,CAAC,CAAC;CAC7D;CAEAC,6BAA6BC,UAAgC;EAC3D,MAAMC,KAAKC,OAAO;EAClB,KAAKT,WAAWU,IAAIF,IAAID,QAAQ;EAChC,IAAII;EACJ,IAAI;GACFA,cAAcJ,SAASK,kBAAkB;IACvC,KAAKhB,kBAAkB;GACzB,CAAC;EACH,SAASiB,OAAO;GACd,KAAKb,WAAWc,OAAON,EAAE;GACzB,IAAI;IACF,KAAKZ,kBAAkB;GACzB,SAASmB,aAAa;IACpBC,QAAQH,MAAME,WAAW;GAC3B;GACA,MAAMF;EACR;EACA,KAAKX,sBAAsBQ,IAAIF,IAAIG,WAAW;EAC9C,KAAKf,kBAAkB;EACvB,aAAa;GACX,KAAKI,WAAWc,OAAON,EAAE;GAEzBG,KADyBT,sBAAsBe,IAAIT,EACnDG,CAAW,GAAG;GACd,KAAKT,sBAAsBY,OAAON,EAAE;GACpC,KAAKZ,kBAAkB;EACzB;CACF;CAEA,+BAAuB,IAAIQ,IAAgB;CAE3CR,oBAAoB;EAClBC,kBAAuB,KAAKqB,YAAY;CAC1C;CAEAN,UAAUO,UAAsB;EAC9B,KAAKD,aAAaE,IAAID,QAAQ;EAC9B,aAAa;GACX,KAAKD,aAAaJ,OAAOK,QAAQ;EACnC;CACF;AACF"}
@@ -1,6 +1,6 @@
1
1
  //#region src/utils/notify-event-listeners.d.ts
2
- type EventListener = (payload?: unknown) => void;
3
- declare const notifyEventListeners: (listeners: Iterable<EventListener>, payload: unknown, errorContext: string) => void;
2
+ type EventListener<T> = (payload: T) => unknown;
3
+ declare const notifyEventListeners: <T>(listeners: Iterable<EventListener<T>>, payloadOrFactory: T | (() => T), errorContext: string) => void;
4
4
  //#endregion
5
5
  export { notifyEventListeners };
6
6
  //# sourceMappingURL=notify-event-listeners.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"notify-event-listeners.d.ts","names":[],"sources":["../../src/utils/notify-event-listeners.ts"],"mappings":";KAAK,iBAAiB;cAET,uBACX,WAAW,SAAS,gBACpB,kBACA"}
1
+ {"version":3,"file":"notify-event-listeners.d.ts","names":[],"sources":["../../src/utils/notify-event-listeners.ts"],"mappings":";KAAK,cAAc,MAAM,SAAS;cAGrB,uBAAwB,GACnC,WAAW,SAAS,cAAc,KAClC,kBAAkB,WAAW,IAC7B"}
@@ -1,10 +1,10 @@
1
1
  //#region src/utils/notify-event-listeners.ts
2
- const notifyEventListeners = (listeners, payload, errorContext) => {
2
+ const notifyEventListeners = (listeners, payloadOrFactory, errorContext) => {
3
3
  const reportError = (error) => {
4
4
  console.error(`[assistant-ui] ${errorContext} listener threw an error`, error);
5
5
  };
6
6
  for (const listener of listeners) try {
7
- const result = listener(payload);
7
+ const result = listener(typeof payloadOrFactory === "function" ? payloadOrFactory() : payloadOrFactory);
8
8
  if (result !== null && (typeof result === "object" || typeof result === "function") && "then" in result && typeof result.then === "function") Promise.resolve(result).catch(reportError);
9
9
  } catch (error) {
10
10
  reportError(error);
@@ -1 +1 @@
1
- {"version":3,"file":"notify-event-listeners.js","names":["EventListener","payload","notifyEventListeners","listeners","Iterable","errorContext","reportError","error","console","listener","result","then","Promise","resolve","catch"],"sources":["../../src/utils/notify-event-listeners.ts"],"sourcesContent":["type EventListener = (payload?: unknown) => void;\n\nexport const notifyEventListeners = (\n listeners: Iterable<EventListener>,\n payload: unknown,\n errorContext: string,\n) => {\n const reportError = (error: unknown) => {\n console.error(\n `[assistant-ui] ${errorContext} listener threw an error`,\n error,\n );\n };\n\n for (const listener of listeners) {\n try {\n const result = listener(payload) as unknown;\n if (\n result !== null &&\n (typeof result === \"object\" || typeof result === \"function\") &&\n \"then\" in result &&\n typeof result.then === \"function\"\n ) {\n void Promise.resolve(result).catch(reportError);\n }\n } catch (error) {\n reportError(error);\n }\n }\n};\n"],"mappings":";AAEA,MAAaE,wBACXC,WACAF,SACAI,iBACG;CACH,MAAMC,eAAeC,UAAmB;EACtCC,QAAQD,MACN,kBAAkBF,aAAY,2BAC9BE,KACF;CACF;CAEA,KAAK,MAAME,YAAYN,WACrB,IAAI;EACF,MAAMO,SAASD,SAASR,OAAO;EAC/B,IACES,WAAW,SACV,OAAOA,WAAW,YAAY,OAAOA,WAAW,eACjD,UAAUA,UACV,OAAOA,OAAOC,SAAS,YAEvB,QAAaE,QAAQH,MAAM,CAAC,CAACI,MAAMR,WAAW;CAElD,SAASC,OAAO;EACdD,YAAYC,KAAK;CACnB;AAEJ"}
1
+ {"version":3,"file":"notify-event-listeners.js","names":["EventListener","payload","T","notifyEventListeners","listeners","Iterable","payloadOrFactory","errorContext","reportError","error","console","listener","result","then","Promise","resolve","catch"],"sources":["../../src/utils/notify-event-listeners.ts"],"sourcesContent":["type EventListener<T> = (payload: T) => unknown;\n\n// A function payload is indistinguishable from a factory and gets invoked; wrap a function-typed payload in a factory.\nexport const notifyEventListeners = <T>(\n listeners: Iterable<EventListener<T>>,\n payloadOrFactory: T | (() => T),\n errorContext: string,\n) => {\n const reportError = (error: unknown) => {\n console.error(\n `[assistant-ui] ${errorContext} listener threw an error`,\n error,\n );\n };\n\n for (const listener of listeners) {\n try {\n const result = listener(\n typeof payloadOrFactory === \"function\"\n ? (payloadOrFactory as () => T)()\n : payloadOrFactory,\n );\n if (\n result !== null &&\n (typeof result === \"object\" || typeof result === \"function\") &&\n \"then\" in result &&\n typeof result.then === \"function\"\n ) {\n void Promise.resolve(result).catch(reportError);\n }\n } catch (error) {\n reportError(error);\n }\n }\n};\n"],"mappings":";AAGA,MAAaG,wBACXC,WACAE,kBACAC,iBACG;CACH,MAAMC,eAAeC,UAAmB;EACtCC,QAAQD,MACN,kBAAkBF,aAAY,2BAC9BE,KACF;CACF;CAEA,KAAK,MAAME,YAAYP,WACrB,IAAI;EACF,MAAMQ,SAASD,SACb,OAAOL,qBAAqB,aACvBA,iBAA6B,IAC9BA,gBACN;EACA,IACEM,WAAW,SACV,OAAOA,WAAW,YAAY,OAAOA,WAAW,eACjD,UAAUA,UACV,OAAOA,OAAOC,SAAS,YAEvB,QAAaE,QAAQH,MAAM,CAAC,CAACI,MAAMR,WAAW;CAElD,SAASC,OAAO;EACdD,YAAYC,KAAK;CACnB;AAEJ"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@assistant-ui/core",
3
- "version": "0.3.13",
3
+ "version": "0.3.14",
4
4
  "description": "Framework-agnostic core runtime for assistant-ui",
5
5
  "keywords": [
6
6
  "assistant",
@@ -59,7 +59,7 @@
59
59
  ],
60
60
  "sideEffects": false,
61
61
  "dependencies": {
62
- "assistant-stream": "^0.3.37",
62
+ "assistant-stream": "^0.3.38",
63
63
  "nanoid": "^6.0.1"
64
64
  },
65
65
  "optionalDevDependencies": {
@@ -95,11 +95,11 @@
95
95
  "react-dom": "^19.2.8",
96
96
  "vitest": "^4.1.10",
97
97
  "zustand": "^5.0.14",
98
- "@assistant-ui/tap": "0.9.12",
99
- "@assistant-ui/vite": "0.0.12",
100
- "assistant-cloud": "0.1.40",
101
- "@assistant-ui/store": "0.3.9",
102
- "@assistant-ui/x-buildutils": "0.0.22"
98
+ "@assistant-ui/store": "0.3.10",
99
+ "@assistant-ui/tap": "0.9.13",
100
+ "@assistant-ui/vite": "0.0.14",
101
+ "@assistant-ui/x-buildutils": "0.0.23",
102
+ "assistant-cloud": "0.1.41"
103
103
  },
104
104
  "publishConfig": {
105
105
  "access": "public",
@@ -1,5 +1,5 @@
1
1
  import { afterEach, describe, expect, it, vi } from "vitest";
2
- import { WebSpeechSynthesisAdapter } from "./speech";
2
+ import { WebSpeechDictationAdapter, WebSpeechSynthesisAdapter } from "./speech";
3
3
 
4
4
  afterEach(() => {
5
5
  vi.restoreAllMocks();
@@ -86,3 +86,120 @@ describe("WebSpeechSynthesisAdapter", () => {
86
86
  );
87
87
  });
88
88
  });
89
+
90
+ describe("WebSpeechDictationAdapter", () => {
91
+ const stubSpeechRecognition = () => {
92
+ const listeners = new Map<string, EventListener>();
93
+ class MockSpeechRecognition {
94
+ lang = "";
95
+ continuous = false;
96
+ interimResults = false;
97
+
98
+ addEventListener(type: string, listener: EventListener) {
99
+ listeners.set(type, listener);
100
+ }
101
+
102
+ start() {}
103
+ stop() {}
104
+ abort() {}
105
+ }
106
+ vi.stubGlobal("window", {
107
+ SpeechRecognition: MockSpeechRecognition,
108
+ });
109
+ return listeners;
110
+ };
111
+
112
+ it("continues notifying dictation listeners when one throws", () => {
113
+ const listeners = stubSpeechRecognition();
114
+ const listenerError = new Error("listener failed");
115
+ const consoleError = vi
116
+ .spyOn(console, "error")
117
+ .mockImplementation(() => {});
118
+ const session = new WebSpeechDictationAdapter().listen();
119
+ const laterStartListener = vi.fn();
120
+ const laterSpeechListener = vi.fn();
121
+ const laterEndListener = vi.fn();
122
+
123
+ session.onSpeechStart(() => {
124
+ throw listenerError;
125
+ });
126
+ session.onSpeechStart(laterStartListener);
127
+ session.onSpeech(() => {
128
+ throw listenerError;
129
+ });
130
+ session.onSpeech(laterSpeechListener);
131
+ session.onSpeechEnd(() => {
132
+ throw listenerError;
133
+ });
134
+ session.onSpeechEnd(laterEndListener);
135
+
136
+ const event = {
137
+ resultIndex: 0,
138
+ results: [
139
+ {
140
+ 0: { transcript: "Hello" },
141
+ isFinal: true,
142
+ },
143
+ ],
144
+ } as unknown as Event;
145
+
146
+ expect(() =>
147
+ listeners.get("speechstart")?.(new Event("speechstart")),
148
+ ).not.toThrow();
149
+ expect(() => listeners.get("result")?.(event)).not.toThrow();
150
+ expect(() => listeners.get("end")?.(new Event("end"))).not.toThrow();
151
+
152
+ expect(laterStartListener).toHaveBeenCalledOnce();
153
+ expect(laterSpeechListener).toHaveBeenCalledWith({
154
+ transcript: "Hello",
155
+ isFinal: true,
156
+ });
157
+ expect(laterEndListener).toHaveBeenCalledWith({
158
+ transcript: "Hello",
159
+ });
160
+ expect(consoleError).toHaveBeenCalledTimes(3);
161
+ expect(consoleError).toHaveBeenCalledWith(
162
+ "[assistant-ui] Dictation listener threw an error",
163
+ listenerError,
164
+ );
165
+ });
166
+
167
+ it("isolates async failures and payload mutations", async () => {
168
+ const listeners = stubSpeechRecognition();
169
+ const listenerError = new Error("async listener failed");
170
+ const consoleError = vi
171
+ .spyOn(console, "error")
172
+ .mockImplementation(() => {});
173
+ const session = new WebSpeechDictationAdapter().listen();
174
+ const laterListener = vi.fn();
175
+
176
+ session.onSpeech(async (result) => {
177
+ result.transcript = "Changed";
178
+ result.isFinal = false;
179
+ throw listenerError;
180
+ });
181
+ session.onSpeech(laterListener);
182
+
183
+ const event = {
184
+ resultIndex: 0,
185
+ results: [
186
+ {
187
+ 0: { transcript: "Hello" },
188
+ isFinal: true,
189
+ },
190
+ ],
191
+ } as unknown as Event;
192
+
193
+ expect(() => listeners.get("result")?.(event)).not.toThrow();
194
+ expect(laterListener).toHaveBeenCalledWith({
195
+ transcript: "Hello",
196
+ isFinal: true,
197
+ });
198
+ await vi.waitFor(() => {
199
+ expect(consoleError).toHaveBeenCalledWith(
200
+ "[assistant-ui] Dictation listener threw an error",
201
+ listenerError,
202
+ );
203
+ });
204
+ });
205
+ });
@@ -1,4 +1,5 @@
1
1
  import type { Unsubscribe } from "../types/unsubscribe";
2
+ import { notifyEventListeners } from "../utils/notify-event-listeners";
2
3
 
3
4
  export namespace SpeechSynthesisAdapter {
4
5
  export type Status =
@@ -22,21 +23,6 @@ export type SpeechSynthesisAdapter = {
22
23
  speak: (text: string) => SpeechSynthesisAdapter.Utterance;
23
24
  };
24
25
 
25
- const notifySpeechSynthesisListeners = (
26
- listeners: Iterable<() => void>,
27
- ): void => {
28
- for (const listener of listeners) {
29
- try {
30
- listener();
31
- } catch (error) {
32
- console.error(
33
- "[assistant-ui] Speech synthesis listener threw an error",
34
- error,
35
- );
36
- }
37
- }
38
- };
39
-
40
26
  export namespace DictationAdapter {
41
27
  export type Status =
42
28
  | {
@@ -79,7 +65,7 @@ export class WebSpeechSynthesisAdapter implements SpeechSynthesisAdapter {
79
65
  if (res.status.type === "ended") return;
80
66
 
81
67
  res.status = { type: "ended", reason, error };
82
- notifySpeechSynthesisListeners(subscribers);
68
+ notifyEventListeners(subscribers, undefined, "Speech synthesis");
83
69
  };
84
70
 
85
71
  utterance.addEventListener("end", () => handleEnd("finished"));
@@ -97,7 +83,9 @@ export class WebSpeechSynthesisAdapter implements SpeechSynthesisAdapter {
97
83
  if (res.status.type === "ended") {
98
84
  let cancelled = false;
99
85
  queueMicrotask(() => {
100
- if (!cancelled) notifySpeechSynthesisListeners([callback]);
86
+ if (!cancelled) {
87
+ notifyEventListeners([callback], undefined, "Speech synthesis");
88
+ }
101
89
  });
102
90
  return () => {
103
91
  cancelled = true;
@@ -270,7 +258,7 @@ export class WebSpeechDictationAdapter implements DictationAdapter {
270
258
  };
271
259
 
272
260
  recognition.addEventListener("speechstart", () => {
273
- for (const cb of speechStartCallbacks) cb();
261
+ notifyEventListeners(speechStartCallbacks, undefined, "Dictation");
274
262
  });
275
263
 
276
264
  recognition.addEventListener("start", () => {
@@ -292,9 +280,17 @@ export class WebSpeechDictationAdapter implements DictationAdapter {
292
280
 
293
281
  if (result.isFinal) {
294
282
  finalTranscript += transcript;
295
- for (const cb of speechCallbacks) cb({ transcript, isFinal: true });
283
+ notifyEventListeners(
284
+ speechCallbacks,
285
+ () => ({ transcript, isFinal: true }),
286
+ "Dictation",
287
+ );
296
288
  } else {
297
- for (const cb of speechCallbacks) cb({ transcript, isFinal: false });
289
+ notifyEventListeners(
290
+ speechCallbacks,
291
+ () => ({ transcript, isFinal: false }),
292
+ "Dictation",
293
+ );
298
294
  }
299
295
  }
300
296
  });
@@ -309,8 +305,11 @@ export class WebSpeechDictationAdapter implements DictationAdapter {
309
305
  updateStatus({ type: "ended", reason: "stopped" });
310
306
  }
311
307
  if (finalTranscript) {
312
- for (const cb of speechEndCallbacks)
313
- cb({ transcript: finalTranscript });
308
+ notifyEventListeners(
309
+ speechEndCallbacks,
310
+ () => ({ transcript: finalTranscript }),
311
+ "Dictation",
312
+ );
314
313
  finalTranscript = "";
315
314
  }
316
315
  });
@@ -39,6 +39,8 @@ export interface MessageFormatAdapter<
39
39
 
40
40
  export type GenericThreadHistoryAdapter<TMessage> = {
41
41
  load(): Promise<MessageFormatRepository<TMessage>>;
42
+ /** Snapshot the current thread so later writes survive a switch. */
43
+ pin?(): void;
42
44
  append(item: MessageFormatItem<TMessage>): Promise<void>;
43
45
  update?(
44
46
  item: MessageFormatItem<TMessage>,
@@ -73,6 +73,75 @@ describe("createVoiceSession", () => {
73
73
  expect(controls.mute).not.toHaveBeenCalled();
74
74
  });
75
75
 
76
+ it("removes the abort listener after disconnecting", async () => {
77
+ const abortController = new AbortController();
78
+ const controls = {
79
+ disconnect: vi.fn(),
80
+ mute: vi.fn(),
81
+ unmute: vi.fn(),
82
+ };
83
+ const session = createVoiceSession(
84
+ { abortSignal: abortController.signal },
85
+ async () => controls,
86
+ );
87
+ await Promise.resolve();
88
+
89
+ session.disconnect();
90
+ session.disconnect();
91
+ abortController.abort();
92
+
93
+ expect(controls.disconnect).toHaveBeenCalledOnce();
94
+ });
95
+
96
+ it("keeps abort teardown available after the session ends", async () => {
97
+ const abortController = new AbortController();
98
+ const controls = {
99
+ disconnect: vi.fn(),
100
+ mute: vi.fn(),
101
+ unmute: vi.fn(),
102
+ };
103
+ let helpers: VoiceSessionHelpers | undefined;
104
+ createVoiceSession(
105
+ { abortSignal: abortController.signal },
106
+ async (sessionHelpers) => {
107
+ helpers = sessionHelpers;
108
+ return controls;
109
+ },
110
+ );
111
+ await Promise.resolve();
112
+ if (!helpers) throw new Error("Voice session setup did not start");
113
+
114
+ helpers.end("error");
115
+ abortController.abort();
116
+
117
+ expect(controls.disconnect).toHaveBeenCalledOnce();
118
+ });
119
+
120
+ it("cleans up when the adapter disconnect throws", async () => {
121
+ const disconnectError = new Error("disconnect failed");
122
+ let helpers: VoiceSessionHelpers | undefined;
123
+ const session = createVoiceSession({}, async (sessionHelpers) => {
124
+ helpers = sessionHelpers;
125
+ return {
126
+ disconnect: () => {
127
+ throw disconnectError;
128
+ },
129
+ mute: vi.fn(),
130
+ unmute: vi.fn(),
131
+ };
132
+ });
133
+ await Promise.resolve();
134
+ if (!helpers) throw new Error("Voice session setup did not start");
135
+ const transcriptListener = vi.fn();
136
+ session.onTranscript(transcriptListener);
137
+
138
+ expect(() => session.disconnect()).toThrow(disconnectError);
139
+ expect(helpers.isDisposed()).toBe(true);
140
+
141
+ helpers.emitTranscript({ role: "assistant", text: "stale" });
142
+ expect(transcriptListener).not.toHaveBeenCalled();
143
+ });
144
+
76
145
  it("continues notifying listeners when one throws", () => {
77
146
  const listenerError = new Error("listener failed");
78
147
  const consoleError = vi