@assistant-ui/core 0.3.17 → 0.3.18

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 (284) 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/index.d.ts +2 -2
  5. package/dist/index.js.map +1 -1
  6. package/dist/internal.d.ts +3 -2
  7. package/dist/internal.js +3 -2
  8. package/dist/model-context/frame/host.d.ts.map +1 -1
  9. package/dist/model-context/frame/host.js +10 -4
  10. package/dist/model-context/frame/host.js.map +1 -1
  11. package/dist/model-context/frame/provider.d.ts +2 -0
  12. package/dist/model-context/frame/provider.d.ts.map +1 -1
  13. package/dist/model-context/frame/provider.js +30 -7
  14. package/dist/model-context/frame/provider.js.map +1 -1
  15. package/dist/model-context/interactable-composer-metadata.d.ts.map +1 -1
  16. package/dist/model-context/interactable-composer-metadata.js +16 -9
  17. package/dist/model-context/interactable-composer-metadata.js.map +1 -1
  18. package/dist/model-context/registry.d.ts.map +1 -1
  19. package/dist/model-context/registry.js +3 -5
  20. package/dist/model-context/registry.js.map +1 -1
  21. package/dist/model-context/types.d.ts.map +1 -1
  22. package/dist/model-context/types.js +5 -4
  23. package/dist/model-context/types.js.map +1 -1
  24. package/dist/react/client/InMemoryThreadList.d.ts.map +1 -1
  25. package/dist/react/client/InMemoryThreadList.js +62 -56
  26. package/dist/react/client/InMemoryThreadList.js.map +1 -1
  27. package/dist/react/client/Interactables.d.ts.map +1 -1
  28. package/dist/react/client/Interactables.js +24 -27
  29. package/dist/react/client/Interactables.js.map +1 -1
  30. package/dist/react/client/RemoteThreadList.d.ts +1 -0
  31. package/dist/react/client/RemoteThreadList.d.ts.map +1 -1
  32. package/dist/react/client/RemoteThreadList.js +151 -141
  33. package/dist/react/client/RemoteThreadList.js.map +1 -1
  34. package/dist/react/client/Tools.d.ts.map +1 -1
  35. package/dist/react/client/Tools.js +18 -16
  36. package/dist/react/client/Tools.js.map +1 -1
  37. package/dist/react/interactables-legacy/Interactables.js +242 -121
  38. package/dist/react/interactables-legacy/Interactables.js.map +1 -1
  39. package/dist/react/interactables-shared/useInteractablePersistenceQueue.d.ts.map +1 -1
  40. package/dist/react/interactables-shared/useInteractablePersistenceQueue.js +11 -9
  41. package/dist/react/interactables-shared/useInteractablePersistenceQueue.js.map +1 -1
  42. package/dist/react/model-context/makeAssistantTool.d.ts +2 -2
  43. package/dist/react/model-context/makeAssistantTool.js +1 -1
  44. package/dist/react/model-context/makeAssistantTool.js.map +1 -1
  45. package/dist/react/model-context/useAssistantTool.d.ts +2 -2
  46. package/dist/react/model-context/useAssistantTool.js +1 -1
  47. package/dist/react/model-context/useAssistantTool.js.map +1 -1
  48. package/dist/react/model-context/useToolArgsStatus.d.ts.map +1 -1
  49. package/dist/react/model-context/useToolArgsStatus.js +2 -1
  50. package/dist/react/model-context/useToolArgsStatus.js.map +1 -1
  51. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.d.ts.map +1 -1
  52. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js +16 -8
  53. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js.map +1 -1
  54. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts +5 -1
  55. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts.map +1 -1
  56. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js +103 -103
  57. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js.map +1 -1
  58. package/dist/react/runtimes/RemoteThreadResource.d.ts +1 -0
  59. package/dist/react/runtimes/RemoteThreadResource.d.ts.map +1 -1
  60. package/dist/react/runtimes/RemoteThreadResource.js +13 -11
  61. package/dist/react/runtimes/RemoteThreadResource.js.map +1 -1
  62. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.d.ts +4 -1
  63. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.d.ts.map +1 -1
  64. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js +401 -34
  65. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js.map +1 -1
  66. package/dist/react/runtimes/cloud/createCloudThreadListAdapter.d.ts.map +1 -1
  67. package/dist/react/runtimes/cloud/createCloudThreadListAdapter.js +2 -1
  68. package/dist/react/runtimes/cloud/createCloudThreadListAdapter.js.map +1 -1
  69. package/dist/react/runtimes/useExternalStoreRuntime.js +75 -46
  70. package/dist/react/runtimes/useExternalStoreRuntime.js.map +1 -1
  71. package/dist/react/runtimes/useRemoteThreadListRuntime.d.ts.map +1 -1
  72. package/dist/react/runtimes/useRemoteThreadListRuntime.js +63 -23
  73. package/dist/react/runtimes/useRemoteThreadListRuntime.js.map +1 -1
  74. package/dist/runtime/api/thread-list-item-runtime.d.ts +7 -3
  75. package/dist/runtime/api/thread-list-item-runtime.d.ts.map +1 -1
  76. package/dist/runtime/api/thread-list-item-runtime.js +2 -2
  77. package/dist/runtime/api/thread-list-item-runtime.js.map +1 -1
  78. package/dist/runtime/api/thread-list-runtime.d.ts +3 -0
  79. package/dist/runtime/api/thread-list-runtime.d.ts.map +1 -1
  80. package/dist/runtime/api/thread-list-runtime.js +18 -14
  81. package/dist/runtime/api/thread-list-runtime.js.map +1 -1
  82. package/dist/runtime/api/thread-runtime.d.ts +4 -0
  83. package/dist/runtime/api/thread-runtime.d.ts.map +1 -1
  84. package/dist/runtime/api/thread-runtime.js +1 -1
  85. package/dist/runtime/api/thread-runtime.js.map +1 -1
  86. package/dist/runtime/base/base-composer-runtime-core.d.ts.map +1 -1
  87. package/dist/runtime/base/base-composer-runtime-core.js +16 -12
  88. package/dist/runtime/base/base-composer-runtime-core.js.map +1 -1
  89. package/dist/runtime/base/base-thread-runtime-core.d.ts.map +1 -1
  90. package/dist/runtime/base/base-thread-runtime-core.js +6 -5
  91. package/dist/runtime/base/base-thread-runtime-core.js.map +1 -1
  92. package/dist/runtime/interfaces/composer-runtime-core.d.ts +12 -5
  93. package/dist/runtime/interfaces/composer-runtime-core.d.ts.map +1 -1
  94. package/dist/runtime/interfaces/thread-list-runtime-core.d.ts +4 -1
  95. package/dist/runtime/interfaces/thread-list-runtime-core.d.ts.map +1 -1
  96. package/dist/runtime/utils/external-message-conversion.js +1 -1
  97. package/dist/runtime/utils/external-message-conversion.js.map +1 -1
  98. package/dist/runtime/utils/message-repository.d.ts +1 -0
  99. package/dist/runtime/utils/message-repository.d.ts.map +1 -1
  100. package/dist/runtime/utils/message-repository.js +15 -6
  101. package/dist/runtime/utils/message-repository.js.map +1 -1
  102. package/dist/runtime/utils/thread-message-like.js +1 -1
  103. package/dist/runtime/utils/thread-message-like.js.map +1 -1
  104. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.d.ts.map +1 -1
  105. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.js +21 -27
  106. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.js.map +1 -1
  107. package/dist/runtimes/internal.d.ts +2 -2
  108. package/dist/runtimes/internal.js +2 -2
  109. package/dist/runtimes/remote-thread-list/remote-thread-state.d.ts +18 -1
  110. package/dist/runtimes/remote-thread-list/remote-thread-state.d.ts.map +1 -1
  111. package/dist/runtimes/remote-thread-list/remote-thread-state.js +130 -42
  112. package/dist/runtimes/remote-thread-list/remote-thread-state.js.map +1 -1
  113. package/dist/runtimes/remote-thread-list/title-generation.d.ts +49 -0
  114. package/dist/runtimes/remote-thread-list/title-generation.d.ts.map +1 -0
  115. package/dist/runtimes/remote-thread-list/title-generation.js +197 -0
  116. package/dist/runtimes/remote-thread-list/title-generation.js.map +1 -0
  117. package/dist/runtimes/remote-thread-list/title.d.ts +3 -1
  118. package/dist/runtimes/remote-thread-list/title.d.ts.map +1 -1
  119. package/dist/runtimes/remote-thread-list/title.js +9 -1
  120. package/dist/runtimes/remote-thread-list/title.js.map +1 -1
  121. package/dist/runtimes/remote-thread-list/types.d.ts +10 -0
  122. package/dist/runtimes/remote-thread-list/types.d.ts.map +1 -1
  123. package/dist/store/clients/external-thread.d.ts.map +1 -1
  124. package/dist/store/clients/external-thread.js +19 -3
  125. package/dist/store/clients/external-thread.js.map +1 -1
  126. package/dist/store/clients/model-context-client.d.ts.map +1 -1
  127. package/dist/store/clients/model-context-client.js +2 -1
  128. package/dist/store/clients/model-context-client.js.map +1 -1
  129. package/dist/store/clients/single-thread-list.d.ts.map +1 -1
  130. package/dist/store/clients/single-thread-list.js +1 -0
  131. package/dist/store/clients/single-thread-list.js.map +1 -1
  132. package/dist/store/clients/suggestions.d.ts.map +1 -1
  133. package/dist/store/clients/suggestions.js +5 -4
  134. package/dist/store/clients/suggestions.js.map +1 -1
  135. package/dist/store/runtime-clients/composer-runtime-client.d.ts +1 -0
  136. package/dist/store/runtime-clients/composer-runtime-client.d.ts.map +1 -1
  137. package/dist/store/runtime-clients/composer-runtime-client.js +97 -61
  138. package/dist/store/runtime-clients/composer-runtime-client.js.map +1 -1
  139. package/dist/store/runtime-clients/message-runtime-client.d.ts +1 -0
  140. package/dist/store/runtime-clients/message-runtime-client.d.ts.map +1 -1
  141. package/dist/store/runtime-clients/message-runtime-client.js +221 -144
  142. package/dist/store/runtime-clients/message-runtime-client.js.map +1 -1
  143. package/dist/store/runtime-clients/thread-list-item-runtime-client.d.ts.map +1 -1
  144. package/dist/store/runtime-clients/thread-list-item-runtime-client.js +1 -1
  145. package/dist/store/runtime-clients/thread-list-item-runtime-client.js.map +1 -1
  146. package/dist/store/runtime-clients/thread-list-runtime-client.d.ts.map +1 -1
  147. package/dist/store/runtime-clients/thread-list-runtime-client.js +54 -52
  148. package/dist/store/runtime-clients/thread-list-runtime-client.js.map +1 -1
  149. package/dist/store/runtime-clients/thread-runtime-client.d.ts.map +1 -1
  150. package/dist/store/runtime-clients/thread-runtime-client.js +193 -121
  151. package/dist/store/runtime-clients/thread-runtime-client.js.map +1 -1
  152. package/dist/store/scopes/composer.d.ts +10 -2
  153. package/dist/store/scopes/composer.d.ts.map +1 -1
  154. package/dist/store/scopes/message.d.ts +30 -1
  155. package/dist/store/scopes/message.d.ts.map +1 -1
  156. package/dist/store/scopes/thread-list-item.d.ts +2 -2
  157. package/dist/store/scopes/thread-list-item.d.ts.map +1 -1
  158. package/dist/store/scopes/thread.d.ts +8 -0
  159. package/dist/store/scopes/thread.d.ts.map +1 -1
  160. package/dist/store/scopes/threads.d.ts +1 -0
  161. package/dist/store/scopes/threads.d.ts.map +1 -1
  162. package/dist/types/attachment.d.ts +2 -1
  163. package/dist/types/attachment.d.ts.map +1 -1
  164. package/dist/types/attachment.js +2 -1
  165. package/dist/types/attachment.js.map +1 -1
  166. package/dist/utils/abortable-iterable.d.ts +19 -0
  167. package/dist/utils/abortable-iterable.d.ts.map +1 -0
  168. package/dist/utils/abortable-iterable.js +72 -0
  169. package/dist/utils/abortable-iterable.js.map +1 -0
  170. package/dist/utils/composite-context-provider.d.ts.map +1 -1
  171. package/dist/utils/composite-context-provider.js +20 -2
  172. package/dist/utils/composite-context-provider.js.map +1 -1
  173. package/dist/utils/record.d.ts +11 -0
  174. package/dist/utils/record.d.ts.map +1 -0
  175. package/dist/utils/record.js +12 -0
  176. package/dist/utils/record.js.map +1 -0
  177. package/package.json +10 -10
  178. package/src/adapters/attachment.test.ts +50 -1
  179. package/src/adapters/attachment.ts +7 -4
  180. package/src/index.ts +1 -0
  181. package/src/internal.ts +4 -0
  182. package/src/model-context/frame/host.test.ts +24 -0
  183. package/src/model-context/frame/host.ts +10 -4
  184. package/src/model-context/frame/provider.test.ts +71 -0
  185. package/src/model-context/frame/provider.ts +37 -7
  186. package/src/model-context/interactable-composer-metadata.test.ts +28 -0
  187. package/src/model-context/interactable-composer-metadata.ts +9 -3
  188. package/src/model-context/registry.test.ts +12 -0
  189. package/src/model-context/registry.ts +3 -2
  190. package/src/model-context/types.test.ts +25 -0
  191. package/src/model-context/types.ts +9 -4
  192. package/src/react/RuntimeAdapter.test.ts +34 -0
  193. package/src/react/client/InMemoryThreadList.ts +1 -0
  194. package/src/react/client/Interactables.test.ts +28 -0
  195. package/src/react/client/Interactables.ts +15 -13
  196. package/src/react/client/RemoteThreadList.background.test.ts +148 -6
  197. package/src/react/client/RemoteThreadList.concurrent.test.tsx +42 -1
  198. package/src/react/client/RemoteThreadList.test.ts +345 -3
  199. package/src/react/client/RemoteThreadList.ts +166 -110
  200. package/src/react/client/Tools.ts +22 -11
  201. package/src/react/interactables-legacy/Interactables.ts +16 -16
  202. package/src/react/interactables-shared/useInteractablePersistenceQueue.ts +9 -10
  203. package/src/react/model-context/makeAssistantTool.ts +2 -2
  204. package/src/react/model-context/useAssistantTool.ts +2 -2
  205. package/src/react/model-context/useToolArgsStatus.test.tsx +30 -0
  206. package/src/react/model-context/useToolArgsStatus.ts +2 -1
  207. package/src/react/runtimes/RemoteThreadListHookInstanceManager.destroy.test.tsx +172 -0
  208. package/src/react/runtimes/RemoteThreadListHookInstanceManager.running.test.tsx +33 -0
  209. package/src/react/runtimes/RemoteThreadListHookInstanceManager.test.tsx +7 -1
  210. package/src/react/runtimes/RemoteThreadListHookInstanceManager.tsx +28 -5
  211. package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.load-error.test.ts +132 -0
  212. package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.switch-delete.test.ts +105 -0
  213. package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.tsx +136 -111
  214. package/src/react/runtimes/RemoteThreadResource.test.ts +9 -0
  215. package/src/react/runtimes/RemoteThreadResource.ts +19 -12
  216. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.test.tsx +483 -6
  217. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.ts +504 -68
  218. package/src/react/runtimes/cloud/createCloudThreadListAdapter.test.ts +15 -0
  219. package/src/react/runtimes/cloud/createCloudThreadListAdapter.ts +1 -0
  220. package/src/react/runtimes/useExternalStoreRuntime.lifecycle.test.tsx +41 -0
  221. package/src/react/runtimes/useExternalStoreRuntime.ts +10 -4
  222. package/src/react/runtimes/useLocalRuntime.test.tsx +20 -0
  223. package/src/react/runtimes/useRemoteThreadListRuntime.load-error.test.tsx +108 -0
  224. package/src/react/runtimes/useRemoteThreadListRuntime.ts +33 -0
  225. package/src/runtime/api/message-runtime.test.ts +75 -0
  226. package/src/runtime/api/thread-list-item-runtime.ts +10 -3
  227. package/src/runtime/api/thread-list-runtime.ts +21 -11
  228. package/src/runtime/api/thread-runtime.test.ts +78 -0
  229. package/src/runtime/api/thread-runtime.ts +8 -1
  230. package/src/runtime/base/base-composer-runtime-core.ts +36 -8
  231. package/src/runtime/base/base-thread-runtime-core.test.ts +55 -1
  232. package/src/runtime/base/base-thread-runtime-core.ts +6 -4
  233. package/src/runtime/interfaces/composer-runtime-core.ts +10 -5
  234. package/src/runtime/interfaces/thread-list-runtime-core.ts +5 -1
  235. package/src/runtime/utils/external-message-conversion.test.ts +68 -0
  236. package/src/runtime/utils/external-message-conversion.ts +1 -1
  237. package/src/runtime/utils/message-repository.ts +29 -17
  238. package/src/runtime/utils/thread-message-like.ts +1 -1
  239. package/src/runtimes/external-store/external-store-thread-list-runtime-core.ts +18 -13
  240. package/src/runtimes/internal.ts +1 -0
  241. package/src/runtimes/remote-thread-list/remote-thread-state.test.ts +128 -2
  242. package/src/runtimes/remote-thread-list/remote-thread-state.ts +182 -42
  243. package/src/runtimes/remote-thread-list/title-generation.test.ts +602 -0
  244. package/src/runtimes/remote-thread-list/title-generation.ts +329 -0
  245. package/src/runtimes/remote-thread-list/title.test.ts +62 -0
  246. package/src/runtimes/remote-thread-list/title.ts +13 -0
  247. package/src/runtimes/remote-thread-list/types.ts +10 -0
  248. package/src/store/clients/external-thread.ts +48 -3
  249. package/src/store/clients/model-context-client.ts +5 -1
  250. package/src/store/clients/single-thread-list.ts +1 -0
  251. package/src/store/clients/suggestions.test.ts +40 -0
  252. package/src/store/clients/suggestions.ts +13 -10
  253. package/src/store/runtime-clients/composer-runtime-client.ts +42 -9
  254. package/src/store/runtime-clients/message-runtime-client.ts +54 -6
  255. package/src/store/runtime-clients/thread-list-item-runtime-client.ts +4 -2
  256. package/src/store/runtime-clients/thread-list-runtime-client.ts +1 -0
  257. package/src/store/runtime-clients/thread-runtime-client.ts +58 -10
  258. package/src/store/scopes/composer.ts +16 -4
  259. package/src/store/scopes/message.ts +14 -0
  260. package/src/store/scopes/thread-list-item.ts +5 -2
  261. package/src/store/scopes/thread.ts +4 -0
  262. package/src/store/scopes/threads.ts +1 -0
  263. package/src/tests/MessageRepository.test.ts +116 -0
  264. package/src/tests/OptimisticState-list-race.test.ts +1 -0
  265. package/src/tests/RemoteThreadListThreadListRuntimeCore-initialize-adapter-change.test.ts +38 -0
  266. package/src/tests/RemoteThreadListThreadListRuntimeCore-load-race.test.ts +47 -0
  267. package/src/tests/RemoteThreadListThreadListRuntimeCore-title-race.test.ts +89 -0
  268. package/src/tests/attachment-adapters.test.ts +14 -0
  269. package/src/tests/base-composer-runtime-core.test.ts +39 -0
  270. package/src/tests/external-store-thread-list-runtime-core.test.ts +83 -0
  271. package/src/tests/external-store-thread-runtime-core-adapter.test.ts +27 -18
  272. package/src/tests/external-thread-attachments.test.tsx +139 -0
  273. package/src/tests/external-thread-parity.test.tsx +23 -0
  274. package/src/tests/remote-thread-list-isLoading.test.ts +1 -0
  275. package/src/tests/thread-list-runtime-getLoadThreadsPromise.test.ts +1 -0
  276. package/src/tests/thread-switch-events.test.tsx +78 -0
  277. package/src/tests/tools-scope-migration.test.tsx +28 -0
  278. package/src/types/attachment.ts +4 -0
  279. package/src/utils/abortable-iterable.test.ts +213 -0
  280. package/src/utils/abortable-iterable.ts +93 -0
  281. package/src/utils/composite-context-provider.test.ts +25 -0
  282. package/src/utils/composite-context-provider.ts +23 -2
  283. package/src/utils/record.test.ts +48 -0
  284. package/src/utils/record.ts +11 -0
@@ -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,23 @@ 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";
49
60
 
50
61
  const RESOLVED_PROMISE = Promise.resolve();
51
62
 
@@ -67,6 +78,7 @@ export type RemoteThreadListProps = {
67
78
  onThreadIdChange?: ((threadId: string | undefined) => void) | undefined;
68
79
  onSwitchToThread?: ((threadId: string) => void) | undefined;
69
80
  onSwitchToNewThread?: (() => void) | undefined;
81
+ /** Called after the backing adapter successfully deletes the thread. */
70
82
  onDelete?: ((threadId: string) => void) | undefined;
71
83
  /**
72
84
  * Keeps every thread the session switched to mounted, so a run continues
@@ -101,16 +113,6 @@ const toInitializeResult = (
101
113
  externalId: result.externalId,
102
114
  });
103
115
 
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
116
  const useThreadListItemClient = (props: {
115
117
  data: RemoteThreadData;
116
118
  isRunning: boolean;
@@ -120,7 +122,7 @@ const useThreadListItemClient = (props: {
120
122
  onArchive: () => void;
121
123
  onUnarchive: () => void;
122
124
  onDelete: () => void;
123
- onGenerateTitle: () => void;
125
+ onGenerateTitle: (options?: { automatic?: boolean }) => void;
124
126
  onInitialize: () => Promise<{
125
127
  remoteId: string;
126
128
  externalId: string | undefined;
@@ -271,7 +273,7 @@ const useRemoteThreadBody = ({
271
273
  useEffect(() => {
272
274
  if (!hasTitleSource || titleFiredRef.current) return;
273
275
  titleFiredRef.current = true;
274
- client.threadListItem.generateTitle();
276
+ client.threadListItem.generateTitle({ automatic: true });
275
277
  }, [hasTitleSource]);
276
278
  return body.methods;
277
279
  });
@@ -343,6 +345,7 @@ const useRemoteThreadListView = ({
343
345
  onGenerateTitle: (
344
346
  threadId: string,
345
347
  messages: readonly ThreadMessage[] | undefined,
348
+ options?: { automatic?: boolean },
346
349
  ) => Promise<void>;
347
350
  onInitialize: (threadId: string) => Promise<{
348
351
  remoteId: string;
@@ -385,7 +388,7 @@ const useRemoteThreadListView = ({
385
388
  onUnarchive: () =>
386
389
  handleThreadListAction("unarchive", () => onUnarchive(data.id)),
387
390
  onDelete: () => handleThreadListAction("delete", () => onDelete(data.id)),
388
- onGenerateTitle: () =>
391
+ onGenerateTitle: (options) =>
389
392
  handleThreadListAction("generate title", () =>
390
393
  onGenerateTitle(
391
394
  data.id,
@@ -394,6 +397,7 @@ const useRemoteThreadListView = ({
394
397
  : mainThreadClient.state.messages) as
395
398
  | readonly ThreadMessage[]
396
399
  | undefined,
400
+ options,
397
401
  ),
398
402
  ),
399
403
  onInitialize: () => onInitialize(data.id),
@@ -489,6 +493,7 @@ const useRemoteThreadList = (
489
493
  adapter,
490
494
  adapterAtLoad: adapter,
491
495
  adapterGeneration: 0,
496
+ titleStates: new Map<string, ThreadTitleState>(),
492
497
  loadGeneration: 0,
493
498
  switchGeneration: 0,
494
499
  loadPromise: undefined as Promise<void> | undefined,
@@ -560,7 +565,7 @@ const useRemoteThreadList = (
560
565
  execute: () => adapter.list(),
561
566
  loading: (state) => {
562
567
  if (generation !== session.loadGeneration) return state;
563
- return { ...state, isLoading: true };
568
+ return { ...state, isLoading: true, loadError: undefined };
564
569
  },
565
570
  then: (state, page) => {
566
571
  if (generation !== session.loadGeneration) return state;
@@ -574,6 +579,7 @@ const useRemoteThreadList = (
574
579
  const merged = {
575
580
  ...state,
576
581
  isLoading: false,
582
+ loadError: undefined,
577
583
  cursor: normalizeCursor(page.nextCursor),
578
584
  threadIds: fresh.threadIds,
579
585
  archivedThreadIds: fresh.archivedThreadIds,
@@ -590,6 +596,7 @@ const useRemoteThreadList = (
590
596
  store.update({
591
597
  ...store.baseValue,
592
598
  isLoading: false,
599
+ loadError: error,
593
600
  });
594
601
  })
595
602
  .then(() => {});
@@ -603,6 +610,7 @@ const useRemoteThreadList = (
603
610
  session.loadMorePromise = undefined;
604
611
  if (adapterChanged) {
605
612
  session.adapterGeneration++;
613
+ session.titleStates.clear();
606
614
  session.switchGeneration++;
607
615
  session.switchTask = undefined;
608
616
  session.adapterAtLoad = adapter;
@@ -627,6 +635,26 @@ const useRemoteThreadList = (
627
635
  store,
628
636
  ]);
629
637
 
638
+ useEffect(() => {
639
+ if (typeof window === "undefined" || typeof document === "undefined") {
640
+ return;
641
+ }
642
+
643
+ const retryAfterError = () => {
644
+ if (store.value.loadError !== undefined) void getLoadThreadsPromise();
645
+ };
646
+ const retryAfterVisible = () => {
647
+ if (document.visibilityState === "visible") retryAfterError();
648
+ };
649
+
650
+ window.addEventListener("online", retryAfterError);
651
+ document.addEventListener("visibilitychange", retryAfterVisible);
652
+ return () => {
653
+ window.removeEventListener("online", retryAfterError);
654
+ document.removeEventListener("visibilitychange", retryAfterVisible);
655
+ };
656
+ }, [getLoadThreadsPromise, store]);
657
+
630
658
  useEffect(() => {
631
659
  void getLoadThreadsPromise();
632
660
  }, [getLoadThreadsPromise]);
@@ -881,70 +909,60 @@ const useRemoteThreadList = (
881
909
  requireAdapterGeneration(adapterGeneration);
882
910
  return result;
883
911
  }
912
+ requireAdapterGeneration(adapterGeneration);
913
+ const initializeTask = currentAdapter.initialize(threadId);
914
+ let removedMappingId: string | undefined;
915
+ let replacementMainThreadId: string | undefined;
884
916
  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
- },
917
+ execute: () => initializeTask,
918
+ optimistic: (state) =>
919
+ promoteNewThreadReducer(state, threadId, initializeTask),
903
920
  then: (state, { remoteId, externalId }) => {
904
921
  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 }),
922
+ // Background mode still owns the initializing body. Single-body mode
923
+ // has already replaced it, so retain the currently mounted body.
924
+ const retainedThreadId = backgroundThreads
925
+ ? threadId
926
+ : session.mainThreadId;
927
+ const reconciliation = reconcileInitializedThread(
928
+ state,
929
+ threadId,
922
930
  remoteId,
923
931
  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
- };
932
+ retainedThreadId,
933
+ );
934
+ removedMappingId = reconciliation.removedMappingId;
935
+ if (removedMappingId === session.mainThreadId) {
936
+ replacementMainThreadId = reconciliation.survivorMappingId;
937
+ }
938
+ return reconciliation.state;
939
939
  },
940
940
  });
941
941
  requireAdapterGeneration(adapterGeneration);
942
+ if (removedMappingId !== undefined) {
943
+ setStartedIds((prev) =>
944
+ prev.filter((startedId) => startedId !== removedMappingId),
945
+ );
946
+ }
947
+ if (
948
+ replacementMainThreadId !== undefined &&
949
+ session.mainThreadId === removedMappingId
950
+ ) {
951
+ assignMainThreadId(replacementMainThreadId);
952
+ }
942
953
  if (threadId === session.mainThreadId) {
943
954
  notifyRemoteId(result.remoteId, true);
944
955
  }
945
956
  return toInitializeResult(result);
946
957
  },
947
- [notifyRemoteId, requireAdapterGeneration, session, store],
958
+ [
959
+ assignMainThreadId,
960
+ backgroundThreads,
961
+ notifyRemoteId,
962
+ requireAdapterGeneration,
963
+ session,
964
+ store,
965
+ ],
948
966
  );
949
967
 
950
968
  const rename = useCallback(
@@ -956,27 +974,43 @@ const useRemoteThreadList = (
956
974
  if (data.status === "new") {
957
975
  throw threadStatusError(threadIdOrRemoteId, data.status, "be renamed");
958
976
  }
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,
977
+ const claim = startThreadTitleRename(
978
+ session.titleStates,
979
+ data.id,
980
+ newTitle,
981
+ );
982
+ return store
983
+ .optimisticUpdate({
984
+ execute: async () => {
985
+ const { remoteId } = await data.initializeTask;
986
+ requireAdapterGeneration(adapterGeneration);
987
+ return currentAdapter.rename(remoteId, newTitle);
988
+ },
989
+ optimistic: (state) => {
990
+ const current = getThreadData(state, threadIdOrRemoteId);
991
+ if (!current) return state;
992
+ return {
993
+ ...state,
994
+ threadData: {
995
+ ...state.threadData,
996
+ [current.id]: {
997
+ ...current,
998
+ title: newTitle,
999
+ },
975
1000
  },
976
- },
977
- };
978
- },
979
- });
1001
+ };
1002
+ },
1003
+ })
1004
+ .then(
1005
+ (result) => {
1006
+ finishThreadTitleRename(session.titleStates, data.id, claim, true);
1007
+ return result;
1008
+ },
1009
+ (error: unknown) => {
1010
+ finishThreadTitleRename(session.titleStates, data.id, claim, false);
1011
+ throw error;
1012
+ },
1013
+ );
980
1014
  },
981
1015
  [requireAdapterGeneration, session, store],
982
1016
  );
@@ -1104,8 +1138,7 @@ const useRemoteThreadList = (
1104
1138
  }
1105
1139
  await ensureNotMain(data.id);
1106
1140
  requireAdapterGeneration(adapterGeneration);
1107
- onDelete?.(data.id);
1108
- return store.optimisticUpdate({
1141
+ const result = await store.optimisticUpdate({
1109
1142
  execute: async () => {
1110
1143
  const { remoteId } = await data.initializeTask;
1111
1144
  requireAdapterGeneration(adapterGeneration);
@@ -1113,6 +1146,13 @@ const useRemoteThreadList = (
1113
1146
  },
1114
1147
  optimistic: (state) => updateStatusReducer(state, data.id, "deleted"),
1115
1148
  });
1149
+ // An adapter swap resets the optimistic layer, and a listed thread's slot
1150
+ // id is its remote id, so a replacement adapter can re-list this slot
1151
+ // while the deletion is in flight.
1152
+ if (getThreadData(store.value, data.id) !== undefined) return result;
1153
+ clearThreadTitleState(session.titleStates, data.id);
1154
+ onDelete?.(data.id);
1155
+ return result;
1116
1156
  },
1117
1157
  [ensureNotMain, onDelete, requireAdapterGeneration, session, store],
1118
1158
  );
@@ -1121,6 +1161,7 @@ const useRemoteThreadList = (
1121
1161
  async (
1122
1162
  threadIdOrRemoteId: string,
1123
1163
  messages: readonly ThreadMessage[] | undefined,
1164
+ options?: { automatic?: boolean },
1124
1165
  ) => {
1125
1166
  const currentAdapter = session.adapter;
1126
1167
  const adapterGeneration = session.adapterGeneration;
@@ -1144,27 +1185,39 @@ const useRemoteThreadList = (
1144
1185
  return;
1145
1186
  }
1146
1187
  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,
1188
+ await runThreadTitleGeneration({
1189
+ states: session.titleStates,
1190
+ threadId: data.id,
1191
+ automatic: options?.automatic === true,
1192
+ generate: async (onTitle) => {
1193
+ const stream = await currentAdapter.generateTitle(remoteId, messages);
1194
+ requireAdapterGeneration(adapterGeneration);
1195
+ await applyTitleStream(stream, onTitle);
1196
+ },
1197
+ rename: async (title) => {
1198
+ requireAdapterGeneration(adapterGeneration);
1199
+ await currentAdapter.rename(remoteId, title);
1200
+ },
1201
+ applyTitle: async (title) => {
1202
+ await store.optimisticUpdate({
1203
+ execute: async () => {},
1204
+ optimistic: (state) => {
1205
+ if (adapterGeneration !== session.adapterGeneration) return state;
1206
+ const current = getThreadData(state, data.id);
1207
+ if (!current) return state;
1208
+ return {
1209
+ ...state,
1210
+ threadData: {
1211
+ ...state.threadData,
1212
+ [current.id]: {
1213
+ ...current,
1214
+ title,
1215
+ },
1163
1216
  },
1164
- },
1165
- };
1166
- },
1167
- });
1217
+ };
1218
+ },
1219
+ });
1220
+ },
1168
1221
  });
1169
1222
  },
1170
1223
  [backgroundThreads, requireAdapterGeneration, session, store],
@@ -1192,7 +1245,8 @@ const useRemoteThreadList = (
1192
1245
  onArchive: (id) => archive(id),
1193
1246
  onUnarchive: (id) => unarchive(id),
1194
1247
  onDelete: (id) => deleteThread(id),
1195
- onGenerateTitle: (id, messages) => generateTitle(id, messages),
1248
+ onGenerateTitle: (id, messages, options) =>
1249
+ generateTitle(id, messages, options),
1196
1250
  onInitialize: async (id) => toInitializeResult(await initialize(id)),
1197
1251
  onDetach: (id) => detach(id),
1198
1252
  });
@@ -1230,6 +1284,7 @@ const useRemoteThreadList = (
1230
1284
  mainThreadId,
1231
1285
  newThreadId: listState.newThreadId ?? null,
1232
1286
  isLoading: listState.isLoading,
1287
+ loadError: listState.loadError,
1233
1288
  isLoadingMore: listState.isLoadingMore,
1234
1289
  hasMore: listState.cursor !== undefined,
1235
1290
  threadIds: listState.threadIds,
@@ -1241,6 +1296,7 @@ const useRemoteThreadList = (
1241
1296
  listState.archivedThreadIds,
1242
1297
  listState.cursor,
1243
1298
  listState.isLoading,
1299
+ listState.loadError,
1244
1300
  listState.isLoadingMore,
1245
1301
  listState.newThreadId,
1246
1302
  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 = {
@@ -18,11 +18,12 @@ import { ModelContext } from "../../store";
18
18
  import { buildInteractableModelContext } from "./interactable-model-context";
19
19
  import { notifySubscribers as notifyStateSubscribers } from "../../subscribable/subscribable";
20
20
  import { useInteractablePersistenceQueue } from "../interactables-shared/useInteractablePersistenceQueue";
21
+ import { nullProtoRecord } from "../../utils/record";
21
22
 
22
23
  const useInteractables = (): ClientOutput<"interactables"> => {
23
24
  const [state, setState] = useState<InteractablesState>(() => ({
24
- definitions: {},
25
- persistence: {},
25
+ definitions: nullProtoRecord(),
26
+ persistence: nullProtoRecord(),
26
27
  }));
27
28
 
28
29
  const clientRef = useAssistantClientRef();
@@ -49,7 +50,7 @@ const useInteractables = (): ClientOutput<"interactables"> => {
49
50
  );
50
51
 
51
52
  const exportState = useCallback((): InteractablePersistedState => {
52
- const result: InteractablePersistedState = {};
53
+ const result = nullProtoRecord<InteractablePersistedState[string]>();
53
54
  for (const [id, def] of Object.entries(stateRef.current.definitions)) {
54
55
  result[id] = { name: def.name, state: def.state };
55
56
  }
@@ -86,7 +87,7 @@ const useInteractables = (): ClientOutput<"interactables"> => {
86
87
  }
87
88
  setStateAndRef((prev) => {
88
89
  let changed = false;
89
- const definitions = { ...prev.definitions };
90
+ const definitions = nullProtoRecord(prev.definitions);
90
91
  for (const [id, entry] of Object.entries(saved)) {
91
92
  if (definitions[id]) {
92
93
  definitions[id] = { ...definitions[id], state: entry.state };
@@ -114,10 +115,9 @@ const useInteractables = (): ClientOutput<"interactables"> => {
114
115
  if (!existing) return prev;
115
116
  return {
116
117
  ...prev,
117
- definitions: {
118
- ...prev.definitions,
118
+ definitions: nullProtoRecord(prev.definitions, {
119
119
  [id]: { ...existing, state: updater(existing.state) },
120
- },
120
+ }),
121
121
  };
122
122
  });
123
123
  if (stateRef.current.definitions[id]) schedulePersistence(id);
@@ -132,10 +132,9 @@ const useInteractables = (): ClientOutput<"interactables"> => {
132
132
  if (!existing) return prev;
133
133
  return {
134
134
  ...prev,
135
- definitions: {
136
- ...prev.definitions,
135
+ definitions: nullProtoRecord(prev.definitions, {
137
136
  [id]: { ...existing, selected },
138
- },
137
+ }),
139
138
  };
140
139
  });
141
140
  },
@@ -194,8 +193,7 @@ const useInteractables = (): ClientOutput<"interactables"> => {
194
193
 
195
194
  setStateAndRef((prev) => ({
196
195
  ...prev,
197
- definitions: {
198
- ...prev.definitions,
196
+ definitions: nullProtoRecord(prev.definitions, {
199
197
  [def.id]: {
200
198
  id: def.id,
201
199
  name: def.name,
@@ -205,7 +203,7 @@ const useInteractables = (): ClientOutput<"interactables"> => {
205
203
  prev.definitions[def.id]?.state ?? detached ?? def.initialState,
206
204
  selected: def.selected,
207
205
  },
208
- },
206
+ }),
209
207
  }));
210
208
 
211
209
  return () => {
@@ -216,9 +214,11 @@ const useInteractables = (): ClientOutput<"interactables"> => {
216
214
  detachedStateRef.current.set(def.id, existing.state);
217
215
  }
218
216
  partialSchemaCacheRef.current.delete(def.id);
219
- const { [def.id]: _, ...rest } = prev.definitions;
220
- const { [def.id]: __, ...restPersistence } = prev.persistence;
221
- return { ...prev, definitions: rest, persistence: restPersistence };
217
+ const definitions = nullProtoRecord(prev.definitions);
218
+ const persistence = nullProtoRecord(prev.persistence);
219
+ delete definitions[def.id];
220
+ delete persistence[def.id];
221
+ return { ...prev, definitions, persistence };
222
222
  });
223
223
  };
224
224
  },
@@ -1,4 +1,5 @@
1
1
  import { useCallback, useRef, type RefObject } from "react";
2
+ import { nullProtoRecord } from "../../utils/record";
2
3
 
3
4
  const PERSISTENCE_DEBOUNCE_MS = 500;
4
5
 
@@ -90,15 +91,13 @@ export const useInteractablePersistenceQueue = <State>({
90
91
  const { adapter, payload, dirtyIds, seq } = resolved;
91
92
  inFlightPersistenceRef.current += 1;
92
93
 
93
- updatePersistenceStatus((prev) => ({
94
- ...prev,
95
- ...Object.fromEntries(
96
- [...dirtyIds].map((id) => [
97
- id,
98
- { isPending: true, error: undefined },
99
- ]),
100
- ),
101
- }));
94
+ updatePersistenceStatus((prev) => {
95
+ const persistence = nullProtoRecord(prev);
96
+ for (const id of dirtyIds) {
97
+ persistence[id] = { isPending: true, error: undefined };
98
+ }
99
+ return persistence;
100
+ });
102
101
 
103
102
  const settleBatch = (status: PersistenceStatus | undefined) => {
104
103
  const settledIds: string[] = [];
@@ -114,7 +113,7 @@ export const useInteractablePersistenceQueue = <State>({
114
113
  if (settledIds.length === 0) return;
115
114
  updatePersistenceStatus((prev) => {
116
115
  let changed = false;
117
- const persistence = { ...prev };
116
+ const persistence = nullProtoRecord(prev);
118
117
  for (const id of settledIds) {
119
118
  if (prev[id] === undefined) continue;
120
119
  if (status === undefined) delete persistence[id];
@@ -8,7 +8,7 @@ import { type AssistantToolProps, useAssistantTool } from "./useAssistantTool";
8
8
  * subtree.
9
9
  *
10
10
  * @deprecated Use a toolkit with `Tools({ toolkit })` and register it via
11
- * `useAui({ tools: Tools({ toolkit }) })` instead. See
11
+ * `AuiConfig({ tools: Tools({ toolkit }) })` on the provider's `config` prop instead. See
12
12
  * https://assistant-ui.com/docs/migrations/toolkit-tools.
13
13
  */
14
14
  export type AssistantTool = FC & {
@@ -25,7 +25,7 @@ export type AssistantTool = FC & {
25
25
  * @param tool - Tool definition and name to register.
26
26
  *
27
27
  * @deprecated Use a toolkit with `Tools({ toolkit })` and register it via
28
- * `useAui({ tools: Tools({ toolkit }) })` instead. See
28
+ * `AuiConfig({ tools: Tools({ toolkit }) })` on the provider's `config` prop instead. See
29
29
  * https://assistant-ui.com/docs/migrations/toolkit-tools.
30
30
  */
31
31
  export const makeAssistantTool = <