@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
@@ -0,0 +1,116 @@
1
+ // @vitest-environment jsdom
2
+
3
+ import { render, waitFor } from "@testing-library/react";
4
+ import { describe, expect, it, vi } from "vitest";
5
+ import { AssistantRuntimeProvider } from "../AssistantRuntimeProvider";
6
+ import { useExternalStoreRuntime } from "./useExternalStoreRuntime";
7
+ import { useLocalRuntime } from "./useLocalRuntime";
8
+ import { useRemoteThreadListRuntime } from "./useRemoteThreadListRuntime";
9
+ import type { AssistantRuntime } from "../../runtime/api/assistant-runtime";
10
+ import type { AppendMessage } from "../../types/message";
11
+ import { makeAdapter } from "../../tests/remote-thread-list-test-helpers";
12
+
13
+ const userMessage = (text: string): AppendMessage => ({
14
+ parentId: null,
15
+ sourceId: null,
16
+ runConfig: {},
17
+ role: "user",
18
+ content: [{ type: "text", text }],
19
+ attachments: [],
20
+ metadata: { custom: {} },
21
+ createdAt: new Date(),
22
+ startRun: false,
23
+ });
24
+
25
+ const getThreadCore = (runtime: AssistantRuntime) =>
26
+ (
27
+ runtime.thread as unknown as {
28
+ __internal_threadBinding: {
29
+ getState(): { append(message: AppendMessage): Promise<void> };
30
+ };
31
+ }
32
+ ).__internal_threadBinding.getState();
33
+
34
+ const testInitializationRetry = async (
35
+ runtimeHook: () => AssistantRuntime,
36
+ onNew?: ReturnType<typeof vi.fn>,
37
+ ) => {
38
+ const initializationError = new Error("initialization failed");
39
+ let initializeCount = 0;
40
+ const adapter = makeAdapter({
41
+ initialize: vi.fn(async (threadId: string) => {
42
+ initializeCount++;
43
+ if (initializeCount === 1) throw initializationError;
44
+ return {
45
+ remoteId: `remote-${threadId}`,
46
+ externalId: `external-${threadId}`,
47
+ };
48
+ }),
49
+ });
50
+ const runtimeRef: { current: AssistantRuntime | null } = { current: null };
51
+
52
+ const App = () => {
53
+ const runtime = useRemoteThreadListRuntime({ adapter, runtimeHook });
54
+ runtimeRef.current = runtime;
55
+ return (
56
+ <AssistantRuntimeProvider runtime={runtime}>
57
+ {null}
58
+ </AssistantRuntimeProvider>
59
+ );
60
+ };
61
+
62
+ render(<App />);
63
+ await waitFor(() => {
64
+ expect(runtimeRef.current?.threads.mainItem.getState().id).toBeDefined();
65
+ });
66
+
67
+ const item = runtimeRef.current!.threads.mainItem;
68
+ const localId = item.getState().id;
69
+ const thread = getThreadCore(runtimeRef.current!);
70
+
71
+ if (onNew) {
72
+ // The external-store core dispatches without holding the append on the
73
+ // initialization barrier; the failure surfaces at the dispatch seam.
74
+ await expect(thread.append(userMessage("first"))).resolves.toBeUndefined();
75
+ // Drain the rejection through RemoteThreadResource's catch so the retry
76
+ // re-arms before the second append; the exact chain depth is not
77
+ // load-bearing.
78
+ for (let i = 0; i < 20; i++) await Promise.resolve();
79
+ } else {
80
+ await expect(thread.append(userMessage("first"))).rejects.toMatchObject({
81
+ name: "MessageNotSentError",
82
+ cause: initializationError,
83
+ });
84
+ }
85
+ expect(item.getState().status).toBe("new");
86
+
87
+ await expect(thread.append(userMessage("second"))).resolves.toBeUndefined();
88
+ expect(adapter.initialize).toHaveBeenCalledTimes(2);
89
+ expect(adapter.initialize).toHaveBeenLastCalledWith(localId);
90
+ await waitFor(() => {
91
+ expect(item.getState()).toMatchObject({
92
+ id: localId,
93
+ status: "regular",
94
+ remoteId: `remote-${localId}`,
95
+ });
96
+ });
97
+ if (onNew) expect(onNew).toHaveBeenCalledTimes(2);
98
+ };
99
+
100
+ describe("RemoteThreadListHookInstanceManager initialization retry", () => {
101
+ it("retries a rejected initialization for local runtimes", async () => {
102
+ await testInitializationRetry(() =>
103
+ useLocalRuntime({
104
+ run: async () => ({ content: [] }),
105
+ }),
106
+ );
107
+ });
108
+
109
+ it("retries a rejected initialization for external-store runtimes", async () => {
110
+ const onNew = vi.fn(async () => {});
111
+ await testInitializationRetry(
112
+ () => useExternalStoreRuntime({ messages: [], onNew }),
113
+ onNew,
114
+ );
115
+ });
116
+ });
@@ -36,7 +36,7 @@ const makeManager = () =>
36
36
  {} as ThreadListRuntimeCore,
37
37
  );
38
38
 
39
- // mirrors what the React binder does on every publication
39
+ // mirrors what the tap fiber does on every publication
40
40
  const publish = (
41
41
  manager: RemoteThreadListHookInstanceManager,
42
42
  threadId: string,
@@ -1,6 +1,9 @@
1
- import { describe, expect, it } from "vitest";
1
+ import { describe, expect, it, vi } from "vitest";
2
2
  import type { ThreadListRuntimeCore } from "../../runtime/interfaces/thread-list-runtime-core";
3
3
  import { RemoteThreadListHookInstanceManager } from "./RemoteThreadListHookInstanceManager";
4
+ import { ExternalStoreThreadRuntimeCore } from "../../runtimes/external-store/external-store-thread-runtime-core";
5
+ import type { ExternalStoreAdapter } from "../../runtimes/external-store/external-store-adapter";
6
+ import type { ModelContextProvider } from "../../model-context/types";
4
7
 
5
8
  describe("RemoteThreadListHookInstanceManager", () => {
6
9
  it("rejects a pending start when the thread runtime is stopped", async () => {
@@ -15,6 +18,54 @@ describe("RemoteThreadListHookInstanceManager", () => {
15
18
  "Thread was deleted before runtime was started",
16
19
  );
17
20
  });
21
+
22
+ it("dispatches an append before the thread runtime stops", async () => {
23
+ let resolveInitialization!: () => void;
24
+ const initialization = new Promise<void>((resolve) => {
25
+ resolveInitialization = resolve;
26
+ });
27
+ const onNew = vi.fn(async () => {});
28
+ const runtime = new ExternalStoreThreadRuntimeCore(
29
+ { getModelContext: () => ({}) } satisfies ModelContextProvider,
30
+ { messages: [], onNew } as ExternalStoreAdapter,
31
+ );
32
+ runtime.__internal_setGetInitializePromise(() => initialization);
33
+ const manager = new RemoteThreadListHookInstanceManager(
34
+ () => ({}) as never,
35
+ {} as ThreadListRuntimeCore,
36
+ );
37
+ const internals = manager as unknown as {
38
+ instances: Map<
39
+ string,
40
+ { runtime: typeof runtime; generation: number; isRunning: boolean }
41
+ >;
42
+ };
43
+ internals.instances.set("thread-1", {
44
+ runtime,
45
+ generation: 0,
46
+ isRunning: false,
47
+ });
48
+
49
+ const appendPromise = runtime.append({
50
+ parentId: null,
51
+ sourceId: null,
52
+ runConfig: {},
53
+ role: "user",
54
+ content: [{ type: "text", text: "hello" }],
55
+ attachments: [],
56
+ metadata: { custom: {} },
57
+ createdAt: new Date(0),
58
+ });
59
+ await Promise.resolve();
60
+
61
+ expect(onNew).toHaveBeenCalledTimes(1);
62
+
63
+ manager.stopThreadRuntime("thread-1");
64
+ resolveInitialization();
65
+
66
+ await appendPromise;
67
+ expect(onNew).toHaveBeenCalledTimes(1);
68
+ });
18
69
  });
19
70
 
20
71
  describe("RemoteThreadListHookInstanceManager.__internal_restartThreadRuntime", () => {
@@ -24,7 +75,7 @@ describe("RemoteThreadListHookInstanceManager.__internal_restartThreadRuntime",
24
75
  {} as ThreadListRuntimeCore,
25
76
  );
26
77
 
27
- // no React binder attaches a runtime in these tests, so the returned promises
78
+ // no AdapterSink attaches a runtime in these tests, so the returned promises
28
79
  // stay pending or reject on stop; neither is what is under test here
29
80
  const start = (manager: RemoteThreadListHookInstanceManager, id: string) => {
30
81
  manager.startThreadRuntime(id).catch(() => {});
@@ -55,7 +106,7 @@ describe("RemoteThreadListHookInstanceManager.__internal_restartThreadRuntime",
55
106
  ([id, { generation }]) => `${id}:${generation}`,
56
107
  );
57
108
 
58
- it("changes the binder key so React remounts the runtime hook", () => {
109
+ it("changes the resource key so the tap host remounts the runtime hook", () => {
59
110
  const manager = makeManager();
60
111
  start(manager, "thread-1");
61
112
  const before = renderedKeys(manager);
@@ -87,7 +138,7 @@ describe("RemoteThreadListHookInstanceManager.__internal_restartThreadRuntime",
87
138
  expect(renderedKeys(manager)).toEqual(["thread-1:0"]);
88
139
  });
89
140
 
90
- it("stop then start in one tick yields a fresh key, so the old still-mounted binder cannot satisfy the new start", () => {
141
+ it("stop then start in one tick yields a fresh key, so the old still-mounted resource cannot satisfy the new start", () => {
91
142
  const manager = makeManager();
92
143
  start(manager, "thread-1");
93
144
  const before = renderedKeys(manager);
@@ -99,7 +150,7 @@ describe("RemoteThreadListHookInstanceManager.__internal_restartThreadRuntime",
99
150
  expect(renderedKeys(manager)).toEqual(["thread-1:1"]);
100
151
  });
101
152
 
102
- // simulates the binder's updateRuntime: publish a runtime for the
153
+ // simulates the resource publish: attach a runtime for the
103
154
  // instance's current generation (or an explicit stale one)
104
155
  const publish = (
105
156
  manager: RemoteThreadListHookInstanceManager,
@@ -160,7 +211,7 @@ describe("RemoteThreadListHookInstanceManager.__internal_restartThreadRuntime",
160
211
  settled = true;
161
212
  });
162
213
 
163
- // an outgoing binder re-publishing for its old generation (e.g. a late
214
+ // an outgoing resource re-publishing for its old generation (e.g. a late
164
215
  // outerSubscribe callback) must not count as the new attachment
165
216
  const staleGeneration =
166
217
  internalsOf(manager).instances.get("thread-1")!.generation - 1;
@@ -0,0 +1,272 @@
1
+ // @vitest-environment jsdom
2
+
3
+ import { act, render, waitFor } from "@testing-library/react";
4
+ import { describe, expect, it, vi } from "vitest";
5
+ import { useSyncExternalStore } from "react";
6
+ import { AssistantRuntimeProvider } from "../AssistantRuntimeProvider";
7
+ import { useExternalStoreRuntime } from "./useExternalStoreRuntime";
8
+ import { useLocalRuntime } from "./useLocalRuntime";
9
+ import { useRemoteThreadListRuntime } from "./useRemoteThreadListRuntime";
10
+ import type { AssistantRuntime } from "../../runtime/api/assistant-runtime";
11
+ import type { AppendMessage, ThreadMessageLike } from "../../types/message";
12
+ import {
13
+ deferred,
14
+ makeAdapter,
15
+ } from "../../tests/remote-thread-list-test-helpers";
16
+
17
+ const userMessage = (text: string): AppendMessage => ({
18
+ parentId: null,
19
+ sourceId: null,
20
+ runConfig: {},
21
+ role: "user",
22
+ content: [{ type: "text", text }],
23
+ attachments: [],
24
+ metadata: { custom: {} },
25
+ createdAt: new Date(),
26
+ startRun: false,
27
+ });
28
+
29
+ const getThreadCore = (runtime: AssistantRuntime) =>
30
+ (
31
+ runtime.thread as unknown as {
32
+ __internal_threadBinding: {
33
+ getState(): { append(message: AppendMessage): Promise<void> };
34
+ };
35
+ }
36
+ ).__internal_threadBinding.getState();
37
+
38
+ describe("RemoteThreadListHookInstanceManager title generation", () => {
39
+ it("generates the title once initialization resolves, without waiting for a run", async () => {
40
+ const initialization = deferred<{
41
+ remoteId: string;
42
+ externalId: string;
43
+ }>();
44
+ const generateTitle = vi.fn(async () => new ReadableStream());
45
+ const adapter = makeAdapter({
46
+ initialize: vi.fn(() => initialization.promise),
47
+ generateTitle,
48
+ });
49
+ const runtimeRef: { current: AssistantRuntime | null } = { current: null };
50
+
51
+ const App = () => {
52
+ const runtime = useRemoteThreadListRuntime({
53
+ adapter,
54
+ runtimeHook: () =>
55
+ useLocalRuntime({ run: async () => ({ content: [] }) }),
56
+ });
57
+ runtimeRef.current = runtime;
58
+ return (
59
+ <AssistantRuntimeProvider runtime={runtime}>
60
+ {null}
61
+ </AssistantRuntimeProvider>
62
+ );
63
+ };
64
+
65
+ render(<App />);
66
+ await waitFor(() => {
67
+ expect(runtimeRef.current?.threads.mainItem.getState().id).toBeDefined();
68
+ });
69
+ const localId = runtimeRef.current!.threads.mainItem.getState().id;
70
+
71
+ void getThreadCore(runtimeRef.current!).append(userMessage("hello"));
72
+ await Promise.resolve();
73
+
74
+ expect(adapter.initialize).toHaveBeenCalledTimes(1);
75
+ expect(generateTitle).not.toHaveBeenCalled();
76
+
77
+ initialization.resolve({
78
+ remoteId: `remote-${localId}`,
79
+ externalId: `external-${localId}`,
80
+ });
81
+
82
+ await waitFor(() => {
83
+ expect(generateTitle).toHaveBeenCalledTimes(1);
84
+ });
85
+ expect(generateTitle).toHaveBeenCalledWith(`remote-${localId}`, [
86
+ expect.objectContaining({
87
+ role: "user",
88
+ content: [expect.objectContaining({ type: "text", text: "hello" })],
89
+ }),
90
+ ]);
91
+ });
92
+
93
+ it("excludes the in-flight assistant message from the title payload", async () => {
94
+ const initialization = deferred<{
95
+ remoteId: string;
96
+ externalId: string;
97
+ }>();
98
+ const generateTitle = vi.fn(async () => new ReadableStream());
99
+ const adapter = makeAdapter({
100
+ initialize: vi.fn(() => initialization.promise),
101
+ generateTitle,
102
+ });
103
+ const runtimeRef: { current: AssistantRuntime | null } = { current: null };
104
+
105
+ const App = () => {
106
+ const runtime = useRemoteThreadListRuntime({
107
+ adapter,
108
+ runtimeHook: () =>
109
+ useLocalRuntime({ run: () => new Promise<never>(() => {}) }),
110
+ });
111
+ runtimeRef.current = runtime;
112
+ return (
113
+ <AssistantRuntimeProvider runtime={runtime}>
114
+ {null}
115
+ </AssistantRuntimeProvider>
116
+ );
117
+ };
118
+
119
+ render(<App />);
120
+ await waitFor(() => {
121
+ expect(runtimeRef.current?.threads.mainItem.getState().id).toBeDefined();
122
+ });
123
+ const localId = runtimeRef.current!.threads.mainItem.getState().id;
124
+
125
+ void getThreadCore(runtimeRef.current!).append({
126
+ ...userMessage("hello"),
127
+ startRun: true,
128
+ });
129
+ await act(async () => {});
130
+
131
+ initialization.resolve({
132
+ remoteId: `remote-${localId}`,
133
+ externalId: `external-${localId}`,
134
+ });
135
+
136
+ await waitFor(() => {
137
+ expect(generateTitle).toHaveBeenCalledTimes(1);
138
+ });
139
+ const [, titledMessages] = generateTitle.mock.calls[0] as [
140
+ string,
141
+ readonly { role: string }[],
142
+ ];
143
+ expect(titledMessages.map((message) => message.role)).toEqual(["user"]);
144
+ });
145
+
146
+ it("waits for a settled message on an agent-initiated first turn", async () => {
147
+ const initialization = deferred<{
148
+ remoteId: string;
149
+ externalId: string;
150
+ }>();
151
+ const run = deferred<{ content: { type: "text"; text: string }[] }>();
152
+ const generateTitle = vi.fn(async () => new ReadableStream());
153
+ const adapter = makeAdapter({
154
+ initialize: vi.fn(() => initialization.promise),
155
+ generateTitle,
156
+ });
157
+ const runtimeRef: { current: AssistantRuntime | null } = { current: null };
158
+
159
+ const App = () => {
160
+ const runtime = useRemoteThreadListRuntime({
161
+ adapter,
162
+ runtimeHook: () => useLocalRuntime({ run: () => run.promise }),
163
+ });
164
+ runtimeRef.current = runtime;
165
+ return (
166
+ <AssistantRuntimeProvider runtime={runtime}>
167
+ {null}
168
+ </AssistantRuntimeProvider>
169
+ );
170
+ };
171
+
172
+ render(<App />);
173
+ await waitFor(() => {
174
+ expect(runtimeRef.current?.threads.mainItem.getState().id).toBeDefined();
175
+ });
176
+ const localId = runtimeRef.current!.threads.mainItem.getState().id;
177
+
178
+ act(() => {
179
+ runtimeRef.current!.thread.startRun({ parentId: null });
180
+ });
181
+ await act(async () => {});
182
+
183
+ initialization.resolve({
184
+ remoteId: `remote-${localId}`,
185
+ externalId: `external-${localId}`,
186
+ });
187
+ await act(async () => {});
188
+
189
+ expect(generateTitle).not.toHaveBeenCalled();
190
+
191
+ run.resolve({ content: [{ type: "text", text: "hi there" }] });
192
+
193
+ await waitFor(() => {
194
+ expect(generateTitle).toHaveBeenCalledTimes(1);
195
+ });
196
+ const [, titledMessages] = generateTitle.mock.calls[0] as [
197
+ string,
198
+ readonly { role: string }[],
199
+ ];
200
+ expect(titledMessages.map((message) => message.role)).toEqual([
201
+ "assistant",
202
+ ]);
203
+ });
204
+
205
+ it("waits for the first message when initialization resolves before the store lands it", async () => {
206
+ const generateTitle = vi.fn(async () => new ReadableStream());
207
+ const adapter = makeAdapter({ generateTitle });
208
+ const store = {
209
+ messages: [] as readonly ThreadMessageLike[],
210
+ listeners: new Set<() => void>(),
211
+ subscribe(callback: () => void) {
212
+ store.listeners.add(callback);
213
+ return () => store.listeners.delete(callback);
214
+ },
215
+ set(messages: readonly ThreadMessageLike[]) {
216
+ store.messages = messages;
217
+ for (const listener of store.listeners) listener();
218
+ },
219
+ };
220
+ const capture: { runtime: AssistantRuntime | null } = { runtime: null };
221
+
222
+ const App = () => {
223
+ const runtime = useRemoteThreadListRuntime({
224
+ adapter,
225
+ runtimeHook: () => {
226
+ const messages = useSyncExternalStore(
227
+ store.subscribe,
228
+ () => store.messages,
229
+ );
230
+ return useExternalStoreRuntime({
231
+ messages,
232
+ convertMessage: (message: ThreadMessageLike) => message,
233
+ onNew: async () => {},
234
+ });
235
+ },
236
+ });
237
+ capture.runtime = runtime;
238
+ return (
239
+ <AssistantRuntimeProvider runtime={runtime}>
240
+ {null}
241
+ </AssistantRuntimeProvider>
242
+ );
243
+ };
244
+
245
+ render(<App />);
246
+ await waitFor(() => {
247
+ expect(capture.runtime?.threads.mainItem.getState().id).toBeDefined();
248
+ });
249
+
250
+ void getThreadCore(capture.runtime!).append(userMessage("hello"));
251
+ await waitFor(() => {
252
+ expect(adapter.initialize).toHaveBeenCalledTimes(1);
253
+ });
254
+
255
+ await act(async () => {});
256
+ expect(generateTitle).not.toHaveBeenCalled();
257
+
258
+ await act(async () => {
259
+ store.set([{ role: "user", content: [{ type: "text", text: "hello" }] }]);
260
+ });
261
+
262
+ await waitFor(() => {
263
+ expect(generateTitle).toHaveBeenCalledTimes(1);
264
+ });
265
+ const [, titledMessages] = generateTitle.mock.calls[0] as [
266
+ string,
267
+ readonly { role: string }[],
268
+ ];
269
+ expect(titledMessages).toHaveLength(1);
270
+ expect(titledMessages[0]).toMatchObject({ role: "user" });
271
+ });
272
+ });