@assistant-ui/core 0.3.16 → 0.3.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (305) hide show
  1. package/dist/index.d.ts +2 -2
  2. package/dist/index.js +2 -2
  3. package/dist/index.js.map +1 -1
  4. package/dist/internal.d.ts +2 -1
  5. package/dist/internal.js +2 -1
  6. package/dist/model-context/frame/provider.d.ts +3 -0
  7. package/dist/model-context/frame/provider.d.ts.map +1 -1
  8. package/dist/model-context/frame/provider.js +71 -21
  9. package/dist/model-context/frame/provider.js.map +1 -1
  10. package/dist/model-context/tool-call-text.d.ts +26 -0
  11. package/dist/model-context/tool-call-text.d.ts.map +1 -0
  12. package/dist/model-context/tool-call-text.js +18 -0
  13. package/dist/model-context/tool-call-text.js.map +1 -0
  14. package/dist/react/RuntimeAdapter.js +1 -1
  15. package/dist/react/adapters/LocalStorageThreadListAdapter.d.ts.map +1 -1
  16. package/dist/react/adapters/LocalStorageThreadListAdapter.js +1 -1
  17. package/dist/react/adapters/LocalStorageThreadListAdapter.js.map +1 -1
  18. package/dist/react/client/AdaptedRemoteThread.js +1 -1
  19. package/dist/react/client/InMemoryThreadList.js +1 -1
  20. package/dist/react/client/Interactables.js +1 -1
  21. package/dist/react/client/RemoteThreadList.d.ts +15 -0
  22. package/dist/react/client/RemoteThreadList.d.ts.map +1 -1
  23. package/dist/react/client/RemoteThreadList.js +240 -138
  24. package/dist/react/client/RemoteThreadList.js.map +1 -1
  25. package/dist/react/client/Tools.d.ts.map +1 -1
  26. package/dist/react/client/Tools.js +6 -2
  27. package/dist/react/client/Tools.js.map +1 -1
  28. package/dist/react/index.d.ts +2 -1
  29. package/dist/react/index.js +2 -1
  30. package/dist/react/interactables-legacy/Interactables.js +4 -4
  31. package/dist/react/interactables-legacy/Interactables.js.map +1 -1
  32. package/dist/react/model-context/toolbox.d.ts +2 -14
  33. package/dist/react/model-context/toolbox.d.ts.map +1 -1
  34. package/dist/react/model-context/toolbox.js +1 -13
  35. package/dist/react/model-context/toolbox.js.map +1 -1
  36. package/dist/react/model-context/useAssistantContext.js +37 -7
  37. package/dist/react/model-context/useAssistantContext.js.map +1 -1
  38. package/dist/react/model-context/useAuiToolOverrides.js +31 -5
  39. package/dist/react/model-context/useAuiToolOverrides.js.map +1 -1
  40. package/dist/react/model-context/useInlineRender.d.ts.map +1 -1
  41. package/dist/react/model-context/useInlineRender.js +10 -36
  42. package/dist/react/model-context/useInlineRender.js.map +1 -1
  43. package/dist/react/primitive-hooks/useMessageError.d.ts.map +1 -1
  44. package/dist/react/primitive-hooks/useMessageError.js +2 -8
  45. package/dist/react/primitive-hooks/useMessageError.js.map +1 -1
  46. package/dist/react/primitive-hooks/useThreadListLoadMore.d.ts.map +1 -1
  47. package/dist/react/primitive-hooks/useThreadListLoadMore.js +2 -4
  48. package/dist/react/primitive-hooks/useThreadListLoadMore.js.map +1 -1
  49. package/dist/react/primitives/composer/ComposerAttachments.d.ts.map +1 -1
  50. package/dist/react/primitives/composer/ComposerAttachments.js +4 -3
  51. package/dist/react/primitives/composer/ComposerAttachments.js.map +1 -1
  52. package/dist/react/primitives/composer/ComposerQueue.d.ts.map +1 -1
  53. package/dist/react/primitives/composer/ComposerQueue.js +4 -3
  54. package/dist/react/primitives/composer/ComposerQueue.js.map +1 -1
  55. package/dist/react/primitives/message/MessageAttachments.d.ts.map +1 -1
  56. package/dist/react/primitives/message/MessageAttachments.js +7 -6
  57. package/dist/react/primitives/message/MessageAttachments.js.map +1 -1
  58. package/dist/react/primitives/message/MessageGroupedParts.js +2 -2
  59. package/dist/react/primitives/message/MessageGroupedParts.js.map +1 -1
  60. package/dist/react/primitives/message/MessageParts.js +3 -3
  61. package/dist/react/primitives/message/MessageParts.js.map +1 -1
  62. package/dist/react/primitives/threadList/ThreadListItems.d.ts.map +1 -1
  63. package/dist/react/primitives/threadList/ThreadListItems.js +5 -4
  64. package/dist/react/primitives/threadList/ThreadListItems.js.map +1 -1
  65. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.d.ts +6 -4
  66. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.d.ts.map +1 -1
  67. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js +44 -22
  68. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js.map +1 -1
  69. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts +6 -5
  70. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts.map +1 -1
  71. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js +79 -62
  72. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js.map +1 -1
  73. package/dist/react/runtimes/RemoteThreadResource.d.ts +1 -6
  74. package/dist/react/runtimes/RemoteThreadResource.d.ts.map +1 -1
  75. package/dist/react/runtimes/RemoteThreadResource.js +3 -3
  76. package/dist/react/runtimes/RemoteThreadResource.js.map +1 -1
  77. package/dist/react/runtimes/assistant-transport/useLatestRef.js +2 -2
  78. package/dist/react/runtimes/assistant-transport/useLatestRef.js.map +1 -1
  79. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.d.ts.map +1 -1
  80. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js +23 -18
  81. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js.map +1 -1
  82. package/dist/react/runtimes/cloud/auiV0.d.ts +5 -1
  83. package/dist/react/runtimes/cloud/auiV0.d.ts.map +1 -1
  84. package/dist/react/runtimes/cloud/auiV0.js.map +1 -1
  85. package/dist/react/runtimes/cloud/createCloudThreadListAdapter.d.ts +29 -0
  86. package/dist/react/runtimes/cloud/createCloudThreadListAdapter.d.ts.map +1 -0
  87. package/dist/react/runtimes/cloud/createCloudThreadListAdapter.js +159 -0
  88. package/dist/react/runtimes/cloud/createCloudThreadListAdapter.js.map +1 -0
  89. package/dist/react/runtimes/cloud/useCloudThreadListAdapter.d.ts +1 -9
  90. package/dist/react/runtimes/cloud/useCloudThreadListAdapter.d.ts.map +1 -1
  91. package/dist/react/runtimes/cloud/useCloudThreadListAdapter.js +16 -153
  92. package/dist/react/runtimes/cloud/useCloudThreadListAdapter.js.map +1 -1
  93. package/dist/react/runtimes/external-message-converter.js +1 -1
  94. package/dist/react/runtimes/external-message-converter.js.map +1 -1
  95. package/dist/react/runtimes/useRemoteThreadListRuntime.d.ts.map +1 -1
  96. package/dist/react/runtimes/useRemoteThreadListRuntime.js +14 -6
  97. package/dist/react/runtimes/useRemoteThreadListRuntime.js.map +1 -1
  98. package/dist/react/types/MessagePartComponentTypes.d.ts +7 -3
  99. package/dist/react/types/MessagePartComponentTypes.d.ts.map +1 -1
  100. package/dist/react/types/scopes/tools.d.ts +2 -0
  101. package/dist/react/types/scopes/tools.d.ts.map +1 -1
  102. package/dist/runtime/api/message-part-runtime.d.ts +2 -2
  103. package/dist/runtime/api/message-part-runtime.d.ts.map +1 -1
  104. package/dist/runtime/api/message-part-runtime.js +1 -1
  105. package/dist/runtime/api/message-part-runtime.js.map +1 -1
  106. package/dist/runtime/api/thread-list-runtime.d.ts +9 -1
  107. package/dist/runtime/api/thread-list-runtime.d.ts.map +1 -1
  108. package/dist/runtime/api/thread-list-runtime.js +5 -0
  109. package/dist/runtime/api/thread-list-runtime.js.map +1 -1
  110. package/dist/runtime/api/thread-runtime.d.ts +1 -1
  111. package/dist/runtime/base/base-thread-runtime-core.d.ts +2 -2
  112. package/dist/runtime/base/base-thread-runtime-core.d.ts.map +1 -1
  113. package/dist/runtime/base/base-thread-runtime-core.js +2 -2
  114. package/dist/runtime/base/base-thread-runtime-core.js.map +1 -1
  115. package/dist/runtime/interfaces/thread-list-runtime-core.d.ts +14 -2
  116. package/dist/runtime/interfaces/thread-list-runtime-core.d.ts.map +1 -1
  117. package/dist/runtime/interfaces/thread-runtime-core.d.ts +15 -1
  118. package/dist/runtime/interfaces/thread-runtime-core.d.ts.map +1 -1
  119. package/dist/runtime/utils/auto-status.d.ts +1 -1
  120. package/dist/runtime/utils/auto-status.d.ts.map +1 -1
  121. package/dist/runtime/utils/auto-status.js +6 -2
  122. package/dist/runtime/utils/auto-status.js.map +1 -1
  123. package/dist/runtime/utils/external-message-conversion.d.ts +3 -2
  124. package/dist/runtime/utils/external-message-conversion.d.ts.map +1 -1
  125. package/dist/runtime/utils/external-message-conversion.js +4 -3
  126. package/dist/runtime/utils/external-message-conversion.js.map +1 -1
  127. package/dist/runtime/utils/resolveToolApprovalResponse.d.ts +6 -3
  128. package/dist/runtime/utils/resolveToolApprovalResponse.d.ts.map +1 -1
  129. package/dist/runtime/utils/resolveToolApprovalResponse.js +12 -3
  130. package/dist/runtime/utils/resolveToolApprovalResponse.js.map +1 -1
  131. package/dist/runtime/utils/thread-message-like.d.ts +2 -10
  132. package/dist/runtime/utils/thread-message-like.d.ts.map +1 -1
  133. package/dist/runtime/utils/thread-message-like.js.map +1 -1
  134. package/dist/runtime/utils/tool-call-tree.d.ts +39 -0
  135. package/dist/runtime/utils/tool-call-tree.d.ts.map +1 -0
  136. package/dist/runtime/utils/tool-call-tree.js +85 -0
  137. package/dist/runtime/utils/tool-call-tree.js.map +1 -0
  138. package/dist/runtimes/external-store/external-store-adapter.d.ts +29 -2
  139. package/dist/runtimes/external-store/external-store-adapter.d.ts.map +1 -1
  140. package/dist/runtimes/external-store/external-store-thread-runtime-core.d.ts +2 -1
  141. package/dist/runtimes/external-store/external-store-thread-runtime-core.d.ts.map +1 -1
  142. package/dist/runtimes/external-store/external-store-thread-runtime-core.js +34 -28
  143. package/dist/runtimes/external-store/external-store-thread-runtime-core.js.map +1 -1
  144. package/dist/runtimes/inert/InertThreadRuntimeCore.d.ts +1 -1
  145. package/dist/runtimes/inert/InertThreadRuntimeCore.d.ts.map +1 -1
  146. package/dist/runtimes/inert/InertThreadRuntimeCore.js.map +1 -1
  147. package/dist/runtimes/local/local-thread-runtime-core.d.ts +1 -1
  148. package/dist/runtimes/local/local-thread-runtime-core.d.ts.map +1 -1
  149. package/dist/runtimes/local/local-thread-runtime-core.js +3 -1
  150. package/dist/runtimes/local/local-thread-runtime-core.js.map +1 -1
  151. package/dist/runtimes/remote-thread-list/remote-thread-state.d.ts +1 -1
  152. package/dist/runtimes/remote-thread-list/remote-thread-state.d.ts.map +1 -1
  153. package/dist/runtimes/remote-thread-list/remote-thread-state.js +30 -13
  154. package/dist/runtimes/remote-thread-list/remote-thread-state.js.map +1 -1
  155. package/dist/runtimes/remote-thread-list/title.d.ts +9 -0
  156. package/dist/runtimes/remote-thread-list/title.d.ts.map +1 -0
  157. package/dist/runtimes/remote-thread-list/title.js +6 -0
  158. package/dist/runtimes/remote-thread-list/title.js.map +1 -0
  159. package/dist/runtimes/remote-thread-list/types.d.ts +6 -5
  160. package/dist/runtimes/remote-thread-list/types.d.ts.map +1 -1
  161. package/dist/runtimes/tool-invocations/ToolInvocationTracker.d.ts +31 -8
  162. package/dist/runtimes/tool-invocations/ToolInvocationTracker.d.ts.map +1 -1
  163. package/dist/runtimes/tool-invocations/ToolInvocationTracker.js +97 -60
  164. package/dist/runtimes/tool-invocations/ToolInvocationTracker.js.map +1 -1
  165. package/dist/store/clients/external-thread.d.ts +1 -1
  166. package/dist/store/clients/external-thread.d.ts.map +1 -1
  167. package/dist/store/clients/external-thread.js +11 -4
  168. package/dist/store/clients/external-thread.js.map +1 -1
  169. package/dist/store/clients/single-thread-list.js +1 -1
  170. package/dist/store/clients/suggestions.d.ts.map +1 -1
  171. package/dist/store/clients/suggestions.js +18 -6
  172. package/dist/store/clients/suggestions.js.map +1 -1
  173. package/dist/store/clients/thread-message-client.js +1 -1
  174. package/dist/store/clients/thread-selection-events.js +1 -1
  175. package/dist/store/index.d.ts +2 -1
  176. package/dist/store/index.js +2 -1
  177. package/dist/store/internal.d.ts +2 -2
  178. package/dist/store/internal.js +2 -2
  179. package/dist/store/primitive-predicates.d.ts +4 -1
  180. package/dist/store/primitive-predicates.d.ts.map +1 -1
  181. package/dist/store/primitive-predicates.js +9 -1
  182. package/dist/store/primitive-predicates.js.map +1 -1
  183. package/dist/store/runtime-clients/composer-runtime-client.js +1 -1
  184. package/dist/store/runtime-clients/message-runtime-client.js +1 -1
  185. package/dist/store/runtime-clients/thread-list-item-runtime-client.js +1 -1
  186. package/dist/store/runtime-clients/thread-list-runtime-client.d.ts.map +1 -1
  187. package/dist/store/runtime-clients/thread-list-runtime-client.js +124 -105
  188. package/dist/store/runtime-clients/thread-list-runtime-client.js.map +1 -1
  189. package/dist/store/runtime-clients/thread-runtime-client.js +1 -1
  190. package/dist/store/runtime-clients/useSubscribable.d.ts +3 -1
  191. package/dist/store/runtime-clients/useSubscribable.d.ts.map +1 -1
  192. package/dist/store/runtime-clients/useSubscribable.js +1 -1
  193. package/dist/store/runtime-clients/useSubscribable.js.map +1 -1
  194. package/dist/store/scopes/part.d.ts +6 -3
  195. package/dist/store/scopes/part.d.ts.map +1 -1
  196. package/dist/subscribable/subscribable.d.ts +8 -1
  197. package/dist/subscribable/subscribable.d.ts.map +1 -1
  198. package/dist/subscribable/subscribable.js +24 -16
  199. package/dist/subscribable/subscribable.js.map +1 -1
  200. package/dist/types/message.d.ts +40 -1
  201. package/dist/types/message.d.ts.map +1 -1
  202. package/dist/types/message.js +7 -1
  203. package/dist/types/message.js.map +1 -1
  204. package/package.json +12 -17
  205. package/src/index.ts +6 -1
  206. package/src/internal.ts +8 -0
  207. package/src/model-context/frame/provider.test.ts +217 -1
  208. package/src/model-context/frame/provider.ts +111 -23
  209. package/src/model-context/tool-call-text.test.ts +67 -0
  210. package/src/model-context/tool-call-text.ts +67 -0
  211. package/src/react/AssistantRuntimeProvider.test.tsx +66 -2
  212. package/src/react/adapters/LocalStorageThreadListAdapter.tsx +1 -3
  213. package/src/react/client/RemoteThreadList.background.test.ts +384 -0
  214. package/src/react/client/RemoteThreadList.concurrent.test.tsx +274 -0
  215. package/src/react/client/RemoteThreadList.test.ts +273 -1
  216. package/src/react/client/RemoteThreadList.ts +293 -83
  217. package/src/react/client/Tools.ts +7 -1
  218. package/src/react/index.ts +4 -0
  219. package/src/react/model-context/modelContextHooks.test.tsx +131 -0
  220. package/src/react/model-context/toolbox.test.ts +9 -9
  221. package/src/react/model-context/toolbox.ts +9 -36
  222. package/src/react/model-context/useAssistantContext.ts +4 -2
  223. package/src/react/model-context/useAuiToolOverrides.ts +4 -2
  224. package/src/react/model-context/useInlineRender.test.tsx +48 -0
  225. package/src/react/model-context/useInlineRender.ts +14 -10
  226. package/src/react/primitive-hooks/useMessageError.ts +2 -19
  227. package/src/react/primitive-hooks/useThreadListLoadMore.ts +2 -3
  228. package/src/react/primitives/composer/ComposerAttachments.test.tsx +76 -0
  229. package/src/react/primitives/composer/ComposerAttachments.tsx +9 -4
  230. package/src/react/primitives/composer/ComposerQueue.test.tsx +72 -0
  231. package/src/react/primitives/composer/ComposerQueue.tsx +7 -4
  232. package/src/react/primitives/message/MessageAttachments.test.tsx +107 -37
  233. package/src/react/primitives/message/MessageAttachments.tsx +10 -7
  234. package/src/react/primitives/message/MessageGroupedParts.tsx +2 -2
  235. package/src/react/primitives/message/MessageParts.tsx +3 -3
  236. package/src/react/primitives/threadList/ThreadListItems.test.tsx +99 -0
  237. package/src/react/primitives/threadList/ThreadListItems.tsx +8 -5
  238. package/src/react/runtimes/RemoteThreadListHookInstanceManager.running.test.tsx +94 -1
  239. package/src/react/runtimes/RemoteThreadListHookInstanceManager.tsx +72 -31
  240. package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.switch-delete.test.ts +169 -0
  241. package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.tsx +114 -68
  242. package/src/react/runtimes/RemoteThreadResource.ts +1 -4
  243. package/src/react/runtimes/assistant-transport/transport-scheduling.test.ts +51 -2
  244. package/src/react/runtimes/assistant-transport/useLatestRef.ts +2 -2
  245. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.test.ts +45 -0
  246. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.test.tsx +65 -1
  247. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.ts +42 -35
  248. package/src/react/runtimes/cloud/auiV0.ts +5 -0
  249. package/src/react/runtimes/cloud/createCloudThreadListAdapter.test.ts +88 -0
  250. package/src/react/runtimes/cloud/createCloudThreadListAdapter.ts +236 -0
  251. package/src/react/runtimes/cloud/useCloudThreadListAdapter.test.tsx +90 -1
  252. package/src/react/runtimes/cloud/useCloudThreadListAdapter.tsx +33 -238
  253. package/src/react/runtimes/external-message-converter.ts +1 -0
  254. package/src/react/runtimes/useRemoteThreadListRuntime.concurrent.test.tsx +216 -0
  255. package/src/react/runtimes/useRemoteThreadListRuntime.ts +24 -6
  256. package/src/react/types/MessagePartComponentTypes.ts +7 -3
  257. package/src/react/types/scopes/tools.ts +2 -0
  258. package/src/runtime/api/message-part-runtime.ts +3 -3
  259. package/src/runtime/api/thread-list-runtime.ts +25 -1
  260. package/src/runtime/base/base-thread-runtime-core.test.ts +94 -3
  261. package/src/runtime/base/base-thread-runtime-core.ts +10 -4
  262. package/src/runtime/interfaces/thread-list-runtime-core.ts +20 -1
  263. package/src/runtime/interfaces/thread-runtime-core.ts +17 -1
  264. package/src/runtime/utils/auto-status.test.ts +57 -0
  265. package/src/runtime/utils/auto-status.ts +13 -1
  266. package/src/runtime/utils/external-message-conversion.test.ts +97 -0
  267. package/src/runtime/utils/external-message-conversion.ts +17 -1
  268. package/src/runtime/utils/resolveToolApprovalResponse.test.ts +73 -0
  269. package/src/runtime/utils/resolveToolApprovalResponse.ts +27 -6
  270. package/src/runtime/utils/thread-message-like.ts +2 -10
  271. package/src/runtime/utils/tool-call-tree.test.ts +191 -0
  272. package/src/runtime/utils/tool-call-tree.ts +97 -0
  273. package/src/runtimes/external-store/external-store-adapter.ts +38 -2
  274. package/src/runtimes/external-store/external-store-thread-runtime-core.ts +56 -33
  275. package/src/runtimes/inert/InertThreadRuntimeCore.ts +1 -1
  276. package/src/runtimes/local/local-thread-runtime-core.test.ts +29 -3
  277. package/src/runtimes/local/local-thread-runtime-core.ts +5 -1
  278. package/src/runtimes/remote-thread-list/remote-thread-state.test.ts +112 -0
  279. package/src/runtimes/remote-thread-list/remote-thread-state.ts +55 -16
  280. package/src/runtimes/remote-thread-list/title.ts +3 -0
  281. package/src/runtimes/remote-thread-list/types.ts +6 -5
  282. package/src/runtimes/tool-invocations/EDGE_CASES.md +110 -5
  283. package/src/runtimes/tool-invocations/ToolInvocationTracker.test.ts +750 -136
  284. package/src/runtimes/tool-invocations/ToolInvocationTracker.ts +144 -91
  285. package/src/store/clients/external-thread.ts +14 -8
  286. package/src/store/clients/suggestions.test.ts +155 -1
  287. package/src/store/clients/suggestions.ts +42 -34
  288. package/src/store/index.ts +4 -0
  289. package/src/store/internal.ts +2 -0
  290. package/src/store/primitive-predicates.ts +35 -0
  291. package/src/store/runtime-clients/thread-list-runtime-client.ts +16 -2
  292. package/src/store/runtime-clients/useSubscribable.ts +8 -2
  293. package/src/store/scopes/part.ts +6 -3
  294. package/src/subscribable/subscribable.test.ts +50 -0
  295. package/src/subscribable/subscribable.ts +33 -21
  296. package/src/tests/RemoteThreadListThreadListRuntimeCore-load-race.test.ts +111 -0
  297. package/src/tests/RemoteThreadListThreadListRuntimeCore-loadMore.test.ts +78 -0
  298. package/src/tests/RemoteThreadListThreadListRuntimeCore-reload.test.ts +52 -0
  299. package/src/tests/external-store-optimistic-id.test.ts +79 -0
  300. package/src/tests/external-store-repository-instance.test.ts +166 -0
  301. package/src/tests/external-store-thread-runtime-core-adapter.test.ts +22 -16
  302. package/src/tests/external-thread-attachments.test.tsx +85 -1
  303. package/src/tests/remote-thread-list-adapter-switch.test.ts +66 -1
  304. package/src/tests/thread-switch-events.test.tsx +397 -2
  305. package/src/types/message.ts +50 -2
@@ -1 +1 @@
1
- {"version":3,"file":"subscribable.js","names":["Unsubscribe","notifyEventListeners","SKIP_UPDATE","Symbol","Subscribable","subscribe","callback","SubscribableWithState","path","TPath","getState","TState","NestedSubscribable","EventSubscribable","event","TEvent","binding","unstable_on","payload","notifySubscribers","subscribers","Iterable","args","TArgs","errors","error","push","length","console","AggregateError","shallowEqual","objA","T","objB","undefined","keysA","Object","keys","key","valueA","valueB","is","BaseSubscribable","_subscribers","Set","add","delete","waitForUpdate","Promise","resolve","unsubscribe","_notifySubscribers","BaseSubject","_subscriptions","_connection","isConnected","_connect","errorContext","_updateConnection","size","ShallowMemoizeSubject","constructor","state","Error","_previousState","_syncState","LazyMemoizeSubject","_previousStateDirty","newState","NestedSubscriptionSubject","outerSubscribe","lastState","innerUnsubscribe","onRuntimeUpdate","outerUnsubscribe","EventSubscriptionSubject","config"],"sources":["../../src/subscribable/subscribable.ts"],"sourcesContent":["import type { Unsubscribe } from \"../types/unsubscribe\";\nimport { notifyEventListeners } from \"../utils/notify-event-listeners\";\n\nexport const SKIP_UPDATE = Symbol(\"skip-update\");\nexport type SKIP_UPDATE = typeof SKIP_UPDATE;\n\nexport type Subscribable = {\n subscribe: (callback: () => void) => Unsubscribe;\n};\n\nexport type SubscribableWithState<TState, TPath> = Subscribable & {\n path: TPath;\n getState: () => TState;\n};\n\nexport type NestedSubscribable<\n TState extends Subscribable | undefined,\n TPath,\n> = SubscribableWithState<TState, TPath>;\n\nexport type EventSubscribable<TEvent extends string> = {\n event: TEvent;\n binding: SubscribableWithState<\n | {\n unstable_on: (\n event: TEvent,\n callback: (payload?: unknown) => void,\n ) => Unsubscribe;\n }\n | undefined,\n unknown\n >;\n};\n\nexport const notifySubscribers = <TArgs extends unknown[]>(\n subscribers: Iterable<(...args: TArgs) => void>,\n ...args: TArgs\n): void => {\n const errors: unknown[] = [];\n for (const callback of subscribers) {\n try {\n callback(...args);\n } catch (error) {\n errors.push(error);\n }\n }\n\n if (errors.length === 1) {\n throw errors[0];\n }\n\n if (errors.length > 1) {\n for (const error of errors) {\n console.error(error);\n }\n throw new AggregateError(errors);\n }\n};\n\nfunction shallowEqual<T extends object>(\n objA: T | undefined,\n objB: T | undefined,\n) {\n if (objA === undefined && objB === undefined) return true;\n if (objA === undefined) return false;\n if (objB === undefined) return false;\n\n const keysA = Object.keys(objA);\n if (keysA.length !== Object.keys(objB).length) return false;\n\n for (const key of keysA) {\n const valueA = objA[key as keyof T];\n const valueB = objB[key as keyof T];\n if (!Object.is(valueA, valueB)) return false;\n }\n\n return true;\n}\n\nexport class BaseSubscribable {\n private _subscribers = new Set<() => void>();\n\n public subscribe(callback: () => void): Unsubscribe {\n this._subscribers.add(callback);\n return () => this._subscribers.delete(callback);\n }\n\n public waitForUpdate() {\n return new Promise<void>((resolve) => {\n const unsubscribe = this.subscribe(() => {\n unsubscribe();\n resolve();\n });\n });\n }\n\n protected _notifySubscribers() {\n notifySubscribers(this._subscribers);\n }\n}\n\n// lazy connect/disconnect: only opens upstream subscription while it has subscribers\nexport abstract class BaseSubject {\n private _subscriptions = new Set<(payload?: unknown) => void>();\n private _connection: Unsubscribe | undefined;\n\n protected get isConnected() {\n return !!this._connection;\n }\n\n protected abstract _connect(): Unsubscribe;\n\n protected notifySubscribers(payload?: unknown, errorContext?: string) {\n if (errorContext) {\n notifyEventListeners(this._subscriptions, payload, errorContext);\n return;\n }\n\n notifySubscribers(this._subscriptions, payload);\n }\n\n private _updateConnection() {\n if (this._subscriptions.size > 0) {\n if (this._connection) return;\n this._connection = this._connect();\n } else {\n this._connection?.();\n this._connection = undefined;\n }\n }\n\n public subscribe(callback: (payload?: unknown) => void) {\n this._subscriptions.add(callback);\n this._updateConnection();\n\n return () => {\n this._subscriptions.delete(callback);\n this._updateConnection();\n };\n }\n}\n\nexport class ShallowMemoizeSubject<TState extends object, TPath>\n extends BaseSubject\n implements SubscribableWithState<TState, TPath>\n{\n public get path() {\n return this.binding.path;\n }\n\n private binding: SubscribableWithState<TState | SKIP_UPDATE, TPath>;\n\n constructor(binding: SubscribableWithState<TState | SKIP_UPDATE, TPath>) {\n super();\n this.binding = binding;\n const state = binding.getState();\n if (state === SKIP_UPDATE)\n throw new Error(\"Entry not available in the store\");\n this._previousState = state;\n }\n\n private _previousState: TState;\n public getState = () => {\n if (!this.isConnected) this._syncState();\n return this._previousState;\n };\n\n private _syncState() {\n const state = this.binding.getState();\n if (state === SKIP_UPDATE) return false;\n if (shallowEqual(state, this._previousState)) return false;\n this._previousState = state;\n return true;\n }\n\n protected _connect() {\n const callback = () => {\n if (this._syncState()) {\n this.notifySubscribers();\n }\n };\n\n const unsubscribe = this.binding.subscribe(callback);\n this._syncState();\n return unsubscribe;\n }\n}\n\nexport class LazyMemoizeSubject<TState extends object, TPath>\n extends BaseSubject\n implements SubscribableWithState<TState, TPath>\n{\n public get path() {\n return this.binding.path;\n }\n\n private binding: SubscribableWithState<TState | SKIP_UPDATE, TPath>;\n\n constructor(binding: SubscribableWithState<TState | SKIP_UPDATE, TPath>) {\n super();\n this.binding = binding;\n }\n\n private _previousStateDirty = true;\n private _previousState: TState | undefined;\n public getState = () => {\n if (!this.isConnected || this._previousStateDirty) {\n const newState = this.binding.getState();\n if (\n newState !== SKIP_UPDATE &&\n (this._previousState === undefined ||\n !shallowEqual(newState, this._previousState))\n ) {\n this._previousState = newState;\n }\n this._previousStateDirty = false;\n }\n if (this._previousState === undefined)\n throw new Error(\"Entry not available in the store\");\n return this._previousState;\n };\n\n protected _connect() {\n const callback = () => {\n this._previousStateDirty = true;\n this.notifySubscribers();\n };\n\n const unsubscribe = this.binding.subscribe(callback);\n this._previousStateDirty = true;\n return unsubscribe;\n }\n}\n\nexport class NestedSubscriptionSubject<\n TState extends Subscribable | undefined,\n TPath,\n>\n extends BaseSubject\n implements\n SubscribableWithState<TState, TPath>,\n NestedSubscribable<TState, TPath>\n{\n public get path() {\n return this.binding.path;\n }\n\n private binding: NestedSubscribable<TState, TPath>;\n\n constructor(binding: NestedSubscribable<TState, TPath>) {\n super();\n this.binding = binding;\n }\n\n public getState() {\n return this.binding.getState();\n }\n\n public outerSubscribe(callback: () => void) {\n return this.binding.subscribe(callback);\n }\n\n protected _connect(): Unsubscribe {\n const callback = () => {\n this.notifySubscribers();\n };\n\n let lastState = this.binding.getState();\n let innerUnsubscribe = lastState?.subscribe(callback);\n const onRuntimeUpdate = () => {\n const newState = this.binding.getState();\n if (newState === lastState) return;\n lastState = newState;\n\n innerUnsubscribe?.();\n innerUnsubscribe = newState?.subscribe(callback);\n\n callback();\n };\n\n const outerUnsubscribe = this.outerSubscribe(onRuntimeUpdate);\n return () => {\n outerUnsubscribe?.();\n innerUnsubscribe?.();\n };\n }\n}\n\nexport class EventSubscriptionSubject<\n TEvent extends string,\n> extends BaseSubject {\n private config: EventSubscribable<TEvent>;\n\n constructor(config: EventSubscribable<TEvent>) {\n super();\n this.config = config;\n }\n\n public getState() {\n return this.config.binding.getState();\n }\n\n public outerSubscribe(callback: () => void) {\n return this.config.binding.subscribe(callback);\n }\n\n protected _connect(): Unsubscribe {\n const errorContext = `Runtime event \"${this.config.event}\"`;\n const callback = (payload?: unknown) => {\n this.notifySubscribers(payload, errorContext);\n };\n\n let lastState = this.config.binding.getState();\n let innerUnsubscribe = lastState?.unstable_on(this.config.event, callback);\n const onRuntimeUpdate = () => {\n const newState = this.config.binding.getState();\n if (newState === lastState) return;\n lastState = newState;\n\n innerUnsubscribe?.();\n innerUnsubscribe = newState?.unstable_on(this.config.event, callback);\n };\n\n const outerUnsubscribe = this.outerSubscribe(onRuntimeUpdate);\n return () => {\n outerUnsubscribe?.();\n innerUnsubscribe?.();\n };\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"subscribable.js","names":["shallowEqual","Unsubscribe","notifyEventListeners","SKIP_UPDATE","Symbol","Subscribable","subscribe","callback","SubscribableWithState","path","TPath","getState","TState","NestedSubscribable","EventSubscribable","event","TEvent","binding","unstable_on","payload","notifySubscribers","subscribers","Iterable","args","TArgs","errors","error","push","length","console","AggregateError","shallowEqualOrUndefined","a","T","b","undefined","BaseSubscribable","_subscribers","Set","add","delete","waitForUpdate","Promise","resolve","unsubscribe","_notifySubscribers","WritableSubscribable","_state","constructor","state","bind","getServerSnapshot","setState","Object","is","BaseSubject","_subscriptions","_connection","isConnected","_connect","errorContext","_updateConnection","size","ShallowMemoizeSubject","Error","_previousState","_syncState","LazyMemoizeSubject","_previousStateDirty","newState","NestedSubscriptionSubject","outerSubscribe","lastState","innerUnsubscribe","onRuntimeUpdate","outerUnsubscribe","EventSubscriptionSubject","config"],"sources":["../../src/subscribable/subscribable.ts"],"sourcesContent":["import { shallowEqual } from \"@assistant-ui/store/client\";\nimport type { Unsubscribe } from \"../types/unsubscribe\";\nimport { notifyEventListeners } from \"../utils/notify-event-listeners\";\n\nexport const SKIP_UPDATE = Symbol(\"skip-update\");\nexport type SKIP_UPDATE = typeof SKIP_UPDATE;\n\nexport type Subscribable = {\n subscribe: (callback: () => void) => Unsubscribe;\n};\n\nexport type SubscribableWithState<TState, TPath> = Subscribable & {\n path: TPath;\n getState: () => TState;\n};\n\nexport type NestedSubscribable<\n TState extends Subscribable | undefined,\n TPath,\n> = SubscribableWithState<TState, TPath>;\n\nexport type EventSubscribable<TEvent extends string> = {\n event: TEvent;\n binding: SubscribableWithState<\n | {\n unstable_on: (\n event: TEvent,\n callback: (payload?: unknown) => void,\n ) => Unsubscribe;\n }\n | undefined,\n unknown\n >;\n};\n\nexport const notifySubscribers = <TArgs extends unknown[]>(\n subscribers: Iterable<(...args: TArgs) => void>,\n ...args: TArgs\n): void => {\n const errors: unknown[] = [];\n for (const callback of subscribers) {\n try {\n callback(...args);\n } catch (error) {\n errors.push(error);\n }\n }\n\n if (errors.length === 1) {\n throw errors[0];\n }\n\n if (errors.length > 1) {\n for (const error of errors) {\n console.error(error);\n }\n throw new AggregateError(errors);\n }\n};\n\nconst shallowEqualOrUndefined = <T extends object>(\n a: T | undefined,\n b: T | undefined,\n) => (a === undefined || b === undefined ? a === b : shallowEqual(a, b));\n\nexport class BaseSubscribable {\n private _subscribers = new Set<() => void>();\n\n public subscribe(callback: () => void): Unsubscribe {\n this._subscribers.add(callback);\n return () => this._subscribers.delete(callback);\n }\n\n public waitForUpdate() {\n return new Promise<void>((resolve) => {\n const unsubscribe = this.subscribe(() => {\n unsubscribe();\n resolve();\n });\n });\n }\n\n protected _notifySubscribers() {\n notifySubscribers(this._subscribers);\n }\n}\n\nexport class WritableSubscribable<TState> extends BaseSubscribable {\n private _state: TState;\n\n constructor(state: TState) {\n super();\n this._state = state;\n this.subscribe = this.subscribe.bind(this);\n this.getState = this.getState.bind(this);\n // Hydration has to agree with what the server rendered, so the server\n // snapshot stays at the creation-time state rather than following writes.\n this.getServerSnapshot = () => state;\n }\n\n public getState(): TState {\n return this._state;\n }\n\n public getServerSnapshot: () => TState;\n\n public setState(state: TState): void {\n if (Object.is(state, this._state)) return;\n this._state = state;\n this._notifySubscribers();\n }\n}\n\n// lazy connect/disconnect: only opens upstream subscription while it has subscribers\nexport abstract class BaseSubject {\n private _subscriptions = new Set<(payload?: unknown) => void>();\n private _connection: Unsubscribe | undefined;\n\n protected get isConnected() {\n return !!this._connection;\n }\n\n protected abstract _connect(): Unsubscribe;\n\n protected notifySubscribers(payload?: unknown, errorContext?: string) {\n if (errorContext) {\n notifyEventListeners(this._subscriptions, payload, errorContext);\n return;\n }\n\n notifySubscribers(this._subscriptions, payload);\n }\n\n private _updateConnection() {\n if (this._subscriptions.size > 0) {\n if (this._connection) return;\n this._connection = this._connect();\n } else {\n this._connection?.();\n this._connection = undefined;\n }\n }\n\n public subscribe(callback: (payload?: unknown) => void) {\n this._subscriptions.add(callback);\n this._updateConnection();\n\n return () => {\n this._subscriptions.delete(callback);\n this._updateConnection();\n };\n }\n}\n\nexport class ShallowMemoizeSubject<TState extends object, TPath>\n extends BaseSubject\n implements SubscribableWithState<TState, TPath>\n{\n public get path() {\n return this.binding.path;\n }\n\n private binding: SubscribableWithState<TState | SKIP_UPDATE, TPath>;\n\n constructor(binding: SubscribableWithState<TState | SKIP_UPDATE, TPath>) {\n super();\n this.binding = binding;\n const state = binding.getState();\n if (state === SKIP_UPDATE)\n throw new Error(\"Entry not available in the store\");\n this._previousState = state;\n }\n\n private _previousState: TState;\n public getState = () => {\n if (!this.isConnected) this._syncState();\n return this._previousState;\n };\n\n private _syncState() {\n const state = this.binding.getState();\n if (state === SKIP_UPDATE) return false;\n if (shallowEqualOrUndefined(state, this._previousState)) return false;\n this._previousState = state;\n return true;\n }\n\n protected _connect() {\n const callback = () => {\n if (this._syncState()) {\n this.notifySubscribers();\n }\n };\n\n const unsubscribe = this.binding.subscribe(callback);\n this._syncState();\n return unsubscribe;\n }\n}\n\nexport class LazyMemoizeSubject<TState extends object, TPath>\n extends BaseSubject\n implements SubscribableWithState<TState, TPath>\n{\n public get path() {\n return this.binding.path;\n }\n\n private binding: SubscribableWithState<TState | SKIP_UPDATE, TPath>;\n\n constructor(binding: SubscribableWithState<TState | SKIP_UPDATE, TPath>) {\n super();\n this.binding = binding;\n }\n\n private _previousStateDirty = true;\n private _previousState: TState | undefined;\n public getState = () => {\n if (!this.isConnected || this._previousStateDirty) {\n const newState = this.binding.getState();\n if (\n newState !== SKIP_UPDATE &&\n (this._previousState === undefined ||\n !shallowEqualOrUndefined(newState, this._previousState))\n ) {\n this._previousState = newState;\n }\n this._previousStateDirty = false;\n }\n if (this._previousState === undefined)\n throw new Error(\"Entry not available in the store\");\n return this._previousState;\n };\n\n protected _connect() {\n const callback = () => {\n this._previousStateDirty = true;\n this.notifySubscribers();\n };\n\n const unsubscribe = this.binding.subscribe(callback);\n this._previousStateDirty = true;\n return unsubscribe;\n }\n}\n\nexport class NestedSubscriptionSubject<\n TState extends Subscribable | undefined,\n TPath,\n>\n extends BaseSubject\n implements\n SubscribableWithState<TState, TPath>,\n NestedSubscribable<TState, TPath>\n{\n public get path() {\n return this.binding.path;\n }\n\n private binding: NestedSubscribable<TState, TPath>;\n\n constructor(binding: NestedSubscribable<TState, TPath>) {\n super();\n this.binding = binding;\n }\n\n public getState() {\n return this.binding.getState();\n }\n\n public outerSubscribe(callback: () => void) {\n return this.binding.subscribe(callback);\n }\n\n protected _connect(): Unsubscribe {\n const callback = () => {\n this.notifySubscribers();\n };\n\n let lastState = this.binding.getState();\n let innerUnsubscribe = lastState?.subscribe(callback);\n const onRuntimeUpdate = () => {\n const newState = this.binding.getState();\n if (newState === lastState) return;\n lastState = newState;\n\n innerUnsubscribe?.();\n innerUnsubscribe = newState?.subscribe(callback);\n\n callback();\n };\n\n const outerUnsubscribe = this.outerSubscribe(onRuntimeUpdate);\n return () => {\n outerUnsubscribe?.();\n innerUnsubscribe?.();\n };\n }\n}\n\nexport class EventSubscriptionSubject<\n TEvent extends string,\n> extends BaseSubject {\n private config: EventSubscribable<TEvent>;\n\n constructor(config: EventSubscribable<TEvent>) {\n super();\n this.config = config;\n }\n\n public getState() {\n return this.config.binding.getState();\n }\n\n public outerSubscribe(callback: () => void) {\n return this.config.binding.subscribe(callback);\n }\n\n protected _connect(): Unsubscribe {\n const errorContext = `Runtime event \"${this.config.event}\"`;\n const callback = (payload?: unknown) => {\n this.notifySubscribers(payload, errorContext);\n };\n\n let lastState = this.config.binding.getState();\n let innerUnsubscribe = lastState?.unstable_on(this.config.event, callback);\n const onRuntimeUpdate = () => {\n const newState = this.config.binding.getState();\n if (newState === lastState) return;\n lastState = newState;\n\n innerUnsubscribe?.();\n innerUnsubscribe = newState?.unstable_on(this.config.event, callback);\n };\n\n const outerUnsubscribe = this.outerSubscribe(onRuntimeUpdate);\n return () => {\n outerUnsubscribe?.();\n innerUnsubscribe?.();\n };\n }\n}\n"],"mappings":"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"}
@@ -153,15 +153,42 @@ type ToolApprovalOption = {
153
153
  description?: string;
154
154
  };
155
155
  };
156
+ /**
157
+ * How an approval request should be presented, and which answers it accepts.
158
+ * Absent: `"decision"`.
159
+ *
160
+ * The approval seam carries a host-owned request addressed by its own id and
161
+ * resolved once; a request wanting an answer these modes cannot express stays
162
+ * on the `interrupt` seam, which hands the tool an arbitrary payload instead.
163
+ * Unlike {@link ToolApprovalOptionKind} the set is closed, because a renderer
164
+ * that cannot cover every mode exhaustively is back to guessing the affordance.
165
+ */
166
+ type ToolApprovalDisplay = "decision" | "select" | "text";
167
+ /**
168
+ * Whether the request asks for a free-form answer, on its own or alongside its
169
+ * options. Renderers read this to decide whether to offer a text affordance,
170
+ * and the runtime reads it to reject a `text` response the host cannot record.
171
+ */
172
+ declare const toolApprovalAcceptsText: (approval: {
173
+ readonly display?: ToolApprovalDisplay;
174
+ readonly allowFreeform?: boolean;
175
+ }) => boolean;
156
176
  type ToolApprovalResponse = {
157
177
  readonly approved: boolean;
178
+ readonly text?: string;
158
179
  readonly reason?: string;
159
180
  } | {
160
181
  readonly optionId: string;
182
+ readonly text?: string;
161
183
  readonly reason?: string;
162
184
  } | {
163
185
  readonly approved: boolean;
164
186
  readonly optionId: string;
187
+ readonly text?: string;
188
+ readonly reason?: string;
189
+ } | {
190
+ /** Answer to a request that asks a question rather than for a decision. */
191
+ readonly text: string;
165
192
  readonly reason?: string;
166
193
  };
167
194
  type ToolCallMessagePart<TArgs = ReadonlyJSONObject, TResult = unknown> = {
@@ -201,6 +228,16 @@ type ToolCallMessagePart<TArgs = ReadonlyJSONObject, TResult = unknown> = {
201
228
  /** Server-side approval gate. `respondToApproval` may only be called while `approved` is undefined and no `resolution` is recorded. */
202
229
  readonly approval?: {
203
230
  readonly id: string;
231
+ /**
232
+ * The question put to the user. A request that asks for something other
233
+ * than permission carries it here, so a renderer can show the question
234
+ * rather than inferring a decision from the tool name.
235
+ */
236
+ readonly prompt?: string;
237
+ /** How the request should be presented. Absent: a plain decision. */
238
+ readonly display?: ToolApprovalDisplay;
239
+ /** Whether a free-form answer is accepted alongside the options. */
240
+ readonly allowFreeform?: boolean;
204
241
  readonly approved?: boolean;
205
242
  readonly reason?: string;
206
243
  readonly isAutomatic?: boolean;
@@ -208,6 +245,8 @@ type ToolCallMessagePart<TArgs = ReadonlyJSONObject, TResult = unknown> = {
208
245
  readonly options?: readonly ToolApprovalOption[];
209
246
  /** The option chosen at resolution, when options were present. */
210
247
  readonly optionId?: string;
248
+ /** The free-form answer recorded at resolution, when one was given. */
249
+ readonly text?: string;
211
250
  /** Terminal non-decision state: the request was cancelled or expired without a user decision. Set by the host. */
212
251
  readonly resolution?: "cancelled" | "expired";
213
252
  };
@@ -361,5 +400,5 @@ type AppendMessage = Omit<ThreadMessage, "id"> & {
361
400
  steer?: boolean | undefined;
362
401
  };
363
402
  //#endregion
364
- export { AppendMessage, DataMessagePart, FileMessagePart, GenerativeUIMessagePart, GenerativeUINode, GenerativeUISpec, ImageMessagePart, MCP_APP_URI_SCHEME, McpAppMetadata, MessagePartStatus, MessagePartStreamStatus, MessageRole, MessageStatus, MessageTiming, PartProviderMetadata, ReasoningMessagePart, RunConfig, SourceMessagePart, SourceProviderMetadata, TextMessagePart, ThreadAssistantMessage, ThreadAssistantMessagePart, ThreadMessage, ThreadStep, ThreadSystemMessage, ThreadUserMessage, ThreadUserMessagePart, ToolApprovalOption, ToolApprovalOptionKind, ToolApprovalResponse, ToolCallMessagePart, ToolCallMessagePartMcpMetadata, ToolCallMessagePartStatus, type ToolCallTiming, type ToolModelContentPart, Unstable_AudioMessagePart, isMcpAppUri };
403
+ export { AppendMessage, DataMessagePart, FileMessagePart, GenerativeUIMessagePart, GenerativeUINode, GenerativeUISpec, ImageMessagePart, MCP_APP_URI_SCHEME, McpAppMetadata, MessagePartStatus, MessagePartStreamStatus, MessageRole, MessageStatus, MessageTiming, PartProviderMetadata, ReasoningMessagePart, RunConfig, SourceMessagePart, SourceProviderMetadata, TextMessagePart, ThreadAssistantMessage, ThreadAssistantMessagePart, ThreadMessage, ThreadStep, ThreadSystemMessage, ThreadUserMessage, ThreadUserMessagePart, ToolApprovalDisplay, ToolApprovalOption, ToolApprovalOptionKind, ToolApprovalResponse, ToolCallMessagePart, ToolCallMessagePartMcpMetadata, ToolCallMessagePartStatus, type ToolCallTiming, type ToolModelContentPart, Unstable_AudioMessagePart, isMcpAppUri, toolApprovalAcceptsText };
365
404
  //# sourceMappingURL=message.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"message.d.ts","names":[],"sources":["../../src/types/message.ts"],"mappings":";;;;KASY;YACA,uBAAuB;;KAGvB;WACD;WACA;WACA,SAAS;WACT,mBAAmB;WACnB;;KAGC;WACD;WACA;WACA,SAAS;WACT;WACA,mBAAmB;WACnB;;KAGC,yBAAyB;KAEzB;WAEG;WACA;WACA;WACA;WACA;WACA,mBAAmB;WACnB;;WAGA;WACA;WACA;WACA;WACA;WACA;WACA;WACA,mBAAmB;WACnB;;KAGH;WACD;WACA;WACA;WACA,mBAAmB;;KAGlB;WACD;WACA;WACA;WACA;;WAEA;WACA,mBAAmB;WACnB;;;;;;;;;;;KAYC;WACD;WACA;aACE;aACA;;;KAID,gBAAgB;WACjB;WACA;WACA,MAAM;;;;;;;;;;;KAYL;;WAIG;;WAEA,QAAQ;;WAER,oBAAoB;;WAEpB;;;;;KAMH;;WAED,MAAM,4BAA4B;;;;;;;;;;KAWjC;WACD;;WAEA,MAAM;;WAEN;WACA;;KAGC;WACD;WACA;WACA;;WAEA;;cAGE;cAEA,cAAe;KAGhB;WACD,MAAM;;KAGL;KAMA;;WAED;;;;;;;WAOA,MAAM;;WAEN;WACA;;WAEA;;WAEA;IAAsB;IAAgB;;;KAGrC;WACG;WAA4B;;WAC5B;WAA2B;;WAE3B;WACA;WACA;;KAGH,oBACV,QAAQ,oBACR;;WAGS;;WAEA;;WAEA;;;;;;WAMA,MAAM;;WAEN,SAAS;;WAET;;WAEA;;WAEA;;WAEA,SAAS;;WAET,MAAM;;WAEN,mBAAmB;;WAEnB,wBAAwB;;WAExB;IAAc;IAAe;;;WAE7B;aACE;aACA;aACA;aACA;;aAEA,mBAAmB;;aAEnB;;aAEA;;;WAGF;;;;;WAKA,oBAAoB;;KAGnB,wBACR,kBACA,mBACA,kBACA,kBACA;KAEQ,6BACR,kBACA,uBACA,sBACA,oBACA,kBACA,mBACA,kBACA;KAEQ;WAEG;;WAGA;;WAGA;WACA;WAMA;;KAGH;WAEG;;WAGA;;WAGA;WACA;;KAQH;;WAGG;;WAEA;;WAGA;WACA;WACA,QAAQ;IAEnB;KAEQ;WAEG;;WAGA;WACA;;WAGA;WACA;;WAGA;WACA;WAOA,QAAQ;;KAGX;WACD;WACA;WACA;WACA;WACA;WACA;WACA;;KAGC;WACD;WACA;aAEM;aACA;;;KAKZ;WACM;WACA,WAAW;;KAGV,sBAAsB;WACvB;WACA,mBAAmB;WACnB;aACE;aACA;aACA;aACA;aACA;aACA;aACA,QAAQ;;;KAIT,oBAAoB;WACrB;WACA,kBAAkB;WAClB,sBAAsB;WACtB;aACE;aACA;aACA;aACA;aACA;aACA;aACA;aACA,QAAQ;;;KAIT,yBAAyB;WAC1B;WACA,kBAAkB;WAClB,QAAQ;WACR;aACE,gBAAgB;aAChB,+BAA+B;aAC/B,wBAAwB;aACxB,gBAAgB;aAChB;eAA+B;;aAC/B,SAAS;;;;;aAKT;aACA,QAAQ;;;KAIhB;WACM,SAAS;WACT;aACE,iBAAiB;aACjB,gCAAgC;aAChC,yBAAyB;aACzB,iBAAiB;aACjB;eAA+B;;aAC/B,SAAS;aACT;aACA,QAAQ;;WAEV,cAAc;;KAGb,gBAAgB,qBACzB,sBAAsB,oBAAoB;KAEjC,cAAc;KAEd;WACD,SAAS;;KAGR,gBAAgB,KAAK;EAC/B;;EAGA;EACA,WAAW;EACX;;EAEA"}
1
+ {"version":3,"file":"message.d.ts","names":[],"sources":["../../src/types/message.ts"],"mappings":";;;;KASY;YACA,uBAAuB;;KAGvB;WACD;WACA;WACA,SAAS;WACT,mBAAmB;WACnB;;KAGC;WACD;WACA;WACA,SAAS;WACT;WACA,mBAAmB;WACnB;;KAGC,yBAAyB;KAEzB;WAEG;WACA;WACA;WACA;WACA;WACA,mBAAmB;WACnB;;WAGA;WACA;WACA;WACA;WACA;WACA;WACA;WACA,mBAAmB;WACnB;;KAGH;WACD;WACA;WACA;WACA,mBAAmB;;KAGlB;WACD;WACA;WACA;WACA;;WAEA;WACA,mBAAmB;WACnB;;;;;;;;;;;KAYC;WACD;WACA;aACE;aACA;;;KAID,gBAAgB;WACjB;WACA;WACA,MAAM;;;;;;;;;;;KAYL;;WAIG;;WAEA,QAAQ;;WAER,oBAAoB;;WAEpB;;;;;KAMH;;WAED,MAAM,4BAA4B;;;;;;;;;;KAWjC;WACD;;WAEA,MAAM;;WAEN;WACA;;KAGC;WACD;WACA;WACA;;WAEA;;cAGE;cAEA,cAAe;KAGhB;WACD,MAAM;;KAGL;KAMA;;WAED;;;;;;;WAOA,MAAM;;WAEN;WACA;;WAEA;;WAEA;IAAsB;IAAgB;;;;;;;;;;;;;KAarC;;;;;;cAOC,0BAA2B;WAC7B,UAAU;WACV;;KAGC;WAEG;WACA;WACA;;WAGA;WACA;WACA;;WAGA;WACA;WACA;WACA;;;WAIA;WACA;;KAGH,oBACV,QAAQ,oBACR;;WAGS;;WAEA;;WAEA;;;;;;WAMA,MAAM;;WAEN,SAAS;;WAET;;WAEA;;WAEA;;WAEA,SAAS;;WAET,MAAM;;WAEN,mBAAmB;;WAEnB,wBAAwB;;WAExB;IAAc;IAAe;;;WAE7B;aACE;;;;;;aAMA;;aAEA,UAAU;;aAEV;aACA;aACA;aACA;;aAEA,mBAAmB;;aAEnB;;aAEA;;aAEA;;;WAGF;;;;;WAKA,oBAAoB;;KAGnB,wBACR,kBACA,mBACA,kBACA,kBACA;KAEQ,6BACR,kBACA,uBACA,sBACA,oBACA,kBACA,mBACA,kBACA;KAEQ;WAEG;;WAGA;;WAGA;WACA;WAMA;;KAGH;WAEG;;WAGA;;WAGA;WACA;;KAQH;;WAGG;;WAEA;;WAGA;WACA;WACA,QAAQ;IAEnB;KAEQ;WAEG;;WAGA;WACA;;WAGA;WACA;;WAGA;WACA;WAOA,QAAQ;;KAGX;WACD;WACA;WACA;WACA;WACA;WACA;WACA;;KAGC;WACD;WACA;aAEM;aACA;;;KAKZ;WACM;WACA,WAAW;;KAGV,sBAAsB;WACvB;WACA,mBAAmB;WACnB;aACE;aACA;aACA;aACA;aACA;aACA;aACA,QAAQ;;;KAIT,oBAAoB;WACrB;WACA,kBAAkB;WAClB,sBAAsB;WACtB;aACE;aACA;aACA;aACA;aACA;aACA;aACA;aACA,QAAQ;;;KAIT,yBAAyB;WAC1B;WACA,kBAAkB;WAClB,QAAQ;WACR;aACE,gBAAgB;aAChB,+BAA+B;aAC/B,wBAAwB;aACxB,gBAAgB;aAChB;eAA+B;;aAC/B,SAAS;;;;;aAKT;aACA,QAAQ;;;KAIhB;WACM,SAAS;WACT;aACE,iBAAiB;aACjB,gCAAgC;aAChC,yBAAyB;aACzB,iBAAiB;aACjB;eAA+B;;aAC/B,SAAS;aACT;aACA,QAAQ;;WAEV,cAAc;;KAGb,gBAAgB,qBACzB,sBAAsB,oBAAoB;KAEjC,cAAc;KAEd;WACD,SAAS;;KAGR,gBAAgB,KAAK;EAC/B;;EAGA;EACA,WAAW;EACX;;EAEA"}
@@ -1,7 +1,13 @@
1
1
  //#region src/types/message.ts
2
2
  const MCP_APP_URI_SCHEME = "ui://";
3
3
  const isMcpAppUri = (uri) => !!uri?.startsWith(MCP_APP_URI_SCHEME);
4
+ /**
5
+ * Whether the request asks for a free-form answer, on its own or alongside its
6
+ * options. Renderers read this to decide whether to offer a text affordance,
7
+ * and the runtime reads it to reject a `text` response the host cannot record.
8
+ */
9
+ const toolApprovalAcceptsText = (approval) => approval.display === "text" || approval.allowFreeform === true;
4
10
  //#endregion
5
- export { MCP_APP_URI_SCHEME, isMcpAppUri };
11
+ export { MCP_APP_URI_SCHEME, isMcpAppUri, toolApprovalAcceptsText };
6
12
 
7
13
  //# sourceMappingURL=message.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"message.js","names":["ReadonlyJSONObject","ReadonlyJSONValue","ToolCallTiming","ToolModelContentPart","CompleteAttachment","PartProviderMetadata","providerName","TextMessagePart","type","text","status","MessagePartStreamStatus","providerMetadata","parentId","ReasoningMessagePart","unstable_summary","SourceProviderMetadata","SourceMessagePart","sourceType","id","url","title","mediaType","filename","ImageMessagePart","image","FileMessagePart","data","mimeType","Unstable_AudioMessagePart","audio","format","DataMessagePart","name","T","GenerativeUINode","component","props","Record","children","key","GenerativeUISpec","root","GenerativeUIMessagePart","spec","McpAppMetadata","resourceUri","visibility","serverId","MCP_APP_URI_SCHEME","isMcpAppUri","uri","startsWith","ToolCallMessagePartMcpMetadata","app","ToolApprovalOptionKind","ToolApprovalOption","kind","label","description","grants","confirm","ToolApprovalResponse","approved","reason","optionId","ToolCallMessagePart","toolCallId","toolName","args","TArgs","result","TResult","isError","argsText","artifact","timing","mcp","modelContent","interrupt","payload","approval","isAutomatic","options","resolution","messages","ThreadMessage","ThreadUserMessagePart","ThreadAssistantMessagePart","MessagePartStatus","error","ToolCallMessagePartStatus","MessageStatus","MessageTiming","streamStartTime","firstTokenTime","totalStreamTime","tokenCount","tokensPerSecond","totalChunks","toolCallCount","ThreadStep","messageId","usage","inputTokens","outputTokens","MessageCommonProps","createdAt","Date","ThreadSystemMessage","role","content","metadata","unstable_state","unstable_annotations","unstable_data","steps","submittedFeedback","custom","ThreadUserMessage","attachments","isOptimistic","ThreadAssistantMessage","BaseThreadMessage","MessageRole","RunConfig","AppendMessage","Omit","sourceId","runConfig","startRun","steer"],"sources":["../../src/types/message.ts"],"sourcesContent":["import type {\n ReadonlyJSONObject,\n ReadonlyJSONValue,\n} from \"assistant-stream/utils\";\nimport type { ToolCallTiming, ToolModelContentPart } from \"assistant-stream\";\nimport type { CompleteAttachment } from \"./attachment\";\n\nexport type { ToolCallTiming, ToolModelContentPart };\n\nexport type PartProviderMetadata = {\n readonly [providerName: string]: ReadonlyJSONObject;\n};\n\nexport type TextMessagePart = {\n readonly type: \"text\";\n readonly text: string;\n readonly status?: MessagePartStreamStatus;\n readonly providerMetadata?: PartProviderMetadata;\n readonly parentId?: string;\n};\n\nexport type ReasoningMessagePart = {\n readonly type: \"reasoning\";\n readonly text: string;\n readonly status?: MessagePartStreamStatus;\n readonly unstable_summary?: string;\n readonly providerMetadata?: PartProviderMetadata;\n readonly parentId?: string;\n};\n\nexport type SourceProviderMetadata = PartProviderMetadata;\n\nexport type SourceMessagePart =\n | {\n readonly type: \"source\";\n readonly sourceType: \"url\";\n readonly id: string;\n readonly url: string;\n readonly title?: string;\n readonly providerMetadata?: SourceProviderMetadata;\n readonly parentId?: string;\n }\n | {\n readonly type: \"source\";\n readonly sourceType: \"document\";\n readonly id: string;\n readonly url?: undefined;\n readonly title: string;\n readonly mediaType: string;\n readonly filename?: string;\n readonly providerMetadata?: SourceProviderMetadata;\n readonly parentId?: string;\n };\n\nexport type ImageMessagePart = {\n readonly type: \"image\";\n readonly image: string;\n readonly filename?: string;\n readonly providerMetadata?: PartProviderMetadata;\n};\n\nexport type FileMessagePart = {\n readonly type: \"file\";\n readonly filename?: string;\n readonly data: string;\n readonly mimeType: string;\n /** How `data` goes on the wire: a url or id reference; omitted = inferred (http(s) → url, else base64). \"url\" is honored by the LangChain-family, A2A, AG-UI, and Google ADK runtimes; \"id\" by the LangChain family only. */\n readonly sourceType?: \"url\" | \"id\";\n readonly providerMetadata?: PartProviderMetadata;\n readonly parentId?: string;\n};\n\n/**\n * @deprecated Use {@link FileMessagePart} with an `audio/*` mime type. `file`\n * is the carrier for every non-image binary modality: it is a member of both\n * the user and assistant unions, carries a filename, and can declare how its\n * payload goes on the wire, none of which this shape can express. The payload\n * form a `file` part needs is still adapter specific; see the Part Types\n * section of the message primitive docs. Honored everywhere it is accepted; it\n * will not gain fields.\n */\nexport type Unstable_AudioMessagePart = {\n readonly type: \"audio\";\n readonly audio: {\n readonly data: string;\n readonly format: \"mp3\" | \"wav\";\n };\n};\n\nexport type DataMessagePart<T = any> = {\n readonly type: \"data\";\n readonly name: string;\n readonly data: T;\n};\n\n/**\n * A JSON spec describing a tree of UI components to render.\n *\n * The agent emits a {@link GenerativeUIMessagePart} containing this spec, and\n * the consumer-provided component allowlist is used to resolve `component`\n * names. Any component referenced that is not present in the allowlist is\n * rejected with a typed error — the allowlist is the security boundary in the\n * default same-realm rendering path.\n */\nexport type GenerativeUINode =\n | string\n | {\n /** Allowlisted component name (resolved against the consumer registry). */\n readonly component: string;\n /** Props passed to the resolved component (must be JSON-serializable). */\n readonly props?: Record<string, unknown>;\n /** Optional children — strings render as text, objects recurse. */\n readonly children?: readonly GenerativeUINode[];\n /** Optional stable key for React reconciliation. */\n readonly key?: string;\n };\n\n/**\n * The root spec for a generative UI tree.\n */\nexport type GenerativeUISpec = {\n /** Root node(s) to render. */\n readonly root: GenerativeUINode | readonly GenerativeUINode[];\n};\n\n/**\n * A message part that carries a JSON spec describing UI to render.\n *\n * Render with `<MessagePrimitive.GenerativeUI components={...} />`. The\n * primitive resolves component names against the consumer-provided allowlist\n * — any unknown name throws a typed error rather than rendering. Stream-\n * friendly: a partially-streamed spec renders progressively.\n */\nexport type GenerativeUIMessagePart = {\n readonly type: \"generative-ui\";\n /** The JSON spec describing the UI tree. */\n readonly spec: GenerativeUISpec;\n /** Optional id (useful for replays / stable keys). */\n readonly id?: string;\n readonly parentId?: string;\n};\n\nexport type McpAppMetadata = {\n readonly resourceUri: string;\n readonly mimeType?: string;\n readonly visibility?: readonly (\"model\" | \"app\")[];\n /** Routable server identity emitted by the agent stack when multiple MCP servers back one agent; for @ag-ui/mcp-apps-middleware this is the configured serverId, falling back to its serverHash. */\n readonly serverId?: string;\n};\n\nexport const MCP_APP_URI_SCHEME = \"ui://\";\n\nexport const isMcpAppUri = (uri: string | undefined): boolean =>\n !!uri?.startsWith(MCP_APP_URI_SCHEME);\n\nexport type ToolCallMessagePartMcpMetadata = {\n readonly app?: McpAppMetadata;\n};\n\nexport type ToolApprovalOptionKind =\n | \"allow-once\"\n | \"allow-always\"\n | \"reject-once\"\n | \"reject-always\";\n\nexport type ToolApprovalOption = {\n /** Opaque, host-defined identifier. Scope semantics (session vs project vs global) belong to the option supplier. */\n readonly id: string;\n /**\n * Decision class. Drives approved-resolution and default rendering.\n * Open union: `_`-prefixed custom kinds are never auto-resolved; they must\n * be answered with an explicit `approved` value (optionally alongside the\n * `optionId`), which is what the default kit sends when one is chosen.\n */\n readonly kind: ToolApprovalOptionKind | (string & {});\n /** Human label. Renderers supply defaults per kind when omitted. */\n readonly label?: string;\n readonly description?: string;\n /** Patterns or rules this option would persist, shown before the user commits. Supplied by the host; never derived by the library. */\n readonly grants?: readonly string[];\n /** Opt-in confirmation step before this option resolves. */\n readonly confirm?: boolean | { title?: string; description?: string };\n};\n\nexport type ToolApprovalResponse =\n | { readonly approved: boolean; readonly reason?: string }\n | { readonly optionId: string; readonly reason?: string }\n | {\n readonly approved: boolean;\n readonly optionId: string;\n readonly reason?: string;\n };\n\nexport type ToolCallMessagePart<\n TArgs = ReadonlyJSONObject,\n TResult = unknown,\n> = {\n /** Identifies this part as a tool call. */\n readonly type: \"tool-call\";\n /** Stable identifier for this invocation of the tool. */\n readonly toolCallId: string;\n /** Name of the tool requested by the model. */\n readonly toolName: string;\n /**\n * Arguments supplied by the model. During streaming this is a partial parse:\n * fields may be missing or incomplete. From a tool-call renderer, use\n * `useToolArgsStatus` to detect which fields are still arriving.\n */\n readonly args: TArgs;\n /** Result returned by the tool, if it has completed. */\n readonly result?: TResult | undefined;\n /** Whether the result represents a tool execution error. */\n readonly isError?: boolean | undefined;\n /** Raw JSON argument text streamed by the model. */\n readonly argsText: string;\n /** UI-only artifact associated with the tool result. */\n readonly artifact?: unknown;\n /** Wall-clock timing for this call, when the runtime or host tracks it. */\n readonly timing?: ToolCallTiming;\n /** MCP app metadata associated with this tool call, when present. */\n readonly mcp?: ToolCallMessagePartMcpMetadata;\n /** Provider metadata associated with this tool call, when present. */\n readonly providerMetadata?: PartProviderMetadata;\n /** Content returned to the model for this tool result. */\n readonly modelContent?: readonly ToolModelContentPart[] | undefined;\n /** Human-input request that must be resolved before the run can continue. */\n readonly interrupt?: { type: \"human\"; payload: unknown };\n /** Server-side approval gate. `respondToApproval` may only be called while `approved` is undefined and no `resolution` is recorded. */\n readonly approval?: {\n readonly id: string;\n readonly approved?: boolean;\n readonly reason?: string;\n readonly isAutomatic?: boolean;\n /** Available decisions for this call. Absent: a plain allow / deny pair. */\n readonly options?: readonly ToolApprovalOption[];\n /** The option chosen at resolution, when options were present. */\n readonly optionId?: string;\n /** Terminal non-decision state: the request was cancelled or expired without a user decision. Set by the host. */\n readonly resolution?: \"cancelled\" | \"expired\";\n };\n /** Parent message-part ID when this part belongs to a nested structure. */\n readonly parentId?: string;\n /**\n * Nested thread messages produced by this tool call, for example a sub-agent\n * conversation.\n */\n readonly messages?: readonly ThreadMessage[];\n};\n\nexport type ThreadUserMessagePart =\n | TextMessagePart\n | ImageMessagePart\n | FileMessagePart\n | DataMessagePart\n | Unstable_AudioMessagePart;\n\nexport type ThreadAssistantMessagePart =\n | TextMessagePart\n | ReasoningMessagePart\n | ToolCallMessagePart\n | SourceMessagePart\n | FileMessagePart\n | ImageMessagePart\n | DataMessagePart\n | GenerativeUIMessagePart;\n\nexport type MessagePartStatus =\n | {\n readonly type: \"running\";\n }\n | {\n readonly type: \"complete\";\n }\n | {\n readonly type: \"incomplete\";\n readonly reason:\n | \"cancelled\"\n | \"length\"\n | \"content-filter\"\n | \"other\"\n | \"error\";\n readonly error?: unknown;\n };\n\nexport type MessagePartStreamStatus =\n | {\n readonly type: \"running\";\n }\n | {\n readonly type: \"complete\";\n }\n | {\n readonly type: \"incomplete\";\n readonly reason:\n | \"cancelled\"\n | \"length\"\n | \"content-filter\"\n | \"other\"\n | \"error\";\n };\n\nexport type ToolCallMessagePartStatus =\n | {\n /** The tool call is waiting for UI or human input before continuing. */\n readonly type: \"requires-action\";\n /** Reason the tool call requires action. */\n readonly reason: \"tool-calls\" | \"interrupt\";\n }\n | {\n readonly type: \"incomplete\";\n readonly reason: \"tool-calls\";\n readonly error?: ReadonlyJSONValue;\n }\n | MessagePartStatus;\n\nexport type MessageStatus =\n | {\n readonly type: \"running\";\n }\n | {\n readonly type: \"requires-action\";\n readonly reason: \"tool-calls\" | \"interrupt\";\n }\n | {\n readonly type: \"complete\";\n readonly reason: \"stop\" | \"unknown\";\n }\n | {\n readonly type: \"incomplete\";\n readonly reason:\n | \"cancelled\"\n | \"tool-calls\"\n | \"length\"\n | \"content-filter\"\n | \"other\"\n | \"error\";\n readonly error?: ReadonlyJSONValue;\n };\n\nexport type MessageTiming = {\n readonly streamStartTime: number;\n readonly firstTokenTime?: number;\n readonly totalStreamTime?: number;\n readonly tokenCount?: number;\n readonly tokensPerSecond?: number;\n readonly totalChunks: number;\n readonly toolCallCount: number;\n};\n\nexport type ThreadStep = {\n readonly messageId?: string;\n readonly usage?:\n | {\n readonly inputTokens: number;\n readonly outputTokens: number;\n }\n | undefined;\n};\n\ntype MessageCommonProps = {\n readonly id: string;\n readonly createdAt: Date;\n};\n\nexport type ThreadSystemMessage = MessageCommonProps & {\n readonly role: \"system\";\n readonly content: readonly [TextMessagePart];\n readonly metadata: {\n readonly unstable_state?: undefined;\n readonly unstable_annotations?: undefined;\n readonly unstable_data?: undefined;\n readonly steps?: undefined;\n readonly submittedFeedback?: undefined;\n readonly timing?: undefined;\n readonly custom: Record<string, unknown>;\n };\n};\n\nexport type ThreadUserMessage = MessageCommonProps & {\n readonly role: \"user\";\n readonly content: readonly ThreadUserMessagePart[];\n readonly attachments: readonly CompleteAttachment[];\n readonly metadata: {\n readonly unstable_state?: undefined;\n readonly unstable_annotations?: undefined;\n readonly unstable_data?: undefined;\n readonly steps?: undefined;\n readonly submittedFeedback?: undefined;\n readonly timing?: undefined;\n readonly isOptimistic?: boolean;\n readonly custom: Record<string, unknown>;\n };\n};\n\nexport type ThreadAssistantMessage = MessageCommonProps & {\n readonly role: \"assistant\";\n readonly content: readonly ThreadAssistantMessagePart[];\n readonly status: MessageStatus;\n readonly metadata: {\n readonly unstable_state: ReadonlyJSONValue;\n readonly unstable_annotations: readonly ReadonlyJSONValue[];\n readonly unstable_data: readonly ReadonlyJSONValue[];\n readonly steps: readonly ThreadStep[];\n readonly submittedFeedback?: { readonly type: \"positive\" | \"negative\" };\n readonly timing?: MessageTiming;\n /**\n * Marks a client-side optimistic placeholder. Such messages are evicted\n * once off the head branch and are never persisted.\n */\n readonly isOptimistic?: boolean;\n readonly custom: Record<string, unknown>;\n };\n};\n\ntype BaseThreadMessage = {\n readonly status?: ThreadAssistantMessage[\"status\"];\n readonly metadata: {\n readonly unstable_state?: ReadonlyJSONValue;\n readonly unstable_annotations?: readonly ReadonlyJSONValue[];\n readonly unstable_data?: readonly ReadonlyJSONValue[];\n readonly steps?: readonly ThreadStep[];\n readonly submittedFeedback?: { readonly type: \"positive\" | \"negative\" };\n readonly timing?: MessageTiming;\n readonly isOptimistic?: boolean;\n readonly custom: Record<string, unknown>;\n };\n readonly attachments?: ThreadUserMessage[\"attachments\"];\n};\n\nexport type ThreadMessage = BaseThreadMessage &\n (ThreadSystemMessage | ThreadUserMessage | ThreadAssistantMessage);\n\nexport type MessageRole = ThreadMessage[\"role\"];\n\nexport type RunConfig = {\n readonly custom?: Record<string, unknown>;\n};\n\nexport type AppendMessage = Omit<ThreadMessage, \"id\"> & {\n parentId: string | null;\n\n /** The ID of the message that was edited or undefined. */\n sourceId: string | null;\n runConfig: RunConfig | undefined;\n startRun?: boolean | undefined;\n /** Process this message next; only meaningful for queue-capable runtimes. */\n steer?: boolean | undefined;\n};\n"],"mappings":";AAsJA,MAAaiD,qBAAqB;AAElC,MAAaC,eAAeC,QAC1B,CAAC,CAACA,KAAKC,WAAWH,kBAAkB"}
1
+ {"version":3,"file":"message.js","names":["ReadonlyJSONObject","ReadonlyJSONValue","ToolCallTiming","ToolModelContentPart","CompleteAttachment","PartProviderMetadata","providerName","TextMessagePart","type","text","status","MessagePartStreamStatus","providerMetadata","parentId","ReasoningMessagePart","unstable_summary","SourceProviderMetadata","SourceMessagePart","sourceType","id","url","title","mediaType","filename","ImageMessagePart","image","FileMessagePart","data","mimeType","Unstable_AudioMessagePart","audio","format","DataMessagePart","name","T","GenerativeUINode","component","props","Record","children","key","GenerativeUISpec","root","GenerativeUIMessagePart","spec","McpAppMetadata","resourceUri","visibility","serverId","MCP_APP_URI_SCHEME","isMcpAppUri","uri","startsWith","ToolCallMessagePartMcpMetadata","app","ToolApprovalOptionKind","ToolApprovalOption","kind","label","description","grants","confirm","ToolApprovalDisplay","toolApprovalAcceptsText","approval","display","allowFreeform","ToolApprovalResponse","approved","reason","optionId","ToolCallMessagePart","toolCallId","toolName","args","TArgs","result","TResult","isError","argsText","artifact","timing","mcp","modelContent","interrupt","payload","prompt","isAutomatic","options","resolution","messages","ThreadMessage","ThreadUserMessagePart","ThreadAssistantMessagePart","MessagePartStatus","error","ToolCallMessagePartStatus","MessageStatus","MessageTiming","streamStartTime","firstTokenTime","totalStreamTime","tokenCount","tokensPerSecond","totalChunks","toolCallCount","ThreadStep","messageId","usage","inputTokens","outputTokens","MessageCommonProps","createdAt","Date","ThreadSystemMessage","role","content","metadata","unstable_state","unstable_annotations","unstable_data","steps","submittedFeedback","custom","ThreadUserMessage","attachments","isOptimistic","ThreadAssistantMessage","BaseThreadMessage","MessageRole","RunConfig","AppendMessage","Omit","sourceId","runConfig","startRun","steer"],"sources":["../../src/types/message.ts"],"sourcesContent":["import type {\n ReadonlyJSONObject,\n ReadonlyJSONValue,\n} from \"assistant-stream/utils\";\nimport type { ToolCallTiming, ToolModelContentPart } from \"assistant-stream\";\nimport type { CompleteAttachment } from \"./attachment\";\n\nexport type { ToolCallTiming, ToolModelContentPart };\n\nexport type PartProviderMetadata = {\n readonly [providerName: string]: ReadonlyJSONObject;\n};\n\nexport type TextMessagePart = {\n readonly type: \"text\";\n readonly text: string;\n readonly status?: MessagePartStreamStatus;\n readonly providerMetadata?: PartProviderMetadata;\n readonly parentId?: string;\n};\n\nexport type ReasoningMessagePart = {\n readonly type: \"reasoning\";\n readonly text: string;\n readonly status?: MessagePartStreamStatus;\n readonly unstable_summary?: string;\n readonly providerMetadata?: PartProviderMetadata;\n readonly parentId?: string;\n};\n\nexport type SourceProviderMetadata = PartProviderMetadata;\n\nexport type SourceMessagePart =\n | {\n readonly type: \"source\";\n readonly sourceType: \"url\";\n readonly id: string;\n readonly url: string;\n readonly title?: string;\n readonly providerMetadata?: SourceProviderMetadata;\n readonly parentId?: string;\n }\n | {\n readonly type: \"source\";\n readonly sourceType: \"document\";\n readonly id: string;\n readonly url?: undefined;\n readonly title: string;\n readonly mediaType: string;\n readonly filename?: string;\n readonly providerMetadata?: SourceProviderMetadata;\n readonly parentId?: string;\n };\n\nexport type ImageMessagePart = {\n readonly type: \"image\";\n readonly image: string;\n readonly filename?: string;\n readonly providerMetadata?: PartProviderMetadata;\n};\n\nexport type FileMessagePart = {\n readonly type: \"file\";\n readonly filename?: string;\n readonly data: string;\n readonly mimeType: string;\n /** How `data` goes on the wire: a url or id reference; omitted = inferred (http(s) → url, else base64). \"url\" is honored by the LangChain-family, A2A, AG-UI, and Google ADK runtimes; \"id\" by the LangChain family only. */\n readonly sourceType?: \"url\" | \"id\";\n readonly providerMetadata?: PartProviderMetadata;\n readonly parentId?: string;\n};\n\n/**\n * @deprecated Use {@link FileMessagePart} with an `audio/*` mime type. `file`\n * is the carrier for every non-image binary modality: it is a member of both\n * the user and assistant unions, carries a filename, and can declare how its\n * payload goes on the wire, none of which this shape can express. The payload\n * form a `file` part needs is still adapter specific; see the Part Types\n * section of the message primitive docs. Honored everywhere it is accepted; it\n * will not gain fields.\n */\nexport type Unstable_AudioMessagePart = {\n readonly type: \"audio\";\n readonly audio: {\n readonly data: string;\n readonly format: \"mp3\" | \"wav\";\n };\n};\n\nexport type DataMessagePart<T = any> = {\n readonly type: \"data\";\n readonly name: string;\n readonly data: T;\n};\n\n/**\n * A JSON spec describing a tree of UI components to render.\n *\n * The agent emits a {@link GenerativeUIMessagePart} containing this spec, and\n * the consumer-provided component allowlist is used to resolve `component`\n * names. Any component referenced that is not present in the allowlist is\n * rejected with a typed error — the allowlist is the security boundary in the\n * default same-realm rendering path.\n */\nexport type GenerativeUINode =\n | string\n | {\n /** Allowlisted component name (resolved against the consumer registry). */\n readonly component: string;\n /** Props passed to the resolved component (must be JSON-serializable). */\n readonly props?: Record<string, unknown>;\n /** Optional children — strings render as text, objects recurse. */\n readonly children?: readonly GenerativeUINode[];\n /** Optional stable key for React reconciliation. */\n readonly key?: string;\n };\n\n/**\n * The root spec for a generative UI tree.\n */\nexport type GenerativeUISpec = {\n /** Root node(s) to render. */\n readonly root: GenerativeUINode | readonly GenerativeUINode[];\n};\n\n/**\n * A message part that carries a JSON spec describing UI to render.\n *\n * Render with `<MessagePrimitive.GenerativeUI components={...} />`. The\n * primitive resolves component names against the consumer-provided allowlist\n * — any unknown name throws a typed error rather than rendering. Stream-\n * friendly: a partially-streamed spec renders progressively.\n */\nexport type GenerativeUIMessagePart = {\n readonly type: \"generative-ui\";\n /** The JSON spec describing the UI tree. */\n readonly spec: GenerativeUISpec;\n /** Optional id (useful for replays / stable keys). */\n readonly id?: string;\n readonly parentId?: string;\n};\n\nexport type McpAppMetadata = {\n readonly resourceUri: string;\n readonly mimeType?: string;\n readonly visibility?: readonly (\"model\" | \"app\")[];\n /** Routable server identity emitted by the agent stack when multiple MCP servers back one agent; for @ag-ui/mcp-apps-middleware this is the configured serverId, falling back to its serverHash. */\n readonly serverId?: string;\n};\n\nexport const MCP_APP_URI_SCHEME = \"ui://\";\n\nexport const isMcpAppUri = (uri: string | undefined): boolean =>\n !!uri?.startsWith(MCP_APP_URI_SCHEME);\n\nexport type ToolCallMessagePartMcpMetadata = {\n readonly app?: McpAppMetadata;\n};\n\nexport type ToolApprovalOptionKind =\n | \"allow-once\"\n | \"allow-always\"\n | \"reject-once\"\n | \"reject-always\";\n\nexport type ToolApprovalOption = {\n /** Opaque, host-defined identifier. Scope semantics (session vs project vs global) belong to the option supplier. */\n readonly id: string;\n /**\n * Decision class. Drives approved-resolution and default rendering.\n * Open union: `_`-prefixed custom kinds are never auto-resolved; they must\n * be answered with an explicit `approved` value (optionally alongside the\n * `optionId`), which is what the default kit sends when one is chosen.\n */\n readonly kind: ToolApprovalOptionKind | (string & {});\n /** Human label. Renderers supply defaults per kind when omitted. */\n readonly label?: string;\n readonly description?: string;\n /** Patterns or rules this option would persist, shown before the user commits. Supplied by the host; never derived by the library. */\n readonly grants?: readonly string[];\n /** Opt-in confirmation step before this option resolves. */\n readonly confirm?: boolean | { title?: string; description?: string };\n};\n\n/**\n * How an approval request should be presented, and which answers it accepts.\n * Absent: `\"decision\"`.\n *\n * The approval seam carries a host-owned request addressed by its own id and\n * resolved once; a request wanting an answer these modes cannot express stays\n * on the `interrupt` seam, which hands the tool an arbitrary payload instead.\n * Unlike {@link ToolApprovalOptionKind} the set is closed, because a renderer\n * that cannot cover every mode exhaustively is back to guessing the affordance.\n */\nexport type ToolApprovalDisplay = \"decision\" | \"select\" | \"text\";\n\n/**\n * Whether the request asks for a free-form answer, on its own or alongside its\n * options. Renderers read this to decide whether to offer a text affordance,\n * and the runtime reads it to reject a `text` response the host cannot record.\n */\nexport const toolApprovalAcceptsText = (approval: {\n readonly display?: ToolApprovalDisplay;\n readonly allowFreeform?: boolean;\n}): boolean => approval.display === \"text\" || approval.allowFreeform === true;\n\nexport type ToolApprovalResponse =\n | {\n readonly approved: boolean;\n readonly text?: string;\n readonly reason?: string;\n }\n | {\n readonly optionId: string;\n readonly text?: string;\n readonly reason?: string;\n }\n | {\n readonly approved: boolean;\n readonly optionId: string;\n readonly text?: string;\n readonly reason?: string;\n }\n | {\n /** Answer to a request that asks a question rather than for a decision. */\n readonly text: string;\n readonly reason?: string;\n };\n\nexport type ToolCallMessagePart<\n TArgs = ReadonlyJSONObject,\n TResult = unknown,\n> = {\n /** Identifies this part as a tool call. */\n readonly type: \"tool-call\";\n /** Stable identifier for this invocation of the tool. */\n readonly toolCallId: string;\n /** Name of the tool requested by the model. */\n readonly toolName: string;\n /**\n * Arguments supplied by the model. During streaming this is a partial parse:\n * fields may be missing or incomplete. From a tool-call renderer, use\n * `useToolArgsStatus` to detect which fields are still arriving.\n */\n readonly args: TArgs;\n /** Result returned by the tool, if it has completed. */\n readonly result?: TResult | undefined;\n /** Whether the result represents a tool execution error. */\n readonly isError?: boolean | undefined;\n /** Raw JSON argument text streamed by the model. */\n readonly argsText: string;\n /** UI-only artifact associated with the tool result. */\n readonly artifact?: unknown;\n /** Wall-clock timing for this call, when the runtime or host tracks it. */\n readonly timing?: ToolCallTiming;\n /** MCP app metadata associated with this tool call, when present. */\n readonly mcp?: ToolCallMessagePartMcpMetadata;\n /** Provider metadata associated with this tool call, when present. */\n readonly providerMetadata?: PartProviderMetadata;\n /** Content returned to the model for this tool result. */\n readonly modelContent?: readonly ToolModelContentPart[] | undefined;\n /** Human-input request that must be resolved before the run can continue. */\n readonly interrupt?: { type: \"human\"; payload: unknown };\n /** Server-side approval gate. `respondToApproval` may only be called while `approved` is undefined and no `resolution` is recorded. */\n readonly approval?: {\n readonly id: string;\n /**\n * The question put to the user. A request that asks for something other\n * than permission carries it here, so a renderer can show the question\n * rather than inferring a decision from the tool name.\n */\n readonly prompt?: string;\n /** How the request should be presented. Absent: a plain decision. */\n readonly display?: ToolApprovalDisplay;\n /** Whether a free-form answer is accepted alongside the options. */\n readonly allowFreeform?: boolean;\n readonly approved?: boolean;\n readonly reason?: string;\n readonly isAutomatic?: boolean;\n /** Available decisions for this call. Absent: a plain allow / deny pair. */\n readonly options?: readonly ToolApprovalOption[];\n /** The option chosen at resolution, when options were present. */\n readonly optionId?: string;\n /** The free-form answer recorded at resolution, when one was given. */\n readonly text?: string;\n /** Terminal non-decision state: the request was cancelled or expired without a user decision. Set by the host. */\n readonly resolution?: \"cancelled\" | \"expired\";\n };\n /** Parent message-part ID when this part belongs to a nested structure. */\n readonly parentId?: string;\n /**\n * Nested thread messages produced by this tool call, for example a sub-agent\n * conversation.\n */\n readonly messages?: readonly ThreadMessage[];\n};\n\nexport type ThreadUserMessagePart =\n | TextMessagePart\n | ImageMessagePart\n | FileMessagePart\n | DataMessagePart\n | Unstable_AudioMessagePart;\n\nexport type ThreadAssistantMessagePart =\n | TextMessagePart\n | ReasoningMessagePart\n | ToolCallMessagePart\n | SourceMessagePart\n | FileMessagePart\n | ImageMessagePart\n | DataMessagePart\n | GenerativeUIMessagePart;\n\nexport type MessagePartStatus =\n | {\n readonly type: \"running\";\n }\n | {\n readonly type: \"complete\";\n }\n | {\n readonly type: \"incomplete\";\n readonly reason:\n | \"cancelled\"\n | \"length\"\n | \"content-filter\"\n | \"other\"\n | \"error\";\n readonly error?: unknown;\n };\n\nexport type MessagePartStreamStatus =\n | {\n readonly type: \"running\";\n }\n | {\n readonly type: \"complete\";\n }\n | {\n readonly type: \"incomplete\";\n readonly reason:\n | \"cancelled\"\n | \"length\"\n | \"content-filter\"\n | \"other\"\n | \"error\";\n };\n\nexport type ToolCallMessagePartStatus =\n | {\n /** The tool call is waiting for UI or human input before continuing. */\n readonly type: \"requires-action\";\n /** Reason the tool call requires action. */\n readonly reason: \"tool-calls\" | \"interrupt\";\n }\n | {\n readonly type: \"incomplete\";\n readonly reason: \"tool-calls\";\n readonly error?: ReadonlyJSONValue;\n }\n | MessagePartStatus;\n\nexport type MessageStatus =\n | {\n readonly type: \"running\";\n }\n | {\n readonly type: \"requires-action\";\n readonly reason: \"tool-calls\" | \"interrupt\";\n }\n | {\n readonly type: \"complete\";\n readonly reason: \"stop\" | \"unknown\";\n }\n | {\n readonly type: \"incomplete\";\n readonly reason:\n | \"cancelled\"\n | \"tool-calls\"\n | \"length\"\n | \"content-filter\"\n | \"other\"\n | \"error\";\n readonly error?: ReadonlyJSONValue;\n };\n\nexport type MessageTiming = {\n readonly streamStartTime: number;\n readonly firstTokenTime?: number;\n readonly totalStreamTime?: number;\n readonly tokenCount?: number;\n readonly tokensPerSecond?: number;\n readonly totalChunks: number;\n readonly toolCallCount: number;\n};\n\nexport type ThreadStep = {\n readonly messageId?: string;\n readonly usage?:\n | {\n readonly inputTokens: number;\n readonly outputTokens: number;\n }\n | undefined;\n};\n\ntype MessageCommonProps = {\n readonly id: string;\n readonly createdAt: Date;\n};\n\nexport type ThreadSystemMessage = MessageCommonProps & {\n readonly role: \"system\";\n readonly content: readonly [TextMessagePart];\n readonly metadata: {\n readonly unstable_state?: undefined;\n readonly unstable_annotations?: undefined;\n readonly unstable_data?: undefined;\n readonly steps?: undefined;\n readonly submittedFeedback?: undefined;\n readonly timing?: undefined;\n readonly custom: Record<string, unknown>;\n };\n};\n\nexport type ThreadUserMessage = MessageCommonProps & {\n readonly role: \"user\";\n readonly content: readonly ThreadUserMessagePart[];\n readonly attachments: readonly CompleteAttachment[];\n readonly metadata: {\n readonly unstable_state?: undefined;\n readonly unstable_annotations?: undefined;\n readonly unstable_data?: undefined;\n readonly steps?: undefined;\n readonly submittedFeedback?: undefined;\n readonly timing?: undefined;\n readonly isOptimistic?: boolean;\n readonly custom: Record<string, unknown>;\n };\n};\n\nexport type ThreadAssistantMessage = MessageCommonProps & {\n readonly role: \"assistant\";\n readonly content: readonly ThreadAssistantMessagePart[];\n readonly status: MessageStatus;\n readonly metadata: {\n readonly unstable_state: ReadonlyJSONValue;\n readonly unstable_annotations: readonly ReadonlyJSONValue[];\n readonly unstable_data: readonly ReadonlyJSONValue[];\n readonly steps: readonly ThreadStep[];\n readonly submittedFeedback?: { readonly type: \"positive\" | \"negative\" };\n readonly timing?: MessageTiming;\n /**\n * Marks a client-side optimistic placeholder. Such messages are evicted\n * once off the head branch and are never persisted.\n */\n readonly isOptimistic?: boolean;\n readonly custom: Record<string, unknown>;\n };\n};\n\ntype BaseThreadMessage = {\n readonly status?: ThreadAssistantMessage[\"status\"];\n readonly metadata: {\n readonly unstable_state?: ReadonlyJSONValue;\n readonly unstable_annotations?: readonly ReadonlyJSONValue[];\n readonly unstable_data?: readonly ReadonlyJSONValue[];\n readonly steps?: readonly ThreadStep[];\n readonly submittedFeedback?: { readonly type: \"positive\" | \"negative\" };\n readonly timing?: MessageTiming;\n readonly isOptimistic?: boolean;\n readonly custom: Record<string, unknown>;\n };\n readonly attachments?: ThreadUserMessage[\"attachments\"];\n};\n\nexport type ThreadMessage = BaseThreadMessage &\n (ThreadSystemMessage | ThreadUserMessage | ThreadAssistantMessage);\n\nexport type MessageRole = ThreadMessage[\"role\"];\n\nexport type RunConfig = {\n readonly custom?: Record<string, unknown>;\n};\n\nexport type AppendMessage = Omit<ThreadMessage, \"id\"> & {\n parentId: string | null;\n\n /** The ID of the message that was edited or undefined. */\n sourceId: string | null;\n runConfig: RunConfig | undefined;\n startRun?: boolean | undefined;\n /** Process this message next; only meaningful for queue-capable runtimes. */\n steer?: boolean | undefined;\n};\n"],"mappings":";AAsJA,MAAaiD,qBAAqB;AAElC,MAAaC,eAAeC,QAC1B,CAAC,CAACA,KAAKC,WAAWH,kBAAkB;;;;;;AAgDtC,MAAac,2BAA2BC,aAGzBA,SAASC,YAAY,UAAUD,SAASE,kBAAkB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@assistant-ui/core",
3
- "version": "0.3.16",
3
+ "version": "0.3.17",
4
4
  "description": "Framework-agnostic core runtime for assistant-ui",
5
5
  "keywords": [
6
6
  "assistant",
@@ -59,19 +59,18 @@
59
59
  ],
60
60
  "sideEffects": false,
61
61
  "dependencies": {
62
- "assistant-stream": "^0.3.40",
62
+ "assistant-stream": "^0.3.41",
63
63
  "nanoid": "^6.0.1"
64
64
  },
65
65
  "optionalDevDependencies": {
66
66
  "@assistant-ui/x-generative-compiler": ">=0.0.3"
67
67
  },
68
68
  "peerDependencies": {
69
- "@assistant-ui/store": "^0.3.0",
70
- "@assistant-ui/tap": "^0.9.0",
69
+ "@assistant-ui/store": "^0.3.12",
70
+ "@assistant-ui/tap": "^0.9.16",
71
71
  "@types/react": "*",
72
72
  "react": "^18 || ^19",
73
- "assistant-cloud": "^0.1.42",
74
- "zustand": "^5.0.11"
73
+ "assistant-cloud": "^0.1.42"
75
74
  },
76
75
  "peerDependenciesMeta": {
77
76
  "@types/react": {
@@ -82,24 +81,20 @@
82
81
  },
83
82
  "react": {
84
83
  "optional": true
85
- },
86
- "zustand": {
87
- "optional": true
88
84
  }
89
85
  },
90
86
  "devDependencies": {
91
- "@assistant-ui/store": "0.3.11",
92
- "@assistant-ui/tap": "0.9.15",
93
- "@assistant-ui/vite": "0.0.15",
94
- "@assistant-ui/x-buildutils": "0.0.24",
95
- "@testing-library/react": "^16.3.2",
87
+ "@assistant-ui/store": "0.3.12",
88
+ "@assistant-ui/tap": "0.9.16",
89
+ "@assistant-ui/vite": "0.0.16",
90
+ "@assistant-ui/x-buildutils": "0.0.25",
91
+ "@testing-library/react": "^16.3.3",
96
92
  "@types/react": "^19.2.18",
97
- "assistant-cloud": "0.1.42",
93
+ "assistant-cloud": "0.1.43",
98
94
  "jsdom": "^30.0.1",
99
95
  "react": "^19.2.8",
100
96
  "react-dom": "^19.2.8",
101
- "vitest": "^4.1.11",
102
- "zustand": "^5.0.15"
97
+ "vitest": "^4.1.11"
103
98
  },
104
99
  "publishConfig": {
105
100
  "access": "public",
package/src/index.ts CHANGED
@@ -22,6 +22,7 @@ export type {
22
22
  GenerativeUINode,
23
23
  GenerativeUISpec,
24
24
  Unstable_AudioMessagePart,
25
+ ToolApprovalDisplay,
25
26
  ToolApprovalOption,
26
27
  ToolApprovalOptionKind,
27
28
  ToolApprovalResponse,
@@ -50,7 +51,11 @@ export type {
50
51
  AppendMessage,
51
52
  } from "./types/message";
52
53
 
53
- export { MCP_APP_URI_SCHEME, isMcpAppUri } from "./types/message";
54
+ export {
55
+ MCP_APP_URI_SCHEME,
56
+ isMcpAppUri,
57
+ toolApprovalAcceptsText,
58
+ } from "./types/message";
54
59
 
55
60
  export type {
56
61
  Attachment,
package/src/internal.ts CHANGED
@@ -77,6 +77,14 @@ export {
77
77
  createToolCallCancellationStub,
78
78
  scanPendingToolCalls,
79
79
  } from "./runtime/utils/pending-tool-calls";
80
+ // Nested tool-call traversal, reused by framework adapters so the
81
+ // ToolCallMessagePart.messages tree is walked one way.
82
+ export {
83
+ iterateToolCallParts,
84
+ mapToolCallPartsDeep,
85
+ type ToolCallTreeEntry,
86
+ walkToolCallTree,
87
+ } from "./runtime/utils/tool-call-tree";
80
88
  export {
81
89
  createAbortableThreadLoad,
82
90
  type AbortableThreadLoadPurpose,
@@ -13,6 +13,7 @@ describe("AssistantFrameProvider", () => {
13
13
  origin: string,
14
14
  source: Window = parentWindow,
15
15
  id = "tool-call-1",
16
+ toolName = "sensitiveTool",
16
17
  ) => {
17
18
  messageHandler?.(
18
19
  new MessageEvent("message", {
@@ -21,7 +22,7 @@ describe("AssistantFrameProvider", () => {
21
22
  message: {
22
23
  type: "tool-call",
23
24
  id,
24
- toolName: "sensitiveTool",
25
+ toolName,
25
26
  args: {},
26
27
  },
27
28
  },
@@ -284,6 +285,179 @@ describe("AssistantFrameProvider", () => {
284
285
  expect(toolResults).toHaveLength(1);
285
286
  });
286
287
 
288
+ it("aborts in-flight tool calls when their provider is removed", async () => {
289
+ let toolSignal: AbortSignal | undefined;
290
+ const execute = vi.fn(
291
+ async (_args: unknown, context: { abortSignal: AbortSignal }) => {
292
+ toolSignal = context.abortSignal;
293
+ await new Promise<never>((_resolve, reject) => {
294
+ context.abortSignal.addEventListener(
295
+ "abort",
296
+ () => reject(context.abortSignal.reason),
297
+ { once: true },
298
+ );
299
+ });
300
+ },
301
+ );
302
+ const removeProvider = AssistantFrameProvider.addModelContextProvider({
303
+ getModelContext: () => ({ tools: { sensitiveTool: { execute } } }),
304
+ });
305
+
306
+ dispatchToolCall(window.location.origin);
307
+ await vi.waitFor(() => expect(toolSignal).toBeDefined());
308
+
309
+ removeProvider();
310
+
311
+ expect(toolSignal?.aborted).toBe(true);
312
+ expect(parentWindow.postMessage).toHaveBeenCalledWith(
313
+ {
314
+ channel: FRAME_MESSAGE_CHANNEL,
315
+ message: {
316
+ type: "tool-result",
317
+ id: "tool-call-1",
318
+ error: "AssistantFrame tool provider has been removed",
319
+ },
320
+ },
321
+ { targetOrigin: window.location.origin },
322
+ );
323
+ await new Promise((resolve) => setTimeout(resolve, 0));
324
+ const toolResults = vi
325
+ .mocked(parentWindow.postMessage)
326
+ .mock.calls.filter(
327
+ ([data]) =>
328
+ (data as { message?: { type?: string } }).message?.type ===
329
+ "tool-result",
330
+ );
331
+ expect(toolResults).toHaveLength(1);
332
+ });
333
+
334
+ it("keeps tool calls active while the same provider remains registered", async () => {
335
+ let toolSignal: AbortSignal | undefined;
336
+ const execute = vi.fn(
337
+ async (_args: unknown, context: { abortSignal: AbortSignal }) => {
338
+ toolSignal = context.abortSignal;
339
+ await new Promise<never>((_resolve, reject) => {
340
+ context.abortSignal.addEventListener(
341
+ "abort",
342
+ () => reject(context.abortSignal.reason),
343
+ { once: true },
344
+ );
345
+ });
346
+ },
347
+ );
348
+ const provider = {
349
+ getModelContext: () => ({ tools: { sensitiveTool: { execute } } }),
350
+ };
351
+ const removeFirst =
352
+ AssistantFrameProvider.addModelContextProvider(provider);
353
+ const removeSecond =
354
+ AssistantFrameProvider.addModelContextProvider(provider);
355
+
356
+ dispatchToolCall(window.location.origin);
357
+ await vi.waitFor(() => expect(toolSignal).toBeDefined());
358
+
359
+ removeFirst();
360
+ expect(toolSignal?.aborted).toBe(false);
361
+
362
+ removeSecond();
363
+ expect(toolSignal?.aborted).toBe(true);
364
+ });
365
+
366
+ it("cancels only calls owned by the removed provider", async () => {
367
+ const shadowedExecute = vi.fn(async () => "shadowed");
368
+ const removeShadowed = AssistantFrameProvider.addModelContextProvider({
369
+ getModelContext: () => ({
370
+ tools: { sensitiveTool: { execute: shadowedExecute } },
371
+ }),
372
+ });
373
+
374
+ let toolSignal: AbortSignal | undefined;
375
+ const execute = vi.fn(
376
+ async (_args: unknown, context: { abortSignal: AbortSignal }) => {
377
+ toolSignal = context.abortSignal;
378
+ await new Promise<never>((_resolve, reject) => {
379
+ context.abortSignal.addEventListener(
380
+ "abort",
381
+ () => reject(context.abortSignal.reason),
382
+ { once: true },
383
+ );
384
+ });
385
+ },
386
+ );
387
+ const removeOwner = AssistantFrameProvider.addModelContextProvider({
388
+ getModelContext: () => ({
389
+ tools: { sensitiveTool: { execute } },
390
+ }),
391
+ });
392
+
393
+ dispatchToolCall(window.location.origin);
394
+ await vi.waitFor(() => expect(toolSignal).toBeDefined());
395
+ expect(shadowedExecute).not.toHaveBeenCalled();
396
+
397
+ removeShadowed();
398
+ expect(toolSignal?.aborted).toBe(false);
399
+
400
+ removeOwner();
401
+ expect(toolSignal?.aborted).toBe(true);
402
+ });
403
+
404
+ it("keeps other providers' tool calls active when one is removed", async () => {
405
+ const signals = new Map<string, AbortSignal>();
406
+ const execute = vi.fn(
407
+ async (
408
+ _args: unknown,
409
+ context: { toolCallId: string; abortSignal: AbortSignal },
410
+ ) => {
411
+ signals.set(context.toolCallId, context.abortSignal);
412
+ await new Promise<never>((_resolve, reject) => {
413
+ context.abortSignal.addEventListener(
414
+ "abort",
415
+ () => reject(context.abortSignal.reason),
416
+ { once: true },
417
+ );
418
+ });
419
+ },
420
+ );
421
+ const removeFirst = AssistantFrameProvider.addModelContextProvider({
422
+ getModelContext: () => ({ tools: { firstTool: { execute } } }),
423
+ });
424
+ const removeSecond = AssistantFrameProvider.addModelContextProvider({
425
+ getModelContext: () => ({ tools: { secondTool: { execute } } }),
426
+ });
427
+
428
+ dispatchToolCall(
429
+ window.location.origin,
430
+ parentWindow,
431
+ "first-call",
432
+ "firstTool",
433
+ );
434
+ dispatchToolCall(
435
+ window.location.origin,
436
+ parentWindow,
437
+ "second-call",
438
+ "secondTool",
439
+ );
440
+ await vi.waitFor(() => expect(signals.size).toBe(2));
441
+
442
+ removeFirst();
443
+
444
+ expect(signals.get("first-call")?.aborted).toBe(true);
445
+ expect(signals.get("second-call")?.aborted).toBe(false);
446
+ const toolResults = vi
447
+ .mocked(parentWindow.postMessage)
448
+ .mock.calls.filter(
449
+ ([data]) =>
450
+ (data as { message?: { type?: string } }).message?.type ===
451
+ "tool-result",
452
+ );
453
+ expect(toolResults).toHaveLength(1);
454
+ expect(toolResults[0]?.[0]).toMatchObject({
455
+ message: { id: "first-call" },
456
+ });
457
+
458
+ removeSecond();
459
+ });
460
+
287
461
  it("upgrades a wildcard origin policy when a strict provider registers", async () => {
288
462
  AssistantFrameProvider.addModelContextProvider(
289
463
  { getModelContext: () => ({}) },
@@ -385,6 +559,48 @@ describe("AssistantFrameProvider", () => {
385
559
  ).not.toThrow();
386
560
  });
387
561
 
562
+ it("cancels reentrant tool calls when registration rolls back", async () => {
563
+ let toolSignal: AbortSignal | undefined;
564
+ const execute = vi.fn(
565
+ async (_args: unknown, context: { abortSignal: AbortSignal }) => {
566
+ toolSignal = context.abortSignal;
567
+ await new Promise<never>((_resolve, reject) => {
568
+ context.abortSignal.addEventListener(
569
+ "abort",
570
+ () => reject(context.abortSignal.reason),
571
+ { once: true },
572
+ );
573
+ });
574
+ },
575
+ );
576
+
577
+ expect(() =>
578
+ AssistantFrameProvider.addModelContextProvider({
579
+ getModelContext: () => ({
580
+ tools: { sensitiveTool: { execute } },
581
+ }),
582
+ subscribe: () => {
583
+ dispatchToolCall(window.location.origin);
584
+ throw new Error("subscribe failed");
585
+ },
586
+ }),
587
+ ).toThrow("subscribe failed");
588
+
589
+ expect(toolSignal?.aborted).toBe(true);
590
+ expect(parentWindow.postMessage).toHaveBeenCalledWith(
591
+ {
592
+ channel: FRAME_MESSAGE_CHANNEL,
593
+ message: {
594
+ type: "tool-result",
595
+ id: "tool-call-1",
596
+ error: "AssistantFrame tool provider has been removed",
597
+ },
598
+ },
599
+ { targetOrigin: window.location.origin },
600
+ );
601
+ await new Promise((resolve) => setTimeout(resolve, 0));
602
+ });
603
+
388
604
  it("keeps an existing registration when the same provider fails to register again", async () => {
389
605
  const execute = vi.fn(async () => "result");
390
606
  const firstUnsubscribe = vi.fn();