@assistant-ui/core 0.3.15 → 0.3.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (455) hide show
  1. package/dist/index.d.ts +2 -2
  2. package/dist/index.js +2 -2
  3. package/dist/index.js.map +1 -1
  4. package/dist/internal.d.ts +8 -2
  5. package/dist/internal.js +9 -3
  6. package/dist/model-context/frame/host.d.ts.map +1 -1
  7. package/dist/model-context/frame/host.js +3 -2
  8. package/dist/model-context/frame/host.js.map +1 -1
  9. package/dist/model-context/frame/provider.d.ts +5 -0
  10. package/dist/model-context/frame/provider.d.ts.map +1 -1
  11. package/dist/model-context/frame/provider.js +141 -28
  12. package/dist/model-context/frame/provider.js.map +1 -1
  13. package/dist/model-context/interactable-composer-metadata.d.ts +1 -1
  14. package/dist/model-context/interactable-composer-metadata.js +1 -1
  15. package/dist/model-context/interactable-composer-metadata.js.map +1 -1
  16. package/dist/model-context/registry.d.ts +2 -2
  17. package/dist/model-context/registry.d.ts.map +1 -1
  18. package/dist/model-context/registry.js +9 -28
  19. package/dist/model-context/registry.js.map +1 -1
  20. package/dist/model-context/tool-call-text.d.ts +26 -0
  21. package/dist/model-context/tool-call-text.d.ts.map +1 -0
  22. package/dist/model-context/tool-call-text.js +18 -0
  23. package/dist/model-context/tool-call-text.js.map +1 -0
  24. package/dist/react/RuntimeAdapter.js +1 -1
  25. package/dist/react/adapters/LocalStorageThreadListAdapter.d.ts.map +1 -1
  26. package/dist/react/adapters/LocalStorageThreadListAdapter.js +1 -1
  27. package/dist/react/adapters/LocalStorageThreadListAdapter.js.map +1 -1
  28. package/dist/react/client/AdaptedRemoteThread.js +1 -1
  29. package/dist/react/client/InMemoryThreadList.d.ts.map +1 -1
  30. package/dist/react/client/InMemoryThreadList.js +25 -21
  31. package/dist/react/client/InMemoryThreadList.js.map +1 -1
  32. package/dist/react/client/Interactables.d.ts.map +1 -1
  33. package/dist/react/client/Interactables.js +60 -178
  34. package/dist/react/client/Interactables.js.map +1 -1
  35. package/dist/react/client/RemoteThreadList.d.ts +15 -0
  36. package/dist/react/client/RemoteThreadList.d.ts.map +1 -1
  37. package/dist/react/client/RemoteThreadList.js +263 -194
  38. package/dist/react/client/RemoteThreadList.js.map +1 -1
  39. package/dist/react/client/Tools.d.ts.map +1 -1
  40. package/dist/react/client/Tools.js +6 -2
  41. package/dist/react/client/Tools.js.map +1 -1
  42. package/dist/react/index.d.ts +4 -2
  43. package/dist/react/index.js +2 -1
  44. package/dist/react/interactables-legacy/Interactables.d.ts.map +1 -1
  45. package/dist/react/interactables-legacy/Interactables.js +60 -178
  46. package/dist/react/interactables-legacy/Interactables.js.map +1 -1
  47. package/dist/react/interactables-shared/useInteractablePersistenceQueue.d.ts +24 -0
  48. package/dist/react/interactables-shared/useInteractablePersistenceQueue.d.ts.map +1 -0
  49. package/dist/react/interactables-shared/useInteractablePersistenceQueue.js +138 -0
  50. package/dist/react/interactables-shared/useInteractablePersistenceQueue.js.map +1 -0
  51. package/dist/react/model-context/toolbox.d.ts +2 -14
  52. package/dist/react/model-context/toolbox.d.ts.map +1 -1
  53. package/dist/react/model-context/toolbox.js +1 -13
  54. package/dist/react/model-context/toolbox.js.map +1 -1
  55. package/dist/react/model-context/useAssistantContext.js +37 -7
  56. package/dist/react/model-context/useAssistantContext.js.map +1 -1
  57. package/dist/react/model-context/useAuiToolOverrides.js +31 -5
  58. package/dist/react/model-context/useAuiToolOverrides.js.map +1 -1
  59. package/dist/react/model-context/useInlineRender.d.ts.map +1 -1
  60. package/dist/react/model-context/useInlineRender.js +10 -36
  61. package/dist/react/model-context/useInlineRender.js.map +1 -1
  62. package/dist/react/primitive-hooks/useMessageError.d.ts.map +1 -1
  63. package/dist/react/primitive-hooks/useMessageError.js +2 -8
  64. package/dist/react/primitive-hooks/useMessageError.js.map +1 -1
  65. package/dist/react/primitive-hooks/useThreadListLoadMore.d.ts.map +1 -1
  66. package/dist/react/primitive-hooks/useThreadListLoadMore.js +2 -4
  67. package/dist/react/primitive-hooks/useThreadListLoadMore.js.map +1 -1
  68. package/dist/react/primitives/composer/ComposerAttachments.d.ts.map +1 -1
  69. package/dist/react/primitives/composer/ComposerAttachments.js +4 -3
  70. package/dist/react/primitives/composer/ComposerAttachments.js.map +1 -1
  71. package/dist/react/primitives/composer/ComposerIf.js.map +1 -1
  72. package/dist/react/primitives/composer/ComposerQueue.d.ts +2 -1
  73. package/dist/react/primitives/composer/ComposerQueue.d.ts.map +1 -1
  74. package/dist/react/primitives/composer/ComposerQueue.js +4 -3
  75. package/dist/react/primitives/composer/ComposerQueue.js.map +1 -1
  76. package/dist/react/primitives/message/MessageAttachments.d.ts.map +1 -1
  77. package/dist/react/primitives/message/MessageAttachments.js +7 -6
  78. package/dist/react/primitives/message/MessageAttachments.js.map +1 -1
  79. package/dist/react/primitives/message/MessageGroupedParts.js +2 -2
  80. package/dist/react/primitives/message/MessageGroupedParts.js.map +1 -1
  81. package/dist/react/primitives/message/MessageParts.js +3 -3
  82. package/dist/react/primitives/message/MessageParts.js.map +1 -1
  83. package/dist/react/primitives/threadList/ThreadListItems.d.ts.map +1 -1
  84. package/dist/react/primitives/threadList/ThreadListItems.js +5 -4
  85. package/dist/react/primitives/threadList/ThreadListItems.js.map +1 -1
  86. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.d.ts +7 -5
  87. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.d.ts.map +1 -1
  88. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js +44 -22
  89. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js.map +1 -1
  90. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts +7 -6
  91. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts.map +1 -1
  92. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js +102 -117
  93. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js.map +1 -1
  94. package/dist/react/runtimes/RemoteThreadResource.d.ts +1 -6
  95. package/dist/react/runtimes/RemoteThreadResource.d.ts.map +1 -1
  96. package/dist/react/runtimes/RemoteThreadResource.js +3 -3
  97. package/dist/react/runtimes/RemoteThreadResource.js.map +1 -1
  98. package/dist/react/runtimes/assistant-transport/runManager.d.ts.map +1 -1
  99. package/dist/react/runtimes/assistant-transport/runManager.js +2 -12
  100. package/dist/react/runtimes/assistant-transport/runManager.js.map +1 -1
  101. package/dist/react/runtimes/assistant-transport/useLatestRef.js +2 -2
  102. package/dist/react/runtimes/assistant-transport/useLatestRef.js.map +1 -1
  103. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.d.ts +3 -10
  104. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.d.ts.map +1 -1
  105. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js +43 -85
  106. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js.map +1 -1
  107. package/dist/react/runtimes/cloud/auiV0.d.ts +5 -1
  108. package/dist/react/runtimes/cloud/auiV0.d.ts.map +1 -1
  109. package/dist/react/runtimes/cloud/auiV0.js.map +1 -1
  110. package/dist/react/runtimes/cloud/createCloudThreadListAdapter.d.ts +29 -0
  111. package/dist/react/runtimes/cloud/createCloudThreadListAdapter.d.ts.map +1 -0
  112. package/dist/react/runtimes/cloud/createCloudThreadListAdapter.js +159 -0
  113. package/dist/react/runtimes/cloud/createCloudThreadListAdapter.js.map +1 -0
  114. package/dist/react/runtimes/cloud/createCloudThreadListAdapterCreateFallback.d.ts +12 -0
  115. package/dist/react/runtimes/cloud/createCloudThreadListAdapterCreateFallback.d.ts.map +1 -0
  116. package/dist/react/runtimes/cloud/createCloudThreadListAdapterCreateFallback.js +10 -0
  117. package/dist/react/runtimes/cloud/createCloudThreadListAdapterCreateFallback.js.map +1 -0
  118. package/dist/react/runtimes/cloud/useCloudThreadListAdapter.d.ts +1 -9
  119. package/dist/react/runtimes/cloud/useCloudThreadListAdapter.d.ts.map +1 -1
  120. package/dist/react/runtimes/cloud/useCloudThreadListAdapter.js +16 -153
  121. package/dist/react/runtimes/cloud/useCloudThreadListAdapter.js.map +1 -1
  122. package/dist/react/runtimes/cloud/useCloudThreadListRuntime.js.map +1 -1
  123. package/dist/react/runtimes/createMessageConverter.d.ts +2 -1
  124. package/dist/react/runtimes/createMessageConverter.d.ts.map +1 -1
  125. package/dist/react/runtimes/external-message-converter.d.ts +6 -25
  126. package/dist/react/runtimes/external-message-converter.d.ts.map +1 -1
  127. package/dist/react/runtimes/external-message-converter.js +33 -233
  128. package/dist/react/runtimes/external-message-converter.js.map +1 -1
  129. package/dist/react/runtimes/useRemoteThreadListRuntime.d.ts.map +1 -1
  130. package/dist/react/runtimes/useRemoteThreadListRuntime.js +14 -6
  131. package/dist/react/runtimes/useRemoteThreadListRuntime.js.map +1 -1
  132. package/dist/react/types/MessagePartComponentTypes.d.ts +7 -3
  133. package/dist/react/types/MessagePartComponentTypes.d.ts.map +1 -1
  134. package/dist/react/types/scopes/tools.d.ts +2 -0
  135. package/dist/react/types/scopes/tools.d.ts.map +1 -1
  136. package/dist/runtime/api/composer-runtime.d.ts +1 -1
  137. package/dist/runtime/api/composer-runtime.js.map +1 -1
  138. package/dist/runtime/api/message-part-runtime.d.ts +2 -2
  139. package/dist/runtime/api/message-part-runtime.d.ts.map +1 -1
  140. package/dist/runtime/api/message-part-runtime.js +1 -1
  141. package/dist/runtime/api/message-part-runtime.js.map +1 -1
  142. package/dist/runtime/api/thread-list-runtime.d.ts +9 -1
  143. package/dist/runtime/api/thread-list-runtime.d.ts.map +1 -1
  144. package/dist/runtime/api/thread-list-runtime.js +5 -0
  145. package/dist/runtime/api/thread-list-runtime.js.map +1 -1
  146. package/dist/runtime/api/thread-runtime.d.ts +2 -3
  147. package/dist/runtime/api/thread-runtime.d.ts.map +1 -1
  148. package/dist/runtime/base/base-composer-runtime-core.d.ts +1 -1
  149. package/dist/runtime/base/base-composer-runtime-core.d.ts.map +1 -1
  150. package/dist/runtime/base/base-composer-runtime-core.js +14 -29
  151. package/dist/runtime/base/base-composer-runtime-core.js.map +1 -1
  152. package/dist/runtime/base/base-thread-runtime-core.d.ts +4 -6
  153. package/dist/runtime/base/base-thread-runtime-core.d.ts.map +1 -1
  154. package/dist/runtime/base/base-thread-runtime-core.js +5 -11
  155. package/dist/runtime/base/base-thread-runtime-core.js.map +1 -1
  156. package/dist/runtime/base/default-thread-composer-runtime-core.d.ts +1 -1
  157. package/dist/runtime/base/default-thread-composer-runtime-core.d.ts.map +1 -1
  158. package/dist/runtime/base/default-thread-composer-runtime-core.js +1 -1
  159. package/dist/runtime/base/default-thread-composer-runtime-core.js.map +1 -1
  160. package/dist/runtime/interfaces/composer-runtime-core.d.ts +1 -1
  161. package/dist/runtime/interfaces/thread-list-runtime-core.d.ts +14 -2
  162. package/dist/runtime/interfaces/thread-list-runtime-core.d.ts.map +1 -1
  163. package/dist/runtime/interfaces/thread-runtime-core.d.ts +16 -2
  164. package/dist/runtime/interfaces/thread-runtime-core.d.ts.map +1 -1
  165. package/dist/runtime/internal.d.ts +2 -1
  166. package/dist/runtime/internal.js +2 -1
  167. package/dist/runtime/queue/external-thread-queue-adapter.d.ts +1 -1
  168. package/dist/runtime/queue/message-queue.d.ts.map +1 -1
  169. package/dist/runtime/queue/message-queue.js +1 -1
  170. package/dist/runtime/queue/message-queue.js.map +1 -1
  171. package/dist/runtime/queue/queue-item.d.ts +12 -0
  172. package/dist/runtime/queue/queue-item.d.ts.map +1 -0
  173. package/dist/runtime/queue/queue-item.js +6 -0
  174. package/dist/runtime/queue/queue-item.js.map +1 -0
  175. package/dist/runtime/utils/abortable-thread-load.d.ts +15 -0
  176. package/dist/runtime/utils/abortable-thread-load.d.ts.map +1 -0
  177. package/dist/runtime/utils/abortable-thread-load.js +35 -0
  178. package/dist/runtime/utils/abortable-thread-load.js.map +1 -0
  179. package/dist/runtime/utils/attachment-add-operations.d.ts +18 -0
  180. package/dist/runtime/utils/attachment-add-operations.d.ts.map +1 -0
  181. package/dist/runtime/utils/attachment-add-operations.js +43 -0
  182. package/dist/runtime/utils/attachment-add-operations.js.map +1 -0
  183. package/dist/runtime/utils/auto-status.d.ts +7 -2
  184. package/dist/runtime/utils/auto-status.d.ts.map +1 -1
  185. package/dist/runtime/utils/auto-status.js +13 -3
  186. package/dist/runtime/utils/auto-status.js.map +1 -1
  187. package/dist/runtime/utils/external-message-conversion.d.ts +48 -0
  188. package/dist/runtime/utils/external-message-conversion.d.ts.map +1 -0
  189. package/dist/runtime/utils/external-message-conversion.js +222 -0
  190. package/dist/runtime/utils/external-message-conversion.js.map +1 -0
  191. package/dist/runtime/utils/external-store-message.d.ts +2 -1
  192. package/dist/runtime/utils/external-store-message.d.ts.map +1 -1
  193. package/dist/runtime/utils/external-store-message.js +2 -1
  194. package/dist/runtime/utils/external-store-message.js.map +1 -1
  195. package/dist/runtime/utils/message-repository-session.d.ts +30 -0
  196. package/dist/runtime/utils/message-repository-session.d.ts.map +1 -0
  197. package/dist/runtime/utils/message-repository-session.js +130 -0
  198. package/dist/runtime/utils/message-repository-session.js.map +1 -0
  199. package/dist/runtime/utils/message-repository.d.ts.map +1 -1
  200. package/dist/runtime/utils/message-repository.js +3 -4
  201. package/dist/runtime/utils/message-repository.js.map +1 -1
  202. package/dist/runtime/utils/pending-tool-calls.d.ts +20 -0
  203. package/dist/runtime/utils/pending-tool-calls.d.ts.map +1 -0
  204. package/dist/runtime/utils/pending-tool-calls.js +22 -0
  205. package/dist/runtime/utils/pending-tool-calls.js.map +1 -0
  206. package/dist/runtime/utils/resolveToolApprovalResponse.d.ts +6 -3
  207. package/dist/runtime/utils/resolveToolApprovalResponse.d.ts.map +1 -1
  208. package/dist/runtime/utils/resolveToolApprovalResponse.js +12 -3
  209. package/dist/runtime/utils/resolveToolApprovalResponse.js.map +1 -1
  210. package/dist/runtime/utils/thread-message-like.d.ts +3 -10
  211. package/dist/runtime/utils/thread-message-like.d.ts.map +1 -1
  212. package/dist/runtime/utils/thread-message-like.js.map +1 -1
  213. package/dist/runtime/utils/tool-call-tree.d.ts +39 -0
  214. package/dist/runtime/utils/tool-call-tree.d.ts.map +1 -0
  215. package/dist/runtime/utils/tool-call-tree.js +85 -0
  216. package/dist/runtime/utils/tool-call-tree.js.map +1 -0
  217. package/dist/runtimes/external-store/external-store-adapter.d.ts +29 -2
  218. package/dist/runtimes/external-store/external-store-adapter.d.ts.map +1 -1
  219. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.d.ts +2 -5
  220. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.d.ts.map +1 -1
  221. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.js +3 -10
  222. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.js.map +1 -1
  223. package/dist/runtimes/external-store/external-store-thread-runtime-core.d.ts +21 -2
  224. package/dist/runtimes/external-store/external-store-thread-runtime-core.d.ts.map +1 -1
  225. package/dist/runtimes/external-store/external-store-thread-runtime-core.js +154 -46
  226. package/dist/runtimes/external-store/external-store-thread-runtime-core.js.map +1 -1
  227. package/dist/runtimes/inert/InertThreadRuntimeCore.d.ts +1 -1
  228. package/dist/runtimes/inert/InertThreadRuntimeCore.d.ts.map +1 -1
  229. package/dist/runtimes/inert/InertThreadRuntimeCore.js.map +1 -1
  230. package/dist/runtimes/local/local-thread-runtime-core.d.ts +4 -2
  231. package/dist/runtimes/local/local-thread-runtime-core.d.ts.map +1 -1
  232. package/dist/runtimes/local/local-thread-runtime-core.js +23 -3
  233. package/dist/runtimes/local/local-thread-runtime-core.js.map +1 -1
  234. package/dist/runtimes/remote-thread-list/remote-thread-state.d.ts +12 -1
  235. package/dist/runtimes/remote-thread-list/remote-thread-state.d.ts.map +1 -1
  236. package/dist/runtimes/remote-thread-list/remote-thread-state.js +107 -14
  237. package/dist/runtimes/remote-thread-list/remote-thread-state.js.map +1 -1
  238. package/dist/runtimes/remote-thread-list/title.d.ts +9 -0
  239. package/dist/runtimes/remote-thread-list/title.d.ts.map +1 -0
  240. package/dist/runtimes/remote-thread-list/title.js +6 -0
  241. package/dist/runtimes/remote-thread-list/title.js.map +1 -0
  242. package/dist/runtimes/remote-thread-list/types.d.ts +6 -5
  243. package/dist/runtimes/remote-thread-list/types.d.ts.map +1 -1
  244. package/dist/runtimes/tool-invocations/ToolInvocationTracker.d.ts +31 -8
  245. package/dist/runtimes/tool-invocations/ToolInvocationTracker.d.ts.map +1 -1
  246. package/dist/runtimes/tool-invocations/ToolInvocationTracker.js +98 -59
  247. package/dist/runtimes/tool-invocations/ToolInvocationTracker.js.map +1 -1
  248. package/dist/store/clients/external-thread.d.ts +1 -1
  249. package/dist/store/clients/external-thread.d.ts.map +1 -1
  250. package/dist/store/clients/external-thread.js +85 -103
  251. package/dist/store/clients/external-thread.js.map +1 -1
  252. package/dist/store/clients/single-thread-list.d.ts.map +1 -1
  253. package/dist/store/clients/single-thread-list.js +2 -2
  254. package/dist/store/clients/single-thread-list.js.map +1 -1
  255. package/dist/store/clients/suggestions.d.ts.map +1 -1
  256. package/dist/store/clients/suggestions.js +33 -12
  257. package/dist/store/clients/suggestions.js.map +1 -1
  258. package/dist/store/clients/thread-message-client.js +1 -1
  259. package/dist/store/clients/thread-selection-events.js +1 -1
  260. package/dist/store/index.d.ts +6 -3
  261. package/dist/store/index.js +2 -1
  262. package/dist/store/internal.d.ts +2 -2
  263. package/dist/store/internal.js +2 -2
  264. package/dist/store/primitive-predicates.d.ts +4 -1
  265. package/dist/store/primitive-predicates.d.ts.map +1 -1
  266. package/dist/store/primitive-predicates.js +9 -1
  267. package/dist/store/primitive-predicates.js.map +1 -1
  268. package/dist/store/runtime-clients/composer-runtime-client.js +1 -1
  269. package/dist/store/runtime-clients/message-runtime-client.js +1 -1
  270. package/dist/store/runtime-clients/thread-list-item-runtime-client.js +1 -1
  271. package/dist/store/runtime-clients/thread-list-runtime-client.d.ts.map +1 -1
  272. package/dist/store/runtime-clients/thread-list-runtime-client.js +124 -105
  273. package/dist/store/runtime-clients/thread-list-runtime-client.js.map +1 -1
  274. package/dist/store/runtime-clients/thread-runtime-client.js +1 -1
  275. package/dist/store/runtime-clients/useSubscribable.d.ts +3 -1
  276. package/dist/store/runtime-clients/useSubscribable.d.ts.map +1 -1
  277. package/dist/store/runtime-clients/useSubscribable.js +1 -1
  278. package/dist/store/runtime-clients/useSubscribable.js.map +1 -1
  279. package/dist/store/scope-registration.d.ts +1 -1
  280. package/dist/store/scopes/composer.d.ts +2 -1
  281. package/dist/store/scopes/composer.d.ts.map +1 -1
  282. package/dist/store/scopes/message.d.ts +1 -1
  283. package/dist/store/scopes/part.d.ts +6 -3
  284. package/dist/store/scopes/part.d.ts.map +1 -1
  285. package/dist/store/scopes/queue-item.d.ts +2 -9
  286. package/dist/store/scopes/queue-item.d.ts.map +1 -1
  287. package/dist/store/scopes/queue-item.js +0 -6
  288. package/dist/subscribable/subscribable.d.ts +8 -1
  289. package/dist/subscribable/subscribable.d.ts.map +1 -1
  290. package/dist/subscribable/subscribable.js +24 -16
  291. package/dist/subscribable/subscribable.js.map +1 -1
  292. package/dist/types/message.d.ts +47 -4
  293. package/dist/types/message.d.ts.map +1 -1
  294. package/dist/types/message.js +7 -1
  295. package/dist/types/message.js.map +1 -1
  296. package/dist/utils/data-url.d.ts +14 -1
  297. package/dist/utils/data-url.d.ts.map +1 -1
  298. package/dist/utils/data-url.js +13 -1
  299. package/dist/utils/data-url.js.map +1 -1
  300. package/dist/utils/invoke-user-callback.d.ts +5 -0
  301. package/dist/utils/invoke-user-callback.d.ts.map +1 -0
  302. package/dist/utils/invoke-user-callback.js +19 -0
  303. package/dist/utils/invoke-user-callback.js.map +1 -0
  304. package/dist/utils/normalizePartStatus.js.map +1 -1
  305. package/package.json +12 -17
  306. package/src/index.ts +6 -1
  307. package/src/internal.ts +20 -0
  308. package/src/model-context/frame/host.test.ts +91 -7
  309. package/src/model-context/frame/host.ts +7 -2
  310. package/src/model-context/frame/provider.test.ts +484 -13
  311. package/src/model-context/frame/provider.ts +205 -35
  312. package/src/model-context/interactable-composer-metadata.ts +1 -1
  313. package/src/model-context/registry.test.ts +44 -0
  314. package/src/model-context/registry.ts +13 -35
  315. package/src/model-context/tool-call-text.test.ts +67 -0
  316. package/src/model-context/tool-call-text.ts +67 -0
  317. package/src/react/AssistantRuntimeProvider.test.tsx +66 -2
  318. package/src/react/adapters/LocalStorageThreadListAdapter.tsx +1 -3
  319. package/src/react/client/InMemoryThreadList.test.tsx +49 -0
  320. package/src/react/client/InMemoryThreadList.ts +7 -1
  321. package/src/react/client/Interactables.ts +19 -178
  322. package/src/react/client/RemoteThreadList.background.test.ts +384 -0
  323. package/src/react/client/RemoteThreadList.concurrent.test.tsx +274 -0
  324. package/src/react/client/RemoteThreadList.test.ts +369 -1
  325. package/src/react/client/RemoteThreadList.ts +320 -142
  326. package/src/react/client/Tools.ts +7 -1
  327. package/src/react/index.ts +4 -0
  328. package/src/react/interactables-legacy/Interactables.ts +20 -177
  329. package/src/react/interactables-shared/useInteractablePersistenceQueue.test.tsx +161 -0
  330. package/src/react/interactables-shared/useInteractablePersistenceQueue.ts +206 -0
  331. package/src/react/model-context/modelContextHooks.test.tsx +131 -0
  332. package/src/react/model-context/toolbox.test.ts +9 -9
  333. package/src/react/model-context/toolbox.ts +9 -36
  334. package/src/react/model-context/useAssistantContext.ts +4 -2
  335. package/src/react/model-context/useAuiToolOverrides.ts +4 -2
  336. package/src/react/model-context/useInlineRender.test.tsx +48 -0
  337. package/src/react/model-context/useInlineRender.ts +14 -10
  338. package/src/react/primitive-hooks/useMessageError.ts +2 -19
  339. package/src/react/primitive-hooks/useThreadListLoadMore.ts +2 -3
  340. package/src/react/primitives/composer/ComposerAttachments.test.tsx +76 -0
  341. package/src/react/primitives/composer/ComposerAttachments.tsx +9 -4
  342. package/src/react/primitives/composer/ComposerQueue.test.tsx +72 -0
  343. package/src/react/primitives/composer/ComposerQueue.tsx +7 -4
  344. package/src/react/primitives/message/MessageAttachments.test.tsx +107 -37
  345. package/src/react/primitives/message/MessageAttachments.tsx +10 -7
  346. package/src/react/primitives/message/MessageGroupedParts.tsx +2 -2
  347. package/src/react/primitives/message/MessageParts.tsx +3 -3
  348. package/src/react/primitives/threadList/ThreadListItems.test.tsx +99 -0
  349. package/src/react/primitives/threadList/ThreadListItems.tsx +8 -5
  350. package/src/react/runtimes/RemoteThreadListHookInstanceManager.running.test.tsx +94 -1
  351. package/src/react/runtimes/RemoteThreadListHookInstanceManager.tsx +72 -31
  352. package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.switch-delete.test.ts +169 -0
  353. package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.tsx +140 -124
  354. package/src/react/runtimes/RemoteThreadResource.ts +1 -4
  355. package/src/react/runtimes/assistant-transport/runManager.ts +4 -17
  356. package/src/react/runtimes/assistant-transport/transport-scheduling.test.ts +51 -2
  357. package/src/react/runtimes/assistant-transport/useLatestRef.ts +2 -2
  358. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.test.ts +45 -0
  359. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.test.tsx +157 -1
  360. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.ts +82 -176
  361. package/src/react/runtimes/cloud/auiV0.ts +5 -0
  362. package/src/react/runtimes/cloud/createCloudThreadListAdapter.test.ts +88 -0
  363. package/src/react/runtimes/cloud/createCloudThreadListAdapter.ts +236 -0
  364. package/src/react/runtimes/cloud/createCloudThreadListAdapterCreateFallback.test.ts +37 -0
  365. package/src/react/runtimes/cloud/createCloudThreadListAdapterCreateFallback.ts +18 -0
  366. package/src/react/runtimes/cloud/useCloudThreadListAdapter.test.tsx +90 -1
  367. package/src/react/runtimes/cloud/useCloudThreadListAdapter.tsx +33 -238
  368. package/src/react/runtimes/external-message-converter.test.ts +0 -14
  369. package/src/react/runtimes/external-message-converter.test.tsx +77 -0
  370. package/src/react/runtimes/external-message-converter.ts +59 -481
  371. package/src/react/runtimes/useRemoteThreadListRuntime.concurrent.test.tsx +216 -0
  372. package/src/react/runtimes/useRemoteThreadListRuntime.ts +24 -6
  373. package/src/react/types/MessagePartComponentTypes.ts +7 -3
  374. package/src/react/types/scopes/tools.ts +2 -0
  375. package/src/runtime/api/composer-runtime.ts +1 -1
  376. package/src/runtime/api/message-part-runtime.ts +3 -3
  377. package/src/runtime/api/message-runtime.test.ts +28 -1
  378. package/src/runtime/api/thread-list-runtime.ts +25 -1
  379. package/src/runtime/base/base-composer-runtime-core.ts +20 -40
  380. package/src/runtime/base/base-thread-runtime-core.test.ts +110 -3
  381. package/src/runtime/base/base-thread-runtime-core.ts +16 -15
  382. package/src/runtime/base/default-thread-composer-runtime-core.ts +1 -4
  383. package/src/runtime/interfaces/composer-runtime-core.ts +1 -1
  384. package/src/runtime/interfaces/thread-list-runtime-core.ts +20 -1
  385. package/src/runtime/interfaces/thread-runtime-core.ts +18 -2
  386. package/src/runtime/internal.ts +5 -0
  387. package/src/runtime/queue/external-thread-queue-adapter.ts +1 -1
  388. package/src/runtime/queue/message-queue.ts +1 -4
  389. package/src/runtime/queue/queue-item.ts +10 -0
  390. package/src/runtime/utils/abortable-thread-load.test.ts +87 -0
  391. package/src/runtime/utils/abortable-thread-load.ts +53 -0
  392. package/src/runtime/utils/attachment-add-operations.ts +59 -0
  393. package/src/runtime/utils/auto-status.test.ts +57 -0
  394. package/src/runtime/utils/auto-status.ts +46 -1
  395. package/src/runtime/utils/external-message-conversion.test.ts +210 -0
  396. package/src/runtime/utils/external-message-conversion.ts +510 -0
  397. package/src/runtime/utils/external-store-message.ts +2 -0
  398. package/src/runtime/utils/message-repository-session.test.ts +195 -0
  399. package/src/runtime/utils/message-repository-session.ts +176 -0
  400. package/src/runtime/utils/message-repository.ts +7 -4
  401. package/src/runtime/utils/pending-tool-calls.test.ts +57 -0
  402. package/src/runtime/utils/pending-tool-calls.ts +35 -0
  403. package/src/runtime/utils/resolveToolApprovalResponse.test.ts +73 -0
  404. package/src/runtime/utils/resolveToolApprovalResponse.ts +27 -6
  405. package/src/runtime/utils/thread-message-like.ts +4 -10
  406. package/src/runtime/utils/tool-call-tree.test.ts +191 -0
  407. package/src/runtime/utils/tool-call-tree.ts +97 -0
  408. package/src/runtimes/external-store/external-store-adapter.ts +38 -2
  409. package/src/runtimes/external-store/external-store-thread-list-runtime-core.ts +6 -14
  410. package/src/runtimes/external-store/external-store-thread-runtime-core.ts +231 -61
  411. package/src/runtimes/inert/InertThreadRuntimeCore.ts +1 -1
  412. package/src/runtimes/local/local-thread-runtime-core.test.ts +201 -4
  413. package/src/runtimes/local/local-thread-runtime-core.ts +53 -4
  414. package/src/runtimes/remote-thread-list/remote-thread-state.test.ts +149 -0
  415. package/src/runtimes/remote-thread-list/remote-thread-state.ts +178 -16
  416. package/src/runtimes/remote-thread-list/title.ts +3 -0
  417. package/src/runtimes/remote-thread-list/types.ts +6 -5
  418. package/src/runtimes/tool-invocations/EDGE_CASES.md +125 -3
  419. package/src/runtimes/tool-invocations/ToolInvocationTracker.test.ts +856 -102
  420. package/src/runtimes/tool-invocations/ToolInvocationTracker.ts +145 -89
  421. package/src/store/clients/external-thread.ts +30 -69
  422. package/src/store/clients/single-thread-list.ts +4 -1
  423. package/src/store/clients/suggestions.test.ts +185 -0
  424. package/src/store/clients/suggestions.ts +42 -35
  425. package/src/store/index.ts +4 -0
  426. package/src/store/internal.ts +2 -0
  427. package/src/store/primitive-predicates.ts +35 -0
  428. package/src/store/runtime-clients/thread-list-runtime-client.ts +16 -2
  429. package/src/store/runtime-clients/useSubscribable.ts +8 -2
  430. package/src/store/scopes/message.ts +1 -1
  431. package/src/store/scopes/part.ts +6 -3
  432. package/src/store/scopes/queue-item.ts +2 -9
  433. package/src/subscribable/subscribable.test.ts +50 -0
  434. package/src/subscribable/subscribable.ts +33 -21
  435. package/src/tests/MessageRepository.test.ts +77 -0
  436. package/src/tests/RemoteThreadListThreadListRuntimeCore-load-race.test.ts +305 -0
  437. package/src/tests/RemoteThreadListThreadListRuntimeCore-loadMore.test.ts +78 -0
  438. package/src/tests/RemoteThreadListThreadListRuntimeCore-reload.test.ts +52 -0
  439. package/src/tests/RemoteThreadListThreadListRuntimeCore-title-race.test.ts +158 -0
  440. package/src/tests/external-store-optimistic-id.test.ts +79 -0
  441. package/src/tests/external-store-repository-instance.test.ts +166 -0
  442. package/src/tests/external-store-thread-runtime-core-adapter.test.ts +186 -16
  443. package/src/tests/external-store-thread-runtime-core.test.ts +642 -0
  444. package/src/tests/external-thread-attachments.test.tsx +258 -1
  445. package/src/tests/remote-thread-list-adapter-switch.test.ts +66 -1
  446. package/src/tests/single-thread-list-item.test.tsx +47 -0
  447. package/src/tests/thread-message-like.test.ts +39 -1
  448. package/src/tests/thread-switch-events.test.tsx +397 -2
  449. package/src/types/message.ts +58 -5
  450. package/src/utils/data-url.test.ts +52 -1
  451. package/src/utils/data-url.ts +21 -0
  452. package/src/utils/invoke-user-callback.test.ts +82 -0
  453. package/src/utils/invoke-user-callback.ts +31 -0
  454. package/src/utils/normalizePartStatus.ts +1 -1
  455. package/dist/store/scopes/queue-item.js.map +0 -1
@@ -5,10 +5,14 @@ import { describe, expect, it, vi } from "vitest";
5
5
  import { AuiProvider, useAui, useAuiEvent } from "@assistant-ui/store";
6
6
  import { AssistantRuntimeProvider } from "../react/AssistantRuntimeProvider";
7
7
  import { useExternalStoreRuntime } from "../react/runtimes/useExternalStoreRuntime";
8
+ import { useLocalRuntime } from "../react/runtimes/useLocalRuntime";
8
9
  import { useRemoteThreadListRuntime } from "../react/runtimes/useRemoteThreadListRuntime";
9
- import { makeAdapter } from "./remote-thread-list-test-helpers";
10
+ import { deferred, makeAdapter } from "./remote-thread-list-test-helpers";
10
11
  import { RuntimeAdapter } from "../react/RuntimeAdapter";
11
- import { AssistantRuntimeImpl } from "../runtime/api/assistant-runtime";
12
+ import {
13
+ AssistantRuntimeImpl,
14
+ type AssistantRuntime,
15
+ } from "../runtime/api/assistant-runtime";
12
16
  import { ExternalStoreRuntimeCore } from "../runtimes/external-store/external-store-runtime-core";
13
17
  import type { ExternalStoreAdapter } from "../runtimes/external-store/external-store-adapter";
14
18
 
@@ -232,3 +236,394 @@ describe("thread switch events", () => {
232
236
  expect(payload.previousThreadId).toMatch(/^__LOCALID_/);
233
237
  });
234
238
  });
239
+
240
+ const createExternalStoreRuntime = (
241
+ runningByThreadId: Map<string, boolean>,
242
+ ) => {
243
+ const threads = [
244
+ { id: "t1", title: "one" },
245
+ { id: "t2", title: "two" },
246
+ ];
247
+ let currentId = "t1";
248
+ const makeStoreAdapter = (): ExternalStoreAdapter<DemoMessage> => ({
249
+ messages: EMPTY_MESSAGES,
250
+ isRunning: runningByThreadId.get(currentId) ?? false,
251
+ convertMessage: (m) => ({
252
+ id: m.id,
253
+ role: m.role,
254
+ content: [{ type: "text", text: m.text }],
255
+ }),
256
+ onNew: async () => {},
257
+ adapters: {
258
+ threadList: {
259
+ threadId: currentId,
260
+ threads: threads.map((t) => ({
261
+ status: "regular" as const,
262
+ id: t.id,
263
+ title: t.title,
264
+ })),
265
+ onSwitchToThread: (threadId: string) => {
266
+ currentId = threadId;
267
+ sync();
268
+ },
269
+ onSwitchToNewThread: () => {},
270
+ },
271
+ },
272
+ });
273
+ const core = new ExternalStoreRuntimeCore(makeStoreAdapter());
274
+ const runtime = new AssistantRuntimeImpl(core);
275
+ const sync = () => core.setAdapter(makeStoreAdapter());
276
+ return { runtime, sync };
277
+ };
278
+
279
+ const listAdapter = () =>
280
+ makeAdapter({
281
+ list: vi.fn(async () => ({
282
+ threads: [
283
+ { status: "regular" as const, remoteId: "thread-a", title: "A" },
284
+ { status: "regular" as const, remoteId: "thread-b", title: "B" },
285
+ ],
286
+ })),
287
+ });
288
+
289
+ const renderRemoteList = (
290
+ adapter: ReturnType<typeof makeAdapter>,
291
+ run: () => Promise<{ content: [] }>,
292
+ ) => {
293
+ const globalRunStart = vi.fn();
294
+ const globalRunEnd = vi.fn();
295
+ const globalInitialize = vi.fn();
296
+ const globalModelContextUpdate = vi.fn();
297
+ const selectedRunStart = vi.fn();
298
+ const selectedRunEnd = vi.fn();
299
+ const selectedInitialize = vi.fn();
300
+ const selectedModelContextUpdate = vi.fn();
301
+ let runtime!: AssistantRuntime;
302
+
303
+ const Listener = () => {
304
+ useAuiEvent({ scope: "*", event: "thread.runStart" }, globalRunStart);
305
+ useAuiEvent({ scope: "*", event: "thread.runEnd" }, globalRunEnd);
306
+ useAuiEvent({ scope: "*", event: "thread.initialize" }, globalInitialize);
307
+ useAuiEvent(
308
+ { scope: "*", event: "thread.modelContextUpdate" },
309
+ globalModelContextUpdate,
310
+ );
311
+ useAuiEvent("thread.runStart", selectedRunStart);
312
+ useAuiEvent("thread.runEnd", selectedRunEnd);
313
+ useAuiEvent("thread.initialize", selectedInitialize);
314
+ useAuiEvent("thread.modelContextUpdate", selectedModelContextUpdate);
315
+ return null;
316
+ };
317
+ const Harness = () => {
318
+ runtime = useRemoteThreadListRuntime({
319
+ adapter,
320
+ initialThreadId: "thread-a",
321
+ runtimeHook: function RuntimeHook() {
322
+ return useLocalRuntime({ run });
323
+ },
324
+ });
325
+ return (
326
+ <AssistantRuntimeProvider runtime={runtime}>
327
+ <Listener />
328
+ </AssistantRuntimeProvider>
329
+ );
330
+ };
331
+ render(<Harness />);
332
+
333
+ return {
334
+ getRuntime: () => runtime,
335
+ globalRunStart,
336
+ globalRunEnd,
337
+ globalInitialize,
338
+ globalModelContextUpdate,
339
+ selectedRunStart,
340
+ selectedRunEnd,
341
+ selectedInitialize,
342
+ selectedModelContextUpdate,
343
+ };
344
+ };
345
+
346
+ const switchTo = async (runtime: AssistantRuntime, remoteId: string) => {
347
+ await act(async () => {
348
+ await runtime.threads.switchToThread(remoteId);
349
+ });
350
+ await waitFor(() => {
351
+ expect(runtime.threads.mainItem.getState().remoteId).toBe(remoteId);
352
+ });
353
+ };
354
+
355
+ describe("background thread run events", () => {
356
+ it("delivers runEnd globally after the running thread is switched away", async () => {
357
+ const runA = deferred<{ content: [] }>();
358
+ const harness = renderRemoteList(listAdapter(), () => runA.promise);
359
+ const runtime = harness.getRuntime();
360
+
361
+ await waitFor(() => {
362
+ expect(runtime.threads.mainItem.getState().remoteId).toBe("thread-a");
363
+ });
364
+ const threadAId = runtime.threads.mainItem.getState().id;
365
+
366
+ await act(async () => {
367
+ runtime.thread.startRun({ parentId: null });
368
+ });
369
+ await waitFor(() => expect(runtime.thread.getState().isRunning).toBe(true));
370
+ await waitFor(() => {
371
+ expect(harness.globalRunStart).toHaveBeenCalledExactlyOnceWith({
372
+ threadId: threadAId,
373
+ });
374
+ expect(harness.selectedRunStart).toHaveBeenCalledExactlyOnceWith({
375
+ threadId: threadAId,
376
+ });
377
+ });
378
+
379
+ await switchTo(runtime, "thread-b");
380
+ await act(async () => {
381
+ runA.resolve({ content: [] });
382
+ });
383
+
384
+ await waitFor(() => {
385
+ expect(harness.globalRunEnd).toHaveBeenCalledExactlyOnceWith({
386
+ threadId: threadAId,
387
+ });
388
+ });
389
+ expect(harness.selectedRunEnd).not.toHaveBeenCalled();
390
+ });
391
+
392
+ it("does not duplicate run events while the running thread is switched back and forth", async () => {
393
+ const runA = deferred<{ content: [] }>();
394
+ const harness = renderRemoteList(listAdapter(), () => runA.promise);
395
+ const runtime = harness.getRuntime();
396
+
397
+ await waitFor(() => {
398
+ expect(runtime.threads.mainItem.getState().remoteId).toBe("thread-a");
399
+ });
400
+ const threadAId = runtime.threads.mainItem.getState().id;
401
+
402
+ await act(async () => {
403
+ runtime.thread.startRun({ parentId: null });
404
+ });
405
+ await waitFor(() => expect(runtime.thread.getState().isRunning).toBe(true));
406
+
407
+ await switchTo(runtime, "thread-b");
408
+ await switchTo(runtime, "thread-a");
409
+ await switchTo(runtime, "thread-b");
410
+ await switchTo(runtime, "thread-a");
411
+ await switchTo(runtime, "thread-b");
412
+
413
+ await act(async () => {
414
+ runA.resolve({ content: [] });
415
+ });
416
+
417
+ await waitFor(() => {
418
+ expect(harness.globalRunEnd).toHaveBeenCalledExactlyOnceWith({
419
+ threadId: threadAId,
420
+ });
421
+ });
422
+ expect(harness.globalRunStart).toHaveBeenCalledExactlyOnceWith({
423
+ threadId: threadAId,
424
+ });
425
+ expect(harness.selectedRunStart).toHaveBeenCalledExactlyOnceWith({
426
+ threadId: threadAId,
427
+ });
428
+ expect(harness.selectedRunEnd).not.toHaveBeenCalled();
429
+ });
430
+
431
+ it("leaves the run to the selected client when its thread is re-selected", async () => {
432
+ const runA = deferred<{ content: [] }>();
433
+ const harness = renderRemoteList(listAdapter(), () => runA.promise);
434
+ const runtime = harness.getRuntime();
435
+
436
+ await waitFor(() => {
437
+ expect(runtime.threads.mainItem.getState().remoteId).toBe("thread-a");
438
+ });
439
+ const threadAId = runtime.threads.mainItem.getState().id;
440
+
441
+ await act(async () => {
442
+ runtime.thread.startRun({ parentId: null });
443
+ });
444
+ await waitFor(() => expect(runtime.thread.getState().isRunning).toBe(true));
445
+
446
+ await switchTo(runtime, "thread-b");
447
+ await switchTo(runtime, "thread-a");
448
+
449
+ await act(async () => {
450
+ runA.resolve({ content: [] });
451
+ });
452
+
453
+ await waitFor(() => {
454
+ expect(harness.selectedRunEnd).toHaveBeenCalledExactlyOnceWith({
455
+ threadId: threadAId,
456
+ });
457
+ });
458
+ expect(harness.globalRunEnd).toHaveBeenCalledExactlyOnceWith({
459
+ threadId: threadAId,
460
+ });
461
+ expect(harness.globalRunStart).toHaveBeenCalledExactlyOnceWith({
462
+ threadId: threadAId,
463
+ });
464
+ expect(harness.selectedRunStart).toHaveBeenCalledExactlyOnceWith({
465
+ threadId: threadAId,
466
+ });
467
+ });
468
+
469
+ it("reports the start and end of a run that is never selected", async () => {
470
+ const backgroundRun = deferred<{ content: [] }>();
471
+ const harness = renderRemoteList(
472
+ listAdapter(),
473
+ () => backgroundRun.promise,
474
+ );
475
+ const runtime = harness.getRuntime();
476
+
477
+ await waitFor(() => {
478
+ expect(runtime.threads.mainItem.getState().remoteId).toBe("thread-a");
479
+ });
480
+ await switchTo(runtime, "thread-b");
481
+ const threadBId = runtime.threads.mainItem.getState().id;
482
+ const threadB = runtime.threads.getById(threadBId);
483
+ await switchTo(runtime, "thread-a");
484
+
485
+ await act(async () => {
486
+ threadB.startRun({ parentId: null });
487
+ });
488
+ await waitFor(() => {
489
+ expect(harness.globalRunStart).toHaveBeenCalledExactlyOnceWith({
490
+ threadId: threadBId,
491
+ });
492
+ });
493
+
494
+ await act(async () => {
495
+ backgroundRun.resolve({ content: [] });
496
+ });
497
+ await waitFor(() => {
498
+ expect(harness.globalRunEnd).toHaveBeenCalledExactlyOnceWith({
499
+ threadId: threadBId,
500
+ });
501
+ });
502
+ expect(harness.selectedRunStart).not.toHaveBeenCalled();
503
+ expect(harness.selectedRunEnd).not.toHaveBeenCalled();
504
+ });
505
+
506
+ it("reports lifecycle events from a thread that is never selected", async () => {
507
+ const backgroundRun = deferred<{ content: [] }>();
508
+ const harness = renderRemoteList(
509
+ listAdapter(),
510
+ () => backgroundRun.promise,
511
+ );
512
+ const runtime = harness.getRuntime();
513
+
514
+ await waitFor(() => {
515
+ expect(runtime.threads.mainItem.getState().remoteId).toBe("thread-a");
516
+ });
517
+ await switchTo(runtime, "thread-b");
518
+ const threadBId = runtime.threads.mainItem.getState().id;
519
+ const threadB = runtime.threads.getById(threadBId);
520
+ await switchTo(runtime, "thread-a");
521
+
522
+ let updateModelContext!: () => void;
523
+ const unregisterModelContext = runtime.registerModelContextProvider({
524
+ getModelContext: () => ({}),
525
+ subscribe: (callback) => {
526
+ updateModelContext = callback;
527
+ return () => {};
528
+ },
529
+ });
530
+ vi.clearAllMocks();
531
+
532
+ await act(async () => {
533
+ threadB.startRun({ parentId: null });
534
+ });
535
+ await waitFor(() => {
536
+ expect(harness.globalInitialize).toHaveBeenCalledExactlyOnceWith({
537
+ threadId: threadBId,
538
+ });
539
+ });
540
+
541
+ await act(async () => {
542
+ updateModelContext();
543
+ });
544
+ await waitFor(() => {
545
+ expect(harness.globalModelContextUpdate).toHaveBeenCalledWith({
546
+ threadId: threadBId,
547
+ });
548
+ });
549
+
550
+ expect(harness.selectedInitialize).not.toHaveBeenCalled();
551
+ unregisterModelContext();
552
+
553
+ await act(async () => {
554
+ backgroundRun.resolve({ content: [] });
555
+ });
556
+ });
557
+
558
+ it.each([
559
+ { operation: "delete", state: "deleted" },
560
+ { operation: "detach", state: "detached" },
561
+ ] as const)(
562
+ "stops reporting a background run once its thread is $state",
563
+ async ({ operation }) => {
564
+ const runA = deferred<{ content: [] }>();
565
+ const runSettled = vi.fn();
566
+ const harness = renderRemoteList(listAdapter(), async () => {
567
+ const result = await runA.promise;
568
+ runSettled();
569
+ return result;
570
+ });
571
+ const runtime = harness.getRuntime();
572
+
573
+ await waitFor(() => {
574
+ expect(runtime.threads.mainItem.getState().remoteId).toBe("thread-a");
575
+ });
576
+ const threadAId = runtime.threads.mainItem.getState().id;
577
+
578
+ await act(async () => {
579
+ runtime.thread.startRun({ parentId: null });
580
+ });
581
+ await waitFor(() =>
582
+ expect(runtime.thread.getState().isRunning).toBe(true),
583
+ );
584
+ await switchTo(runtime, "thread-b");
585
+
586
+ await act(async () => {
587
+ const item = runtime.threads.getItemById(threadAId);
588
+ if (operation === "delete") await item.delete();
589
+ else item.detach();
590
+ });
591
+ await act(async () => {
592
+ runA.resolve({ content: [] });
593
+ });
594
+
595
+ await waitFor(() => expect(runSettled).toHaveBeenCalledExactlyOnceWith());
596
+ expect(harness.globalRunEnd).not.toHaveBeenCalled();
597
+ },
598
+ );
599
+
600
+ it("emits nothing for a thread list that keeps only the main runtime", async () => {
601
+ const runningByThreadId = new Map<string, boolean>();
602
+ const { runtime, sync } = createExternalStoreRuntime(runningByThreadId);
603
+ const globalRunEnd = vi.fn();
604
+ const Listener = () => {
605
+ useAuiEvent({ scope: "*", event: "thread.runEnd" }, globalRunEnd);
606
+ return null;
607
+ };
608
+ const Harness = () => {
609
+ const aui = useAui({ threads: RuntimeAdapter(runtime) } as never);
610
+ return (
611
+ <AuiProvider value={aui}>
612
+ <Listener />
613
+ </AuiProvider>
614
+ );
615
+ };
616
+ render(<Harness />);
617
+ await act(async () => {});
618
+
619
+ runningByThreadId.set("t1", true);
620
+ await act(async () => sync());
621
+ await waitFor(() => expect(runtime.thread.getState().isRunning).toBe(true));
622
+
623
+ await act(async () => {
624
+ await runtime.threads.switchToThread("t2");
625
+ });
626
+ expect(runtime.threads.getState().mainThreadId).toBe("t2");
627
+ expect(globalRunEnd).not.toHaveBeenCalled();
628
+ });
629
+ });
@@ -168,9 +168,9 @@ export type ToolApprovalOption = {
168
168
  readonly id: string;
169
169
  /**
170
170
  * Decision class. Drives approved-resolution and default rendering.
171
- * Open union: `_`-prefixed custom kinds are never auto-resolved and the
172
- * default kit skips them; they must be answered with an explicit
173
- * `approved` value (optionally alongside the `optionId`).
171
+ * Open union: `_`-prefixed custom kinds are never auto-resolved; they must
172
+ * be answered with an explicit `approved` value (optionally alongside the
173
+ * `optionId`), which is what the default kit sends when one is chosen.
174
174
  */
175
175
  readonly kind: ToolApprovalOptionKind | (string & {});
176
176
  /** Human label. Renderers supply defaults per kind when omitted. */
@@ -182,12 +182,48 @@ export type ToolApprovalOption = {
182
182
  readonly confirm?: boolean | { title?: string; description?: string };
183
183
  };
184
184
 
185
+ /**
186
+ * How an approval request should be presented, and which answers it accepts.
187
+ * Absent: `"decision"`.
188
+ *
189
+ * The approval seam carries a host-owned request addressed by its own id and
190
+ * resolved once; a request wanting an answer these modes cannot express stays
191
+ * on the `interrupt` seam, which hands the tool an arbitrary payload instead.
192
+ * Unlike {@link ToolApprovalOptionKind} the set is closed, because a renderer
193
+ * that cannot cover every mode exhaustively is back to guessing the affordance.
194
+ */
195
+ export type ToolApprovalDisplay = "decision" | "select" | "text";
196
+
197
+ /**
198
+ * Whether the request asks for a free-form answer, on its own or alongside its
199
+ * options. Renderers read this to decide whether to offer a text affordance,
200
+ * and the runtime reads it to reject a `text` response the host cannot record.
201
+ */
202
+ export const toolApprovalAcceptsText = (approval: {
203
+ readonly display?: ToolApprovalDisplay;
204
+ readonly allowFreeform?: boolean;
205
+ }): boolean => approval.display === "text" || approval.allowFreeform === true;
206
+
185
207
  export type ToolApprovalResponse =
186
- | { readonly approved: boolean; readonly reason?: string }
187
- | { readonly optionId: string; readonly reason?: string }
208
+ | {
209
+ readonly approved: boolean;
210
+ readonly text?: string;
211
+ readonly reason?: string;
212
+ }
213
+ | {
214
+ readonly optionId: string;
215
+ readonly text?: string;
216
+ readonly reason?: string;
217
+ }
188
218
  | {
189
219
  readonly approved: boolean;
190
220
  readonly optionId: string;
221
+ readonly text?: string;
222
+ readonly reason?: string;
223
+ }
224
+ | {
225
+ /** Answer to a request that asks a question rather than for a decision. */
226
+ readonly text: string;
191
227
  readonly reason?: string;
192
228
  };
193
229
 
@@ -228,6 +264,16 @@ export type ToolCallMessagePart<
228
264
  /** Server-side approval gate. `respondToApproval` may only be called while `approved` is undefined and no `resolution` is recorded. */
229
265
  readonly approval?: {
230
266
  readonly id: string;
267
+ /**
268
+ * The question put to the user. A request that asks for something other
269
+ * than permission carries it here, so a renderer can show the question
270
+ * rather than inferring a decision from the tool name.
271
+ */
272
+ readonly prompt?: string;
273
+ /** How the request should be presented. Absent: a plain decision. */
274
+ readonly display?: ToolApprovalDisplay;
275
+ /** Whether a free-form answer is accepted alongside the options. */
276
+ readonly allowFreeform?: boolean;
231
277
  readonly approved?: boolean;
232
278
  readonly reason?: string;
233
279
  readonly isAutomatic?: boolean;
@@ -235,6 +281,8 @@ export type ToolCallMessagePart<
235
281
  readonly options?: readonly ToolApprovalOption[];
236
282
  /** The option chosen at resolution, when options were present. */
237
283
  readonly optionId?: string;
284
+ /** The free-form answer recorded at resolution, when one was given. */
285
+ readonly text?: string;
238
286
  /** Terminal non-decision state: the request was cancelled or expired without a user decision. Set by the host. */
239
287
  readonly resolution?: "cancelled" | "expired";
240
288
  };
@@ -306,6 +354,11 @@ export type ToolCallMessagePartStatus =
306
354
  /** Reason the tool call requires action. */
307
355
  readonly reason: "tool-calls" | "interrupt";
308
356
  }
357
+ | {
358
+ readonly type: "incomplete";
359
+ readonly reason: "tool-calls";
360
+ readonly error?: ReadonlyJSONValue;
361
+ }
309
362
  | MessagePartStatus;
310
363
 
311
364
  export type MessageStatus =
@@ -1,5 +1,9 @@
1
1
  import { describe, it, expect } from "vitest";
2
- import { httpUrlPattern, parseDataUrl } from "./data-url";
2
+ import {
3
+ httpUrlPattern,
4
+ parseDataUrl,
5
+ resolveFilePartSource,
6
+ } from "./data-url";
3
7
 
4
8
  describe("parseDataUrl", () => {
5
9
  it("parses a base64 data URL", () => {
@@ -72,3 +76,50 @@ describe("httpUrlPattern", () => {
72
76
  },
73
77
  );
74
78
  });
79
+
80
+ describe("resolveFilePartSource", () => {
81
+ it("uses an explicit url source type for opaque values", () => {
82
+ expect(
83
+ resolveFilePartSource({
84
+ data: "provider-file-id",
85
+ mimeType: "application/pdf",
86
+ sourceType: "url",
87
+ }),
88
+ ).toEqual({ kind: "url", url: "provider-file-id" });
89
+ });
90
+
91
+ it("uses http URLs as url sources", () => {
92
+ expect(
93
+ resolveFilePartSource({
94
+ data: "https://example.com/file.pdf",
95
+ mimeType: "application/pdf",
96
+ }),
97
+ ).toEqual({ kind: "url", url: "https://example.com/file.pdf" });
98
+ });
99
+
100
+ it("decodes data URLs and uses their media type", () => {
101
+ expect(
102
+ resolveFilePartSource({
103
+ data: "data:image/png;base64,aGVsbG8=",
104
+ mimeType: "application/octet-stream",
105
+ }),
106
+ ).toEqual({
107
+ kind: "data",
108
+ data: "aGVsbG8=",
109
+ mimeType: "image/png",
110
+ });
111
+ });
112
+
113
+ it("passes through opaque data with its declared media type", () => {
114
+ expect(
115
+ resolveFilePartSource({
116
+ data: "provider-file-id",
117
+ mimeType: "application/pdf",
118
+ }),
119
+ ).toEqual({
120
+ kind: "data",
121
+ data: "provider-file-id",
122
+ mimeType: "application/pdf",
123
+ });
124
+ });
125
+ });
@@ -1,5 +1,9 @@
1
1
  export const httpUrlPattern = /^https?:\/\//i;
2
2
 
3
+ export type FilePartSource =
4
+ | { kind: "url"; url: string }
5
+ | { kind: "data"; data: string; mimeType: string };
6
+
3
7
  export function parseDataUrl(
4
8
  value: string,
5
9
  ): { mimeType: string; data: string } | null {
@@ -8,6 +12,23 @@ export function parseDataUrl(
8
12
  return { mimeType: match[1]!.toLowerCase(), data: match[2]! };
9
13
  }
10
14
 
15
+ export const resolveFilePartSource = (part: {
16
+ data: string;
17
+ mimeType: string;
18
+ sourceType?: string | undefined;
19
+ }): FilePartSource => {
20
+ if (part.sourceType === "url" || httpUrlPattern.test(part.data)) {
21
+ return { kind: "url", url: part.data };
22
+ }
23
+
24
+ const parsed = parseDataUrl(part.data);
25
+ return {
26
+ kind: "data",
27
+ data: parsed?.data ?? part.data,
28
+ mimeType: parsed?.mimeType ?? part.mimeType,
29
+ };
30
+ };
31
+
11
32
  /**
12
33
  * Whether a payload is something `new URL()` accepts. Adapters that place a
13
34
  * `FileMessagePart` payload into a url-typed wire field use this to decide
@@ -0,0 +1,82 @@
1
+ import { afterEach, describe, expect, it, vi } from "vitest";
2
+ import { invokeUserCallback } from "./invoke-user-callback";
3
+
4
+ describe("invokeUserCallback", () => {
5
+ afterEach(() => {
6
+ vi.restoreAllMocks();
7
+ });
8
+
9
+ it("invokes a callback synchronously with its arguments", () => {
10
+ const callback = vi.fn((left: number, right: number) => left + right);
11
+
12
+ expect(invokeUserCallback("test", "onSuccess", callback, 2, 3)).toBe(
13
+ undefined,
14
+ );
15
+ expect(callback).toHaveBeenCalledOnce();
16
+ expect(callback).toHaveBeenCalledWith(2, 3);
17
+ });
18
+
19
+ it("reports and swallows a synchronous throw", () => {
20
+ const error = new Error("sync failure");
21
+ const consoleError = vi
22
+ .spyOn(console, "error")
23
+ .mockImplementation(() => {});
24
+
25
+ expect(() =>
26
+ invokeUserCallback("test", "onError", () => {
27
+ throw error;
28
+ }),
29
+ ).not.toThrow();
30
+ expect(consoleError).toHaveBeenCalledOnce();
31
+ expect(consoleError).toHaveBeenCalledWith(
32
+ "[test] onError callback threw an error",
33
+ error,
34
+ );
35
+ });
36
+
37
+ it("reports an asynchronous rejection and resolves", async () => {
38
+ const error = new Error("async failure");
39
+ const consoleError = vi
40
+ .spyOn(console, "error")
41
+ .mockImplementation(() => {});
42
+
43
+ await expect(
44
+ invokeUserCallback("test", "onError", async () => {
45
+ throw error;
46
+ }),
47
+ ).resolves.toBeUndefined();
48
+ expect(consoleError).toHaveBeenCalledOnce();
49
+ expect(consoleError).toHaveBeenCalledWith(
50
+ "[test] onError callback threw an error",
51
+ error,
52
+ );
53
+ });
54
+
55
+ it("resolves only after an asynchronous callback settles", async () => {
56
+ let settleCallback: (() => void) | undefined;
57
+ const callbackPromise = new Promise<void>((resolve) => {
58
+ settleCallback = resolve;
59
+ });
60
+ let settled = false;
61
+
62
+ const result = invokeUserCallback(
63
+ "test",
64
+ "onFinish",
65
+ () => callbackPromise,
66
+ );
67
+ void result?.then(() => {
68
+ settled = true;
69
+ });
70
+
71
+ await Promise.resolve();
72
+ expect(settled).toBe(false);
73
+
74
+ settleCallback?.();
75
+ await result;
76
+ expect(settled).toBe(true);
77
+ });
78
+
79
+ it("does nothing when the callback is undefined", () => {
80
+ expect(invokeUserCallback("test", "onMissing", undefined)).toBeUndefined();
81
+ });
82
+ });