@assistant-ui/core 0.3.16 → 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 (474) 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 +3 -3
  5. package/dist/index.js +2 -2
  6. package/dist/index.js.map +1 -1
  7. package/dist/internal.d.ts +4 -2
  8. package/dist/internal.js +4 -2
  9. package/dist/model-context/frame/host.d.ts.map +1 -1
  10. package/dist/model-context/frame/host.js +10 -4
  11. package/dist/model-context/frame/host.js.map +1 -1
  12. package/dist/model-context/frame/provider.d.ts +5 -0
  13. package/dist/model-context/frame/provider.d.ts.map +1 -1
  14. package/dist/model-context/frame/provider.js +101 -28
  15. package/dist/model-context/frame/provider.js.map +1 -1
  16. package/dist/model-context/interactable-composer-metadata.d.ts.map +1 -1
  17. package/dist/model-context/interactable-composer-metadata.js +16 -9
  18. package/dist/model-context/interactable-composer-metadata.js.map +1 -1
  19. package/dist/model-context/registry.d.ts.map +1 -1
  20. package/dist/model-context/registry.js +3 -5
  21. package/dist/model-context/registry.js.map +1 -1
  22. package/dist/model-context/tool-call-text.d.ts +26 -0
  23. package/dist/model-context/tool-call-text.d.ts.map +1 -0
  24. package/dist/model-context/tool-call-text.js +18 -0
  25. package/dist/model-context/tool-call-text.js.map +1 -0
  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/RuntimeAdapter.js +1 -1
  30. package/dist/react/adapters/LocalStorageThreadListAdapter.d.ts.map +1 -1
  31. package/dist/react/adapters/LocalStorageThreadListAdapter.js +1 -1
  32. package/dist/react/adapters/LocalStorageThreadListAdapter.js.map +1 -1
  33. package/dist/react/client/AdaptedRemoteThread.js +1 -1
  34. package/dist/react/client/InMemoryThreadList.d.ts.map +1 -1
  35. package/dist/react/client/InMemoryThreadList.js +63 -57
  36. package/dist/react/client/InMemoryThreadList.js.map +1 -1
  37. package/dist/react/client/Interactables.d.ts.map +1 -1
  38. package/dist/react/client/Interactables.js +25 -28
  39. package/dist/react/client/Interactables.js.map +1 -1
  40. package/dist/react/client/RemoteThreadList.d.ts +16 -0
  41. package/dist/react/client/RemoteThreadList.d.ts.map +1 -1
  42. package/dist/react/client/RemoteThreadList.js +354 -242
  43. package/dist/react/client/RemoteThreadList.js.map +1 -1
  44. package/dist/react/client/Tools.d.ts.map +1 -1
  45. package/dist/react/client/Tools.js +24 -18
  46. package/dist/react/client/Tools.js.map +1 -1
  47. package/dist/react/index.d.ts +2 -1
  48. package/dist/react/index.js +2 -1
  49. package/dist/react/interactables-legacy/Interactables.js +246 -125
  50. package/dist/react/interactables-legacy/Interactables.js.map +1 -1
  51. package/dist/react/interactables-shared/useInteractablePersistenceQueue.d.ts.map +1 -1
  52. package/dist/react/interactables-shared/useInteractablePersistenceQueue.js +11 -9
  53. package/dist/react/interactables-shared/useInteractablePersistenceQueue.js.map +1 -1
  54. package/dist/react/model-context/makeAssistantTool.d.ts +2 -2
  55. package/dist/react/model-context/makeAssistantTool.js +1 -1
  56. package/dist/react/model-context/makeAssistantTool.js.map +1 -1
  57. package/dist/react/model-context/toolbox.d.ts +2 -14
  58. package/dist/react/model-context/toolbox.d.ts.map +1 -1
  59. package/dist/react/model-context/toolbox.js +1 -13
  60. package/dist/react/model-context/toolbox.js.map +1 -1
  61. package/dist/react/model-context/useAssistantContext.js +37 -7
  62. package/dist/react/model-context/useAssistantContext.js.map +1 -1
  63. package/dist/react/model-context/useAssistantTool.d.ts +2 -2
  64. package/dist/react/model-context/useAssistantTool.js +1 -1
  65. package/dist/react/model-context/useAssistantTool.js.map +1 -1
  66. package/dist/react/model-context/useAuiToolOverrides.js +31 -5
  67. package/dist/react/model-context/useAuiToolOverrides.js.map +1 -1
  68. package/dist/react/model-context/useInlineRender.d.ts.map +1 -1
  69. package/dist/react/model-context/useInlineRender.js +10 -36
  70. package/dist/react/model-context/useInlineRender.js.map +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/primitive-hooks/useMessageError.d.ts.map +1 -1
  75. package/dist/react/primitive-hooks/useMessageError.js +2 -8
  76. package/dist/react/primitive-hooks/useMessageError.js.map +1 -1
  77. package/dist/react/primitive-hooks/useThreadListLoadMore.d.ts.map +1 -1
  78. package/dist/react/primitive-hooks/useThreadListLoadMore.js +2 -4
  79. package/dist/react/primitive-hooks/useThreadListLoadMore.js.map +1 -1
  80. package/dist/react/primitives/composer/ComposerAttachments.d.ts.map +1 -1
  81. package/dist/react/primitives/composer/ComposerAttachments.js +4 -3
  82. package/dist/react/primitives/composer/ComposerAttachments.js.map +1 -1
  83. package/dist/react/primitives/composer/ComposerQueue.d.ts.map +1 -1
  84. package/dist/react/primitives/composer/ComposerQueue.js +4 -3
  85. package/dist/react/primitives/composer/ComposerQueue.js.map +1 -1
  86. package/dist/react/primitives/message/MessageAttachments.d.ts.map +1 -1
  87. package/dist/react/primitives/message/MessageAttachments.js +7 -6
  88. package/dist/react/primitives/message/MessageAttachments.js.map +1 -1
  89. package/dist/react/primitives/message/MessageGroupedParts.js +2 -2
  90. package/dist/react/primitives/message/MessageGroupedParts.js.map +1 -1
  91. package/dist/react/primitives/message/MessageParts.js +3 -3
  92. package/dist/react/primitives/message/MessageParts.js.map +1 -1
  93. package/dist/react/primitives/threadList/ThreadListItems.d.ts.map +1 -1
  94. package/dist/react/primitives/threadList/ThreadListItems.js +5 -4
  95. package/dist/react/primitives/threadList/ThreadListItems.js.map +1 -1
  96. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.d.ts +6 -4
  97. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.d.ts.map +1 -1
  98. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js +59 -29
  99. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js.map +1 -1
  100. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts +11 -6
  101. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts.map +1 -1
  102. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js +160 -143
  103. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js.map +1 -1
  104. package/dist/react/runtimes/RemoteThreadResource.d.ts +2 -6
  105. package/dist/react/runtimes/RemoteThreadResource.d.ts.map +1 -1
  106. package/dist/react/runtimes/RemoteThreadResource.js +15 -13
  107. package/dist/react/runtimes/RemoteThreadResource.js.map +1 -1
  108. package/dist/react/runtimes/assistant-transport/useLatestRef.js +2 -2
  109. package/dist/react/runtimes/assistant-transport/useLatestRef.js.map +1 -1
  110. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.d.ts +4 -1
  111. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.d.ts.map +1 -1
  112. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js +424 -52
  113. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js.map +1 -1
  114. package/dist/react/runtimes/cloud/auiV0.d.ts +5 -1
  115. package/dist/react/runtimes/cloud/auiV0.d.ts.map +1 -1
  116. package/dist/react/runtimes/cloud/auiV0.js.map +1 -1
  117. package/dist/react/runtimes/cloud/createCloudThreadListAdapter.d.ts +29 -0
  118. package/dist/react/runtimes/cloud/createCloudThreadListAdapter.d.ts.map +1 -0
  119. package/dist/react/runtimes/cloud/createCloudThreadListAdapter.js +160 -0
  120. package/dist/react/runtimes/cloud/createCloudThreadListAdapter.js.map +1 -0
  121. package/dist/react/runtimes/cloud/useCloudThreadListAdapter.d.ts +1 -9
  122. package/dist/react/runtimes/cloud/useCloudThreadListAdapter.d.ts.map +1 -1
  123. package/dist/react/runtimes/cloud/useCloudThreadListAdapter.js +16 -153
  124. package/dist/react/runtimes/cloud/useCloudThreadListAdapter.js.map +1 -1
  125. package/dist/react/runtimes/external-message-converter.js +1 -1
  126. package/dist/react/runtimes/external-message-converter.js.map +1 -1
  127. package/dist/react/runtimes/useExternalStoreRuntime.js +75 -46
  128. package/dist/react/runtimes/useExternalStoreRuntime.js.map +1 -1
  129. package/dist/react/runtimes/useRemoteThreadListRuntime.d.ts.map +1 -1
  130. package/dist/react/runtimes/useRemoteThreadListRuntime.js +77 -29
  131. package/dist/react/runtimes/useRemoteThreadListRuntime.js.map +1 -1
  132. package/dist/react/types/MessagePartComponentTypes.d.ts +7 -3
  133. package/dist/react/types/MessagePartComponentTypes.d.ts.map +1 -1
  134. package/dist/react/types/scopes/tools.d.ts +2 -0
  135. package/dist/react/types/scopes/tools.d.ts.map +1 -1
  136. package/dist/runtime/api/message-part-runtime.d.ts +2 -2
  137. package/dist/runtime/api/message-part-runtime.d.ts.map +1 -1
  138. package/dist/runtime/api/message-part-runtime.js +1 -1
  139. package/dist/runtime/api/message-part-runtime.js.map +1 -1
  140. package/dist/runtime/api/thread-list-item-runtime.d.ts +7 -3
  141. package/dist/runtime/api/thread-list-item-runtime.d.ts.map +1 -1
  142. package/dist/runtime/api/thread-list-item-runtime.js +2 -2
  143. package/dist/runtime/api/thread-list-item-runtime.js.map +1 -1
  144. package/dist/runtime/api/thread-list-runtime.d.ts +12 -1
  145. package/dist/runtime/api/thread-list-runtime.d.ts.map +1 -1
  146. package/dist/runtime/api/thread-list-runtime.js +23 -14
  147. package/dist/runtime/api/thread-list-runtime.js.map +1 -1
  148. package/dist/runtime/api/thread-runtime.d.ts +5 -1
  149. package/dist/runtime/api/thread-runtime.d.ts.map +1 -1
  150. package/dist/runtime/api/thread-runtime.js +1 -1
  151. package/dist/runtime/api/thread-runtime.js.map +1 -1
  152. package/dist/runtime/base/base-composer-runtime-core.d.ts.map +1 -1
  153. package/dist/runtime/base/base-composer-runtime-core.js +16 -12
  154. package/dist/runtime/base/base-composer-runtime-core.js.map +1 -1
  155. package/dist/runtime/base/base-thread-runtime-core.d.ts +2 -2
  156. package/dist/runtime/base/base-thread-runtime-core.d.ts.map +1 -1
  157. package/dist/runtime/base/base-thread-runtime-core.js +8 -7
  158. package/dist/runtime/base/base-thread-runtime-core.js.map +1 -1
  159. package/dist/runtime/interfaces/composer-runtime-core.d.ts +12 -5
  160. package/dist/runtime/interfaces/composer-runtime-core.d.ts.map +1 -1
  161. package/dist/runtime/interfaces/thread-list-runtime-core.d.ts +18 -3
  162. package/dist/runtime/interfaces/thread-list-runtime-core.d.ts.map +1 -1
  163. package/dist/runtime/interfaces/thread-runtime-core.d.ts +15 -1
  164. package/dist/runtime/interfaces/thread-runtime-core.d.ts.map +1 -1
  165. package/dist/runtime/utils/auto-status.d.ts +1 -1
  166. package/dist/runtime/utils/auto-status.d.ts.map +1 -1
  167. package/dist/runtime/utils/auto-status.js +6 -2
  168. package/dist/runtime/utils/auto-status.js.map +1 -1
  169. package/dist/runtime/utils/external-message-conversion.d.ts +3 -2
  170. package/dist/runtime/utils/external-message-conversion.d.ts.map +1 -1
  171. package/dist/runtime/utils/external-message-conversion.js +5 -4
  172. package/dist/runtime/utils/external-message-conversion.js.map +1 -1
  173. package/dist/runtime/utils/message-repository.d.ts +1 -0
  174. package/dist/runtime/utils/message-repository.d.ts.map +1 -1
  175. package/dist/runtime/utils/message-repository.js +15 -6
  176. package/dist/runtime/utils/message-repository.js.map +1 -1
  177. package/dist/runtime/utils/resolveToolApprovalResponse.d.ts +6 -3
  178. package/dist/runtime/utils/resolveToolApprovalResponse.d.ts.map +1 -1
  179. package/dist/runtime/utils/resolveToolApprovalResponse.js +12 -3
  180. package/dist/runtime/utils/resolveToolApprovalResponse.js.map +1 -1
  181. package/dist/runtime/utils/thread-message-like.d.ts +2 -10
  182. package/dist/runtime/utils/thread-message-like.d.ts.map +1 -1
  183. package/dist/runtime/utils/thread-message-like.js +1 -1
  184. package/dist/runtime/utils/thread-message-like.js.map +1 -1
  185. package/dist/runtime/utils/tool-call-tree.d.ts +39 -0
  186. package/dist/runtime/utils/tool-call-tree.d.ts.map +1 -0
  187. package/dist/runtime/utils/tool-call-tree.js +85 -0
  188. package/dist/runtime/utils/tool-call-tree.js.map +1 -0
  189. package/dist/runtimes/external-store/external-store-adapter.d.ts +29 -2
  190. package/dist/runtimes/external-store/external-store-adapter.d.ts.map +1 -1
  191. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.d.ts.map +1 -1
  192. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.js +21 -27
  193. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.js.map +1 -1
  194. package/dist/runtimes/external-store/external-store-thread-runtime-core.d.ts +2 -1
  195. package/dist/runtimes/external-store/external-store-thread-runtime-core.d.ts.map +1 -1
  196. package/dist/runtimes/external-store/external-store-thread-runtime-core.js +34 -28
  197. package/dist/runtimes/external-store/external-store-thread-runtime-core.js.map +1 -1
  198. package/dist/runtimes/inert/InertThreadRuntimeCore.d.ts +1 -1
  199. package/dist/runtimes/inert/InertThreadRuntimeCore.d.ts.map +1 -1
  200. package/dist/runtimes/inert/InertThreadRuntimeCore.js.map +1 -1
  201. package/dist/runtimes/internal.d.ts +2 -2
  202. package/dist/runtimes/internal.js +2 -2
  203. package/dist/runtimes/local/local-thread-runtime-core.d.ts +1 -1
  204. package/dist/runtimes/local/local-thread-runtime-core.d.ts.map +1 -1
  205. package/dist/runtimes/local/local-thread-runtime-core.js +3 -1
  206. package/dist/runtimes/local/local-thread-runtime-core.js.map +1 -1
  207. package/dist/runtimes/remote-thread-list/remote-thread-state.d.ts +18 -1
  208. package/dist/runtimes/remote-thread-list/remote-thread-state.d.ts.map +1 -1
  209. package/dist/runtimes/remote-thread-list/remote-thread-state.js +148 -43
  210. package/dist/runtimes/remote-thread-list/remote-thread-state.js.map +1 -1
  211. package/dist/runtimes/remote-thread-list/title-generation.d.ts +49 -0
  212. package/dist/runtimes/remote-thread-list/title-generation.d.ts.map +1 -0
  213. package/dist/runtimes/remote-thread-list/title-generation.js +197 -0
  214. package/dist/runtimes/remote-thread-list/title-generation.js.map +1 -0
  215. package/dist/runtimes/remote-thread-list/title.d.ts +11 -0
  216. package/dist/runtimes/remote-thread-list/title.d.ts.map +1 -0
  217. package/dist/runtimes/remote-thread-list/title.js +14 -0
  218. package/dist/runtimes/remote-thread-list/title.js.map +1 -0
  219. package/dist/runtimes/remote-thread-list/types.d.ts +16 -5
  220. package/dist/runtimes/remote-thread-list/types.d.ts.map +1 -1
  221. package/dist/runtimes/tool-invocations/ToolInvocationTracker.d.ts +31 -8
  222. package/dist/runtimes/tool-invocations/ToolInvocationTracker.d.ts.map +1 -1
  223. package/dist/runtimes/tool-invocations/ToolInvocationTracker.js +97 -60
  224. package/dist/runtimes/tool-invocations/ToolInvocationTracker.js.map +1 -1
  225. package/dist/store/clients/external-thread.d.ts +1 -1
  226. package/dist/store/clients/external-thread.d.ts.map +1 -1
  227. package/dist/store/clients/external-thread.js +30 -7
  228. package/dist/store/clients/external-thread.js.map +1 -1
  229. package/dist/store/clients/model-context-client.d.ts.map +1 -1
  230. package/dist/store/clients/model-context-client.js +2 -1
  231. package/dist/store/clients/model-context-client.js.map +1 -1
  232. package/dist/store/clients/single-thread-list.d.ts.map +1 -1
  233. package/dist/store/clients/single-thread-list.js +2 -1
  234. package/dist/store/clients/single-thread-list.js.map +1 -1
  235. package/dist/store/clients/suggestions.d.ts.map +1 -1
  236. package/dist/store/clients/suggestions.js +19 -6
  237. package/dist/store/clients/suggestions.js.map +1 -1
  238. package/dist/store/clients/thread-message-client.js +1 -1
  239. package/dist/store/clients/thread-selection-events.js +1 -1
  240. package/dist/store/index.d.ts +2 -1
  241. package/dist/store/index.js +2 -1
  242. package/dist/store/internal.d.ts +2 -2
  243. package/dist/store/internal.js +2 -2
  244. package/dist/store/primitive-predicates.d.ts +4 -1
  245. package/dist/store/primitive-predicates.d.ts.map +1 -1
  246. package/dist/store/primitive-predicates.js +9 -1
  247. package/dist/store/primitive-predicates.js.map +1 -1
  248. package/dist/store/runtime-clients/composer-runtime-client.d.ts +1 -0
  249. package/dist/store/runtime-clients/composer-runtime-client.d.ts.map +1 -1
  250. package/dist/store/runtime-clients/composer-runtime-client.js +98 -62
  251. package/dist/store/runtime-clients/composer-runtime-client.js.map +1 -1
  252. package/dist/store/runtime-clients/message-runtime-client.d.ts +1 -0
  253. package/dist/store/runtime-clients/message-runtime-client.d.ts.map +1 -1
  254. package/dist/store/runtime-clients/message-runtime-client.js +221 -144
  255. package/dist/store/runtime-clients/message-runtime-client.js.map +1 -1
  256. package/dist/store/runtime-clients/thread-list-item-runtime-client.d.ts.map +1 -1
  257. package/dist/store/runtime-clients/thread-list-item-runtime-client.js +2 -2
  258. package/dist/store/runtime-clients/thread-list-item-runtime-client.js.map +1 -1
  259. package/dist/store/runtime-clients/thread-list-runtime-client.d.ts.map +1 -1
  260. package/dist/store/runtime-clients/thread-list-runtime-client.js +128 -107
  261. package/dist/store/runtime-clients/thread-list-runtime-client.js.map +1 -1
  262. package/dist/store/runtime-clients/thread-runtime-client.d.ts.map +1 -1
  263. package/dist/store/runtime-clients/thread-runtime-client.js +194 -122
  264. package/dist/store/runtime-clients/thread-runtime-client.js.map +1 -1
  265. package/dist/store/runtime-clients/useSubscribable.d.ts +3 -1
  266. package/dist/store/runtime-clients/useSubscribable.d.ts.map +1 -1
  267. package/dist/store/runtime-clients/useSubscribable.js +1 -1
  268. package/dist/store/runtime-clients/useSubscribable.js.map +1 -1
  269. package/dist/store/scopes/composer.d.ts +10 -2
  270. package/dist/store/scopes/composer.d.ts.map +1 -1
  271. package/dist/store/scopes/message.d.ts +30 -1
  272. package/dist/store/scopes/message.d.ts.map +1 -1
  273. package/dist/store/scopes/part.d.ts +6 -3
  274. package/dist/store/scopes/part.d.ts.map +1 -1
  275. package/dist/store/scopes/thread-list-item.d.ts +2 -2
  276. package/dist/store/scopes/thread-list-item.d.ts.map +1 -1
  277. package/dist/store/scopes/thread.d.ts +8 -0
  278. package/dist/store/scopes/thread.d.ts.map +1 -1
  279. package/dist/store/scopes/threads.d.ts +1 -0
  280. package/dist/store/scopes/threads.d.ts.map +1 -1
  281. package/dist/subscribable/subscribable.d.ts +8 -1
  282. package/dist/subscribable/subscribable.d.ts.map +1 -1
  283. package/dist/subscribable/subscribable.js +24 -16
  284. package/dist/subscribable/subscribable.js.map +1 -1
  285. package/dist/types/attachment.d.ts +2 -1
  286. package/dist/types/attachment.d.ts.map +1 -1
  287. package/dist/types/attachment.js +2 -1
  288. package/dist/types/attachment.js.map +1 -1
  289. package/dist/types/message.d.ts +40 -1
  290. package/dist/types/message.d.ts.map +1 -1
  291. package/dist/types/message.js +7 -1
  292. package/dist/types/message.js.map +1 -1
  293. package/dist/utils/abortable-iterable.d.ts +19 -0
  294. package/dist/utils/abortable-iterable.d.ts.map +1 -0
  295. package/dist/utils/abortable-iterable.js +72 -0
  296. package/dist/utils/abortable-iterable.js.map +1 -0
  297. package/dist/utils/composite-context-provider.d.ts.map +1 -1
  298. package/dist/utils/composite-context-provider.js +20 -2
  299. package/dist/utils/composite-context-provider.js.map +1 -1
  300. package/dist/utils/record.d.ts +11 -0
  301. package/dist/utils/record.d.ts.map +1 -0
  302. package/dist/utils/record.js +12 -0
  303. package/dist/utils/record.js.map +1 -0
  304. package/package.json +12 -17
  305. package/src/adapters/attachment.test.ts +50 -1
  306. package/src/adapters/attachment.ts +7 -4
  307. package/src/index.ts +7 -1
  308. package/src/internal.ts +12 -0
  309. package/src/model-context/frame/host.test.ts +24 -0
  310. package/src/model-context/frame/host.ts +10 -4
  311. package/src/model-context/frame/provider.test.ts +288 -1
  312. package/src/model-context/frame/provider.ts +148 -30
  313. package/src/model-context/interactable-composer-metadata.test.ts +28 -0
  314. package/src/model-context/interactable-composer-metadata.ts +9 -3
  315. package/src/model-context/registry.test.ts +12 -0
  316. package/src/model-context/registry.ts +3 -2
  317. package/src/model-context/tool-call-text.test.ts +67 -0
  318. package/src/model-context/tool-call-text.ts +67 -0
  319. package/src/model-context/types.test.ts +25 -0
  320. package/src/model-context/types.ts +9 -4
  321. package/src/react/AssistantRuntimeProvider.test.tsx +66 -2
  322. package/src/react/RuntimeAdapter.test.ts +34 -0
  323. package/src/react/adapters/LocalStorageThreadListAdapter.tsx +1 -3
  324. package/src/react/client/InMemoryThreadList.ts +1 -0
  325. package/src/react/client/Interactables.test.ts +28 -0
  326. package/src/react/client/Interactables.ts +15 -13
  327. package/src/react/client/RemoteThreadList.background.test.ts +526 -0
  328. package/src/react/client/RemoteThreadList.concurrent.test.tsx +315 -0
  329. package/src/react/client/RemoteThreadList.test.ts +617 -3
  330. package/src/react/client/RemoteThreadList.ts +433 -167
  331. package/src/react/client/Tools.ts +29 -12
  332. package/src/react/index.ts +4 -0
  333. package/src/react/interactables-legacy/Interactables.ts +16 -16
  334. package/src/react/interactables-shared/useInteractablePersistenceQueue.ts +9 -10
  335. package/src/react/model-context/makeAssistantTool.ts +2 -2
  336. package/src/react/model-context/modelContextHooks.test.tsx +131 -0
  337. package/src/react/model-context/toolbox.test.ts +9 -9
  338. package/src/react/model-context/toolbox.ts +9 -36
  339. package/src/react/model-context/useAssistantContext.ts +4 -2
  340. package/src/react/model-context/useAssistantTool.ts +2 -2
  341. package/src/react/model-context/useAuiToolOverrides.ts +4 -2
  342. package/src/react/model-context/useInlineRender.test.tsx +48 -0
  343. package/src/react/model-context/useInlineRender.ts +14 -10
  344. package/src/react/model-context/useToolArgsStatus.test.tsx +30 -0
  345. package/src/react/model-context/useToolArgsStatus.ts +2 -1
  346. package/src/react/primitive-hooks/useMessageError.ts +2 -19
  347. package/src/react/primitive-hooks/useThreadListLoadMore.ts +2 -3
  348. package/src/react/primitives/composer/ComposerAttachments.test.tsx +76 -0
  349. package/src/react/primitives/composer/ComposerAttachments.tsx +9 -4
  350. package/src/react/primitives/composer/ComposerQueue.test.tsx +72 -0
  351. package/src/react/primitives/composer/ComposerQueue.tsx +7 -4
  352. package/src/react/primitives/message/MessageAttachments.test.tsx +107 -37
  353. package/src/react/primitives/message/MessageAttachments.tsx +10 -7
  354. package/src/react/primitives/message/MessageGroupedParts.tsx +2 -2
  355. package/src/react/primitives/message/MessageParts.tsx +3 -3
  356. package/src/react/primitives/threadList/ThreadListItems.test.tsx +99 -0
  357. package/src/react/primitives/threadList/ThreadListItems.tsx +8 -5
  358. package/src/react/runtimes/RemoteThreadListHookInstanceManager.destroy.test.tsx +172 -0
  359. package/src/react/runtimes/RemoteThreadListHookInstanceManager.running.test.tsx +127 -1
  360. package/src/react/runtimes/RemoteThreadListHookInstanceManager.test.tsx +7 -1
  361. package/src/react/runtimes/RemoteThreadListHookInstanceManager.tsx +99 -35
  362. package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.load-error.test.ts +132 -0
  363. package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.switch-delete.test.ts +274 -0
  364. package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.tsx +223 -152
  365. package/src/react/runtimes/RemoteThreadResource.test.ts +9 -0
  366. package/src/react/runtimes/RemoteThreadResource.ts +20 -16
  367. package/src/react/runtimes/assistant-transport/transport-scheduling.test.ts +51 -2
  368. package/src/react/runtimes/assistant-transport/useLatestRef.ts +2 -2
  369. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.test.ts +45 -0
  370. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.test.tsx +546 -5
  371. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.ts +546 -103
  372. package/src/react/runtimes/cloud/auiV0.ts +5 -0
  373. package/src/react/runtimes/cloud/createCloudThreadListAdapter.test.ts +103 -0
  374. package/src/react/runtimes/cloud/createCloudThreadListAdapter.ts +237 -0
  375. package/src/react/runtimes/cloud/useCloudThreadListAdapter.test.tsx +90 -1
  376. package/src/react/runtimes/cloud/useCloudThreadListAdapter.tsx +33 -238
  377. package/src/react/runtimes/external-message-converter.ts +1 -0
  378. package/src/react/runtimes/useExternalStoreRuntime.lifecycle.test.tsx +41 -0
  379. package/src/react/runtimes/useExternalStoreRuntime.ts +10 -4
  380. package/src/react/runtimes/useLocalRuntime.test.tsx +20 -0
  381. package/src/react/runtimes/useRemoteThreadListRuntime.concurrent.test.tsx +216 -0
  382. package/src/react/runtimes/useRemoteThreadListRuntime.load-error.test.tsx +108 -0
  383. package/src/react/runtimes/useRemoteThreadListRuntime.ts +57 -6
  384. package/src/react/types/MessagePartComponentTypes.ts +7 -3
  385. package/src/react/types/scopes/tools.ts +2 -0
  386. package/src/runtime/api/message-part-runtime.ts +3 -3
  387. package/src/runtime/api/message-runtime.test.ts +75 -0
  388. package/src/runtime/api/thread-list-item-runtime.ts +10 -3
  389. package/src/runtime/api/thread-list-runtime.ts +46 -12
  390. package/src/runtime/api/thread-runtime.test.ts +78 -0
  391. package/src/runtime/api/thread-runtime.ts +8 -1
  392. package/src/runtime/base/base-composer-runtime-core.ts +36 -8
  393. package/src/runtime/base/base-thread-runtime-core.test.ts +149 -4
  394. package/src/runtime/base/base-thread-runtime-core.ts +16 -8
  395. package/src/runtime/interfaces/composer-runtime-core.ts +10 -5
  396. package/src/runtime/interfaces/thread-list-runtime-core.ts +25 -2
  397. package/src/runtime/interfaces/thread-runtime-core.ts +17 -1
  398. package/src/runtime/utils/auto-status.test.ts +57 -0
  399. package/src/runtime/utils/auto-status.ts +13 -1
  400. package/src/runtime/utils/external-message-conversion.test.ts +165 -0
  401. package/src/runtime/utils/external-message-conversion.ts +18 -2
  402. package/src/runtime/utils/message-repository.ts +29 -17
  403. package/src/runtime/utils/resolveToolApprovalResponse.test.ts +73 -0
  404. package/src/runtime/utils/resolveToolApprovalResponse.ts +27 -6
  405. package/src/runtime/utils/thread-message-like.ts +3 -11
  406. package/src/runtime/utils/tool-call-tree.test.ts +191 -0
  407. package/src/runtime/utils/tool-call-tree.ts +97 -0
  408. package/src/runtimes/external-store/external-store-adapter.ts +38 -2
  409. package/src/runtimes/external-store/external-store-thread-list-runtime-core.ts +18 -13
  410. package/src/runtimes/external-store/external-store-thread-runtime-core.ts +56 -33
  411. package/src/runtimes/inert/InertThreadRuntimeCore.ts +1 -1
  412. package/src/runtimes/internal.ts +1 -0
  413. package/src/runtimes/local/local-thread-runtime-core.test.ts +29 -3
  414. package/src/runtimes/local/local-thread-runtime-core.ts +5 -1
  415. package/src/runtimes/remote-thread-list/remote-thread-state.test.ts +238 -0
  416. package/src/runtimes/remote-thread-list/remote-thread-state.ts +223 -44
  417. package/src/runtimes/remote-thread-list/title-generation.test.ts +602 -0
  418. package/src/runtimes/remote-thread-list/title-generation.ts +329 -0
  419. package/src/runtimes/remote-thread-list/title.test.ts +62 -0
  420. package/src/runtimes/remote-thread-list/title.ts +16 -0
  421. package/src/runtimes/remote-thread-list/types.ts +16 -5
  422. package/src/runtimes/tool-invocations/EDGE_CASES.md +110 -5
  423. package/src/runtimes/tool-invocations/ToolInvocationTracker.test.ts +750 -136
  424. package/src/runtimes/tool-invocations/ToolInvocationTracker.ts +144 -91
  425. package/src/store/clients/external-thread.ts +62 -11
  426. package/src/store/clients/model-context-client.ts +5 -1
  427. package/src/store/clients/single-thread-list.ts +1 -0
  428. package/src/store/clients/suggestions.test.ts +195 -1
  429. package/src/store/clients/suggestions.ts +45 -34
  430. package/src/store/index.ts +4 -0
  431. package/src/store/internal.ts +2 -0
  432. package/src/store/primitive-predicates.ts +35 -0
  433. package/src/store/runtime-clients/composer-runtime-client.ts +42 -9
  434. package/src/store/runtime-clients/message-runtime-client.ts +54 -6
  435. package/src/store/runtime-clients/thread-list-item-runtime-client.ts +4 -2
  436. package/src/store/runtime-clients/thread-list-runtime-client.ts +17 -2
  437. package/src/store/runtime-clients/thread-runtime-client.ts +58 -10
  438. package/src/store/runtime-clients/useSubscribable.ts +8 -2
  439. package/src/store/scopes/composer.ts +16 -4
  440. package/src/store/scopes/message.ts +14 -0
  441. package/src/store/scopes/part.ts +6 -3
  442. package/src/store/scopes/thread-list-item.ts +5 -2
  443. package/src/store/scopes/thread.ts +4 -0
  444. package/src/store/scopes/threads.ts +1 -0
  445. package/src/subscribable/subscribable.test.ts +50 -0
  446. package/src/subscribable/subscribable.ts +33 -21
  447. package/src/tests/MessageRepository.test.ts +116 -0
  448. package/src/tests/OptimisticState-list-race.test.ts +1 -0
  449. package/src/tests/RemoteThreadListThreadListRuntimeCore-initialize-adapter-change.test.ts +38 -0
  450. package/src/tests/RemoteThreadListThreadListRuntimeCore-load-race.test.ts +158 -0
  451. package/src/tests/RemoteThreadListThreadListRuntimeCore-loadMore.test.ts +78 -0
  452. package/src/tests/RemoteThreadListThreadListRuntimeCore-reload.test.ts +52 -0
  453. package/src/tests/RemoteThreadListThreadListRuntimeCore-title-race.test.ts +89 -0
  454. package/src/tests/attachment-adapters.test.ts +14 -0
  455. package/src/tests/base-composer-runtime-core.test.ts +39 -0
  456. package/src/tests/external-store-optimistic-id.test.ts +79 -0
  457. package/src/tests/external-store-repository-instance.test.ts +166 -0
  458. package/src/tests/external-store-thread-list-runtime-core.test.ts +83 -0
  459. package/src/tests/external-store-thread-runtime-core-adapter.test.ts +49 -34
  460. package/src/tests/external-thread-attachments.test.tsx +224 -1
  461. package/src/tests/external-thread-parity.test.tsx +23 -0
  462. package/src/tests/remote-thread-list-adapter-switch.test.ts +66 -1
  463. package/src/tests/remote-thread-list-isLoading.test.ts +1 -0
  464. package/src/tests/thread-list-runtime-getLoadThreadsPromise.test.ts +1 -0
  465. package/src/tests/thread-switch-events.test.tsx +475 -2
  466. package/src/tests/tools-scope-migration.test.tsx +28 -0
  467. package/src/types/attachment.ts +4 -0
  468. package/src/types/message.ts +50 -2
  469. package/src/utils/abortable-iterable.test.ts +213 -0
  470. package/src/utils/abortable-iterable.ts +93 -0
  471. package/src/utils/composite-context-provider.test.ts +25 -0
  472. package/src/utils/composite-context-provider.ts +23 -2
  473. package/src/utils/record.test.ts +48 -0
  474. package/src/utils/record.ts +11 -0
@@ -0,0 +1,97 @@
1
+ import type {
2
+ ThreadAssistantMessage,
3
+ ThreadAssistantMessagePart,
4
+ ThreadMessage,
5
+ ToolCallMessagePart,
6
+ } from "../../types/message";
7
+
8
+ export type ToolCallTreeEntry = {
9
+ readonly part: ToolCallMessagePart;
10
+ /** Id of the message whose content directly holds `part`. */
11
+ readonly messageId: string;
12
+ };
13
+
14
+ /**
15
+ * Walk every tool-call part reachable from `messages`. A tool call projects a
16
+ * child run as nested messages on `ToolCallMessagePart.messages`, so a
17
+ * subagent's own calls live below the top-level content and a seam that only
18
+ * scans the top level cannot reach them.
19
+ *
20
+ * Parts arrive in document order, each one ahead of its own descendants.
21
+ * `messageId` names the message that directly holds the part, which for a
22
+ * nested call is the child run's message rather than the top-level message the
23
+ * tree hangs from. Only assistant messages are descended, the same rule
24
+ * {@link mapToolCallPartsDeep} rewrites under, so a part this reports is always
25
+ * a part that can be written back.
26
+ */
27
+ export function* walkToolCallTree(
28
+ messages: readonly ThreadMessage[],
29
+ ): Generator<ToolCallTreeEntry> {
30
+ for (const message of messages) {
31
+ if (message?.role !== "assistant" || !Array.isArray(message.content)) {
32
+ continue;
33
+ }
34
+ for (const part of message.content) {
35
+ if (!part || part.type !== "tool-call") continue;
36
+ yield { part, messageId: message.id };
37
+ if (part.messages?.length) yield* walkToolCallTree(part.messages);
38
+ }
39
+ }
40
+ }
41
+
42
+ /**
43
+ * Content-level form of {@link walkToolCallTree}, for a caller that already
44
+ * holds one assistant message's content and does not need the owning id.
45
+ */
46
+ export function* iterateToolCallParts(
47
+ content: readonly ThreadAssistantMessagePart[],
48
+ ): Generator<ToolCallMessagePart> {
49
+ for (const part of content) {
50
+ if (!part || part.type !== "tool-call") continue;
51
+ yield part;
52
+ if (part.messages?.length) {
53
+ for (const entry of walkToolCallTree(part.messages)) {
54
+ yield entry.part;
55
+ }
56
+ }
57
+ }
58
+ }
59
+
60
+ /**
61
+ * Rebuild `content` with `fn` applied to every tool-call part in the tree.
62
+ * Arrays and nested messages are reused wherever `fn` returned the part it was
63
+ * given, so an unchanged subtree keeps its identity and `changed` reports
64
+ * whether anything moved.
65
+ */
66
+ export function mapToolCallPartsDeep(
67
+ content: readonly ThreadAssistantMessagePart[],
68
+ fn: (part: ToolCallMessagePart) => ToolCallMessagePart,
69
+ ): { content: readonly ThreadAssistantMessagePart[]; changed: boolean } {
70
+ let changed = false;
71
+ const next = content.map((part): ThreadAssistantMessagePart => {
72
+ if (part.type !== "tool-call") return part;
73
+ let mapped = fn(part);
74
+ if (mapped.messages !== undefined) {
75
+ let nestedChanged = false;
76
+ const nestedMessages = mapped.messages.map((nested) => {
77
+ if (nested.role !== "assistant" || !Array.isArray(nested.content)) {
78
+ return nested;
79
+ }
80
+ const assistant = nested as ThreadAssistantMessage;
81
+ const result = mapToolCallPartsDeep(assistant.content, fn);
82
+ if (!result.changed) return nested;
83
+ nestedChanged = true;
84
+ return { ...assistant, content: result.content };
85
+ });
86
+ if (nestedChanged) {
87
+ mapped =
88
+ mapped === part
89
+ ? { ...part, messages: nestedMessages }
90
+ : { ...mapped, messages: nestedMessages };
91
+ }
92
+ }
93
+ if (mapped !== part) changed = true;
94
+ return mapped;
95
+ });
96
+ return changed ? { content: next, changed } : { content, changed };
97
+ }
@@ -1,4 +1,8 @@
1
- import type { AppendMessage, ThreadMessage } from "../../types/message";
1
+ import type {
2
+ AppendMessage,
3
+ ThreadMessage,
4
+ ToolCallMessagePart,
5
+ } from "../../types/message";
2
6
  import type { ThreadMessageLike } from "../../runtime/utils/thread-message-like";
3
7
  import type { AttachmentAdapter } from "../../adapters/attachment";
4
8
  import type {
@@ -14,7 +18,10 @@ import type {
14
18
  ResumeRunConfig,
15
19
  ThreadSuggestion,
16
20
  } from "../../runtime/interfaces/thread-runtime-core";
17
- import type { ExportedMessageRepository } from "../../runtime/utils/message-repository";
21
+ import type {
22
+ ExportedMessageRepository,
23
+ MessageRepository,
24
+ } from "../../runtime/utils/message-repository";
18
25
  import type { ReadonlyJSONValue } from "assistant-stream/utils";
19
26
  import type { ToolExecutionStatus } from "../tool-invocations/ToolInvocationTracker";
20
27
  import type { ExternalThreadQueueAdapter } from "../../runtime/queue/external-thread-queue-adapter";
@@ -105,6 +112,15 @@ type ExternalStoreAdapterBase<T> = {
105
112
  isLoading?: boolean | undefined;
106
113
  messages?: readonly T[];
107
114
  messageRepository?: ExportedMessageRepository;
115
+ /**
116
+ * An externally owned message repository instance. When provided, the
117
+ * thread runtime adopts it as its branch store and swaps to it atomically
118
+ * whenever a different instance is passed, so hosts that route multiple
119
+ * conversations through one runtime keep each conversation's history and
120
+ * branches isolated in its own instance. Omit it to keep the runtime's own
121
+ * repository.
122
+ */
123
+ unstable_messageRepositoryInstance?: MessageRepository | undefined;
108
124
  suggestions?: readonly ThreadSuggestion[] | undefined;
109
125
  state?: ReadonlyJSONValue | undefined;
110
126
  extras?: unknown;
@@ -207,6 +223,26 @@ type ExternalStoreAdapterBase<T> = {
207
223
  * `modelContent` populated when present.
208
224
  */
209
225
  unstable_enableToolInvocations?: boolean | undefined;
226
+ /**
227
+ * Decides whether a tool call's result is produced on the client. Only
228
+ * consulted when `unstable_enableToolInvocations` is `true`.
229
+ *
230
+ * A provider that runs tools itself answers its own calls, and its result
231
+ * arrives one or more snapshots after the call's arguments complete. In
232
+ * that window the call is complete and result-less, so a registered tool
233
+ * of the same name would otherwise execute locally and produce a result
234
+ * the provider never asked for. An adapter that can tell the two apart
235
+ * supplies this predicate; it is read once per tool call, when the call is
236
+ * first observed live.
237
+ *
238
+ * The predicate is also what licenses running a frontend tool while the
239
+ * provider's run is still open. Without it, ownership is unknown until the
240
+ * run ends, so a registered tool executes only once the run's outcome is
241
+ * known and cannot fire on a call the provider was about to answer or gate.
242
+ */
243
+ unstable_isClientToolCall?:
244
+ | ((toolCall: ToolCallMessagePart) => boolean)
245
+ | undefined;
210
246
  /**
211
247
  * Receives the current per-tool-call execution status map whenever it
212
248
  * changes. Only invoked when `unstable_enableToolInvocations` is `true`
@@ -6,6 +6,7 @@ import type {
6
6
  import type { ExternalStoreThreadListAdapter } from "./external-store-adapter";
7
7
  import { invalidateThreadRuntime } from "../../runtime/utils/thread-runtime-lifecycle";
8
8
  import { BaseSubscribable } from "../../subscribable/subscribable";
9
+ import { nullProtoRecord } from "../../utils/record";
9
10
 
10
11
  export type ExternalStoreThreadFactory = () => ExternalStoreThreadRuntimeCore;
11
12
 
@@ -19,9 +20,11 @@ const DEFAULT_THREAD = Object.freeze({
19
20
  status: "regular",
20
21
  });
21
22
  const RESOLVED_PROMISE = Promise.resolve();
22
- const DEFAULT_THREAD_DATA = Object.freeze({
23
- [DEFAULT_THREAD_ID]: DEFAULT_THREAD,
24
- });
23
+ const DEFAULT_THREAD_DATA = Object.freeze(
24
+ nullProtoRecord<ThreadListItemCoreState>({
25
+ [DEFAULT_THREAD_ID]: DEFAULT_THREAD,
26
+ }),
27
+ );
25
28
 
26
29
  export class ExternalStoreThreadListRuntimeCore
27
30
  extends BaseSubscribable
@@ -84,7 +87,9 @@ export class ExternalStoreThreadListRuntimeCore
84
87
  }
85
88
 
86
89
  public getItemById(threadId: string) {
87
- return this._threadData[threadId];
90
+ return Object.hasOwn(this._threadData, threadId)
91
+ ? this._threadData[threadId]
92
+ : undefined;
88
93
  }
89
94
 
90
95
  public __internal_setAdapter(
@@ -105,6 +110,7 @@ export class ExternalStoreThreadListRuntimeCore
105
110
 
106
111
  if (
107
112
  !initialLoad &&
113
+ (previousAdapter.isLoading ?? false) === (adapter.isLoading ?? false) &&
108
114
  previousThreadId === newThreadId &&
109
115
  previousThreads === newThreads &&
110
116
  previousArchivedThreads === newArchivedThreads
@@ -117,9 +123,9 @@ export class ExternalStoreThreadListRuntimeCore
117
123
  previousArchivedThreads !== newArchivedThreads ||
118
124
  previousThreadId !== newThreadId
119
125
  ) {
120
- this._threadData = {
121
- ...DEFAULT_THREAD_DATA,
122
- ...Object.fromEntries(
126
+ this._threadData = nullProtoRecord(
127
+ DEFAULT_THREAD_DATA,
128
+ Object.fromEntries(
123
129
  adapter.threads?.map((t) => [
124
130
  t.id,
125
131
  {
@@ -130,7 +136,7 @@ export class ExternalStoreThreadListRuntimeCore
130
136
  },
131
137
  ]) ?? [],
132
138
  ),
133
- ...Object.fromEntries(
139
+ Object.fromEntries(
134
140
  adapter.archivedThreads?.map((t) => [
135
141
  t.id,
136
142
  {
@@ -141,7 +147,7 @@ export class ExternalStoreThreadListRuntimeCore
141
147
  },
142
148
  ]) ?? [],
143
149
  ),
144
- };
150
+ );
145
151
  }
146
152
 
147
153
  if (previousThreads !== newThreads) {
@@ -160,16 +166,15 @@ export class ExternalStoreThreadListRuntimeCore
160
166
  this._mainThread = this.threadFactory();
161
167
  }
162
168
 
163
- if (!this._threadData[this._mainThreadId]) {
164
- this._threadData = {
165
- ...this._threadData,
169
+ if (!Object.hasOwn(this._threadData, this._mainThreadId)) {
170
+ this._threadData = nullProtoRecord(this._threadData, {
166
171
  [this._mainThreadId]: {
167
172
  id: this._mainThreadId,
168
173
  remoteId: undefined,
169
174
  externalId: undefined,
170
175
  status: "regular",
171
176
  },
172
- };
177
+ });
173
178
  }
174
179
 
175
180
  this._notifySubscribers();
@@ -1,3 +1,4 @@
1
+ import { shallowEqual } from "@assistant-ui/store/client";
1
2
  import type { AppendMessage, ThreadMessage } from "../../types/message";
2
3
  import type { Attachment } from "../../types/attachment";
3
4
  import type {
@@ -28,6 +29,7 @@ import {
28
29
  type ThreadMessageLike,
29
30
  } from "../../runtime/utils/thread-message-like";
30
31
  import { getThreadMessageText } from "../../utils/text";
32
+ import { shallowArrayEqual } from "../../runtime/utils/external-message-conversion";
31
33
  import type {
32
34
  RuntimeCapabilities,
33
35
  ThreadRuntimeCore,
@@ -43,6 +45,7 @@ import {
43
45
  MessageRepository,
44
46
  } from "../../runtime/utils/message-repository";
45
47
  import { generateId } from "../../utils/id";
48
+ import { walkToolCallTree } from "../../runtime/utils/tool-call-tree";
46
49
  import {
47
50
  ToolInvocationTracker,
48
51
  type ToolExecutionStatus,
@@ -57,7 +60,7 @@ import {
57
60
  const EMPTY_ARRAY: readonly ThreadSuggestion[] = Object.freeze([]);
58
61
 
59
62
  const observeAdapterCallback = (
60
- name: "onAddToolResult" | "onRespondToToolApproval" | "onCancel",
63
+ name: "onAddToolResult" | "onCancel",
61
64
  result: Promise<void> | void,
62
65
  ) => {
63
66
  void Promise.resolve(result).catch((error) => {
@@ -68,15 +71,6 @@ const observeAdapterCallback = (
68
71
  });
69
72
  };
70
73
 
71
- const shallowEqual = (a: object, b: object): boolean => {
72
- const aKeys = Object.keys(a);
73
- if (aKeys.length !== Object.keys(b).length) return false;
74
- for (const key of aKeys) {
75
- if ((a as any)[key] !== (b as any)[key]) return false;
76
- }
77
- return true;
78
- };
79
-
80
74
  export const hasUpcomingMessage = (
81
75
  isRunning: boolean,
82
76
  messages: readonly ThreadMessage[],
@@ -154,6 +148,11 @@ export class ExternalStoreThreadRuntimeCore
154
148
  // and a delete whose confirmation races a send keeps its eviction.
155
149
  private _pendingDeleteEvictions = new Set<string>();
156
150
 
151
+ // Placeholder id for the upcoming assistant message, reused across snapshot
152
+ // passes while the same tail message awaits its response so the placeholder
153
+ // keeps one identity per response.
154
+ private _optimistic: { id: string; parentId: string | null } | null = null;
155
+
157
156
  private _store!: ExternalStoreAdapter<any>;
158
157
 
159
158
  private _getInitializePromise?: () => Promise<unknown> | undefined;
@@ -246,6 +245,14 @@ export class ExternalStoreThreadRuntimeCore
246
245
  const oldStore = this._store as ExternalStoreAdapter<any> | undefined;
247
246
  this._store = store;
248
247
  const isRunning = this._getEffectiveIsRunning(store);
248
+ const repositoryInstance = store.unstable_messageRepositoryInstance;
249
+ const repositoryChanged =
250
+ repositoryInstance !== undefined &&
251
+ repositoryInstance !== this.repository;
252
+ if (repositoryChanged) {
253
+ this.repository = repositoryInstance;
254
+ this._pendingDeleteEvictions.clear();
255
+ }
249
256
  if (oldStore?.queue !== store.queue) {
250
257
  this._transformedQueue = undefined;
251
258
  store.queue?.__internal_setDispatchTransform?.((message) => {
@@ -296,6 +303,7 @@ export class ExternalStoreThreadRuntimeCore
296
303
  // Handle messageRepository
297
304
  if (
298
305
  oldStore &&
306
+ !repositoryChanged &&
299
307
  oldStore.isRunning === store.isRunning &&
300
308
  oldStore.messageRepository === store.messageRepository &&
301
309
  previousIsRunning === isRunning
@@ -308,7 +316,11 @@ export class ExternalStoreThreadRuntimeCore
308
316
  const headId =
309
317
  store.messageRepository.headId ?? incoming.at(-1)?.message.id ?? null;
310
318
 
311
- if (oldStore && oldStore.messageRepository === store.messageRepository) {
319
+ if (
320
+ oldStore &&
321
+ !repositoryChanged &&
322
+ oldStore.messageRepository === store.messageRepository
323
+ ) {
312
324
  this.repository.resetHead(headId);
313
325
  messages = this.repository.getMessages();
314
326
  } else {
@@ -333,6 +345,7 @@ export class ExternalStoreThreadRuntimeCore
333
345
  if (oldStore.convertMessage !== store.convertMessage) {
334
346
  this._converter = new ThreadMessageConverter();
335
347
  } else if (
348
+ !repositoryChanged &&
336
349
  oldStore.isRunning === store.isRunning &&
337
350
  oldStore.messages === store.messages &&
338
351
  previousIsRunning === isRunning
@@ -436,9 +449,13 @@ export class ExternalStoreThreadRuntimeCore
436
449
  // (prior placeholders, mid-run id-swap siblings); export() never persists them.
437
450
  let optimisticId: string | null = null;
438
451
  if (hasUpcomingMessage(isRunning, messages)) {
439
- optimisticId = generateId();
452
+ const parentId = messages.at(-1)?.id ?? null;
453
+ if (this._optimistic?.parentId !== parentId) {
454
+ this._optimistic = { id: generateId(), parentId };
455
+ }
456
+ optimisticId = this._optimistic.id;
440
457
  this.repository.addOrUpdateMessage(
441
- messages.at(-1)?.id ?? null,
458
+ parentId,
442
459
  fromThreadMessageLike(
443
460
  { role: "assistant", content: [], metadata: { isOptimistic: true } },
444
461
  optimisticId,
@@ -447,10 +464,20 @@ export class ExternalStoreThreadRuntimeCore
447
464
  );
448
465
  }
449
466
 
467
+ if (optimisticId === null) this._optimistic = null;
450
468
  this.repository.resetHead(optimisticId ?? messages.at(-1)?.id ?? null);
451
469
 
452
- this._messages = this.repository.getMessages();
470
+ const messagesSnapshot = this.repository.getMessages();
471
+ if (
472
+ !this._messages ||
473
+ !shallowArrayEqual(this._messages, messagesSnapshot)
474
+ ) {
475
+ this._messages = messagesSnapshot;
476
+ }
453
477
 
478
+ if (repositoryChanged) {
479
+ this._runTrackerUpdate(() => this._toolInvocations?.reset());
480
+ }
454
481
  this._runTrackerUpdate(() => this._driveToolInvocations());
455
482
 
456
483
  this._notifySubscribers();
@@ -529,6 +556,7 @@ export class ExternalStoreThreadRuntimeCore
529
556
  }
530
557
  },
531
558
  },
559
+ (toolCall) => this._store.unstable_isClientToolCall?.(toolCall),
532
560
  );
533
561
  }
534
562
 
@@ -557,17 +585,9 @@ export class ExternalStoreThreadRuntimeCore
557
585
  private _findMessageIdForToolCall(toolCallId: string): string | undefined {
558
586
  if (this._messagesForToolCallIndex !== this._messages) {
559
587
  this._toolCallToMessageId.clear();
560
- const visit = (messages: readonly ThreadMessage[]): void => {
561
- for (const message of messages) {
562
- if (!Array.isArray(message.content)) continue;
563
- for (const part of message.content) {
564
- if (!part || part.type !== "tool-call") continue;
565
- this._toolCallToMessageId.set(part.toolCallId, message.id);
566
- if (part.messages) visit(part.messages);
567
- }
568
- }
569
- };
570
- visit(this._messages);
588
+ for (const { part, messageId } of walkToolCallTree(this._messages)) {
589
+ this._toolCallToMessageId.set(part.toolCallId, messageId);
590
+ }
571
591
  this._messagesForToolCallIndex = this._messages;
572
592
  }
573
593
  return this._toolCallToMessageId.get(toolCallId);
@@ -671,7 +691,7 @@ export class ExternalStoreThreadRuntimeCore
671
691
  // user messages — matches the satellites' historical opt-in cancel
672
692
  // behavior, which is now built in.
673
693
  if (message.startRun ?? message.role === "user") {
674
- await this._toolInvocations?.abort();
694
+ await this._toolInvocations?.abort({ discardPending: true });
675
695
  }
676
696
  if (!isThreadRuntimeGenerationCurrent(this, generation)) return;
677
697
 
@@ -772,7 +792,7 @@ export class ExternalStoreThreadRuntimeCore
772
792
  // Auto-abort in-flight client-side tool executions when a run reloads;
773
793
  // any results that land afterward would target a turn that no longer
774
794
  // exists. See `append` above for full rationale.
775
- await this._toolInvocations?.abort();
795
+ await this._toolInvocations?.abort({ discardPending: true });
776
796
 
777
797
  await this._store.onReload(config.parentId, config);
778
798
  }
@@ -826,7 +846,7 @@ export class ExternalStoreThreadRuntimeCore
826
846
  // Abort any in-flight client-side tool executions. Fire-and-forget —
827
847
  // the abort resolves once executions settle, but we don't gate the
828
848
  // cancel on it.
829
- void this._toolInvocations?.abort();
849
+ void this._toolInvocations?.abort({ discardPending: true });
830
850
 
831
851
  // Before the run is aborted, so the settle it produces keeps the pending
832
852
  // items instead of dispatching the next one at the moment the user
@@ -936,13 +956,16 @@ export class ExternalStoreThreadRuntimeCore
936
956
  );
937
957
  }
938
958
 
939
- public respondToToolApproval(options: RespondToToolApprovalOptions) {
959
+ public respondToToolApproval(
960
+ options: RespondToToolApprovalOptions,
961
+ ): Promise<void> {
940
962
  if (!this._store.onRespondToToolApproval)
941
963
  throw new Error("Runtime does not support tool approvals.");
942
- observeAdapterCallback(
943
- "onRespondToToolApproval",
944
- this._store.onRespondToToolApproval(options),
945
- );
964
+ try {
965
+ return Promise.resolve(this._store.onRespondToToolApproval(options));
966
+ } catch (error) {
967
+ return Promise.reject(error);
968
+ }
946
969
  }
947
970
 
948
971
  public override reset(initialMessages?: readonly ThreadMessageLike[]) {
@@ -136,7 +136,7 @@ export abstract class InertThreadRuntimeCore
136
136
  throw this.error;
137
137
  }
138
138
 
139
- respondToToolApproval(): void {
139
+ respondToToolApproval(): Promise<void> {
140
140
  throw this.error;
141
141
  }
142
142
 
@@ -31,6 +31,7 @@ export type {
31
31
  export {
32
32
  createThreadMappingId,
33
33
  getThreadData,
34
+ promoteNewThreadReducer,
34
35
  updateStatusReducer,
35
36
  } from "./remote-thread-list/remote-thread-state";
36
37
  export type {
@@ -5,7 +5,7 @@ import type {
5
5
  ChatModelRunOptions,
6
6
  ChatModelRunResult,
7
7
  } from "../../runtime/utils/chat-model-adapter";
8
- import type { AppendMessage } from "../../types/message";
8
+ import type { AppendMessage, ToolCallMessagePart } from "../../types/message";
9
9
  import type { LocalRuntimeOptionsBase } from "./local-runtime-options";
10
10
  import {
11
11
  ExportedMessageRepository,
@@ -61,7 +61,7 @@ const userMessage = (text: string): AppendMessage => ({
61
61
 
62
62
  const toolCallPart = (
63
63
  toolName: string,
64
- approval?: { id: string; resolution?: "cancelled" | "expired" },
64
+ approval?: ToolCallMessagePart["approval"],
65
65
  ) => ({
66
66
  type: "tool-call" as const,
67
67
  toolCallId: `call-${toolName}`,
@@ -73,7 +73,7 @@ const toolCallPart = (
73
73
 
74
74
  const toolCallResult = (
75
75
  toolName: string,
76
- approval?: { id: string; resolution?: "cancelled" | "expired" },
76
+ approval?: ToolCallMessagePart["approval"],
77
77
  ): ChatModelRunResult => ({
78
78
  content: [toolCallPart(toolName, approval)],
79
79
  status: { type: "requires-action", reason: "tool-calls" },
@@ -485,6 +485,32 @@ describe("LocalThreadRuntimeCore tool approvals", () => {
485
485
  });
486
486
  });
487
487
 
488
+ it("records a free-form answer alongside the decision", async () => {
489
+ const { thread, runs } = createApprovalThread(
490
+ toolCallResult("send_email", { id: "a1", display: "text" }),
491
+ );
492
+
493
+ await thread.append(userMessage("send an email"));
494
+ await flush();
495
+
496
+ await thread.respondToToolApproval({
497
+ approvalId: "a1",
498
+ approved: true,
499
+ text: "Quarterly update",
500
+ });
501
+ await flush();
502
+
503
+ const toolCall = runs[1]!
504
+ .unstable_getMessage()
505
+ .content.find((part) => part.type === "tool-call");
506
+ expect(toolCall?.approval).toEqual({
507
+ id: "a1",
508
+ display: "text",
509
+ approved: true,
510
+ text: "Quarterly update",
511
+ });
512
+ });
513
+
488
514
  it("treats a terminal resolution as non-pending and continues the run", async () => {
489
515
  const { thread, runs } = createApprovalThread(
490
516
  toolCallResult("deploy", { id: "a1", resolution: "expired" }),
@@ -900,8 +900,9 @@ export class LocalThreadRuntimeCore
900
900
  approvalId,
901
901
  approved,
902
902
  optionId,
903
+ text,
903
904
  reason,
904
- }: RespondToToolApprovalOptions) {
905
+ }: RespondToToolApprovalOptions): Promise<void> {
905
906
  let message = this.repository
906
907
  .getMessages()
907
908
  .findLast(
@@ -944,6 +945,7 @@ export class LocalThreadRuntimeCore
944
945
  ...targetApproval,
945
946
  approved,
946
947
  ...(optionId != null && { optionId }),
948
+ ...(text != null && { text }),
947
949
  ...(reason != null && { reason }),
948
950
  };
949
951
  if (approved) return { ...c, approval };
@@ -968,5 +970,7 @@ export class LocalThreadRuntimeCore
968
970
  } else {
969
971
  this._persistPausedMessage(parentId, message);
970
972
  }
973
+
974
+ return Promise.resolve();
971
975
  }
972
976
  }