@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
@@ -25,6 +25,8 @@ import {
25
25
  createThreadMappingId,
26
26
  getThreadData,
27
27
  normalizeCursor,
28
+ reconcileInitializedThread,
29
+ promoteNewThreadReducer,
28
30
  updateStatusReducer,
29
31
  type RemoteThreadData,
30
32
  type RemoteThreadState,
@@ -38,14 +40,24 @@ import type {
38
40
  } from "../../runtimes/remote-thread-list/types";
39
41
  import { ThreadListAdapterChangedError } from "../../runtimes/remote-thread-list/adapter-changed";
40
42
  import type { ThreadMessage } from "../../types/message";
41
- import { AssistantMessageStream } from "assistant-stream";
42
43
  import { handleThreadListAction } from "../../store/runtime-clients/handle-thread-list-action";
43
44
  import {
44
45
  inMemoryThreadListTransformScopes,
45
46
  type InMemoryThreadListProps,
46
47
  } from "./InMemoryThreadList";
47
48
  import { AdaptedRemoteThread } from "./AdaptedRemoteThread";
48
- import { isTitleSourceMessage } from "../../runtimes/remote-thread-list/title";
49
+ import {
50
+ applyTitleStream,
51
+ isTitleSourceMessage,
52
+ } from "../../runtimes/remote-thread-list/title";
53
+ import {
54
+ clearThreadTitleState,
55
+ finishThreadTitleRename,
56
+ runThreadTitleGeneration,
57
+ startThreadTitleRename,
58
+ type ThreadTitleState,
59
+ } from "../../runtimes/remote-thread-list/title-generation";
60
+ import { invokeUserCallback } from "../../utils/invoke-user-callback";
49
61
 
50
62
  const RESOLVED_PROMISE = Promise.resolve();
51
63
 
@@ -67,6 +79,7 @@ export type RemoteThreadListProps = {
67
79
  onThreadIdChange?: ((threadId: string | undefined) => void) | undefined;
68
80
  onSwitchToThread?: ((threadId: string) => void) | undefined;
69
81
  onSwitchToNewThread?: (() => void) | undefined;
82
+ /** Called after the backing adapter successfully deletes the thread. */
70
83
  onDelete?: ((threadId: string) => void) | undefined;
71
84
  /**
72
85
  * Keeps every thread the session switched to mounted, so a run continues
@@ -101,16 +114,6 @@ const toInitializeResult = (
101
114
  externalId: result.externalId,
102
115
  });
103
116
 
104
- const applyTitleStream = async (
105
- stream: Parameters<typeof AssistantMessageStream.fromAssistantStream>[0],
106
- onTitle: (title: string | undefined) => Promise<void>,
107
- ) => {
108
- const messageStream = AssistantMessageStream.fromAssistantStream(stream);
109
- for await (const result of messageStream) {
110
- await onTitle(result.parts.filter((part) => part.type === "text")[0]?.text);
111
- }
112
- };
113
-
114
117
  const useThreadListItemClient = (props: {
115
118
  data: RemoteThreadData;
116
119
  isRunning: boolean;
@@ -120,7 +123,7 @@ const useThreadListItemClient = (props: {
120
123
  onArchive: () => void;
121
124
  onUnarchive: () => void;
122
125
  onDelete: () => void;
123
- onGenerateTitle: () => void;
126
+ onGenerateTitle: (options?: { automatic?: boolean }) => void;
124
127
  onInitialize: () => Promise<{
125
128
  remoteId: string;
126
129
  externalId: string | undefined;
@@ -271,7 +274,7 @@ const useRemoteThreadBody = ({
271
274
  useEffect(() => {
272
275
  if (!hasTitleSource || titleFiredRef.current) return;
273
276
  titleFiredRef.current = true;
274
- client.threadListItem.generateTitle();
277
+ client.threadListItem.generateTitle({ automatic: true });
275
278
  }, [hasTitleSource]);
276
279
  return body.methods;
277
280
  });
@@ -343,6 +346,7 @@ const useRemoteThreadListView = ({
343
346
  onGenerateTitle: (
344
347
  threadId: string,
345
348
  messages: readonly ThreadMessage[] | undefined,
349
+ options?: { automatic?: boolean },
346
350
  ) => Promise<void>;
347
351
  onInitialize: (threadId: string) => Promise<{
348
352
  remoteId: string;
@@ -385,7 +389,7 @@ const useRemoteThreadListView = ({
385
389
  onUnarchive: () =>
386
390
  handleThreadListAction("unarchive", () => onUnarchive(data.id)),
387
391
  onDelete: () => handleThreadListAction("delete", () => onDelete(data.id)),
388
- onGenerateTitle: () =>
392
+ onGenerateTitle: (options) =>
389
393
  handleThreadListAction("generate title", () =>
390
394
  onGenerateTitle(
391
395
  data.id,
@@ -394,6 +398,7 @@ const useRemoteThreadListView = ({
394
398
  : mainThreadClient.state.messages) as
395
399
  | readonly ThreadMessage[]
396
400
  | undefined,
401
+ options,
397
402
  ),
398
403
  ),
399
404
  onInitialize: () => onInitialize(data.id),
@@ -489,6 +494,7 @@ const useRemoteThreadList = (
489
494
  adapter,
490
495
  adapterAtLoad: adapter,
491
496
  adapterGeneration: 0,
497
+ titleStates: new Map<string, ThreadTitleState>(),
492
498
  loadGeneration: 0,
493
499
  switchGeneration: 0,
494
500
  loadPromise: undefined as Promise<void> | undefined,
@@ -545,7 +551,14 @@ const useRemoteThreadList = (
545
551
  (remoteId: string | undefined, emit: boolean) => {
546
552
  if (session.lastNotifiedRemoteId === remoteId) return;
547
553
  session.lastNotifiedRemoteId = remoteId;
548
- if (emit) session.onThreadIdChange?.(remoteId);
554
+ if (emit) {
555
+ invokeUserCallback(
556
+ "assistant-ui",
557
+ "onThreadIdChange",
558
+ session.onThreadIdChange,
559
+ remoteId,
560
+ );
561
+ }
549
562
  },
550
563
  [session],
551
564
  );
@@ -560,7 +573,7 @@ const useRemoteThreadList = (
560
573
  execute: () => adapter.list(),
561
574
  loading: (state) => {
562
575
  if (generation !== session.loadGeneration) return state;
563
- return { ...state, isLoading: true };
576
+ return { ...state, isLoading: true, loadError: undefined };
564
577
  },
565
578
  then: (state, page) => {
566
579
  if (generation !== session.loadGeneration) return state;
@@ -574,6 +587,7 @@ const useRemoteThreadList = (
574
587
  const merged = {
575
588
  ...state,
576
589
  isLoading: false,
590
+ loadError: undefined,
577
591
  cursor: normalizeCursor(page.nextCursor),
578
592
  threadIds: fresh.threadIds,
579
593
  archivedThreadIds: fresh.archivedThreadIds,
@@ -590,6 +604,7 @@ const useRemoteThreadList = (
590
604
  store.update({
591
605
  ...store.baseValue,
592
606
  isLoading: false,
607
+ loadError: error,
593
608
  });
594
609
  })
595
610
  .then(() => {});
@@ -603,6 +618,7 @@ const useRemoteThreadList = (
603
618
  session.loadMorePromise = undefined;
604
619
  if (adapterChanged) {
605
620
  session.adapterGeneration++;
621
+ session.titleStates.clear();
606
622
  session.switchGeneration++;
607
623
  session.switchTask = undefined;
608
624
  session.adapterAtLoad = adapter;
@@ -627,6 +643,26 @@ const useRemoteThreadList = (
627
643
  store,
628
644
  ]);
629
645
 
646
+ useEffect(() => {
647
+ if (typeof window === "undefined" || typeof document === "undefined") {
648
+ return;
649
+ }
650
+
651
+ const retryAfterError = () => {
652
+ if (store.value.loadError !== undefined) void getLoadThreadsPromise();
653
+ };
654
+ const retryAfterVisible = () => {
655
+ if (document.visibilityState === "visible") retryAfterError();
656
+ };
657
+
658
+ window.addEventListener("online", retryAfterError);
659
+ document.addEventListener("visibilitychange", retryAfterVisible);
660
+ return () => {
661
+ window.removeEventListener("online", retryAfterError);
662
+ document.removeEventListener("visibilitychange", retryAfterVisible);
663
+ };
664
+ }, [getLoadThreadsPromise, store]);
665
+
630
666
  useEffect(() => {
631
667
  void getLoadThreadsPromise();
632
668
  }, [getLoadThreadsPromise]);
@@ -881,70 +917,60 @@ const useRemoteThreadList = (
881
917
  requireAdapterGeneration(adapterGeneration);
882
918
  return result;
883
919
  }
920
+ requireAdapterGeneration(adapterGeneration);
921
+ const initializeTask = currentAdapter.initialize(threadId);
922
+ let removedMappingId: string | undefined;
923
+ let replacementMainThreadId: string | undefined;
884
924
  const result = await store.optimisticUpdate({
885
- execute: () => {
886
- requireAdapterGeneration(adapterGeneration);
887
- return currentAdapter.initialize(threadId);
888
- },
889
- optimistic: (state) => updateStatusReducer(state, threadId, "regular"),
890
- loading: (state, task) => {
891
- const mappingId = createThreadMappingId(threadId);
892
- return {
893
- ...state,
894
- threadData: {
895
- ...state.threadData,
896
- [mappingId]: {
897
- ...state.threadData[mappingId],
898
- initializeTask: task,
899
- },
900
- },
901
- };
902
- },
925
+ execute: () => initializeTask,
926
+ optimistic: (state) =>
927
+ promoteNewThreadReducer(state, threadId, initializeTask),
903
928
  then: (state, { remoteId, externalId }) => {
904
929
  if (adapterGeneration !== session.adapterGeneration) return state;
905
- const data = getThreadData(state, threadId);
906
- if (!data) return state;
907
- const mappingId = createThreadMappingId(threadId);
908
- // A list() response that landed while this initialize was in flight
909
- // could not know the remote id yet, so it may have minted its own
910
- // slot for it; that slot collapses into this one.
911
- const listedMappingId = state.threadIdMap[remoteId];
912
- const orphan =
913
- listedMappingId !== undefined && listedMappingId !== mappingId
914
- ? state.threadData[listedMappingId]
915
- : undefined;
916
-
917
- const threadData = { ...state.threadData };
918
- if (orphan !== undefined) delete threadData[listedMappingId!];
919
- threadData[mappingId] = {
920
- ...data,
921
- initializeTask: Promise.resolve({ remoteId, externalId }),
930
+ // Background mode still owns the initializing body. Single-body mode
931
+ // has already replaced it, so retain the currently mounted body.
932
+ const retainedThreadId = backgroundThreads
933
+ ? threadId
934
+ : session.mainThreadId;
935
+ const reconciliation = reconcileInitializedThread(
936
+ state,
937
+ threadId,
922
938
  remoteId,
923
939
  externalId,
924
- } as RemoteThreadData;
925
-
926
- const rewire = (ids: readonly string[]) =>
927
- orphan === undefined ? ids : ids.filter((id) => id !== orphan.id);
928
-
929
- return {
930
- ...state,
931
- threadIds: rewire(state.threadIds),
932
- archivedThreadIds: rewire(state.archivedThreadIds),
933
- threadIdMap: {
934
- ...state.threadIdMap,
935
- [remoteId]: mappingId,
936
- },
937
- threadData,
938
- };
940
+ retainedThreadId,
941
+ );
942
+ removedMappingId = reconciliation.removedMappingId;
943
+ if (removedMappingId === session.mainThreadId) {
944
+ replacementMainThreadId = reconciliation.survivorMappingId;
945
+ }
946
+ return reconciliation.state;
939
947
  },
940
948
  });
941
949
  requireAdapterGeneration(adapterGeneration);
950
+ if (removedMappingId !== undefined) {
951
+ setStartedIds((prev) =>
952
+ prev.filter((startedId) => startedId !== removedMappingId),
953
+ );
954
+ }
955
+ if (
956
+ replacementMainThreadId !== undefined &&
957
+ session.mainThreadId === removedMappingId
958
+ ) {
959
+ assignMainThreadId(replacementMainThreadId);
960
+ }
942
961
  if (threadId === session.mainThreadId) {
943
962
  notifyRemoteId(result.remoteId, true);
944
963
  }
945
964
  return toInitializeResult(result);
946
965
  },
947
- [notifyRemoteId, requireAdapterGeneration, session, store],
966
+ [
967
+ assignMainThreadId,
968
+ backgroundThreads,
969
+ notifyRemoteId,
970
+ requireAdapterGeneration,
971
+ session,
972
+ store,
973
+ ],
948
974
  );
949
975
 
950
976
  const rename = useCallback(
@@ -956,27 +982,43 @@ const useRemoteThreadList = (
956
982
  if (data.status === "new") {
957
983
  throw threadStatusError(threadIdOrRemoteId, data.status, "be renamed");
958
984
  }
959
- return store.optimisticUpdate({
960
- execute: async () => {
961
- const { remoteId } = await data.initializeTask;
962
- requireAdapterGeneration(adapterGeneration);
963
- return currentAdapter.rename(remoteId, newTitle);
964
- },
965
- optimistic: (state) => {
966
- const current = getThreadData(state, threadIdOrRemoteId);
967
- if (!current) return state;
968
- return {
969
- ...state,
970
- threadData: {
971
- ...state.threadData,
972
- [current.id]: {
973
- ...current,
974
- title: newTitle,
985
+ const claim = startThreadTitleRename(
986
+ session.titleStates,
987
+ data.id,
988
+ newTitle,
989
+ );
990
+ return store
991
+ .optimisticUpdate({
992
+ execute: async () => {
993
+ const { remoteId } = await data.initializeTask;
994
+ requireAdapterGeneration(adapterGeneration);
995
+ return currentAdapter.rename(remoteId, newTitle);
996
+ },
997
+ optimistic: (state) => {
998
+ const current = getThreadData(state, threadIdOrRemoteId);
999
+ if (!current) return state;
1000
+ return {
1001
+ ...state,
1002
+ threadData: {
1003
+ ...state.threadData,
1004
+ [current.id]: {
1005
+ ...current,
1006
+ title: newTitle,
1007
+ },
975
1008
  },
976
- },
977
- };
978
- },
979
- });
1009
+ };
1010
+ },
1011
+ })
1012
+ .then(
1013
+ (result) => {
1014
+ finishThreadTitleRename(session.titleStates, data.id, claim, true);
1015
+ return result;
1016
+ },
1017
+ (error: unknown) => {
1018
+ finishThreadTitleRename(session.titleStates, data.id, claim, false);
1019
+ throw error;
1020
+ },
1021
+ );
980
1022
  },
981
1023
  [requireAdapterGeneration, session, store],
982
1024
  );
@@ -1104,8 +1146,7 @@ const useRemoteThreadList = (
1104
1146
  }
1105
1147
  await ensureNotMain(data.id);
1106
1148
  requireAdapterGeneration(adapterGeneration);
1107
- onDelete?.(data.id);
1108
- return store.optimisticUpdate({
1149
+ const result = await store.optimisticUpdate({
1109
1150
  execute: async () => {
1110
1151
  const { remoteId } = await data.initializeTask;
1111
1152
  requireAdapterGeneration(adapterGeneration);
@@ -1113,6 +1154,13 @@ const useRemoteThreadList = (
1113
1154
  },
1114
1155
  optimistic: (state) => updateStatusReducer(state, data.id, "deleted"),
1115
1156
  });
1157
+ // An adapter swap resets the optimistic layer, and a listed thread's slot
1158
+ // id is its remote id, so a replacement adapter can re-list this slot
1159
+ // while the deletion is in flight.
1160
+ if (getThreadData(store.value, data.id) !== undefined) return result;
1161
+ clearThreadTitleState(session.titleStates, data.id);
1162
+ onDelete?.(data.id);
1163
+ return result;
1116
1164
  },
1117
1165
  [ensureNotMain, onDelete, requireAdapterGeneration, session, store],
1118
1166
  );
@@ -1121,6 +1169,7 @@ const useRemoteThreadList = (
1121
1169
  async (
1122
1170
  threadIdOrRemoteId: string,
1123
1171
  messages: readonly ThreadMessage[] | undefined,
1172
+ options?: { automatic?: boolean },
1124
1173
  ) => {
1125
1174
  const currentAdapter = session.adapter;
1126
1175
  const adapterGeneration = session.adapterGeneration;
@@ -1144,27 +1193,39 @@ const useRemoteThreadList = (
1144
1193
  return;
1145
1194
  }
1146
1195
  if (!messages) return;
1147
- const stream = await currentAdapter.generateTitle(remoteId, messages);
1148
- requireAdapterGeneration(adapterGeneration);
1149
- await applyTitleStream(stream, async (newTitle) => {
1150
- await store.optimisticUpdate({
1151
- execute: async () => {},
1152
- optimistic: (state) => {
1153
- if (adapterGeneration !== session.adapterGeneration) return state;
1154
- const current = getThreadData(state, data.id);
1155
- if (!current) return state;
1156
- return {
1157
- ...state,
1158
- threadData: {
1159
- ...state.threadData,
1160
- [current.id]: {
1161
- ...current,
1162
- title: newTitle,
1196
+ await runThreadTitleGeneration({
1197
+ states: session.titleStates,
1198
+ threadId: data.id,
1199
+ automatic: options?.automatic === true,
1200
+ generate: async (onTitle) => {
1201
+ const stream = await currentAdapter.generateTitle(remoteId, messages);
1202
+ requireAdapterGeneration(adapterGeneration);
1203
+ await applyTitleStream(stream, onTitle);
1204
+ },
1205
+ rename: async (title) => {
1206
+ requireAdapterGeneration(adapterGeneration);
1207
+ await currentAdapter.rename(remoteId, title);
1208
+ },
1209
+ applyTitle: async (title) => {
1210
+ await store.optimisticUpdate({
1211
+ execute: async () => {},
1212
+ optimistic: (state) => {
1213
+ if (adapterGeneration !== session.adapterGeneration) return state;
1214
+ const current = getThreadData(state, data.id);
1215
+ if (!current) return state;
1216
+ return {
1217
+ ...state,
1218
+ threadData: {
1219
+ ...state.threadData,
1220
+ [current.id]: {
1221
+ ...current,
1222
+ title,
1223
+ },
1163
1224
  },
1164
- },
1165
- };
1166
- },
1167
- });
1225
+ };
1226
+ },
1227
+ });
1228
+ },
1168
1229
  });
1169
1230
  },
1170
1231
  [backgroundThreads, requireAdapterGeneration, session, store],
@@ -1192,7 +1253,8 @@ const useRemoteThreadList = (
1192
1253
  onArchive: (id) => archive(id),
1193
1254
  onUnarchive: (id) => unarchive(id),
1194
1255
  onDelete: (id) => deleteThread(id),
1195
- onGenerateTitle: (id, messages) => generateTitle(id, messages),
1256
+ onGenerateTitle: (id, messages, options) =>
1257
+ generateTitle(id, messages, options),
1196
1258
  onInitialize: async (id) => toInitializeResult(await initialize(id)),
1197
1259
  onDetach: (id) => detach(id),
1198
1260
  });
@@ -1201,7 +1263,12 @@ const useRemoteThreadList = (
1201
1263
  useEffect(() => {
1202
1264
  if (session.lastNotifiedRemoteId === mainRemoteId) return;
1203
1265
  session.lastNotifiedRemoteId = mainRemoteId;
1204
- onThreadIdChange?.(mainRemoteId);
1266
+ invokeUserCallback(
1267
+ "assistant-ui",
1268
+ "onThreadIdChange",
1269
+ onThreadIdChange,
1270
+ mainRemoteId,
1271
+ );
1205
1272
  }, [mainRemoteId, onThreadIdChange, session]);
1206
1273
 
1207
1274
  useEffect(() => {
@@ -1230,6 +1297,7 @@ const useRemoteThreadList = (
1230
1297
  mainThreadId,
1231
1298
  newThreadId: listState.newThreadId ?? null,
1232
1299
  isLoading: listState.isLoading,
1300
+ loadError: listState.loadError,
1233
1301
  isLoadingMore: listState.isLoadingMore,
1234
1302
  hasMore: listState.cursor !== undefined,
1235
1303
  threadIds: listState.threadIds,
@@ -1241,6 +1309,7 @@ const useRemoteThreadList = (
1241
1309
  listState.archivedThreadIds,
1242
1310
  listState.cursor,
1243
1311
  listState.isLoading,
1312
+ listState.loadError,
1244
1313
  listState.isLoadingMore,
1245
1314
  listState.newThreadId,
1246
1315
  listState.threadIds,
@@ -21,6 +21,7 @@ import {
21
21
  } from "../model-context/toolbox";
22
22
  import type { ToolCallMessagePartComponent } from "../types/MessagePartComponentTypes";
23
23
  import { ModelContext } from "../../store/clients/model-context-client";
24
+ import { nullProtoRecord } from "../../utils/record";
24
25
 
25
26
  export type { McpAppResourceOutput };
26
27
 
@@ -44,7 +45,9 @@ const useTools = ({
44
45
  const mcpAppOutputs = useResources(mcpApp ? [withKey("mcpApp", mcpApp)] : []);
45
46
  const mcpAppOutput = mcpAppOutputs[0];
46
47
 
47
- const [toolUIs, setToolUIs] = useState<ToolsState["toolUIs"]>(() => ({}));
48
+ const [toolUIs, setToolUIs] = useState<ToolsState["toolUIs"]>(() =>
49
+ nullProtoRecord(),
50
+ );
48
51
 
49
52
  const state = useMemo(
50
53
  (): ToolsState => ({
@@ -73,20 +76,25 @@ const useTools = ({
73
76
  standalone: options?.standalone ?? false,
74
77
  };
75
78
 
76
- setToolUIs((prev) => ({
77
- ...prev,
78
- [toolName]: [...(prev[toolName] ?? []), registration],
79
- }));
79
+ setToolUIs((prev) => {
80
+ const next = nullProtoRecord(prev);
81
+ next[toolName] = [...(next[toolName] ?? []), registration];
82
+ return next;
83
+ });
80
84
 
81
85
  return () => {
82
86
  setToolUIs((prev) => {
83
- const next = prev[toolName]?.filter((r) => r !== registration) ?? [];
84
- if (next.length > 0) return { ...prev, [toolName]: next };
87
+ const registrations =
88
+ prev[toolName]?.filter((r) => r !== registration) ?? [];
89
+ const next = nullProtoRecord(prev);
90
+ if (registrations.length > 0) {
91
+ next[toolName] = registrations;
92
+ return next;
93
+ }
85
94
  // Drop the key entirely so repeatedly mounted/unmounted tools
86
95
  // don't leave empty arrays accumulating across a long session.
87
- const rest = { ...prev };
88
- delete rest[toolName];
89
- return rest;
96
+ delete next[toolName];
97
+ return next;
90
98
  });
91
99
  };
92
100
  },
@@ -108,6 +116,9 @@ const useTools = ({
108
116
  : undefined);
109
117
  if (render) {
110
118
  unsubscribes.push(
119
+ // Registration has to be undone on unmount, so the registry write and
120
+ // its unsubscribe belong to the same effect.
121
+ // eslint-disable-next-line react-hooks/set-state-in-effect
111
122
  setToolUI(toolName, render, {
112
123
  standalone: isStandaloneToolDisplay(tool),
113
124
  renderText: toolRenderText,
@@ -141,7 +152,7 @@ const useTools = ({
141
152
  acc[name] = rest as Tool<any, any>;
142
153
  return acc;
143
154
  },
144
- {} as Record<string, Tool<any, any>>,
155
+ nullProtoRecord<Tool<any, any>>(),
145
156
  );
146
157
 
147
158
  const modelContextProvider = {
@@ -241,6 +241,50 @@ describe("buildInteractableModelContext", () => {
241
241
  expect(mockGenerateId).toHaveBeenCalledOnce();
242
242
  });
243
243
 
244
+ it.each(["__proto__", "toString"])(
245
+ "mints ids for items in a prototype-named array field %s",
246
+ async (field) => {
247
+ const itemSchema = {
248
+ type: "object" as const,
249
+ properties: {
250
+ id: { type: "string" as const },
251
+ title: { type: "string" as const },
252
+ },
253
+ required: ["id", "title"],
254
+ };
255
+ const schema = {
256
+ type: "object" as const,
257
+ properties: Object.fromEntries([
258
+ [field, { type: "array" as const, items: itemSchema }],
259
+ ]),
260
+ };
261
+ const state = Object.fromEntries([[field, []]]);
262
+ const defs = { b1: def("b1", "taskBoard", state) };
263
+ const { ctx } = build(defs, new Map([["b1", schema]]));
264
+ const args = Object.fromEntries([
265
+ ["id", "b1"],
266
+ [field, { add: [{ title: "Write tests" }] }],
267
+ ]);
268
+
269
+ const result = (await ctx!.tools["update_taskBoard"]!.execute!(
270
+ args,
271
+ {} as never,
272
+ )) as {
273
+ addedItemIds: Record<string, string[]>;
274
+ };
275
+
276
+ expect(Object.getPrototypeOf(result.addedItemIds)).toBe(
277
+ Object.prototype,
278
+ );
279
+ expect(Object.hasOwn(result.addedItemIds, field)).toBe(true);
280
+ expect(result.addedItemIds[field]).toEqual(["generated-id"]);
281
+ expect(Object.hasOwn(defs.b1.state as object, field)).toBe(true);
282
+ expect((defs.b1.state as Record<string, unknown>)[field]).toEqual([
283
+ { title: "Write tests", id: "generated-id" },
284
+ ]);
285
+ },
286
+ );
287
+
244
288
  it("rejects an unknown id and lists valid ids", async () => {
245
289
  const defs = { n1: def("n1", "note"), n2: def("n2", "note") };
246
290
  const { ctx, setDefState } = build(defs);
@@ -8,6 +8,7 @@ import {
8
8
  } from "../../model-context/interactable-composer-metadata";
9
9
  import { generateId } from "../../utils/id";
10
10
  import { isRecord } from "../../utils/json/is-json";
11
+ import { nullProtoRecord } from "../../utils/record";
11
12
 
12
13
  export type PartialJSONSchema = ReturnType<typeof toJSONSchema>;
13
14
 
@@ -243,7 +244,7 @@ export function buildInteractableModelContext(
243
244
  }
244
245
  const baseline = streamBaselines.get(toolCallId);
245
246
  streamBaselines.delete(toolCallId);
246
- const addedItemIds: Record<string, string[]> = {};
247
+ const addedItemIds = nullProtoRecord<string[]>();
247
248
  setDefState(target.id, (prev) =>
248
249
  shallowMergeInteractableState(prev, partial, {
249
250
  arrayBaseline:
@@ -264,7 +265,7 @@ export function buildInteractableModelContext(
264
265
  addedItemIds?: Record<string, string[]>;
265
266
  } = { success: true, id: target.id };
266
267
  if (Object.keys(addedItemIds).length > 0) {
267
- result.addedItemIds = addedItemIds;
268
+ result.addedItemIds = { ...addedItemIds };
268
269
  }
269
270
  return result;
270
271
  },
@@ -217,8 +217,12 @@ export { useExternalStoreSharedOptions } from "./runtimes/useExternalStoreShared
217
217
  export {
218
218
  useExternalMessageConverter,
219
219
  convertExternalMessages,
220
+ createExternalMessageConversionCache,
221
+ } from "./runtimes/external-message-converter";
222
+ export type {
223
+ ExternalMessageConversionCache,
224
+ JoinStrategy,
220
225
  } from "./runtimes/external-message-converter";
221
- export type { JoinStrategy } from "./runtimes/external-message-converter";
222
226
  export { createMessageConverter } from "./runtimes/createMessageConverter";
223
227
  export {
224
228
  useStreamingTiming,