@assistant-ui/react 0.14.27 → 0.14.29

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 (257) hide show
  1. package/dist/client/ExternalThread.d.ts +11 -1
  2. package/dist/client/ExternalThread.d.ts.map +1 -1
  3. package/dist/client/ExternalThread.js +758 -513
  4. package/dist/client/ExternalThread.js.map +1 -1
  5. package/dist/context/react/utils/createContextStoreHook.d.ts.map +1 -1
  6. package/dist/context/react/utils/createStateHookForRuntime.d.ts.map +1 -1
  7. package/dist/legacy-runtime/AssistantRuntimeProvider.js.map +1 -1
  8. package/dist/legacy-runtime/cloud/auiV0.d.ts +12 -1
  9. package/dist/legacy-runtime/cloud/auiV0.d.ts.map +1 -1
  10. package/dist/legacy-runtime/cloud/auiV0.js +2 -1
  11. package/dist/legacy-runtime/cloud/auiV0.js.map +1 -1
  12. package/dist/legacy-runtime/runtime-cores/assistant-transport/commandQueue.d.ts +1 -1
  13. package/dist/legacy-runtime/runtime-cores/assistant-transport/commandQueue.d.ts.map +1 -1
  14. package/dist/legacy-runtime/runtime-cores/assistant-transport/commandQueue.js +7 -6
  15. package/dist/legacy-runtime/runtime-cores/assistant-transport/commandQueue.js.map +1 -1
  16. package/dist/legacy-runtime/runtime-cores/assistant-transport/runManager.d.ts.map +1 -1
  17. package/dist/legacy-runtime/runtime-cores/assistant-transport/runManager.js +30 -15
  18. package/dist/legacy-runtime/runtime-cores/assistant-transport/runManager.js.map +1 -1
  19. package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.d.ts.map +1 -1
  20. package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.js +8 -4
  21. package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.js.map +1 -1
  22. package/dist/legacy-runtime/runtime-cores/assistant-transport/useConvertedState.js +1 -0
  23. package/dist/legacy-runtime/runtime-cores/assistant-transport/useConvertedState.js.map +1 -1
  24. package/dist/mcp-apps/McpAppRenderer.d.ts.map +1 -1
  25. package/dist/model-context/makeAssistantVisible.d.ts.map +1 -1
  26. package/dist/model-context/makeAssistantVisible.js +7 -1
  27. package/dist/model-context/makeAssistantVisible.js.map +1 -1
  28. package/dist/primitives/actionBar/ActionBarCopy.d.ts.map +1 -1
  29. package/dist/primitives/actionBar/ActionBarEdit.d.ts.map +1 -1
  30. package/dist/primitives/actionBar/ActionBarExportMarkdown.d.ts.map +1 -1
  31. package/dist/primitives/actionBar/ActionBarFeedbackNegative.d.ts.map +1 -1
  32. package/dist/primitives/actionBar/ActionBarFeedbackPositive.d.ts.map +1 -1
  33. package/dist/primitives/actionBar/ActionBarReload.d.ts.map +1 -1
  34. package/dist/primitives/actionBar/ActionBarRoot.d.ts.map +1 -1
  35. package/dist/primitives/actionBar/ActionBarRoot.js +3 -3
  36. package/dist/primitives/actionBar/ActionBarRoot.js.map +1 -1
  37. package/dist/primitives/actionBar/ActionBarSpeak.d.ts.map +1 -1
  38. package/dist/primitives/actionBar/ActionBarStopSpeaking.d.ts.map +1 -1
  39. package/dist/primitives/actionBar/useActionBarFloatStatus.d.ts +7 -6
  40. package/dist/primitives/actionBar/useActionBarFloatStatus.d.ts.map +1 -1
  41. package/dist/primitives/actionBar/useActionBarFloatStatus.js +10 -11
  42. package/dist/primitives/actionBar/useActionBarFloatStatus.js.map +1 -1
  43. package/dist/primitives/actionBarMore/ActionBarMoreContent.d.ts.map +1 -1
  44. package/dist/primitives/actionBarMore/ActionBarMoreContent.js.map +1 -1
  45. package/dist/primitives/actionBarMore/ActionBarMoreItem.d.ts.map +1 -1
  46. package/dist/primitives/actionBarMore/ActionBarMoreSeparator.d.ts.map +1 -1
  47. package/dist/primitives/actionBarMore/ActionBarMoreTrigger.d.ts.map +1 -1
  48. package/dist/primitives/assistantModal/AssistantModalAnchor.d.ts.map +1 -1
  49. package/dist/primitives/assistantModal/AssistantModalContent.d.ts.map +1 -1
  50. package/dist/primitives/assistantModal/AssistantModalContent.js.map +1 -1
  51. package/dist/primitives/assistantModal/AssistantModalTrigger.d.ts.map +1 -1
  52. package/dist/primitives/assistantModal/popoverRenderPrimitives.d.ts.map +1 -1
  53. package/dist/primitives/attachment/AttachmentRemove.d.ts.map +1 -1
  54. package/dist/primitives/attachment/AttachmentRemove.js +1 -0
  55. package/dist/primitives/attachment/AttachmentRemove.js.map +1 -1
  56. package/dist/primitives/attachment/AttachmentRoot.d.ts.map +1 -1
  57. package/dist/primitives/attachment/AttachmentThumb.d.ts.map +1 -1
  58. package/dist/primitives/attachment/AttachmentThumb.js.map +1 -1
  59. package/dist/primitives/branchPicker/BranchPickerNext.d.ts.map +1 -1
  60. package/dist/primitives/branchPicker/BranchPickerPrevious.d.ts.map +1 -1
  61. package/dist/primitives/branchPicker/BranchPickerRoot.d.ts.map +1 -1
  62. package/dist/primitives/branchPicker/BranchPickerRoot.js.map +1 -1
  63. package/dist/primitives/chainOfThought/ChainOfThoughtAccordionTrigger.d.ts.map +1 -1
  64. package/dist/primitives/chainOfThought/ChainOfThoughtAccordionTrigger.js +1 -0
  65. package/dist/primitives/chainOfThought/ChainOfThoughtAccordionTrigger.js.map +1 -1
  66. package/dist/primitives/chainOfThought/ChainOfThoughtRoot.d.ts.map +1 -1
  67. package/dist/primitives/composer/ComposerAddAttachment.d.ts.map +1 -1
  68. package/dist/primitives/composer/ComposerAddAttachment.js +8 -2
  69. package/dist/primitives/composer/ComposerAddAttachment.js.map +1 -1
  70. package/dist/primitives/composer/ComposerAttachmentDropzone.d.ts.map +1 -1
  71. package/dist/primitives/composer/ComposerAttachmentDropzone.js +1 -3
  72. package/dist/primitives/composer/ComposerAttachmentDropzone.js.map +1 -1
  73. package/dist/primitives/composer/ComposerCancel.d.ts.map +1 -1
  74. package/dist/primitives/composer/ComposerDictate.d.ts.map +1 -1
  75. package/dist/primitives/composer/ComposerDictationTranscript.d.ts.map +1 -1
  76. package/dist/primitives/composer/ComposerDictationTranscript.js.map +1 -1
  77. package/dist/primitives/composer/ComposerInput.js +6 -4
  78. package/dist/primitives/composer/ComposerInput.js.map +1 -1
  79. package/dist/primitives/composer/ComposerQuote.d.ts.map +1 -1
  80. package/dist/primitives/composer/ComposerQuote.js.map +1 -1
  81. package/dist/primitives/composer/ComposerRoot.d.ts +5 -0
  82. package/dist/primitives/composer/ComposerRoot.d.ts.map +1 -1
  83. package/dist/primitives/composer/ComposerRoot.js +86 -40
  84. package/dist/primitives/composer/ComposerRoot.js.map +1 -1
  85. package/dist/primitives/composer/ComposerSend.d.ts.map +1 -1
  86. package/dist/primitives/composer/ComposerSend.js +1 -0
  87. package/dist/primitives/composer/ComposerSend.js.map +1 -1
  88. package/dist/primitives/composer/ComposerStopDictation.d.ts.map +1 -1
  89. package/dist/primitives/composer/ComposerStopDictation.js +1 -0
  90. package/dist/primitives/composer/ComposerStopDictation.js.map +1 -1
  91. package/dist/primitives/composer/trigger/TriggerPopover.d.ts.map +1 -1
  92. package/dist/primitives/composer/trigger/TriggerPopover.js.map +1 -1
  93. package/dist/primitives/composer/trigger/TriggerPopoverBack.d.ts.map +1 -1
  94. package/dist/primitives/composer/trigger/TriggerPopoverCategories.d.ts.map +1 -1
  95. package/dist/primitives/composer/trigger/TriggerPopoverCategories.js.map +1 -1
  96. package/dist/primitives/composer/trigger/TriggerPopoverItems.d.ts.map +1 -1
  97. package/dist/primitives/composer/trigger/TriggerPopoverItems.js.map +1 -1
  98. package/dist/primitives/composer/trigger/TriggerPopoverResource.d.ts.map +1 -1
  99. package/dist/primitives/composer/trigger/TriggerPopoverRootContext.js.map +1 -1
  100. package/dist/primitives/composer/trigger/detectTrigger.d.ts +1 -1
  101. package/dist/primitives/composer/trigger/triggerDetectionResource.d.ts.map +1 -1
  102. package/dist/primitives/composer/trigger/triggerKeyboardResource.d.ts.map +1 -1
  103. package/dist/primitives/composer/trigger/triggerNavigationResource.d.ts.map +1 -1
  104. package/dist/primitives/composer/trigger/triggerSelectionResource.d.ts.map +1 -1
  105. package/dist/primitives/dropdownMenuRenderPrimitives.d.ts.map +1 -1
  106. package/dist/primitives/error/ErrorMessage.d.ts.map +1 -1
  107. package/dist/primitives/error/ErrorMessage.js.map +1 -1
  108. package/dist/primitives/error/ErrorRoot.d.ts.map +1 -1
  109. package/dist/primitives/message/MessageParts.js.map +1 -1
  110. package/dist/primitives/message/MessagePartsGrouped.js.map +1 -1
  111. package/dist/primitives/message/MessageRoot.d.ts.map +1 -1
  112. package/dist/primitives/messagePart/MessagePartImage.d.ts.map +1 -1
  113. package/dist/primitives/messagePart/MessagePartText.d.ts.map +1 -1
  114. package/dist/primitives/messagePart/MessagePartText.js.map +1 -1
  115. package/dist/primitives/messagePart/useMessagePartData.d.ts +3 -2
  116. package/dist/primitives/messagePart/useMessagePartData.d.ts.map +1 -1
  117. package/dist/primitives/messagePart/useMessagePartData.js +3 -2
  118. package/dist/primitives/messagePart/useMessagePartData.js.map +1 -1
  119. package/dist/primitives/messagePart/useMessagePartFile.d.ts +5 -4
  120. package/dist/primitives/messagePart/useMessagePartFile.d.ts.map +1 -1
  121. package/dist/primitives/messagePart/useMessagePartFile.js +11 -3
  122. package/dist/primitives/messagePart/useMessagePartFile.js.map +1 -1
  123. package/dist/primitives/messagePart/useMessagePartImage.d.ts +5 -4
  124. package/dist/primitives/messagePart/useMessagePartImage.d.ts.map +1 -1
  125. package/dist/primitives/messagePart/useMessagePartImage.js +10 -3
  126. package/dist/primitives/messagePart/useMessagePartImage.js.map +1 -1
  127. package/dist/primitives/messagePart/useMessagePartReasoning.d.ts +5 -4
  128. package/dist/primitives/messagePart/useMessagePartReasoning.d.ts.map +1 -1
  129. package/dist/primitives/messagePart/useMessagePartReasoning.js +10 -3
  130. package/dist/primitives/messagePart/useMessagePartReasoning.js.map +1 -1
  131. package/dist/primitives/messagePart/useMessagePartSource.d.ts +6 -4
  132. package/dist/primitives/messagePart/useMessagePartSource.d.ts.map +1 -1
  133. package/dist/primitives/messagePart/useMessagePartSource.js +12 -3
  134. package/dist/primitives/messagePart/useMessagePartSource.js.map +1 -1
  135. package/dist/primitives/messagePart/useMessagePartText.d.ts +6 -5
  136. package/dist/primitives/messagePart/useMessagePartText.d.ts.map +1 -1
  137. package/dist/primitives/messagePart/useMessagePartText.js +10 -3
  138. package/dist/primitives/messagePart/useMessagePartText.js.map +1 -1
  139. package/dist/primitives/queueItem/QueueItemRemove.d.ts.map +1 -1
  140. package/dist/primitives/queueItem/QueueItemRemove.js +1 -0
  141. package/dist/primitives/queueItem/QueueItemRemove.js.map +1 -1
  142. package/dist/primitives/queueItem/QueueItemSteer.d.ts.map +1 -1
  143. package/dist/primitives/queueItem/QueueItemSteer.js +1 -0
  144. package/dist/primitives/queueItem/QueueItemSteer.js.map +1 -1
  145. package/dist/primitives/queueItem/QueueItemText.d.ts.map +1 -1
  146. package/dist/primitives/queueItem/QueueItemText.js.map +1 -1
  147. package/dist/primitives/selectionToolbar/SelectionToolbarQuote.d.ts.map +1 -1
  148. package/dist/primitives/selectionToolbar/SelectionToolbarRoot.d.ts.map +1 -1
  149. package/dist/primitives/selectionToolbar/SelectionToolbarRoot.js.map +1 -1
  150. package/dist/primitives/suggestion/SuggestionDescription.d.ts.map +1 -1
  151. package/dist/primitives/suggestion/SuggestionDescription.js.map +1 -1
  152. package/dist/primitives/suggestion/SuggestionTitle.d.ts.map +1 -1
  153. package/dist/primitives/suggestion/SuggestionTitle.js.map +1 -1
  154. package/dist/primitives/suggestion/SuggestionTrigger.d.ts.map +1 -1
  155. package/dist/primitives/suggestion/SuggestionTrigger.js +1 -0
  156. package/dist/primitives/suggestion/SuggestionTrigger.js.map +1 -1
  157. package/dist/primitives/thread/ThreadRoot.d.ts.map +1 -1
  158. package/dist/primitives/thread/ThreadScrollToBottom.d.ts.map +1 -1
  159. package/dist/primitives/thread/ThreadScrollToBottom.js +1 -0
  160. package/dist/primitives/thread/ThreadScrollToBottom.js.map +1 -1
  161. package/dist/primitives/thread/ThreadSuggestion.d.ts.map +1 -1
  162. package/dist/primitives/thread/ThreadViewport.d.ts.map +1 -1
  163. package/dist/primitives/thread/ThreadViewport.js +53 -19
  164. package/dist/primitives/thread/ThreadViewport.js.map +1 -1
  165. package/dist/primitives/thread/ThreadViewportFooter.d.ts.map +1 -1
  166. package/dist/primitives/thread/topAnchor/createReserveObservers.d.ts.map +1 -1
  167. package/dist/primitives/thread/topAnchor/mountTopAnchorReserve.d.ts +4 -0
  168. package/dist/primitives/thread/topAnchor/mountTopAnchorReserve.d.ts.map +1 -1
  169. package/dist/primitives/thread/topAnchor/mountTopAnchorReserve.js +14 -1
  170. package/dist/primitives/thread/topAnchor/mountTopAnchorReserve.js.map +1 -1
  171. package/dist/primitives/thread/topAnchor/topAnchorTurn.d.ts +13 -1
  172. package/dist/primitives/thread/topAnchor/topAnchorTurn.d.ts.map +1 -1
  173. package/dist/primitives/thread/topAnchor/topAnchorTurn.js +14 -1
  174. package/dist/primitives/thread/topAnchor/topAnchorTurn.js.map +1 -1
  175. package/dist/primitives/thread/useThreadViewportAutoScroll.js +2 -1
  176. package/dist/primitives/thread/useThreadViewportAutoScroll.js.map +1 -1
  177. package/dist/primitives/threadList/ThreadListLoadMore.d.ts.map +1 -1
  178. package/dist/primitives/threadList/ThreadListNew.d.ts.map +1 -1
  179. package/dist/primitives/threadList/ThreadListRoot.d.ts.map +1 -1
  180. package/dist/primitives/threadList/ThreadListRoot.js.map +1 -1
  181. package/dist/primitives/threadListFocusGroup.d.ts.map +1 -1
  182. package/dist/primitives/threadListItem/ThreadListItemArchive.d.ts.map +1 -1
  183. package/dist/primitives/threadListItem/ThreadListItemDelete.d.ts.map +1 -1
  184. package/dist/primitives/threadListItem/ThreadListItemRoot.d.ts.map +1 -1
  185. package/dist/primitives/threadListItem/ThreadListItemRoot.js.map +1 -1
  186. package/dist/primitives/threadListItem/ThreadListItemTrigger.d.ts.map +1 -1
  187. package/dist/primitives/threadListItem/ThreadListItemTrigger.js.map +1 -1
  188. package/dist/primitives/threadListItem/ThreadListItemUnarchive.d.ts.map +1 -1
  189. package/dist/primitives/threadListItemMore/ThreadListItemMoreContent.d.ts.map +1 -1
  190. package/dist/primitives/threadListItemMore/ThreadListItemMoreContent.js.map +1 -1
  191. package/dist/primitives/threadListItemMore/ThreadListItemMoreItem.d.ts.map +1 -1
  192. package/dist/primitives/threadListItemMore/ThreadListItemMoreRoot.js.map +1 -1
  193. package/dist/primitives/threadListItemMore/ThreadListItemMoreSeparator.d.ts.map +1 -1
  194. package/dist/primitives/threadListItemMore/ThreadListItemMoreTrigger.d.ts.map +1 -1
  195. package/dist/tests/setup.d.ts +1 -1
  196. package/dist/utils/Primitive.d.ts.map +1 -1
  197. package/dist/utils/Primitive.js.map +1 -1
  198. package/dist/utils/createActionButton.d.ts.map +1 -1
  199. package/dist/utils/hooks/useOnResizeContent.js +1 -0
  200. package/dist/utils/hooks/useOnResizeContent.js.map +1 -1
  201. package/dist/utils/hooks/useSizeHandle.js +1 -0
  202. package/dist/utils/hooks/useSizeHandle.js.map +1 -1
  203. package/dist/utils/smooth/useSmooth.d.ts.map +1 -1
  204. package/dist/utils/smooth/useSmooth.js +2 -2
  205. package/dist/utils/smooth/useSmooth.js.map +1 -1
  206. package/package.json +20 -20
  207. package/src/client/ExternalThread.ts +341 -53
  208. package/src/legacy-runtime/cloud/auiV0.ts +14 -0
  209. package/src/legacy-runtime/runtime-cores/assistant-transport/commandQueue.ts +4 -1
  210. package/src/legacy-runtime/runtime-cores/assistant-transport/runManager.ts +37 -10
  211. package/src/legacy-runtime/runtime-cores/assistant-transport/transport-delivery.test.tsx +79 -0
  212. package/src/legacy-runtime/runtime-cores/assistant-transport/transport-parentid.test.tsx +97 -0
  213. package/src/legacy-runtime/runtime-cores/assistant-transport/transport-scheduling.test.ts +113 -2
  214. package/src/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransport.spec.md +10 -2
  215. package/src/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.test.tsx +189 -0
  216. package/src/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.ts +20 -4
  217. package/src/model-context/makeAssistantVisible.test.tsx +146 -0
  218. package/src/model-context/makeAssistantVisible.tsx +18 -1
  219. package/src/primitives/actionBar/useActionBarFloatStatus.ts +7 -5
  220. package/src/primitives/composer/ComposerAddAttachment.test.tsx +108 -0
  221. package/src/primitives/composer/ComposerAddAttachment.ts +10 -4
  222. package/src/primitives/composer/ComposerAttachmentDropzone.test.tsx +3 -2
  223. package/src/primitives/composer/ComposerAttachmentDropzone.tsx +3 -2
  224. package/src/primitives/composer/ComposerInput.test.tsx +98 -1
  225. package/src/primitives/composer/ComposerInput.tsx +10 -8
  226. package/src/primitives/composer/ComposerRoot.test.tsx +226 -5
  227. package/src/primitives/composer/ComposerRoot.tsx +50 -1
  228. package/src/primitives/messagePart/useMessagePartData.ts +3 -2
  229. package/src/primitives/messagePart/useMessagePartFile.ts +19 -7
  230. package/src/primitives/messagePart/useMessagePartImage.ts +18 -7
  231. package/src/primitives/messagePart/useMessagePartReasoning.ts +15 -6
  232. package/src/primitives/messagePart/useMessagePartSource.ts +20 -7
  233. package/src/primitives/messagePart/useMessagePartText.ts +15 -5
  234. package/src/primitives/thread/ThreadViewport.tsx +15 -0
  235. package/src/primitives/thread/topAnchor/mountTopAnchorReserve.test.ts +243 -0
  236. package/src/primitives/thread/topAnchor/mountTopAnchorReserve.ts +28 -7
  237. package/src/primitives/thread/topAnchor/topAnchorTurn.test.ts +69 -0
  238. package/src/primitives/thread/topAnchor/topAnchorTurn.ts +29 -0
  239. package/src/primitives/thread/useThreadViewportAutoScroll.test.tsx +86 -1
  240. package/src/primitives/thread/useThreadViewportAutoScroll.ts +6 -1
  241. package/src/tests/DataRenderers.test.tsx +212 -0
  242. package/src/tests/RemoteThreadListRuntime.adapterProvider.test.tsx +1 -11
  243. package/src/tests/external-thread-attachments.test.tsx +372 -0
  244. package/src/tests/external-thread-parity.test.tsx +232 -0
  245. package/src/tests/generative-ui.test.tsx +1 -46
  246. package/src/tests/messagePartSwitchRace.test.tsx +172 -0
  247. package/src/tests/messagePartTypeMismatch.test.tsx +146 -0
  248. package/src/tests/threadMessageById.test.tsx +0 -7
  249. package/src/utils/smooth/useSmooth.ts +7 -4
  250. package/src/tests/BaseComposerRuntimeCore.test.ts +0 -612
  251. package/src/tests/BaseSubscribable.test.ts +0 -54
  252. package/src/tests/DataRenderers.test.ts +0 -355
  253. package/src/tests/ExternalStoreThreadRuntimeCore.test.ts +0 -685
  254. package/src/tests/LocalThreadRuntimeCore.test.ts +0 -324
  255. package/src/tests/auiV0Encode.test.ts +0 -188
  256. package/src/tests/external-message-converter.test.ts +0 -346
  257. package/src/tests/shouldContinue.test.ts +0 -122
@@ -1,4 +1,11 @@
1
- import { useState, useMemo, useEffect, useEffectEvent } from "react";
1
+ import {
2
+ useState,
3
+ useMemo,
4
+ useEffect,
5
+ useEffectEvent,
6
+ useCallback,
7
+ useRef,
8
+ } from "react";
2
9
  import { resource, withKey } from "@assistant-ui/tap";
3
10
  import {
4
11
  type ClientElement,
@@ -10,20 +17,30 @@ import {
10
17
  } from "@assistant-ui/store";
11
18
 
12
19
  import type {
20
+ AddToolResultOptions,
13
21
  AppendMessage,
14
22
  Attachment,
23
+ AttachmentAdapter,
15
24
  CreateAttachment,
25
+ PendingAttachment,
16
26
  RespondToToolApprovalOptions,
27
+ ResumeToolCallOptions,
28
+ MessagePartStatus,
17
29
  ThreadAssistantMessagePart,
18
30
  ThreadUserMessagePart,
19
31
  ThreadMessage,
32
+ ToolCallMessagePartStatus,
20
33
  ExternalThreadQueueAdapter,
21
34
  ExternalThreadBranchAdapter,
22
35
  } from "@assistant-ui/core";
36
+ import { ToolResponse } from "assistant-stream";
37
+ import type { ReadonlyJSONValue } from "assistant-stream/utils";
23
38
  import type { QueueItemState } from "@assistant-ui/core/store";
24
39
  import type { ComposerSendOptions } from "@assistant-ui/core/store";
25
40
  import {
41
+ fileMatchesAccept,
26
42
  getThreadMessageText,
43
+ isCreateAttachment,
27
44
  resolveToolApprovalResponse,
28
45
  } from "@assistant-ui/core/internal";
29
46
  import { ModelContext, Suggestions } from "@assistant-ui/core/store";
@@ -37,9 +54,35 @@ export type ExternalThreadMessage = ThreadMessage & {
37
54
  id: string;
38
55
  };
39
56
 
57
+ const COMPLETE_STATUS: MessagePartStatus = Object.freeze({
58
+ type: "complete",
59
+ });
60
+
61
+ // Legacy runtime parity (toMessagePartStatus): a tool call without a result
62
+ // carries its message's status; the last part of a streaming message streams.
63
+ const derivePartStatus = (
64
+ message: ExternalThreadMessage,
65
+ partIndex: number,
66
+ part: ThreadAssistantMessagePart | ThreadUserMessagePart,
67
+ ): ToolCallMessagePartStatus => {
68
+ if (message.role !== "assistant" || !message.status) return COMPLETE_STATUS;
69
+
70
+ if (part.type === "tool-call") {
71
+ if (!part.result) return message.status as ToolCallMessagePartStatus;
72
+ return COMPLETE_STATUS;
73
+ }
74
+
75
+ const isLastPart = partIndex === Math.max(0, message.content.length - 1);
76
+ if (message.status.type === "requires-action") return COMPLETE_STATUS;
77
+ return isLastPart ? (message.status as MessagePartStatus) : COMPLETE_STATUS;
78
+ };
79
+
40
80
  export type ExternalThreadProps = {
41
81
  messages: readonly ExternalThreadMessage[];
42
82
  isRunning?: boolean;
83
+ isLoading?: boolean | undefined;
84
+ state?: ReadonlyJSONValue | undefined;
85
+ extras?: unknown;
43
86
  /**
44
87
  * Whether sending new messages is currently disabled. When `true`, the
45
88
  * thread composer's input remains usable but `send()` is a no-op and
@@ -56,6 +99,12 @@ export type ExternalThreadProps = {
56
99
  onReload?: (parentId: string | null) => void;
57
100
  onStartRun?: () => void;
58
101
  onCancel?: () => void;
102
+ onResume?: (() => void) | undefined;
103
+ onAddToolResult?: ((options: AddToolResultOptions) => void) | undefined;
104
+ /** Callback for resuming a tool call that is waiting for human input. */
105
+ onResumeToolCall?: ((options: ResumeToolCallOptions) => void) | undefined;
106
+ onLoadExternalState?: ((state: unknown) => void) | undefined;
107
+ attachmentAdapter?: AttachmentAdapter | undefined;
59
108
  /** Queue adapter for runtimes that support message queuing and steering. */
60
109
  queue?: ExternalThreadQueueAdapter;
61
110
  /** Branch adapter for runtimes that track sibling variants of messages. */
@@ -67,6 +116,7 @@ export type ExternalThreadProps = {
67
116
  type MessageClientProps = {
68
117
  message: ExternalThreadMessage;
69
118
  index: number;
119
+ parentId: string | null;
70
120
  onEdit?: (message: AppendMessage) => void;
71
121
  onReload?: () => void;
72
122
  queue?: ExternalThreadQueueAdapter | undefined;
@@ -74,17 +124,24 @@ type MessageClientProps = {
74
124
  onRespondToToolApproval?:
75
125
  | ((options: RespondToToolApprovalOptions) => void)
76
126
  | undefined;
127
+ onAddToolResult?: ((options: AddToolResultOptions) => void) | undefined;
128
+ onResumeToolCall?: ((options: ResumeToolCallOptions) => void) | undefined;
129
+ attachmentAdapter?: AttachmentAdapter | undefined;
77
130
  };
78
131
 
79
132
  // Message Client - minimal implementation
80
133
  const useMessageClient = ({
81
134
  message,
82
135
  index,
136
+ parentId,
83
137
  onEdit,
84
138
  onReload,
85
139
  queue,
86
140
  branches,
87
141
  onRespondToToolApproval,
142
+ onAddToolResult,
143
+ onResumeToolCall,
144
+ attachmentAdapter,
88
145
  }: MessageClientProps): ClientOutput<"message"> => {
89
146
  const [isCopied, setIsCopied] = useState(false);
90
147
  const [isHovering, setIsHovering] = useState(false);
@@ -92,7 +149,17 @@ const useMessageClient = ({
92
149
 
93
150
  const partClients = useClientLookup(
94
151
  message.content.map((part, idx) =>
95
- withKey(idx, PartResource({ part, onRespondToToolApproval })),
152
+ withKey(
153
+ idx,
154
+ PartResource({
155
+ part,
156
+ status: derivePartStatus(message, idx, part),
157
+ messageId: message.id,
158
+ onRespondToToolApproval,
159
+ onAddToolResult,
160
+ onResumeToolCall,
161
+ }),
162
+ ),
96
163
  ),
97
164
  );
98
165
 
@@ -120,7 +187,7 @@ const useMessageClient = ({
120
187
  queue?.clear("edit");
121
188
  onEdit?.({
122
189
  ...msg,
123
- parentId: message.id,
190
+ parentId,
124
191
  sourceId: message.id,
125
192
  });
126
193
  setIsEditing(false);
@@ -136,6 +203,7 @@ const useMessageClient = ({
136
203
  onSend: handleSendEdit,
137
204
  message,
138
205
  queue,
206
+ attachmentAdapter,
139
207
  }),
140
208
  );
141
209
 
@@ -148,7 +216,7 @@ const useMessageClient = ({
148
216
  return {
149
217
  ...message,
150
218
  attachments: message.attachments ?? [],
151
- parentId: null,
219
+ parentId,
152
220
  isLast: false, // Will be set by thread
153
221
  branchNumber,
154
222
  branchCount,
@@ -161,6 +229,7 @@ const useMessageClient = ({
161
229
  };
162
230
  }, [
163
231
  message,
232
+ parentId,
164
233
  isCopied,
165
234
  isHovering,
166
235
  index,
@@ -219,28 +288,65 @@ const MessageClient = resource(useMessageClient);
219
288
 
220
289
  type PartResourceProps = {
221
290
  part: ThreadAssistantMessagePart | ThreadUserMessagePart;
291
+ status: ToolCallMessagePartStatus;
292
+ messageId: string;
222
293
  onRespondToToolApproval?:
223
294
  | ((options: RespondToToolApprovalOptions) => void)
224
295
  | undefined;
296
+ onAddToolResult?: ((options: AddToolResultOptions) => void) | undefined;
297
+ onResumeToolCall?: ((options: ResumeToolCallOptions) => void) | undefined;
225
298
  };
226
299
 
227
300
  // Part Client - minimal implementation
228
301
  const usePartResource = ({
229
302
  part,
303
+ status,
304
+ messageId,
230
305
  onRespondToToolApproval,
306
+ onAddToolResult,
307
+ onResumeToolCall,
231
308
  }: PartResourceProps): ClientOutput<"part"> => {
232
309
  const state = useMemo(
233
310
  () => ({
234
311
  ...part,
235
- status: { type: "complete" as const },
312
+ status,
236
313
  }),
237
- [part],
314
+ [part, status],
238
315
  );
239
316
 
240
317
  return {
241
318
  getState: () => state,
242
- addToolResult: () => {},
243
- resumeToolCall: () => {},
319
+ addToolResult: (result) => {
320
+ if (!onAddToolResult)
321
+ throw new Error(
322
+ "Runtime does not support tool results (onAddToolResult is not set).",
323
+ );
324
+ if (part.type !== "tool-call")
325
+ throw new Error("Tried to add tool result on non-tool message part");
326
+
327
+ const response = ToolResponse.toResponse(result);
328
+ onAddToolResult({
329
+ messageId,
330
+ toolName: part.toolName,
331
+ toolCallId: part.toolCallId,
332
+ result: response.result as ReadonlyJSONValue,
333
+ isError: response.isError,
334
+ ...(response.artifact !== undefined && { artifact: response.artifact }),
335
+ ...(response.modelContent !== undefined && {
336
+ modelContent: response.modelContent,
337
+ }),
338
+ });
339
+ },
340
+ resumeToolCall: (payload) => {
341
+ if (!onResumeToolCall)
342
+ throw new Error(
343
+ "Runtime does not support resuming tool calls (onResumeToolCall is not set).",
344
+ );
345
+ if (part.type !== "tool-call")
346
+ throw new Error("Tried to resume tool call on non-tool message part");
347
+
348
+ onResumeToolCall({ toolCallId: part.toolCallId, payload });
349
+ },
244
350
  respondToToolApproval: (response) => {
245
351
  if (!onRespondToToolApproval)
246
352
  throw new Error("Runtime does not support tool approvals.");
@@ -268,7 +374,7 @@ const PartResource = resource(usePartResource);
268
374
 
269
375
  type AttachmentResourceProps = {
270
376
  attachment: Attachment;
271
- onRemove?: () => void;
377
+ onRemove?: () => void | Promise<void>;
272
378
  };
273
379
 
274
380
  // Attachment Client - minimal implementation
@@ -279,7 +385,7 @@ const useAttachmentResource = ({
279
385
  return {
280
386
  getState: () => attachment,
281
387
  remove: async () => {
282
- onRemove?.();
388
+ await onRemove?.();
283
389
  },
284
390
  };
285
391
  };
@@ -296,6 +402,7 @@ type ComposerClientResourceProps = {
296
402
  onSend?: (message: AppendMessage) => void;
297
403
  message?: ExternalThreadMessage;
298
404
  queue?: ExternalThreadQueueAdapter | undefined;
405
+ attachmentAdapter?: AttachmentAdapter | undefined;
299
406
  };
300
407
 
301
408
  const useQueueItemClient = ({
@@ -316,6 +423,33 @@ const useQueueItemClient = ({
316
423
 
317
424
  const QueueItemClient = resource(useQueueItemClient);
318
425
 
426
+ const drainAdapterAdd = async (
427
+ result: ReturnType<AttachmentAdapter["add"]>,
428
+ upsert: (attachment: Attachment) => void,
429
+ ) => {
430
+ if (Symbol.asyncIterator in result) {
431
+ for await (const attachment of result) {
432
+ upsert(attachment);
433
+ }
434
+ } else {
435
+ upsert(await result);
436
+ }
437
+ };
438
+
439
+ // State whose setter tracks the latest value in a ref, so imperative
440
+ // call sequences (setText immediately followed by send) observe the write
441
+ // before React re-renders — legacy composer parity.
442
+ const useLiveState = <T>(initial: T) => {
443
+ const [state, setState] = useState(initial);
444
+ const ref = useRef(state);
445
+ const set = useCallback((next: T | ((prev: T) => T)) => {
446
+ ref.current =
447
+ typeof next === "function" ? (next as (prev: T) => T)(ref.current) : next;
448
+ setState(ref.current);
449
+ }, []);
450
+ return [state, set, ref] as const;
451
+ };
452
+
319
453
  // Composer Client - minimal implementation
320
454
  const useComposerClientResource = ({
321
455
  type,
@@ -327,12 +461,19 @@ const useComposerClientResource = ({
327
461
  onSend,
328
462
  message,
329
463
  queue,
464
+ attachmentAdapter,
330
465
  }: ComposerClientResourceProps): ClientOutput<"composer"> => {
331
- const [text, setText] = useState("");
332
- const [role, setRole] = useState<"user" | "assistant" | "system">("user");
333
- const [runConfig, setRunConfig] = useState<Record<string, unknown>>({});
334
- const [attachments, setAttachments] = useState<readonly Attachment[]>([]);
335
- const [quote, setQuote] = useState<
466
+ const [text, setText, textRef] = useLiveState("");
467
+ const [role, setRole, roleRef] = useLiveState<
468
+ "user" | "assistant" | "system"
469
+ >("user");
470
+ const [runConfig, setRunConfig, runConfigRef] = useLiveState<
471
+ Record<string, unknown>
472
+ >({});
473
+ const [attachments, setAttachments, attachmentsRef] = useLiveState<
474
+ readonly Attachment[]
475
+ >([]);
476
+ const [quote, setQuote, quoteRef] = useLiveState<
336
477
  { readonly text: string; readonly messageId: string } | undefined
337
478
  >(undefined);
338
479
 
@@ -358,19 +499,41 @@ const useComposerClientResource = ({
358
499
  }, [isEditing]);
359
500
 
360
501
  const attachmentClients = useClientLookup(
361
- attachments.map((attachment, idx) =>
502
+ attachments.map((attachment) =>
362
503
  withKey(
363
504
  attachment.id,
364
505
  AttachmentResource({
365
506
  attachment,
366
- onRemove: () => {
367
- setAttachments(attachments.filter((_, i) => i !== idx));
507
+ onRemove: async () => {
508
+ await attachmentAdapter?.remove(attachment);
509
+ setAttachments((prev) =>
510
+ prev.filter((a) => a.id !== attachment.id),
511
+ );
368
512
  },
369
513
  }),
370
514
  ),
371
515
  ),
372
516
  );
373
517
 
518
+ const removePendingAttachments = async (removed: readonly Attachment[]) => {
519
+ if (!attachmentAdapter) return;
520
+ await Promise.all(
521
+ removed
522
+ .filter((a) => a.status.type !== "complete")
523
+ .map((a) => attachmentAdapter.remove(a)),
524
+ );
525
+ };
526
+
527
+ const upsertAttachment = (attachment: Attachment) => {
528
+ setAttachments((prev) => {
529
+ const idx = prev.findIndex((a) => a.id === attachment.id);
530
+ if (idx === -1) return [...prev, attachment];
531
+ const next = [...prev];
532
+ next[idx] = attachment;
533
+ return next;
534
+ });
535
+ };
536
+
374
537
  const queueItems = queue?.items ?? EMPTY_QUEUE_ITEMS;
375
538
  const queueItemClients = useClientLookup(
376
539
  queueItems.map((item) =>
@@ -395,7 +558,7 @@ const useComposerClientResource = ({
395
558
  isEditing,
396
559
  canCancel,
397
560
  canSend: isEditing && !isEmpty && !isSendDisabled,
398
- attachmentAccept: "*",
561
+ attachmentAccept: attachmentAdapter?.accept ?? "*",
399
562
  isEmpty,
400
563
  type,
401
564
  dictation: undefined,
@@ -414,6 +577,7 @@ const useComposerClientResource = ({
414
577
  attachments.length,
415
578
  quote,
416
579
  queueItems,
580
+ attachmentAdapter?.accept,
417
581
  ]);
418
582
 
419
583
  return {
@@ -422,7 +586,24 @@ const useComposerClientResource = ({
422
586
  setRole,
423
587
  setRunConfig,
424
588
  addAttachment: async (fileOrAttachment: File | CreateAttachment) => {
425
- if (fileOrAttachment instanceof File) {
589
+ if (attachmentAdapter) {
590
+ const file = isCreateAttachment(fileOrAttachment)
591
+ ? {
592
+ name: fileOrAttachment.name,
593
+ type: fileOrAttachment.contentType ?? "",
594
+ }
595
+ : { name: fileOrAttachment.name, type: fileOrAttachment.type };
596
+ if (!fileMatchesAccept(file, attachmentAdapter.accept))
597
+ throw new Error(
598
+ `File type ${file.type || "unknown"} is not accepted. Accepted types: ${attachmentAdapter.accept}`,
599
+ );
600
+ }
601
+ if (!isCreateAttachment(fileOrAttachment) && attachmentAdapter) {
602
+ await drainAdapterAdd(
603
+ attachmentAdapter.add({ file: fileOrAttachment }),
604
+ upsertAttachment,
605
+ );
606
+ } else if (!isCreateAttachment(fileOrAttachment)) {
426
607
  const newAttachment: Attachment = {
427
608
  id: Math.random().toString(36).substring(7),
428
609
  type: "file",
@@ -432,7 +613,7 @@ const useComposerClientResource = ({
432
613
  status: { type: "complete" },
433
614
  content: [],
434
615
  };
435
- setAttachments([...attachments, newAttachment]);
616
+ setAttachments((prev) => [...prev, newAttachment]);
436
617
  } else {
437
618
  const newAttachment: Attachment = {
438
619
  id: fileOrAttachment.id ?? Math.random().toString(36).substring(7),
@@ -442,11 +623,13 @@ const useComposerClientResource = ({
442
623
  content: fileOrAttachment.content,
443
624
  status: { type: "complete" },
444
625
  };
445
- setAttachments([...attachments, newAttachment]);
626
+ setAttachments((prev) => [...prev, newAttachment]);
446
627
  }
447
628
  },
448
629
  clearAttachments: async () => {
630
+ const removed = attachmentsRef.current;
449
631
  setAttachments([]);
632
+ await removePendingAttachments(removed);
450
633
  },
451
634
  attachment: (selector) => {
452
635
  if ("id" in selector) {
@@ -455,37 +638,69 @@ const useComposerClientResource = ({
455
638
  return attachmentClients.get(selector);
456
639
  },
457
640
  reset: async () => {
641
+ const removed = attachmentsRef.current;
458
642
  setText("");
459
643
  setRole("user");
460
644
  setRunConfig({});
461
645
  setAttachments([]);
462
646
  setQuote(undefined);
647
+ await removePendingAttachments(removed);
463
648
  },
464
649
  send: (opts?: ComposerSendOptions) => {
465
- if (!state.canSend) return;
466
-
467
- const currentQuote = quote;
468
- const composedMessage: AppendMessage = {
469
- role,
470
- content: text ? [{ type: "text" as const, text }] : [],
471
- attachments: attachments as any,
472
- createdAt: new Date(),
473
- parentId: null,
474
- sourceId: null,
475
- runConfig,
476
- startRun: opts?.startRun,
477
- metadata: {
478
- custom: { ...(currentQuote ? { quote: currentQuote } : {}) },
479
- },
480
- };
481
- if (queue) {
482
- queue.enqueue(composedMessage, { steer: opts?.steer ?? false });
483
- } else {
484
- onSend?.(composedMessage);
485
- }
650
+ const currentQuote = quoteRef.current;
651
+ const currentText = textRef.current;
652
+ const currentAttachments = attachmentsRef.current;
653
+ const isEmpty = !currentText.trim() && !currentAttachments.length;
654
+ if (!isEditing || isEmpty || isSendDisabled) return;
655
+
486
656
  setText("");
487
657
  setAttachments([]);
488
658
  setQuote(undefined);
659
+
660
+ const dispatch = (sendAttachments: readonly Attachment[]) => {
661
+ const composedMessage: AppendMessage = {
662
+ role: roleRef.current,
663
+ content: currentText
664
+ ? [{ type: "text" as const, text: currentText }]
665
+ : [],
666
+ attachments: sendAttachments as any,
667
+ createdAt: new Date(),
668
+ parentId: null,
669
+ sourceId: null,
670
+ runConfig: runConfigRef.current,
671
+ startRun: opts?.startRun,
672
+ metadata: {
673
+ custom: { ...(currentQuote ? { quote: currentQuote } : {}) },
674
+ },
675
+ };
676
+ if (queue) {
677
+ queue.enqueue(composedMessage, { steer: opts?.steer ?? false });
678
+ } else {
679
+ onSend?.(composedMessage);
680
+ }
681
+ };
682
+
683
+ if (attachmentAdapter && currentAttachments.length > 0) {
684
+ void Promise.all(
685
+ currentAttachments.map((attachment) =>
686
+ attachment.status.type === "complete"
687
+ ? attachment
688
+ : attachmentAdapter.send(attachment as PendingAttachment),
689
+ ),
690
+ ).then(dispatch, (error) => {
691
+ // Upload failed: merge the failed send back into the draft.
692
+ setText((prev) =>
693
+ currentText && prev
694
+ ? currentText + "\n" + prev
695
+ : currentText || prev,
696
+ );
697
+ setQuote((prev) => prev ?? currentQuote);
698
+ setAttachments((prev) => [...currentAttachments, ...prev]);
699
+ console.error("Failed to send attachments", error);
700
+ });
701
+ } else {
702
+ dispatch(currentAttachments);
703
+ }
489
704
  },
490
705
  cancel: onCancel,
491
706
  beginEdit: () => {
@@ -502,20 +717,50 @@ const useComposerClientResource = ({
502
717
 
503
718
  const ComposerClientResource = resource(useComposerClientResource);
504
719
 
720
+ const dedupeMessagesById = (messages: readonly ExternalThreadMessage[]) => {
721
+ const seenIds = new Set<string>();
722
+ const deduped: ExternalThreadMessage[] = [];
723
+ for (let i = messages.length - 1; i >= 0; i--) {
724
+ const message = messages[i]!;
725
+ if (seenIds.has(message.id)) {
726
+ console.warn(
727
+ `ExternalThread: duplicate message id "${message.id}" in the provided messages array; keeping the last occurrence.`,
728
+ );
729
+ continue;
730
+ }
731
+ seenIds.add(message.id);
732
+ deduped.push(message);
733
+ }
734
+ return deduped.length === messages.length ? messages : deduped.reverse();
735
+ };
736
+
505
737
  // External Thread Client
506
738
  const useExternalThread = ({
507
- messages,
739
+ messages: messagesProp,
508
740
  isRunning = false,
741
+ isLoading = false,
742
+ state: threadState,
743
+ extras,
509
744
  isSendDisabled = false,
510
745
  onNew,
511
746
  onEdit,
512
747
  onReload,
513
748
  onStartRun,
514
749
  onCancel,
750
+ onResume,
751
+ onAddToolResult,
752
+ onResumeToolCall,
753
+ onLoadExternalState,
754
+ attachmentAdapter,
515
755
  queue,
516
756
  branches,
517
757
  onRespondToToolApproval,
518
758
  }: ExternalThreadProps): ClientOutput<"thread"> => {
759
+ const messages = useMemo(
760
+ () => dedupeMessagesById(messagesProp),
761
+ [messagesProp],
762
+ );
763
+
519
764
  const handleReload = (messageId: string) => {
520
765
  const messageIndex = messages.findIndex((m) => m.id === messageId);
521
766
  if (messageIndex === -1) return;
@@ -530,10 +775,14 @@ const useExternalThread = ({
530
775
  const props: MessageClientProps = {
531
776
  message: msg,
532
777
  index,
778
+ parentId: index > 0 ? messages[index - 1]!.id : null,
533
779
  onReload: () => handleReload(msg.id),
534
780
  queue,
535
781
  branches,
536
782
  onRespondToToolApproval,
783
+ onAddToolResult,
784
+ onResumeToolCall,
785
+ attachmentAdapter,
537
786
  };
538
787
  if (onEdit) props.onEdit = onEdit;
539
788
  return withKey(msg.id, MessageClient(props));
@@ -546,9 +795,25 @@ const useExternalThread = ({
546
795
  };
547
796
 
548
797
  const handleSendNew = (message: AppendMessage) => {
549
- onNew?.(message);
798
+ // The composer does not know the thread; stamp the current head as the
799
+ // parent (legacy composer parity).
800
+ onNew?.({ ...message, parentId: messages.at(-1)?.id ?? null });
550
801
  };
551
802
 
803
+ const headId = messages.at(-1)?.id ?? null;
804
+ const composerQueue = useMemo(
805
+ (): ExternalThreadQueueAdapter | undefined =>
806
+ queue && {
807
+ ...queue,
808
+ enqueue: (message, options) =>
809
+ queue.enqueue(
810
+ { ...message, parentId: message.parentId ?? headId },
811
+ options,
812
+ ),
813
+ },
814
+ [queue, headId],
815
+ );
816
+
552
817
  const composerClient = useClientResource(
553
818
  ComposerClientResource({
554
819
  type: "thread",
@@ -557,12 +822,16 @@ const useExternalThread = ({
557
822
  isSendDisabled,
558
823
  onCancel: handleCancelRun,
559
824
  onSend: handleSendNew,
560
- queue,
825
+ queue: composerQueue,
826
+ attachmentAdapter,
561
827
  }),
562
828
  );
563
829
 
564
830
  const hasQueue = !!queue;
565
831
  const hasBranches = !!branches;
832
+ const hasEdit = !!onEdit;
833
+ const hasReload = !!onReload;
834
+ const hasAttachments = !!attachmentAdapter;
566
835
  const state = useMemo(() => {
567
836
  const messageStates = messageClients.state.map((s, idx, arr) => ({
568
837
  ...s,
@@ -572,15 +841,15 @@ const useExternalThread = ({
572
841
  return {
573
842
  isEmpty: messages.length === 0,
574
843
  isDisabled: false,
575
- isLoading: false,
844
+ isLoading,
576
845
  isRunning,
577
846
  capabilities: {
578
- edit: false,
847
+ edit: hasEdit,
579
848
  delete: false,
580
- reload: false,
849
+ reload: hasReload,
581
850
  cancel: isRunning,
582
851
  speech: false,
583
- attachments: false,
852
+ attachments: hasAttachments,
584
853
  feedback: false,
585
854
  voice: false,
586
855
  switchToBranch: hasBranches,
@@ -590,9 +859,9 @@ const useExternalThread = ({
590
859
  queue: hasQueue,
591
860
  },
592
861
  messages: messageStates,
593
- state: {},
862
+ state: threadState ?? {},
594
863
  suggestions: [],
595
- extras: undefined,
864
+ extras,
596
865
  speech: undefined,
597
866
  voice: undefined,
598
867
  composer: composerClient.state,
@@ -600,8 +869,14 @@ const useExternalThread = ({
600
869
  }, [
601
870
  messages,
602
871
  isRunning,
872
+ isLoading,
873
+ threadState,
874
+ extras,
603
875
  hasQueue,
604
876
  hasBranches,
877
+ hasEdit,
878
+ hasReload,
879
+ hasAttachments,
605
880
  messageClients.state,
606
881
  composerClient.state,
607
882
  ]);
@@ -643,8 +918,21 @@ const useExternalThread = ({
643
918
  startRun: () => {
644
919
  onStartRun?.();
645
920
  },
646
- resumeRun: () => {},
921
+ resumeRun: () => {
922
+ if (!onResume)
923
+ throw new Error(
924
+ "Runtime does not support resuming runs (onResume is not set).",
925
+ );
926
+ onResume();
927
+ },
647
928
  cancelRun: handleCancelRun,
929
+ importExternalState: (state: unknown) => {
930
+ if (!onLoadExternalState)
931
+ throw new Error(
932
+ "Runtime does not support importing external states (onLoadExternalState is not set).",
933
+ );
934
+ onLoadExternalState(state);
935
+ },
648
936
  getModelContext: () => ({ tools: {}, config: {} }),
649
937
  export: () => ({ messages: [] }),
650
938
  import: () => {},