@assistant-ui/core 0.3.14 → 0.3.16

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 (341) hide show
  1. package/dist/adapters/voice.js +11 -0
  2. package/dist/adapters/voice.js.map +1 -1
  3. package/dist/index.js +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 +1 -1
  7. package/dist/model-context/frame/host.d.ts.map +1 -1
  8. package/dist/model-context/frame/host.js +20 -5
  9. package/dist/model-context/frame/host.js.map +1 -1
  10. package/dist/model-context/frame/provider.d.ts +6 -0
  11. package/dist/model-context/frame/provider.d.ts.map +1 -1
  12. package/dist/model-context/frame/provider.js +126 -16
  13. package/dist/model-context/frame/provider.js.map +1 -1
  14. package/dist/model-context/frame/types.d.ts +4 -1
  15. package/dist/model-context/frame/types.d.ts.map +1 -1
  16. package/dist/model-context/frame/types.js.map +1 -1
  17. package/dist/model-context/interactable-composer-metadata.d.ts +1 -1
  18. package/dist/model-context/interactable-composer-metadata.js +1 -1
  19. package/dist/model-context/interactable-composer-metadata.js.map +1 -1
  20. package/dist/model-context/registry.d.ts +2 -2
  21. package/dist/model-context/registry.d.ts.map +1 -1
  22. package/dist/model-context/registry.js +9 -28
  23. package/dist/model-context/registry.js.map +1 -1
  24. package/dist/react/client/InMemoryThreadList.d.ts.map +1 -1
  25. package/dist/react/client/InMemoryThreadList.js +24 -20
  26. package/dist/react/client/InMemoryThreadList.js.map +1 -1
  27. package/dist/react/client/Interactables.d.ts.map +1 -1
  28. package/dist/react/client/Interactables.js +59 -177
  29. package/dist/react/client/Interactables.js.map +1 -1
  30. package/dist/react/client/RemoteThreadList.d.ts +1 -1
  31. package/dist/react/client/RemoteThreadList.d.ts.map +1 -1
  32. package/dist/react/client/RemoteThreadList.js +117 -78
  33. package/dist/react/client/RemoteThreadList.js.map +1 -1
  34. package/dist/react/index.d.ts +2 -1
  35. package/dist/react/interactables-legacy/Interactables.d.ts.map +1 -1
  36. package/dist/react/interactables-legacy/Interactables.js +56 -174
  37. package/dist/react/interactables-legacy/Interactables.js.map +1 -1
  38. package/dist/react/interactables-shared/useInteractablePersistenceQueue.d.ts +24 -0
  39. package/dist/react/interactables-shared/useInteractablePersistenceQueue.d.ts.map +1 -0
  40. package/dist/react/interactables-shared/useInteractablePersistenceQueue.js +138 -0
  41. package/dist/react/interactables-shared/useInteractablePersistenceQueue.js.map +1 -0
  42. package/dist/react/primitive-hooks/useActionBarCopy.js +7 -1
  43. package/dist/react/primitive-hooks/useActionBarCopy.js.map +1 -1
  44. package/dist/react/primitive-hooks/useThreadIsEmpty.d.ts +3 -0
  45. package/dist/react/primitive-hooks/useThreadIsEmpty.d.ts.map +1 -1
  46. package/dist/react/primitive-hooks/useThreadIsEmpty.js +3 -0
  47. package/dist/react/primitive-hooks/useThreadIsEmpty.js.map +1 -1
  48. package/dist/react/primitive-hooks/useThreadIsRunning.d.ts +3 -0
  49. package/dist/react/primitive-hooks/useThreadIsRunning.d.ts.map +1 -1
  50. package/dist/react/primitive-hooks/useThreadIsRunning.js +3 -0
  51. package/dist/react/primitive-hooks/useThreadIsRunning.js.map +1 -1
  52. package/dist/react/primitives/composer/ComposerIf.js.map +1 -1
  53. package/dist/react/primitives/composer/ComposerQueue.d.ts +2 -1
  54. package/dist/react/primitives/composer/ComposerQueue.d.ts.map +1 -1
  55. package/dist/react/primitives/message/MessageParts.js.map +1 -1
  56. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.d.ts +3 -1
  57. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.d.ts.map +1 -1
  58. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js +6 -0
  59. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js.map +1 -1
  60. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts +7 -1
  61. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts.map +1 -1
  62. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js +174 -64
  63. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js.map +1 -1
  64. package/dist/react/runtimes/RemoteThreadResource.d.ts.map +1 -1
  65. package/dist/react/runtimes/RemoteThreadResource.js +2 -0
  66. package/dist/react/runtimes/RemoteThreadResource.js.map +1 -1
  67. package/dist/react/runtimes/assistant-transport/runManager.d.ts.map +1 -1
  68. package/dist/react/runtimes/assistant-transport/runManager.js +2 -12
  69. package/dist/react/runtimes/assistant-transport/runManager.js.map +1 -1
  70. package/dist/react/runtimes/assistant-transport/useAssistantTransportRuntime.d.ts.map +1 -1
  71. package/dist/react/runtimes/assistant-transport/useAssistantTransportRuntime.js +1 -1
  72. package/dist/react/runtimes/assistant-transport/useAssistantTransportRuntime.js.map +1 -1
  73. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.d.ts +3 -10
  74. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.d.ts.map +1 -1
  75. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js +20 -67
  76. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js.map +1 -1
  77. package/dist/react/runtimes/cloud/createCloudThreadListAdapterCreateFallback.d.ts +12 -0
  78. package/dist/react/runtimes/cloud/createCloudThreadListAdapterCreateFallback.d.ts.map +1 -0
  79. package/dist/react/runtimes/cloud/createCloudThreadListAdapterCreateFallback.js +10 -0
  80. package/dist/react/runtimes/cloud/createCloudThreadListAdapterCreateFallback.js.map +1 -0
  81. package/dist/react/runtimes/cloud/useCloudThreadListAdapter.d.ts.map +1 -1
  82. package/dist/react/runtimes/cloud/useCloudThreadListAdapter.js +38 -5
  83. package/dist/react/runtimes/cloud/useCloudThreadListAdapter.js.map +1 -1
  84. package/dist/react/runtimes/cloud/useCloudThreadListRuntime.js.map +1 -1
  85. package/dist/react/runtimes/createMessageConverter.d.ts +2 -1
  86. package/dist/react/runtimes/createMessageConverter.d.ts.map +1 -1
  87. package/dist/react/runtimes/external-message-converter.d.ts +6 -25
  88. package/dist/react/runtimes/external-message-converter.d.ts.map +1 -1
  89. package/dist/react/runtimes/external-message-converter.js +33 -233
  90. package/dist/react/runtimes/external-message-converter.js.map +1 -1
  91. package/dist/runtime/api/composer-runtime.d.ts +1 -1
  92. package/dist/runtime/api/composer-runtime.js.map +1 -1
  93. package/dist/runtime/api/thread-runtime.d.ts +1 -2
  94. package/dist/runtime/api/thread-runtime.d.ts.map +1 -1
  95. package/dist/runtime/base/base-composer-runtime-core.d.ts +1 -1
  96. package/dist/runtime/base/base-composer-runtime-core.d.ts.map +1 -1
  97. package/dist/runtime/base/base-composer-runtime-core.js +14 -29
  98. package/dist/runtime/base/base-composer-runtime-core.js.map +1 -1
  99. package/dist/runtime/base/base-thread-runtime-core.d.ts +2 -4
  100. package/dist/runtime/base/base-thread-runtime-core.d.ts.map +1 -1
  101. package/dist/runtime/base/base-thread-runtime-core.js +3 -9
  102. package/dist/runtime/base/base-thread-runtime-core.js.map +1 -1
  103. package/dist/runtime/base/default-thread-composer-runtime-core.d.ts +1 -1
  104. package/dist/runtime/base/default-thread-composer-runtime-core.d.ts.map +1 -1
  105. package/dist/runtime/base/default-thread-composer-runtime-core.js +1 -1
  106. package/dist/runtime/base/default-thread-composer-runtime-core.js.map +1 -1
  107. package/dist/runtime/interfaces/composer-runtime-core.d.ts +1 -1
  108. package/dist/runtime/interfaces/thread-runtime-core.d.ts +1 -1
  109. package/dist/runtime/internal.d.ts +2 -1
  110. package/dist/runtime/internal.js +2 -1
  111. package/dist/runtime/queue/external-thread-queue-adapter.d.ts +1 -1
  112. package/dist/runtime/queue/message-queue.d.ts.map +1 -1
  113. package/dist/runtime/queue/message-queue.js +1 -1
  114. package/dist/runtime/queue/message-queue.js.map +1 -1
  115. package/dist/runtime/queue/queue-item.d.ts +12 -0
  116. package/dist/runtime/queue/queue-item.d.ts.map +1 -0
  117. package/dist/runtime/queue/queue-item.js +6 -0
  118. package/dist/runtime/queue/queue-item.js.map +1 -0
  119. package/dist/runtime/utils/abortable-thread-load.d.ts +15 -0
  120. package/dist/runtime/utils/abortable-thread-load.d.ts.map +1 -0
  121. package/dist/runtime/utils/abortable-thread-load.js +35 -0
  122. package/dist/runtime/utils/abortable-thread-load.js.map +1 -0
  123. package/dist/runtime/utils/attachment-add-operations.d.ts +18 -0
  124. package/dist/runtime/utils/attachment-add-operations.d.ts.map +1 -0
  125. package/dist/runtime/utils/attachment-add-operations.js +43 -0
  126. package/dist/runtime/utils/attachment-add-operations.js.map +1 -0
  127. package/dist/runtime/utils/auto-status.d.ts +6 -1
  128. package/dist/runtime/utils/auto-status.d.ts.map +1 -1
  129. package/dist/runtime/utils/auto-status.js +7 -1
  130. package/dist/runtime/utils/auto-status.js.map +1 -1
  131. package/dist/runtime/utils/external-message-conversion.d.ts +47 -0
  132. package/dist/runtime/utils/external-message-conversion.d.ts.map +1 -0
  133. package/dist/runtime/utils/external-message-conversion.js +221 -0
  134. package/dist/runtime/utils/external-message-conversion.js.map +1 -0
  135. package/dist/runtime/utils/external-store-message.d.ts +2 -1
  136. package/dist/runtime/utils/external-store-message.d.ts.map +1 -1
  137. package/dist/runtime/utils/external-store-message.js +2 -1
  138. package/dist/runtime/utils/external-store-message.js.map +1 -1
  139. package/dist/runtime/utils/message-repository-session.d.ts +30 -0
  140. package/dist/runtime/utils/message-repository-session.d.ts.map +1 -0
  141. package/dist/runtime/utils/message-repository-session.js +130 -0
  142. package/dist/runtime/utils/message-repository-session.js.map +1 -0
  143. package/dist/runtime/utils/message-repository.d.ts.map +1 -1
  144. package/dist/runtime/utils/message-repository.js +30 -22
  145. package/dist/runtime/utils/message-repository.js.map +1 -1
  146. package/dist/runtime/utils/pending-tool-calls.d.ts +20 -0
  147. package/dist/runtime/utils/pending-tool-calls.d.ts.map +1 -0
  148. package/dist/runtime/utils/pending-tool-calls.js +22 -0
  149. package/dist/runtime/utils/pending-tool-calls.js.map +1 -0
  150. package/dist/runtime/utils/thread-message-like.d.ts +2 -1
  151. package/dist/runtime/utils/thread-message-like.d.ts.map +1 -1
  152. package/dist/runtime/utils/thread-message-like.js.map +1 -1
  153. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.d.ts +2 -5
  154. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.d.ts.map +1 -1
  155. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.js +3 -10
  156. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.js.map +1 -1
  157. package/dist/runtimes/external-store/external-store-thread-runtime-core.d.ts +19 -1
  158. package/dist/runtimes/external-store/external-store-thread-runtime-core.d.ts.map +1 -1
  159. package/dist/runtimes/external-store/external-store-thread-runtime-core.js +124 -20
  160. package/dist/runtimes/external-store/external-store-thread-runtime-core.js.map +1 -1
  161. package/dist/runtimes/inert/InertThreadRuntimeCore.d.ts +102 -0
  162. package/dist/runtimes/inert/InertThreadRuntimeCore.d.ts.map +1 -0
  163. package/dist/runtimes/inert/InertThreadRuntimeCore.js +156 -0
  164. package/dist/runtimes/inert/InertThreadRuntimeCore.js.map +1 -0
  165. package/dist/runtimes/local/local-thread-runtime-core.d.ts +3 -1
  166. package/dist/runtimes/local/local-thread-runtime-core.d.ts.map +1 -1
  167. package/dist/runtimes/local/local-thread-runtime-core.js +45 -8
  168. package/dist/runtimes/local/local-thread-runtime-core.js.map +1 -1
  169. package/dist/runtimes/readonly/ReadonlyThreadRuntimeCore.d.ts +12 -60
  170. package/dist/runtimes/readonly/ReadonlyThreadRuntimeCore.d.ts.map +1 -1
  171. package/dist/runtimes/readonly/ReadonlyThreadRuntimeCore.js +10 -145
  172. package/dist/runtimes/readonly/ReadonlyThreadRuntimeCore.js.map +1 -1
  173. package/dist/runtimes/remote-thread-list/adapter-changed.d.ts +9 -0
  174. package/dist/runtimes/remote-thread-list/adapter-changed.d.ts.map +1 -0
  175. package/dist/runtimes/remote-thread-list/adapter-changed.js +13 -0
  176. package/dist/runtimes/remote-thread-list/adapter-changed.js.map +1 -0
  177. package/dist/runtimes/remote-thread-list/empty-thread-core.d.ts.map +1 -1
  178. package/dist/runtimes/remote-thread-list/empty-thread-core.js +16 -155
  179. package/dist/runtimes/remote-thread-list/empty-thread-core.js.map +1 -1
  180. package/dist/runtimes/remote-thread-list/optimistic-state.d.ts +2 -0
  181. package/dist/runtimes/remote-thread-list/optimistic-state.d.ts.map +1 -1
  182. package/dist/runtimes/remote-thread-list/optimistic-state.js +11 -0
  183. package/dist/runtimes/remote-thread-list/optimistic-state.js.map +1 -1
  184. package/dist/runtimes/remote-thread-list/remote-thread-state.d.ts +12 -1
  185. package/dist/runtimes/remote-thread-list/remote-thread-state.d.ts.map +1 -1
  186. package/dist/runtimes/remote-thread-list/remote-thread-state.js +77 -1
  187. package/dist/runtimes/remote-thread-list/remote-thread-state.js.map +1 -1
  188. package/dist/runtimes/remote-thread-list/types.d.ts +3 -0
  189. package/dist/runtimes/remote-thread-list/types.d.ts.map +1 -1
  190. package/dist/runtimes/tool-invocations/ToolInvocationTracker.d.ts.map +1 -1
  191. package/dist/runtimes/tool-invocations/ToolInvocationTracker.js +2 -0
  192. package/dist/runtimes/tool-invocations/ToolInvocationTracker.js.map +1 -1
  193. package/dist/store/clients/external-thread.d.ts.map +1 -1
  194. package/dist/store/clients/external-thread.js +75 -100
  195. package/dist/store/clients/external-thread.js.map +1 -1
  196. package/dist/store/clients/single-thread-list.d.ts.map +1 -1
  197. package/dist/store/clients/single-thread-list.js +1 -1
  198. package/dist/store/clients/single-thread-list.js.map +1 -1
  199. package/dist/store/clients/suggestions.d.ts.map +1 -1
  200. package/dist/store/clients/suggestions.js +16 -7
  201. package/dist/store/clients/suggestions.js.map +1 -1
  202. package/dist/store/index.d.ts +4 -2
  203. package/dist/store/runtime-clients/handle-runtime-action.d.ts.map +1 -1
  204. package/dist/store/runtime-clients/handle-runtime-action.js +2 -0
  205. package/dist/store/runtime-clients/handle-runtime-action.js.map +1 -1
  206. package/dist/store/scope-registration.d.ts +1 -1
  207. package/dist/store/scopes/composer.d.ts +2 -1
  208. package/dist/store/scopes/composer.d.ts.map +1 -1
  209. package/dist/store/scopes/message.d.ts +1 -1
  210. package/dist/store/scopes/queue-item.d.ts +2 -9
  211. package/dist/store/scopes/queue-item.d.ts.map +1 -1
  212. package/dist/store/scopes/queue-item.js +0 -6
  213. package/dist/types/message.d.ts +8 -4
  214. package/dist/types/message.d.ts.map +1 -1
  215. package/dist/types/message.js.map +1 -1
  216. package/dist/utils/data-url.d.ts +14 -1
  217. package/dist/utils/data-url.d.ts.map +1 -1
  218. package/dist/utils/data-url.js +13 -1
  219. package/dist/utils/data-url.js.map +1 -1
  220. package/dist/utils/invoke-user-callback.d.ts +5 -0
  221. package/dist/utils/invoke-user-callback.d.ts.map +1 -0
  222. package/dist/utils/invoke-user-callback.js +19 -0
  223. package/dist/utils/invoke-user-callback.js.map +1 -0
  224. package/dist/utils/normalizePartStatus.js.map +1 -1
  225. package/dist/utils/silent-runtime-action.d.ts +6 -0
  226. package/dist/utils/silent-runtime-action.d.ts.map +1 -0
  227. package/dist/utils/silent-runtime-action.js +7 -0
  228. package/dist/utils/silent-runtime-action.js.map +1 -0
  229. package/package.json +10 -10
  230. package/src/adapters/voice.test.ts +66 -1
  231. package/src/adapters/voice.ts +8 -0
  232. package/src/internal.ts +13 -0
  233. package/src/model-context/frame/host.test.ts +152 -9
  234. package/src/model-context/frame/host.ts +23 -5
  235. package/src/model-context/frame/provider.test.ts +601 -19
  236. package/src/model-context/frame/provider.ts +158 -20
  237. package/src/model-context/frame/types.ts +5 -0
  238. package/src/model-context/interactable-composer-metadata.ts +1 -1
  239. package/src/model-context/registry.test.ts +44 -0
  240. package/src/model-context/registry.ts +13 -35
  241. package/src/react/client/InMemoryThreadList.test.tsx +49 -0
  242. package/src/react/client/InMemoryThreadList.ts +7 -1
  243. package/src/react/client/Interactables.ts +19 -178
  244. package/src/react/client/RemoteThreadList.test.ts +292 -0
  245. package/src/react/client/RemoteThreadList.ts +119 -82
  246. package/src/react/interactables-legacy/Interactables.ts +20 -177
  247. package/src/react/interactables-shared/useInteractablePersistenceQueue.test.tsx +161 -0
  248. package/src/react/interactables-shared/useInteractablePersistenceQueue.ts +206 -0
  249. package/src/react/primitive-hooks/useActionBarCopy.test.ts +15 -0
  250. package/src/react/primitive-hooks/useActionBarCopy.ts +13 -3
  251. package/src/react/primitive-hooks/useThreadIsEmpty.ts +3 -0
  252. package/src/react/primitive-hooks/useThreadIsRunning.ts +3 -0
  253. package/src/react/runtimes/RemoteThreadListHookInstanceManager.tsx +12 -0
  254. package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.republish.test.ts +87 -0
  255. package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.tsx +251 -76
  256. package/src/react/runtimes/RemoteThreadResource.ts +2 -0
  257. package/src/react/runtimes/assistant-transport/runManager.ts +4 -17
  258. package/src/react/runtimes/assistant-transport/useAssistantTransportRuntime.test.tsx +58 -0
  259. package/src/react/runtimes/assistant-transport/useAssistantTransportRuntime.ts +3 -1
  260. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.test.tsx +92 -0
  261. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.ts +40 -141
  262. package/src/react/runtimes/cloud/createCloudThreadListAdapterCreateFallback.test.ts +37 -0
  263. package/src/react/runtimes/cloud/createCloudThreadListAdapterCreateFallback.ts +18 -0
  264. package/src/react/runtimes/cloud/useCloudThreadListAdapter.test.tsx +98 -3
  265. package/src/react/runtimes/cloud/useCloudThreadListAdapter.tsx +68 -6
  266. package/src/react/runtimes/external-message-converter.test.ts +0 -14
  267. package/src/react/runtimes/external-message-converter.test.tsx +77 -0
  268. package/src/react/runtimes/external-message-converter.ts +58 -481
  269. package/src/react/runtimes/useLocalRuntime.test.tsx +5 -5
  270. package/src/runtime/api/composer-runtime.ts +1 -1
  271. package/src/runtime/api/message-runtime.test.ts +28 -1
  272. package/src/runtime/base/base-composer-runtime-core.ts +20 -40
  273. package/src/runtime/base/base-thread-runtime-core.test.ts +16 -0
  274. package/src/runtime/base/base-thread-runtime-core.ts +6 -11
  275. package/src/runtime/base/default-thread-composer-runtime-core.ts +1 -4
  276. package/src/runtime/interfaces/composer-runtime-core.ts +1 -1
  277. package/src/runtime/interfaces/thread-runtime-core.ts +1 -1
  278. package/src/runtime/internal.ts +5 -0
  279. package/src/runtime/queue/external-thread-queue-adapter.ts +1 -1
  280. package/src/runtime/queue/message-queue.ts +1 -4
  281. package/src/runtime/queue/queue-item.ts +10 -0
  282. package/src/runtime/utils/abortable-thread-load.test.ts +87 -0
  283. package/src/runtime/utils/abortable-thread-load.ts +53 -0
  284. package/src/runtime/utils/attachment-add-operations.ts +59 -0
  285. package/src/runtime/utils/auto-status.ts +33 -0
  286. package/src/runtime/utils/external-message-conversion.test.ts +113 -0
  287. package/src/runtime/utils/external-message-conversion.ts +494 -0
  288. package/src/runtime/utils/external-store-message.ts +2 -0
  289. package/src/runtime/utils/message-repository-session.test.ts +195 -0
  290. package/src/runtime/utils/message-repository-session.ts +176 -0
  291. package/src/runtime/utils/message-repository.ts +33 -22
  292. package/src/runtime/utils/pending-tool-calls.test.ts +57 -0
  293. package/src/runtime/utils/pending-tool-calls.ts +35 -0
  294. package/src/runtime/utils/thread-message-like.ts +2 -0
  295. package/src/runtimes/external-store/external-store-thread-list-runtime-core.ts +6 -14
  296. package/src/runtimes/external-store/external-store-thread-runtime-core.ts +179 -28
  297. package/src/runtimes/inert/InertThreadRuntimeCore.test.ts +253 -0
  298. package/src/runtimes/inert/InertThreadRuntimeCore.ts +232 -0
  299. package/src/runtimes/local/local-thread-runtime-core.test.ts +491 -1
  300. package/src/runtimes/local/local-thread-runtime-core.ts +86 -11
  301. package/src/runtimes/readonly/ReadonlyThreadRuntimeCore.ts +17 -209
  302. package/src/runtimes/remote-thread-list/adapter-changed.ts +10 -0
  303. package/src/runtimes/remote-thread-list/empty-thread-core.ts +28 -221
  304. package/src/runtimes/remote-thread-list/optimistic-state.test.ts +18 -0
  305. package/src/runtimes/remote-thread-list/optimistic-state.ts +13 -0
  306. package/src/runtimes/remote-thread-list/remote-thread-state.test.ts +37 -0
  307. package/src/runtimes/remote-thread-list/remote-thread-state.ts +123 -0
  308. package/src/runtimes/remote-thread-list/types.ts +4 -0
  309. package/src/runtimes/tool-invocations/EDGE_CASES.md +17 -0
  310. package/src/runtimes/tool-invocations/ToolInvocationTracker.test.ts +140 -0
  311. package/src/runtimes/tool-invocations/ToolInvocationTracker.ts +3 -0
  312. package/src/store/clients/external-thread.ts +17 -62
  313. package/src/store/clients/single-thread-list.ts +4 -1
  314. package/src/store/clients/suggestions.test.ts +31 -0
  315. package/src/store/clients/suggestions.ts +18 -19
  316. package/src/store/runtime-clients/handle-runtime-action.test.ts +40 -0
  317. package/src/store/runtime-clients/handle-runtime-action.ts +3 -0
  318. package/src/store/scopes/message.ts +1 -1
  319. package/src/store/scopes/queue-item.ts +2 -9
  320. package/src/tests/MessageRepository.test.ts +125 -0
  321. package/src/tests/RemoteThreadListThreadListRuntimeCore-errors.test.ts +2 -2
  322. package/src/tests/RemoteThreadListThreadListRuntimeCore-load-race.test.ts +194 -0
  323. package/src/tests/RemoteThreadListThreadListRuntimeCore-loadMore.test.ts +8 -6
  324. package/src/tests/RemoteThreadListThreadListRuntimeCore-title-race.test.ts +158 -0
  325. package/src/tests/empty-thread-core.test.ts +6 -0
  326. package/src/tests/external-store-thread-runtime-core-adapter.test.ts +184 -0
  327. package/src/tests/external-store-thread-runtime-core.test.ts +642 -0
  328. package/src/tests/external-thread-attachments.test.tsx +173 -0
  329. package/src/tests/external-thread-isEmpty.test.tsx +100 -0
  330. package/src/tests/remote-thread-list-adapter-switch.test.ts +383 -0
  331. package/src/tests/single-thread-list-item.test.tsx +47 -0
  332. package/src/tests/thread-message-like.test.ts +39 -1
  333. package/src/tests/useRemoteThreadListRuntime-controlled.test.tsx +67 -0
  334. package/src/types/message.ts +9 -4
  335. package/src/utils/data-url.test.ts +52 -1
  336. package/src/utils/data-url.ts +21 -0
  337. package/src/utils/invoke-user-callback.test.ts +82 -0
  338. package/src/utils/invoke-user-callback.ts +31 -0
  339. package/src/utils/normalizePartStatus.ts +1 -1
  340. package/src/utils/silent-runtime-action.ts +6 -0
  341. package/dist/store/scopes/queue-item.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"ToolInvocationTracker.js","names":["process","env","NODE_ENV","createAssistantStreamController","ToolCallStreamController","ToolResponse","unstable_toolResultStream","Tool","ToolModelContentPart","ToolResultStreamOptions","AssistantMetaTransformStream","ReadonlyJSONValue","isJSONValueEqual","ThreadMessage","TOOL_EXECUTION_ID","Symbol","for","ToolExecutionStatus","type","payload","AddToolResultCommand","toolCallId","toolName","result","isError","artifact","modelContent","ToolCallEntry","argsText","hasResult","executionId","skipExecute","controller","argsComplete","SettledResolver","executionIds","ReadonlySet","resolve","isArgsTextComplete","JSON","parse","parseArgsText","undefined","isEquivalentCompleteArgsText","previous","next","previousValue","nextValue","getToolExecutionId","value","Record","PropertyKey","ToolInvocationTracker","_getTools","_callbacks","Callbacks","_entries","Map","_humanInput","reject","reason","_executing","Set","_settledResolvers","_statuses","_ac","AbortController","_pendingRestore","_lastSnapshot","Snapshot","_isRunning","_controller","ReturnType","_pipelineDead","_pipelineRestartUsed","constructor","getTools","callbacks","_initPipeline","stream","human","_onHumanInput","transform","_getWrappedTools","signal","onExecutionStart","id","_name","_onExecutionStart","onExecutionEnd","_onExecutionEnd","pipeThrough","pipeTo","WritableStream","write","chunk","_handleResultChunk","err","console","error","catch","setState","snapshot","_demoteEntriesToRestored","clear","messages","isRunning","isLoading","restoreFromLoading","previousIsRunning","_processMessages","reset","abort","size","_invokeOnStatusesChange","Promise","forEach","Error","push","resume","handlers","get","delete","_setStatus","getStatuses","ReadonlyMap","tools","Object","fromEntries","entries","map","name","tool","execute","streamCall","wrappedTool","args","context","Parameters","entry","_captureExecution","reader","set","add","_deleteStatus","pending","some","has","length","meta","_invokeOnResult","command","onResult","onStatusesChange","status","_hasExecutableTool","_shouldCloseArgsStream","_startActiveEntry","toolCallController","addToolCallPart","_processArgsText","content","shouldWriteArgsText","warn","startsWith","shouldClose","close","delta","slice","append","isRestore","message","Array","isArray","existing","signatureChanged","activeController","setResponse","ExecutionStatus","statuses"],"sources":["../../../src/runtimes/tool-invocations/ToolInvocationTracker.ts"],"sourcesContent":["declare const process: { env: { NODE_ENV?: string } };\n\nimport {\n createAssistantStreamController,\n type ToolCallStreamController,\n ToolResponse,\n unstable_toolResultStream,\n type Tool,\n type ToolModelContentPart,\n type ToolResultStreamOptions,\n} from \"assistant-stream\";\nimport {\n AssistantMetaTransformStream,\n type ReadonlyJSONValue,\n} from \"assistant-stream/utils\";\nimport { isJSONValueEqual } from \"../../utils/json/is-json-equal\";\nimport type { ThreadMessage } from \"../../types/message\";\n\nconst TOOL_EXECUTION_ID = Symbol.for(\"assistant-stream.tool-execution-id\");\n\n/**\n * Streaming execution state for a frontend tool.\n */\nexport type ToolExecutionStatus =\n | { type: \"executing\" }\n | {\n type: \"interrupt\";\n payload: { type: \"human\"; payload: unknown };\n };\n\nexport type AddToolResultCommand = {\n readonly type: \"add-tool-result\";\n readonly toolCallId: string;\n readonly toolName: string;\n readonly result: ReadonlyJSONValue;\n readonly isError: boolean;\n readonly artifact?: ReadonlyJSONValue;\n readonly modelContent?: readonly ToolModelContentPart[];\n};\n\ntype ToolCallEntry = {\n toolName: string;\n argsText: string;\n hasResult: boolean;\n executionId?: symbol;\n skipExecute?: boolean;\n} & (\n | {\n /** Restored phase — observed during a history-load snapshot. */\n controller?: undefined;\n argsComplete?: undefined;\n }\n | {\n /** Active phase — chunks are flowing through `controller`. */\n controller: ToolCallStreamController;\n argsComplete: boolean;\n }\n);\n\ntype SettledResolver = {\n executionIds: ReadonlySet<symbol>;\n resolve: () => void;\n};\n\nconst isArgsTextComplete = (argsText: string) => {\n try {\n JSON.parse(argsText);\n return true;\n } catch {\n return false;\n }\n};\n\nconst parseArgsText = (argsText: string) => {\n try {\n return JSON.parse(argsText);\n } catch {\n return undefined;\n }\n};\n\nconst isEquivalentCompleteArgsText = (previous: string, next: string) => {\n const previousValue = parseArgsText(previous);\n const nextValue = parseArgsText(next);\n if (previousValue === undefined || nextValue === undefined) return false;\n return isJSONValueEqual(previousValue, nextValue);\n};\n\nconst getToolExecutionId = (value: object): symbol | undefined =>\n (value as Record<PropertyKey, unknown>)[TOOL_EXECUTION_ID] as\n | symbol\n | undefined;\n\n/**\n * Plain-class port of the former `useToolInvocations` React hook. Owns the\n * assistant-stream pipeline that drives client-side `streamCall` / `execute`\n * for tool-call parts surfaced by a thread runtime, plus the per-tool-call\n * status map that consumers render against.\n *\n * **Contract**: `streamCall` (and `execute`) fires exactly once per logical\n * `toolCallId`. Args mutations after first completion, result replacement,\n * and result clearing are *not* surfaced through additional `streamCall`\n * invocations — by design — so hosts cannot observe spurious re-fires of\n * side effects. The follow-up `reader.events()` API will expose those\n * post-completion transitions to consumers that opt in.\n *\n * State-transition safety: every public method that observes runtime state\n * (`setState`, `reset`, `abort`, `resume`) wraps its work in try/catch and\n * logs to `console.error` rather than throwing. The tracker is built into\n * the hot message-processing path, so a malformed snapshot must never crash\n * the host runtime. See ./EDGE_CASES.md for the known non-trivial state\n * transitions and what each does today.\n *\n * @deprecated Internal — for framework bindings; not a public API. May change without notice.\n */\nexport class ToolInvocationTracker {\n private readonly _getTools: () => Record<string, Tool> | undefined;\n private readonly _callbacks: ToolInvocationTracker.Callbacks;\n\n private readonly _entries = new Map<string, ToolCallEntry>();\n private readonly _humanInput = new Map<\n string,\n {\n executionId: symbol;\n resolve: (payload: unknown) => void;\n reject: (reason: unknown) => void;\n }\n >();\n private readonly _executing = new Set<symbol>();\n private readonly _settledResolvers: SettledResolver[] = [];\n\n private _statuses = new Map<string, ToolExecutionStatus>();\n\n private _ac: AbortController = new AbortController();\n private _pendingRestore = true;\n\n /** Cached last snapshot, used to skip processing on identical re-renders. */\n private _lastSnapshot: ToolInvocationTracker.Snapshot | null = null;\n private _isRunning = false;\n\n private _controller!: ReturnType<typeof createAssistantStreamController>[1];\n\n /**\n * Set when the assistant-stream pipeline has died (errored out via\n * `.pipeTo(...).catch(...)`). The next `setState` re-initializes the\n * pipeline and demotes all active entries to restored so they survive\n * across the restart without re-firing `streamCall` (preserves the\n * \"exactly once\" contract). Capped at a single auto-restart per session\n * — repeated failures keep the tracker dead with a more visible error.\n */\n private _pipelineDead = false;\n private _pipelineRestartUsed = false;\n\n constructor(\n getTools: () => Record<string, Tool> | undefined,\n callbacks: ToolInvocationTracker.Callbacks,\n ) {\n this._getTools = getTools;\n this._callbacks = callbacks;\n\n this._initPipeline();\n }\n\n /**\n * Build the assistant-stream pipeline. Called once from the constructor\n * and at most once again if `_pipelineDead` is set (see F.4 in\n * EDGE_CASES.md).\n */\n private _initPipeline(): void {\n const [stream, controller] = createAssistantStreamController();\n this._controller = controller;\n\n const human = (\n toolCallId: string,\n payload: unknown,\n executionId?: symbol,\n ) => this._onHumanInput(toolCallId, payload, executionId);\n const transform = unstable_toolResultStream(\n () => this._getWrappedTools(),\n () => this._ac.signal,\n human,\n {\n onExecutionStart: (id: string, _name: string, executionId?: symbol) =>\n this._onExecutionStart(id, executionId),\n onExecutionEnd: (id: string, _name: string, executionId?: symbol) =>\n this._onExecutionEnd(id, executionId),\n } as ToolResultStreamOptions,\n );\n\n stream\n .pipeThrough(transform)\n .pipeThrough(new AssistantMetaTransformStream())\n .pipeTo(\n new WritableStream({\n write: (chunk) => {\n try {\n if (chunk.type !== \"result\") return;\n this._handleResultChunk(chunk);\n } catch (err) {\n console.error(\n \"[ToolInvocationTracker] result chunk handling failed\",\n err,\n );\n }\n },\n }),\n )\n .catch((err) => {\n console.error(\n \"[ToolInvocationTracker] stream pipeline failed; will attempt single restart on next setState\",\n err,\n );\n this._pipelineDead = true;\n });\n }\n\n // ───────────────────────── public API ─────────────────────────\n\n /**\n * Feed the next observed snapshot into the tracker. Called from the host\n * runtime whenever its message list / running state changes.\n */\n public setState(snapshot: ToolInvocationTracker.Snapshot): void {\n try {\n // Recover from a dead pipeline before processing anything. We demote\n // all active entries to \"restored\" so the rebuilt pipeline does not\n // re-fire `streamCall` for tool calls that already fired pre-death;\n // preserves the \"exactly once per toolCallId\" contract.\n if (this._pipelineDead) {\n if (this._pipelineRestartUsed) {\n // Already retried once and failed again. Stay dead.\n return;\n }\n this._pipelineRestartUsed = true;\n this._pipelineDead = false;\n this._demoteEntriesToRestored();\n this._executing.clear();\n this._ac = new AbortController();\n this._initPipeline();\n // Fall through and process the snapshot against the fresh pipeline.\n }\n\n // Identical snapshot — skip processing entirely. Note: external-store\n // runtimes rebuild the messages array on every adapter update, so this\n // fast-path rarely triggers there; it's primarily for the React-hook\n // shim where state references are stable.\n if (\n this._lastSnapshot &&\n this._lastSnapshot.messages === snapshot.messages &&\n this._lastSnapshot.isRunning === snapshot.isRunning &&\n this._lastSnapshot.isLoading === snapshot.isLoading\n ) {\n return;\n }\n\n // While the host is still loading initial state, treat every snapshot\n // as historical: tool calls are recorded so the next live snapshot can\n // diff against them, but `streamCall` / `execute` do not fire.\n const restoreFromLoading = snapshot.isLoading === true;\n if (restoreFromLoading) {\n this._pendingRestore = true;\n }\n\n // E.4 / AF3 — only mark `_lastSnapshot`/`_isRunning` as observed after\n // processing succeeds. If `_processMessages` throws, the next snapshot\n // (even if identical) gets re-processed against the recovered state.\n const previousIsRunning = this._isRunning;\n this._isRunning = snapshot.isRunning;\n try {\n this._processMessages(snapshot.messages);\n } catch (err) {\n this._isRunning = previousIsRunning;\n throw err;\n }\n this._lastSnapshot = snapshot;\n this._pendingRestore = false;\n } catch (err) {\n console.error(\n \"[ToolInvocationTracker] setState failed; snapshot dropped\",\n err,\n );\n }\n }\n\n /**\n * Reset the tracker so the next observed snapshot is treated as historical.\n * Clears entries and aborts any in-flight executions. Used by callers like\n * `importExternalState` to mark a freshly loaded state as restored.\n */\n public reset(): void {\n try {\n this._pendingRestore = true;\n this._entries.clear();\n this._lastSnapshot = null;\n void this.abort();\n // Statuses are cleared synchronously: discarded executions may never\n // settle (aborting hands the signal to the tool, it does not force\n // settlement), and a late settlement of one sibling must not republish\n // the others. `_deleteStatus` no-ops for cleared ids, so post-reset\n // settlements stay silent.\n if (this._statuses.size > 0) {\n this._statuses = new Map();\n this._invokeOnStatusesChange();\n }\n } catch (err) {\n console.error(\"[ToolInvocationTracker] reset failed\", err);\n }\n }\n\n /**\n * Abort any in-flight `execute()` invocations. Resolves once all of them\n * have settled (or immediately if none are running).\n */\n public abort(): Promise<void> {\n try {\n this._humanInput.forEach(({ reject }) => {\n try {\n reject(new Error(\"Tool execution aborted\"));\n } catch {\n // host rejection handler threw — already in the abort path,\n // swallow so we continue cleaning up.\n }\n });\n this._humanInput.clear();\n\n this._ac.abort();\n this._ac = new AbortController();\n\n if (this._executing.size === 0) {\n return Promise.resolve();\n }\n const executionIds = new Set(this._executing);\n return new Promise<void>((resolve) => {\n this._settledResolvers.push({ executionIds, resolve });\n });\n } catch (err) {\n console.error(\"[ToolInvocationTracker] abort failed\", err);\n return Promise.resolve();\n }\n }\n\n /**\n * Resolve a pending human-input request for the given tool call. Returns\n * `true` if a pending request was resumed, `false` if the tracker has no\n * outstanding request for that id (the caller should fall back to its own\n * dispatch path).\n */\n public resume(toolCallId: string, payload: unknown): boolean {\n try {\n const handlers = this._humanInput.get(toolCallId);\n if (!handlers) return false;\n this._humanInput.delete(toolCallId);\n this._setStatus(toolCallId, { type: \"executing\" });\n handlers.resolve(payload);\n return true;\n } catch (err) {\n console.error(\"[ToolInvocationTracker] resume failed\", err);\n return false;\n }\n }\n\n /**\n * Returns the current tool execution status map. The returned `Map` is\n * the tracker's internal store — do not mutate it. Treat the reference\n * as a snapshot that may be replaced wholesale on the next status\n * transition.\n */\n public getStatuses(): ReadonlyMap<string, ToolExecutionStatus> {\n return this._statuses;\n }\n\n // ───────────────────── internal: tool wrapping ─────────────────────\n\n private _getWrappedTools(): Record<string, Tool> | undefined {\n const tools = this._getTools();\n if (!tools) return undefined;\n\n return Object.fromEntries(\n Object.entries(tools).map(([name, tool]) => {\n const execute = tool.execute;\n const streamCall = tool.streamCall;\n if (execute === undefined && streamCall === undefined)\n return [name, tool];\n\n const wrappedTool = {\n ...tool,\n ...(execute !== undefined && {\n execute: (...[args, context]: Parameters<typeof execute>) => {\n const executionId = getToolExecutionId(context);\n const entry = this._captureExecution(\n context.toolCallId,\n executionId,\n );\n if (!entry || entry.skipExecute) {\n return new Promise(() => {}) as never;\n }\n return execute(args, context);\n },\n }),\n ...(streamCall !== undefined && {\n streamCall: (\n ...[reader, context]: Parameters<typeof streamCall>\n ) => {\n const executionId = getToolExecutionId(context);\n const entry = this._captureExecution(\n context.toolCallId,\n executionId,\n );\n if (!entry) return;\n return streamCall(reader, context);\n },\n }),\n } as Tool;\n return [name, wrappedTool];\n }),\n ) as Record<string, Tool>;\n }\n\n private _captureExecution(\n toolCallId: string,\n executionId: symbol | undefined,\n ): ToolCallEntry | undefined {\n if (executionId === undefined) return undefined;\n const entry = this._entries.get(toolCallId);\n if (!entry?.controller) return undefined;\n if (entry.executionId === undefined) entry.executionId = executionId;\n return entry.executionId === executionId ? entry : undefined;\n }\n\n // ──────────────── internal: execution lifecycle callbacks ────────────────\n\n private _onHumanInput(\n toolCallId: string,\n payload: unknown,\n executionId?: symbol,\n ): Promise<unknown> {\n return new Promise<unknown>((resolve, reject) => {\n // A discarded execution must not resurrect a status entry or park an\n // unanswerable request after its id has been reused.\n const entry = this._entries.get(toolCallId);\n if (!entry?.controller || entry.executionId !== executionId) {\n reject(new Error(\"Tool execution aborted\"));\n return;\n }\n const previous = this._humanInput.get(toolCallId);\n if (previous) {\n try {\n previous.reject(\n new Error(\"Human input request was superseded by a new request\"),\n );\n } catch {\n // host rejection handler threw; ignore and proceed\n }\n }\n this._humanInput.set(toolCallId, {\n executionId: executionId!,\n resolve,\n reject,\n });\n this._setStatus(toolCallId, {\n type: \"interrupt\",\n payload: { type: \"human\", payload },\n });\n });\n }\n\n private _onExecutionStart(\n toolCallId: string,\n executionId: symbol | undefined,\n ): void {\n if (!this._captureExecution(toolCallId, executionId)) return;\n const entry = this._entries.get(toolCallId)!;\n if (entry.skipExecute) return;\n\n this._executing.add(executionId!);\n this._setStatus(toolCallId, { type: \"executing\" });\n }\n\n private _onExecutionEnd(\n toolCallId: string,\n executionId: symbol | undefined,\n ): void {\n if (executionId === undefined || !this._executing.delete(executionId))\n return;\n\n const entry = this._entries.get(toolCallId);\n if (entry?.executionId === executionId) this._deleteStatus(toolCallId);\n\n const pending: SettledResolver[] = [];\n this._settledResolvers.forEach(({ executionIds, resolve }) => {\n if ([...executionIds].some((id) => this._executing.has(id))) {\n pending.push({ executionIds, resolve });\n return;\n }\n try {\n resolve();\n } catch {\n // ignore — settled-resolver consumer threw\n }\n });\n this._settledResolvers.length = 0;\n this._settledResolvers.push(...pending);\n }\n\n private _handleResultChunk(chunk: {\n type: \"result\";\n result: ReadonlyJSONValue;\n isError: boolean;\n artifact?: ReadonlyJSONValue;\n modelContent?: readonly ToolModelContentPart[];\n meta: { toolCallId: string; toolName: string };\n }): void {\n const toolCallId = chunk.meta.toolCallId;\n const executionId = getToolExecutionId(chunk);\n const entry = this._entries.get(toolCallId);\n\n if (!entry || entry.executionId !== executionId) return;\n\n // The host already set the result (via the live snapshot's\n // `setResponse` path). Suppress the executor's redundant emit.\n if (entry?.hasResult) return;\n\n this._invokeOnResult({\n type: \"add-tool-result\",\n toolCallId,\n toolName: chunk.meta.toolName,\n result: chunk.result,\n isError: chunk.isError,\n ...(chunk.artifact !== undefined && { artifact: chunk.artifact }),\n ...(chunk.modelContent !== undefined && {\n modelContent: chunk.modelContent,\n }),\n });\n }\n\n // ──────────────── internal: callback invocation (AF1/AF2) ────────────────\n\n private _invokeOnResult(command: AddToolResultCommand): void {\n try {\n this._callbacks.onResult(command);\n } catch (err) {\n console.error(\n \"[ToolInvocationTracker] onResult callback threw; result dropped\",\n err,\n );\n }\n }\n\n private _invokeOnStatusesChange(): void {\n try {\n this._callbacks.onStatusesChange(this._statuses);\n } catch (err) {\n console.error(\n \"[ToolInvocationTracker] onStatusesChange callback threw; status change not propagated\",\n err,\n );\n }\n }\n\n // ──────────────── internal: status map mutations ────────────────\n\n private _setStatus(toolCallId: string, status: ToolExecutionStatus): void {\n const next = new Map(this._statuses);\n next.set(toolCallId, status);\n this._statuses = next;\n this._invokeOnStatusesChange();\n }\n\n private _deleteStatus(toolCallId: string): void {\n if (!this._statuses.has(toolCallId)) return;\n const next = new Map(this._statuses);\n next.delete(toolCallId);\n this._statuses = next;\n this._invokeOnStatusesChange();\n }\n\n // ──────────────── internal: snapshot processing ────────────────\n\n private _hasExecutableTool(toolName: string): boolean {\n const tool = this._getTools()?.[toolName];\n return tool?.execute !== undefined || tool?.streamCall !== undefined;\n }\n\n private _shouldCloseArgsStream({\n toolName,\n argsText,\n hasResult,\n }: {\n toolName: string;\n argsText: string;\n hasResult: boolean;\n }): boolean {\n if (hasResult) return true;\n if (!this._hasExecutableTool(toolName)) {\n return !this._isRunning && isArgsTextComplete(argsText);\n }\n return isArgsTextComplete(argsText);\n }\n\n private _startActiveEntry(\n toolCallId: string,\n toolName: string,\n skipExecute: boolean,\n ): ToolCallEntry {\n const toolCallController = this._controller.addToolCallPart({\n toolName,\n toolCallId,\n });\n const entry: ToolCallEntry = {\n toolName,\n controller: toolCallController,\n argsText: \"\",\n hasResult: false,\n skipExecute,\n argsComplete: false,\n };\n this._entries.set(toolCallId, entry);\n return entry;\n }\n\n /**\n * Demote every active entry back to the restored phase. Used by the\n * pipeline-restart path so that, after a fresh pipeline is built, the\n * next observed snapshot does not re-fire `streamCall` for tool calls\n * that already fired pre-death. Args / hasResult tracking is preserved\n * so signature comparisons still work.\n */\n private _demoteEntriesToRestored(): void {\n for (const [toolCallId, entry] of this._entries) {\n if (!entry.controller) continue;\n this._entries.set(toolCallId, {\n toolName: entry.toolName,\n argsText: entry.argsText,\n hasResult: entry.hasResult,\n });\n }\n }\n\n private _processArgsText(\n entry: ToolCallEntry,\n content: {\n toolCallId: string;\n toolName: string;\n argsText: string;\n result?: unknown;\n },\n ): void {\n if (!entry.controller) return;\n const hasResult = content.result !== undefined;\n\n if (content.argsText !== entry.argsText) {\n let shouldWriteArgsText = true;\n\n if (entry.argsComplete) {\n if (isEquivalentCompleteArgsText(entry.argsText, content.argsText)) {\n // A.3 — key reorder. Track new text, no re-fire needed.\n entry.argsText = content.argsText;\n shouldWriteArgsText = false;\n } else {\n // A.4 — args changed after first completion. Under the\n // \"exactly once per toolCallId\" contract we do not restart the\n // stream. The host's existing `streamCall` keeps its original\n // args view; the snapshot's new text is recorded for diffing\n // but not surfaced. Events API in a follow-up will expose this\n // to consumers that opt in.\n if (process.env.NODE_ENV !== \"production\") {\n console.warn(\n \"[ToolInvocationTracker] argsText changed after first completion; not re-firing streamCall (see EDGE_CASES.md A.4)\",\n {\n previous: entry.argsText,\n next: content.argsText,\n toolCallId: content.toolCallId,\n },\n );\n }\n shouldWriteArgsText = false;\n }\n } else if (!content.argsText.startsWith(entry.argsText)) {\n if (\n isArgsTextComplete(entry.argsText) &&\n isArgsTextComplete(content.argsText) &&\n isEquivalentCompleteArgsText(entry.argsText, content.argsText)\n ) {\n const shouldClose = this._shouldCloseArgsStream({\n toolName: content.toolName,\n argsText: content.argsText,\n hasResult,\n });\n if (shouldClose) entry.controller.argsText.close();\n entry.argsText = content.argsText;\n entry.argsComplete = shouldClose;\n shouldWriteArgsText = false;\n } else {\n // A.2 — args regressed mid-stream. Under the \"exactly once\"\n // contract we do not restart. The controller keeps whatever\n // prefix we already streamed; subsequent prefix-respecting\n // updates can still flow against it. Snapshots that never\n // re-converge to a prefix will leave the controller's args\n // view stale relative to the snapshot. Events API in a\n // follow-up will expose this to consumers that opt in.\n if (process.env.NODE_ENV !== \"production\") {\n console.warn(\n \"[ToolInvocationTracker] argsText regressed mid-stream; not restarting (see EDGE_CASES.md A.2)\",\n {\n previous: entry.argsText,\n next: content.argsText,\n toolCallId: content.toolCallId,\n },\n );\n }\n shouldWriteArgsText = false;\n }\n }\n\n if (shouldWriteArgsText && entry.controller) {\n const delta = content.argsText.slice(entry.argsText.length);\n entry.controller.argsText.append(delta);\n const shouldClose = this._shouldCloseArgsStream({\n toolName: content.toolName,\n argsText: content.argsText,\n hasResult,\n });\n if (shouldClose) entry.controller.argsText.close();\n entry.argsText = content.argsText;\n entry.argsComplete = shouldClose;\n }\n }\n\n if (!entry.argsComplete && entry.controller) {\n // ToolExecutionStream parses the streamed prefix on close, so the close\n // gates on the streamed content; a divergent snapshot (A.2) can be\n // complete while the controller still holds an incomplete stale prefix.\n const shouldClose = this._shouldCloseArgsStream({\n toolName: content.toolName,\n argsText: entry.argsText,\n hasResult,\n });\n if (shouldClose) {\n entry.controller.argsText.close();\n entry.argsComplete = true;\n }\n }\n }\n\n private _processMessages(messages: readonly ThreadMessage[]): void {\n const isRestore = this._pendingRestore;\n\n for (const message of messages) {\n if (!message || !Array.isArray((message as ThreadMessage).content)) {\n continue;\n }\n for (const content of message.content as readonly ThreadMessage[\"content\"][number][]) {\n if (!content || content.type !== \"tool-call\") continue;\n\n const existing = this._entries.get(content.toolCallId);\n\n if (isRestore) {\n // Don't overwrite an already-active entry (e.g. live tool-call\n // observed before this restore snapshot landed). Restore can\n // only seed entries the runtime has never seen.\n if (!existing?.controller) {\n this._entries.set(content.toolCallId, {\n toolName: content.toolName,\n argsText: content.argsText,\n hasResult: content.result !== undefined,\n });\n }\n if (content.messages) this._processMessages(content.messages);\n continue;\n }\n\n // Live snapshot.\n let entry = existing;\n\n if (entry && !entry.controller) {\n // Restored entry observed in a live snapshot. Promote if its\n // signature has changed; otherwise treat as still-historical.\n const signatureChanged =\n content.argsText !== entry.argsText ||\n (content.result !== undefined) !== entry.hasResult;\n if (!signatureChanged) {\n if (content.messages) this._processMessages(content.messages);\n continue;\n }\n this._entries.delete(content.toolCallId);\n entry = undefined;\n }\n\n if (!entry) {\n entry = this._startActiveEntry(\n content.toolCallId,\n content.toolName,\n content.result !== undefined,\n );\n }\n\n this._processArgsText(entry, content);\n\n if (content.result !== undefined && !entry.hasResult) {\n // `entry` is in active phase from this point — either just\n // created by `_startActiveEntry`, or pre-existing with a live\n // controller. Narrow once instead of asserting at every use.\n const { controller: activeController } = entry;\n if (!activeController) continue;\n entry.hasResult = true;\n entry.argsComplete = true;\n activeController.setResponse(\n new ToolResponse({\n result: content.result as ReadonlyJSONValue,\n artifact: content.artifact as ReadonlyJSONValue | undefined,\n isError: content.isError,\n ...(content.modelContent !== undefined\n ? { modelContent: content.modelContent }\n : {}),\n }),\n );\n activeController.close();\n }\n\n if (content.messages) this._processMessages(content.messages);\n }\n }\n }\n}\n\nexport namespace ToolInvocationTracker {\n export type ExecutionStatus = ToolExecutionStatus;\n\n export type Snapshot = {\n readonly messages: readonly ThreadMessage[];\n /** Whether the producing runtime is currently streaming new output. */\n readonly isRunning: boolean;\n /**\n * Whether the producing runtime is still loading historical state.\n * When `true`, every snapshot is treated as historical (no `streamCall` /\n * `execute` fires). When `false`, processing resumes as live.\n */\n readonly isLoading?: boolean;\n };\n\n export type Callbacks = {\n /**\n * Invoked when a client-side `execute()` returns a result and the runtime\n * needs to feed it back into the conversation.\n */\n onResult: (command: AddToolResultCommand) => void;\n /**\n * Invoked whenever the per-tool-call status map changes (executing /\n * interrupt / cleared). The callback receives a fresh map; mutating the\n * argument is not supported.\n */\n onStatusesChange: (\n statuses: ReadonlyMap<string, ToolExecutionStatus>,\n ) => void;\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ToolInvocationTracker.js","names":["process","env","NODE_ENV","createAssistantStreamController","ToolCallStreamController","ToolResponse","unstable_toolResultStream","Tool","ToolModelContentPart","ToolResultStreamOptions","AssistantMetaTransformStream","ReadonlyJSONValue","isJSONValueEqual","ThreadMessage","TOOL_EXECUTION_ID","Symbol","for","ToolExecutionStatus","type","payload","AddToolResultCommand","toolCallId","toolName","result","isError","artifact","modelContent","ToolCallEntry","argsText","hasResult","executionId","skipExecute","controller","argsComplete","SettledResolver","executionIds","ReadonlySet","resolve","isArgsTextComplete","JSON","parse","parseArgsText","undefined","isEquivalentCompleteArgsText","previous","next","previousValue","nextValue","getToolExecutionId","value","Record","PropertyKey","ToolInvocationTracker","_getTools","_callbacks","Callbacks","_entries","Map","_humanInput","reject","reason","_executing","Set","_settledResolvers","_statuses","_ac","AbortController","_pendingRestore","_lastSnapshot","Snapshot","_isRunning","_controller","ReturnType","_pipelineDead","_pipelineRestartUsed","constructor","getTools","callbacks","_initPipeline","stream","human","_onHumanInput","transform","_getWrappedTools","signal","onExecutionStart","id","_name","_onExecutionStart","onExecutionEnd","_onExecutionEnd","pipeThrough","pipeTo","WritableStream","write","chunk","_handleResultChunk","err","console","error","catch","setState","snapshot","_demoteEntriesToRestored","clear","messages","isRunning","isLoading","restoreFromLoading","previousIsRunning","_processMessages","reset","abort","size","_invokeOnStatusesChange","Promise","forEach","Error","push","resume","handlers","get","delete","_setStatus","getStatuses","ReadonlyMap","tools","Object","fromEntries","entries","map","name","tool","execute","streamCall","wrappedTool","args","context","Parameters","entry","_captureExecution","reader","set","add","_deleteStatus","pending","some","has","length","meta","_invokeOnResult","command","onResult","onStatusesChange","status","_hasExecutableTool","_shouldCloseArgsStream","_startActiveEntry","toolCallController","addToolCallPart","_processArgsText","content","shouldWriteArgsText","warn","startsWith","shouldClose","close","delta","slice","append","isRestore","message","Array","isArray","existing","signatureChanged","approval","activeController","setResponse","ExecutionStatus","statuses"],"sources":["../../../src/runtimes/tool-invocations/ToolInvocationTracker.ts"],"sourcesContent":["declare const process: { env: { NODE_ENV?: string } };\n\nimport {\n createAssistantStreamController,\n type ToolCallStreamController,\n ToolResponse,\n unstable_toolResultStream,\n type Tool,\n type ToolModelContentPart,\n type ToolResultStreamOptions,\n} from \"assistant-stream\";\nimport {\n AssistantMetaTransformStream,\n type ReadonlyJSONValue,\n} from \"assistant-stream/utils\";\nimport { isJSONValueEqual } from \"../../utils/json/is-json-equal\";\nimport type { ThreadMessage } from \"../../types/message\";\n\nconst TOOL_EXECUTION_ID = Symbol.for(\"assistant-stream.tool-execution-id\");\n\n/**\n * Streaming execution state for a frontend tool.\n */\nexport type ToolExecutionStatus =\n | { type: \"executing\" }\n | {\n type: \"interrupt\";\n payload: { type: \"human\"; payload: unknown };\n };\n\nexport type AddToolResultCommand = {\n readonly type: \"add-tool-result\";\n readonly toolCallId: string;\n readonly toolName: string;\n readonly result: ReadonlyJSONValue;\n readonly isError: boolean;\n readonly artifact?: ReadonlyJSONValue;\n readonly modelContent?: readonly ToolModelContentPart[];\n};\n\ntype ToolCallEntry = {\n toolName: string;\n argsText: string;\n hasResult: boolean;\n executionId?: symbol;\n skipExecute?: boolean;\n} & (\n | {\n /** Restored phase — observed during a history-load snapshot. */\n controller?: undefined;\n argsComplete?: undefined;\n }\n | {\n /** Active phase — chunks are flowing through `controller`. */\n controller: ToolCallStreamController;\n argsComplete: boolean;\n }\n);\n\ntype SettledResolver = {\n executionIds: ReadonlySet<symbol>;\n resolve: () => void;\n};\n\nconst isArgsTextComplete = (argsText: string) => {\n try {\n JSON.parse(argsText);\n return true;\n } catch {\n return false;\n }\n};\n\nconst parseArgsText = (argsText: string) => {\n try {\n return JSON.parse(argsText);\n } catch {\n return undefined;\n }\n};\n\nconst isEquivalentCompleteArgsText = (previous: string, next: string) => {\n const previousValue = parseArgsText(previous);\n const nextValue = parseArgsText(next);\n if (previousValue === undefined || nextValue === undefined) return false;\n return isJSONValueEqual(previousValue, nextValue);\n};\n\nconst getToolExecutionId = (value: object): symbol | undefined =>\n (value as Record<PropertyKey, unknown>)[TOOL_EXECUTION_ID] as\n | symbol\n | undefined;\n\n/**\n * Plain-class port of the former `useToolInvocations` React hook. Owns the\n * assistant-stream pipeline that drives client-side `streamCall` / `execute`\n * for tool-call parts surfaced by a thread runtime, plus the per-tool-call\n * status map that consumers render against.\n *\n * **Contract**: `streamCall` (and `execute`) fires exactly once per logical\n * `toolCallId`. Args mutations after first completion, result replacement,\n * and result clearing are *not* surfaced through additional `streamCall`\n * invocations — by design — so hosts cannot observe spurious re-fires of\n * side effects. The follow-up `reader.events()` API will expose those\n * post-completion transitions to consumers that opt in.\n *\n * State-transition safety: every public method that observes runtime state\n * (`setState`, `reset`, `abort`, `resume`) wraps its work in try/catch and\n * logs to `console.error` rather than throwing. The tracker is built into\n * the hot message-processing path, so a malformed snapshot must never crash\n * the host runtime. See ./EDGE_CASES.md for the known non-trivial state\n * transitions and what each does today.\n *\n * @deprecated Internal — for framework bindings; not a public API. May change without notice.\n */\nexport class ToolInvocationTracker {\n private readonly _getTools: () => Record<string, Tool> | undefined;\n private readonly _callbacks: ToolInvocationTracker.Callbacks;\n\n private readonly _entries = new Map<string, ToolCallEntry>();\n private readonly _humanInput = new Map<\n string,\n {\n executionId: symbol;\n resolve: (payload: unknown) => void;\n reject: (reason: unknown) => void;\n }\n >();\n private readonly _executing = new Set<symbol>();\n private readonly _settledResolvers: SettledResolver[] = [];\n\n private _statuses = new Map<string, ToolExecutionStatus>();\n\n private _ac: AbortController = new AbortController();\n private _pendingRestore = true;\n\n /** Cached last snapshot, used to skip processing on identical re-renders. */\n private _lastSnapshot: ToolInvocationTracker.Snapshot | null = null;\n private _isRunning = false;\n\n private _controller!: ReturnType<typeof createAssistantStreamController>[1];\n\n /**\n * Set when the assistant-stream pipeline has died (errored out via\n * `.pipeTo(...).catch(...)`). The next `setState` re-initializes the\n * pipeline and demotes all active entries to restored so they survive\n * across the restart without re-firing `streamCall` (preserves the\n * \"exactly once\" contract). Capped at a single auto-restart per session\n * — repeated failures keep the tracker dead with a more visible error.\n */\n private _pipelineDead = false;\n private _pipelineRestartUsed = false;\n\n constructor(\n getTools: () => Record<string, Tool> | undefined,\n callbacks: ToolInvocationTracker.Callbacks,\n ) {\n this._getTools = getTools;\n this._callbacks = callbacks;\n\n this._initPipeline();\n }\n\n /**\n * Build the assistant-stream pipeline. Called once from the constructor\n * and at most once again if `_pipelineDead` is set (see F.4 in\n * EDGE_CASES.md).\n */\n private _initPipeline(): void {\n const [stream, controller] = createAssistantStreamController();\n this._controller = controller;\n\n const human = (\n toolCallId: string,\n payload: unknown,\n executionId?: symbol,\n ) => this._onHumanInput(toolCallId, payload, executionId);\n const transform = unstable_toolResultStream(\n () => this._getWrappedTools(),\n () => this._ac.signal,\n human,\n {\n onExecutionStart: (id: string, _name: string, executionId?: symbol) =>\n this._onExecutionStart(id, executionId),\n onExecutionEnd: (id: string, _name: string, executionId?: symbol) =>\n this._onExecutionEnd(id, executionId),\n } as ToolResultStreamOptions,\n );\n\n stream\n .pipeThrough(transform)\n .pipeThrough(new AssistantMetaTransformStream())\n .pipeTo(\n new WritableStream({\n write: (chunk) => {\n try {\n if (chunk.type !== \"result\") return;\n this._handleResultChunk(chunk);\n } catch (err) {\n console.error(\n \"[ToolInvocationTracker] result chunk handling failed\",\n err,\n );\n }\n },\n }),\n )\n .catch((err) => {\n console.error(\n \"[ToolInvocationTracker] stream pipeline failed; will attempt single restart on next setState\",\n err,\n );\n this._pipelineDead = true;\n });\n }\n\n // ───────────────────────── public API ─────────────────────────\n\n /**\n * Feed the next observed snapshot into the tracker. Called from the host\n * runtime whenever its message list / running state changes.\n */\n public setState(snapshot: ToolInvocationTracker.Snapshot): void {\n try {\n // Recover from a dead pipeline before processing anything. We demote\n // all active entries to \"restored\" so the rebuilt pipeline does not\n // re-fire `streamCall` for tool calls that already fired pre-death;\n // preserves the \"exactly once per toolCallId\" contract.\n if (this._pipelineDead) {\n if (this._pipelineRestartUsed) {\n // Already retried once and failed again. Stay dead.\n return;\n }\n this._pipelineRestartUsed = true;\n this._pipelineDead = false;\n this._demoteEntriesToRestored();\n this._executing.clear();\n this._ac = new AbortController();\n this._initPipeline();\n // Fall through and process the snapshot against the fresh pipeline.\n }\n\n // Identical snapshot — skip processing entirely. Note: external-store\n // runtimes rebuild the messages array on every adapter update, so this\n // fast-path rarely triggers there; it's primarily for the React-hook\n // shim where state references are stable.\n if (\n this._lastSnapshot &&\n this._lastSnapshot.messages === snapshot.messages &&\n this._lastSnapshot.isRunning === snapshot.isRunning &&\n this._lastSnapshot.isLoading === snapshot.isLoading\n ) {\n return;\n }\n\n // While the host is still loading initial state, treat every snapshot\n // as historical: tool calls are recorded so the next live snapshot can\n // diff against them, but `streamCall` / `execute` do not fire.\n const restoreFromLoading = snapshot.isLoading === true;\n if (restoreFromLoading) {\n this._pendingRestore = true;\n }\n\n // E.4 / AF3 — only mark `_lastSnapshot`/`_isRunning` as observed after\n // processing succeeds. If `_processMessages` throws, the next snapshot\n // (even if identical) gets re-processed against the recovered state.\n const previousIsRunning = this._isRunning;\n this._isRunning = snapshot.isRunning;\n try {\n this._processMessages(snapshot.messages);\n } catch (err) {\n this._isRunning = previousIsRunning;\n throw err;\n }\n this._lastSnapshot = snapshot;\n this._pendingRestore = false;\n } catch (err) {\n console.error(\n \"[ToolInvocationTracker] setState failed; snapshot dropped\",\n err,\n );\n }\n }\n\n /**\n * Reset the tracker so the next observed snapshot is treated as historical.\n * Clears entries and aborts any in-flight executions. Used by callers like\n * `importExternalState` to mark a freshly loaded state as restored.\n */\n public reset(): void {\n try {\n this._pendingRestore = true;\n this._entries.clear();\n this._lastSnapshot = null;\n void this.abort();\n // Statuses are cleared synchronously: discarded executions may never\n // settle (aborting hands the signal to the tool, it does not force\n // settlement), and a late settlement of one sibling must not republish\n // the others. `_deleteStatus` no-ops for cleared ids, so post-reset\n // settlements stay silent.\n if (this._statuses.size > 0) {\n this._statuses = new Map();\n this._invokeOnStatusesChange();\n }\n } catch (err) {\n console.error(\"[ToolInvocationTracker] reset failed\", err);\n }\n }\n\n /**\n * Abort any in-flight `execute()` invocations. Resolves once all of them\n * have settled (or immediately if none are running).\n */\n public abort(): Promise<void> {\n try {\n this._humanInput.forEach(({ reject }) => {\n try {\n reject(new Error(\"Tool execution aborted\"));\n } catch {\n // host rejection handler threw — already in the abort path,\n // swallow so we continue cleaning up.\n }\n });\n this._humanInput.clear();\n\n this._ac.abort();\n this._ac = new AbortController();\n\n if (this._executing.size === 0) {\n return Promise.resolve();\n }\n const executionIds = new Set(this._executing);\n return new Promise<void>((resolve) => {\n this._settledResolvers.push({ executionIds, resolve });\n });\n } catch (err) {\n console.error(\"[ToolInvocationTracker] abort failed\", err);\n return Promise.resolve();\n }\n }\n\n /**\n * Resolve a pending human-input request for the given tool call. Returns\n * `true` if a pending request was resumed, `false` if the tracker has no\n * outstanding request for that id (the caller should fall back to its own\n * dispatch path).\n */\n public resume(toolCallId: string, payload: unknown): boolean {\n try {\n const handlers = this._humanInput.get(toolCallId);\n if (!handlers) return false;\n this._humanInput.delete(toolCallId);\n this._setStatus(toolCallId, { type: \"executing\" });\n handlers.resolve(payload);\n return true;\n } catch (err) {\n console.error(\"[ToolInvocationTracker] resume failed\", err);\n return false;\n }\n }\n\n /**\n * Returns the current tool execution status map. The returned `Map` is\n * the tracker's internal store — do not mutate it. Treat the reference\n * as a snapshot that may be replaced wholesale on the next status\n * transition.\n */\n public getStatuses(): ReadonlyMap<string, ToolExecutionStatus> {\n return this._statuses;\n }\n\n // ───────────────────── internal: tool wrapping ─────────────────────\n\n private _getWrappedTools(): Record<string, Tool> | undefined {\n const tools = this._getTools();\n if (!tools) return undefined;\n\n return Object.fromEntries(\n Object.entries(tools).map(([name, tool]) => {\n const execute = tool.execute;\n const streamCall = tool.streamCall;\n if (execute === undefined && streamCall === undefined)\n return [name, tool];\n\n const wrappedTool = {\n ...tool,\n ...(execute !== undefined && {\n execute: (...[args, context]: Parameters<typeof execute>) => {\n const executionId = getToolExecutionId(context);\n const entry = this._captureExecution(\n context.toolCallId,\n executionId,\n );\n if (!entry || entry.skipExecute) {\n return new Promise(() => {}) as never;\n }\n return execute(args, context);\n },\n }),\n ...(streamCall !== undefined && {\n streamCall: (\n ...[reader, context]: Parameters<typeof streamCall>\n ) => {\n const executionId = getToolExecutionId(context);\n const entry = this._captureExecution(\n context.toolCallId,\n executionId,\n );\n if (!entry) return;\n return streamCall(reader, context);\n },\n }),\n } as Tool;\n return [name, wrappedTool];\n }),\n ) as Record<string, Tool>;\n }\n\n private _captureExecution(\n toolCallId: string,\n executionId: symbol | undefined,\n ): ToolCallEntry | undefined {\n if (executionId === undefined) return undefined;\n const entry = this._entries.get(toolCallId);\n if (!entry?.controller) return undefined;\n if (entry.executionId === undefined) entry.executionId = executionId;\n return entry.executionId === executionId ? entry : undefined;\n }\n\n // ──────────────── internal: execution lifecycle callbacks ────────────────\n\n private _onHumanInput(\n toolCallId: string,\n payload: unknown,\n executionId?: symbol,\n ): Promise<unknown> {\n return new Promise<unknown>((resolve, reject) => {\n // A discarded execution must not resurrect a status entry or park an\n // unanswerable request after its id has been reused.\n const entry = this._entries.get(toolCallId);\n if (!entry?.controller || entry.executionId !== executionId) {\n reject(new Error(\"Tool execution aborted\"));\n return;\n }\n const previous = this._humanInput.get(toolCallId);\n if (previous) {\n try {\n previous.reject(\n new Error(\"Human input request was superseded by a new request\"),\n );\n } catch {\n // host rejection handler threw; ignore and proceed\n }\n }\n this._humanInput.set(toolCallId, {\n executionId: executionId!,\n resolve,\n reject,\n });\n this._setStatus(toolCallId, {\n type: \"interrupt\",\n payload: { type: \"human\", payload },\n });\n });\n }\n\n private _onExecutionStart(\n toolCallId: string,\n executionId: symbol | undefined,\n ): void {\n if (!this._captureExecution(toolCallId, executionId)) return;\n const entry = this._entries.get(toolCallId)!;\n if (entry.skipExecute) return;\n\n this._executing.add(executionId!);\n this._setStatus(toolCallId, { type: \"executing\" });\n }\n\n private _onExecutionEnd(\n toolCallId: string,\n executionId: symbol | undefined,\n ): void {\n if (executionId === undefined || !this._executing.delete(executionId))\n return;\n\n const entry = this._entries.get(toolCallId);\n if (entry?.executionId === executionId) this._deleteStatus(toolCallId);\n\n const pending: SettledResolver[] = [];\n this._settledResolvers.forEach(({ executionIds, resolve }) => {\n if ([...executionIds].some((id) => this._executing.has(id))) {\n pending.push({ executionIds, resolve });\n return;\n }\n try {\n resolve();\n } catch {\n // ignore — settled-resolver consumer threw\n }\n });\n this._settledResolvers.length = 0;\n this._settledResolvers.push(...pending);\n }\n\n private _handleResultChunk(chunk: {\n type: \"result\";\n result: ReadonlyJSONValue;\n isError: boolean;\n artifact?: ReadonlyJSONValue;\n modelContent?: readonly ToolModelContentPart[];\n meta: { toolCallId: string; toolName: string };\n }): void {\n const toolCallId = chunk.meta.toolCallId;\n const executionId = getToolExecutionId(chunk);\n const entry = this._entries.get(toolCallId);\n\n if (!entry || entry.executionId !== executionId) return;\n\n // The host already set the result (via the live snapshot's\n // `setResponse` path). Suppress the executor's redundant emit.\n if (entry?.hasResult) return;\n if (entry.skipExecute) return;\n\n this._invokeOnResult({\n type: \"add-tool-result\",\n toolCallId,\n toolName: chunk.meta.toolName,\n result: chunk.result,\n isError: chunk.isError,\n ...(chunk.artifact !== undefined && { artifact: chunk.artifact }),\n ...(chunk.modelContent !== undefined && {\n modelContent: chunk.modelContent,\n }),\n });\n }\n\n // ──────────────── internal: callback invocation (AF1/AF2) ────────────────\n\n private _invokeOnResult(command: AddToolResultCommand): void {\n try {\n this._callbacks.onResult(command);\n } catch (err) {\n console.error(\n \"[ToolInvocationTracker] onResult callback threw; result dropped\",\n err,\n );\n }\n }\n\n private _invokeOnStatusesChange(): void {\n try {\n this._callbacks.onStatusesChange(this._statuses);\n } catch (err) {\n console.error(\n \"[ToolInvocationTracker] onStatusesChange callback threw; status change not propagated\",\n err,\n );\n }\n }\n\n // ──────────────── internal: status map mutations ────────────────\n\n private _setStatus(toolCallId: string, status: ToolExecutionStatus): void {\n const next = new Map(this._statuses);\n next.set(toolCallId, status);\n this._statuses = next;\n this._invokeOnStatusesChange();\n }\n\n private _deleteStatus(toolCallId: string): void {\n if (!this._statuses.has(toolCallId)) return;\n const next = new Map(this._statuses);\n next.delete(toolCallId);\n this._statuses = next;\n this._invokeOnStatusesChange();\n }\n\n // ──────────────── internal: snapshot processing ────────────────\n\n private _hasExecutableTool(toolName: string): boolean {\n const tool = this._getTools()?.[toolName];\n return tool?.execute !== undefined || tool?.streamCall !== undefined;\n }\n\n private _shouldCloseArgsStream({\n toolName,\n argsText,\n hasResult,\n }: {\n toolName: string;\n argsText: string;\n hasResult: boolean;\n }): boolean {\n if (hasResult) return true;\n if (!this._hasExecutableTool(toolName)) {\n return !this._isRunning && isArgsTextComplete(argsText);\n }\n return isArgsTextComplete(argsText);\n }\n\n private _startActiveEntry(\n toolCallId: string,\n toolName: string,\n skipExecute: boolean,\n ): ToolCallEntry {\n const toolCallController = this._controller.addToolCallPart({\n toolName,\n toolCallId,\n });\n const entry: ToolCallEntry = {\n toolName,\n controller: toolCallController,\n argsText: \"\",\n hasResult: false,\n skipExecute,\n argsComplete: false,\n };\n this._entries.set(toolCallId, entry);\n return entry;\n }\n\n /**\n * Demote every active entry back to the restored phase. Used by the\n * pipeline-restart path so that, after a fresh pipeline is built, the\n * next observed snapshot does not re-fire `streamCall` for tool calls\n * that already fired pre-death. Args / hasResult tracking is preserved\n * so signature comparisons still work.\n */\n private _demoteEntriesToRestored(): void {\n for (const [toolCallId, entry] of this._entries) {\n if (!entry.controller) continue;\n this._entries.set(toolCallId, {\n toolName: entry.toolName,\n argsText: entry.argsText,\n hasResult: entry.hasResult,\n });\n }\n }\n\n private _processArgsText(\n entry: ToolCallEntry,\n content: {\n toolCallId: string;\n toolName: string;\n argsText: string;\n result?: unknown;\n },\n ): void {\n if (!entry.controller) return;\n const hasResult = content.result !== undefined;\n\n if (content.argsText !== entry.argsText) {\n let shouldWriteArgsText = true;\n\n if (entry.argsComplete) {\n if (isEquivalentCompleteArgsText(entry.argsText, content.argsText)) {\n // A.3 — key reorder. Track new text, no re-fire needed.\n entry.argsText = content.argsText;\n shouldWriteArgsText = false;\n } else {\n // A.4 — args changed after first completion. Under the\n // \"exactly once per toolCallId\" contract we do not restart the\n // stream. The host's existing `streamCall` keeps its original\n // args view; the snapshot's new text is recorded for diffing\n // but not surfaced. Events API in a follow-up will expose this\n // to consumers that opt in.\n if (process.env.NODE_ENV !== \"production\") {\n console.warn(\n \"[ToolInvocationTracker] argsText changed after first completion; not re-firing streamCall (see EDGE_CASES.md A.4)\",\n {\n previous: entry.argsText,\n next: content.argsText,\n toolCallId: content.toolCallId,\n },\n );\n }\n shouldWriteArgsText = false;\n }\n } else if (!content.argsText.startsWith(entry.argsText)) {\n if (\n isArgsTextComplete(entry.argsText) &&\n isArgsTextComplete(content.argsText) &&\n isEquivalentCompleteArgsText(entry.argsText, content.argsText)\n ) {\n const shouldClose = this._shouldCloseArgsStream({\n toolName: content.toolName,\n argsText: content.argsText,\n hasResult,\n });\n if (shouldClose) entry.controller.argsText.close();\n entry.argsText = content.argsText;\n entry.argsComplete = shouldClose;\n shouldWriteArgsText = false;\n } else {\n // A.2 — args regressed mid-stream. Under the \"exactly once\"\n // contract we do not restart. The controller keeps whatever\n // prefix we already streamed; subsequent prefix-respecting\n // updates can still flow against it. Snapshots that never\n // re-converge to a prefix will leave the controller's args\n // view stale relative to the snapshot. Events API in a\n // follow-up will expose this to consumers that opt in.\n if (process.env.NODE_ENV !== \"production\") {\n console.warn(\n \"[ToolInvocationTracker] argsText regressed mid-stream; not restarting (see EDGE_CASES.md A.2)\",\n {\n previous: entry.argsText,\n next: content.argsText,\n toolCallId: content.toolCallId,\n },\n );\n }\n shouldWriteArgsText = false;\n }\n }\n\n if (shouldWriteArgsText && entry.controller) {\n const delta = content.argsText.slice(entry.argsText.length);\n entry.controller.argsText.append(delta);\n const shouldClose = this._shouldCloseArgsStream({\n toolName: content.toolName,\n argsText: content.argsText,\n hasResult,\n });\n if (shouldClose) entry.controller.argsText.close();\n entry.argsText = content.argsText;\n entry.argsComplete = shouldClose;\n }\n }\n\n if (!entry.argsComplete && entry.controller) {\n // ToolExecutionStream parses the streamed prefix on close, so the close\n // gates on the streamed content; a divergent snapshot (A.2) can be\n // complete while the controller still holds an incomplete stale prefix.\n const shouldClose = this._shouldCloseArgsStream({\n toolName: content.toolName,\n argsText: entry.argsText,\n hasResult,\n });\n if (shouldClose) {\n entry.controller.argsText.close();\n entry.argsComplete = true;\n }\n }\n }\n\n private _processMessages(messages: readonly ThreadMessage[]): void {\n const isRestore = this._pendingRestore;\n\n for (const message of messages) {\n if (!message || !Array.isArray((message as ThreadMessage).content)) {\n continue;\n }\n for (const content of message.content as readonly ThreadMessage[\"content\"][number][]) {\n if (!content || content.type !== \"tool-call\") continue;\n\n const existing = this._entries.get(content.toolCallId);\n\n if (isRestore) {\n // Don't overwrite an already-active entry (e.g. live tool-call\n // observed before this restore snapshot landed). Restore can\n // only seed entries the runtime has never seen.\n if (!existing?.controller) {\n this._entries.set(content.toolCallId, {\n toolName: content.toolName,\n argsText: content.argsText,\n hasResult: content.result !== undefined,\n });\n }\n if (content.messages) this._processMessages(content.messages);\n continue;\n }\n\n // Live snapshot.\n let entry = existing;\n\n if (entry && !entry.controller) {\n // Restored entry observed in a live snapshot. Promote if its\n // signature has changed; otherwise treat as still-historical.\n const signatureChanged =\n content.argsText !== entry.argsText ||\n (content.result !== undefined) !== entry.hasResult;\n if (!signatureChanged) {\n if (content.messages) this._processMessages(content.messages);\n continue;\n }\n this._entries.delete(content.toolCallId);\n entry = undefined;\n }\n\n if (!entry) {\n entry = this._startActiveEntry(\n content.toolCallId,\n content.toolName,\n content.result !== undefined,\n );\n }\n\n if (content.approval !== undefined) entry.skipExecute = true;\n\n this._processArgsText(entry, content);\n\n if (content.result !== undefined && !entry.hasResult) {\n // `entry` is in active phase from this point — either just\n // created by `_startActiveEntry`, or pre-existing with a live\n // controller. Narrow once instead of asserting at every use.\n const { controller: activeController } = entry;\n if (!activeController) continue;\n entry.hasResult = true;\n entry.argsComplete = true;\n activeController.setResponse(\n new ToolResponse({\n result: content.result as ReadonlyJSONValue,\n artifact: content.artifact as ReadonlyJSONValue | undefined,\n isError: content.isError,\n ...(content.modelContent !== undefined\n ? { modelContent: content.modelContent }\n : {}),\n }),\n );\n activeController.close();\n }\n\n if (content.messages) this._processMessages(content.messages);\n }\n }\n }\n}\n\nexport namespace ToolInvocationTracker {\n export type ExecutionStatus = ToolExecutionStatus;\n\n export type Snapshot = {\n readonly messages: readonly ThreadMessage[];\n /** Whether the producing runtime is currently streaming new output. */\n readonly isRunning: boolean;\n /**\n * Whether the producing runtime is still loading historical state.\n * When `true`, every snapshot is treated as historical (no `streamCall` /\n * `execute` fires). When `false`, processing resumes as live.\n */\n readonly isLoading?: boolean;\n };\n\n export type Callbacks = {\n /**\n * Invoked when a client-side `execute()` returns a result and the runtime\n * needs to feed it back into the conversation.\n */\n onResult: (command: AddToolResultCommand) => void;\n /**\n * Invoked whenever the per-tool-call status map changes (executing /\n * interrupt / cleared). The callback receives a fresh map; mutating the\n * argument is not supported.\n */\n onStatusesChange: (\n statuses: ReadonlyMap<string, ToolExecutionStatus>,\n ) => void;\n };\n}\n"],"mappings":";;;;AAkBA,MAAMc,oBAAoBC,OAAOC,IAAI,oCAAoC;AA8CzE,MAAMsB,sBAAsBV,aAAqB;CAC/C,IAAI;EACFW,KAAKC,MAAMZ,QAAQ;EACnB,OAAO;CACT,QAAQ;EACN,OAAO;CACT;AACF;AAEA,MAAMa,iBAAiBb,aAAqB;CAC1C,IAAI;EACF,OAAOW,KAAKC,MAAMZ,QAAQ;CAC5B,QAAQ;EACN;CACF;AACF;AAEA,MAAMe,gCAAgCC,UAAkBC,SAAiB;CACvE,MAAMC,gBAAgBL,cAAcG,QAAQ;CAC5C,MAAMG,YAAYN,cAAcI,IAAI;CACpC,IAAIC,kBAAkBJ,KAAAA,KAAaK,cAAcL,KAAAA,GAAW,OAAO;CACnE,OAAO9B,iBAAiBkC,eAAeC,SAAS;AAClD;AAEA,MAAMC,sBAAsBC,UACzBA,MAAuCnC;;;;;;;;;;;;;;;;;;;;;;;AA0B1C,IAAasC,wBAAb,MAAmC;CACjC;CACA;CAEA,2BAA4B,IAAIK,IAA2B;CAC3D,8BAA+B,IAAIA,IAOjC;CACF,6BAA8B,IAAIK,IAAY;CAC9C,oBAAwD,CAAA;CAExD,4BAAoB,IAAIL,IAAiC;CAEzD,MAA+B,IAAIS,gBAAgB;CACnD,kBAA0B;;CAG1B,gBAA+D;CAC/D,aAAqB;CAErB;;;;;;;;;CAUA,gBAAwB;CACxB,uBAA+B;CAE/BS,YACEC,UACAC,WACA;EACA,KAAKxB,YAAYuB;EACjB,KAAKtB,aAAauB;EAElB,KAAKC,cAAc;CACrB;;;;;;CAOA,gBAA8B;EAC5B,MAAM,CAACC,QAAQ/C,cAAc7B,gCAAgC;EAC7D,KAAKoE,cAAcvC;EAEnB,MAAMgD,SACJ3D,YACAF,SACAW,gBACG,KAAKmD,cAAc5D,YAAYF,SAASW,WAAW;EACxD,MAAMoD,YAAY5E,gCACV,KAAK6E,iBAAiB,SACtB,KAAKlB,IAAImB,QACfJ,OACA;GACEK,mBAAmBC,IAAYC,OAAezD,gBAC5C,KAAK0D,kBAAkBF,IAAIxD,WAAW;GACxC2D,iBAAiBH,IAAYC,OAAezD,gBAC1C,KAAK4D,gBAAgBJ,IAAIxD,WAAW;EACxC,CACF;EAEAiD,OACGY,YAAYT,SAAS,CAAC,CACtBS,YAAY,IAAIjF,6BAA6B,CAAC,CAAC,CAC/CkF,OACC,IAAIC,eAAe,EACjBC,QAAQC,UAAU;GAChB,IAAI;IACF,IAAIA,MAAM7E,SAAS,UAAU;IAC7B,KAAK8E,mBAAmBD,KAAK;GAC/B,SAASE,KAAK;IACZC,QAAQC,MACN,wDACAF,GACF;GACF;EACF,EACF,CAAC,CACH,CAAC,CACAG,OAAOH,QAAQ;GACdC,QAAQC,MACN,gGACAF,GACF;GACA,KAAKxB,gBAAgB;EACvB,CAAC;CACL;;;;;CAQA,SAAgB6B,UAAgD;EAC9D,IAAI;GAKF,IAAI,KAAK7B,eAAe;IACtB,IAAI,KAAKC,sBAEP;IAEF,KAAKA,uBAAuB;IAC5B,KAAKD,gBAAgB;IACrB,KAAK8B,yBAAyB;IAC9B,KAAK1C,WAAW2C,MAAM;IACtB,KAAKvC,MAAM,IAAIC,gBAAgB;IAC/B,KAAKY,cAAc;GAErB;GAMA,IACE,KAAKV,iBACL,KAAKA,cAAcqC,aAAaH,SAASG,YACzC,KAAKrC,cAAcsC,cAAcJ,SAASI,aAC1C,KAAKtC,cAAcuC,cAAcL,SAASK,WAE1C;GAOF,IAD2BL,SAASK,cAAc,MAEhD,KAAKxC,kBAAkB;GAMzB,MAAM0C,oBAAoB,KAAKvC;GAC/B,KAAKA,aAAagC,SAASI;GAC3B,IAAI;IACF,KAAKI,iBAAiBR,SAASG,QAAQ;GACzC,SAASR,KAAK;IACZ,KAAK3B,aAAauC;IAClB,MAAMZ;GACR;GACA,KAAK7B,gBAAgBkC;GACrB,KAAKnC,kBAAkB;EACzB,SAAS8B,KAAK;GACZC,QAAQC,MACN,6DACAF,GACF;EACF;CACF;;;;;;CAOA,QAAqB;EACnB,IAAI;GACF,KAAK9B,kBAAkB;GACvB,KAAKX,SAASgD,MAAM;GACpB,KAAKpC,gBAAgB;GACrB,KAAU4C,MAAM;GAMhB,IAAI,KAAKhD,UAAUiD,OAAO,GAAG;IAC3B,KAAKjD,4BAAY,IAAIP,IAAI;IACzB,KAAKyD,wBAAwB;GAC/B;EACF,SAASjB,KAAK;GACZC,QAAQC,MAAM,wCAAwCF,GAAG;EAC3D;CACF;;;;;CAMA,QAA8B;EAC5B,IAAI;GACF,KAAKvC,YAAY0D,SAAS,EAAEzD,aAAa;IACvC,IAAI;KACFA,uBAAO,IAAI0D,MAAM,wBAAwB,CAAC;IAC5C,QAAQ,CAEN;GAEJ,CAAC;GACD,KAAK3D,YAAY8C,MAAM;GAEvB,KAAKvC,IAAI+C,MAAM;GACf,KAAK/C,MAAM,IAAIC,gBAAgB;GAE/B,IAAI,KAAKL,WAAWoD,SAAS,GAC3B,OAAOE,QAAQ9E,QAAQ;GAEzB,MAAMF,eAAe,IAAI2B,IAAI,KAAKD,UAAU;GAC5C,OAAO,IAAIsD,SAAe9E,YAAY;IACpC,KAAK0B,kBAAkBuD,KAAK;KAAEnF;KAAcE;IAAQ,CAAC;GACvD,CAAC;EACH,SAAS4D,KAAK;GACZC,QAAQC,MAAM,wCAAwCF,GAAG;GACzD,OAAOkB,QAAQ9E,QAAQ;EACzB;CACF;;;;;;;CAQA,OAAchB,YAAoBF,SAA2B;EAC3D,IAAI;GACF,MAAMqG,WAAW,KAAK9D,YAAY+D,IAAIpG,UAAU;GAChD,IAAI,CAACmG,UAAU,OAAO;GACtB,KAAK9D,YAAYgE,OAAOrG,UAAU;GAClC,KAAKsG,WAAWtG,YAAY,EAAEH,MAAM,YAAY,CAAC;GACjDsG,SAASnF,QAAQlB,OAAO;GACxB,OAAO;EACT,SAAS8E,KAAK;GACZC,QAAQC,MAAM,yCAAyCF,GAAG;GAC1D,OAAO;EACT;CACF;;;;;;;CAQA,cAA+D;EAC7D,OAAO,KAAKjC;CACd;CAIA,mBAA6D;EAC3D,MAAM8D,QAAQ,KAAKzE,UAAU;EAC7B,IAAI,CAACyE,OAAO,OAAOpF,KAAAA;EAEnB,OAAOqF,OAAOC,YACZD,OAAOE,QAAQH,KAAK,CAAC,CAACI,KAAK,CAACC,MAAMC,UAAU;GAC1C,MAAMC,UAAUD,KAAKC;GACrB,MAAMC,aAAaF,KAAKE;GACxB,IAAID,YAAY3F,KAAAA,KAAa4F,eAAe5F,KAAAA,GAC1C,OAAO,CAACyF,MAAMC,IAAI;GA+BpB,OAAO,CAACD,MAAMI;IA5BZ,GAAGH;IACH,GAAIC,YAAY3F,KAAAA,KAAa,EAC3B2F,UAAU,GAAG,CAACG,MAAMC,aAAyC;KAC3D,MAAM3G,cAAckB,mBAAmByF,OAAO;KAC9C,MAAME,QAAQ,KAAKC,kBACjBH,QAAQpH,YACRS,WACF;KACA,IAAI,CAAC6G,SAASA,MAAM5G,aAClB,OAAO,IAAIoF,cAAc,CAAC,CAAC;KAE7B,OAAOkB,QAAQG,MAAMC,OAAO;IAC9B,EACF;IACA,GAAIH,eAAe5F,KAAAA,KAAa,EAC9B4F,aACE,GAAG,CAACO,QAAQJ,aACT;KACH,MAAM3G,cAAckB,mBAAmByF,OAAO;KAK9C,IAAI,CAJU,KAAKG,kBACjBH,QAAQpH,YACRS,WAEG6G,GAAO;KACZ,OAAOL,WAAWO,QAAQJ,OAAO;IACnC,EACF;GAEYF,CAAW;EAC3B,CAAC,CACH;CACF;CAEA,kBACElH,YACAS,aAC2B;EAC3B,IAAIA,gBAAgBY,KAAAA,GAAW,OAAOA,KAAAA;EACtC,MAAMiG,QAAQ,KAAKnF,SAASiE,IAAIpG,UAAU;EAC1C,IAAI,CAACsH,OAAO3G,YAAY,OAAOU,KAAAA;EAC/B,IAAIiG,MAAM7G,gBAAgBY,KAAAA,GAAWiG,MAAM7G,cAAcA;EACzD,OAAO6G,MAAM7G,gBAAgBA,cAAc6G,QAAQjG,KAAAA;CACrD;CAIA,cACErB,YACAF,SACAW,aACkB;EAClB,OAAO,IAAIqF,SAAkB9E,SAASsB,WAAW;GAG/C,MAAMgF,QAAQ,KAAKnF,SAASiE,IAAIpG,UAAU;GAC1C,IAAI,CAACsH,OAAO3G,cAAc2G,MAAM7G,gBAAgBA,aAAa;IAC3D6B,uBAAO,IAAI0D,MAAM,wBAAwB,CAAC;IAC1C;GACF;GACA,MAAMzE,WAAW,KAAKc,YAAY+D,IAAIpG,UAAU;GAChD,IAAIuB,UACF,IAAI;IACFA,SAASe,uBACP,IAAI0D,MAAM,qDAAqD,CACjE;GACF,QAAQ,CACN;GAGJ,KAAK3D,YAAYoF,IAAIzH,YAAY;IAClBS;IACbO;IACAsB;GACF,CAAC;GACD,KAAKgE,WAAWtG,YAAY;IAC1BH,MAAM;IACNC,SAAS;KAAED,MAAM;KAASC;IAAQ;GACpC,CAAC;EACH,CAAC;CACH;CAEA,kBACEE,YACAS,aACM;EACN,IAAI,CAAC,KAAK8G,kBAAkBvH,YAAYS,WAAW,GAAG;EAEtD,IADc,KAAK0B,SAASiE,IAAIpG,UAC5BsH,CAAK,CAAC5G,aAAa;EAEvB,KAAK8B,WAAWkF,IAAIjH,WAAY;EAChC,KAAK6F,WAAWtG,YAAY,EAAEH,MAAM,YAAY,CAAC;CACnD;CAEA,gBACEG,YACAS,aACM;EACN,IAAIA,gBAAgBY,KAAAA,KAAa,CAAC,KAAKmB,WAAW6D,OAAO5F,WAAW,GAClE;EAGF,IADc,KAAK0B,SAASiE,IAAIpG,UAC5BsH,CAAK,EAAE7G,gBAAgBA,aAAa,KAAKkH,cAAc3H,UAAU;EAErE,MAAM4H,UAA6B,CAAA;EACnC,KAAKlF,kBAAkBqD,SAAS,EAAEjF,cAAcE,cAAc;GAC5D,IAAI,CAAC,GAAGF,YAAY,CAAC,CAAC+G,MAAM5D,OAAO,KAAKzB,WAAWsF,IAAI7D,EAAE,CAAC,GAAG;IAC3D2D,QAAQ3B,KAAK;KAAEnF;KAAcE;IAAQ,CAAC;IACtC;GACF;GACA,IAAI;IACFA,QAAQ;GACV,QAAQ,CACN;EAEJ,CAAC;EACD,KAAK0B,kBAAkBqF,SAAS;EAChC,KAAKrF,kBAAkBuD,KAAK,GAAG2B,OAAO;CACxC;CAEA,mBAA2BlD,OAOlB;EACP,MAAM1E,aAAa0E,MAAMsD,KAAKhI;EAC9B,MAAMS,cAAckB,mBAAmB+C,KAAK;EAC5C,MAAM4C,QAAQ,KAAKnF,SAASiE,IAAIpG,UAAU;EAE1C,IAAI,CAACsH,SAASA,MAAM7G,gBAAgBA,aAAa;EAIjD,IAAI6G,OAAO9G,WAAW;EACtB,IAAI8G,MAAM5G,aAAa;EAEvB,KAAKuH,gBAAgB;GACnBpI,MAAM;GACNG;GACAC,UAAUyE,MAAMsD,KAAK/H;GACrBC,QAAQwE,MAAMxE;GACdC,SAASuE,MAAMvE;GACf,GAAIuE,MAAMtE,aAAaiB,KAAAA,KAAa,EAAEjB,UAAUsE,MAAMtE,SAAS;GAC/D,GAAIsE,MAAMrE,iBAAiBgB,KAAAA,KAAa,EACtChB,cAAcqE,MAAMrE,aACtB;EACF,CAAC;CACH;CAIA,gBAAwB6H,SAAqC;EAC3D,IAAI;GACF,KAAKjG,WAAWkG,SAASD,OAAO;EAClC,SAAStD,KAAK;GACZC,QAAQC,MACN,mEACAF,GACF;EACF;CACF;CAEA,0BAAwC;EACtC,IAAI;GACF,KAAK3C,WAAWmG,iBAAiB,KAAKzF,SAAS;EACjD,SAASiC,KAAK;GACZC,QAAQC,MACN,yFACAF,GACF;EACF;CACF;CAIA,WAAmB5E,YAAoBqI,QAAmC;EACxE,MAAM7G,OAAO,IAAIY,IAAI,KAAKO,SAAS;EACnCnB,KAAKiG,IAAIzH,YAAYqI,MAAM;EAC3B,KAAK1F,YAAYnB;EACjB,KAAKqE,wBAAwB;CAC/B;CAEA,cAAsB7F,YAA0B;EAC9C,IAAI,CAAC,KAAK2C,UAAUmF,IAAI9H,UAAU,GAAG;EACrC,MAAMwB,OAAO,IAAIY,IAAI,KAAKO,SAAS;EACnCnB,KAAK6E,OAAOrG,UAAU;EACtB,KAAK2C,YAAYnB;EACjB,KAAKqE,wBAAwB;CAC/B;CAIA,mBAA2B5F,UAA2B;EACpD,MAAM8G,OAAO,KAAK/E,UAAU,CAAC,GAAG/B;EAChC,OAAO8G,MAAMC,YAAY3F,KAAAA,KAAa0F,MAAME,eAAe5F,KAAAA;CAC7D;CAEA,uBAA+B,EAC7BpB,UACAM,UACAC,aAKU;EACV,IAAIA,WAAW,OAAO;EACtB,IAAI,CAAC,KAAK8H,mBAAmBrI,QAAQ,GACnC,OAAO,CAAC,KAAKgD,cAAchC,mBAAmBV,QAAQ;EAExD,OAAOU,mBAAmBV,QAAQ;CACpC;CAEA,kBACEP,YACAC,UACAS,aACe;EAKf,MAAM4G,QAAuB;GAC3BrH;GACAU,YANyB,KAAKuC,YAAYwF,gBAAgB;IAC1DzI;IACAD;GACF,CAGcyI;GACZlI,UAAU;GACVC,WAAW;GACXE;GACAE,cAAc;EAChB;EACA,KAAKuB,SAASsF,IAAIzH,YAAYsH,KAAK;EACnC,OAAOA;CACT;;;;;;;;CASA,2BAAyC;EACvC,KAAK,MAAM,CAACtH,YAAYsH,UAAU,KAAKnF,UAAU;GAC/C,IAAI,CAACmF,MAAM3G,YAAY;GACvB,KAAKwB,SAASsF,IAAIzH,YAAY;IAC5BC,UAAUqH,MAAMrH;IAChBM,UAAU+G,MAAM/G;IAChBC,WAAW8G,MAAM9G;GACnB,CAAC;EACH;CACF;CAEA,iBACE8G,OACAsB,SAMM;EACN,IAAI,CAACtB,MAAM3G,YAAY;EACvB,MAAMH,YAAYoI,QAAQ1I,WAAWmB,KAAAA;EAErC,IAAIuH,QAAQrI,aAAa+G,MAAM/G,UAAU;GACvC,IAAIsI,sBAAsB;GAE1B,IAAIvB,MAAM1G,cAAc;IACtB,IAAIU,6BAA6BgG,MAAM/G,UAAUqI,QAAQrI,QAAQ,GAAG;KAElE+G,MAAM/G,WAAWqI,QAAQrI;KACzBsI,sBAAsB;IACxB,OAAO;KAOL,IAAIlK,QAAQC,IAAIC,aAAa,cAC3BgG,QAAQiE,KACN,qHACA;MACEvH,UAAU+F,MAAM/G;MAChBiB,MAAMoH,QAAQrI;MACdP,YAAY4I,QAAQ5I;KACtB,CACF;KAEF6I,sBAAsB;IACxB;GACF,OAAO,IAAI,CAACD,QAAQrI,SAASwI,WAAWzB,MAAM/G,QAAQ,GAAG;IACvD,IACEU,mBAAmBqG,MAAM/G,QAAQ,KACjCU,mBAAmB2H,QAAQrI,QAAQ,KACnCe,6BAA6BgG,MAAM/G,UAAUqI,QAAQrI,QAAQ,GAC7D;KACA,MAAMyI,cAAc,KAAKT,uBAAuB;MAC9CtI,UAAU2I,QAAQ3I;MAClBM,UAAUqI,QAAQrI;MAClBC;KACF,CAAC;KACD,IAAIwI,aAAa1B,MAAM3G,WAAWJ,SAAS0I,MAAM;KACjD3B,MAAM/G,WAAWqI,QAAQrI;KACzB+G,MAAM1G,eAAeoI;KACrBH,sBAAsB;IACxB,OAAO;KAQL,IAAIlK,QAAQC,IAAIC,aAAa,cAC3BgG,QAAQiE,KACN,iGACA;MACEvH,UAAU+F,MAAM/G;MAChBiB,MAAMoH,QAAQrI;MACdP,YAAY4I,QAAQ5I;KACtB,CACF;KAEF6I,sBAAsB;IACxB;GACF;GAEA,IAAIA,uBAAuBvB,MAAM3G,YAAY;IAC3C,MAAMuI,QAAQN,QAAQrI,SAAS4I,MAAM7B,MAAM/G,SAASwH,MAAM;IAC1DT,MAAM3G,WAAWJ,SAAS6I,OAAOF,KAAK;IACtC,MAAMF,cAAc,KAAKT,uBAAuB;KAC9CtI,UAAU2I,QAAQ3I;KAClBM,UAAUqI,QAAQrI;KAClBC;IACF,CAAC;IACD,IAAIwI,aAAa1B,MAAM3G,WAAWJ,SAAS0I,MAAM;IACjD3B,MAAM/G,WAAWqI,QAAQrI;IACzB+G,MAAM1G,eAAeoI;GACvB;EACF;EAEA,IAAI,CAAC1B,MAAM1G,gBAAgB0G,MAAM3G,YAIX;OAAA,KAAK4H,uBAAuB;IAC9CtI,UAAU2I,QAAQ3I;IAClBM,UAAU+G,MAAM/G;IAChBC;GACF,CACIwI,GAAa;IACf1B,MAAM3G,WAAWJ,SAAS0I,MAAM;IAChC3B,MAAM1G,eAAe;GACvB;;CAEJ;CAEA,iBAAyBwE,UAA0C;EACjE,MAAMiE,YAAY,KAAKvG;EAEvB,KAAK,MAAMwG,WAAWlE,UAAU;GAC9B,IAAI,CAACkE,WAAW,CAACC,MAAMC,QAASF,QAA0BV,OAAO,GAC/D;GAEF,KAAK,MAAMA,WAAWU,QAAQV,SAAwD;IACpF,IAAI,CAACA,WAAWA,QAAQ/I,SAAS,aAAa;IAE9C,MAAM4J,WAAW,KAAKtH,SAASiE,IAAIwC,QAAQ5I,UAAU;IAErD,IAAIqJ,WAAW;KAIb,IAAI,CAACI,UAAU9I,YACb,KAAKwB,SAASsF,IAAImB,QAAQ5I,YAAY;MACpCC,UAAU2I,QAAQ3I;MAClBM,UAAUqI,QAAQrI;MAClBC,WAAWoI,QAAQ1I,WAAWmB,KAAAA;KAChC,CAAC;KAEH,IAAIuH,QAAQxD,UAAU,KAAKK,iBAAiBmD,QAAQxD,QAAQ;KAC5D;IACF;IAGA,IAAIkC,QAAQmC;IAEZ,IAAInC,SAAS,CAACA,MAAM3G,YAAY;KAM9B,IAAI,EAFFiI,QAAQrI,aAAa+G,MAAM/G,YAC1BqI,QAAQ1I,WAAWmB,KAAAA,MAAeiG,MAAM9G,YACpB;MACrB,IAAIoI,QAAQxD,UAAU,KAAKK,iBAAiBmD,QAAQxD,QAAQ;MAC5D;KACF;KACA,KAAKjD,SAASkE,OAAOuC,QAAQ5I,UAAU;KACvCsH,QAAQjG,KAAAA;IACV;IAEA,IAAI,CAACiG,OACHA,QAAQ,KAAKkB,kBACXI,QAAQ5I,YACR4I,QAAQ3I,UACR2I,QAAQ1I,WAAWmB,KAAAA,CACrB;IAGF,IAAIuH,QAAQe,aAAatI,KAAAA,GAAWiG,MAAM5G,cAAc;IAExD,KAAKiI,iBAAiBrB,OAAOsB,OAAO;IAEpC,IAAIA,QAAQ1I,WAAWmB,KAAAA,KAAa,CAACiG,MAAM9G,WAAW;KAIpD,MAAM,EAAEG,YAAYiJ,qBAAqBtC;KACzC,IAAI,CAACsC,kBAAkB;KACvBtC,MAAM9G,YAAY;KAClB8G,MAAM1G,eAAe;KACrBgJ,iBAAiBC,YACf,IAAI7K,aAAa;MACfkB,QAAQ0I,QAAQ1I;MAChBE,UAAUwI,QAAQxI;MAClBD,SAASyI,QAAQzI;MACjB,GAAIyI,QAAQvI,iBAAiBgB,KAAAA,IACzB,EAAEhB,cAAcuI,QAAQvI,aAAa,IACrC,CAAC;KACP,CAAC,CACH;KACAuJ,iBAAiBX,MAAM;IACzB;IAEA,IAAIL,QAAQxD,UAAU,KAAKK,iBAAiBmD,QAAQxD,QAAQ;GAC9D;EACF;CACF;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"external-thread.d.ts","names":[],"sources":["../../../src/store/clients/external-thread.ts"],"mappings":";;;;;;;;;;KAyDY,wBAAwB;EAClC;;KAgBU;EACV,mBAAmB;EACnB;EACA;EACA,QAAQ;EACR;;;;;;;EAOA;;;;;EAKA,SAAS,SAAS;EAClB,UAAU,SAAS;EACnB,YAAY;EACZ;EACA;EACA;;;;;;;EAOA,yBAAyB;EACzB,oBAAoB,SAAS;;EAE7B,qBAAqB,SAAS;EAC9B,wBAAwB;EACxB,oBAAoB;EACpB,kBAAkB;EAClB,gBAAgB;;EAEhB,QAAQ;;EAER,WAAW;;EAEX,2BAA2B,SAAS;;cAqkCzB,4CAAc,SAAA,yBAAA"}
1
+ {"version":3,"file":"external-thread.d.ts","names":[],"sources":["../../../src/store/clients/external-thread.ts"],"mappings":";;;;;;;;;;KA6DY,wBAAwB;EAClC;;KAgBU;EACV,mBAAmB;EACnB;EACA;EACA,QAAQ;EACR;;;;;;;EAOA;;;;;EAKA,SAAS,SAAS;EAClB,UAAU,SAAS;EACnB,YAAY;EACZ;EACA;EACA;;;;;;;EAOA,yBAAyB;EACzB,oBAAoB,SAAS;;EAE7B,qBAAqB,SAAS;EAC9B,wBAAwB;EACxB,oBAAoB;EACpB,kBAAkB;EAClB,gBAAgB;;EAEhB,QAAQ;;EAER,WAAW;;EAEX,2BAA2B,SAAS;;cAohCzB,4CAAc,SAAA,yBAAA"}
@@ -3,6 +3,7 @@ import { getThreadMessageText } from "../../utils/text.js";
3
3
  import { toMessagePartStatus } from "../../utils/normalizePartStatus.js";
4
4
  import { resolveToolApprovalResponse } from "../../runtime/utils/resolveToolApprovalResponse.js";
5
5
  import { isCreateAttachment } from "../../types/attachment.js";
6
+ import { AttachmentAddOperations, drainAttachmentAdd } from "../../runtime/utils/attachment-add-operations.js";
6
7
  import { ThreadSuggestions } from "./suggestions.js";
7
8
  import { ModelContext } from "./model-context-client.js";
8
9
  import { DataRenderers } from "../../react/client/DataRenderers.js";
@@ -353,39 +354,6 @@ const useAttachmentResource = ({ attachment, onRemove }) => {
353
354
  };
354
355
  };
355
356
  const AttachmentResource = resource(useAttachmentResource);
356
- var AttachmentAddOperations = class {
357
- operations = /* @__PURE__ */ new Set();
358
- start() {
359
- const operation = {
360
- cancelled: false,
361
- attachmentIds: /* @__PURE__ */ new Set()
362
- };
363
- this.operations.add(operation);
364
- return operation;
365
- }
366
- accept(operation, attachmentId) {
367
- if (operation.cancelled) return false;
368
- operation.attachmentIds.add(attachmentId);
369
- return true;
370
- }
371
- finish(operation) {
372
- this.operations.delete(operation);
373
- }
374
- isCancelled(operation) {
375
- return operation.cancelled;
376
- }
377
- cancel(attachmentId) {
378
- for (const operation of [...this.operations]) {
379
- if (!operation.attachmentIds.has(attachmentId)) continue;
380
- operation.cancelled = true;
381
- this.operations.delete(operation);
382
- }
383
- }
384
- cancelAll() {
385
- for (const operation of this.operations) operation.cancelled = true;
386
- this.operations.clear();
387
- }
388
- };
389
357
  const useQueueItemClient = ({ item, onMove, onRemove }) => {
390
358
  return {
391
359
  getState: () => item,
@@ -398,11 +366,6 @@ const useQueueItemClient = ({ item, onMove, onRemove }) => {
398
366
  };
399
367
  };
400
368
  const QueueItemClient = resource(useQueueItemClient);
401
- const drainAdapterAdd = async (result, upsert) => {
402
- if (Symbol.asyncIterator in result) {
403
- for await (const attachment of result) if (!upsert(attachment)) break;
404
- } else upsert(await result);
405
- };
406
369
  const useLiveState = (initial) => {
407
370
  const $ = c(3);
408
371
  const [state, setState] = useState(initial);
@@ -429,7 +392,7 @@ const useLiveState = (initial) => {
429
392
  return t1;
430
393
  };
431
394
  const useComposerClientResource = (t0) => {
432
- const $ = c(105);
395
+ const $ = c(108);
433
396
  const { type, canCancel, isRunning: t1, isSendDisabled: t2, onCancel, onBeginEdit, onSend, message, queue, attachmentAdapter } = t0;
434
397
  const isRunning = t1 === void 0 ? false : t1;
435
398
  const isSendDisabled = t2 === void 0 ? false : t2;
@@ -611,7 +574,7 @@ const useComposerClientResource = (t0) => {
611
574
  if (!isCreateAttachment(fileOrAttachment) && attachmentAdapter) {
612
575
  const operation = attachmentAddOperations.start();
613
576
  try {
614
- await drainAdapterAdd(attachmentAdapter.add({ file: fileOrAttachment }), (attachment_1) => {
577
+ await drainAttachmentAdd(attachmentAdapter.add({ file: fileOrAttachment }), (attachment_1) => {
615
578
  if (!attachmentAddOperations.accept(operation, attachment_1.id)) return false;
616
579
  upsertAttachment(attachment_1);
617
580
  return true;
@@ -756,18 +719,27 @@ const useComposerClientResource = (t0) => {
756
719
  $[78] = t19;
757
720
  } else t19 = $[78];
758
721
  let t20;
759
- if ($[79] !== onCancel || $[80] !== setIsEditing || $[81] !== type) {
722
+ if ($[79] !== attachmentsRef || $[80] !== onCancel || $[81] !== removePendingAttachments || $[82] !== setAttachments || $[83] !== setIsEditing || $[84] !== type) {
760
723
  t20 = () => {
724
+ if (type === "edit") {
725
+ attachmentAddOperations.cancelAll();
726
+ const removed_2 = attachmentsRef.current;
727
+ setAttachments([]);
728
+ removePendingAttachments(removed_2).catch(_temp7);
729
+ }
761
730
  onCancel?.();
762
731
  if (type === "edit") setIsEditing(false);
763
732
  };
764
- $[79] = onCancel;
765
- $[80] = setIsEditing;
766
- $[81] = type;
767
- $[82] = t20;
768
- } else t20 = $[82];
733
+ $[79] = attachmentsRef;
734
+ $[80] = onCancel;
735
+ $[81] = removePendingAttachments;
736
+ $[82] = setAttachments;
737
+ $[83] = setIsEditing;
738
+ $[84] = type;
739
+ $[85] = t20;
740
+ } else t20 = $[85];
769
741
  let t21;
770
- if ($[83] !== isEditingRef || $[84] !== onBeginEdit || $[85] !== setIsEditing || $[86] !== type || $[87] !== updateFromMessage) {
742
+ if ($[86] !== isEditingRef || $[87] !== onBeginEdit || $[88] !== setIsEditing || $[89] !== type || $[90] !== updateFromMessage) {
771
743
  t21 = () => {
772
744
  onBeginEdit?.();
773
745
  if (type === "thread") return;
@@ -775,24 +747,24 @@ const useComposerClientResource = (t0) => {
775
747
  setIsEditing(true);
776
748
  updateFromMessage();
777
749
  };
778
- $[83] = isEditingRef;
779
- $[84] = onBeginEdit;
780
- $[85] = setIsEditing;
781
- $[86] = type;
782
- $[87] = updateFromMessage;
783
- $[88] = t21;
784
- } else t21 = $[88];
750
+ $[86] = isEditingRef;
751
+ $[87] = onBeginEdit;
752
+ $[88] = setIsEditing;
753
+ $[89] = type;
754
+ $[90] = updateFromMessage;
755
+ $[91] = t21;
756
+ } else t21 = $[91];
785
757
  let t22;
786
- if ($[89] !== queueItemClients) {
758
+ if ($[92] !== queueItemClients) {
787
759
  t22 = (selector_0) => {
788
760
  if ("id" in selector_0) return queueItemClients.get({ key: selector_0.id });
789
761
  return queueItemClients.get(selector_0);
790
762
  };
791
- $[89] = queueItemClients;
792
- $[90] = t22;
793
- } else t22 = $[90];
763
+ $[92] = queueItemClients;
764
+ $[93] = t22;
765
+ } else t22 = $[93];
794
766
  let t23;
795
- if ($[91] !== setQuote || $[92] !== setRole || $[93] !== setRunConfig || $[94] !== setText || $[95] !== t14 || $[96] !== t15 || $[97] !== t16 || $[98] !== t17 || $[99] !== t18 || $[100] !== t19 || $[101] !== t20 || $[102] !== t21 || $[103] !== t22) {
767
+ if ($[94] !== setQuote || $[95] !== setRole || $[96] !== setRunConfig || $[97] !== setText || $[98] !== t14 || $[99] !== t15 || $[100] !== t16 || $[101] !== t17 || $[102] !== t18 || $[103] !== t19 || $[104] !== t20 || $[105] !== t21 || $[106] !== t22) {
796
768
  t23 = {
797
769
  getState: t14,
798
770
  setText,
@@ -805,26 +777,26 @@ const useComposerClientResource = (t0) => {
805
777
  send: t19,
806
778
  cancel: t20,
807
779
  beginEdit: t21,
808
- startDictation: _temp7,
809
- stopDictation: _temp8,
780
+ startDictation: _temp8,
781
+ stopDictation: _temp9,
810
782
  setQuote,
811
783
  queueItem: t22
812
784
  };
813
- $[91] = setQuote;
814
- $[92] = setRole;
815
- $[93] = setRunConfig;
816
- $[94] = setText;
817
- $[95] = t14;
818
- $[96] = t15;
819
- $[97] = t16;
820
- $[98] = t17;
821
- $[99] = t18;
822
- $[100] = t19;
823
- $[101] = t20;
824
- $[102] = t21;
825
- $[103] = t22;
826
- $[104] = t23;
827
- } else t23 = $[104];
785
+ $[94] = setQuote;
786
+ $[95] = setRole;
787
+ $[96] = setRunConfig;
788
+ $[97] = setText;
789
+ $[98] = t14;
790
+ $[99] = t15;
791
+ $[100] = t16;
792
+ $[101] = t17;
793
+ $[102] = t18;
794
+ $[103] = t19;
795
+ $[104] = t20;
796
+ $[105] = t21;
797
+ $[106] = t22;
798
+ $[107] = t23;
799
+ } else t23 = $[107];
828
800
  return t23;
829
801
  };
830
802
  const ComposerClientResource = resource(useComposerClientResource);
@@ -1157,12 +1129,12 @@ const useExternalThread = (t0) => {
1157
1129
  const hasSpeech = !!speechAdapter;
1158
1130
  let t25;
1159
1131
  if ($[59] !== messageClients.state) {
1160
- t25 = messageClients.state.map(_temp9);
1132
+ t25 = messageClients.state.map(_temp0);
1161
1133
  $[59] = messageClients.state;
1162
1134
  $[60] = t25;
1163
1135
  } else t25 = $[60];
1164
1136
  const messageStates = t25;
1165
- const t26 = messages.length === 0;
1137
+ const t26 = messages.length === 0 && !isLoading;
1166
1138
  let t27;
1167
1139
  if ($[61] !== hasAttachments || $[62] !== hasBranches || $[63] !== hasCancel || $[64] !== hasEdit || $[65] !== hasFeedback || $[66] !== hasQueue || $[67] !== hasRefetchThread || $[68] !== hasReload || $[69] !== hasSpeech) {
1168
1140
  t27 = {
@@ -1326,24 +1298,24 @@ const useExternalThread = (t0) => {
1326
1298
  composer: t31,
1327
1299
  suggestions: t32,
1328
1300
  append: t33,
1329
- deleteMessage: _temp0,
1301
+ deleteMessage: _temp1,
1330
1302
  startRun: t34,
1331
1303
  resumeRun: t35,
1332
1304
  cancelRun: handleCancelRun,
1333
1305
  ...t36,
1334
1306
  importExternalState: t37,
1335
- getModelContext: _temp1,
1336
- export: _temp10,
1337
- import: _temp11,
1338
- reset: _temp12,
1307
+ getModelContext: _temp10,
1308
+ export: _temp11,
1309
+ import: _temp12,
1310
+ reset: _temp13,
1339
1311
  message: t38,
1340
1312
  stopSpeaking: speechController.stop,
1341
- connectVoice: _temp13,
1342
- disconnectVoice: _temp14,
1343
- getVoiceVolume: _temp15,
1344
- subscribeVoiceVolume: _temp17,
1345
- muteVoice: _temp18,
1346
- unmuteVoice: _temp19
1313
+ connectVoice: _temp14,
1314
+ disconnectVoice: _temp15,
1315
+ getVoiceVolume: _temp16,
1316
+ subscribeVoiceVolume: _temp18,
1317
+ muteVoice: _temp19,
1318
+ unmuteVoice: _temp20
1347
1319
  };
1348
1320
  $[103] = handleCancelRun;
1349
1321
  $[104] = speechController.stop;
@@ -1408,37 +1380,40 @@ function _temp5(part_0) {
1408
1380
  function _temp6(a_0) {
1409
1381
  return a_0.status.type !== "complete";
1410
1382
  }
1411
- function _temp7() {}
1383
+ function _temp7(error_1) {
1384
+ console.error("Failed to remove cancelled edit attachments", error_1);
1385
+ }
1412
1386
  function _temp8() {}
1413
- function _temp9(s, idx, arr) {
1387
+ function _temp9() {}
1388
+ function _temp0(s, idx, arr) {
1414
1389
  return {
1415
1390
  ...s,
1416
1391
  isLast: idx === arr.length - 1
1417
1392
  };
1418
1393
  }
1419
- function _temp0() {}
1420
- function _temp1() {
1394
+ function _temp1() {}
1395
+ function _temp10() {
1421
1396
  return {
1422
1397
  tools: {},
1423
1398
  config: {}
1424
1399
  };
1425
1400
  }
1426
- function _temp10() {
1401
+ function _temp11() {
1427
1402
  return { messages: [] };
1428
1403
  }
1429
- function _temp11() {}
1430
1404
  function _temp12() {}
1431
1405
  function _temp13() {}
1432
1406
  function _temp14() {}
1433
- function _temp15() {
1407
+ function _temp15() {}
1408
+ function _temp16() {
1434
1409
  return 0;
1435
1410
  }
1436
- function _temp16() {}
1437
- function _temp17() {
1438
- return _temp16;
1411
+ function _temp17() {}
1412
+ function _temp18() {
1413
+ return _temp17;
1439
1414
  }
1440
- function _temp18() {}
1441
1415
  function _temp19() {}
1416
+ function _temp20() {}
1442
1417
  //#endregion
1443
1418
  export { ExternalThread };
1444
1419