@assistant-ui/react 0.14.16 → 0.14.19

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 (380) hide show
  1. package/dist/client/ExternalThread.d.ts +5 -3
  2. package/dist/client/ExternalThread.d.ts.map +1 -1
  3. package/dist/client/ExternalThread.js +745 -255
  4. package/dist/client/ExternalThread.js.map +1 -1
  5. package/dist/client/InMemoryThreadList.d.ts +1 -1
  6. package/dist/client/InMemoryThreadList.d.ts.map +1 -1
  7. package/dist/client/InMemoryThreadList.js +299 -113
  8. package/dist/client/InMemoryThreadList.js.map +1 -1
  9. package/dist/client/SingleThreadList.d.ts +1 -6
  10. package/dist/client/SingleThreadList.d.ts.map +1 -1
  11. package/dist/client/SingleThreadList.js +143 -55
  12. package/dist/client/SingleThreadList.js.map +1 -1
  13. package/dist/context/ReadonlyStore.js.map +1 -1
  14. package/dist/context/providers/MessageProvider.js +38 -5
  15. package/dist/context/providers/MessageProvider.js.map +1 -1
  16. package/dist/context/providers/ThreadViewportProvider.js +76 -20
  17. package/dist/context/providers/ThreadViewportProvider.js.map +1 -1
  18. package/dist/context/react/ThreadViewportContext.js.map +1 -1
  19. package/dist/context/react/utils/createContextHook.js.map +1 -1
  20. package/dist/context/react/utils/createContextStoreHook.js +17 -2
  21. package/dist/context/react/utils/createContextStoreHook.js.map +1 -1
  22. package/dist/context/react/utils/createStateHookForRuntime.js.map +1 -1
  23. package/dist/context/react/utils/ensureBinding.js.map +1 -1
  24. package/dist/context/react/utils/useRuntimeState.js +18 -2
  25. package/dist/context/react/utils/useRuntimeState.js.map +1 -1
  26. package/dist/context/stores/ThreadViewport.js.map +1 -1
  27. package/dist/devtools/DevToolsHooks.js.map +1 -1
  28. package/dist/hooks/useMessageQuote.js.map +1 -1
  29. package/dist/hooks/useMessageTiming.js +4 -1
  30. package/dist/hooks/useMessageTiming.js.map +1 -1
  31. package/dist/hooks/useToolCallElapsed.d.ts +23 -0
  32. package/dist/hooks/useToolCallElapsed.d.ts.map +1 -0
  33. package/dist/hooks/useToolCallElapsed.js +72 -0
  34. package/dist/hooks/useToolCallElapsed.js.map +1 -0
  35. package/dist/index.d.ts +6 -2
  36. package/dist/index.js +5 -1
  37. package/dist/internal.js.map +1 -1
  38. package/dist/legacy-runtime/AssistantRuntimeProvider.js +46 -10
  39. package/dist/legacy-runtime/AssistantRuntimeProvider.js.map +1 -1
  40. package/dist/legacy-runtime/cloud/auiV0.js.map +1 -1
  41. package/dist/legacy-runtime/cloud/useCloudThreadListRuntime.js +27 -6
  42. package/dist/legacy-runtime/cloud/useCloudThreadListRuntime.js.map +1 -1
  43. package/dist/legacy-runtime/hooks/AssistantContext.js +13 -2
  44. package/dist/legacy-runtime/hooks/AssistantContext.js.map +1 -1
  45. package/dist/legacy-runtime/hooks/AttachmentContext.js +9 -1
  46. package/dist/legacy-runtime/hooks/AttachmentContext.js.map +1 -1
  47. package/dist/legacy-runtime/hooks/ComposerContext.js +9 -1
  48. package/dist/legacy-runtime/hooks/ComposerContext.js.map +1 -1
  49. package/dist/legacy-runtime/hooks/MessageContext.js +12 -2
  50. package/dist/legacy-runtime/hooks/MessageContext.js.map +1 -1
  51. package/dist/legacy-runtime/hooks/MessagePartContext.js +9 -1
  52. package/dist/legacy-runtime/hooks/MessagePartContext.js.map +1 -1
  53. package/dist/legacy-runtime/hooks/ThreadContext.js +33 -5
  54. package/dist/legacy-runtime/hooks/ThreadContext.js.map +1 -1
  55. package/dist/legacy-runtime/hooks/ThreadListItemContext.js +9 -1
  56. package/dist/legacy-runtime/hooks/ThreadListItemContext.js.map +1 -1
  57. package/dist/legacy-runtime/runtime-cores/assistant-transport/commandQueue.js +3 -3
  58. package/dist/legacy-runtime/runtime-cores/assistant-transport/commandQueue.js.map +1 -1
  59. package/dist/legacy-runtime/runtime-cores/assistant-transport/replayBoundaryStream.js +71 -31
  60. package/dist/legacy-runtime/runtime-cores/assistant-transport/replayBoundaryStream.js.map +1 -1
  61. package/dist/legacy-runtime/runtime-cores/assistant-transport/runManager.js.map +1 -1
  62. package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.js +24 -16
  63. package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.js.map +1 -1
  64. package/dist/legacy-runtime/runtime-cores/assistant-transport/useConvertedState.js +17 -12
  65. package/dist/legacy-runtime/runtime-cores/assistant-transport/useConvertedState.js.map +1 -1
  66. package/dist/legacy-runtime/runtime-cores/assistant-transport/useLatestRef.js +17 -3
  67. package/dist/legacy-runtime/runtime-cores/assistant-transport/useLatestRef.js.map +1 -1
  68. package/dist/mcp-apps/McpAppRenderer.d.ts +2 -10
  69. package/dist/mcp-apps/McpAppRenderer.d.ts.map +1 -1
  70. package/dist/mcp-apps/McpAppRenderer.js +9 -8
  71. package/dist/mcp-apps/McpAppRenderer.js.map +1 -1
  72. package/dist/mcp-apps/McpAppsRemoteHost.d.ts +1 -8
  73. package/dist/mcp-apps/McpAppsRemoteHost.d.ts.map +1 -1
  74. package/dist/mcp-apps/McpAppsRemoteHost.js +6 -5
  75. package/dist/mcp-apps/McpAppsRemoteHost.js.map +1 -1
  76. package/dist/mcp-apps/app-frame.js +33 -14
  77. package/dist/mcp-apps/app-frame.js.map +1 -1
  78. package/dist/mcp-apps/bridge.js.map +1 -1
  79. package/dist/mcp-apps/types.js.map +1 -1
  80. package/dist/mcp-apps/utils.js.map +1 -1
  81. package/dist/model-context/frame/useAssistantFrameHost.js +32 -14
  82. package/dist/model-context/frame/useAssistantFrameHost.js.map +1 -1
  83. package/dist/model-context/makeAssistantVisible.js +64 -26
  84. package/dist/model-context/makeAssistantVisible.js.map +1 -1
  85. package/dist/primitives/actionBar/ActionBarCopy.js +94 -20
  86. package/dist/primitives/actionBar/ActionBarCopy.js.map +1 -1
  87. package/dist/primitives/actionBar/ActionBarEdit.js.map +1 -1
  88. package/dist/primitives/actionBar/ActionBarExportMarkdown.js +105 -37
  89. package/dist/primitives/actionBar/ActionBarExportMarkdown.js.map +1 -1
  90. package/dist/primitives/actionBar/ActionBarFeedbackNegative.js +60 -11
  91. package/dist/primitives/actionBar/ActionBarFeedbackNegative.js.map +1 -1
  92. package/dist/primitives/actionBar/ActionBarFeedbackPositive.js +60 -11
  93. package/dist/primitives/actionBar/ActionBarFeedbackPositive.js.map +1 -1
  94. package/dist/primitives/actionBar/ActionBarInteractionContext.js +3 -1
  95. package/dist/primitives/actionBar/ActionBarInteractionContext.js.map +1 -1
  96. package/dist/primitives/actionBar/ActionBarReload.js.map +1 -1
  97. package/dist/primitives/actionBar/ActionBarRoot.js +84 -25
  98. package/dist/primitives/actionBar/ActionBarRoot.js.map +1 -1
  99. package/dist/primitives/actionBar/ActionBarSpeak.js.map +1 -1
  100. package/dist/primitives/actionBar/ActionBarStopSpeaking.js +45 -14
  101. package/dist/primitives/actionBar/ActionBarStopSpeaking.js.map +1 -1
  102. package/dist/primitives/actionBar/useActionBarFloatStatus.js +22 -10
  103. package/dist/primitives/actionBar/useActionBarFloatStatus.js.map +1 -1
  104. package/dist/primitives/actionBar.js.map +1 -1
  105. package/dist/primitives/actionBarMore/ActionBarMoreContent.js +44 -7
  106. package/dist/primitives/actionBarMore/ActionBarMoreContent.js.map +1 -1
  107. package/dist/primitives/actionBarMore/ActionBarMoreItem.js +28 -6
  108. package/dist/primitives/actionBarMore/ActionBarMoreItem.js.map +1 -1
  109. package/dist/primitives/actionBarMore/ActionBarMoreRoot.js +103 -36
  110. package/dist/primitives/actionBarMore/ActionBarMoreRoot.js.map +1 -1
  111. package/dist/primitives/actionBarMore/ActionBarMoreSeparator.js +28 -6
  112. package/dist/primitives/actionBarMore/ActionBarMoreSeparator.js.map +1 -1
  113. package/dist/primitives/actionBarMore/ActionBarMoreTrigger.js +28 -6
  114. package/dist/primitives/actionBarMore/ActionBarMoreTrigger.js.map +1 -1
  115. package/dist/primitives/actionBarMore/scope.js.map +1 -1
  116. package/dist/primitives/actionBarMore.js.map +1 -1
  117. package/dist/primitives/assistantModal/AssistantModalAnchor.js +27 -6
  118. package/dist/primitives/assistantModal/AssistantModalAnchor.js.map +1 -1
  119. package/dist/primitives/assistantModal/AssistantModalContent.js +71 -10
  120. package/dist/primitives/assistantModal/AssistantModalContent.js.map +1 -1
  121. package/dist/primitives/assistantModal/AssistantModalRoot.js +93 -26
  122. package/dist/primitives/assistantModal/AssistantModalRoot.js.map +1 -1
  123. package/dist/primitives/assistantModal/AssistantModalTrigger.js +27 -6
  124. package/dist/primitives/assistantModal/AssistantModalTrigger.js.map +1 -1
  125. package/dist/primitives/assistantModal/scope.js.map +1 -1
  126. package/dist/primitives/assistantModal.js.map +1 -1
  127. package/dist/primitives/attachment/AttachmentName.js +13 -1
  128. package/dist/primitives/attachment/AttachmentName.js.map +1 -1
  129. package/dist/primitives/attachment/AttachmentRemove.js +11 -4
  130. package/dist/primitives/attachment/AttachmentRemove.js.map +1 -1
  131. package/dist/primitives/attachment/AttachmentRoot.js +13 -4
  132. package/dist/primitives/attachment/AttachmentRoot.js.map +1 -1
  133. package/dist/primitives/attachment/AttachmentThumb.js +20 -9
  134. package/dist/primitives/attachment/AttachmentThumb.js.map +1 -1
  135. package/dist/primitives/attachment.js.map +1 -1
  136. package/dist/primitives/branchPicker/BranchPickerCount.js +14 -2
  137. package/dist/primitives/branchPicker/BranchPickerCount.js.map +1 -1
  138. package/dist/primitives/branchPicker/BranchPickerNext.js.map +1 -1
  139. package/dist/primitives/branchPicker/BranchPickerNumber.js +14 -2
  140. package/dist/primitives/branchPicker/BranchPickerNumber.js.map +1 -1
  141. package/dist/primitives/branchPicker/BranchPickerPrevious.js.map +1 -1
  142. package/dist/primitives/branchPicker/BranchPickerRoot.js +34 -6
  143. package/dist/primitives/branchPicker/BranchPickerRoot.js.map +1 -1
  144. package/dist/primitives/branchPicker.js.map +1 -1
  145. package/dist/primitives/chainOfThought/ChainOfThoughtAccordionTrigger.js +16 -5
  146. package/dist/primitives/chainOfThought/ChainOfThoughtAccordionTrigger.js.map +1 -1
  147. package/dist/primitives/chainOfThought/ChainOfThoughtRoot.js +13 -4
  148. package/dist/primitives/chainOfThought/ChainOfThoughtRoot.js.map +1 -1
  149. package/dist/primitives/chainOfThought.js.map +1 -1
  150. package/dist/primitives/composer/ComposerAddAttachment.js +37 -24
  151. package/dist/primitives/composer/ComposerAddAttachment.js.map +1 -1
  152. package/dist/primitives/composer/ComposerAttachmentDropzone.js +124 -49
  153. package/dist/primitives/composer/ComposerAttachmentDropzone.js.map +1 -1
  154. package/dist/primitives/composer/ComposerCancel.js.map +1 -1
  155. package/dist/primitives/composer/ComposerDictate.js.map +1 -1
  156. package/dist/primitives/composer/ComposerDictationTranscript.js +32 -7
  157. package/dist/primitives/composer/ComposerDictationTranscript.js.map +1 -1
  158. package/dist/primitives/composer/ComposerInput.js +29 -29
  159. package/dist/primitives/composer/ComposerInput.js.map +1 -1
  160. package/dist/primitives/composer/ComposerInputPluginContext.js +71 -25
  161. package/dist/primitives/composer/ComposerInputPluginContext.js.map +1 -1
  162. package/dist/primitives/composer/ComposerQuote.js +92 -23
  163. package/dist/primitives/composer/ComposerQuote.js.map +1 -1
  164. package/dist/primitives/composer/ComposerRoot.js +45 -11
  165. package/dist/primitives/composer/ComposerRoot.js.map +1 -1
  166. package/dist/primitives/composer/ComposerSend.js +9 -2
  167. package/dist/primitives/composer/ComposerSend.js.map +1 -1
  168. package/dist/primitives/composer/ComposerStopDictation.js +15 -5
  169. package/dist/primitives/composer/ComposerStopDictation.js.map +1 -1
  170. package/dist/primitives/composer/trigger/TriggerPopover.d.ts.map +1 -1
  171. package/dist/primitives/composer/trigger/TriggerPopover.js +215 -75
  172. package/dist/primitives/composer/trigger/TriggerPopover.js.map +1 -1
  173. package/dist/primitives/composer/trigger/TriggerPopoverAction.js.map +1 -1
  174. package/dist/primitives/composer/trigger/TriggerPopoverBack.js +35 -7
  175. package/dist/primitives/composer/trigger/TriggerPopoverBack.js.map +1 -1
  176. package/dist/primitives/composer/trigger/TriggerPopoverCategories.js +134 -28
  177. package/dist/primitives/composer/trigger/TriggerPopoverCategories.js.map +1 -1
  178. package/dist/primitives/composer/trigger/TriggerPopoverDirective.js.map +1 -1
  179. package/dist/primitives/composer/trigger/TriggerPopoverItems.js +132 -28
  180. package/dist/primitives/composer/trigger/TriggerPopoverItems.js.map +1 -1
  181. package/dist/primitives/composer/trigger/TriggerPopoverResource.d.ts +2 -10
  182. package/dist/primitives/composer/trigger/TriggerPopoverResource.d.ts.map +1 -1
  183. package/dist/primitives/composer/trigger/TriggerPopoverResource.js +126 -53
  184. package/dist/primitives/composer/trigger/TriggerPopoverResource.js.map +1 -1
  185. package/dist/primitives/composer/trigger/TriggerPopoverRootContext.js +181 -78
  186. package/dist/primitives/composer/trigger/TriggerPopoverRootContext.js.map +1 -1
  187. package/dist/primitives/composer/trigger/detectTrigger.js.map +1 -1
  188. package/dist/primitives/composer/trigger/index.js.map +1 -1
  189. package/dist/primitives/composer/trigger/triggerDetectionResource.d.ts +2 -6
  190. package/dist/primitives/composer/trigger/triggerDetectionResource.d.ts.map +1 -1
  191. package/dist/primitives/composer/trigger/triggerDetectionResource.js +30 -15
  192. package/dist/primitives/composer/trigger/triggerDetectionResource.js.map +1 -1
  193. package/dist/primitives/composer/trigger/triggerKeyboardResource.d.ts +2 -17
  194. package/dist/primitives/composer/trigger/triggerKeyboardResource.d.ts.map +1 -1
  195. package/dist/primitives/composer/trigger/triggerKeyboardResource.js +117 -59
  196. package/dist/primitives/composer/trigger/triggerKeyboardResource.js.map +1 -1
  197. package/dist/primitives/composer/trigger/triggerNavigationResource.d.ts +2 -10
  198. package/dist/primitives/composer/trigger/triggerNavigationResource.d.ts.map +1 -1
  199. package/dist/primitives/composer/trigger/triggerNavigationResource.js +204 -71
  200. package/dist/primitives/composer/trigger/triggerNavigationResource.js.map +1 -1
  201. package/dist/primitives/composer/trigger/triggerSelectionResource.d.ts +2 -10
  202. package/dist/primitives/composer/trigger/triggerSelectionResource.d.ts.map +1 -1
  203. package/dist/primitives/composer/trigger/triggerSelectionResource.js +51 -14
  204. package/dist/primitives/composer/trigger/triggerSelectionResource.js.map +1 -1
  205. package/dist/primitives/composer.js.map +1 -1
  206. package/dist/primitives/dropdownMenuRenderPrimitives.js.map +1 -1
  207. package/dist/primitives/error/ErrorMessage.js +28 -6
  208. package/dist/primitives/error/ErrorMessage.js.map +1 -1
  209. package/dist/primitives/error/ErrorRoot.js +14 -5
  210. package/dist/primitives/error/ErrorRoot.js.map +1 -1
  211. package/dist/primitives/error.js.map +1 -1
  212. package/dist/primitives/message/MessageError.js +2 -1
  213. package/dist/primitives/message/MessageError.js.map +1 -1
  214. package/dist/primitives/message/MessageIf.js +50 -20
  215. package/dist/primitives/message/MessageIf.js.map +1 -1
  216. package/dist/primitives/message/MessageParts.js +41 -7
  217. package/dist/primitives/message/MessageParts.js.map +1 -1
  218. package/dist/primitives/message/MessagePartsGrouped.js +399 -94
  219. package/dist/primitives/message/MessagePartsGrouped.js.map +1 -1
  220. package/dist/primitives/message/MessageRoot.js +197 -65
  221. package/dist/primitives/message/MessageRoot.js.map +1 -1
  222. package/dist/primitives/message.js.map +1 -1
  223. package/dist/primitives/messagePart/MessagePartImage.js +15 -5
  224. package/dist/primitives/messagePart/MessagePartImage.js.map +1 -1
  225. package/dist/primitives/messagePart/MessagePartText.d.ts +5 -2
  226. package/dist/primitives/messagePart/MessagePartText.d.ts.map +1 -1
  227. package/dist/primitives/messagePart/MessagePartText.js +35 -7
  228. package/dist/primitives/messagePart/MessagePartText.js.map +1 -1
  229. package/dist/primitives/messagePart/useMessagePartData.js +5 -4
  230. package/dist/primitives/messagePart/useMessagePartData.js.map +1 -1
  231. package/dist/primitives/messagePart/useMessagePartFile.js +5 -4
  232. package/dist/primitives/messagePart/useMessagePartFile.js.map +1 -1
  233. package/dist/primitives/messagePart/useMessagePartImage.js +5 -4
  234. package/dist/primitives/messagePart/useMessagePartImage.js.map +1 -1
  235. package/dist/primitives/messagePart/useMessagePartReasoning.js +5 -4
  236. package/dist/primitives/messagePart/useMessagePartReasoning.js.map +1 -1
  237. package/dist/primitives/messagePart/useMessagePartSource.js +5 -4
  238. package/dist/primitives/messagePart/useMessagePartSource.js.map +1 -1
  239. package/dist/primitives/messagePart/useMessagePartText.js +5 -4
  240. package/dist/primitives/messagePart/useMessagePartText.js.map +1 -1
  241. package/dist/primitives/messagePart.js.map +1 -1
  242. package/dist/primitives/queueItem/QueueItemRemove.js +11 -4
  243. package/dist/primitives/queueItem/QueueItemRemove.js.map +1 -1
  244. package/dist/primitives/queueItem/QueueItemSteer.js +11 -4
  245. package/dist/primitives/queueItem/QueueItemSteer.js.map +1 -1
  246. package/dist/primitives/queueItem/QueueItemText.js +20 -6
  247. package/dist/primitives/queueItem/QueueItemText.js.map +1 -1
  248. package/dist/primitives/queueItem.js.map +1 -1
  249. package/dist/primitives/reasoning/useScrollLock.js +61 -34
  250. package/dist/primitives/reasoning/useScrollLock.js.map +1 -1
  251. package/dist/primitives/selectionToolbar/SelectionToolbarQuote.js +56 -16
  252. package/dist/primitives/selectionToolbar/SelectionToolbarQuote.js.map +1 -1
  253. package/dist/primitives/selectionToolbar/SelectionToolbarRoot.js +120 -59
  254. package/dist/primitives/selectionToolbar/SelectionToolbarRoot.js.map +1 -1
  255. package/dist/primitives/selectionToolbar.js.map +1 -1
  256. package/dist/primitives/suggestion/SuggestionDescription.js +20 -6
  257. package/dist/primitives/suggestion/SuggestionDescription.js.map +1 -1
  258. package/dist/primitives/suggestion/SuggestionTitle.js +20 -6
  259. package/dist/primitives/suggestion/SuggestionTitle.js.map +1 -1
  260. package/dist/primitives/suggestion/SuggestionTrigger.js +39 -26
  261. package/dist/primitives/suggestion/SuggestionTrigger.js.map +1 -1
  262. package/dist/primitives/suggestion.js.map +1 -1
  263. package/dist/primitives/thread/ThreadEmpty.js +6 -2
  264. package/dist/primitives/thread/ThreadEmpty.js.map +1 -1
  265. package/dist/primitives/thread/ThreadIf.js +32 -10
  266. package/dist/primitives/thread/ThreadIf.js.map +1 -1
  267. package/dist/primitives/thread/ThreadRoot.js +13 -4
  268. package/dist/primitives/thread/ThreadRoot.js.map +1 -1
  269. package/dist/primitives/thread/ThreadScrollToBottom.js +24 -6
  270. package/dist/primitives/thread/ThreadScrollToBottom.js.map +1 -1
  271. package/dist/primitives/thread/ThreadSuggestion.js +18 -6
  272. package/dist/primitives/thread/ThreadSuggestion.js.map +1 -1
  273. package/dist/primitives/thread/ThreadViewport.js +185 -47
  274. package/dist/primitives/thread/ThreadViewport.js.map +1 -1
  275. package/dist/primitives/thread/ThreadViewportFooter.js +22 -9
  276. package/dist/primitives/thread/ThreadViewportFooter.js.map +1 -1
  277. package/dist/primitives/thread/topAnchor/computeTopAnchorSlack.js.map +1 -1
  278. package/dist/primitives/thread/topAnchor/createReserveObservers.js.map +1 -1
  279. package/dist/primitives/thread/topAnchor/mountTopAnchorReserve.js.map +1 -1
  280. package/dist/primitives/thread/topAnchor/topAnchorTurn.js.map +1 -1
  281. package/dist/primitives/thread/topAnchor/topAnchorUtils.js.map +1 -1
  282. package/dist/primitives/thread/topAnchor/useTopAnchorReserve.js +19 -4
  283. package/dist/primitives/thread/topAnchor/useTopAnchorReserve.js.map +1 -1
  284. package/dist/primitives/thread/useThreadViewportAutoScroll.d.ts.map +1 -1
  285. package/dist/primitives/thread/useThreadViewportAutoScroll.js +21 -16
  286. package/dist/primitives/thread/useThreadViewportAutoScroll.js.map +1 -1
  287. package/dist/primitives/thread.js.map +1 -1
  288. package/dist/primitives/threadList/ThreadListLoadMore.js.map +1 -1
  289. package/dist/primitives/threadList/ThreadListNew.js +53 -11
  290. package/dist/primitives/threadList/ThreadListNew.js.map +1 -1
  291. package/dist/primitives/threadList/ThreadListRoot.js +13 -4
  292. package/dist/primitives/threadList/ThreadListRoot.js.map +1 -1
  293. package/dist/primitives/threadList.js.map +1 -1
  294. package/dist/primitives/threadListItem/ThreadListItemArchive.js.map +1 -1
  295. package/dist/primitives/threadListItem/ThreadListItemDelete.js.map +1 -1
  296. package/dist/primitives/threadListItem/ThreadListItemRoot.js +26 -7
  297. package/dist/primitives/threadListItem/ThreadListItemRoot.js.map +1 -1
  298. package/dist/primitives/threadListItem/ThreadListItemTrigger.js.map +1 -1
  299. package/dist/primitives/threadListItem/ThreadListItemUnarchive.js.map +1 -1
  300. package/dist/primitives/threadListItem.js.map +1 -1
  301. package/dist/primitives/threadListItemMore/ThreadListItemMoreContent.js +44 -7
  302. package/dist/primitives/threadListItemMore/ThreadListItemMoreContent.js.map +1 -1
  303. package/dist/primitives/threadListItemMore/ThreadListItemMoreItem.js +28 -6
  304. package/dist/primitives/threadListItemMore/ThreadListItemMoreItem.js.map +1 -1
  305. package/dist/primitives/threadListItemMore/ThreadListItemMoreRoot.js +25 -5
  306. package/dist/primitives/threadListItemMore/ThreadListItemMoreRoot.js.map +1 -1
  307. package/dist/primitives/threadListItemMore/ThreadListItemMoreSeparator.js +28 -6
  308. package/dist/primitives/threadListItemMore/ThreadListItemMoreSeparator.js.map +1 -1
  309. package/dist/primitives/threadListItemMore/ThreadListItemMoreTrigger.js +28 -6
  310. package/dist/primitives/threadListItemMore/ThreadListItemMoreTrigger.js.map +1 -1
  311. package/dist/primitives/threadListItemMore/scope.js.map +1 -1
  312. package/dist/primitives/threadListItemMore.js.map +1 -1
  313. package/dist/sandbox-host/SandboxHost.js.map +1 -1
  314. package/dist/tests/remote-thread-list-test-helpers.js.map +1 -1
  315. package/dist/tests/setup.js.map +1 -1
  316. package/dist/unstable/useComposerInputHistory.d.ts +30 -0
  317. package/dist/unstable/useComposerInputHistory.d.ts.map +1 -0
  318. package/dist/unstable/useComposerInputHistory.js +117 -0
  319. package/dist/unstable/useComposerInputHistory.js.map +1 -0
  320. package/dist/unstable/useMentionAdapter.js.map +1 -1
  321. package/dist/unstable/useMessageStallDetection.d.ts +29 -0
  322. package/dist/unstable/useMessageStallDetection.d.ts.map +1 -0
  323. package/dist/unstable/useMessageStallDetection.js +69 -0
  324. package/dist/unstable/useMessageStallDetection.js.map +1 -0
  325. package/dist/unstable/useSlashCommandAdapter.js.map +1 -1
  326. package/dist/utils/Primitive.js +57 -12
  327. package/dist/utils/Primitive.js.map +1 -1
  328. package/dist/utils/createActionButton.js +23 -7
  329. package/dist/utils/createActionButton.js.map +1 -1
  330. package/dist/utils/getSelectionMessageId.js.map +1 -1
  331. package/dist/utils/hooks/useManagedRef.js +16 -8
  332. package/dist/utils/hooks/useManagedRef.js.map +1 -1
  333. package/dist/utils/hooks/useMediaQuery.js +25 -10
  334. package/dist/utils/hooks/useMediaQuery.js.map +1 -1
  335. package/dist/utils/hooks/useOnResizeContent.js +29 -19
  336. package/dist/utils/hooks/useOnResizeContent.js.map +1 -1
  337. package/dist/utils/hooks/useOnScrollToBottom.js +20 -4
  338. package/dist/utils/hooks/useOnScrollToBottom.js.map +1 -1
  339. package/dist/utils/hooks/useSizeHandle.js +23 -15
  340. package/dist/utils/hooks/useSizeHandle.js.map +1 -1
  341. package/dist/utils/json/is-json-equal.js.map +1 -1
  342. package/dist/utils/json/is-json.js.map +1 -1
  343. package/dist/utils/smooth/SmoothContext.js +41 -11
  344. package/dist/utils/smooth/SmoothContext.js.map +1 -1
  345. package/dist/utils/smooth/useSmooth.d.ts +40 -2
  346. package/dist/utils/smooth/useSmooth.d.ts.map +1 -1
  347. package/dist/utils/smooth/useSmooth.js +52 -13
  348. package/dist/utils/smooth/useSmooth.js.map +1 -1
  349. package/dist/utils/useToolArgsFieldStatus.d.ts +2 -2
  350. package/dist/utils/useToolArgsFieldStatus.d.ts.map +1 -1
  351. package/dist/utils/useToolArgsFieldStatus.js +13 -5
  352. package/dist/utils/useToolArgsFieldStatus.js.map +1 -1
  353. package/package.json +6 -6
  354. package/src/client/ExternalThread.ts +146 -74
  355. package/src/client/InMemoryThreadList.ts +23 -21
  356. package/src/client/SingleThreadList.ts +29 -27
  357. package/src/hooks/useToolCallElapsed.ts +52 -0
  358. package/src/index.ts +19 -0
  359. package/src/mcp-apps/McpAppRenderer.tsx +5 -3
  360. package/src/mcp-apps/McpAppsRemoteHost.ts +5 -3
  361. package/src/primitives/composer/ComposerInput.test.tsx +1 -1
  362. package/src/primitives/composer/ComposerInput.tsx +3 -3
  363. package/src/primitives/composer/trigger/TriggerPopover.tsx +4 -5
  364. package/src/primitives/composer/trigger/TriggerPopoverResource.ts +5 -3
  365. package/src/primitives/composer/trigger/triggerDetectionResource.ts +21 -21
  366. package/src/primitives/composer/trigger/triggerKeyboardResource.test.ts +5 -4
  367. package/src/primitives/composer/trigger/triggerKeyboardResource.ts +99 -101
  368. package/src/primitives/composer/trigger/triggerNavigationResource.ts +92 -98
  369. package/src/primitives/composer/trigger/triggerSelectionResource.ts +76 -76
  370. package/src/primitives/messagePart/MessagePartText.tsx +3 -2
  371. package/src/primitives/reasoning/useScrollLock.ts +25 -2
  372. package/src/primitives/thread/useThreadViewportAutoScroll.ts +8 -0
  373. package/src/tests/external-thread-branches.test.tsx +160 -0
  374. package/src/tests/shouldContinue.test.ts +33 -0
  375. package/src/tests/toolCallTiming.test.tsx +221 -0
  376. package/src/unstable/useComposerInputHistory.test.tsx +201 -0
  377. package/src/unstable/useComposerInputHistory.ts +160 -0
  378. package/src/unstable/useMessageStallDetection.ts +91 -0
  379. package/src/utils/smooth/useSmooth.test.tsx +95 -0
  380. package/src/utils/smooth/useSmooth.ts +82 -10
@@ -13,18 +13,25 @@ import type {
13
13
  AppendMessage,
14
14
  Attachment,
15
15
  CreateAttachment,
16
+ RespondToToolApprovalOptions,
16
17
  ThreadAssistantMessagePart,
17
18
  ThreadUserMessagePart,
18
19
  ThreadMessage,
19
20
  ExternalThreadQueueAdapter,
21
+ ExternalThreadBranchAdapter,
20
22
  } from "@assistant-ui/core";
21
23
  import type { QueueItemState } from "@assistant-ui/core/store";
22
24
  import type { ComposerSendOptions } from "@assistant-ui/core/store";
25
+ import {
26
+ getThreadMessageText,
27
+ resolveToolApprovalResponse,
28
+ } from "@assistant-ui/core/internal";
23
29
  import { ModelContext, Suggestions } from "@assistant-ui/core/store";
24
30
  import { Tools, DataRenderers } from "@assistant-ui/core/react";
25
31
  import { SingleThreadList } from "./SingleThreadList";
26
32
 
27
33
  const EMPTY_QUEUE_ITEMS: readonly QueueItemState[] = [];
34
+ const EMPTY_BRANCH_IDS: readonly string[] = [];
28
35
 
29
36
  export type ExternalThreadMessage = ThreadMessage & {
30
37
  id: string;
@@ -51,6 +58,10 @@ export type ExternalThreadProps = {
51
58
  onCancel?: () => void;
52
59
  /** Queue adapter for runtimes that support message queuing and steering. */
53
60
  queue?: ExternalThreadQueueAdapter;
61
+ /** Branch adapter for runtimes that track sibling variants of messages. */
62
+ branches?: ExternalThreadBranchAdapter;
63
+ /** Callback for tool approval decisions. Absent: responding to an approval throws a capability error. */
64
+ onRespondToToolApproval?: (options: RespondToToolApprovalOptions) => void;
54
65
  };
55
66
 
56
67
  type MessageClientProps = {
@@ -59,38 +70,42 @@ type MessageClientProps = {
59
70
  onEdit?: (message: AppendMessage) => void;
60
71
  onReload?: () => void;
61
72
  queue?: ExternalThreadQueueAdapter | undefined;
73
+ branches?: ExternalThreadBranchAdapter | undefined;
74
+ onRespondToToolApproval?:
75
+ | ((options: RespondToToolApprovalOptions) => void)
76
+ | undefined;
62
77
  };
63
78
 
64
79
  // Message Client - minimal implementation
65
- const MessageClient = resource(function MessageClient({
80
+ const useMessageClient = ({
66
81
  message,
67
82
  index,
68
83
  onEdit,
69
84
  onReload,
70
85
  queue,
71
- }: MessageClientProps): ClientOutput<"message"> {
86
+ branches,
87
+ onRespondToToolApproval,
88
+ }: MessageClientProps): ClientOutput<"message"> => {
72
89
  const [isCopied, setIsCopied] = useState(false);
73
90
  const [isHovering, setIsHovering] = useState(false);
74
91
  const [isEditing, setIsEditing] = useState(false);
75
92
 
76
93
  const partClients = useClientLookup(
77
- () =>
78
- message.content.map((part, idx) => withKey(idx, PartResource({ part }))),
79
- [message.content],
94
+ message.content.map((part, idx) =>
95
+ withKey(idx, PartResource({ part, onRespondToToolApproval })),
96
+ ),
80
97
  );
81
98
 
82
99
  const attachmentClients = useClientLookup(
83
- () =>
84
- (message.attachments ?? []).map((attachment) =>
85
- withKey(
86
- attachment.id,
87
- AttachmentResource({
88
- attachment,
89
- onRemove: () => {},
90
- }),
91
- ),
100
+ (message.attachments ?? []).map((attachment) =>
101
+ withKey(
102
+ attachment.id,
103
+ AttachmentResource({
104
+ attachment,
105
+ onRemove: () => {},
106
+ }),
92
107
  ),
93
- [message.attachments],
108
+ ),
94
109
  );
95
110
 
96
111
  const handleBeginEdit = () => {
@@ -124,14 +139,19 @@ const MessageClient = resource(function MessageClient({
124
139
  }),
125
140
  );
126
141
 
142
+ const branchIds = branches?.getBranches(message.id) ?? EMPTY_BRANCH_IDS;
143
+ const branchIndex = branchIds.indexOf(message.id);
144
+ const branchNumber = branchIndex === -1 ? 1 : branchIndex + 1;
145
+ const branchCount = branchIndex === -1 ? 1 : branchIds.length;
146
+
127
147
  const state = useMemo(() => {
128
148
  return {
129
149
  ...message,
130
150
  attachments: message.attachments ?? [],
131
151
  parentId: null,
132
152
  isLast: false, // Will be set by thread
133
- branchNumber: 1,
134
- branchCount: 1,
153
+ branchNumber,
154
+ branchCount,
135
155
  speech: undefined,
136
156
  parts: partClients.state,
137
157
  isCopied,
@@ -146,20 +166,35 @@ const MessageClient = resource(function MessageClient({
146
166
  index,
147
167
  composerClient.state,
148
168
  partClients.state,
169
+ branchNumber,
170
+ branchCount,
149
171
  ]);
150
172
 
151
173
  return {
152
174
  getState: () => state,
153
175
  composer: () => composerClient.methods,
176
+ delete: () => {},
154
177
  reload: () => {
155
178
  onReload?.();
156
179
  },
157
180
  speak: () => {},
158
181
  stopSpeaking: () => {},
159
182
  submitFeedback: () => {},
160
- switchToBranch: () => {},
161
- getCopyText: () =>
162
- message.content.map((c) => ("text" in c ? c.text : "")).join(""),
183
+ switchToBranch: ({ position, branchId }) => {
184
+ if (!branches) return;
185
+ const target =
186
+ branchId ??
187
+ (branchIndex === -1
188
+ ? undefined
189
+ : position === "previous"
190
+ ? branchIds[branchIndex - 1]
191
+ : position === "next"
192
+ ? branchIds[branchIndex + 1]
193
+ : undefined);
194
+ if (target !== undefined && target !== message.id)
195
+ branches.switchToBranch(target);
196
+ },
197
+ getCopyText: () => getThreadMessageText(message),
163
198
  part: (selector) => {
164
199
  if ("index" in selector) {
165
200
  return partClients.get(selector);
@@ -178,16 +213,22 @@ const MessageClient = resource(function MessageClient({
178
213
  setIsCopied,
179
214
  setIsHovering,
180
215
  };
181
- });
216
+ };
217
+
218
+ const MessageClient = resource(useMessageClient);
182
219
 
183
220
  type PartResourceProps = {
184
221
  part: ThreadAssistantMessagePart | ThreadUserMessagePart;
222
+ onRespondToToolApproval?:
223
+ | ((options: RespondToToolApprovalOptions) => void)
224
+ | undefined;
185
225
  };
186
226
 
187
227
  // Part Client - minimal implementation
188
- const PartResource = resource(function PartResource({
228
+ const usePartResource = ({
189
229
  part,
190
- }: PartResourceProps): ClientOutput<"part"> {
230
+ onRespondToToolApproval,
231
+ }: PartResourceProps): ClientOutput<"part"> => {
191
232
  const state = useMemo(
192
233
  () => ({
193
234
  ...part,
@@ -200,9 +241,30 @@ const PartResource = resource(function PartResource({
200
241
  getState: () => state,
201
242
  addToolResult: () => {},
202
243
  resumeToolCall: () => {},
203
- respondToToolApproval: () => {},
244
+ respondToToolApproval: (response) => {
245
+ if (!onRespondToToolApproval)
246
+ throw new Error("Runtime does not support tool approvals.");
247
+
248
+ if (part.type !== "tool-call")
249
+ throw new Error(
250
+ "Tried to respond to tool approval on non-tool message part",
251
+ );
252
+
253
+ if (
254
+ !part.approval ||
255
+ part.approval.approved !== undefined ||
256
+ part.approval.resolution !== undefined
257
+ )
258
+ throw new Error("Tool call has no pending approval");
259
+
260
+ onRespondToToolApproval(
261
+ resolveToolApprovalResponse(part.approval, response),
262
+ );
263
+ },
204
264
  };
205
- });
265
+ };
266
+
267
+ const PartResource = resource(usePartResource);
206
268
 
207
269
  type AttachmentResourceProps = {
208
270
  attachment: Attachment;
@@ -210,17 +272,19 @@ type AttachmentResourceProps = {
210
272
  };
211
273
 
212
274
  // Attachment Client - minimal implementation
213
- const AttachmentResource = resource(function AttachmentResource({
275
+ const useAttachmentResource = ({
214
276
  attachment,
215
277
  onRemove,
216
- }: AttachmentResourceProps): ClientOutput<"attachment"> {
278
+ }: AttachmentResourceProps): ClientOutput<"attachment"> => {
217
279
  return {
218
280
  getState: () => attachment,
219
281
  remove: async () => {
220
282
  onRemove?.();
221
283
  },
222
284
  };
223
- });
285
+ };
286
+
287
+ const AttachmentResource = resource(useAttachmentResource);
224
288
 
225
289
  type ComposerClientResourceProps = {
226
290
  type: "thread" | "edit";
@@ -234,7 +298,7 @@ type ComposerClientResourceProps = {
234
298
  queue?: ExternalThreadQueueAdapter | undefined;
235
299
  };
236
300
 
237
- const QueueItemClient = resource(function QueueItemClient({
301
+ const useQueueItemClient = ({
238
302
  item,
239
303
  onSteer,
240
304
  onRemove,
@@ -242,16 +306,18 @@ const QueueItemClient = resource(function QueueItemClient({
242
306
  item: QueueItemState;
243
307
  onSteer: () => void;
244
308
  onRemove: () => void;
245
- }): ClientOutput<"queueItem"> {
309
+ }): ClientOutput<"queueItem"> => {
246
310
  return {
247
311
  getState: () => item,
248
312
  steer: onSteer,
249
313
  remove: onRemove,
250
314
  };
251
- });
315
+ };
316
+
317
+ const QueueItemClient = resource(useQueueItemClient);
252
318
 
253
319
  // Composer Client - minimal implementation
254
- const ComposerClientResource = resource(function ComposerClientResource({
320
+ const useComposerClientResource = ({
255
321
  type,
256
322
  isEditing,
257
323
  canCancel,
@@ -261,7 +327,7 @@ const ComposerClientResource = resource(function ComposerClientResource({
261
327
  onSend,
262
328
  message,
263
329
  queue,
264
- }: ComposerClientResourceProps): ClientOutput<"composer"> {
330
+ }: ComposerClientResourceProps): ClientOutput<"composer"> => {
265
331
  const [text, setText] = useState("");
266
332
  const [role, setRole] = useState<"user" | "assistant" | "system">("user");
267
333
  const [runConfig, setRunConfig] = useState<Record<string, unknown>>({});
@@ -292,35 +358,31 @@ const ComposerClientResource = resource(function ComposerClientResource({
292
358
  }, [isEditing]);
293
359
 
294
360
  const attachmentClients = useClientLookup(
295
- () =>
296
- attachments.map((attachment, idx) =>
297
- withKey(
298
- attachment.id,
299
- AttachmentResource({
300
- attachment,
301
- onRemove: () => {
302
- setAttachments(attachments.filter((_, i) => i !== idx));
303
- },
304
- }),
305
- ),
361
+ attachments.map((attachment, idx) =>
362
+ withKey(
363
+ attachment.id,
364
+ AttachmentResource({
365
+ attachment,
366
+ onRemove: () => {
367
+ setAttachments(attachments.filter((_, i) => i !== idx));
368
+ },
369
+ }),
306
370
  ),
307
- [attachments],
371
+ ),
308
372
  );
309
373
 
310
374
  const queueItems = queue?.items ?? EMPTY_QUEUE_ITEMS;
311
375
  const queueItemClients = useClientLookup(
312
- () =>
313
- queueItems.map((item) =>
314
- withKey(
315
- item.id,
316
- QueueItemClient({
317
- item,
318
- onSteer: () => queue?.steer(item.id),
319
- onRemove: () => queue?.remove(item.id),
320
- }),
321
- ),
376
+ queueItems.map((item) =>
377
+ withKey(
378
+ item.id,
379
+ QueueItemClient({
380
+ item,
381
+ onSteer: () => queue?.steer(item.id),
382
+ onRemove: () => queue?.remove(item.id),
383
+ }),
322
384
  ),
323
- [queueItems],
385
+ ),
324
386
  );
325
387
 
326
388
  const state = useMemo(() => {
@@ -436,10 +498,12 @@ const ComposerClientResource = resource(function ComposerClientResource({
436
498
  return queueItemClients.get(selector);
437
499
  },
438
500
  };
439
- });
501
+ };
502
+
503
+ const ComposerClientResource = resource(useComposerClientResource);
440
504
 
441
505
  // External Thread Client
442
- export const ExternalThread = resource(function ExternalThread({
506
+ const useExternalThread = ({
443
507
  messages,
444
508
  isRunning = false,
445
509
  isSendDisabled = false,
@@ -449,7 +513,9 @@ export const ExternalThread = resource(function ExternalThread({
449
513
  onStartRun,
450
514
  onCancel,
451
515
  queue,
452
- }: ExternalThreadProps): ClientOutput<"thread"> {
516
+ branches,
517
+ onRespondToToolApproval,
518
+ }: ExternalThreadProps): ClientOutput<"thread"> => {
453
519
  const handleReload = (messageId: string) => {
454
520
  const messageIndex = messages.findIndex((m) => m.id === messageId);
455
521
  if (messageIndex === -1) return;
@@ -460,18 +526,18 @@ export const ExternalThread = resource(function ExternalThread({
460
526
  };
461
527
 
462
528
  const messageClients = useClientLookup(
463
- () =>
464
- messages.map((msg, index) => {
465
- const props: MessageClientProps = {
466
- message: msg,
467
- index,
468
- onReload: () => handleReload(msg.id),
469
- queue,
470
- };
471
- if (onEdit) props.onEdit = onEdit;
472
- return withKey(msg.id, MessageClient(props));
473
- }),
474
- [messages, onEdit, queue],
529
+ messages.map((msg, index) => {
530
+ const props: MessageClientProps = {
531
+ message: msg,
532
+ index,
533
+ onReload: () => handleReload(msg.id),
534
+ queue,
535
+ branches,
536
+ onRespondToToolApproval,
537
+ };
538
+ if (onEdit) props.onEdit = onEdit;
539
+ return withKey(msg.id, MessageClient(props));
540
+ }),
475
541
  );
476
542
 
477
543
  const handleCancelRun = () => {
@@ -496,6 +562,7 @@ export const ExternalThread = resource(function ExternalThread({
496
562
  );
497
563
 
498
564
  const hasQueue = !!queue;
565
+ const hasBranches = !!branches;
499
566
  const state = useMemo(() => {
500
567
  const messageStates = messageClients.state.map((s, idx, arr) => ({
501
568
  ...s,
@@ -509,13 +576,14 @@ export const ExternalThread = resource(function ExternalThread({
509
576
  isRunning,
510
577
  capabilities: {
511
578
  edit: false,
579
+ delete: false,
512
580
  reload: false,
513
581
  cancel: isRunning,
514
582
  speech: false,
515
583
  attachments: false,
516
584
  feedback: false,
517
585
  voice: false,
518
- switchToBranch: false,
586
+ switchToBranch: hasBranches,
519
587
  switchBranchDuringRun: false,
520
588
  unstable_copy: false,
521
589
  dictation: false,
@@ -533,6 +601,7 @@ export const ExternalThread = resource(function ExternalThread({
533
601
  messages,
534
602
  isRunning,
535
603
  hasQueue,
604
+ hasBranches,
536
605
  messageClients.state,
537
606
  composerClient.state,
538
607
  ]);
@@ -570,6 +639,7 @@ export const ExternalThread = resource(function ExternalThread({
570
639
  onNew?.(appendMessage);
571
640
  }
572
641
  },
642
+ deleteMessage: () => {},
573
643
  startRun: () => {
574
644
  onStartRun?.();
575
645
  },
@@ -593,9 +663,11 @@ export const ExternalThread = resource(function ExternalThread({
593
663
  muteVoice: () => {},
594
664
  unmuteVoice: () => {},
595
665
  };
596
- });
666
+ };
667
+
668
+ export const ExternalThread = resource(useExternalThread);
597
669
 
598
- attachTransformScopes(ExternalThread, (scopes, parent) => {
670
+ attachTransformScopes(useExternalThread, (scopes, parent) => {
599
671
  if (!scopes.threads && parent.threads.source === null) {
600
672
  const threadElement = scopes.thread as ClientElement<"thread">;
601
673
  scopes.threads = SingleThreadList({ thread: threadElement });
@@ -27,14 +27,14 @@ type ThreadData = {
27
27
  };
28
28
 
29
29
  // ThreadListItem Client
30
- const ThreadListItemClient = resource(function ThreadListItemClient(props: {
30
+ const useThreadListItemClient = (props: {
31
31
  data: ThreadData;
32
32
  onSwitchTo: () => void;
33
33
  onUpdateCustom: (custom: Record<string, unknown> | undefined) => void;
34
34
  onArchive: () => void;
35
35
  onUnarchive: () => void;
36
36
  onDelete: () => void;
37
- }): ClientOutput<"threadListItem"> {
37
+ }): ClientOutput<"threadListItem"> => {
38
38
  const { data, onSwitchTo, onUpdateCustom, onArchive, onUnarchive, onDelete } =
39
39
  props;
40
40
  const state = useMemo(
@@ -61,12 +61,14 @@ const ThreadListItemClient = resource(function ThreadListItemClient(props: {
61
61
  initialize: async () => ({ remoteId: data.id, externalId: undefined }),
62
62
  detach: () => {},
63
63
  };
64
- });
64
+ };
65
+
66
+ const ThreadListItemClient = resource(useThreadListItemClient);
65
67
 
66
68
  // InMemoryThreadList Client
67
- export const InMemoryThreadList = resource(function InMemoryThreadList(
69
+ const useInMemoryThreadList = (
68
70
  props: InMemoryThreadListProps,
69
- ): ClientOutput<"threads"> {
71
+ ): ClientOutput<"threads"> => {
70
72
  const {
71
73
  thread: threadFactory,
72
74
  onSwitchToThread,
@@ -127,21 +129,19 @@ export const InMemoryThreadList = resource(function InMemoryThreadList(
127
129
  };
128
130
 
129
131
  const threadListItems = useClientLookup(
130
- () =>
131
- threads.map((t) =>
132
- withKey(
133
- t.id,
134
- ThreadListItemClient({
135
- data: t,
136
- onSwitchTo: () => handleSwitchToThread(t.id),
137
- onUpdateCustom: (custom) => handleUpdateCustom(t.id, custom),
138
- onArchive: () => handleArchive(t.id),
139
- onUnarchive: () => handleUnarchive(t.id),
140
- onDelete: () => handleDelete(t.id),
141
- }),
142
- ),
132
+ threads.map((t) =>
133
+ withKey(
134
+ t.id,
135
+ ThreadListItemClient({
136
+ data: t,
137
+ onSwitchTo: () => handleSwitchToThread(t.id),
138
+ onUpdateCustom: (custom) => handleUpdateCustom(t.id, custom),
139
+ onArchive: () => handleArchive(t.id),
140
+ onUnarchive: () => handleUnarchive(t.id),
141
+ onDelete: () => handleDelete(t.id),
142
+ }),
143
143
  ),
144
- [threads],
144
+ ),
145
145
  );
146
146
 
147
147
  // Create the main thread
@@ -184,9 +184,11 @@ export const InMemoryThreadList = resource(function InMemoryThreadList(
184
184
  },
185
185
  thread: () => mainThreadClient.methods,
186
186
  };
187
- });
187
+ };
188
+
189
+ export const InMemoryThreadList = resource(useInMemoryThreadList);
188
190
 
189
- attachTransformScopes(InMemoryThreadList, (scopes, parent) => {
191
+ attachTransformScopes(useInMemoryThreadList, (scopes, parent) => {
190
192
  scopes.thread ??= Derived({
191
193
  source: "threads",
192
194
  query: { type: "main" },
@@ -9,31 +9,31 @@ import {
9
9
  const RESOLVED_PROMISE = Promise.resolve();
10
10
  const THREAD_ID = "default";
11
11
 
12
- const SingleThreadListItem = resource(
13
- function SingleThreadListItem(): ClientOutput<"threadListItem"> {
14
- const [custom, setCustom] = useState<Record<string, unknown> | undefined>();
12
+ const useSingleThreadListItem = (): ClientOutput<"threadListItem"> => {
13
+ const [custom, setCustom] = useState<Record<string, unknown> | undefined>();
15
14
 
16
- return {
17
- getState: () => ({
18
- id: THREAD_ID,
19
- remoteId: undefined,
20
- externalId: undefined,
21
- title: undefined,
22
- status: "regular",
23
- custom,
24
- }),
25
- switchTo: () => {},
26
- rename: () => {},
27
- updateCustom: setCustom,
28
- archive: () => {},
29
- unarchive: () => {},
30
- delete: () => {},
31
- generateTitle: () => {},
32
- initialize: async () => ({ remoteId: THREAD_ID, externalId: undefined }),
33
- detach: () => {},
34
- };
35
- },
36
- );
15
+ return {
16
+ getState: () => ({
17
+ id: THREAD_ID,
18
+ remoteId: undefined,
19
+ externalId: undefined,
20
+ title: undefined,
21
+ status: "regular",
22
+ custom,
23
+ }),
24
+ switchTo: () => {},
25
+ rename: () => {},
26
+ updateCustom: setCustom,
27
+ archive: () => {},
28
+ unarchive: () => {},
29
+ delete: () => {},
30
+ generateTitle: () => {},
31
+ initialize: async () => ({ remoteId: THREAD_ID, externalId: undefined }),
32
+ detach: () => {},
33
+ };
34
+ };
35
+
36
+ const SingleThreadListItem = resource(useSingleThreadListItem);
37
37
 
38
38
  type SingleThreadListProps = {
39
39
  thread: ClientElement<"thread">;
@@ -44,9 +44,9 @@ type SingleThreadListProps = {
44
44
  * Automatically provided by ExternalThread when no threads scope exists.
45
45
  * Mounts the provided thread resource element.
46
46
  */
47
- export const SingleThreadList = resource(function SingleThreadList({
47
+ const useSingleThreadList = ({
48
48
  thread,
49
- }: SingleThreadListProps): ClientOutput<"threads"> {
49
+ }: SingleThreadListProps): ClientOutput<"threads"> => {
50
50
  const itemClient = useClientResource(SingleThreadListItem());
51
51
  const threadClient = useClientResource(thread);
52
52
 
@@ -105,4 +105,6 @@ export const SingleThreadList = resource(function SingleThreadList({
105
105
  return threadClient.methods;
106
106
  },
107
107
  };
108
- });
108
+ };
109
+
110
+ export const SingleThreadList = resource(useSingleThreadList);
@@ -0,0 +1,52 @@
1
+ "use client";
2
+
3
+ import { useEffect, useState } from "react";
4
+ import { useAui, useAuiState } from "@assistant-ui/store";
5
+
6
+ /**
7
+ * Hook that returns the elapsed wall-clock time of the current tool call in
8
+ * milliseconds, ticking once per second while the call runs.
9
+ *
10
+ * Reads `part.timing`. Returns `undefined` when the part is not a tool call,
11
+ * carries no timing, ended without a recorded completion (the duration is
12
+ * unknown), or when no message part scope is available (so kit components
13
+ * stay renderable standalone, e.g. in docs previews).
14
+ *
15
+ * @example
16
+ * ```tsx
17
+ * function ToolDuration() {
18
+ * const elapsedMs = useToolCallElapsed();
19
+ * if (elapsedMs === undefined) return null;
20
+ * return <span>{(elapsedMs / 1000).toFixed(1)}s</span>;
21
+ * }
22
+ * ```
23
+ */
24
+ export const useToolCallElapsed = (): number | undefined => {
25
+ const aui = useAui();
26
+ const hasPart = aui.part.source !== null;
27
+ const timing = useAuiState((s) =>
28
+ hasPart && s.part.type === "tool-call" ? s.part.timing : undefined,
29
+ );
30
+ const partRunning = useAuiState(
31
+ (s) =>
32
+ hasPart &&
33
+ s.part.type === "tool-call" &&
34
+ s.part.status.type === "running",
35
+ );
36
+ const running =
37
+ timing !== undefined && timing.completedAt === undefined && partRunning;
38
+ const [now, setNow] = useState(() => Date.now());
39
+
40
+ useEffect(() => {
41
+ if (!running) return undefined;
42
+ setNow(Date.now());
43
+ const id = setInterval(() => setNow(Date.now()), 1000);
44
+ return () => clearInterval(id);
45
+ }, [running]);
46
+
47
+ if (timing === undefined) return undefined;
48
+ if (timing.completedAt !== undefined)
49
+ return Math.max(0, timing.completedAt - timing.startedAt);
50
+ if (!running) return undefined;
51
+ return Math.max(0, now - timing.startedAt);
52
+ };