@assistant-ui/core 0.3.15 → 0.3.17

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 (455) hide show
  1. package/dist/index.d.ts +2 -2
  2. package/dist/index.js +2 -2
  3. package/dist/index.js.map +1 -1
  4. package/dist/internal.d.ts +8 -2
  5. package/dist/internal.js +9 -3
  6. package/dist/model-context/frame/host.d.ts.map +1 -1
  7. package/dist/model-context/frame/host.js +3 -2
  8. package/dist/model-context/frame/host.js.map +1 -1
  9. package/dist/model-context/frame/provider.d.ts +5 -0
  10. package/dist/model-context/frame/provider.d.ts.map +1 -1
  11. package/dist/model-context/frame/provider.js +141 -28
  12. package/dist/model-context/frame/provider.js.map +1 -1
  13. package/dist/model-context/interactable-composer-metadata.d.ts +1 -1
  14. package/dist/model-context/interactable-composer-metadata.js +1 -1
  15. package/dist/model-context/interactable-composer-metadata.js.map +1 -1
  16. package/dist/model-context/registry.d.ts +2 -2
  17. package/dist/model-context/registry.d.ts.map +1 -1
  18. package/dist/model-context/registry.js +9 -28
  19. package/dist/model-context/registry.js.map +1 -1
  20. package/dist/model-context/tool-call-text.d.ts +26 -0
  21. package/dist/model-context/tool-call-text.d.ts.map +1 -0
  22. package/dist/model-context/tool-call-text.js +18 -0
  23. package/dist/model-context/tool-call-text.js.map +1 -0
  24. package/dist/react/RuntimeAdapter.js +1 -1
  25. package/dist/react/adapters/LocalStorageThreadListAdapter.d.ts.map +1 -1
  26. package/dist/react/adapters/LocalStorageThreadListAdapter.js +1 -1
  27. package/dist/react/adapters/LocalStorageThreadListAdapter.js.map +1 -1
  28. package/dist/react/client/AdaptedRemoteThread.js +1 -1
  29. package/dist/react/client/InMemoryThreadList.d.ts.map +1 -1
  30. package/dist/react/client/InMemoryThreadList.js +25 -21
  31. package/dist/react/client/InMemoryThreadList.js.map +1 -1
  32. package/dist/react/client/Interactables.d.ts.map +1 -1
  33. package/dist/react/client/Interactables.js +60 -178
  34. package/dist/react/client/Interactables.js.map +1 -1
  35. package/dist/react/client/RemoteThreadList.d.ts +15 -0
  36. package/dist/react/client/RemoteThreadList.d.ts.map +1 -1
  37. package/dist/react/client/RemoteThreadList.js +263 -194
  38. package/dist/react/client/RemoteThreadList.js.map +1 -1
  39. package/dist/react/client/Tools.d.ts.map +1 -1
  40. package/dist/react/client/Tools.js +6 -2
  41. package/dist/react/client/Tools.js.map +1 -1
  42. package/dist/react/index.d.ts +4 -2
  43. package/dist/react/index.js +2 -1
  44. package/dist/react/interactables-legacy/Interactables.d.ts.map +1 -1
  45. package/dist/react/interactables-legacy/Interactables.js +60 -178
  46. package/dist/react/interactables-legacy/Interactables.js.map +1 -1
  47. package/dist/react/interactables-shared/useInteractablePersistenceQueue.d.ts +24 -0
  48. package/dist/react/interactables-shared/useInteractablePersistenceQueue.d.ts.map +1 -0
  49. package/dist/react/interactables-shared/useInteractablePersistenceQueue.js +138 -0
  50. package/dist/react/interactables-shared/useInteractablePersistenceQueue.js.map +1 -0
  51. package/dist/react/model-context/toolbox.d.ts +2 -14
  52. package/dist/react/model-context/toolbox.d.ts.map +1 -1
  53. package/dist/react/model-context/toolbox.js +1 -13
  54. package/dist/react/model-context/toolbox.js.map +1 -1
  55. package/dist/react/model-context/useAssistantContext.js +37 -7
  56. package/dist/react/model-context/useAssistantContext.js.map +1 -1
  57. package/dist/react/model-context/useAuiToolOverrides.js +31 -5
  58. package/dist/react/model-context/useAuiToolOverrides.js.map +1 -1
  59. package/dist/react/model-context/useInlineRender.d.ts.map +1 -1
  60. package/dist/react/model-context/useInlineRender.js +10 -36
  61. package/dist/react/model-context/useInlineRender.js.map +1 -1
  62. package/dist/react/primitive-hooks/useMessageError.d.ts.map +1 -1
  63. package/dist/react/primitive-hooks/useMessageError.js +2 -8
  64. package/dist/react/primitive-hooks/useMessageError.js.map +1 -1
  65. package/dist/react/primitive-hooks/useThreadListLoadMore.d.ts.map +1 -1
  66. package/dist/react/primitive-hooks/useThreadListLoadMore.js +2 -4
  67. package/dist/react/primitive-hooks/useThreadListLoadMore.js.map +1 -1
  68. package/dist/react/primitives/composer/ComposerAttachments.d.ts.map +1 -1
  69. package/dist/react/primitives/composer/ComposerAttachments.js +4 -3
  70. package/dist/react/primitives/composer/ComposerAttachments.js.map +1 -1
  71. package/dist/react/primitives/composer/ComposerIf.js.map +1 -1
  72. package/dist/react/primitives/composer/ComposerQueue.d.ts +2 -1
  73. package/dist/react/primitives/composer/ComposerQueue.d.ts.map +1 -1
  74. package/dist/react/primitives/composer/ComposerQueue.js +4 -3
  75. package/dist/react/primitives/composer/ComposerQueue.js.map +1 -1
  76. package/dist/react/primitives/message/MessageAttachments.d.ts.map +1 -1
  77. package/dist/react/primitives/message/MessageAttachments.js +7 -6
  78. package/dist/react/primitives/message/MessageAttachments.js.map +1 -1
  79. package/dist/react/primitives/message/MessageGroupedParts.js +2 -2
  80. package/dist/react/primitives/message/MessageGroupedParts.js.map +1 -1
  81. package/dist/react/primitives/message/MessageParts.js +3 -3
  82. package/dist/react/primitives/message/MessageParts.js.map +1 -1
  83. package/dist/react/primitives/threadList/ThreadListItems.d.ts.map +1 -1
  84. package/dist/react/primitives/threadList/ThreadListItems.js +5 -4
  85. package/dist/react/primitives/threadList/ThreadListItems.js.map +1 -1
  86. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.d.ts +7 -5
  87. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.d.ts.map +1 -1
  88. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js +44 -22
  89. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js.map +1 -1
  90. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts +7 -6
  91. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts.map +1 -1
  92. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js +102 -117
  93. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js.map +1 -1
  94. package/dist/react/runtimes/RemoteThreadResource.d.ts +1 -6
  95. package/dist/react/runtimes/RemoteThreadResource.d.ts.map +1 -1
  96. package/dist/react/runtimes/RemoteThreadResource.js +3 -3
  97. package/dist/react/runtimes/RemoteThreadResource.js.map +1 -1
  98. package/dist/react/runtimes/assistant-transport/runManager.d.ts.map +1 -1
  99. package/dist/react/runtimes/assistant-transport/runManager.js +2 -12
  100. package/dist/react/runtimes/assistant-transport/runManager.js.map +1 -1
  101. package/dist/react/runtimes/assistant-transport/useLatestRef.js +2 -2
  102. package/dist/react/runtimes/assistant-transport/useLatestRef.js.map +1 -1
  103. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.d.ts +3 -10
  104. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.d.ts.map +1 -1
  105. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js +43 -85
  106. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js.map +1 -1
  107. package/dist/react/runtimes/cloud/auiV0.d.ts +5 -1
  108. package/dist/react/runtimes/cloud/auiV0.d.ts.map +1 -1
  109. package/dist/react/runtimes/cloud/auiV0.js.map +1 -1
  110. package/dist/react/runtimes/cloud/createCloudThreadListAdapter.d.ts +29 -0
  111. package/dist/react/runtimes/cloud/createCloudThreadListAdapter.d.ts.map +1 -0
  112. package/dist/react/runtimes/cloud/createCloudThreadListAdapter.js +159 -0
  113. package/dist/react/runtimes/cloud/createCloudThreadListAdapter.js.map +1 -0
  114. package/dist/react/runtimes/cloud/createCloudThreadListAdapterCreateFallback.d.ts +12 -0
  115. package/dist/react/runtimes/cloud/createCloudThreadListAdapterCreateFallback.d.ts.map +1 -0
  116. package/dist/react/runtimes/cloud/createCloudThreadListAdapterCreateFallback.js +10 -0
  117. package/dist/react/runtimes/cloud/createCloudThreadListAdapterCreateFallback.js.map +1 -0
  118. package/dist/react/runtimes/cloud/useCloudThreadListAdapter.d.ts +1 -9
  119. package/dist/react/runtimes/cloud/useCloudThreadListAdapter.d.ts.map +1 -1
  120. package/dist/react/runtimes/cloud/useCloudThreadListAdapter.js +16 -153
  121. package/dist/react/runtimes/cloud/useCloudThreadListAdapter.js.map +1 -1
  122. package/dist/react/runtimes/cloud/useCloudThreadListRuntime.js.map +1 -1
  123. package/dist/react/runtimes/createMessageConverter.d.ts +2 -1
  124. package/dist/react/runtimes/createMessageConverter.d.ts.map +1 -1
  125. package/dist/react/runtimes/external-message-converter.d.ts +6 -25
  126. package/dist/react/runtimes/external-message-converter.d.ts.map +1 -1
  127. package/dist/react/runtimes/external-message-converter.js +33 -233
  128. package/dist/react/runtimes/external-message-converter.js.map +1 -1
  129. package/dist/react/runtimes/useRemoteThreadListRuntime.d.ts.map +1 -1
  130. package/dist/react/runtimes/useRemoteThreadListRuntime.js +14 -6
  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/composer-runtime.d.ts +1 -1
  137. package/dist/runtime/api/composer-runtime.js.map +1 -1
  138. package/dist/runtime/api/message-part-runtime.d.ts +2 -2
  139. package/dist/runtime/api/message-part-runtime.d.ts.map +1 -1
  140. package/dist/runtime/api/message-part-runtime.js +1 -1
  141. package/dist/runtime/api/message-part-runtime.js.map +1 -1
  142. package/dist/runtime/api/thread-list-runtime.d.ts +9 -1
  143. package/dist/runtime/api/thread-list-runtime.d.ts.map +1 -1
  144. package/dist/runtime/api/thread-list-runtime.js +5 -0
  145. package/dist/runtime/api/thread-list-runtime.js.map +1 -1
  146. package/dist/runtime/api/thread-runtime.d.ts +2 -3
  147. package/dist/runtime/api/thread-runtime.d.ts.map +1 -1
  148. package/dist/runtime/base/base-composer-runtime-core.d.ts +1 -1
  149. package/dist/runtime/base/base-composer-runtime-core.d.ts.map +1 -1
  150. package/dist/runtime/base/base-composer-runtime-core.js +14 -29
  151. package/dist/runtime/base/base-composer-runtime-core.js.map +1 -1
  152. package/dist/runtime/base/base-thread-runtime-core.d.ts +4 -6
  153. package/dist/runtime/base/base-thread-runtime-core.d.ts.map +1 -1
  154. package/dist/runtime/base/base-thread-runtime-core.js +5 -11
  155. package/dist/runtime/base/base-thread-runtime-core.js.map +1 -1
  156. package/dist/runtime/base/default-thread-composer-runtime-core.d.ts +1 -1
  157. package/dist/runtime/base/default-thread-composer-runtime-core.d.ts.map +1 -1
  158. package/dist/runtime/base/default-thread-composer-runtime-core.js +1 -1
  159. package/dist/runtime/base/default-thread-composer-runtime-core.js.map +1 -1
  160. package/dist/runtime/interfaces/composer-runtime-core.d.ts +1 -1
  161. package/dist/runtime/interfaces/thread-list-runtime-core.d.ts +14 -2
  162. package/dist/runtime/interfaces/thread-list-runtime-core.d.ts.map +1 -1
  163. package/dist/runtime/interfaces/thread-runtime-core.d.ts +16 -2
  164. package/dist/runtime/interfaces/thread-runtime-core.d.ts.map +1 -1
  165. package/dist/runtime/internal.d.ts +2 -1
  166. package/dist/runtime/internal.js +2 -1
  167. package/dist/runtime/queue/external-thread-queue-adapter.d.ts +1 -1
  168. package/dist/runtime/queue/message-queue.d.ts.map +1 -1
  169. package/dist/runtime/queue/message-queue.js +1 -1
  170. package/dist/runtime/queue/message-queue.js.map +1 -1
  171. package/dist/runtime/queue/queue-item.d.ts +12 -0
  172. package/dist/runtime/queue/queue-item.d.ts.map +1 -0
  173. package/dist/runtime/queue/queue-item.js +6 -0
  174. package/dist/runtime/queue/queue-item.js.map +1 -0
  175. package/dist/runtime/utils/abortable-thread-load.d.ts +15 -0
  176. package/dist/runtime/utils/abortable-thread-load.d.ts.map +1 -0
  177. package/dist/runtime/utils/abortable-thread-load.js +35 -0
  178. package/dist/runtime/utils/abortable-thread-load.js.map +1 -0
  179. package/dist/runtime/utils/attachment-add-operations.d.ts +18 -0
  180. package/dist/runtime/utils/attachment-add-operations.d.ts.map +1 -0
  181. package/dist/runtime/utils/attachment-add-operations.js +43 -0
  182. package/dist/runtime/utils/attachment-add-operations.js.map +1 -0
  183. package/dist/runtime/utils/auto-status.d.ts +7 -2
  184. package/dist/runtime/utils/auto-status.d.ts.map +1 -1
  185. package/dist/runtime/utils/auto-status.js +13 -3
  186. package/dist/runtime/utils/auto-status.js.map +1 -1
  187. package/dist/runtime/utils/external-message-conversion.d.ts +48 -0
  188. package/dist/runtime/utils/external-message-conversion.d.ts.map +1 -0
  189. package/dist/runtime/utils/external-message-conversion.js +222 -0
  190. package/dist/runtime/utils/external-message-conversion.js.map +1 -0
  191. package/dist/runtime/utils/external-store-message.d.ts +2 -1
  192. package/dist/runtime/utils/external-store-message.d.ts.map +1 -1
  193. package/dist/runtime/utils/external-store-message.js +2 -1
  194. package/dist/runtime/utils/external-store-message.js.map +1 -1
  195. package/dist/runtime/utils/message-repository-session.d.ts +30 -0
  196. package/dist/runtime/utils/message-repository-session.d.ts.map +1 -0
  197. package/dist/runtime/utils/message-repository-session.js +130 -0
  198. package/dist/runtime/utils/message-repository-session.js.map +1 -0
  199. package/dist/runtime/utils/message-repository.d.ts.map +1 -1
  200. package/dist/runtime/utils/message-repository.js +3 -4
  201. package/dist/runtime/utils/message-repository.js.map +1 -1
  202. package/dist/runtime/utils/pending-tool-calls.d.ts +20 -0
  203. package/dist/runtime/utils/pending-tool-calls.d.ts.map +1 -0
  204. package/dist/runtime/utils/pending-tool-calls.js +22 -0
  205. package/dist/runtime/utils/pending-tool-calls.js.map +1 -0
  206. package/dist/runtime/utils/resolveToolApprovalResponse.d.ts +6 -3
  207. package/dist/runtime/utils/resolveToolApprovalResponse.d.ts.map +1 -1
  208. package/dist/runtime/utils/resolveToolApprovalResponse.js +12 -3
  209. package/dist/runtime/utils/resolveToolApprovalResponse.js.map +1 -1
  210. package/dist/runtime/utils/thread-message-like.d.ts +3 -10
  211. package/dist/runtime/utils/thread-message-like.d.ts.map +1 -1
  212. package/dist/runtime/utils/thread-message-like.js.map +1 -1
  213. package/dist/runtime/utils/tool-call-tree.d.ts +39 -0
  214. package/dist/runtime/utils/tool-call-tree.d.ts.map +1 -0
  215. package/dist/runtime/utils/tool-call-tree.js +85 -0
  216. package/dist/runtime/utils/tool-call-tree.js.map +1 -0
  217. package/dist/runtimes/external-store/external-store-adapter.d.ts +29 -2
  218. package/dist/runtimes/external-store/external-store-adapter.d.ts.map +1 -1
  219. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.d.ts +2 -5
  220. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.d.ts.map +1 -1
  221. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.js +3 -10
  222. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.js.map +1 -1
  223. package/dist/runtimes/external-store/external-store-thread-runtime-core.d.ts +21 -2
  224. package/dist/runtimes/external-store/external-store-thread-runtime-core.d.ts.map +1 -1
  225. package/dist/runtimes/external-store/external-store-thread-runtime-core.js +154 -46
  226. package/dist/runtimes/external-store/external-store-thread-runtime-core.js.map +1 -1
  227. package/dist/runtimes/inert/InertThreadRuntimeCore.d.ts +1 -1
  228. package/dist/runtimes/inert/InertThreadRuntimeCore.d.ts.map +1 -1
  229. package/dist/runtimes/inert/InertThreadRuntimeCore.js.map +1 -1
  230. package/dist/runtimes/local/local-thread-runtime-core.d.ts +4 -2
  231. package/dist/runtimes/local/local-thread-runtime-core.d.ts.map +1 -1
  232. package/dist/runtimes/local/local-thread-runtime-core.js +23 -3
  233. package/dist/runtimes/local/local-thread-runtime-core.js.map +1 -1
  234. package/dist/runtimes/remote-thread-list/remote-thread-state.d.ts +12 -1
  235. package/dist/runtimes/remote-thread-list/remote-thread-state.d.ts.map +1 -1
  236. package/dist/runtimes/remote-thread-list/remote-thread-state.js +107 -14
  237. package/dist/runtimes/remote-thread-list/remote-thread-state.js.map +1 -1
  238. package/dist/runtimes/remote-thread-list/title.d.ts +9 -0
  239. package/dist/runtimes/remote-thread-list/title.d.ts.map +1 -0
  240. package/dist/runtimes/remote-thread-list/title.js +6 -0
  241. package/dist/runtimes/remote-thread-list/title.js.map +1 -0
  242. package/dist/runtimes/remote-thread-list/types.d.ts +6 -5
  243. package/dist/runtimes/remote-thread-list/types.d.ts.map +1 -1
  244. package/dist/runtimes/tool-invocations/ToolInvocationTracker.d.ts +31 -8
  245. package/dist/runtimes/tool-invocations/ToolInvocationTracker.d.ts.map +1 -1
  246. package/dist/runtimes/tool-invocations/ToolInvocationTracker.js +98 -59
  247. package/dist/runtimes/tool-invocations/ToolInvocationTracker.js.map +1 -1
  248. package/dist/store/clients/external-thread.d.ts +1 -1
  249. package/dist/store/clients/external-thread.d.ts.map +1 -1
  250. package/dist/store/clients/external-thread.js +85 -103
  251. package/dist/store/clients/external-thread.js.map +1 -1
  252. package/dist/store/clients/single-thread-list.d.ts.map +1 -1
  253. package/dist/store/clients/single-thread-list.js +2 -2
  254. package/dist/store/clients/single-thread-list.js.map +1 -1
  255. package/dist/store/clients/suggestions.d.ts.map +1 -1
  256. package/dist/store/clients/suggestions.js +33 -12
  257. package/dist/store/clients/suggestions.js.map +1 -1
  258. package/dist/store/clients/thread-message-client.js +1 -1
  259. package/dist/store/clients/thread-selection-events.js +1 -1
  260. package/dist/store/index.d.ts +6 -3
  261. package/dist/store/index.js +2 -1
  262. package/dist/store/internal.d.ts +2 -2
  263. package/dist/store/internal.js +2 -2
  264. package/dist/store/primitive-predicates.d.ts +4 -1
  265. package/dist/store/primitive-predicates.d.ts.map +1 -1
  266. package/dist/store/primitive-predicates.js +9 -1
  267. package/dist/store/primitive-predicates.js.map +1 -1
  268. package/dist/store/runtime-clients/composer-runtime-client.js +1 -1
  269. package/dist/store/runtime-clients/message-runtime-client.js +1 -1
  270. package/dist/store/runtime-clients/thread-list-item-runtime-client.js +1 -1
  271. package/dist/store/runtime-clients/thread-list-runtime-client.d.ts.map +1 -1
  272. package/dist/store/runtime-clients/thread-list-runtime-client.js +124 -105
  273. package/dist/store/runtime-clients/thread-list-runtime-client.js.map +1 -1
  274. package/dist/store/runtime-clients/thread-runtime-client.js +1 -1
  275. package/dist/store/runtime-clients/useSubscribable.d.ts +3 -1
  276. package/dist/store/runtime-clients/useSubscribable.d.ts.map +1 -1
  277. package/dist/store/runtime-clients/useSubscribable.js +1 -1
  278. package/dist/store/runtime-clients/useSubscribable.js.map +1 -1
  279. package/dist/store/scope-registration.d.ts +1 -1
  280. package/dist/store/scopes/composer.d.ts +2 -1
  281. package/dist/store/scopes/composer.d.ts.map +1 -1
  282. package/dist/store/scopes/message.d.ts +1 -1
  283. package/dist/store/scopes/part.d.ts +6 -3
  284. package/dist/store/scopes/part.d.ts.map +1 -1
  285. package/dist/store/scopes/queue-item.d.ts +2 -9
  286. package/dist/store/scopes/queue-item.d.ts.map +1 -1
  287. package/dist/store/scopes/queue-item.js +0 -6
  288. package/dist/subscribable/subscribable.d.ts +8 -1
  289. package/dist/subscribable/subscribable.d.ts.map +1 -1
  290. package/dist/subscribable/subscribable.js +24 -16
  291. package/dist/subscribable/subscribable.js.map +1 -1
  292. package/dist/types/message.d.ts +47 -4
  293. package/dist/types/message.d.ts.map +1 -1
  294. package/dist/types/message.js +7 -1
  295. package/dist/types/message.js.map +1 -1
  296. package/dist/utils/data-url.d.ts +14 -1
  297. package/dist/utils/data-url.d.ts.map +1 -1
  298. package/dist/utils/data-url.js +13 -1
  299. package/dist/utils/data-url.js.map +1 -1
  300. package/dist/utils/invoke-user-callback.d.ts +5 -0
  301. package/dist/utils/invoke-user-callback.d.ts.map +1 -0
  302. package/dist/utils/invoke-user-callback.js +19 -0
  303. package/dist/utils/invoke-user-callback.js.map +1 -0
  304. package/dist/utils/normalizePartStatus.js.map +1 -1
  305. package/package.json +12 -17
  306. package/src/index.ts +6 -1
  307. package/src/internal.ts +20 -0
  308. package/src/model-context/frame/host.test.ts +91 -7
  309. package/src/model-context/frame/host.ts +7 -2
  310. package/src/model-context/frame/provider.test.ts +484 -13
  311. package/src/model-context/frame/provider.ts +205 -35
  312. package/src/model-context/interactable-composer-metadata.ts +1 -1
  313. package/src/model-context/registry.test.ts +44 -0
  314. package/src/model-context/registry.ts +13 -35
  315. package/src/model-context/tool-call-text.test.ts +67 -0
  316. package/src/model-context/tool-call-text.ts +67 -0
  317. package/src/react/AssistantRuntimeProvider.test.tsx +66 -2
  318. package/src/react/adapters/LocalStorageThreadListAdapter.tsx +1 -3
  319. package/src/react/client/InMemoryThreadList.test.tsx +49 -0
  320. package/src/react/client/InMemoryThreadList.ts +7 -1
  321. package/src/react/client/Interactables.ts +19 -178
  322. package/src/react/client/RemoteThreadList.background.test.ts +384 -0
  323. package/src/react/client/RemoteThreadList.concurrent.test.tsx +274 -0
  324. package/src/react/client/RemoteThreadList.test.ts +369 -1
  325. package/src/react/client/RemoteThreadList.ts +320 -142
  326. package/src/react/client/Tools.ts +7 -1
  327. package/src/react/index.ts +4 -0
  328. package/src/react/interactables-legacy/Interactables.ts +20 -177
  329. package/src/react/interactables-shared/useInteractablePersistenceQueue.test.tsx +161 -0
  330. package/src/react/interactables-shared/useInteractablePersistenceQueue.ts +206 -0
  331. package/src/react/model-context/modelContextHooks.test.tsx +131 -0
  332. package/src/react/model-context/toolbox.test.ts +9 -9
  333. package/src/react/model-context/toolbox.ts +9 -36
  334. package/src/react/model-context/useAssistantContext.ts +4 -2
  335. package/src/react/model-context/useAuiToolOverrides.ts +4 -2
  336. package/src/react/model-context/useInlineRender.test.tsx +48 -0
  337. package/src/react/model-context/useInlineRender.ts +14 -10
  338. package/src/react/primitive-hooks/useMessageError.ts +2 -19
  339. package/src/react/primitive-hooks/useThreadListLoadMore.ts +2 -3
  340. package/src/react/primitives/composer/ComposerAttachments.test.tsx +76 -0
  341. package/src/react/primitives/composer/ComposerAttachments.tsx +9 -4
  342. package/src/react/primitives/composer/ComposerQueue.test.tsx +72 -0
  343. package/src/react/primitives/composer/ComposerQueue.tsx +7 -4
  344. package/src/react/primitives/message/MessageAttachments.test.tsx +107 -37
  345. package/src/react/primitives/message/MessageAttachments.tsx +10 -7
  346. package/src/react/primitives/message/MessageGroupedParts.tsx +2 -2
  347. package/src/react/primitives/message/MessageParts.tsx +3 -3
  348. package/src/react/primitives/threadList/ThreadListItems.test.tsx +99 -0
  349. package/src/react/primitives/threadList/ThreadListItems.tsx +8 -5
  350. package/src/react/runtimes/RemoteThreadListHookInstanceManager.running.test.tsx +94 -1
  351. package/src/react/runtimes/RemoteThreadListHookInstanceManager.tsx +72 -31
  352. package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.switch-delete.test.ts +169 -0
  353. package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.tsx +140 -124
  354. package/src/react/runtimes/RemoteThreadResource.ts +1 -4
  355. package/src/react/runtimes/assistant-transport/runManager.ts +4 -17
  356. package/src/react/runtimes/assistant-transport/transport-scheduling.test.ts +51 -2
  357. package/src/react/runtimes/assistant-transport/useLatestRef.ts +2 -2
  358. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.test.ts +45 -0
  359. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.test.tsx +157 -1
  360. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.ts +82 -176
  361. package/src/react/runtimes/cloud/auiV0.ts +5 -0
  362. package/src/react/runtimes/cloud/createCloudThreadListAdapter.test.ts +88 -0
  363. package/src/react/runtimes/cloud/createCloudThreadListAdapter.ts +236 -0
  364. package/src/react/runtimes/cloud/createCloudThreadListAdapterCreateFallback.test.ts +37 -0
  365. package/src/react/runtimes/cloud/createCloudThreadListAdapterCreateFallback.ts +18 -0
  366. package/src/react/runtimes/cloud/useCloudThreadListAdapter.test.tsx +90 -1
  367. package/src/react/runtimes/cloud/useCloudThreadListAdapter.tsx +33 -238
  368. package/src/react/runtimes/external-message-converter.test.ts +0 -14
  369. package/src/react/runtimes/external-message-converter.test.tsx +77 -0
  370. package/src/react/runtimes/external-message-converter.ts +59 -481
  371. package/src/react/runtimes/useRemoteThreadListRuntime.concurrent.test.tsx +216 -0
  372. package/src/react/runtimes/useRemoteThreadListRuntime.ts +24 -6
  373. package/src/react/types/MessagePartComponentTypes.ts +7 -3
  374. package/src/react/types/scopes/tools.ts +2 -0
  375. package/src/runtime/api/composer-runtime.ts +1 -1
  376. package/src/runtime/api/message-part-runtime.ts +3 -3
  377. package/src/runtime/api/message-runtime.test.ts +28 -1
  378. package/src/runtime/api/thread-list-runtime.ts +25 -1
  379. package/src/runtime/base/base-composer-runtime-core.ts +20 -40
  380. package/src/runtime/base/base-thread-runtime-core.test.ts +110 -3
  381. package/src/runtime/base/base-thread-runtime-core.ts +16 -15
  382. package/src/runtime/base/default-thread-composer-runtime-core.ts +1 -4
  383. package/src/runtime/interfaces/composer-runtime-core.ts +1 -1
  384. package/src/runtime/interfaces/thread-list-runtime-core.ts +20 -1
  385. package/src/runtime/interfaces/thread-runtime-core.ts +18 -2
  386. package/src/runtime/internal.ts +5 -0
  387. package/src/runtime/queue/external-thread-queue-adapter.ts +1 -1
  388. package/src/runtime/queue/message-queue.ts +1 -4
  389. package/src/runtime/queue/queue-item.ts +10 -0
  390. package/src/runtime/utils/abortable-thread-load.test.ts +87 -0
  391. package/src/runtime/utils/abortable-thread-load.ts +53 -0
  392. package/src/runtime/utils/attachment-add-operations.ts +59 -0
  393. package/src/runtime/utils/auto-status.test.ts +57 -0
  394. package/src/runtime/utils/auto-status.ts +46 -1
  395. package/src/runtime/utils/external-message-conversion.test.ts +210 -0
  396. package/src/runtime/utils/external-message-conversion.ts +510 -0
  397. package/src/runtime/utils/external-store-message.ts +2 -0
  398. package/src/runtime/utils/message-repository-session.test.ts +195 -0
  399. package/src/runtime/utils/message-repository-session.ts +176 -0
  400. package/src/runtime/utils/message-repository.ts +7 -4
  401. package/src/runtime/utils/pending-tool-calls.test.ts +57 -0
  402. package/src/runtime/utils/pending-tool-calls.ts +35 -0
  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 +4 -10
  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 +6 -14
  410. package/src/runtimes/external-store/external-store-thread-runtime-core.ts +231 -61
  411. package/src/runtimes/inert/InertThreadRuntimeCore.ts +1 -1
  412. package/src/runtimes/local/local-thread-runtime-core.test.ts +201 -4
  413. package/src/runtimes/local/local-thread-runtime-core.ts +53 -4
  414. package/src/runtimes/remote-thread-list/remote-thread-state.test.ts +149 -0
  415. package/src/runtimes/remote-thread-list/remote-thread-state.ts +178 -16
  416. package/src/runtimes/remote-thread-list/title.ts +3 -0
  417. package/src/runtimes/remote-thread-list/types.ts +6 -5
  418. package/src/runtimes/tool-invocations/EDGE_CASES.md +125 -3
  419. package/src/runtimes/tool-invocations/ToolInvocationTracker.test.ts +856 -102
  420. package/src/runtimes/tool-invocations/ToolInvocationTracker.ts +145 -89
  421. package/src/store/clients/external-thread.ts +30 -69
  422. package/src/store/clients/single-thread-list.ts +4 -1
  423. package/src/store/clients/suggestions.test.ts +185 -0
  424. package/src/store/clients/suggestions.ts +42 -35
  425. package/src/store/index.ts +4 -0
  426. package/src/store/internal.ts +2 -0
  427. package/src/store/primitive-predicates.ts +35 -0
  428. package/src/store/runtime-clients/thread-list-runtime-client.ts +16 -2
  429. package/src/store/runtime-clients/useSubscribable.ts +8 -2
  430. package/src/store/scopes/message.ts +1 -1
  431. package/src/store/scopes/part.ts +6 -3
  432. package/src/store/scopes/queue-item.ts +2 -9
  433. package/src/subscribable/subscribable.test.ts +50 -0
  434. package/src/subscribable/subscribable.ts +33 -21
  435. package/src/tests/MessageRepository.test.ts +77 -0
  436. package/src/tests/RemoteThreadListThreadListRuntimeCore-load-race.test.ts +305 -0
  437. package/src/tests/RemoteThreadListThreadListRuntimeCore-loadMore.test.ts +78 -0
  438. package/src/tests/RemoteThreadListThreadListRuntimeCore-reload.test.ts +52 -0
  439. package/src/tests/RemoteThreadListThreadListRuntimeCore-title-race.test.ts +158 -0
  440. package/src/tests/external-store-optimistic-id.test.ts +79 -0
  441. package/src/tests/external-store-repository-instance.test.ts +166 -0
  442. package/src/tests/external-store-thread-runtime-core-adapter.test.ts +186 -16
  443. package/src/tests/external-store-thread-runtime-core.test.ts +642 -0
  444. package/src/tests/external-thread-attachments.test.tsx +258 -1
  445. package/src/tests/remote-thread-list-adapter-switch.test.ts +66 -1
  446. package/src/tests/single-thread-list-item.test.tsx +47 -0
  447. package/src/tests/thread-message-like.test.ts +39 -1
  448. package/src/tests/thread-switch-events.test.tsx +397 -2
  449. package/src/types/message.ts +58 -5
  450. package/src/utils/data-url.test.ts +52 -1
  451. package/src/utils/data-url.ts +21 -0
  452. package/src/utils/invoke-user-callback.test.ts +82 -0
  453. package/src/utils/invoke-user-callback.ts +31 -0
  454. package/src/utils/normalizePartStatus.ts +1 -1
  455. package/dist/store/scopes/queue-item.js.map +0 -1
@@ -47,6 +47,7 @@ const createAssistantMessage = (
47
47
  toolCallId?: string;
48
48
  toolName?: string;
49
49
  nestedMessages?: ThreadAssistantMessage[];
50
+ approval?: { id: string; approved?: boolean };
50
51
  },
51
52
  ): ThreadAssistantMessage => ({
52
53
  id: "m-1",
@@ -70,10 +71,21 @@ const createAssistantMessage = (
70
71
  ...(options?.result !== undefined && { result: options.result }),
71
72
  ...(options?.isError !== undefined && { isError: options.isError }),
72
73
  ...(options?.nestedMessages && { messages: options.nestedMessages }),
74
+ ...(options?.approval && { approval: options.approval }),
73
75
  },
74
76
  ],
75
77
  });
76
78
 
79
+ /**
80
+ * The stream failure that sets this flag cannot be provoked through the public
81
+ * API: `unstable_toolResultStream` turns a throwing tool into an error result
82
+ * rather than an error chunk, so the transform never rejects. The recovery
83
+ * branch it guards is reachable on its own.
84
+ */
85
+ const killPipeline = (tracker: ToolInvocationTracker) => {
86
+ (tracker as unknown as { _pipelineDead: boolean })._pipelineDead = true;
87
+ };
88
+
77
89
  describe("ToolInvocationTracker", () => {
78
90
  it("does not crash and does not re-fire streamCall when tool argsText regresses mid-stream", async () => {
79
91
  // The tracker holds the contract: streamCall fires exactly once per
@@ -235,6 +247,96 @@ describe("ToolInvocationTracker", () => {
235
247
  }
236
248
  });
237
249
 
250
+ it("keeps a human-input interrupt that execute requests before its first await", async () => {
251
+ const execute = vi.fn(async (_args, { human }) => ({
252
+ approved: (await human({ request: "approve" })) === true,
253
+ }));
254
+ const getTools = () => ({
255
+ weatherSearch: {
256
+ parameters: { type: "object", properties: {} },
257
+ execute,
258
+ } satisfies Tool,
259
+ });
260
+ const onResult = vi.fn();
261
+ let statuses: Record<string, ToolExecutionStatus> = {};
262
+ const onStatusesChange = (s: ReadonlyMap<string, ToolExecutionStatus>) => {
263
+ statuses = Object.fromEntries(s);
264
+ };
265
+
266
+ const tracker = new ToolInvocationTracker(getTools, {
267
+ onResult,
268
+ onStatusesChange,
269
+ });
270
+ tracker.setState(createState([], false));
271
+ tracker.setState(
272
+ createState(
273
+ [createAssistantMessage('{"query":"London"}', { query: "London" })],
274
+ false,
275
+ ),
276
+ );
277
+
278
+ await waitFor(() => {
279
+ expect(statuses["tool-1"]?.type).toBe("interrupt");
280
+ });
281
+
282
+ expect(tracker.resume("tool-1", true)).toBe(true);
283
+
284
+ await waitFor(() => {
285
+ expect(onResult).toHaveBeenCalledWith(
286
+ expect.objectContaining({
287
+ toolCallId: "tool-1",
288
+ result: { approved: true },
289
+ }),
290
+ );
291
+ });
292
+ expect(statuses).toEqual({});
293
+ });
294
+
295
+ it("marks a fresh execution as executing when an earlier one left a human-input request behind", async () => {
296
+ const execute = vi
297
+ .fn()
298
+ .mockImplementationOnce((_args, { human }) =>
299
+ human({ request: "approve" }),
300
+ )
301
+ .mockImplementationOnce(() => new Promise(() => {}));
302
+ const getTools = () => ({
303
+ weatherSearch: {
304
+ parameters: { type: "object", properties: {} },
305
+ execute,
306
+ } satisfies Tool,
307
+ });
308
+ let statuses: Record<string, ToolExecutionStatus> = {};
309
+ const tracker = new ToolInvocationTracker(getTools, {
310
+ onResult: vi.fn(),
311
+ onStatusesChange: (s: ReadonlyMap<string, ToolExecutionStatus>) => {
312
+ statuses = Object.fromEntries(s);
313
+ },
314
+ });
315
+ tracker.setState(createState([], false));
316
+ tracker.setState(
317
+ createState(
318
+ [createAssistantMessage('{"query":"London"}', { query: "London" })],
319
+ false,
320
+ ),
321
+ );
322
+ await waitFor(() => {
323
+ expect(statuses["tool-1"]?.type).toBe("interrupt");
324
+ });
325
+
326
+ killPipeline(tracker);
327
+ tracker.setState(
328
+ createState(
329
+ [createAssistantMessage('{"query":"Paris"}', { query: "Paris" })],
330
+ false,
331
+ ),
332
+ );
333
+
334
+ await waitFor(() => {
335
+ expect(execute).toHaveBeenCalledTimes(2);
336
+ expect(statuses["tool-1"]?.type).toBe("executing");
337
+ });
338
+ });
339
+
238
340
  it("does not auto-submit a parse-error result for a non-executable tool whose divergent argsText closes", async () => {
239
341
  // Same close-gating mismatch as the executable case, but for a tool with
240
342
  // no frontend execute. Closing on the divergent complete snapshot would
@@ -304,19 +406,21 @@ describe("ToolInvocationTracker", () => {
304
406
  onResult,
305
407
  onStatusesChange,
306
408
  });
307
- tracker.setState(createState([]));
409
+ tracker.setState(createState([], false));
308
410
 
309
411
  // Single monotonic snapshot growing to a complete value triggers execute.
310
412
  tracker.setState(
311
- createState([
312
- createAssistantMessage('{"query":"London"', { query: "London" }),
313
- ]),
413
+ createState(
414
+ [createAssistantMessage('{"query":"London"', { query: "London" })],
415
+ false,
416
+ ),
314
417
  );
315
418
 
316
419
  tracker.setState(
317
- createState([
318
- createAssistantMessage('{"query":"London"}', { query: "London" }),
319
- ]),
420
+ createState(
421
+ [createAssistantMessage('{"query":"London"}', { query: "London" })],
422
+ false,
423
+ ),
320
424
  );
321
425
 
322
426
  await waitFor(() => {
@@ -370,6 +474,627 @@ describe("ToolInvocationTracker", () => {
370
474
  });
371
475
  });
372
476
 
477
+ it("never executes a tool call that carries a provider approval", async () => {
478
+ const execute = vi.fn(async () => ({ deleted: true }));
479
+ const getTools = () => ({
480
+ deleteFile: {
481
+ parameters: { type: "object", properties: {} },
482
+ execute,
483
+ } satisfies Tool,
484
+ });
485
+ const onResult = vi.fn();
486
+ const tracker = new ToolInvocationTracker(getTools, {
487
+ onResult,
488
+ onStatusesChange: () => {},
489
+ });
490
+ tracker.setState(createState([]));
491
+
492
+ const gated = (approval: { id: string; approved?: boolean }) =>
493
+ createAssistantMessage(
494
+ '{"path":"/tmp/a"}',
495
+ { path: "/tmp/a" },
496
+ { toolName: "deleteFile", approval },
497
+ );
498
+
499
+ tracker.setState(createState([gated({ id: "approval-1" })], false));
500
+ await new Promise((r) => setTimeout(r, 0));
501
+ expect(execute).not.toHaveBeenCalled();
502
+
503
+ tracker.setState(
504
+ createState([gated({ id: "approval-1", approved: true })], true),
505
+ );
506
+ await new Promise((r) => setTimeout(r, 0));
507
+
508
+ expect(execute).not.toHaveBeenCalled();
509
+ expect(onResult).not.toHaveBeenCalled();
510
+ });
511
+
512
+ it("stops executing a live tool call once a provider approval lands", async () => {
513
+ const execute = vi.fn(async () => ({ deleted: true }));
514
+ const getTools = () => ({
515
+ deleteFile: {
516
+ parameters: { type: "object", properties: {} },
517
+ execute,
518
+ } satisfies Tool,
519
+ });
520
+ const onResult = vi.fn();
521
+ const tracker = new ToolInvocationTracker(getTools, {
522
+ onResult,
523
+ onStatusesChange: () => {},
524
+ });
525
+ tracker.setState(createState([]));
526
+
527
+ tracker.setState(
528
+ createState(
529
+ [
530
+ createAssistantMessage(
531
+ '{"path":"/tmp',
532
+ {},
533
+ { toolName: "deleteFile" },
534
+ ),
535
+ ],
536
+ true,
537
+ ),
538
+ );
539
+ tracker.setState(
540
+ createState(
541
+ [
542
+ createAssistantMessage(
543
+ '{"path":"/tmp/a"}',
544
+ { path: "/tmp/a" },
545
+ { toolName: "deleteFile", approval: { id: "approval-1" } },
546
+ ),
547
+ ],
548
+ true,
549
+ ),
550
+ );
551
+ await new Promise((r) => setTimeout(r, 0));
552
+
553
+ expect(execute).not.toHaveBeenCalled();
554
+ expect(onResult).not.toHaveBeenCalled();
555
+ });
556
+
557
+ it("drops the result of an in-flight execute once a provider approval lands", async () => {
558
+ let resolveExecute!: (value: { deleted: boolean }) => void;
559
+ const execute = vi.fn(
560
+ () =>
561
+ new Promise<{ deleted: boolean }>((r) => {
562
+ resolveExecute = r;
563
+ }),
564
+ );
565
+ const getTools = () => ({
566
+ deleteFile: {
567
+ parameters: { type: "object", properties: {} },
568
+ execute,
569
+ } satisfies Tool,
570
+ });
571
+ const onResult = vi.fn();
572
+ const tracker = new ToolInvocationTracker(
573
+ getTools,
574
+ { onResult, onStatusesChange: () => {} },
575
+ () => true,
576
+ );
577
+ tracker.setState(createState([]));
578
+
579
+ tracker.setState(
580
+ createState(
581
+ [
582
+ createAssistantMessage(
583
+ '{"path":"/tmp/a"}',
584
+ { path: "/tmp/a" },
585
+ {
586
+ toolName: "deleteFile",
587
+ },
588
+ ),
589
+ ],
590
+ true,
591
+ ),
592
+ );
593
+ await waitFor(() => expect(execute).toHaveBeenCalledTimes(1));
594
+
595
+ tracker.setState(
596
+ createState(
597
+ [
598
+ createAssistantMessage(
599
+ '{"path":"/tmp/a"}',
600
+ { path: "/tmp/a" },
601
+ {
602
+ toolName: "deleteFile",
603
+ approval: { id: "approval-1" },
604
+ },
605
+ ),
606
+ ],
607
+ true,
608
+ ),
609
+ );
610
+ resolveExecute({ deleted: true });
611
+ await new Promise((r) => setTimeout(r, 0));
612
+
613
+ expect(onResult).not.toHaveBeenCalled();
614
+ });
615
+
616
+ it("does not execute a registered tool while the provider's run is open", async () => {
617
+ const execute = vi.fn(async () => ({ deleted: true }));
618
+ const getTools = () => ({
619
+ deleteFile: {
620
+ parameters: { type: "object", properties: {} },
621
+ execute,
622
+ } satisfies Tool,
623
+ });
624
+ const onResult = vi.fn();
625
+ const tracker = new ToolInvocationTracker(getTools, {
626
+ onResult,
627
+ onStatusesChange: () => {},
628
+ });
629
+ tracker.setState(createState([]));
630
+
631
+ const complete = (isRunning: boolean) =>
632
+ createState(
633
+ [
634
+ createAssistantMessage(
635
+ '{"path":"/tmp/a"}',
636
+ { path: "/tmp/a" },
637
+ { toolName: "deleteFile" },
638
+ ),
639
+ ],
640
+ isRunning,
641
+ );
642
+
643
+ tracker.setState(complete(true));
644
+ await new Promise((r) => setTimeout(r, 0));
645
+ expect(execute).not.toHaveBeenCalled();
646
+
647
+ tracker.setState(complete(false));
648
+ await waitFor(() => expect(execute).toHaveBeenCalledTimes(1));
649
+ await waitFor(() => expect(onResult).toHaveBeenCalledTimes(1));
650
+ });
651
+
652
+ it("streams a streamCall tool's args while the run is open and executes it after", async () => {
653
+ const streamed: unknown[] = [];
654
+ const streamCall = vi.fn(
655
+ async (reader: {
656
+ args: { streamValues: () => AsyncIterable<unknown> };
657
+ }) => {
658
+ for await (const partial of reader.args.streamValues())
659
+ streamed.push(partial);
660
+ },
661
+ );
662
+ const execute = vi.fn(async () => ({ deleted: true }));
663
+ const getTools = () => ({
664
+ deleteFile: {
665
+ parameters: { type: "object", properties: {} },
666
+ streamCall,
667
+ execute,
668
+ } satisfies Tool,
669
+ });
670
+ const onResult = vi.fn();
671
+ const tracker = new ToolInvocationTracker(getTools, {
672
+ onResult,
673
+ onStatusesChange: () => {},
674
+ });
675
+ tracker.setState(createState([], false));
676
+
677
+ tracker.setState(
678
+ createState([
679
+ createAssistantMessage(
680
+ '{"path":"/tmp/a"',
681
+ { path: "/tmp/a" },
682
+ {
683
+ toolName: "deleteFile",
684
+ },
685
+ ),
686
+ ]),
687
+ );
688
+ tracker.setState(
689
+ createState([
690
+ createAssistantMessage(
691
+ '{"path":"/tmp/a"}',
692
+ { path: "/tmp/a" },
693
+ {
694
+ toolName: "deleteFile",
695
+ },
696
+ ),
697
+ ]),
698
+ );
699
+
700
+ await waitFor(() => expect(streamCall).toHaveBeenCalledTimes(1));
701
+ await waitFor(() => expect(streamed.length).toBeGreaterThan(0));
702
+ expect(execute).not.toHaveBeenCalled();
703
+
704
+ tracker.setState(
705
+ createState(
706
+ [
707
+ createAssistantMessage(
708
+ '{"path":"/tmp/a"}',
709
+ { path: "/tmp/a" },
710
+ {
711
+ toolName: "deleteFile",
712
+ },
713
+ ),
714
+ ],
715
+ false,
716
+ ),
717
+ );
718
+
719
+ await waitFor(() => expect(execute).toHaveBeenCalledTimes(1));
720
+ await waitFor(() => expect(onResult).toHaveBeenCalledTimes(1));
721
+ expect(streamCall).toHaveBeenCalledTimes(1);
722
+ });
723
+
724
+ it("never executes a registered tool that was waiting when the turn was aborted", async () => {
725
+ const execute = vi.fn(async () => ({ deleted: true }));
726
+ const getTools = () => ({
727
+ deleteFile: {
728
+ parameters: { type: "object", properties: {} },
729
+ execute,
730
+ } satisfies Tool,
731
+ });
732
+ const onResult = vi.fn();
733
+ const tracker = new ToolInvocationTracker(getTools, {
734
+ onResult,
735
+ onStatusesChange: () => {},
736
+ });
737
+ tracker.setState(createState([], false));
738
+
739
+ const complete = (isRunning: boolean) =>
740
+ createState(
741
+ [
742
+ createAssistantMessage(
743
+ '{"path":"/tmp/a"}',
744
+ { path: "/tmp/a" },
745
+ { toolName: "deleteFile" },
746
+ ),
747
+ ],
748
+ isRunning,
749
+ );
750
+
751
+ tracker.setState(complete(true));
752
+ await new Promise((r) => setTimeout(r, 0));
753
+ expect(execute).not.toHaveBeenCalled();
754
+
755
+ await tracker.abort({ discardPending: true });
756
+
757
+ tracker.setState(complete(false));
758
+ await new Promise((r) => setTimeout(r, 0));
759
+
760
+ expect(execute).not.toHaveBeenCalled();
761
+ expect(onResult).not.toHaveBeenCalled();
762
+ });
763
+
764
+ it("still executes a waiting tool when an abort only interrupts the turn", async () => {
765
+ const execute = vi.fn(async () => ({ deleted: true }));
766
+ const getTools = () => ({
767
+ deleteFile: {
768
+ parameters: { type: "object", properties: {} },
769
+ execute,
770
+ } satisfies Tool,
771
+ });
772
+ const onResult = vi.fn();
773
+ const tracker = new ToolInvocationTracker(getTools, {
774
+ onResult,
775
+ onStatusesChange: () => {},
776
+ });
777
+ tracker.setState(createState([], false));
778
+
779
+ const complete = (isRunning: boolean) =>
780
+ createState(
781
+ [
782
+ createAssistantMessage(
783
+ '{"path":"/tmp/a"}',
784
+ { path: "/tmp/a" },
785
+ { toolName: "deleteFile" },
786
+ ),
787
+ ],
788
+ isRunning,
789
+ );
790
+
791
+ tracker.setState(complete(true));
792
+ await new Promise((r) => setTimeout(r, 0));
793
+
794
+ await tracker.abort();
795
+
796
+ tracker.setState(complete(false));
797
+ await waitFor(() => expect(execute).toHaveBeenCalledTimes(1));
798
+ });
799
+
800
+ it("keeps a discarded tool call skipped once its args complete", async () => {
801
+ const execute = vi.fn(async () => ({ deleted: true }));
802
+ const getTools = () => ({
803
+ deleteFile: {
804
+ parameters: { type: "object", properties: {} },
805
+ execute,
806
+ } satisfies Tool,
807
+ });
808
+ const onResult = vi.fn();
809
+ const tracker = new ToolInvocationTracker(getTools, {
810
+ onResult,
811
+ onStatusesChange: () => {},
812
+ });
813
+ tracker.setState(createState([], false));
814
+
815
+ const live = (argsText: string, isRunning: boolean) =>
816
+ createState(
817
+ [
818
+ createAssistantMessage(
819
+ argsText,
820
+ { path: "/tmp/a" },
821
+ { toolName: "deleteFile" },
822
+ ),
823
+ ],
824
+ isRunning,
825
+ );
826
+
827
+ tracker.setState(live('{"path":"/tm', true));
828
+ await new Promise((r) => setTimeout(r, 0));
829
+
830
+ await tracker.abort({ discardPending: true });
831
+
832
+ tracker.setState(live('{"path":"/tmp/a"}', false));
833
+ await new Promise((r) => setTimeout(r, 0));
834
+
835
+ expect(execute).not.toHaveBeenCalled();
836
+ expect(onResult).not.toHaveBeenCalled();
837
+ });
838
+
839
+ it("keeps a discarded call skipped when the pipeline restarts before it settles", async () => {
840
+ const execute = vi.fn(async () => ({ deleted: true }));
841
+ const getTools = () => ({
842
+ deleteFile: {
843
+ parameters: { type: "object", properties: {} },
844
+ execute,
845
+ } satisfies Tool,
846
+ });
847
+ const onResult = vi.fn();
848
+ const tracker = new ToolInvocationTracker(getTools, {
849
+ onResult,
850
+ onStatusesChange: () => {},
851
+ });
852
+ tracker.setState(createState([], false));
853
+
854
+ const complete = (isRunning: boolean) =>
855
+ createState(
856
+ [
857
+ createAssistantMessage(
858
+ '{"path":"/tmp/a"}',
859
+ { path: "/tmp/a" },
860
+ { toolName: "deleteFile" },
861
+ ),
862
+ ],
863
+ isRunning,
864
+ );
865
+
866
+ tracker.setState(complete(true));
867
+ await new Promise((r) => setTimeout(r, 0));
868
+ await tracker.abort({ discardPending: true });
869
+
870
+ killPipeline(tracker);
871
+ tracker.setState(complete(false));
872
+ await new Promise((r) => setTimeout(r, 0));
873
+
874
+ expect(execute).not.toHaveBeenCalled();
875
+ expect(onResult).not.toHaveBeenCalled();
876
+ });
877
+
878
+ it("still executes a waiting call when the pipeline restarts before it settles", async () => {
879
+ const execute = vi.fn(async () => ({ deleted: true }));
880
+ const getTools = () => ({
881
+ deleteFile: {
882
+ parameters: { type: "object", properties: {} },
883
+ execute,
884
+ } satisfies Tool,
885
+ });
886
+ const onResult = vi.fn();
887
+ const tracker = new ToolInvocationTracker(getTools, {
888
+ onResult,
889
+ onStatusesChange: () => {},
890
+ });
891
+ tracker.setState(createState([], false));
892
+
893
+ const complete = (isRunning: boolean) =>
894
+ createState(
895
+ [
896
+ createAssistantMessage(
897
+ '{"path":"/tmp/a"}',
898
+ { path: "/tmp/a" },
899
+ { toolName: "deleteFile" },
900
+ ),
901
+ ],
902
+ isRunning,
903
+ );
904
+
905
+ tracker.setState(complete(true));
906
+ await new Promise((r) => setTimeout(r, 0));
907
+ expect(execute).not.toHaveBeenCalled();
908
+
909
+ killPipeline(tracker);
910
+ tracker.setState(complete(false));
911
+
912
+ await waitFor(() => expect(execute).toHaveBeenCalledTimes(1));
913
+ await waitFor(() => expect(onResult).toHaveBeenCalledTimes(1));
914
+ });
915
+
916
+ it("never executes a registered tool the provider gates before its run ends", async () => {
917
+ const execute = vi.fn(async () => ({ deleted: true }));
918
+ const getTools = () => ({
919
+ deleteFile: {
920
+ parameters: { type: "object", properties: {} },
921
+ execute,
922
+ } satisfies Tool,
923
+ });
924
+ const onResult = vi.fn();
925
+ const tracker = new ToolInvocationTracker(getTools, {
926
+ onResult,
927
+ onStatusesChange: () => {},
928
+ });
929
+ tracker.setState(createState([]));
930
+
931
+ tracker.setState(
932
+ createState(
933
+ [
934
+ createAssistantMessage(
935
+ '{"path":"/tmp/a"}',
936
+ { path: "/tmp/a" },
937
+ { toolName: "deleteFile" },
938
+ ),
939
+ ],
940
+ true,
941
+ ),
942
+ );
943
+ await new Promise((r) => setTimeout(r, 0));
944
+
945
+ tracker.setState(
946
+ createState(
947
+ [
948
+ createAssistantMessage(
949
+ '{"path":"/tmp/a"}',
950
+ { path: "/tmp/a" },
951
+ { toolName: "deleteFile", approval: { id: "approval-1" } },
952
+ ),
953
+ ],
954
+ false,
955
+ ),
956
+ );
957
+ await new Promise((r) => setTimeout(r, 0));
958
+
959
+ expect(execute).not.toHaveBeenCalled();
960
+ expect(onResult).not.toHaveBeenCalled();
961
+ });
962
+
963
+ it("never executes a tool call the adapter reports as provider-owned", async () => {
964
+ const execute = vi.fn(async () => ({ deleted: true }));
965
+ const getTools = () => ({
966
+ deleteFile: {
967
+ parameters: { type: "object", properties: {} },
968
+ execute,
969
+ } satisfies Tool,
970
+ });
971
+ const onResult = vi.fn();
972
+ const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
973
+
974
+ try {
975
+ const tracker = new ToolInvocationTracker(
976
+ getTools,
977
+ { onResult, onStatusesChange: () => {} },
978
+ () => false,
979
+ );
980
+ tracker.setState(createState([]));
981
+
982
+ tracker.setState(
983
+ createState(
984
+ [
985
+ createAssistantMessage(
986
+ '{"path":"/tmp/a"}',
987
+ { path: "/tmp/a" },
988
+ { toolName: "deleteFile" },
989
+ ),
990
+ ],
991
+ true,
992
+ ),
993
+ );
994
+ await new Promise((r) => setTimeout(r, 0));
995
+
996
+ expect(execute).not.toHaveBeenCalled();
997
+ expect(onResult).not.toHaveBeenCalled();
998
+ expect(warnSpy).toHaveBeenCalledTimes(1);
999
+
1000
+ tracker.setState(
1001
+ createState(
1002
+ [
1003
+ createAssistantMessage(
1004
+ '{"path":"/tmp/a"}',
1005
+ { path: "/tmp/a" },
1006
+ { toolName: "deleteFile", result: { server: "deleted" } },
1007
+ ),
1008
+ ],
1009
+ false,
1010
+ ),
1011
+ );
1012
+ await new Promise((r) => setTimeout(r, 0));
1013
+
1014
+ expect(execute).not.toHaveBeenCalled();
1015
+ expect(onResult).not.toHaveBeenCalled();
1016
+ } finally {
1017
+ warnSpy.mockRestore();
1018
+ }
1019
+ });
1020
+
1021
+ it("executes a tool call the adapter reports as client-owned", async () => {
1022
+ const execute = vi.fn(async () => ({ deleted: true }));
1023
+ const getTools = () => ({
1024
+ deleteFile: {
1025
+ parameters: { type: "object", properties: {} },
1026
+ execute,
1027
+ } satisfies Tool,
1028
+ });
1029
+ const onResult = vi.fn();
1030
+ const tracker = new ToolInvocationTracker(
1031
+ getTools,
1032
+ { onResult, onStatusesChange: () => {} },
1033
+ (toolCall) => toolCall.toolCallId === "tool-1",
1034
+ );
1035
+ tracker.setState(createState([]));
1036
+
1037
+ tracker.setState(
1038
+ createState(
1039
+ [
1040
+ createAssistantMessage(
1041
+ '{"path":"/tmp/a"}',
1042
+ { path: "/tmp/a" },
1043
+ { toolName: "deleteFile" },
1044
+ ),
1045
+ ],
1046
+ true,
1047
+ ),
1048
+ );
1049
+ await waitFor(() => expect(execute).toHaveBeenCalledTimes(1));
1050
+ await waitFor(() => expect(onResult).toHaveBeenCalledTimes(1));
1051
+ });
1052
+
1053
+ it("keeps the result of a call the adapter stops reporting as client-owned mid-execution", async () => {
1054
+ let resolveExecute!: (value: { deleted: boolean }) => void;
1055
+ const execute = vi.fn(
1056
+ () =>
1057
+ new Promise<{ deleted: boolean }>((r) => {
1058
+ resolveExecute = r;
1059
+ }),
1060
+ );
1061
+ const getTools = () => ({
1062
+ deleteFile: {
1063
+ parameters: { type: "object", properties: {} },
1064
+ execute,
1065
+ } satisfies Tool,
1066
+ });
1067
+ const onResult = vi.fn();
1068
+ let clientOwned = true;
1069
+ const tracker = new ToolInvocationTracker(
1070
+ getTools,
1071
+ { onResult, onStatusesChange: () => {} },
1072
+ () => clientOwned,
1073
+ );
1074
+ tracker.setState(createState([]));
1075
+
1076
+ const live = () =>
1077
+ createState(
1078
+ [
1079
+ createAssistantMessage(
1080
+ '{"path":"/tmp/a"}',
1081
+ { path: "/tmp/a" },
1082
+ { toolName: "deleteFile" },
1083
+ ),
1084
+ ],
1085
+ true,
1086
+ );
1087
+
1088
+ tracker.setState(live());
1089
+ await waitFor(() => expect(execute).toHaveBeenCalledTimes(1));
1090
+
1091
+ clientOwned = false;
1092
+ tracker.setState(live());
1093
+ resolveExecute({ deleted: true });
1094
+
1095
+ await waitFor(() => expect(onResult).toHaveBeenCalledTimes(1));
1096
+ });
1097
+
373
1098
  it("does not re-execute asynchronously loaded resolved tool calls after reset", async () => {
374
1099
  const execute = vi.fn(async () => ({ forecast: "ok" }));
375
1100
  const getTools = () => ({
@@ -385,12 +1110,13 @@ describe("ToolInvocationTracker", () => {
385
1110
  onResult,
386
1111
  onStatusesChange,
387
1112
  });
388
- tracker.setState(createState([]));
1113
+ tracker.setState(createState([], false));
389
1114
 
390
1115
  tracker.setState(
391
- createState([
392
- createAssistantMessage('{"query":"London"}', { query: "London" }),
393
- ]),
1116
+ createState(
1117
+ [createAssistantMessage('{"query":"London"}', { query: "London" })],
1118
+ false,
1119
+ ),
394
1120
  );
395
1121
 
396
1122
  await waitFor(() => {
@@ -401,16 +1127,19 @@ describe("ToolInvocationTracker", () => {
401
1127
 
402
1128
  await Promise.resolve();
403
1129
 
404
- tracker.setState(createState([]));
1130
+ tracker.setState(createState([], false));
405
1131
 
406
1132
  tracker.setState(
407
- createState([
408
- createAssistantMessage(
409
- '{"query":"London"}',
410
- { query: "London" },
411
- { result: { source: "history" } },
412
- ),
413
- ]),
1133
+ createState(
1134
+ [
1135
+ createAssistantMessage(
1136
+ '{"query":"London"}',
1137
+ { query: "London" },
1138
+ { result: { source: "history" } },
1139
+ ),
1140
+ ],
1141
+ false,
1142
+ ),
414
1143
  );
415
1144
 
416
1145
  await waitFor(() => {
@@ -448,20 +1177,23 @@ describe("ToolInvocationTracker", () => {
448
1177
  onResult,
449
1178
  onStatusesChange,
450
1179
  });
451
- tracker.setState(createState([]));
1180
+ tracker.setState(createState([], false));
452
1181
 
453
1182
  tracker.setState(
454
- createState([
455
- createAssistantMessage(
456
- '{"query":"parent"}',
457
- { query: "parent" },
458
- {
459
- result: { source: "history" },
460
- toolName: "resolvedOnly",
461
- nestedMessages: [nestedMessage],
462
- },
463
- ),
464
- ]),
1183
+ createState(
1184
+ [
1185
+ createAssistantMessage(
1186
+ '{"query":"parent"}',
1187
+ { query: "parent" },
1188
+ {
1189
+ result: { source: "history" },
1190
+ toolName: "resolvedOnly",
1191
+ nestedMessages: [nestedMessage],
1192
+ },
1193
+ ),
1194
+ ],
1195
+ false,
1196
+ ),
465
1197
  );
466
1198
 
467
1199
  await waitFor(() => {
@@ -607,15 +1339,18 @@ describe("ToolInvocationTracker", () => {
607
1339
  onResult,
608
1340
  onStatusesChange,
609
1341
  });
610
- tracker.setState(createState([]));
1342
+ tracker.setState(createState([], false));
611
1343
 
612
1344
  tracker.setState(
613
- createState([
614
- createAssistantMessage('{"a":1,"b":2}', {
615
- a: 1,
616
- b: 2,
617
- }),
618
- ]),
1345
+ createState(
1346
+ [
1347
+ createAssistantMessage('{"a":1,"b":2}', {
1348
+ a: 1,
1349
+ b: 2,
1350
+ }),
1351
+ ],
1352
+ false,
1353
+ ),
619
1354
  );
620
1355
 
621
1356
  await waitFor(() => {
@@ -623,18 +1358,21 @@ describe("ToolInvocationTracker", () => {
623
1358
  });
624
1359
 
625
1360
  tracker.setState(
626
- createState([
627
- createAssistantMessage(
628
- '{"b":2,"a":1}',
629
- {
630
- a: 1,
631
- b: 2,
632
- },
633
- {
634
- result: { source: "backend" },
635
- },
636
- ),
637
- ]),
1361
+ createState(
1362
+ [
1363
+ createAssistantMessage(
1364
+ '{"b":2,"a":1}',
1365
+ {
1366
+ a: 1,
1367
+ b: 2,
1368
+ },
1369
+ {
1370
+ result: { source: "backend" },
1371
+ },
1372
+ ),
1373
+ ],
1374
+ false,
1375
+ ),
638
1376
  );
639
1377
 
640
1378
  resolveExecute?.({ source: "client" });
@@ -1192,11 +1930,12 @@ describe("ToolInvocationTracker reset", () => {
1192
1930
  execute: vi.fn(() => new Promise(() => {})),
1193
1931
  } satisfies Tool,
1194
1932
  });
1195
- tracker.setState(createState([]));
1933
+ tracker.setState(createState([], false));
1196
1934
  tracker.setState(
1197
- createState([
1198
- createAssistantMessage('{"query":"London"}', { query: "London" }),
1199
- ]),
1935
+ createState(
1936
+ [createAssistantMessage('{"query":"London"}', { query: "London" })],
1937
+ false,
1938
+ ),
1200
1939
  );
1201
1940
  await waitFor(() => {
1202
1941
  expect(statuses()["tool-1"]?.type).toBe("executing");
@@ -1210,15 +1949,18 @@ describe("ToolInvocationTracker reset", () => {
1210
1949
  // discarded id back in through the whole-map republication. The first
1211
1950
  // post-reset snapshot is the fresh session's empty state, consuming the
1212
1951
  // pending-restore mark.
1213
- tracker.setState(createState([]));
1952
+ tracker.setState(createState([], false));
1214
1953
  tracker.setState(
1215
- createState([
1216
- createAssistantMessage(
1217
- '{"query":"Paris"}',
1218
- { query: "Paris" },
1219
- { toolCallId: "tool-2" },
1220
- ),
1221
- ]),
1954
+ createState(
1955
+ [
1956
+ createAssistantMessage(
1957
+ '{"query":"Paris"}',
1958
+ { query: "Paris" },
1959
+ { toolCallId: "tool-2" },
1960
+ ),
1961
+ ],
1962
+ false,
1963
+ ),
1222
1964
  );
1223
1965
  await waitFor(() => {
1224
1966
  expect(statuses()["tool-2"]?.type).toBe("executing");
@@ -1246,19 +1988,22 @@ describe("ToolInvocationTracker reset", () => {
1246
1988
  execute: vi.fn(() => new Promise(() => {})),
1247
1989
  } satisfies Tool,
1248
1990
  });
1249
- tracker.setState(createState([]));
1991
+ tracker.setState(createState([], false));
1250
1992
  tracker.setState(
1251
- createState([
1252
- createAssistantMessage('{"query":"London"}', { query: "London" }),
1253
- createAssistantMessage(
1254
- '{"query":"London"}',
1255
- { query: "London" },
1256
- {
1257
- toolCallId: "tool-2",
1258
- toolName: "pressureSearch",
1259
- },
1260
- ),
1261
- ]),
1993
+ createState(
1994
+ [
1995
+ createAssistantMessage('{"query":"London"}', { query: "London" }),
1996
+ createAssistantMessage(
1997
+ '{"query":"London"}',
1998
+ { query: "London" },
1999
+ {
2000
+ toolCallId: "tool-2",
2001
+ toolName: "pressureSearch",
2002
+ },
2003
+ ),
2004
+ ],
2005
+ false,
2006
+ ),
1262
2007
  );
1263
2008
  await waitFor(() => {
1264
2009
  expect(statuses()["tool-1"]?.type).toBe("executing");
@@ -1286,11 +2031,12 @@ describe("ToolInvocationTracker reset", () => {
1286
2031
  }),
1287
2032
  } satisfies Tool,
1288
2033
  });
1289
- tracker.setState(createState([]));
2034
+ tracker.setState(createState([], false));
1290
2035
  tracker.setState(
1291
- createState([
1292
- createAssistantMessage('{"query":"London"}', { query: "London" }),
1293
- ]),
2036
+ createState(
2037
+ [createAssistantMessage('{"query":"London"}', { query: "London" })],
2038
+ false,
2039
+ ),
1294
2040
  );
1295
2041
  await waitFor(() => {
1296
2042
  expect(statuses()["tool-1"]?.type).toBe("executing");
@@ -1317,11 +2063,12 @@ describe("ToolInvocationTracker reset", () => {
1317
2063
  } satisfies Tool,
1318
2064
  });
1319
2065
 
1320
- tracker.setState(createState([]));
2066
+ tracker.setState(createState([], false));
1321
2067
  tracker.setState(
1322
- createState([
1323
- createAssistantMessage('{"query":"London"}', { query: "London" }),
1324
- ]),
2068
+ createState(
2069
+ [createAssistantMessage('{"query":"London"}', { query: "London" })],
2070
+ false,
2071
+ ),
1325
2072
  );
1326
2073
  await waitFor(() => {
1327
2074
  expect(humanFns).toHaveLength(1);
@@ -1329,15 +2076,18 @@ describe("ToolInvocationTracker reset", () => {
1329
2076
  });
1330
2077
 
1331
2078
  tracker.reset();
1332
- tracker.setState(createState([]));
2079
+ tracker.setState(createState([], false));
1333
2080
  tracker.setState(
1334
- createState([
1335
- createAssistantMessage(
1336
- '{"query":"Paris"}',
1337
- { query: "Paris" },
1338
- { toolCallId: "tool-1" },
1339
- ),
1340
- ]),
2081
+ createState(
2082
+ [
2083
+ createAssistantMessage(
2084
+ '{"query":"Paris"}',
2085
+ { query: "Paris" },
2086
+ { toolCallId: "tool-1" },
2087
+ ),
2088
+ ],
2089
+ false,
2090
+ ),
1341
2091
  );
1342
2092
  await waitFor(() => expect(humanFns).toHaveLength(2));
1343
2093
 
@@ -1378,24 +2128,28 @@ describe("ToolInvocationTracker reset", () => {
1378
2128
  { onResult, onStatusesChange: vi.fn() },
1379
2129
  );
1380
2130
 
1381
- tracker.setState(createState([]));
2131
+ tracker.setState(createState([], false));
1382
2132
  tracker.setState(
1383
- createState([
1384
- createAssistantMessage('{"query":"London"}', { query: "London" }),
1385
- ]),
2133
+ createState(
2134
+ [createAssistantMessage('{"query":"London"}', { query: "London" })],
2135
+ false,
2136
+ ),
1386
2137
  );
1387
2138
  await waitFor(() => expect(executions).toHaveLength(1));
1388
2139
 
1389
2140
  tracker.reset();
1390
- tracker.setState(createState([]));
2141
+ tracker.setState(createState([], false));
1391
2142
  tracker.setState(
1392
- createState([
1393
- createAssistantMessage(
1394
- '{"query":"Paris"}',
1395
- { query: "Paris" },
1396
- { toolCallId: "tool-1" },
1397
- ),
1398
- ]),
2143
+ createState(
2144
+ [
2145
+ createAssistantMessage(
2146
+ '{"query":"Paris"}',
2147
+ { query: "Paris" },
2148
+ { toolCallId: "tool-1" },
2149
+ ),
2150
+ ],
2151
+ false,
2152
+ ),
1399
2153
  );
1400
2154
  await waitFor(() => expect(executions).toHaveLength(2));
1401
2155
  await new Promise((resolve) => setTimeout(resolve, 0));