@assistant-ui/core 0.3.17 → 0.3.19

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 (470) hide show
  1. package/dist/adapters/attachment.d.ts.map +1 -1
  2. package/dist/adapters/attachment.js +5 -4
  3. package/dist/adapters/attachment.js.map +1 -1
  4. package/dist/adapters/speech.d.ts.map +1 -1
  5. package/dist/adapters/speech.js +13 -3
  6. package/dist/adapters/speech.js.map +1 -1
  7. package/dist/adapters/thread-history.d.ts +4 -0
  8. package/dist/adapters/thread-history.d.ts.map +1 -1
  9. package/dist/index.d.ts +3 -3
  10. package/dist/index.js.map +1 -1
  11. package/dist/internal.d.ts +4 -3
  12. package/dist/internal.js +3 -2
  13. package/dist/model-context/frame/host.d.ts.map +1 -1
  14. package/dist/model-context/frame/host.js +35 -14
  15. package/dist/model-context/frame/host.js.map +1 -1
  16. package/dist/model-context/frame/provider.d.ts +2 -0
  17. package/dist/model-context/frame/provider.d.ts.map +1 -1
  18. package/dist/model-context/frame/provider.js +30 -7
  19. package/dist/model-context/frame/provider.js.map +1 -1
  20. package/dist/model-context/interactable-composer-metadata.d.ts.map +1 -1
  21. package/dist/model-context/interactable-composer-metadata.js +27 -18
  22. package/dist/model-context/interactable-composer-metadata.js.map +1 -1
  23. package/dist/model-context/registry.d.ts.map +1 -1
  24. package/dist/model-context/registry.js +3 -5
  25. package/dist/model-context/registry.js.map +1 -1
  26. package/dist/model-context/types.d.ts.map +1 -1
  27. package/dist/model-context/types.js +5 -4
  28. package/dist/model-context/types.js.map +1 -1
  29. package/dist/react/adapters/LocalStorageThreadListAdapter.d.ts.map +1 -1
  30. package/dist/react/adapters/LocalStorageThreadListAdapter.js +45 -7
  31. package/dist/react/adapters/LocalStorageThreadListAdapter.js.map +1 -1
  32. package/dist/react/client/DataRenderers.d.ts.map +1 -1
  33. package/dist/react/client/DataRenderers.js +20 -15
  34. package/dist/react/client/DataRenderers.js.map +1 -1
  35. package/dist/react/client/InMemoryThreadList.d.ts.map +1 -1
  36. package/dist/react/client/InMemoryThreadList.js +62 -56
  37. package/dist/react/client/InMemoryThreadList.js.map +1 -1
  38. package/dist/react/client/Interactables.d.ts.map +1 -1
  39. package/dist/react/client/Interactables.js +35 -32
  40. package/dist/react/client/Interactables.js.map +1 -1
  41. package/dist/react/client/RemoteThreadList.d.ts +1 -0
  42. package/dist/react/client/RemoteThreadList.d.ts.map +1 -1
  43. package/dist/react/client/RemoteThreadList.js +154 -143
  44. package/dist/react/client/RemoteThreadList.js.map +1 -1
  45. package/dist/react/client/Tools.d.ts.map +1 -1
  46. package/dist/react/client/Tools.js +18 -16
  47. package/dist/react/client/Tools.js.map +1 -1
  48. package/dist/react/client/interactable-model-context.d.ts.map +1 -1
  49. package/dist/react/client/interactable-model-context.js +3 -2
  50. package/dist/react/client/interactable-model-context.js.map +1 -1
  51. package/dist/react/index.d.ts +2 -2
  52. package/dist/react/index.js +2 -2
  53. package/dist/react/interactables-legacy/Interactables.js +242 -121
  54. package/dist/react/interactables-legacy/Interactables.js.map +1 -1
  55. package/dist/react/interactables-legacy/useAssistantInteractable.d.ts.map +1 -1
  56. package/dist/react/interactables-legacy/useAssistantInteractable.js +3 -0
  57. package/dist/react/interactables-legacy/useAssistantInteractable.js.map +1 -1
  58. package/dist/react/interactables-shared/useInteractablePersistenceQueue.d.ts.map +1 -1
  59. package/dist/react/interactables-shared/useInteractablePersistenceQueue.js +11 -9
  60. package/dist/react/interactables-shared/useInteractablePersistenceQueue.js.map +1 -1
  61. package/dist/react/model-context/makeAssistantTool.d.ts +2 -2
  62. package/dist/react/model-context/makeAssistantTool.js +1 -1
  63. package/dist/react/model-context/makeAssistantTool.js.map +1 -1
  64. package/dist/react/model-context/useAssistantTool.d.ts +2 -2
  65. package/dist/react/model-context/useAssistantTool.js +1 -1
  66. package/dist/react/model-context/useAssistantTool.js.map +1 -1
  67. package/dist/react/model-context/useInteractable.d.ts.map +1 -1
  68. package/dist/react/model-context/useInteractable.js +4 -1
  69. package/dist/react/model-context/useInteractable.js.map +1 -1
  70. package/dist/react/model-context/useInteractableVersions.js +1 -1
  71. package/dist/react/model-context/useToolArgsStatus.d.ts.map +1 -1
  72. package/dist/react/model-context/useToolArgsStatus.js +2 -1
  73. package/dist/react/model-context/useToolArgsStatus.js.map +1 -1
  74. package/dist/react/primitives/message/MessageGroupedParts.d.ts +11 -2
  75. package/dist/react/primitives/message/MessageGroupedParts.d.ts.map +1 -1
  76. package/dist/react/primitives/message/MessageGroupedParts.js +3 -2
  77. package/dist/react/primitives/message/MessageGroupedParts.js.map +1 -1
  78. package/dist/react/primitives/part/PartMessages.d.ts +1 -0
  79. package/dist/react/primitives/part/PartMessages.d.ts.map +1 -1
  80. package/dist/react/primitives/part/PartMessages.js +1 -0
  81. package/dist/react/primitives/part/PartMessages.js.map +1 -1
  82. package/dist/react/primitives/thread/ThreadMessages.d.ts.map +1 -1
  83. package/dist/react/primitives/thread/ThreadMessages.js +6 -5
  84. package/dist/react/primitives/thread/ThreadMessages.js.map +1 -1
  85. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.d.ts.map +1 -1
  86. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js +16 -8
  87. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js.map +1 -1
  88. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts +5 -1
  89. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts.map +1 -1
  90. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js +105 -104
  91. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js.map +1 -1
  92. package/dist/react/runtimes/RemoteThreadResource.d.ts +1 -0
  93. package/dist/react/runtimes/RemoteThreadResource.d.ts.map +1 -1
  94. package/dist/react/runtimes/RemoteThreadResource.js +13 -11
  95. package/dist/react/runtimes/RemoteThreadResource.js.map +1 -1
  96. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.d.ts +5 -24
  97. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.d.ts.map +1 -1
  98. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js +354 -226
  99. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js.map +1 -1
  100. package/dist/react/runtimes/cloud/CloudFileAttachmentAdapter.d.ts +1 -0
  101. package/dist/react/runtimes/cloud/CloudFileAttachmentAdapter.d.ts.map +1 -1
  102. package/dist/react/runtimes/cloud/CloudFileAttachmentAdapter.js +22 -2
  103. package/dist/react/runtimes/cloud/CloudFileAttachmentAdapter.js.map +1 -1
  104. package/dist/react/runtimes/cloud/auiV0.d.ts +40 -15
  105. package/dist/react/runtimes/cloud/auiV0.d.ts.map +1 -1
  106. package/dist/react/runtimes/cloud/auiV0.js +62 -15
  107. package/dist/react/runtimes/cloud/auiV0.js.map +1 -1
  108. package/dist/react/runtimes/cloud/createCloudThreadListAdapter.d.ts +2 -1
  109. package/dist/react/runtimes/cloud/createCloudThreadListAdapter.d.ts.map +1 -1
  110. package/dist/react/runtimes/cloud/createCloudThreadListAdapter.js +6 -1
  111. package/dist/react/runtimes/cloud/createCloudThreadListAdapter.js.map +1 -1
  112. package/dist/react/runtimes/cloud/sdkIdentity.d.ts +6 -0
  113. package/dist/react/runtimes/cloud/sdkIdentity.d.ts.map +1 -0
  114. package/dist/react/runtimes/cloud/sdkIdentity.js +9 -0
  115. package/dist/react/runtimes/cloud/sdkIdentity.js.map +1 -0
  116. package/dist/react/runtimes/external-message-converter.d.ts +12 -2
  117. package/dist/react/runtimes/external-message-converter.d.ts.map +1 -1
  118. package/dist/react/runtimes/external-message-converter.js +26 -61
  119. package/dist/react/runtimes/external-message-converter.js.map +1 -1
  120. package/dist/react/runtimes/useExternalStoreRuntime.js +75 -46
  121. package/dist/react/runtimes/useExternalStoreRuntime.js.map +1 -1
  122. package/dist/react/runtimes/useRemoteThreadListRuntime.d.ts.map +1 -1
  123. package/dist/react/runtimes/useRemoteThreadListRuntime.js +64 -24
  124. package/dist/react/runtimes/useRemoteThreadListRuntime.js.map +1 -1
  125. package/dist/react/utils/groupParts.d.ts +6 -1
  126. package/dist/react/utils/groupParts.d.ts.map +1 -1
  127. package/dist/react/utils/groupParts.js +6 -0
  128. package/dist/react/utils/groupParts.js.map +1 -1
  129. package/dist/react/utils/useJSONEqual.d.ts +3 -1
  130. package/dist/react/utils/useJSONEqual.d.ts.map +1 -1
  131. package/dist/react/utils/useJSONEqual.js +6 -1
  132. package/dist/react/utils/useJSONEqual.js.map +1 -1
  133. package/dist/react/utils/useJSONSchemaDependency.d.ts +7 -0
  134. package/dist/react/utils/useJSONSchemaDependency.d.ts.map +1 -0
  135. package/dist/react/utils/useJSONSchemaDependency.js +20 -0
  136. package/dist/react/utils/useJSONSchemaDependency.js.map +1 -0
  137. package/dist/runtime/api/thread-list-item-runtime.d.ts +7 -3
  138. package/dist/runtime/api/thread-list-item-runtime.d.ts.map +1 -1
  139. package/dist/runtime/api/thread-list-item-runtime.js +2 -2
  140. package/dist/runtime/api/thread-list-item-runtime.js.map +1 -1
  141. package/dist/runtime/api/thread-list-runtime.d.ts +3 -0
  142. package/dist/runtime/api/thread-list-runtime.d.ts.map +1 -1
  143. package/dist/runtime/api/thread-list-runtime.js +18 -14
  144. package/dist/runtime/api/thread-list-runtime.js.map +1 -1
  145. package/dist/runtime/api/thread-runtime.d.ts +4 -0
  146. package/dist/runtime/api/thread-runtime.d.ts.map +1 -1
  147. package/dist/runtime/api/thread-runtime.js +3 -6
  148. package/dist/runtime/api/thread-runtime.js.map +1 -1
  149. package/dist/runtime/base/base-composer-runtime-core.d.ts +1 -0
  150. package/dist/runtime/base/base-composer-runtime-core.d.ts.map +1 -1
  151. package/dist/runtime/base/base-composer-runtime-core.js +62 -22
  152. package/dist/runtime/base/base-composer-runtime-core.js.map +1 -1
  153. package/dist/runtime/base/base-thread-runtime-core.d.ts +2 -0
  154. package/dist/runtime/base/base-thread-runtime-core.d.ts.map +1 -1
  155. package/dist/runtime/base/base-thread-runtime-core.js +161 -69
  156. package/dist/runtime/base/base-thread-runtime-core.js.map +1 -1
  157. package/dist/runtime/interfaces/composer-runtime-core.d.ts +12 -5
  158. package/dist/runtime/interfaces/composer-runtime-core.d.ts.map +1 -1
  159. package/dist/runtime/interfaces/thread-list-runtime-core.d.ts +4 -1
  160. package/dist/runtime/interfaces/thread-list-runtime-core.d.ts.map +1 -1
  161. package/dist/runtime/interfaces/thread-runtime-core.d.ts +6 -0
  162. package/dist/runtime/interfaces/thread-runtime-core.d.ts.map +1 -1
  163. package/dist/runtime/queue/message-queue.d.ts +5 -0
  164. package/dist/runtime/queue/message-queue.d.ts.map +1 -1
  165. package/dist/runtime/queue/message-queue.js +61 -6
  166. package/dist/runtime/queue/message-queue.js.map +1 -1
  167. package/dist/runtime/utils/auto-status.d.ts +6 -2
  168. package/dist/runtime/utils/auto-status.d.ts.map +1 -1
  169. package/dist/runtime/utils/auto-status.js +12 -4
  170. package/dist/runtime/utils/auto-status.js.map +1 -1
  171. package/dist/runtime/utils/external-message-conversion.d.ts +16 -4
  172. package/dist/runtime/utils/external-message-conversion.d.ts.map +1 -1
  173. package/dist/runtime/utils/external-message-conversion.js +49 -13
  174. package/dist/runtime/utils/external-message-conversion.js.map +1 -1
  175. package/dist/runtime/utils/message-repository.d.ts +1 -0
  176. package/dist/runtime/utils/message-repository.d.ts.map +1 -1
  177. package/dist/runtime/utils/message-repository.js +18 -9
  178. package/dist/runtime/utils/message-repository.js.map +1 -1
  179. package/dist/runtime/utils/thread-message-like.d.ts +2 -1
  180. package/dist/runtime/utils/thread-message-like.d.ts.map +1 -1
  181. package/dist/runtime/utils/thread-message-like.js +5 -3
  182. package/dist/runtime/utils/thread-message-like.js.map +1 -1
  183. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.d.ts.map +1 -1
  184. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.js +21 -27
  185. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.js.map +1 -1
  186. package/dist/runtimes/external-store/external-store-thread-runtime-core.d.ts.map +1 -1
  187. package/dist/runtimes/external-store/external-store-thread-runtime-core.js +8 -1
  188. package/dist/runtimes/external-store/external-store-thread-runtime-core.js.map +1 -1
  189. package/dist/runtimes/internal.d.ts +3 -3
  190. package/dist/runtimes/internal.js +2 -2
  191. package/dist/runtimes/local/local-thread-runtime-core.d.ts +1 -1
  192. package/dist/runtimes/local/local-thread-runtime-core.d.ts.map +1 -1
  193. package/dist/runtimes/local/local-thread-runtime-core.js +4 -1
  194. package/dist/runtimes/local/local-thread-runtime-core.js.map +1 -1
  195. package/dist/runtimes/remote-thread-list/remote-thread-state.d.ts +18 -1
  196. package/dist/runtimes/remote-thread-list/remote-thread-state.d.ts.map +1 -1
  197. package/dist/runtimes/remote-thread-list/remote-thread-state.js +130 -42
  198. package/dist/runtimes/remote-thread-list/remote-thread-state.js.map +1 -1
  199. package/dist/runtimes/remote-thread-list/title-generation.d.ts +49 -0
  200. package/dist/runtimes/remote-thread-list/title-generation.d.ts.map +1 -0
  201. package/dist/runtimes/remote-thread-list/title-generation.js +197 -0
  202. package/dist/runtimes/remote-thread-list/title-generation.js.map +1 -0
  203. package/dist/runtimes/remote-thread-list/title.d.ts +3 -1
  204. package/dist/runtimes/remote-thread-list/title.d.ts.map +1 -1
  205. package/dist/runtimes/remote-thread-list/title.js +9 -1
  206. package/dist/runtimes/remote-thread-list/title.js.map +1 -1
  207. package/dist/runtimes/remote-thread-list/types.d.ts +10 -0
  208. package/dist/runtimes/remote-thread-list/types.d.ts.map +1 -1
  209. package/dist/runtimes/tool-invocations/ToolInvocationTracker.d.ts.map +1 -1
  210. package/dist/runtimes/tool-invocations/ToolInvocationTracker.js +2 -1
  211. package/dist/runtimes/tool-invocations/ToolInvocationTracker.js.map +1 -1
  212. package/dist/store/clients/external-thread.d.ts.map +1 -1
  213. package/dist/store/clients/external-thread.js +260 -203
  214. package/dist/store/clients/external-thread.js.map +1 -1
  215. package/dist/store/clients/model-context-client.d.ts.map +1 -1
  216. package/dist/store/clients/model-context-client.js +2 -1
  217. package/dist/store/clients/model-context-client.js.map +1 -1
  218. package/dist/store/clients/single-thread-list.d.ts.map +1 -1
  219. package/dist/store/clients/single-thread-list.js +1 -0
  220. package/dist/store/clients/single-thread-list.js.map +1 -1
  221. package/dist/store/clients/suggestions.d.ts.map +1 -1
  222. package/dist/store/clients/suggestions.js +5 -4
  223. package/dist/store/clients/suggestions.js.map +1 -1
  224. package/dist/store/clients/thread-tasks.d.ts +13 -0
  225. package/dist/store/clients/thread-tasks.d.ts.map +1 -0
  226. package/dist/store/clients/thread-tasks.js +69 -0
  227. package/dist/store/clients/thread-tasks.js.map +1 -0
  228. package/dist/store/index.d.ts +3 -2
  229. package/dist/store/index.js +3 -3
  230. package/dist/store/primitive-predicates.d.ts.map +1 -1
  231. package/dist/store/primitive-predicates.js +4 -4
  232. package/dist/store/primitive-predicates.js.map +1 -1
  233. package/dist/store/runtime-clients/composer-runtime-client.d.ts +1 -0
  234. package/dist/store/runtime-clients/composer-runtime-client.d.ts.map +1 -1
  235. package/dist/store/runtime-clients/composer-runtime-client.js +97 -61
  236. package/dist/store/runtime-clients/composer-runtime-client.js.map +1 -1
  237. package/dist/store/runtime-clients/message-runtime-client.d.ts +1 -0
  238. package/dist/store/runtime-clients/message-runtime-client.d.ts.map +1 -1
  239. package/dist/store/runtime-clients/message-runtime-client.js +221 -144
  240. package/dist/store/runtime-clients/message-runtime-client.js.map +1 -1
  241. package/dist/store/runtime-clients/thread-list-item-runtime-client.d.ts.map +1 -1
  242. package/dist/store/runtime-clients/thread-list-item-runtime-client.js +1 -1
  243. package/dist/store/runtime-clients/thread-list-item-runtime-client.js.map +1 -1
  244. package/dist/store/runtime-clients/thread-list-runtime-client.d.ts.map +1 -1
  245. package/dist/store/runtime-clients/thread-list-runtime-client.js +54 -52
  246. package/dist/store/runtime-clients/thread-list-runtime-client.js.map +1 -1
  247. package/dist/store/runtime-clients/thread-runtime-client.d.ts.map +1 -1
  248. package/dist/store/runtime-clients/thread-runtime-client.js +246 -126
  249. package/dist/store/runtime-clients/thread-runtime-client.js.map +1 -1
  250. package/dist/store/scope-registration.d.ts +2 -0
  251. package/dist/store/scope-registration.d.ts.map +1 -1
  252. package/dist/store/scopes/composer.d.ts +10 -2
  253. package/dist/store/scopes/composer.d.ts.map +1 -1
  254. package/dist/store/scopes/message.d.ts +30 -1
  255. package/dist/store/scopes/message.d.ts.map +1 -1
  256. package/dist/store/scopes/task.d.ts +36 -0
  257. package/dist/store/scopes/task.d.ts.map +1 -0
  258. package/dist/store/scopes/task.js +0 -0
  259. package/dist/store/scopes/thread-list-item.d.ts +2 -2
  260. package/dist/store/scopes/thread-list-item.d.ts.map +1 -1
  261. package/dist/store/scopes/thread.d.ts +28 -0
  262. package/dist/store/scopes/thread.d.ts.map +1 -1
  263. package/dist/store/scopes/threads.d.ts +1 -0
  264. package/dist/store/scopes/threads.d.ts.map +1 -1
  265. package/dist/subscribable/subscribable.d.ts +2 -1
  266. package/dist/subscribable/subscribable.d.ts.map +1 -1
  267. package/dist/subscribable/subscribable.js +23 -15
  268. package/dist/subscribable/subscribable.js.map +1 -1
  269. package/dist/types/attachment.d.ts +2 -1
  270. package/dist/types/attachment.d.ts.map +1 -1
  271. package/dist/types/attachment.js +2 -1
  272. package/dist/types/attachment.js.map +1 -1
  273. package/dist/types/index.d.ts +2 -2
  274. package/dist/types/message.d.ts +8 -1
  275. package/dist/types/message.d.ts.map +1 -1
  276. package/dist/types/message.js.map +1 -1
  277. package/dist/utils/abortable-iterable.d.ts +19 -0
  278. package/dist/utils/abortable-iterable.d.ts.map +1 -0
  279. package/dist/utils/abortable-iterable.js +88 -0
  280. package/dist/utils/abortable-iterable.js.map +1 -0
  281. package/dist/utils/composite-context-provider.d.ts.map +1 -1
  282. package/dist/utils/composite-context-provider.js +20 -2
  283. package/dist/utils/composite-context-provider.js.map +1 -1
  284. package/dist/utils/getGroupStatus.d.ts +18 -2
  285. package/dist/utils/getGroupStatus.d.ts.map +1 -1
  286. package/dist/utils/getGroupStatus.js +32 -7
  287. package/dist/utils/getGroupStatus.js.map +1 -1
  288. package/dist/utils/record.d.ts +11 -0
  289. package/dist/utils/record.d.ts.map +1 -0
  290. package/dist/utils/record.js +12 -0
  291. package/dist/utils/record.js.map +1 -0
  292. package/dist/utils/wire-media.d.ts.map +1 -1
  293. package/dist/utils/wire-media.js +2 -2
  294. package/dist/utils/wire-media.js.map +1 -1
  295. package/package.json +11 -11
  296. package/src/adapters/attachment.test.ts +50 -1
  297. package/src/adapters/attachment.ts +7 -4
  298. package/src/adapters/speech.test.ts +236 -0
  299. package/src/adapters/speech.ts +17 -10
  300. package/src/adapters/thread-history.ts +3 -0
  301. package/src/index.ts +2 -0
  302. package/src/internal.ts +4 -0
  303. package/src/model-context/frame/host.test.ts +137 -1
  304. package/src/model-context/frame/host.ts +49 -16
  305. package/src/model-context/frame/provider.test.ts +71 -0
  306. package/src/model-context/frame/provider.ts +37 -7
  307. package/src/model-context/interactable-composer-metadata.test.ts +58 -0
  308. package/src/model-context/interactable-composer-metadata.ts +19 -13
  309. package/src/model-context/registry.test.ts +12 -0
  310. package/src/model-context/registry.ts +3 -2
  311. package/src/model-context/types.test.ts +25 -0
  312. package/src/model-context/types.ts +9 -4
  313. package/src/react/RuntimeAdapter.test.ts +34 -0
  314. package/src/react/adapters/LocalStorageThreadListAdapter.test.ts +244 -0
  315. package/src/react/adapters/LocalStorageThreadListAdapter.tsx +104 -7
  316. package/src/react/client/DataRenderers.test.tsx +45 -0
  317. package/src/react/client/DataRenderers.ts +13 -9
  318. package/src/react/client/InMemoryThreadList.ts +1 -0
  319. package/src/react/client/Interactables.test.ts +72 -1
  320. package/src/react/client/Interactables.ts +22 -14
  321. package/src/react/client/RemoteThreadList.background.test.ts +148 -6
  322. package/src/react/client/RemoteThreadList.concurrent.test.tsx +42 -1
  323. package/src/react/client/RemoteThreadList.test.ts +386 -4
  324. package/src/react/client/RemoteThreadList.ts +181 -112
  325. package/src/react/client/Tools.ts +22 -11
  326. package/src/react/client/interactable-model-context.test.ts +44 -0
  327. package/src/react/client/interactable-model-context.ts +3 -2
  328. package/src/react/index.ts +5 -1
  329. package/src/react/interactables-legacy/Interactables.ts +16 -16
  330. package/src/react/interactables-legacy/useAssistantInteractable.test.tsx +132 -0
  331. package/src/react/interactables-legacy/useAssistantInteractable.ts +10 -1
  332. package/src/react/interactables-shared/useInteractablePersistenceQueue.ts +9 -10
  333. package/src/react/model-context/makeAssistantTool.ts +2 -2
  334. package/src/react/model-context/useAssistantTool.ts +2 -2
  335. package/src/react/model-context/useInteractable.test.tsx +177 -0
  336. package/src/react/model-context/useInteractable.ts +3 -0
  337. package/src/react/model-context/useToolArgsStatus.test.tsx +30 -0
  338. package/src/react/model-context/useToolArgsStatus.ts +2 -1
  339. package/src/react/primitive-hooks/useActionBarCopy.test.ts +0 -1
  340. package/src/react/primitive-hooks/useSuggestionTrigger.test.ts +0 -1
  341. package/src/react/primitives/message/MessageGroupedParts.test.tsx +96 -0
  342. package/src/react/primitives/message/MessageGroupedParts.tsx +15 -4
  343. package/src/react/primitives/part/PartMessages.test.tsx +104 -1
  344. package/src/react/primitives/part/PartMessages.tsx +1 -0
  345. package/src/react/primitives/thread/ThreadMessages.test.tsx +142 -0
  346. package/src/react/primitives/thread/ThreadMessages.tsx +8 -5
  347. package/src/react/providers/ReadonlyThreadProvider.test.tsx +56 -0
  348. package/src/react/runtimes/RemoteThreadListHookInstanceManager.destroy.test.tsx +172 -0
  349. package/src/react/runtimes/RemoteThreadListHookInstanceManager.running.test.tsx +33 -0
  350. package/src/react/runtimes/RemoteThreadListHookInstanceManager.test.tsx +7 -1
  351. package/src/react/runtimes/RemoteThreadListHookInstanceManager.tsx +28 -5
  352. package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.load-error.test.ts +132 -0
  353. package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.switch-delete.test.ts +105 -0
  354. package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.tsx +143 -112
  355. package/src/react/runtimes/RemoteThreadResource.test.ts +9 -0
  356. package/src/react/runtimes/RemoteThreadResource.ts +19 -12
  357. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.test.ts +20 -14
  358. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.test.tsx +703 -6
  359. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.ts +492 -402
  360. package/src/react/runtimes/cloud/CloudFileAttachmentAdapter.test.ts +77 -0
  361. package/src/react/runtimes/cloud/CloudFileAttachmentAdapter.ts +27 -1
  362. package/src/react/runtimes/cloud/auiV0.ts +160 -25
  363. package/src/react/runtimes/cloud/createCloudThreadListAdapter.test.ts +36 -0
  364. package/src/react/runtimes/cloud/createCloudThreadListAdapter.ts +8 -1
  365. package/src/react/runtimes/cloud/sdkIdentity.ts +9 -0
  366. package/src/react/runtimes/cloud/useCloudThreadListAdapter.test.tsx +18 -4
  367. package/src/react/runtimes/external-message-converter.test.ts +136 -1
  368. package/src/react/runtimes/external-message-converter.ts +39 -93
  369. package/src/react/runtimes/useExternalStoreRuntime.lifecycle.test.tsx +41 -0
  370. package/src/react/runtimes/useExternalStoreRuntime.ts +10 -4
  371. package/src/react/runtimes/useLocalRuntime.test.tsx +20 -0
  372. package/src/react/runtimes/useRemoteThreadListRuntime.load-error.test.tsx +108 -0
  373. package/src/react/runtimes/useRemoteThreadListRuntime.ts +34 -1
  374. package/src/react/utils/groupParts.ts +12 -1
  375. package/src/react/utils/useJSONEqual.ts +9 -0
  376. package/src/react/utils/useJSONSchemaDependency.ts +22 -0
  377. package/src/runtime/api/message-runtime.test.ts +75 -0
  378. package/src/runtime/api/thread-list-item-runtime.ts +10 -3
  379. package/src/runtime/api/thread-list-runtime.ts +21 -11
  380. package/src/runtime/api/thread-runtime.test.ts +136 -0
  381. package/src/runtime/api/thread-runtime.ts +10 -5
  382. package/src/runtime/base/base-composer-runtime-core.ts +105 -24
  383. package/src/runtime/base/base-thread-runtime-core.test.ts +1063 -2
  384. package/src/runtime/base/base-thread-runtime-core.ts +228 -87
  385. package/src/runtime/interfaces/composer-runtime-core.ts +10 -5
  386. package/src/runtime/interfaces/thread-list-runtime-core.ts +5 -1
  387. package/src/runtime/interfaces/thread-runtime-core.ts +6 -0
  388. package/src/runtime/queue/message-queue.ts +68 -6
  389. package/src/runtime/utils/auto-status.test.ts +112 -1
  390. package/src/runtime/utils/auto-status.ts +31 -5
  391. package/src/runtime/utils/external-message-conversion.test.ts +202 -0
  392. package/src/runtime/utils/external-message-conversion.ts +123 -32
  393. package/src/runtime/utils/message-repository.test.ts +49 -0
  394. package/src/runtime/utils/message-repository.ts +32 -20
  395. package/src/runtime/utils/thread-message-like.test.ts +52 -0
  396. package/src/runtime/utils/thread-message-like.ts +5 -1
  397. package/src/runtimes/external-store/external-store-thread-list-runtime-core.ts +18 -13
  398. package/src/runtimes/external-store/external-store-thread-runtime-core.ts +31 -1
  399. package/src/runtimes/internal.ts +1 -0
  400. package/src/runtimes/local/local-thread-runtime-core.test.ts +21 -0
  401. package/src/runtimes/local/local-thread-runtime-core.ts +11 -1
  402. package/src/runtimes/remote-thread-list/remote-thread-state.test.ts +128 -2
  403. package/src/runtimes/remote-thread-list/remote-thread-state.ts +182 -42
  404. package/src/runtimes/remote-thread-list/title-generation.test.ts +602 -0
  405. package/src/runtimes/remote-thread-list/title-generation.ts +329 -0
  406. package/src/runtimes/remote-thread-list/title.test.ts +62 -0
  407. package/src/runtimes/remote-thread-list/title.ts +13 -0
  408. package/src/runtimes/remote-thread-list/types.ts +10 -0
  409. package/src/runtimes/tool-invocations/EDGE_CASES.md +2 -6
  410. package/src/runtimes/tool-invocations/ToolInvocationTracker.test.ts +133 -0
  411. package/src/runtimes/tool-invocations/ToolInvocationTracker.ts +10 -6
  412. package/src/store/clients/external-thread.ts +67 -4
  413. package/src/store/clients/model-context-client.ts +5 -1
  414. package/src/store/clients/single-thread-list.ts +1 -0
  415. package/src/store/clients/suggestions.test.ts +40 -0
  416. package/src/store/clients/suggestions.ts +13 -10
  417. package/src/store/clients/thread-tasks.test.ts +274 -0
  418. package/src/store/clients/thread-tasks.ts +138 -0
  419. package/src/store/index.ts +8 -0
  420. package/src/store/primitive-predicates.test.ts +48 -5
  421. package/src/store/primitive-predicates.ts +7 -2
  422. package/src/store/runtime-clients/composer-runtime-client.ts +42 -9
  423. package/src/store/runtime-clients/message-runtime-client.ts +54 -6
  424. package/src/store/runtime-clients/thread-list-item-runtime-client.ts +4 -2
  425. package/src/store/runtime-clients/thread-list-runtime-client.ts +1 -0
  426. package/src/store/runtime-clients/thread-runtime-client.ts +89 -11
  427. package/src/store/scope-registration.ts +2 -0
  428. package/src/store/scopes/composer.ts +16 -4
  429. package/src/store/scopes/message.ts +14 -0
  430. package/src/store/scopes/task.ts +34 -0
  431. package/src/store/scopes/thread-list-item.ts +5 -2
  432. package/src/store/scopes/thread.ts +20 -0
  433. package/src/store/scopes/threads.ts +1 -0
  434. package/src/store/thread-tasks.test.tsx +209 -0
  435. package/src/subscribable/subscribable.test.ts +156 -1
  436. package/src/subscribable/subscribable.ts +25 -15
  437. package/src/tests/MessageRepository.test.ts +117 -5
  438. package/src/tests/OptimisticState-list-race.test.ts +1 -0
  439. package/src/tests/RemoteThreadListThreadListRuntimeCore-initialize-adapter-change.test.ts +38 -0
  440. package/src/tests/RemoteThreadListThreadListRuntimeCore-load-race.test.ts +47 -0
  441. package/src/tests/RemoteThreadListThreadListRuntimeCore-title-race.test.ts +89 -0
  442. package/src/tests/attachment-adapters.test.ts +14 -0
  443. package/src/tests/auiV0Encode.test.ts +300 -0
  444. package/src/tests/base-composer-runtime-core-send.test.ts +51 -0
  445. package/src/tests/base-composer-runtime-core.test.ts +353 -2
  446. package/src/tests/external-store-thread-list-runtime-core.test.ts +83 -0
  447. package/src/tests/external-store-thread-runtime-core-adapter.test.ts +178 -18
  448. package/src/tests/external-thread-attachments.test.tsx +208 -0
  449. package/src/tests/external-thread-parity.test.tsx +57 -0
  450. package/src/tests/groupParts.test.ts +71 -0
  451. package/src/tests/message-queue.test.ts +253 -0
  452. package/src/tests/remote-thread-list-isLoading.test.ts +1 -0
  453. package/src/tests/remote-thread-list-reactive-threadId.test.ts +51 -0
  454. package/src/tests/thread-list-runtime-getLoadThreadsPromise.test.ts +1 -0
  455. package/src/tests/thread-switch-events.test.tsx +78 -0
  456. package/src/tests/tools-scope-migration.test.tsx +28 -0
  457. package/src/tests/useRemoteThreadListRuntime-controlled.test.tsx +43 -0
  458. package/src/types/attachment.ts +4 -0
  459. package/src/types/index.ts +1 -0
  460. package/src/types/message.ts +8 -0
  461. package/src/utils/abortable-iterable.test.ts +328 -0
  462. package/src/utils/abortable-iterable.ts +104 -0
  463. package/src/utils/composite-context-provider.test.ts +25 -0
  464. package/src/utils/composite-context-provider.ts +23 -2
  465. package/src/utils/getGroupStatus.test.ts +70 -4
  466. package/src/utils/getGroupStatus.ts +35 -12
  467. package/src/utils/record.test.ts +48 -0
  468. package/src/utils/record.ts +11 -0
  469. package/src/utils/wire-media.test.ts +10 -0
  470. package/src/utils/wire-media.ts +6 -2
@@ -435,6 +435,259 @@ describe("createMessageQueue", () => {
435
435
  });
436
436
  });
437
437
 
438
+ describe("synchronous dispatch failures", () => {
439
+ it("restores a message when the driver throws", () => {
440
+ const error = new Error("dispatch failed");
441
+ const run = vi.fn(() => {
442
+ throw error;
443
+ });
444
+ const { adapter } = createMessageQueue({ run });
445
+
446
+ expect(() => adapter.enqueue(msg("first"))).toThrow(error);
447
+ expect(prompts(adapter.items)).toEqual(["first"]);
448
+
449
+ run.mockImplementation(() => undefined);
450
+ adapter.enqueue(msg("second"));
451
+
452
+ expect(run).toHaveBeenCalledTimes(2);
453
+ expect(run).toHaveBeenLastCalledWith(
454
+ expect.objectContaining({ content: [{ type: "text", text: "first" }] }),
455
+ { steer: false },
456
+ );
457
+ expect(prompts(adapter.items)).toEqual(["second"]);
458
+ });
459
+
460
+ it("does not restore work after the driver starts its run", () => {
461
+ const error = new Error("dispatch failed");
462
+ let fail = true;
463
+ let controller!: ReturnType<typeof createMessageQueue>;
464
+ const run = vi.fn(() => {
465
+ if (!fail) return;
466
+ controller.notifyBusy();
467
+ throw error;
468
+ });
469
+ controller = createMessageQueue({ run });
470
+
471
+ expect(() => controller.adapter.enqueue(msg("first"))).toThrow(error);
472
+ expect(prompts(controller.adapter.items)).toEqual([]);
473
+
474
+ fail = false;
475
+ controller.adapter.enqueue(msg("second"));
476
+ expect(run).toHaveBeenCalledOnce();
477
+
478
+ controller.notifyIdle();
479
+ expect(run).toHaveBeenCalledTimes(2);
480
+ expect(run).toHaveBeenLastCalledWith(
481
+ expect.objectContaining({
482
+ content: [{ type: "text", text: "second" }],
483
+ }),
484
+ { steer: false },
485
+ );
486
+ expect(prompts(controller.adapter.items)).toEqual([]);
487
+ });
488
+
489
+ it("restores a message when its dispatch transform throws", () => {
490
+ const error = new Error("transform failed");
491
+ const run = vi.fn();
492
+ const { adapter } = createMessageQueue({ run });
493
+ adapter.__internal_setDispatchTransform(() => {
494
+ throw error;
495
+ });
496
+
497
+ expect(() => adapter.enqueue(msg("first"))).toThrow(error);
498
+ expect(run).not.toHaveBeenCalled();
499
+ expect(prompts(adapter.items)).toEqual(["first"]);
500
+
501
+ adapter.__internal_setDispatchTransform((message) => message);
502
+ adapter.enqueue(msg("second"));
503
+
504
+ expect(run).toHaveBeenCalledWith(
505
+ expect.objectContaining({ content: [{ type: "text", text: "first" }] }),
506
+ { steer: false },
507
+ );
508
+ expect(prompts(adapter.items)).toEqual(["second"]);
509
+ });
510
+
511
+ it("restores a steer when cancellation throws", () => {
512
+ const error = new Error("cancel failed");
513
+ const run = vi.fn();
514
+ const cancel = vi.fn(() => {
515
+ throw error;
516
+ });
517
+ const { adapter, notifyIdle } = createMessageQueue({ run, cancel });
518
+
519
+ adapter.enqueue(msg("active"));
520
+ expect(() => adapter.steer(msg("urgent"))).toThrow(error);
521
+ expect(prompts(adapter.steerItems)).toEqual(["urgent"]);
522
+
523
+ notifyIdle();
524
+ expect(run).toHaveBeenCalledOnce();
525
+
526
+ cancel.mockImplementation(() => undefined);
527
+ adapter.enqueue(msg("later"));
528
+ expect(run).toHaveBeenLastCalledWith(
529
+ expect.objectContaining({
530
+ content: [{ type: "text", text: "urgent" }],
531
+ }),
532
+ { steer: false },
533
+ );
534
+ expect(prompts(adapter.items)).toEqual(["later"]);
535
+ });
536
+
537
+ it("restores a steer when its dispatch transform throws", () => {
538
+ const error = new Error("transform failed");
539
+ const run = vi.fn();
540
+ const cancel = vi.fn();
541
+ const { adapter, notifyIdle } = createMessageQueue({ run, cancel });
542
+
543
+ adapter.enqueue(msg("active"));
544
+ adapter.__internal_setDispatchTransform(() => {
545
+ throw error;
546
+ });
547
+
548
+ expect(() => adapter.steer(msg("urgent"))).toThrow(error);
549
+ expect(cancel).toHaveBeenCalledOnce();
550
+ expect(prompts(adapter.steerItems)).toEqual(["urgent"]);
551
+
552
+ notifyIdle();
553
+ adapter.__internal_setDispatchTransform((message) => message);
554
+ adapter.enqueue(msg("later"));
555
+
556
+ expect(run).toHaveBeenLastCalledWith(
557
+ expect.objectContaining({
558
+ content: [{ type: "text", text: "urgent" }],
559
+ }),
560
+ { steer: false },
561
+ );
562
+ expect(prompts(adapter.items)).toEqual(["later"]);
563
+ });
564
+
565
+ it("restores a steer when its replacement run throws", () => {
566
+ const error = new Error("steer failed");
567
+ let failSteer = true;
568
+ const run = vi.fn(
569
+ (_message: AppendMessage, options: { steer: boolean }) => {
570
+ if (options.steer && failSteer) throw error;
571
+ },
572
+ );
573
+ const { adapter, notifyIdle } = createMessageQueue({
574
+ run,
575
+ cancel: vi.fn(),
576
+ });
577
+
578
+ adapter.enqueue(msg("active"));
579
+ expect(() => adapter.steer(msg("urgent"))).toThrow(error);
580
+ expect(prompts(adapter.steerItems)).toEqual(["urgent"]);
581
+
582
+ failSteer = false;
583
+ notifyIdle();
584
+ expect(run).toHaveBeenCalledTimes(2);
585
+
586
+ adapter.enqueue(msg("later"));
587
+ expect(run).toHaveBeenLastCalledWith(
588
+ expect.objectContaining({
589
+ content: [{ type: "text", text: "urgent" }],
590
+ }),
591
+ { steer: false },
592
+ );
593
+ expect(prompts(adapter.items)).toEqual(["later"]);
594
+ });
595
+
596
+ it("recovers when cancellation settles before the replacement throws", () => {
597
+ const error = new Error("steer failed");
598
+ let failSteer = true;
599
+ let controller!: ReturnType<typeof createMessageQueue>;
600
+ controller = createMessageQueue({
601
+ run: (_message, options) => {
602
+ if (options.steer && failSteer) throw error;
603
+ },
604
+ cancel: () => controller.notifyIdle(),
605
+ });
606
+
607
+ controller.adapter.enqueue(msg("active"));
608
+ expect(() => controller.adapter.steer(msg("urgent"))).toThrow(error);
609
+ expect(prompts(controller.adapter.steerItems)).toEqual(["urgent"]);
610
+
611
+ failSteer = false;
612
+ controller.adapter.enqueue(msg("later"));
613
+
614
+ expect(prompts(controller.adapter.steerItems)).toEqual([]);
615
+ expect(prompts(controller.adapter.items)).toEqual(["later"]);
616
+ });
617
+
618
+ it("does not restore a steer after its replacement run starts", () => {
619
+ const error = new Error("steer failed");
620
+ let failSteer = true;
621
+ let controller!: ReturnType<typeof createMessageQueue>;
622
+ const run = vi.fn(
623
+ (_message: AppendMessage, options: { steer: boolean }) => {
624
+ if (!options.steer || !failSteer) return;
625
+ controller.notifyBusy();
626
+ throw error;
627
+ },
628
+ );
629
+ controller = createMessageQueue({
630
+ run,
631
+ cancel: () => controller.notifyIdle(),
632
+ });
633
+
634
+ controller.adapter.enqueue(msg("active"));
635
+ expect(() => controller.adapter.steer(msg("urgent"))).toThrow(error);
636
+ expect(prompts(controller.adapter.steerItems)).toEqual([]);
637
+
638
+ failSteer = false;
639
+ controller.adapter.enqueue(msg("later"));
640
+ expect(run).toHaveBeenCalledTimes(2);
641
+
642
+ controller.notifyIdle();
643
+ expect(run).toHaveBeenCalledTimes(3);
644
+ expect(run).toHaveBeenLastCalledWith(
645
+ expect.objectContaining({
646
+ content: [{ type: "text", text: "later" }],
647
+ }),
648
+ { steer: false },
649
+ );
650
+ expect(prompts(controller.adapter.items)).toEqual([]);
651
+ });
652
+
653
+ it("restores an unanchored move when its replacement run throws", () => {
654
+ const error = new Error("steer failed");
655
+ let failSteer = true;
656
+ const run = vi.fn(
657
+ (_message: AppendMessage, options: { steer: boolean }) => {
658
+ if (options.steer && failSteer) throw error;
659
+ },
660
+ );
661
+ const { adapter, notifyIdle } = createMessageQueue({
662
+ run,
663
+ cancel: vi.fn(),
664
+ });
665
+
666
+ adapter.enqueue(msg("active"));
667
+ adapter.enqueue(msg("first"));
668
+ adapter.enqueue(msg("second"));
669
+ adapter.enqueue(msg("moved"));
670
+ const movedId = adapter.items[2]!.id;
671
+
672
+ expect(() => adapter.move(movedId, { lane: "steer" })).toThrow(error);
673
+ expect(adapter.steerItems).toHaveLength(0);
674
+ expect(prompts(adapter.items)).toEqual(["first", "second", "moved"]);
675
+ expect(adapter.items[2]?.id).toBe(movedId);
676
+
677
+ failSteer = false;
678
+ notifyIdle();
679
+ adapter.enqueue(msg("later"));
680
+
681
+ expect(run).toHaveBeenLastCalledWith(
682
+ expect.objectContaining({
683
+ content: [{ type: "text", text: "first" }],
684
+ }),
685
+ { steer: false },
686
+ );
687
+ expect(prompts(adapter.items)).toEqual(["second", "moved", "later"]);
688
+ });
689
+ });
690
+
438
691
  it("notifyCancelled keeps items and pauses advance until the next send", () => {
439
692
  const run = vi.fn();
440
693
  const { adapter, notifyIdle, notifyCancelled } = createMessageQueue({
@@ -26,6 +26,7 @@ type ListResult = {
26
26
 
27
27
  const INITIAL_STATE: RemoteThreadState = {
28
28
  isLoading: true,
29
+ loadError: undefined,
29
30
  isLoadingMore: false,
30
31
  cursor: undefined,
31
32
  newThreadId: undefined,
@@ -88,4 +88,55 @@ describe("onThreadIdChange", () => {
88
88
  expect(emitted).not.toBe(localId);
89
89
  }
90
90
  });
91
+
92
+ it("does not reject a completed switch when the callback throws", async () => {
93
+ const callbackError = new Error("host callback failed");
94
+ const errorSpy = vi.spyOn(console, "error").mockImplementation(() => {});
95
+ try {
96
+ const cb = vi.fn(() => {
97
+ throw callbackError;
98
+ });
99
+ const { core } = createCoreWithCallback(cb);
100
+ await flush();
101
+
102
+ await expect(core.switchToThread("existing-1")).resolves.toBeUndefined();
103
+
104
+ expect(core.mainThreadId).toBe("existing-1");
105
+ expect(cb).toHaveBeenCalledExactlyOnceWith("existing-1");
106
+ expect(errorSpy).toHaveBeenCalledWith(
107
+ "[assistant-ui] onThreadIdChange callback threw an error",
108
+ callbackError,
109
+ );
110
+
111
+ await expect(core.switchToThread("existing-2")).resolves.toBeUndefined();
112
+ expect(core.mainThreadId).toBe("existing-2");
113
+ expect(cb).toHaveBeenLastCalledWith("existing-2");
114
+ } finally {
115
+ errorSpy.mockRestore();
116
+ }
117
+ });
118
+
119
+ it("contains a rejected callback promise", async () => {
120
+ const callbackError = new Error("async host callback failed");
121
+ const errorSpy = vi.spyOn(console, "error").mockImplementation(() => {});
122
+ try {
123
+ const cb = vi.fn(async () => {
124
+ throw callbackError;
125
+ });
126
+ const { core } = createCoreWithCallback(cb);
127
+ await flush();
128
+
129
+ await expect(core.switchToThread("existing-1")).resolves.toBeUndefined();
130
+ await vi.waitFor(() => {
131
+ expect(errorSpy).toHaveBeenCalledWith(
132
+ "[assistant-ui] onThreadIdChange callback threw an error",
133
+ callbackError,
134
+ );
135
+ });
136
+
137
+ expect(core.mainThreadId).toBe("existing-1");
138
+ } finally {
139
+ errorSpy.mockRestore();
140
+ }
141
+ });
91
142
  });
@@ -9,6 +9,7 @@ const createMockCore = (
9
9
  loadPromise: Promise<void> = Promise.resolve(),
10
10
  ): ThreadListRuntimeCore => ({
11
11
  isLoading: false,
12
+ loadError: undefined,
12
13
  mainThreadId: MAIN_THREAD_ID,
13
14
  newThreadId: undefined,
14
15
  threadIds: [MAIN_THREAD_ID],
@@ -352,6 +352,84 @@ const switchTo = async (runtime: AssistantRuntime, remoteId: string) => {
352
352
  });
353
353
  };
354
354
 
355
+ describe("ThreadListRuntime.getById", () => {
356
+ it("keeps background messages and metadata bound to the requested thread", async () => {
357
+ const harness = renderRemoteList(listAdapter(), async () => ({
358
+ content: [],
359
+ }));
360
+ const runtime = harness.getRuntime();
361
+ await waitFor(() => {
362
+ expect(runtime.threads.mainItem.getState().remoteId).toBe("thread-a");
363
+ });
364
+ await act(async () => {
365
+ runtime.thread.reset([
366
+ {
367
+ id: "message-a",
368
+ role: "user",
369
+ content: [{ type: "text", text: "A" }],
370
+ },
371
+ ]);
372
+ });
373
+ await switchTo(runtime, "thread-b");
374
+ await act(async () => {
375
+ runtime.thread.reset([
376
+ {
377
+ id: "message-b",
378
+ role: "user",
379
+ content: [{ type: "text", text: "B" }],
380
+ },
381
+ ]);
382
+ });
383
+ const threadBId = runtime.threads.mainItem.getState().id;
384
+ await switchTo(runtime, "thread-a");
385
+
386
+ const threadB = runtime.threads.getById(threadBId);
387
+ expect(threadB.getState()).toMatchObject({
388
+ threadId: threadBId,
389
+ messages: [{ id: "message-b" }],
390
+ metadata: {
391
+ id: threadBId,
392
+ remoteId: "thread-b",
393
+ title: "B",
394
+ isMain: false,
395
+ },
396
+ });
397
+ expect(runtime.thread.getState()).toMatchObject({
398
+ messages: [{ id: "message-a" }],
399
+ metadata: { remoteId: "thread-a", isMain: true },
400
+ });
401
+
402
+ let observed = threadB.getState();
403
+ const unsubscribe = threadB.subscribe(() => {
404
+ observed = threadB.getState();
405
+ });
406
+ try {
407
+ await act(async () => {
408
+ await runtime.threads.getItemById(threadBId).rename("Renamed B");
409
+ });
410
+ expect(observed).toMatchObject({
411
+ threadId: threadBId,
412
+ messages: [{ id: "message-b" }],
413
+ metadata: { title: "Renamed B", isMain: false },
414
+ });
415
+
416
+ await switchTo(runtime, "thread-b");
417
+ expect(observed.metadata).toMatchObject({
418
+ title: "Renamed B",
419
+ isMain: true,
420
+ });
421
+ await switchTo(runtime, "thread-a");
422
+ expect(observed).toMatchObject({
423
+ threadId: threadBId,
424
+ messages: [{ id: "message-b" }],
425
+ metadata: { title: "Renamed B", isMain: false },
426
+ });
427
+ } finally {
428
+ unsubscribe();
429
+ }
430
+ });
431
+ });
432
+
355
433
  describe("background thread run events", () => {
356
434
  it("delivers runEnd globally after the running thread is switched away", async () => {
357
435
  const runA = deferred<{ content: [] }>();
@@ -23,6 +23,34 @@ afterEach(() => {
23
23
  });
24
24
 
25
25
  describe("Tools model-context registration", () => {
26
+ it("registers and removes a tool UI named __proto__", async () => {
27
+ let aui!: AnyClient;
28
+ const Harness = () => {
29
+ aui = useAui({ tools: Tools({}) } as never);
30
+ return null;
31
+ };
32
+ render(<Harness />);
33
+
34
+ let remove!: () => void;
35
+ await act(async () => {
36
+ remove = aui.tools().setToolUI("__proto__", () => null);
37
+ await vi.waitFor(() => {
38
+ const toolUIs = aui.tools().getState().toolUIs;
39
+ expect(Object.hasOwn(toolUIs, "__proto__")).toBe(true);
40
+ expect(toolUIs.__proto__).toHaveLength(1);
41
+ });
42
+ });
43
+
44
+ await act(async () => {
45
+ remove();
46
+ await vi.waitFor(() =>
47
+ expect(Object.hasOwn(aui.tools().getState().toolUIs, "__proto__")).toBe(
48
+ false,
49
+ ),
50
+ );
51
+ });
52
+ });
53
+
26
54
  it("migrates to a structurally replaced modelContext scope", async () => {
27
55
  let aui!: AnyClient;
28
56
  const Harness = ({ generation }: { generation: number }) => {
@@ -269,6 +269,49 @@ describe("useRemoteThreadListRuntime controlled threadId", () => {
269
269
  expect(onThreadIdChange).toHaveBeenLastCalledWith(undefined);
270
270
  });
271
271
 
272
+ it("contains rejected callback promises on runtime-initiated switches", async () => {
273
+ const callbackError = new Error("async host callback failed");
274
+ const errorSpy = vi.spyOn(console, "error").mockImplementation(() => {});
275
+ const adapter = makeAdapter();
276
+ const onThreadIdChange = vi.fn(async () => {
277
+ throw callbackError;
278
+ });
279
+ const runtimeRef: RuntimeRef = { current: null };
280
+
281
+ const { unmount } = render(
282
+ <ControlledRuntime
283
+ adapter={adapter}
284
+ threadId="thread-a"
285
+ onThreadIdChange={onThreadIdChange}
286
+ runtimeRef={runtimeRef}
287
+ />,
288
+ );
289
+
290
+ try {
291
+ await waitForRemoteThread(runtimeRef, "thread-a");
292
+
293
+ await act(async () => {
294
+ await expect(
295
+ runtimeRef.current!.threads.switchToThread("thread-b"),
296
+ ).resolves.toBeUndefined();
297
+ });
298
+
299
+ await waitFor(() => {
300
+ expect(errorSpy).toHaveBeenCalledWith(
301
+ "[assistant-ui] onThreadIdChange callback threw an error",
302
+ callbackError,
303
+ );
304
+ });
305
+ expect(onThreadIdChange).toHaveBeenCalledExactlyOnceWith("thread-b");
306
+ expect(runtimeRef.current!.threads.mainItem.getState().remoteId).toBe(
307
+ "thread-b",
308
+ );
309
+ } finally {
310
+ unmount();
311
+ errorSpy.mockRestore();
312
+ }
313
+ });
314
+
272
315
  it("does not retain suppression after an initial switch fails", async () => {
273
316
  let threadAFetchCount = 0;
274
317
  const adapter = makeAdapter({
@@ -57,3 +57,7 @@ export const isCreateAttachment = (
57
57
  attachment: File | CreateAttachment,
58
58
  ): attachment is CreateAttachment =>
59
59
  "content" in attachment && !("lastModified" in attachment);
60
+
61
+ export const isAttachmentComplete = (
62
+ attachment: Attachment,
63
+ ): attachment is CompleteAttachment => attachment.status.type === "complete";
@@ -23,6 +23,7 @@ export type {
23
23
  MessageStatus,
24
24
  // Thread messages
25
25
  MessageTiming,
26
+ MessageModality,
26
27
  ThreadStep,
27
28
  ThreadSystemMessage,
28
29
  ThreadUserMessage,
@@ -395,6 +395,8 @@ export type MessageTiming = {
395
395
  readonly toolCallCount: number;
396
396
  };
397
397
 
398
+ export type MessageModality = "voice";
399
+
398
400
  export type ThreadStep = {
399
401
  readonly messageId?: string;
400
402
  readonly usage?:
@@ -420,6 +422,7 @@ export type ThreadSystemMessage = MessageCommonProps & {
420
422
  readonly steps?: undefined;
421
423
  readonly submittedFeedback?: undefined;
422
424
  readonly timing?: undefined;
425
+ readonly modality?: undefined;
423
426
  readonly custom: Record<string, unknown>;
424
427
  };
425
428
  };
@@ -436,6 +439,8 @@ export type ThreadUserMessage = MessageCommonProps & {
436
439
  readonly submittedFeedback?: undefined;
437
440
  readonly timing?: undefined;
438
441
  readonly isOptimistic?: boolean;
442
+ /** Set when the message was produced by a voice session rather than typed. */
443
+ readonly modality?: MessageModality;
439
444
  readonly custom: Record<string, unknown>;
440
445
  };
441
446
  };
@@ -456,6 +461,8 @@ export type ThreadAssistantMessage = MessageCommonProps & {
456
461
  * once off the head branch and are never persisted.
457
462
  */
458
463
  readonly isOptimistic?: boolean;
464
+ /** Set when the message was produced by a voice session rather than typed. */
465
+ readonly modality?: MessageModality;
459
466
  readonly custom: Record<string, unknown>;
460
467
  };
461
468
  };
@@ -470,6 +477,7 @@ type BaseThreadMessage = {
470
477
  readonly submittedFeedback?: { readonly type: "positive" | "negative" };
471
478
  readonly timing?: MessageTiming;
472
479
  readonly isOptimistic?: boolean;
480
+ readonly modality?: MessageModality;
473
481
  readonly custom: Record<string, unknown>;
474
482
  };
475
483
  readonly attachments?: ThreadUserMessage["attachments"];