@assistant-ui/core 0.3.20 → 0.3.21

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 (905) hide show
  1. package/dist/adapters/attachment.d.ts +42 -34
  2. package/dist/adapters/attachment.d.ts.map +1 -1
  3. package/dist/adapters/attachment.js +51 -21
  4. package/dist/adapters/attachment.js.map +1 -1
  5. package/dist/adapters/directive-formatter.d.ts +1 -3
  6. package/dist/adapters/directive-formatter.d.ts.map +1 -1
  7. package/dist/adapters/feedback.d.ts +6 -7
  8. package/dist/adapters/feedback.d.ts.map +1 -1
  9. package/dist/adapters/index.d.ts +14 -9
  10. package/dist/adapters/index.d.ts.map +1 -0
  11. package/dist/adapters/index.js +1 -1
  12. package/dist/adapters/speech.d.ts +57 -58
  13. package/dist/adapters/speech.d.ts.map +1 -1
  14. package/dist/adapters/speech.js +16 -2
  15. package/dist/adapters/speech.js.map +1 -1
  16. package/dist/adapters/suggestion.d.ts +14 -16
  17. package/dist/adapters/suggestion.d.ts.map +1 -1
  18. package/dist/adapters/thread-history.d.ts +54 -51
  19. package/dist/adapters/thread-history.d.ts.map +1 -1
  20. package/dist/adapters/trigger.d.ts +7 -9
  21. package/dist/adapters/trigger.d.ts.map +1 -1
  22. package/dist/adapters/voice.d.ts +46 -40
  23. package/dist/adapters/voice.d.ts.map +1 -1
  24. package/dist/adapters/voice.js +17 -3
  25. package/dist/adapters/voice.js.map +1 -1
  26. package/dist/index.d.ts +70 -56
  27. package/dist/index.d.ts.map +1 -0
  28. package/dist/index.js +6 -6
  29. package/dist/index.js.map +1 -1
  30. package/dist/internal/duplicate-detection.d.ts +0 -2
  31. package/dist/internal/duplicate-detection.d.ts.map +1 -1
  32. package/dist/internal.d.ts +27 -58
  33. package/dist/internal.d.ts.map +1 -0
  34. package/dist/internal.js +8 -6
  35. package/dist/model-context/frame/host.d.ts +19 -21
  36. package/dist/model-context/frame/host.d.ts.map +1 -1
  37. package/dist/model-context/frame/host.js +3 -1
  38. package/dist/model-context/frame/host.js.map +1 -1
  39. package/dist/model-context/frame/provider.d.ts +27 -28
  40. package/dist/model-context/frame/provider.d.ts.map +1 -1
  41. package/dist/model-context/frame/provider.js +6 -0
  42. package/dist/model-context/frame/provider.js.map +1 -1
  43. package/dist/model-context/frame/types.d.ts +19 -21
  44. package/dist/model-context/frame/types.d.ts.map +1 -1
  45. package/dist/model-context/frame/validate.d.ts +3 -0
  46. package/dist/model-context/frame/validate.d.ts.map +1 -0
  47. package/dist/model-context/frame/validate.js +26 -0
  48. package/dist/model-context/frame/validate.js.map +1 -0
  49. package/dist/model-context/interactable-composer-metadata.d.ts +42 -29
  50. package/dist/model-context/interactable-composer-metadata.d.ts.map +1 -1
  51. package/dist/model-context/interactable-composer-metadata.js +30 -6
  52. package/dist/model-context/interactable-composer-metadata.js.map +1 -1
  53. package/dist/model-context/registry-handles.d.ts +6 -8
  54. package/dist/model-context/registry-handles.d.ts.map +1 -1
  55. package/dist/model-context/registry.d.ts +14 -16
  56. package/dist/model-context/registry.d.ts.map +1 -1
  57. package/dist/model-context/tool-call-text.d.ts +13 -14
  58. package/dist/model-context/tool-call-text.d.ts.map +1 -1
  59. package/dist/model-context/tool.d.ts +10 -11
  60. package/dist/model-context/tool.d.ts.map +1 -1
  61. package/dist/model-context/types.d.ts +32 -34
  62. package/dist/model-context/types.d.ts.map +1 -1
  63. package/dist/react/AssistantProvider.d.ts +6 -8
  64. package/dist/react/AssistantProvider.d.ts.map +1 -1
  65. package/dist/react/AssistantRuntimeProvider.d.ts +23 -25
  66. package/dist/react/AssistantRuntimeProvider.d.ts.map +1 -1
  67. package/dist/react/RuntimeAdapter.d.ts +2 -5
  68. package/dist/react/RuntimeAdapter.d.ts.map +1 -1
  69. package/dist/react/adapters/LocalStorageThreadListAdapter.d.ts +24 -18
  70. package/dist/react/adapters/LocalStorageThreadListAdapter.d.ts.map +1 -1
  71. package/dist/react/adapters/LocalStorageThreadListAdapter.js +58 -25
  72. package/dist/react/adapters/LocalStorageThreadListAdapter.js.map +1 -1
  73. package/dist/react/adapters/TitleGenerationAdapter.d.ts +2 -4
  74. package/dist/react/adapters/TitleGenerationAdapter.d.ts.map +1 -1
  75. package/dist/react/client/AdaptedRemoteThread.d.ts +5 -7
  76. package/dist/react/client/AdaptedRemoteThread.d.ts.map +1 -1
  77. package/dist/react/client/DataRenderers.d.ts +1 -3
  78. package/dist/react/client/DataRenderers.d.ts.map +1 -1
  79. package/dist/react/client/InMemoryThreadList.d.ts +6 -8
  80. package/dist/react/client/InMemoryThreadList.d.ts.map +1 -1
  81. package/dist/react/client/Interactables.d.ts +2 -4
  82. package/dist/react/client/Interactables.d.ts.map +1 -1
  83. package/dist/react/client/Interactables.js +306 -101
  84. package/dist/react/client/Interactables.js.map +1 -1
  85. package/dist/react/client/RemoteThreadList.d.ts +32 -34
  86. package/dist/react/client/RemoteThreadList.d.ts.map +1 -1
  87. package/dist/react/client/RemoteThreadList.js +2 -1
  88. package/dist/react/client/RemoteThreadList.js.map +1 -1
  89. package/dist/react/client/Tools.d.ts +9 -11
  90. package/dist/react/client/Tools.d.ts.map +1 -1
  91. package/dist/react/client/Tools.js +4 -8
  92. package/dist/react/client/Tools.js.map +1 -1
  93. package/dist/react/client/interactable-model-context.d.ts +9 -10
  94. package/dist/react/client/interactable-model-context.d.ts.map +1 -1
  95. package/dist/react/client/interactable-model-context.js +32 -22
  96. package/dist/react/client/interactable-model-context.js.map +1 -1
  97. package/dist/react/index.d.ts +160 -122
  98. package/dist/react/index.d.ts.map +1 -0
  99. package/dist/react/index.js +5 -5
  100. package/dist/react/interactables-legacy/Interactables.d.ts +1 -3
  101. package/dist/react/interactables-legacy/Interactables.d.ts.map +1 -1
  102. package/dist/react/interactables-legacy/Interactables.js +15 -8
  103. package/dist/react/interactables-legacy/Interactables.js.map +1 -1
  104. package/dist/react/interactables-legacy/interactable-model-context.d.ts +6 -8
  105. package/dist/react/interactables-legacy/interactable-model-context.d.ts.map +1 -1
  106. package/dist/react/interactables-legacy/interactable-model-context.js +13 -14
  107. package/dist/react/interactables-legacy/interactable-model-context.js.map +1 -1
  108. package/dist/react/interactables-legacy/scopes.d.ts +33 -36
  109. package/dist/react/interactables-legacy/scopes.d.ts.map +1 -1
  110. package/dist/react/interactables-legacy/useAssistantInteractable.d.ts +6 -8
  111. package/dist/react/interactables-legacy/useAssistantInteractable.d.ts.map +1 -1
  112. package/dist/react/interactables-legacy/useInteractableState.d.ts +6 -7
  113. package/dist/react/interactables-legacy/useInteractableState.d.ts.map +1 -1
  114. package/dist/react/interactables-shared/useInteractablePersistenceQueue.d.ts +23 -13
  115. package/dist/react/interactables-shared/useInteractablePersistenceQueue.d.ts.map +1 -1
  116. package/dist/react/interactables-shared/useInteractablePersistenceQueue.js +54 -25
  117. package/dist/react/interactables-shared/useInteractablePersistenceQueue.js.map +1 -1
  118. package/dist/react/model-context/define-mcp-toolkit.d.ts +11 -13
  119. package/dist/react/model-context/define-mcp-toolkit.d.ts.map +1 -1
  120. package/dist/react/model-context/define-toolkit.d.ts +12 -4
  121. package/dist/react/model-context/define-toolkit.d.ts.map +1 -1
  122. package/dist/react/model-context/external-tool.d.ts +0 -2
  123. package/dist/react/model-context/external-tool.d.ts.map +1 -1
  124. package/dist/react/model-context/human-tool.d.ts +0 -2
  125. package/dist/react/model-context/human-tool.d.ts.map +1 -1
  126. package/dist/react/model-context/interactableTool.d.ts +23 -25
  127. package/dist/react/model-context/interactableTool.d.ts.map +1 -1
  128. package/dist/react/model-context/makeAssistantDataUI.d.ts +4 -6
  129. package/dist/react/model-context/makeAssistantDataUI.d.ts.map +1 -1
  130. package/dist/react/model-context/makeAssistantTool.d.ts +4 -6
  131. package/dist/react/model-context/makeAssistantTool.d.ts.map +1 -1
  132. package/dist/react/model-context/makeAssistantToolUI.d.ts +4 -6
  133. package/dist/react/model-context/makeAssistantToolUI.d.ts.map +1 -1
  134. package/dist/react/model-context/provider-tool.d.ts +3 -4
  135. package/dist/react/model-context/provider-tool.d.ts.map +1 -1
  136. package/dist/react/model-context/stub-tool.d.ts +0 -2
  137. package/dist/react/model-context/stub-tool.d.ts.map +1 -1
  138. package/dist/react/model-context/toolbox.d.ts +37 -24
  139. package/dist/react/model-context/toolbox.d.ts.map +1 -1
  140. package/dist/react/model-context/useAssistantContext.d.ts +2 -5
  141. package/dist/react/model-context/useAssistantContext.d.ts.map +1 -1
  142. package/dist/react/model-context/useAssistantDataUI.d.ts +5 -7
  143. package/dist/react/model-context/useAssistantDataUI.d.ts.map +1 -1
  144. package/dist/react/model-context/useAssistantInstructions.d.ts +2 -5
  145. package/dist/react/model-context/useAssistantInstructions.d.ts.map +1 -1
  146. package/dist/react/model-context/useAssistantTool.d.ts +8 -11
  147. package/dist/react/model-context/useAssistantTool.d.ts.map +1 -1
  148. package/dist/react/model-context/useAssistantToolUI.d.ts +11 -13
  149. package/dist/react/model-context/useAssistantToolUI.d.ts.map +1 -1
  150. package/dist/react/model-context/useAuiToolOverrides.d.ts +2 -3
  151. package/dist/react/model-context/useAuiToolOverrides.d.ts.map +1 -1
  152. package/dist/react/model-context/useInlineRender.d.ts +2 -4
  153. package/dist/react/model-context/useInlineRender.d.ts.map +1 -1
  154. package/dist/react/model-context/useInteractable.d.ts +37 -36
  155. package/dist/react/model-context/useInteractable.d.ts.map +1 -1
  156. package/dist/react/model-context/useInteractableState.d.ts +10 -8
  157. package/dist/react/model-context/useInteractableState.d.ts.map +1 -1
  158. package/dist/react/model-context/useInteractableVersions.d.ts +3 -5
  159. package/dist/react/model-context/useInteractableVersions.d.ts.map +1 -1
  160. package/dist/react/model-context/useToolArgsStatus.d.ts +5 -6
  161. package/dist/react/model-context/useToolArgsStatus.d.ts.map +1 -1
  162. package/dist/react/primitive-hooks/useActionBarCopy.d.ts +6 -8
  163. package/dist/react/primitive-hooks/useActionBarCopy.d.ts.map +1 -1
  164. package/dist/react/primitive-hooks/useActionBarEdit.d.ts +2 -4
  165. package/dist/react/primitive-hooks/useActionBarEdit.d.ts.map +1 -1
  166. package/dist/react/primitive-hooks/useActionBarFeedback.d.ts +4 -6
  167. package/dist/react/primitive-hooks/useActionBarFeedback.d.ts.map +1 -1
  168. package/dist/react/primitive-hooks/useActionBarReload.d.ts +2 -4
  169. package/dist/react/primitive-hooks/useActionBarReload.d.ts.map +1 -1
  170. package/dist/react/primitive-hooks/useActionBarSpeak.d.ts +2 -4
  171. package/dist/react/primitive-hooks/useActionBarSpeak.d.ts.map +1 -1
  172. package/dist/react/primitive-hooks/useActionBarStopSpeaking.d.ts +2 -4
  173. package/dist/react/primitive-hooks/useActionBarStopSpeaking.d.ts.map +1 -1
  174. package/dist/react/primitive-hooks/useBranchPickerNext.d.ts +2 -4
  175. package/dist/react/primitive-hooks/useBranchPickerNext.d.ts.map +1 -1
  176. package/dist/react/primitive-hooks/useBranchPickerPrevious.d.ts +2 -4
  177. package/dist/react/primitive-hooks/useBranchPickerPrevious.d.ts.map +1 -1
  178. package/dist/react/primitive-hooks/useComposerAddAttachment.d.ts +3 -5
  179. package/dist/react/primitive-hooks/useComposerAddAttachment.d.ts.map +1 -1
  180. package/dist/react/primitive-hooks/useComposerCancel.d.ts +2 -4
  181. package/dist/react/primitive-hooks/useComposerCancel.d.ts.map +1 -1
  182. package/dist/react/primitive-hooks/useComposerDictate.d.ts +2 -4
  183. package/dist/react/primitive-hooks/useComposerDictate.d.ts.map +1 -1
  184. package/dist/react/primitive-hooks/useComposerSend.d.ts +3 -5
  185. package/dist/react/primitive-hooks/useComposerSend.d.ts.map +1 -1
  186. package/dist/react/primitive-hooks/useEditComposerCancel.d.ts +1 -3
  187. package/dist/react/primitive-hooks/useEditComposerCancel.d.ts.map +1 -1
  188. package/dist/react/primitive-hooks/useEditComposerSend.d.ts +2 -4
  189. package/dist/react/primitive-hooks/useEditComposerSend.d.ts.map +1 -1
  190. package/dist/react/primitive-hooks/useMessageBranching.d.ts +4 -6
  191. package/dist/react/primitive-hooks/useMessageBranching.d.ts.map +1 -1
  192. package/dist/react/primitive-hooks/useMessageError.d.ts +0 -2
  193. package/dist/react/primitive-hooks/useMessageError.d.ts.map +1 -1
  194. package/dist/react/primitive-hooks/useMessageReload.d.ts +2 -4
  195. package/dist/react/primitive-hooks/useMessageReload.d.ts.map +1 -1
  196. package/dist/react/primitive-hooks/useSuggestionTrigger.d.ts +6 -8
  197. package/dist/react/primitive-hooks/useSuggestionTrigger.d.ts.map +1 -1
  198. package/dist/react/primitive-hooks/useSuggestionTrigger.js +4 -4
  199. package/dist/react/primitive-hooks/useSuggestionTrigger.js.map +1 -1
  200. package/dist/react/primitive-hooks/useThreadIsEmpty.d.ts +0 -2
  201. package/dist/react/primitive-hooks/useThreadIsEmpty.d.ts.map +1 -1
  202. package/dist/react/primitive-hooks/useThreadIsRunning.d.ts +0 -2
  203. package/dist/react/primitive-hooks/useThreadIsRunning.d.ts.map +1 -1
  204. package/dist/react/primitive-hooks/useThreadListItemArchive.d.ts +1 -3
  205. package/dist/react/primitive-hooks/useThreadListItemArchive.d.ts.map +1 -1
  206. package/dist/react/primitive-hooks/useThreadListItemDelete.d.ts +1 -3
  207. package/dist/react/primitive-hooks/useThreadListItemDelete.d.ts.map +1 -1
  208. package/dist/react/primitive-hooks/useThreadListItemTrigger.d.ts +1 -3
  209. package/dist/react/primitive-hooks/useThreadListItemTrigger.d.ts.map +1 -1
  210. package/dist/react/primitive-hooks/useThreadListItemUnarchive.d.ts +1 -3
  211. package/dist/react/primitive-hooks/useThreadListItemUnarchive.d.ts.map +1 -1
  212. package/dist/react/primitive-hooks/useThreadListLoadMore.d.ts +2 -4
  213. package/dist/react/primitive-hooks/useThreadListLoadMore.d.ts.map +1 -1
  214. package/dist/react/primitive-hooks/useThreadListNew.d.ts +1 -3
  215. package/dist/react/primitive-hooks/useThreadListNew.d.ts.map +1 -1
  216. package/dist/react/primitive-hooks/useThreadMessageIds.d.ts +0 -2
  217. package/dist/react/primitive-hooks/useThreadMessageIds.d.ts.map +1 -1
  218. package/dist/react/primitive-hooks/useThreadMessages.d.ts +1 -3
  219. package/dist/react/primitive-hooks/useThreadMessages.d.ts.map +1 -1
  220. package/dist/react/primitive-hooks/useVoice.d.ts +5 -7
  221. package/dist/react/primitive-hooks/useVoice.d.ts.map +1 -1
  222. package/dist/react/primitives/chainOfThought/ChainOfThoughtParts.d.ts +25 -26
  223. package/dist/react/primitives/chainOfThought/ChainOfThoughtParts.d.ts.map +1 -1
  224. package/dist/react/primitives/composer/ComposerAttachments.d.ts +22 -23
  225. package/dist/react/primitives/composer/ComposerAttachments.d.ts.map +1 -1
  226. package/dist/react/primitives/composer/ComposerIf.d.ts +10 -9
  227. package/dist/react/primitives/composer/ComposerIf.d.ts.map +1 -1
  228. package/dist/react/primitives/composer/ComposerQueue.d.ts +11 -14
  229. package/dist/react/primitives/composer/ComposerQueue.d.ts.map +1 -1
  230. package/dist/react/primitives/generativeUI/GenerativeUI.d.ts +26 -28
  231. package/dist/react/primitives/generativeUI/GenerativeUI.d.ts.map +1 -1
  232. package/dist/react/primitives/generativeUI/GenerativeUI.js +16 -6
  233. package/dist/react/primitives/generativeUI/GenerativeUI.js.map +1 -1
  234. package/dist/react/primitives/message/MessageAttachments.d.ts +26 -23
  235. package/dist/react/primitives/message/MessageAttachments.d.ts.map +1 -1
  236. package/dist/react/primitives/message/MessageAttachments.js +14 -15
  237. package/dist/react/primitives/message/MessageAttachments.js.map +1 -1
  238. package/dist/react/primitives/message/MessageGroupedParts.d.ts +120 -117
  239. package/dist/react/primitives/message/MessageGroupedParts.d.ts.map +1 -1
  240. package/dist/react/primitives/message/MessageGroupedParts.js +3 -3
  241. package/dist/react/primitives/message/MessageGroupedParts.js.map +1 -1
  242. package/dist/react/primitives/message/MessageParts.d.ts +199 -185
  243. package/dist/react/primitives/message/MessageParts.d.ts.map +1 -1
  244. package/dist/react/primitives/message/MessageParts.js +124 -82
  245. package/dist/react/primitives/message/MessageParts.js.map +1 -1
  246. package/dist/react/primitives/message/MessageQuote.d.ts +6 -8
  247. package/dist/react/primitives/message/MessageQuote.d.ts.map +1 -1
  248. package/dist/react/primitives/messagePart/MessagePartInProgress.d.ts +2 -4
  249. package/dist/react/primitives/messagePart/MessagePartInProgress.d.ts.map +1 -1
  250. package/dist/react/primitives/part/PartMessages.d.ts +13 -15
  251. package/dist/react/primitives/part/PartMessages.d.ts.map +1 -1
  252. package/dist/react/primitives/thread/ThreadMessages.d.ts +54 -56
  253. package/dist/react/primitives/thread/ThreadMessages.d.ts.map +1 -1
  254. package/dist/react/primitives/thread/ThreadSuggestions.d.ts +20 -21
  255. package/dist/react/primitives/thread/ThreadSuggestions.d.ts.map +1 -1
  256. package/dist/react/primitives/thread/ThreadSuggestions.js +24 -5
  257. package/dist/react/primitives/thread/ThreadSuggestions.js.map +1 -1
  258. package/dist/react/primitives/threadList/ThreadListItems.d.ts +22 -23
  259. package/dist/react/primitives/threadList/ThreadListItems.d.ts.map +1 -1
  260. package/dist/react/primitives/threadListItem/ThreadListItemTitle.d.ts +4 -6
  261. package/dist/react/primitives/threadListItem/ThreadListItemTitle.d.ts.map +1 -1
  262. package/dist/react/providers/AttachmentByIndexProvider.d.ts +3 -5
  263. package/dist/react/providers/AttachmentByIndexProvider.d.ts.map +1 -1
  264. package/dist/react/providers/ChainOfThoughtByIndicesProvider.d.ts +3 -5
  265. package/dist/react/providers/ChainOfThoughtByIndicesProvider.d.ts.map +1 -1
  266. package/dist/react/providers/ChainOfThoughtPartByIndexProvider.d.ts +2 -4
  267. package/dist/react/providers/ChainOfThoughtPartByIndexProvider.d.ts.map +1 -1
  268. package/dist/react/providers/MessageByIdProvider.d.ts +2 -4
  269. package/dist/react/providers/MessageByIdProvider.d.ts.map +1 -1
  270. package/dist/react/providers/MessageByIndexProvider.d.ts +2 -4
  271. package/dist/react/providers/MessageByIndexProvider.d.ts.map +1 -1
  272. package/dist/react/providers/PartByIndexProvider.d.ts +2 -4
  273. package/dist/react/providers/PartByIndexProvider.d.ts.map +1 -1
  274. package/dist/react/providers/QueueItemByIndexProvider.d.ts +2 -4
  275. package/dist/react/providers/QueueItemByIndexProvider.d.ts.map +1 -1
  276. package/dist/react/providers/ReadonlyThreadProvider.d.ts +5 -7
  277. package/dist/react/providers/ReadonlyThreadProvider.d.ts.map +1 -1
  278. package/dist/react/providers/ReadonlyThreadProvider.js.map +1 -1
  279. package/dist/react/providers/SuggestionByIndexProvider.d.ts +2 -4
  280. package/dist/react/providers/SuggestionByIndexProvider.d.ts.map +1 -1
  281. package/dist/react/providers/TextMessagePartProvider.d.ts +3 -5
  282. package/dist/react/providers/TextMessagePartProvider.d.ts.map +1 -1
  283. package/dist/react/providers/TextMessagePartProvider.js +5 -1
  284. package/dist/react/providers/TextMessagePartProvider.js.map +1 -1
  285. package/dist/react/providers/ThreadListItemByIndexProvider.d.ts +3 -5
  286. package/dist/react/providers/ThreadListItemByIndexProvider.d.ts.map +1 -1
  287. package/dist/react/providers/ThreadListItemRuntimeProvider.d.ts +3 -6
  288. package/dist/react/providers/ThreadListItemRuntimeProvider.d.ts.map +1 -1
  289. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.d.ts +209 -219
  290. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.d.ts.map +1 -1
  291. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js +53 -39
  292. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js.map +1 -1
  293. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts +185 -196
  294. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts.map +1 -1
  295. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js +13 -4
  296. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js.map +1 -1
  297. package/dist/react/runtimes/RemoteThreadResource.d.ts +14 -19
  298. package/dist/react/runtimes/RemoteThreadResource.d.ts.map +1 -1
  299. package/dist/react/runtimes/RemoteThreadResource.js +3 -1
  300. package/dist/react/runtimes/RemoteThreadResource.js.map +1 -1
  301. package/dist/react/runtimes/RuntimeAdapterProvider.d.ts +8 -9
  302. package/dist/react/runtimes/RuntimeAdapterProvider.d.ts.map +1 -1
  303. package/dist/react/runtimes/assistant-transport/commandQueue.d.ts +9 -11
  304. package/dist/react/runtimes/assistant-transport/commandQueue.d.ts.map +1 -1
  305. package/dist/react/runtimes/assistant-transport/replayBoundaryStream.d.ts +4 -5
  306. package/dist/react/runtimes/assistant-transport/replayBoundaryStream.d.ts.map +1 -1
  307. package/dist/react/runtimes/assistant-transport/replayBoundaryStream.js +9 -2
  308. package/dist/react/runtimes/assistant-transport/replayBoundaryStream.js.map +1 -1
  309. package/dist/react/runtimes/assistant-transport/runManager.d.ts +7 -9
  310. package/dist/react/runtimes/assistant-transport/runManager.d.ts.map +1 -1
  311. package/dist/react/runtimes/assistant-transport/runManager.js +4 -1
  312. package/dist/react/runtimes/assistant-transport/runManager.js.map +1 -1
  313. package/dist/react/runtimes/assistant-transport/types.d.ts +106 -100
  314. package/dist/react/runtimes/assistant-transport/types.d.ts.map +1 -1
  315. package/dist/react/runtimes/assistant-transport/useAssistantTransportRuntime.d.ts +3 -5
  316. package/dist/react/runtimes/assistant-transport/useAssistantTransportRuntime.d.ts.map +1 -1
  317. package/dist/react/runtimes/assistant-transport/useAssistantTransportRuntime.js +17 -6
  318. package/dist/react/runtimes/assistant-transport/useAssistantTransportRuntime.js.map +1 -1
  319. package/dist/react/runtimes/assistant-transport/useConvertedState.d.ts +2 -4
  320. package/dist/react/runtimes/assistant-transport/useConvertedState.d.ts.map +1 -1
  321. package/dist/react/runtimes/assistant-transport/useLatestRef.d.ts +1 -3
  322. package/dist/react/runtimes/assistant-transport/useLatestRef.d.ts.map +1 -1
  323. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.d.ts +5 -7
  324. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.d.ts.map +1 -1
  325. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js +410 -213
  326. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js.map +1 -1
  327. package/dist/react/runtimes/cloud/CloudFileAttachmentAdapter.d.ts +14 -16
  328. package/dist/react/runtimes/cloud/CloudFileAttachmentAdapter.d.ts.map +1 -1
  329. package/dist/react/runtimes/cloud/CloudFileAttachmentAdapter.js +8 -4
  330. package/dist/react/runtimes/cloud/CloudFileAttachmentAdapter.js.map +1 -1
  331. package/dist/react/runtimes/cloud/auiV0.d.ts +136 -123
  332. package/dist/react/runtimes/cloud/auiV0.d.ts.map +1 -1
  333. package/dist/react/runtimes/cloud/auiV0.js +60 -9
  334. package/dist/react/runtimes/cloud/auiV0.js.map +1 -1
  335. package/dist/react/runtimes/cloud/createCloudThreadListAdapter.d.ts +9 -10
  336. package/dist/react/runtimes/cloud/createCloudThreadListAdapter.d.ts.map +1 -1
  337. package/dist/react/runtimes/cloud/createCloudThreadListAdapterCreateFallback.d.ts +4 -5
  338. package/dist/react/runtimes/cloud/createCloudThreadListAdapterCreateFallback.d.ts.map +1 -1
  339. package/dist/react/runtimes/cloud/sdkIdentity.d.ts +1 -3
  340. package/dist/react/runtimes/cloud/sdkIdentity.d.ts.map +1 -1
  341. package/dist/react/runtimes/cloud/sdkIdentity.js +1 -1
  342. package/dist/react/runtimes/cloud/useCloudThreadListAdapter.d.ts +2 -4
  343. package/dist/react/runtimes/cloud/useCloudThreadListAdapter.d.ts.map +1 -1
  344. package/dist/react/runtimes/cloud/useCloudThreadListRuntime.d.ts +8 -9
  345. package/dist/react/runtimes/cloud/useCloudThreadListRuntime.d.ts.map +1 -1
  346. package/dist/react/runtimes/createMessageConverter.d.ts +14 -17
  347. package/dist/react/runtimes/createMessageConverter.d.ts.map +1 -1
  348. package/dist/react/runtimes/createMessageConverter.js.map +1 -1
  349. package/dist/react/runtimes/createRuntimeExtras.d.ts +11 -13
  350. package/dist/react/runtimes/createRuntimeExtras.d.ts.map +1 -1
  351. package/dist/react/runtimes/external-message-converter.d.ts +14 -18
  352. package/dist/react/runtimes/external-message-converter.d.ts.map +1 -1
  353. package/dist/react/runtimes/external-store-history-copy.d.ts +25 -0
  354. package/dist/react/runtimes/external-store-history-copy.d.ts.map +1 -0
  355. package/dist/react/runtimes/external-store-history-copy.js +148 -0
  356. package/dist/react/runtimes/external-store-history-copy.js.map +1 -0
  357. package/dist/react/runtimes/useExternalStoreRuntime.d.ts +2 -4
  358. package/dist/react/runtimes/useExternalStoreRuntime.d.ts.map +1 -1
  359. package/dist/react/runtimes/useExternalStoreRuntime.js +72 -35
  360. package/dist/react/runtimes/useExternalStoreRuntime.js.map +1 -1
  361. package/dist/react/runtimes/useExternalStoreSharedOptions.d.ts +1 -3
  362. package/dist/react/runtimes/useExternalStoreSharedOptions.d.ts.map +1 -1
  363. package/dist/react/runtimes/useLocalRuntime.d.ts +26 -37
  364. package/dist/react/runtimes/useLocalRuntime.d.ts.map +1 -1
  365. package/dist/react/runtimes/useLocalRuntime.js +142 -34
  366. package/dist/react/runtimes/useLocalRuntime.js.map +1 -1
  367. package/dist/react/runtimes/useRemoteThreadListRuntime.d.ts +2 -4
  368. package/dist/react/runtimes/useRemoteThreadListRuntime.d.ts.map +1 -1
  369. package/dist/react/runtimes/useRuntimeAdapters.d.ts +1 -3
  370. package/dist/react/runtimes/useRuntimeAdapters.d.ts.map +1 -1
  371. package/dist/react/runtimes/useStreamingTiming.d.ts +3 -5
  372. package/dist/react/runtimes/useStreamingTiming.d.ts.map +1 -1
  373. package/dist/react/types/MessagePartComponentTypes.d.ts +46 -43
  374. package/dist/react/types/MessagePartComponentTypes.d.ts.map +1 -1
  375. package/dist/react/types/scopes/dataRenderers.d.ts +8 -11
  376. package/dist/react/types/scopes/dataRenderers.d.ts.map +1 -1
  377. package/dist/react/types/scopes/interactables.d.ts +50 -49
  378. package/dist/react/types/scopes/interactables.d.ts.map +1 -1
  379. package/dist/react/types/scopes/tools.d.ts +17 -19
  380. package/dist/react/types/scopes/tools.d.ts.map +1 -1
  381. package/dist/react/types/store-augmentation.d.ts +10 -11
  382. package/dist/react/types/store-augmentation.d.ts.map +1 -1
  383. package/dist/react/utils/getMessageQuote.d.ts +5 -6
  384. package/dist/react/utils/getMessageQuote.d.ts.map +1 -1
  385. package/dist/react/utils/groupParts.d.ts +28 -29
  386. package/dist/react/utils/groupParts.d.ts.map +1 -1
  387. package/dist/react/utils/useJSONEqual.d.ts +1 -3
  388. package/dist/react/utils/useJSONEqual.d.ts.map +1 -1
  389. package/dist/react/utils/useJSONSchemaDependency.d.ts +2 -4
  390. package/dist/react/utils/useJSONSchemaDependency.d.ts.map +1 -1
  391. package/dist/runtime/api/assistant-runtime.d.ts +26 -28
  392. package/dist/runtime/api/assistant-runtime.d.ts.map +1 -1
  393. package/dist/runtime/api/attachment-runtime.d.ts +46 -43
  394. package/dist/runtime/api/attachment-runtime.d.ts.map +1 -1
  395. package/dist/runtime/api/attachment-runtime.js.map +1 -1
  396. package/dist/runtime/api/bindings.d.ts +31 -30
  397. package/dist/runtime/api/bindings.d.ts.map +1 -1
  398. package/dist/runtime/api/composer-runtime.d.ts +207 -193
  399. package/dist/runtime/api/composer-runtime.d.ts.map +1 -1
  400. package/dist/runtime/api/composer-runtime.js +4 -0
  401. package/dist/runtime/api/composer-runtime.js.map +1 -1
  402. package/dist/runtime/api/message-part-runtime.d.ts +28 -26
  403. package/dist/runtime/api/message-part-runtime.d.ts.map +1 -1
  404. package/dist/runtime/api/message-part-runtime.js +22 -0
  405. package/dist/runtime/api/message-part-runtime.js.map +1 -1
  406. package/dist/runtime/api/message-runtime.d.ts +87 -84
  407. package/dist/runtime/api/message-runtime.d.ts.map +1 -1
  408. package/dist/runtime/api/message-runtime.js +1 -1
  409. package/dist/runtime/api/message-runtime.js.map +1 -1
  410. package/dist/runtime/api/paths.d.ts +48 -50
  411. package/dist/runtime/api/paths.d.ts.map +1 -1
  412. package/dist/runtime/api/thread-list-item-runtime.d.ts +64 -66
  413. package/dist/runtime/api/thread-list-item-runtime.d.ts.map +1 -1
  414. package/dist/runtime/api/thread-list-item-runtime.js.map +1 -1
  415. package/dist/runtime/api/thread-list-runtime.d.ts +73 -76
  416. package/dist/runtime/api/thread-list-runtime.d.ts.map +1 -1
  417. package/dist/runtime/api/thread-list-runtime.js.map +1 -1
  418. package/dist/runtime/api/thread-runtime.d.ts +299 -300
  419. package/dist/runtime/api/thread-runtime.d.ts.map +1 -1
  420. package/dist/runtime/api/thread-runtime.js +2 -1
  421. package/dist/runtime/api/thread-runtime.js.map +1 -1
  422. package/dist/runtime/base/base-assistant-runtime-core.d.ts +8 -10
  423. package/dist/runtime/base/base-assistant-runtime-core.d.ts.map +1 -1
  424. package/dist/runtime/base/base-composer-runtime-core.d.ts +149 -99
  425. package/dist/runtime/base/base-composer-runtime-core.d.ts.map +1 -1
  426. package/dist/runtime/base/base-composer-runtime-core.js +400 -122
  427. package/dist/runtime/base/base-composer-runtime-core.js.map +1 -1
  428. package/dist/runtime/base/base-thread-runtime-core.d.ts +120 -108
  429. package/dist/runtime/base/base-thread-runtime-core.d.ts.map +1 -1
  430. package/dist/runtime/base/base-thread-runtime-core.js +109 -41
  431. package/dist/runtime/base/base-thread-runtime-core.js.map +1 -1
  432. package/dist/runtime/base/default-edit-composer-runtime-core.d.ts +29 -29
  433. package/dist/runtime/base/default-edit-composer-runtime-core.d.ts.map +1 -1
  434. package/dist/runtime/base/default-edit-composer-runtime-core.js +10 -1
  435. package/dist/runtime/base/default-edit-composer-runtime-core.js.map +1 -1
  436. package/dist/runtime/base/default-thread-composer-runtime-core.d.ts +27 -29
  437. package/dist/runtime/base/default-thread-composer-runtime-core.d.ts.map +1 -1
  438. package/dist/runtime/base/default-thread-composer-runtime-core.js +21 -6
  439. package/dist/runtime/base/default-thread-composer-runtime-core.js.map +1 -1
  440. package/dist/runtime/branch/external-thread-branch-adapter.d.ts +14 -16
  441. package/dist/runtime/branch/external-thread-branch-adapter.d.ts.map +1 -1
  442. package/dist/runtime/interfaces/assistant-runtime-core.d.ts +7 -9
  443. package/dist/runtime/interfaces/assistant-runtime-core.d.ts.map +1 -1
  444. package/dist/runtime/interfaces/composer-runtime-core.d.ts +88 -72
  445. package/dist/runtime/interfaces/composer-runtime-core.d.ts.map +1 -1
  446. package/dist/runtime/interfaces/thread-list-runtime-core.d.ts +63 -65
  447. package/dist/runtime/interfaces/thread-list-runtime-core.d.ts.map +1 -1
  448. package/dist/runtime/interfaces/thread-runtime-core.d.ts +210 -197
  449. package/dist/runtime/interfaces/thread-runtime-core.d.ts.map +1 -1
  450. package/dist/runtime/internal.d.ts +25 -20
  451. package/dist/runtime/internal.d.ts.map +1 -0
  452. package/dist/runtime/queue/external-thread-queue-adapter.d.ts +35 -37
  453. package/dist/runtime/queue/external-thread-queue-adapter.d.ts.map +1 -1
  454. package/dist/runtime/queue/message-queue.d.ts +29 -31
  455. package/dist/runtime/queue/message-queue.d.ts.map +1 -1
  456. package/dist/runtime/queue/message-queue.js +1 -1
  457. package/dist/runtime/queue/queue-item.d.ts +5 -7
  458. package/dist/runtime/queue/queue-item.d.ts.map +1 -1
  459. package/dist/runtime/utils/abortable-thread-load.d.ts +7 -8
  460. package/dist/runtime/utils/abortable-thread-load.d.ts.map +1 -1
  461. package/dist/runtime/utils/attachment-add-operations.d.ts +13 -11
  462. package/dist/runtime/utils/attachment-add-operations.d.ts.map +1 -1
  463. package/dist/runtime/utils/attachment-add-operations.js +24 -2
  464. package/dist/runtime/utils/attachment-add-operations.js.map +1 -1
  465. package/dist/runtime/utils/attachment-send-operations.d.ts +12 -0
  466. package/dist/runtime/utils/attachment-send-operations.d.ts.map +1 -0
  467. package/dist/runtime/utils/attachment-send-operations.js +34 -0
  468. package/dist/runtime/utils/attachment-send-operations.js.map +1 -0
  469. package/dist/runtime/utils/auto-status.d.ts +4 -5
  470. package/dist/runtime/utils/auto-status.d.ts.map +1 -1
  471. package/dist/runtime/utils/auto-status.js +3 -5
  472. package/dist/runtime/utils/auto-status.js.map +1 -1
  473. package/dist/runtime/utils/chat-model-adapter.d.ts +26 -27
  474. package/dist/runtime/utils/chat-model-adapter.d.ts.map +1 -1
  475. package/dist/runtime/utils/external-message-conversion.d.ts +32 -32
  476. package/dist/runtime/utils/external-message-conversion.d.ts.map +1 -1
  477. package/dist/runtime/utils/external-message-conversion.js +3 -3
  478. package/dist/runtime/utils/external-message-conversion.js.map +1 -1
  479. package/dist/runtime/utils/external-store-message.d.ts +2 -4
  480. package/dist/runtime/utils/external-store-message.d.ts.map +1 -1
  481. package/dist/runtime/utils/message-repository-session.d.ts +21 -23
  482. package/dist/runtime/utils/message-repository-session.d.ts.map +1 -1
  483. package/dist/runtime/utils/message-repository.d.ts +50 -51
  484. package/dist/runtime/utils/message-repository.d.ts.map +1 -1
  485. package/dist/runtime/utils/message-repository.js +8 -7
  486. package/dist/runtime/utils/message-repository.js.map +1 -1
  487. package/dist/runtime/utils/pending-tool-calls.d.ts +10 -11
  488. package/dist/runtime/utils/pending-tool-calls.d.ts.map +1 -1
  489. package/dist/runtime/utils/resolveToolApprovalResponse.d.ts +6 -8
  490. package/dist/runtime/utils/resolveToolApprovalResponse.d.ts.map +1 -1
  491. package/dist/runtime/utils/runtime-extras-brand.d.ts +4 -6
  492. package/dist/runtime/utils/runtime-extras-brand.d.ts.map +1 -1
  493. package/dist/runtime/utils/stored-message-parts.d.ts +5 -5
  494. package/dist/runtime/utils/stored-message-parts.d.ts.map +1 -1
  495. package/dist/runtime/utils/stored-message-parts.js +22 -2
  496. package/dist/runtime/utils/stored-message-parts.js.map +1 -1
  497. package/dist/runtime/utils/streaming-timing.d.ts +19 -21
  498. package/dist/runtime/utils/streaming-timing.d.ts.map +1 -1
  499. package/dist/runtime/utils/thread-message-like.d.ts +36 -32
  500. package/dist/runtime/utils/thread-message-like.d.ts.map +1 -1
  501. package/dist/runtime/utils/thread-message-like.js +16 -6
  502. package/dist/runtime/utils/thread-message-like.js.map +1 -1
  503. package/dist/runtime/utils/thread-runtime-lifecycle.d.ts +4 -5
  504. package/dist/runtime/utils/thread-runtime-lifecycle.d.ts.map +1 -1
  505. package/dist/runtime/utils/thread-runtime-lifecycle.js +31 -4
  506. package/dist/runtime/utils/thread-runtime-lifecycle.js.map +1 -1
  507. package/dist/runtime/utils/tool-call-tree.d.ts +6 -8
  508. package/dist/runtime/utils/tool-call-tree.d.ts.map +1 -1
  509. package/dist/runtime/utils/tool-interactions.d.ts +10 -0
  510. package/dist/runtime/utils/tool-interactions.d.ts.map +1 -0
  511. package/dist/runtime/utils/tool-interactions.js +92 -0
  512. package/dist/runtime/utils/tool-interactions.js.map +1 -0
  513. package/dist/runtimes/assistant-transport/utils.d.ts +0 -2
  514. package/dist/runtimes/assistant-transport/utils.d.ts.map +1 -1
  515. package/dist/runtimes/external-store/external-store-adapter.d.ts +215 -198
  516. package/dist/runtimes/external-store/external-store-adapter.d.ts.map +1 -1
  517. package/dist/runtimes/external-store/external-store-runtime-core.d.ts +5 -6
  518. package/dist/runtimes/external-store/external-store-runtime-core.d.ts.map +1 -1
  519. package/dist/runtimes/external-store/external-store-runtime-core.js +4 -1
  520. package/dist/runtimes/external-store/external-store-runtime-core.js.map +1 -1
  521. package/dist/runtimes/external-store/external-store-shared-options.d.ts +1 -3
  522. package/dist/runtimes/external-store/external-store-shared-options.d.ts.map +1 -1
  523. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.d.ts +38 -40
  524. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.d.ts.map +1 -1
  525. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.js +2 -2
  526. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.js.map +1 -1
  527. package/dist/runtimes/external-store/external-store-thread-runtime-core.d.ts +117 -124
  528. package/dist/runtimes/external-store/external-store-thread-runtime-core.d.ts.map +1 -1
  529. package/dist/runtimes/external-store/external-store-thread-runtime-core.js +44 -14
  530. package/dist/runtimes/external-store/external-store-thread-runtime-core.js.map +1 -1
  531. package/dist/runtimes/external-store/thread-message-converter.d.ts +3 -5
  532. package/dist/runtimes/external-store/thread-message-converter.d.ts.map +1 -1
  533. package/dist/runtimes/inert/InertThreadRuntimeCore.d.ts +94 -95
  534. package/dist/runtimes/inert/InertThreadRuntimeCore.d.ts.map +1 -1
  535. package/dist/runtimes/inert/InertThreadRuntimeCore.js +3 -0
  536. package/dist/runtimes/inert/InertThreadRuntimeCore.js.map +1 -1
  537. package/dist/runtimes/internal.d.ts +20 -16
  538. package/dist/runtimes/internal.d.ts.map +1 -0
  539. package/dist/runtimes/local/local-runtime-core.d.ts +6 -8
  540. package/dist/runtimes/local/local-runtime-core.d.ts.map +1 -1
  541. package/dist/runtimes/local/local-runtime-options.d.ts +40 -42
  542. package/dist/runtimes/local/local-runtime-options.d.ts.map +1 -1
  543. package/dist/runtimes/local/local-thread-list-runtime-core.d.ts +40 -42
  544. package/dist/runtimes/local/local-thread-list-runtime-core.d.ts.map +1 -1
  545. package/dist/runtimes/local/local-thread-runtime-core.d.ts +93 -97
  546. package/dist/runtimes/local/local-thread-runtime-core.d.ts.map +1 -1
  547. package/dist/runtimes/local/local-thread-runtime-core.js +178 -31
  548. package/dist/runtimes/local/local-thread-runtime-core.js.map +1 -1
  549. package/dist/runtimes/local/should-continue.d.ts +1 -3
  550. package/dist/runtimes/local/should-continue.d.ts.map +1 -1
  551. package/dist/runtimes/readonly/ReadonlyThreadRuntimeCore.d.ts +76 -53
  552. package/dist/runtimes/readonly/ReadonlyThreadRuntimeCore.d.ts.map +1 -1
  553. package/dist/runtimes/readonly/ReadonlyThreadRuntimeCore.js +28 -1
  554. package/dist/runtimes/readonly/ReadonlyThreadRuntimeCore.js.map +1 -1
  555. package/dist/runtimes/remote-thread-list/adapter/in-memory.d.ts +11 -13
  556. package/dist/runtimes/remote-thread-list/adapter/in-memory.d.ts.map +1 -1
  557. package/dist/runtimes/remote-thread-list/adapter-changed.d.ts +2 -4
  558. package/dist/runtimes/remote-thread-list/adapter-changed.d.ts.map +1 -1
  559. package/dist/runtimes/remote-thread-list/empty-thread-core.d.ts +1 -3
  560. package/dist/runtimes/remote-thread-list/empty-thread-core.d.ts.map +1 -1
  561. package/dist/runtimes/remote-thread-list/optimistic-state.d.ts +33 -29
  562. package/dist/runtimes/remote-thread-list/optimistic-state.d.ts.map +1 -1
  563. package/dist/runtimes/remote-thread-list/optimistic-state.js +8 -3
  564. package/dist/runtimes/remote-thread-list/optimistic-state.js.map +1 -1
  565. package/dist/runtimes/remote-thread-list/remote-thread-state.d.ts +44 -46
  566. package/dist/runtimes/remote-thread-list/remote-thread-state.d.ts.map +1 -1
  567. package/dist/runtimes/remote-thread-list/remote-thread-state.js +1 -1
  568. package/dist/runtimes/remote-thread-list/title-generation.d.ts +25 -26
  569. package/dist/runtimes/remote-thread-list/title-generation.d.ts.map +1 -1
  570. package/dist/runtimes/remote-thread-list/title.d.ts +3 -5
  571. package/dist/runtimes/remote-thread-list/title.d.ts.map +1 -1
  572. package/dist/runtimes/remote-thread-list/types.d.ts +111 -112
  573. package/dist/runtimes/remote-thread-list/types.d.ts.map +1 -1
  574. package/dist/runtimes/tool-invocations/ToolInvocationTracker.d.ts +144 -145
  575. package/dist/runtimes/tool-invocations/ToolInvocationTracker.d.ts.map +1 -1
  576. package/dist/runtimes/tool-invocations/ToolInvocationTracker.js +22 -7
  577. package/dist/runtimes/tool-invocations/ToolInvocationTracker.js.map +1 -1
  578. package/dist/store/clients/chain-of-thought-client.d.ts +7 -9
  579. package/dist/store/clients/chain-of-thought-client.d.ts.map +1 -1
  580. package/dist/store/clients/external-thread.d.ts +53 -54
  581. package/dist/store/clients/external-thread.d.ts.map +1 -1
  582. package/dist/store/clients/external-thread.js +1256 -731
  583. package/dist/store/clients/external-thread.js.map +1 -1
  584. package/dist/store/clients/model-context-client.d.ts +1 -3
  585. package/dist/store/clients/model-context-client.d.ts.map +1 -1
  586. package/dist/store/clients/no-op-composer-client.d.ts +2 -4
  587. package/dist/store/clients/no-op-composer-client.d.ts.map +1 -1
  588. package/dist/store/clients/no-op-composer-client.js +3 -1
  589. package/dist/store/clients/no-op-composer-client.js.map +1 -1
  590. package/dist/store/clients/runtime-adapter.d.ts +1 -3
  591. package/dist/store/clients/runtime-adapter.d.ts.map +1 -1
  592. package/dist/store/clients/single-thread-list.d.ts +3 -4
  593. package/dist/store/clients/single-thread-list.d.ts.map +1 -1
  594. package/dist/store/clients/submission-message.d.ts +8 -0
  595. package/dist/store/clients/submission-message.d.ts.map +1 -0
  596. package/dist/store/clients/submission-message.js +22 -0
  597. package/dist/store/clients/submission-message.js.map +1 -0
  598. package/dist/store/clients/suggestions.d.ts +5 -7
  599. package/dist/store/clients/suggestions.d.ts.map +1 -1
  600. package/dist/store/clients/thread-message-client.d.ts +10 -9
  601. package/dist/store/clients/thread-message-client.d.ts.map +1 -1
  602. package/dist/store/clients/thread-message-client.js +81 -82
  603. package/dist/store/clients/thread-message-client.js.map +1 -1
  604. package/dist/store/clients/thread-selection-events.d.ts +0 -2
  605. package/dist/store/clients/thread-selection-events.d.ts.map +1 -1
  606. package/dist/store/clients/thread-tasks.d.ts +4 -6
  607. package/dist/store/clients/thread-tasks.d.ts.map +1 -1
  608. package/dist/store/env.d.ts +0 -2
  609. package/dist/store/env.d.ts.map +1 -1
  610. package/dist/store/index.d.ts +33 -36
  611. package/dist/store/index.d.ts.map +1 -0
  612. package/dist/store/index.js +5 -5
  613. package/dist/store/internal.d.ts +12 -12
  614. package/dist/store/internal.d.ts.map +1 -0
  615. package/dist/store/internal.js +2 -2
  616. package/dist/store/primitive-predicates.d.ts +3 -4
  617. package/dist/store/primitive-predicates.d.ts.map +1 -1
  618. package/dist/store/primitive-predicates.js +9 -4
  619. package/dist/store/primitive-predicates.js.map +1 -1
  620. package/dist/store/runtime-clients/attachment-runtime-client.d.ts +3 -5
  621. package/dist/store/runtime-clients/attachment-runtime-client.d.ts.map +1 -1
  622. package/dist/store/runtime-clients/composer-runtime-client.d.ts +10 -12
  623. package/dist/store/runtime-clients/composer-runtime-client.d.ts.map +1 -1
  624. package/dist/store/runtime-clients/composer-runtime-client.js +65 -62
  625. package/dist/store/runtime-clients/composer-runtime-client.js.map +1 -1
  626. package/dist/store/runtime-clients/handle-runtime-action.d.ts +0 -2
  627. package/dist/store/runtime-clients/handle-runtime-action.d.ts.map +1 -1
  628. package/dist/store/runtime-clients/handle-thread-list-action.d.ts +0 -2
  629. package/dist/store/runtime-clients/handle-thread-list-action.d.ts.map +1 -1
  630. package/dist/store/runtime-clients/liveRef.d.ts +1 -3
  631. package/dist/store/runtime-clients/liveRef.d.ts.map +1 -1
  632. package/dist/store/runtime-clients/message-part-runtime-client.d.ts +3 -5
  633. package/dist/store/runtime-clients/message-part-runtime-client.d.ts.map +1 -1
  634. package/dist/store/runtime-clients/message-part-runtime-client.js +26 -16
  635. package/dist/store/runtime-clients/message-part-runtime-client.js.map +1 -1
  636. package/dist/store/runtime-clients/message-runtime-client.d.ts +9 -9
  637. package/dist/store/runtime-clients/message-runtime-client.d.ts.map +1 -1
  638. package/dist/store/runtime-clients/message-runtime-client.js +121 -113
  639. package/dist/store/runtime-clients/message-runtime-client.js.map +1 -1
  640. package/dist/store/runtime-clients/thread-list-item-runtime-client.d.ts +4 -6
  641. package/dist/store/runtime-clients/thread-list-item-runtime-client.d.ts.map +1 -1
  642. package/dist/store/runtime-clients/thread-list-runtime-client.d.ts +5 -7
  643. package/dist/store/runtime-clients/thread-list-runtime-client.d.ts.map +1 -1
  644. package/dist/store/runtime-clients/thread-runtime-client.d.ts +3 -5
  645. package/dist/store/runtime-clients/thread-runtime-client.d.ts.map +1 -1
  646. package/dist/store/runtime-clients/thread-runtime-client.js +221 -158
  647. package/dist/store/runtime-clients/thread-runtime-client.js.map +1 -1
  648. package/dist/store/runtime-clients/useSubscribable.d.ts +2 -4
  649. package/dist/store/runtime-clients/useSubscribable.d.ts.map +1 -1
  650. package/dist/store/scope-registration.d.ts +28 -29
  651. package/dist/store/scope-registration.d.ts.map +1 -1
  652. package/dist/store/scopes/attachment.d.ts +15 -17
  653. package/dist/store/scopes/attachment.d.ts.map +1 -1
  654. package/dist/store/scopes/chain-of-thought.d.ts +28 -29
  655. package/dist/store/scopes/chain-of-thought.d.ts.map +1 -1
  656. package/dist/store/scopes/composer.d.ts +141 -128
  657. package/dist/store/scopes/composer.d.ts.map +1 -1
  658. package/dist/store/scopes/message.d.ts +114 -107
  659. package/dist/store/scopes/message.d.ts.map +1 -1
  660. package/dist/store/scopes/model-context.d.ts +7 -9
  661. package/dist/store/scopes/model-context.d.ts.map +1 -1
  662. package/dist/store/scopes/part.d.ts +49 -45
  663. package/dist/store/scopes/part.d.ts.map +1 -1
  664. package/dist/store/scopes/queue-item.d.ts +18 -20
  665. package/dist/store/scopes/queue-item.d.ts.map +1 -1
  666. package/dist/store/scopes/suggestion.d.ts +10 -12
  667. package/dist/store/scopes/suggestion.d.ts.map +1 -1
  668. package/dist/store/scopes/suggestions.d.ts +13 -15
  669. package/dist/store/scopes/suggestions.d.ts.map +1 -1
  670. package/dist/store/scopes/task.d.ts +23 -25
  671. package/dist/store/scopes/task.d.ts.map +1 -1
  672. package/dist/store/scopes/thread-list-item.d.ts +63 -65
  673. package/dist/store/scopes/thread-list-item.d.ts.map +1 -1
  674. package/dist/store/scopes/thread.d.ts +192 -194
  675. package/dist/store/scopes/thread.d.ts.map +1 -1
  676. package/dist/store/scopes/threads.d.ts +45 -47
  677. package/dist/store/scopes/threads.d.ts.map +1 -1
  678. package/dist/subscribable/subscribable.d.ts +58 -53
  679. package/dist/subscribable/subscribable.d.ts.map +1 -1
  680. package/dist/subscribable/subscribable.js +35 -7
  681. package/dist/subscribable/subscribable.js.map +1 -1
  682. package/dist/types/attachment.d.ts +26 -27
  683. package/dist/types/attachment.d.ts.map +1 -1
  684. package/dist/types/augmentations.d.ts +4 -4
  685. package/dist/types/augmentations.d.ts.map +1 -1
  686. package/dist/types/directive.d.ts +11 -13
  687. package/dist/types/directive.d.ts.map +1 -1
  688. package/dist/types/error.d.ts +7 -8
  689. package/dist/types/error.d.ts.map +1 -1
  690. package/dist/types/index.d.ts +7 -7
  691. package/dist/types/index.d.ts.map +1 -0
  692. package/dist/types/message.d.ts +322 -284
  693. package/dist/types/message.d.ts.map +1 -1
  694. package/dist/types/message.js.map +1 -1
  695. package/dist/types/quote.d.ts +2 -4
  696. package/dist/types/quote.d.ts.map +1 -1
  697. package/dist/types/trigger.d.ts +8 -10
  698. package/dist/types/trigger.d.ts.map +1 -1
  699. package/dist/types/unsubscribe.d.ts +0 -2
  700. package/dist/types/unsubscribe.d.ts.map +1 -1
  701. package/dist/utils/abortable-iterable.d.ts +0 -2
  702. package/dist/utils/abortable-iterable.d.ts.map +1 -1
  703. package/dist/utils/composite-context-provider.d.ts +8 -11
  704. package/dist/utils/composite-context-provider.d.ts.map +1 -1
  705. package/dist/utils/data-url.d.ts +10 -12
  706. package/dist/utils/data-url.d.ts.map +1 -1
  707. package/dist/utils/getGroupStatus.d.ts +17 -18
  708. package/dist/utils/getGroupStatus.d.ts.map +1 -1
  709. package/dist/utils/id.d.ts +0 -2
  710. package/dist/utils/id.d.ts.map +1 -1
  711. package/dist/utils/image-media-type.d.ts +0 -2
  712. package/dist/utils/image-media-type.d.ts.map +1 -1
  713. package/dist/utils/invoke-user-callback.d.ts +0 -2
  714. package/dist/utils/invoke-user-callback.d.ts.map +1 -1
  715. package/dist/utils/json/is-json-equal.d.ts +0 -2
  716. package/dist/utils/json/is-json-equal.d.ts.map +1 -1
  717. package/dist/utils/json/is-json.d.ts +1 -3
  718. package/dist/utils/json/is-json.d.ts.map +1 -1
  719. package/dist/utils/json/stable-stringify-tool-args.d.ts +1 -3
  720. package/dist/utils/json/stable-stringify-tool-args.d.ts.map +1 -1
  721. package/dist/utils/normalizePartStatus.d.ts +2 -3
  722. package/dist/utils/normalizePartStatus.d.ts.map +1 -1
  723. package/dist/utils/normalizePartStatus.js +3 -2
  724. package/dist/utils/normalizePartStatus.js.map +1 -1
  725. package/dist/utils/notify-event-listeners.d.ts +1 -2
  726. package/dist/utils/notify-event-listeners.d.ts.map +1 -1
  727. package/dist/utils/record.d.ts +0 -2
  728. package/dist/utils/record.d.ts.map +1 -1
  729. package/dist/utils/silent-runtime-action.d.ts +0 -2
  730. package/dist/utils/silent-runtime-action.d.ts.map +1 -1
  731. package/dist/utils/text.d.ts +1 -3
  732. package/dist/utils/text.d.ts.map +1 -1
  733. package/dist/utils/wire-media.d.ts +0 -2
  734. package/dist/utils/wire-media.d.ts.map +1 -1
  735. package/package.json +11 -10
  736. package/src/adapters/attachment.test.ts +98 -1
  737. package/src/adapters/attachment.ts +70 -20
  738. package/src/adapters/speech.test.ts +94 -0
  739. package/src/adapters/speech.ts +16 -2
  740. package/src/adapters/thread-history.ts +16 -5
  741. package/src/adapters/voice.test.ts +169 -0
  742. package/src/adapters/voice.ts +25 -4
  743. package/src/index.ts +14 -2
  744. package/src/internal.ts +7 -0
  745. package/src/model-context/frame/host.test.ts +51 -1
  746. package/src/model-context/frame/host.ts +3 -1
  747. package/src/model-context/frame/provider.test.ts +61 -0
  748. package/src/model-context/frame/provider.ts +8 -1
  749. package/src/model-context/frame/validate.ts +57 -0
  750. package/src/model-context/interactable-composer-metadata.test.ts +82 -0
  751. package/src/model-context/interactable-composer-metadata.ts +45 -2
  752. package/src/react/adapters/LocalStorageThreadListAdapter.test.ts +594 -0
  753. package/src/react/adapters/LocalStorageThreadListAdapter.tsx +98 -35
  754. package/src/react/client/Interactables.test.ts +853 -1
  755. package/src/react/client/Interactables.ts +325 -45
  756. package/src/react/client/RemoteThreadList.initialize-failure.test.ts +124 -0
  757. package/src/react/client/RemoteThreadList.ts +6 -0
  758. package/src/react/client/Tools.ts +2 -3
  759. package/src/react/client/interactable-model-context.test.ts +56 -13
  760. package/src/react/client/interactable-model-context.ts +48 -23
  761. package/src/react/index.ts +2 -2
  762. package/src/react/interactables-legacy/Interactables.test.ts +212 -1
  763. package/src/react/interactables-legacy/Interactables.ts +25 -14
  764. package/src/react/interactables-legacy/interactable-model-context.ts +26 -24
  765. package/src/react/interactables-shared/useInteractablePersistenceQueue.test.tsx +22 -2
  766. package/src/react/interactables-shared/useInteractablePersistenceQueue.ts +63 -11
  767. package/src/react/primitive-hooks/useSuggestionTrigger.test.ts +33 -0
  768. package/src/react/primitive-hooks/useSuggestionTrigger.ts +7 -4
  769. package/src/react/primitives/generativeUI/GenerativeUI.test.tsx +124 -8
  770. package/src/react/primitives/generativeUI/GenerativeUI.tsx +35 -15
  771. package/src/react/primitives/message/MessageAttachments.test.tsx +2 -6
  772. package/src/react/primitives/message/MessageAttachments.tsx +14 -8
  773. package/src/react/primitives/message/MessageGroupedParts.test.tsx +151 -7
  774. package/src/react/primitives/message/MessageGroupedParts.tsx +19 -6
  775. package/src/react/primitives/message/MessageParts.test.tsx +84 -0
  776. package/src/react/primitives/message/MessageParts.tool-interaction.test.tsx +69 -0
  777. package/src/react/primitives/message/MessageParts.tsx +29 -8
  778. package/src/react/primitives/thread/ThreadSuggestions.test.tsx +97 -0
  779. package/src/react/primitives/thread/ThreadSuggestions.tsx +29 -6
  780. package/src/react/providers/ReadonlyThreadProvider.tsx +2 -2
  781. package/src/react/providers/TextMessagePartProvider.tsx +3 -0
  782. package/src/react/runtimes/RemoteThreadListHookInstanceManager.running.test.tsx +93 -0
  783. package/src/react/runtimes/RemoteThreadListHookInstanceManager.test.tsx +160 -3
  784. package/src/react/runtimes/RemoteThreadListHookInstanceManager.title.test.tsx +38 -0
  785. package/src/react/runtimes/RemoteThreadListHookInstanceManager.tsx +73 -53
  786. package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.tsx +19 -3
  787. package/src/react/runtimes/RemoteThreadResource.ts +5 -2
  788. package/src/react/runtimes/assistant-transport/replayBoundaryStream.test.ts +3 -1
  789. package/src/react/runtimes/assistant-transport/replayBoundaryStream.ts +10 -2
  790. package/src/react/runtimes/assistant-transport/runManager.ts +3 -0
  791. package/src/react/runtimes/assistant-transport/transport-parentid.test.tsx +57 -0
  792. package/src/react/runtimes/assistant-transport/transport-thread-id.test.tsx +571 -0
  793. package/src/react/runtimes/assistant-transport/types.ts +9 -1
  794. package/src/react/runtimes/assistant-transport/useAssistantTransport.spec.md +10 -0
  795. package/src/react/runtimes/assistant-transport/useAssistantTransportRuntime.test.tsx +42 -0
  796. package/src/react/runtimes/assistant-transport/useAssistantTransportRuntime.ts +33 -9
  797. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.copy.integration.test.tsx +289 -0
  798. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.copy.test.tsx +471 -0
  799. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.engagement.test.tsx +330 -0
  800. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.test.ts +78 -2
  801. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.test.tsx +361 -2
  802. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.ts +571 -216
  803. package/src/react/runtimes/cloud/CloudFileAttachmentAdapter.test.ts +25 -0
  804. package/src/react/runtimes/cloud/CloudFileAttachmentAdapter.ts +7 -3
  805. package/src/react/runtimes/cloud/auiV0.ts +158 -9
  806. package/src/react/runtimes/cloud/auiV0DecodeSafely.test.ts +352 -1
  807. package/src/react/runtimes/createMessageConverter.ts +9 -3
  808. package/src/react/runtimes/external-store-history-copy.ts +223 -0
  809. package/src/react/runtimes/useExternalStoreRuntime.history-copy.test.tsx +483 -0
  810. package/src/react/runtimes/useExternalStoreRuntime.lifecycle.test.tsx +76 -1
  811. package/src/react/runtimes/useExternalStoreRuntime.ts +25 -5
  812. package/src/react/runtimes/useLocalRuntime.test.tsx +145 -1
  813. package/src/react/runtimes/useLocalRuntime.ts +8 -5
  814. package/src/react/types/MessagePartComponentTypes.ts +10 -0
  815. package/src/react/types/scopes/interactables.ts +3 -0
  816. package/src/runtime/api/attachment-runtime.ts +10 -5
  817. package/src/runtime/api/bindings.ts +6 -1
  818. package/src/runtime/api/composer-runtime.ts +29 -6
  819. package/src/runtime/api/message-part-runtime.test.ts +215 -0
  820. package/src/runtime/api/message-part-runtime.ts +40 -0
  821. package/src/runtime/api/message-runtime.test.ts +112 -2
  822. package/src/runtime/api/message-runtime.ts +10 -5
  823. package/src/runtime/api/thread-list-item-runtime.ts +5 -5
  824. package/src/runtime/api/thread-list-runtime.ts +3 -3
  825. package/src/runtime/api/thread-runtime.test.ts +59 -0
  826. package/src/runtime/api/thread-runtime.ts +21 -11
  827. package/src/runtime/base/base-composer-runtime-core.ts +548 -153
  828. package/src/runtime/base/base-thread-runtime-core.test.ts +744 -9
  829. package/src/runtime/base/base-thread-runtime-core.ts +154 -27
  830. package/src/runtime/base/default-edit-composer-runtime-core.ts +13 -1
  831. package/src/runtime/base/default-thread-composer-runtime-core.ts +29 -10
  832. package/src/runtime/interfaces/composer-runtime-core.ts +19 -0
  833. package/src/runtime/interfaces/thread-runtime-core.ts +22 -1
  834. package/src/runtime/utils/attachment-add-operations.ts +45 -2
  835. package/src/runtime/utils/attachment-send-operations.ts +54 -0
  836. package/src/runtime/utils/auto-status.test.ts +54 -0
  837. package/src/runtime/utils/auto-status.ts +3 -10
  838. package/src/runtime/utils/external-message-conversion.test.ts +48 -0
  839. package/src/runtime/utils/external-message-conversion.ts +3 -4
  840. package/src/runtime/utils/message-repository.test.ts +69 -1
  841. package/src/runtime/utils/message-repository.ts +14 -7
  842. package/src/runtime/utils/stored-message-parts.ts +43 -1
  843. package/src/runtime/utils/thread-message-like.test.ts +58 -0
  844. package/src/runtime/utils/thread-message-like.ts +72 -46
  845. package/src/runtime/utils/thread-runtime-lifecycle.test.ts +67 -0
  846. package/src/runtime/utils/thread-runtime-lifecycle.ts +42 -8
  847. package/src/runtime/utils/tool-interactions.test.ts +193 -0
  848. package/src/runtime/utils/tool-interactions.ts +147 -0
  849. package/src/runtimes/external-store/external-store-adapter.ts +22 -1
  850. package/src/runtimes/external-store/external-store-runtime-core.ts +10 -2
  851. package/src/runtimes/external-store/external-store-thread-list-runtime-core.ts +2 -2
  852. package/src/runtimes/external-store/external-store-thread-runtime-core.test.ts +49 -0
  853. package/src/runtimes/external-store/external-store-thread-runtime-core.ts +76 -29
  854. package/src/runtimes/inert/InertThreadRuntimeCore.test.ts +127 -56
  855. package/src/runtimes/inert/InertThreadRuntimeCore.ts +10 -1
  856. package/src/runtimes/local/local-thread-runtime-core.edit-cancellation.test.ts +70 -0
  857. package/src/runtimes/local/local-thread-runtime-core.test.ts +1649 -108
  858. package/src/runtimes/local/local-thread-runtime-core.ts +301 -47
  859. package/src/runtimes/readonly/ReadonlyThreadRuntimeCore.ts +53 -1
  860. package/src/runtimes/remote-thread-list/optimistic-state.ts +8 -3
  861. package/src/runtimes/tool-invocations/EDGE_CASES.md +19 -4
  862. package/src/runtimes/tool-invocations/ToolInvocationTracker.test.ts +392 -39
  863. package/src/runtimes/tool-invocations/ToolInvocationTracker.ts +31 -8
  864. package/src/store/clients/external-thread.test.ts +145 -0
  865. package/src/store/clients/external-thread.ts +635 -131
  866. package/src/store/clients/model-context-client.test.ts +8 -8
  867. package/src/store/clients/no-op-composer-client.ts +2 -0
  868. package/src/store/clients/submission-message.ts +27 -0
  869. package/src/store/clients/thread-message-client.test.ts +39 -0
  870. package/src/store/clients/thread-message-client.ts +13 -6
  871. package/src/store/index.ts +2 -2
  872. package/src/store/internal.ts +1 -0
  873. package/src/store/primitive-predicates.test.ts +103 -0
  874. package/src/store/primitive-predicates.ts +15 -3
  875. package/src/store/runtime-clients/composer-runtime-client.ts +4 -3
  876. package/src/store/runtime-clients/message-part-runtime-client.test.ts +42 -0
  877. package/src/store/runtime-clients/message-part-runtime-client.ts +4 -0
  878. package/src/store/runtime-clients/message-runtime-client.ts +5 -0
  879. package/src/store/runtime-clients/thread-runtime-client.test.tsx +138 -5
  880. package/src/store/runtime-clients/thread-runtime-client.ts +46 -9
  881. package/src/store/scopes/composer.ts +22 -4
  882. package/src/store/scopes/message.ts +8 -0
  883. package/src/store/scopes/part.ts +8 -0
  884. package/src/subscribable/subscribable.test.ts +198 -1
  885. package/src/subscribable/subscribable.ts +49 -7
  886. package/src/tests/RemoteThreadListThreadListRuntimeCore-errors.test.ts +95 -1
  887. package/src/tests/auiV0Encode.test.ts +408 -1
  888. package/src/tests/base-composer-runtime-core-send.test.ts +722 -29
  889. package/src/tests/base-composer-runtime-core.test.ts +196 -0
  890. package/src/tests/composer-can-send.test.ts +98 -1
  891. package/src/tests/composer-submission.test.tsx +796 -0
  892. package/src/tests/default-edit-composer-runtime-core.test.ts +102 -2
  893. package/src/tests/external-store-thread-list-runtime-core.test.ts +111 -0
  894. package/src/tests/external-store-thread-runtime-core-adapter.test.ts +581 -1
  895. package/src/tests/external-store-thread-runtime-core.test.ts +186 -0
  896. package/src/tests/external-thread-attachments.test.tsx +741 -3
  897. package/src/tests/external-thread-isLast.test.tsx +69 -0
  898. package/src/tests/get-thread-state-isRunning.test.ts +2 -2
  899. package/src/tests/remote-thread-list-adapter-switch.test.ts +45 -1
  900. package/src/types/message.ts +49 -2
  901. package/src/utils/normalizePartStatus.ts +14 -1
  902. package/dist/tests/remote-thread-list-test-helpers.d.ts +0 -15
  903. package/dist/tests/remote-thread-list-test-helpers.d.ts.map +0 -1
  904. package/dist/tests/remote-thread-list-test-helpers.js +0 -59
  905. package/dist/tests/remote-thread-list-test-helpers.js.map +0 -1
@@ -5,7 +5,11 @@ import type {
5
5
  ChatModelRunOptions,
6
6
  ChatModelRunResult,
7
7
  } from "../../runtime/utils/chat-model-adapter";
8
- import type { AppendMessage, ToolCallMessagePart } from "../../types/message";
8
+ import type {
9
+ AppendMessage,
10
+ ThreadMessage,
11
+ ToolCallMessagePart,
12
+ } from "../../types/message";
9
13
  import type { ThreadHistoryAdapter } from "../../adapters/thread-history";
10
14
  import type { LocalRuntimeOptionsBase } from "./local-runtime-options";
11
15
  import {
@@ -151,176 +155,1330 @@ describe("LocalThreadRuntimeCore events", () => {
151
155
  });
152
156
 
153
157
  describe("LocalThreadRuntimeCore history persistence", () => {
154
- it("surfaces failed user persistence without abandoning the run", async () => {
155
- const persistenceError = new Error("history unavailable");
156
- const run = vi.fn(async () => ({
157
- content: [{ type: "text" as const, text: "done" }],
158
- }));
159
- const append = vi.fn(async (item: ExportedMessageRepositoryItem) => {
160
- if (item.message.role === "user") throw persistenceError;
158
+ it("keeps streaming after feedback arrives and persists the completed message", async () => {
159
+ let releaseStream!: () => void;
160
+ const gate = new Promise<void>((resolve) => {
161
+ releaseStream = resolve;
161
162
  });
163
+ const appendHistory = vi.fn(
164
+ async (_item: ExportedMessageRepositoryItem) => {},
165
+ );
162
166
  const thread = createThread(
163
- { run },
167
+ {
168
+ async *run() {
169
+ yield { content: [{ type: "text", text: "a" }] };
170
+ await gate;
171
+ yield { content: [{ type: "text", text: "ab" }] };
172
+ },
173
+ },
164
174
  {
165
175
  history: {
166
176
  async load() {
167
177
  return { messages: [] };
168
178
  },
169
- append,
179
+ append: appendHistory,
180
+ async update() {},
170
181
  },
171
182
  },
172
183
  );
173
184
 
174
- await expect(thread.append(userMessage("hello"))).rejects.toBe(
175
- persistenceError,
185
+ const send = thread.append(userMessage("hello"));
186
+ await vi.waitFor(() =>
187
+ expect(thread.messages.at(-1)?.content).toHaveLength(1),
176
188
  );
189
+ const messageId = thread.messages.at(-1)!.id;
190
+ thread.submitFeedback({ messageId, type: "positive" });
191
+ releaseStream();
192
+ await send;
177
193
 
178
- expect(append).toHaveBeenCalledTimes(2);
179
- expect(run).toHaveBeenCalledOnce();
194
+ const assistant = thread.messages.at(-1);
195
+ expect(assistant?.status?.type).toBe("complete");
196
+ expect(assistant?.content).toEqual([{ type: "text", text: "ab" }]);
197
+ expect(assistant?.metadata.submittedFeedback).toEqual({
198
+ type: "positive",
199
+ });
200
+ expect(appendHistory.mock.calls.at(-1)?.[0].message).toEqual(assistant);
180
201
  });
181
202
 
182
- it("surfaces failed persistence for messages that do not start a run", async () => {
183
- const persistenceError = new Error("history unavailable");
184
- const run = vi.fn();
203
+ it("persists the completed message when feedback lands before the adapter returns", async () => {
204
+ let releaseTeardown!: () => void;
205
+ const teardown = new Promise<void>((resolve) => {
206
+ releaseTeardown = resolve;
207
+ });
208
+ const appendHistory = vi.fn(
209
+ async (_item: ExportedMessageRepositoryItem) => {},
210
+ );
185
211
  const thread = createThread(
186
- { run },
212
+ {
213
+ async *run() {
214
+ yield {
215
+ content: [{ type: "text", text: "done" }],
216
+ status: { type: "complete", reason: "stop" },
217
+ } satisfies ChatModelRunResult;
218
+ await teardown;
219
+ },
220
+ },
187
221
  {
188
222
  history: {
189
223
  async load() {
190
224
  return { messages: [] };
191
225
  },
192
- async append() {
193
- throw persistenceError;
194
- },
226
+ append: appendHistory,
227
+ async update() {},
195
228
  },
196
229
  },
197
230
  );
198
231
 
199
- await expect(
200
- thread.append({ ...userMessage("hello"), startRun: false }),
201
- ).rejects.toBe(persistenceError);
232
+ const send = thread.append(userMessage("hello"));
233
+ await vi.waitFor(() =>
234
+ expect(thread.messages.at(-1)?.content).toEqual([
235
+ { type: "text", text: "done" },
236
+ ]),
237
+ );
238
+ thread.submitFeedback({
239
+ messageId: thread.messages.at(-1)!.id,
240
+ type: "positive",
241
+ });
242
+ releaseTeardown();
243
+ await send;
202
244
 
203
- expect(run).not.toHaveBeenCalled();
245
+ const assistant = thread.messages.at(-1);
246
+ expect(assistant?.metadata.submittedFeedback).toEqual({
247
+ type: "positive",
248
+ });
249
+ expect(appendHistory.mock.calls.at(-1)?.[0].message).toEqual(assistant);
204
250
  });
205
251
 
206
- it("handles failed persistence before notifying no-run subscribers", async () => {
207
- const persistenceError = new Error("history unavailable");
208
- const listenerError = new Error("listener unavailable");
252
+ it.each(["feedback", "tool result"])(
253
+ "keeps streaming when %s arrives first and the other writer follows",
254
+ async (first) => {
255
+ let releaseStream!: () => void;
256
+ const gate = new Promise<void>((resolve) => {
257
+ releaseStream = resolve;
258
+ });
259
+ const thread = createThread({
260
+ async *run() {
261
+ yield { content: [toolCallPart("lookup_weather")] };
262
+ await gate;
263
+ yield {
264
+ content: [
265
+ toolCallPart("lookup_weather"),
266
+ { type: "text", text: "It is sunny." },
267
+ ],
268
+ } satisfies ChatModelRunResult;
269
+ },
270
+ });
271
+
272
+ const send = thread.append(userMessage("weather"));
273
+ await vi.waitFor(() =>
274
+ expect(thread.messages.at(-1)?.content).toHaveLength(1),
275
+ );
276
+ const messageId = thread.messages.at(-1)!.id;
277
+ const submitFeedback = () =>
278
+ thread.submitFeedback({ messageId, type: "positive" });
279
+ const addToolResult = () =>
280
+ thread.addToolResult({
281
+ messageId,
282
+ toolCallId: "call-lookup_weather",
283
+ toolName: "lookup_weather",
284
+ result: { temperature: 21 },
285
+ isError: false,
286
+ });
287
+ if (first === "feedback") {
288
+ submitFeedback();
289
+ addToolResult();
290
+ } else {
291
+ addToolResult();
292
+ submitFeedback();
293
+ }
294
+ releaseStream();
295
+ await send;
296
+
297
+ const assistant = thread.messages.at(-1);
298
+ expect(assistant?.status?.type).toBe("complete");
299
+ expect(assistant?.content).toEqual([
300
+ expect.objectContaining({ result: { temperature: 21 } }),
301
+ { type: "text", text: "It is sunny." },
302
+ ]);
303
+ expect(assistant?.metadata.submittedFeedback).toEqual({
304
+ type: "positive",
305
+ });
306
+ },
307
+ );
308
+
309
+ it("keeps streaming after a tool result arrives and persists the completed message", async () => {
310
+ let releaseStream!: () => void;
311
+ const gate = new Promise<void>((resolve) => {
312
+ releaseStream = resolve;
313
+ });
314
+ const appendHistory = vi.fn(
315
+ async (_item: ExportedMessageRepositoryItem) => {},
316
+ );
209
317
  const thread = createThread(
210
- { run: vi.fn() },
318
+ {
319
+ async *run() {
320
+ yield { content: [toolCallPart("lookup_weather")] };
321
+ await gate;
322
+ yield {
323
+ content: [
324
+ {
325
+ ...toolCallPart("lookup_weather"),
326
+ result: { temperature: 20 },
327
+ isPreliminary: true,
328
+ artifact: { preview: true },
329
+ modelContent: [{ type: "text", text: "preview" }],
330
+ },
331
+ { type: "text", text: "It is sunny." },
332
+ ],
333
+ } satisfies ChatModelRunResult;
334
+ },
335
+ },
211
336
  {
212
337
  history: {
213
338
  async load() {
214
339
  return { messages: [] };
215
340
  },
216
- async append() {
217
- throw persistenceError;
218
- },
341
+ append: appendHistory,
342
+ async update() {},
219
343
  },
220
344
  },
221
345
  );
222
- thread.subscribe(() => {
223
- throw listenerError;
346
+
347
+ const send = thread.append(userMessage("weather"));
348
+ await vi.waitFor(() =>
349
+ expect(thread.messages.at(-1)?.content).toHaveLength(1),
350
+ );
351
+ const messageId = thread.messages.at(-1)!.id;
352
+ thread.addToolResult({
353
+ messageId,
354
+ toolCallId: "call-lookup_weather",
355
+ toolName: "lookup_weather",
356
+ result: { temperature: 21 },
357
+ isError: false,
224
358
  });
359
+ releaseStream();
360
+ await send;
225
361
 
226
- await expect(
227
- thread.append({ ...userMessage("hello"), startRun: false }),
228
- ).rejects.toBe(listenerError);
229
- await flush();
362
+ const assistant = thread.messages.at(-1);
363
+ expect(assistant?.status?.type).toBe("complete");
364
+ expect(assistant?.content).toEqual([
365
+ expect.objectContaining({ result: { temperature: 21 } }),
366
+ { type: "text", text: "It is sunny." },
367
+ ]);
368
+ expect(assistant?.content[0]).not.toHaveProperty("artifact");
369
+ expect(assistant?.content[0]).not.toHaveProperty("modelContent");
370
+ expect(appendHistory.mock.calls.at(-1)?.[0].message).toEqual(assistant);
230
371
  });
231
- });
232
372
 
233
- describe("LocalThreadRuntimeCore - detach", () => {
234
- it("drops a pending append when detached", async () => {
235
- let resolveInitialization!: () => void;
236
- const initialization = new Promise<void>((resolve) => {
237
- resolveInitialization = resolve;
373
+ it("does not settle a preliminary tool result from a later stream chunk", async () => {
374
+ const thread = createThread(
375
+ {
376
+ async *run() {
377
+ yield {
378
+ content: [
379
+ {
380
+ ...toolCallPart("lookup_weather"),
381
+ result: { temperature: 20 },
382
+ isPreliminary: true,
383
+ },
384
+ ],
385
+ };
386
+ yield { content: [toolCallPart("lookup_weather")] };
387
+ },
388
+ },
389
+ { maxSteps: 1 },
390
+ );
391
+
392
+ await thread.append(userMessage("weather"));
393
+
394
+ expect(thread.messages.at(-1)?.content[0]).not.toHaveProperty("result");
395
+ expect(thread.messages.at(-1)?.content[0]).not.toHaveProperty(
396
+ "isPreliminary",
397
+ );
398
+ });
399
+
400
+ it.each([false, true])(
401
+ "does not carry adapter-only results into a later snapshot (preliminary: %s)",
402
+ async (preliminary) => {
403
+ const nextPart = {
404
+ ...toolCallPart("lookup_weather"),
405
+ ...(preliminary && {
406
+ result: "preview",
407
+ isPreliminary: true,
408
+ artifact: { preview: true },
409
+ modelContent: [{ type: "text" as const, text: "preview" }],
410
+ }),
411
+ };
412
+ const thread = createThread({
413
+ async *run() {
414
+ yield {
415
+ content: [{ ...toolCallPart("lookup_weather"), result: "first" }],
416
+ };
417
+ yield { content: [nextPart] };
418
+ },
419
+ });
420
+
421
+ await thread.append(userMessage("weather"));
422
+
423
+ expect(thread.messages.at(-1)?.content).toEqual([nextPart]);
424
+ },
425
+ );
426
+
427
+ it("keeps external results matched to their own tool-call occurrence", async () => {
428
+ let releaseStream!: () => void;
429
+ const gate = new Promise<void>((resolve) => {
430
+ releaseStream = resolve;
238
431
  });
239
- const run = vi.fn(async () => ({
240
- content: [{ type: "text" as const, text: "done" }],
241
- }));
242
- const thread = createThread({ run });
243
- thread.__internal_setGetInitializePromise(() => initialization);
432
+ const thread = createThread(
433
+ {
434
+ async *run() {
435
+ yield {
436
+ content: [
437
+ toolCallPart("lookup_weather"),
438
+ { ...toolCallPart("lookup_time"), result: "noon" },
439
+ ],
440
+ };
441
+ await gate;
442
+ yield {
443
+ content: [
444
+ toolCallPart("lookup_weather"),
445
+ toolCallPart("lookup_weather"),
446
+ toolCallPart("lookup_time"),
447
+ ],
448
+ };
449
+ yield {
450
+ content: [
451
+ toolCallPart("lookup_weather"),
452
+ toolCallPart("lookup_weather"),
453
+ toolCallPart("lookup_time"),
454
+ { type: "text", text: "done" },
455
+ ],
456
+ };
457
+ },
458
+ },
459
+ { maxSteps: 1 },
460
+ );
244
461
 
245
- const appendPromise = thread.append(userMessage("hello"));
246
- await Promise.resolve();
247
- thread.detach();
248
- resolveInitialization();
462
+ const send = thread.append(userMessage("weather"));
463
+ await vi.waitFor(() =>
464
+ expect(thread.messages.at(-1)?.content).toHaveLength(2),
465
+ );
466
+ thread.addToolResult({
467
+ messageId: thread.messages.at(-1)!.id,
468
+ toolCallId: "call-lookup_weather",
469
+ toolName: "lookup_weather",
470
+ result: "first",
471
+ isError: false,
472
+ });
473
+ releaseStream();
474
+ await send;
249
475
 
250
- await appendPromise;
251
- expect(run).not.toHaveBeenCalled();
252
- expect(thread.messages).toEqual([]);
476
+ expect(thread.messages.at(-1)?.content[0]).toMatchObject({
477
+ result: "first",
478
+ });
479
+ expect(thread.messages.at(-1)?.content[1]).not.toHaveProperty("result");
480
+ expect(thread.messages.at(-1)?.content[2]).not.toHaveProperty("result");
253
481
  });
254
- });
255
482
 
256
- describe("LocalThreadRuntimeCore optimistic append", () => {
257
- it("paints the appended message before initialization resolves", async () => {
258
- let resolveInitialization!: () => void;
259
- const initialization = new Promise<void>((resolve) => {
260
- resolveInitialization = resolve;
483
+ it("keeps an external result on a tool call id reused by a later roundtrip", async () => {
484
+ let releaseStream!: () => void;
485
+ const gate = new Promise<void>((resolve) => {
486
+ releaseStream = resolve;
487
+ });
488
+ let calls = 0;
489
+ const thread = createThread({
490
+ async *run() {
491
+ calls++;
492
+ if (calls === 1) {
493
+ yield {
494
+ content: [{ ...toolCallPart("lookup_weather"), result: "first" }],
495
+ status: { type: "requires-action", reason: "tool-calls" },
496
+ } satisfies ChatModelRunResult;
497
+ return;
498
+ }
499
+ yield { content: [toolCallPart("lookup_weather")] };
500
+ await gate;
501
+ yield { content: [toolCallPart("lookup_weather")] };
502
+ yield {
503
+ content: [
504
+ toolCallPart("lookup_weather"),
505
+ { type: "text", text: "done" },
506
+ ],
507
+ };
508
+ },
261
509
  });
262
- const run = vi.fn(async () => ({
263
- content: [{ type: "text" as const, text: "done" }],
264
- }));
265
- const thread = createThread({ run });
266
- thread.__internal_setGetInitializePromise(() => initialization);
267
- const onUpdate = vi.fn();
268
- thread.subscribe(onUpdate);
269
510
 
270
- const appendPromise = thread.append(userMessage("hello"));
271
- await Promise.resolve();
511
+ const send = thread.append(userMessage("weather"));
512
+ await vi.waitFor(() =>
513
+ expect(thread.messages.at(-1)?.content).toHaveLength(2),
514
+ );
515
+ thread.addToolResult({
516
+ messageId: thread.messages.at(-1)!.id,
517
+ toolCallId: "call-lookup_weather",
518
+ toolName: "lookup_weather",
519
+ result: "second",
520
+ isError: false,
521
+ });
522
+ releaseStream();
523
+ await send;
272
524
 
273
- expect(thread.messages).toHaveLength(1);
274
- expect(thread.messages[0]?.role).toBe("user");
275
- expect(onUpdate).toHaveBeenCalled();
276
- expect(run).not.toHaveBeenCalled();
525
+ expect(thread.messages.at(-1)?.content).toEqual([
526
+ expect.objectContaining({ result: "first" }),
527
+ expect.objectContaining({ result: "second" }),
528
+ { type: "text", text: "done" },
529
+ ]);
530
+ });
277
531
 
278
- resolveInitialization();
279
- await appendPromise;
280
- expect(run).toHaveBeenCalledTimes(1);
532
+ it("records an interaction on a settled tool call and persists it", async () => {
533
+ const update = vi.fn(async (_item: ExportedMessageRepositoryItem) => {});
534
+ const interaction = {
535
+ type: "action" as const,
536
+ occurredAt: 1,
537
+ payload: { $input: "confirm" },
538
+ };
539
+ const thread = createThread(
540
+ {
541
+ async run() {
542
+ return { content: [toolCallPart("lookup_weather")] };
543
+ },
544
+ },
545
+ {
546
+ history: {
547
+ async load() {
548
+ return { messages: [] };
549
+ },
550
+ async append() {},
551
+ update,
552
+ },
553
+ },
554
+ );
555
+
556
+ await thread.append(userMessage("what is the weather"));
557
+ const assistantMessage = thread.messages.at(-1)!;
558
+
559
+ await thread.unstable_recordToolInteraction({
560
+ messageId: assistantMessage.id,
561
+ toolCallId: "call-lookup_weather",
562
+ interaction,
563
+ });
564
+
565
+ expect(thread.messages.at(-1)?.content[0]).toMatchObject({
566
+ unstable_interactions: { entries: [interaction] },
567
+ });
568
+ await vi.waitFor(() => expect(update).toHaveBeenCalledOnce());
569
+ expect(update.mock.calls[0]?.[0].message.content[0]).toMatchObject({
570
+ unstable_interactions: { entries: [interaction] },
571
+ });
281
572
  });
282
573
 
283
- it("silently drops a detached append even when initialization rejects", async () => {
284
- let rejectInitialization!: (error: unknown) => void;
285
- const initialization = new Promise<void>((_, reject) => {
286
- rejectInitialization = reject;
574
+ it("keeps a recorded interaction through the next streamed update", async () => {
575
+ let releaseStream!: () => void;
576
+ const gate = new Promise<void>((resolve) => {
577
+ releaseStream = resolve;
578
+ });
579
+ const append = vi.fn(async (_item: ExportedMessageRepositoryItem) => {});
580
+ const interaction = {
581
+ type: "human-response" as const,
582
+ occurredAt: 1,
583
+ payload: "confirm",
584
+ };
585
+ const thread = createThread(
586
+ {
587
+ async *run() {
588
+ yield { content: [toolCallPart("lookup_weather")] };
589
+ await gate;
590
+ yield {
591
+ content: [
592
+ toolCallPart("lookup_weather"),
593
+ { type: "text", text: "It is sunny." },
594
+ ],
595
+ };
596
+ },
597
+ },
598
+ {
599
+ history: {
600
+ async load() {
601
+ return { messages: [] };
602
+ },
603
+ append,
604
+ },
605
+ },
606
+ );
607
+
608
+ const send = thread.append(userMessage("what is the weather"));
609
+ await vi.waitFor(() =>
610
+ expect(thread.messages.at(-1)?.content).toHaveLength(1),
611
+ );
612
+ const messageId = thread.messages.at(-1)!.id;
613
+
614
+ await thread.unstable_recordToolInteraction({
615
+ messageId,
616
+ toolCallId: "call-lookup_weather",
617
+ interaction,
618
+ });
619
+ expect(thread.messages.at(-1)?.content[0]).toMatchObject({
620
+ unstable_interactions: { entries: [interaction] },
621
+ });
622
+
623
+ releaseStream();
624
+ await send;
625
+
626
+ expect(thread.messages.at(-1)?.content[0]).toMatchObject({
627
+ unstable_interactions: { entries: [interaction] },
628
+ });
629
+ expect(append.mock.calls.at(-1)?.[0].message.content[0]).toMatchObject({
630
+ unstable_interactions: { entries: [interaction] },
631
+ });
632
+ });
633
+
634
+ it("rejects interactions for unknown messages or tool calls", async () => {
635
+ const thread = createThread({
636
+ async run() {
637
+ return { content: [toolCallPart("lookup_weather")] };
638
+ },
639
+ });
640
+ const interaction = {
641
+ type: "action" as const,
642
+ occurredAt: 1,
643
+ payload: { $input: "confirm" },
644
+ };
645
+
646
+ await thread.append(userMessage("what is the weather"));
647
+ const assistantMessage = thread.messages.at(-1)!;
648
+
649
+ await expect(
650
+ thread.unstable_recordToolInteraction({
651
+ messageId: "missing",
652
+ toolCallId: "call-lookup_weather",
653
+ interaction,
654
+ }),
655
+ ).rejects.toThrow("non-existing message");
656
+ await expect(
657
+ thread.unstable_recordToolInteraction({
658
+ messageId: assistantMessage.id,
659
+ toolCallId: "missing",
660
+ interaction,
661
+ }),
662
+ ).rejects.toThrow("non-existing tool call");
663
+ });
664
+
665
+ it("excludes recorded interactions from model input", async () => {
666
+ const runs: ChatModelRunOptions[] = [];
667
+ const interaction = {
668
+ type: "action" as const,
669
+ occurredAt: 1,
670
+ payload: { $input: "confirm" },
671
+ };
672
+ const nestedMessage: ThreadMessage = {
673
+ id: "nested-message",
674
+ role: "assistant",
675
+ createdAt: new Date(0),
676
+ content: [
677
+ {
678
+ ...toolCallPart("nested_weather"),
679
+ unstable_interactions: { entries: [interaction] },
680
+ },
681
+ ],
682
+ status: { type: "complete", reason: "stop" },
683
+ metadata: {
684
+ unstable_state: null,
685
+ unstable_annotations: [],
686
+ unstable_data: [],
687
+ steps: [],
688
+ custom: {},
689
+ },
690
+ };
691
+ const thread = createThread({
692
+ async run(options) {
693
+ runs.push(options);
694
+ return runs.length === 1
695
+ ? {
696
+ content: [
697
+ {
698
+ ...toolCallPart("lookup_weather"),
699
+ messages: [nestedMessage],
700
+ },
701
+ ],
702
+ }
703
+ : { content: [{ type: "text", text: "done" }] };
704
+ },
705
+ });
706
+
707
+ await thread.append(userMessage("what is the weather"));
708
+ const assistantMessage = thread.messages.at(-1)!;
709
+ await thread.unstable_recordToolInteraction({
710
+ messageId: assistantMessage.id,
711
+ toolCallId: "call-lookup_weather",
712
+ interaction,
713
+ });
714
+ await thread.append({
715
+ ...userMessage("and tomorrow"),
716
+ parentId: assistantMessage.id,
717
+ });
718
+
719
+ const modelToolCall = runs[1]?.messages
720
+ .find((message) => message.role === "assistant")
721
+ ?.content.find((part) => part.type === "tool-call");
722
+ expect(modelToolCall).not.toHaveProperty("unstable_interactions");
723
+ expect(modelToolCall?.messages?.[0]?.content[0]).not.toHaveProperty(
724
+ "unstable_interactions",
725
+ );
726
+ });
727
+
728
+ it("exposes a tool result added mid-run to the adapter before its next chunk", async () => {
729
+ let releaseStream!: () => void;
730
+ const gate = new Promise<void>((resolve) => {
731
+ releaseStream = resolve;
732
+ });
733
+ let seen: unknown;
734
+ const thread = createThread({
735
+ async *run({ unstable_getMessage }) {
736
+ yield { content: [toolCallPart("lookup_weather")] };
737
+ await gate;
738
+ seen = unstable_getMessage().content;
739
+ yield {
740
+ content: [
741
+ toolCallPart("lookup_weather"),
742
+ { type: "text", text: "done" },
743
+ ],
744
+ };
745
+ },
746
+ });
747
+
748
+ const send = thread.append(userMessage("weather"));
749
+ await vi.waitFor(() =>
750
+ expect(thread.messages.at(-1)?.content).toHaveLength(1),
751
+ );
752
+ thread.addToolResult({
753
+ messageId: thread.messages.at(-1)!.id,
754
+ toolCallId: "call-lookup_weather",
755
+ toolName: "lookup_weather",
756
+ result: { temperature: 21 },
757
+ isError: false,
758
+ });
759
+ releaseStream();
760
+ await send;
761
+
762
+ expect(seen).toEqual([
763
+ expect.objectContaining({ result: { temperature: 21 } }),
764
+ ]);
765
+ });
766
+
767
+ it("carries multiple external results through a replacement chain", async () => {
768
+ let releaseStream!: () => void;
769
+ const gate = new Promise<void>((resolve) => {
770
+ releaseStream = resolve;
771
+ });
772
+ const thread = createThread({
773
+ async *run() {
774
+ const content = [
775
+ toolCallPart("lookup_weather"),
776
+ toolCallPart("lookup_time"),
777
+ ];
778
+ yield { content };
779
+ await gate;
780
+ yield { content: [...content, { type: "text", text: "done" }] };
781
+ },
782
+ });
783
+
784
+ const send = thread.append(userMessage("weather and time"));
785
+ await vi.waitFor(() =>
786
+ expect(thread.messages.at(-1)?.content).toHaveLength(2),
787
+ );
788
+ const messageId = thread.messages.at(-1)!.id;
789
+ for (const toolName of ["lookup_weather", "lookup_time"]) {
790
+ thread.addToolResult({
791
+ messageId,
792
+ toolCallId: `call-${toolName}`,
793
+ toolName,
794
+ result: `${toolName} result`,
795
+ isError: false,
796
+ artifact: { toolName },
797
+ modelContent: [{ type: "text", text: toolName }],
798
+ });
799
+ }
800
+ releaseStream();
801
+ await send;
802
+
803
+ expect(thread.messages.at(-1)?.content).toEqual([
804
+ ...["lookup_weather", "lookup_time"].map((toolName) => ({
805
+ ...toolCallPart(toolName),
806
+ result: `${toolName} result`,
807
+ isError: false,
808
+ artifact: { toolName },
809
+ modelContent: [{ type: "text", text: toolName }],
810
+ })),
811
+ { type: "text", text: "done" },
812
+ ]);
813
+ });
814
+
815
+ it("persists a tool result added after a completed message", async () => {
816
+ const update = vi.fn(async (_item: ExportedMessageRepositoryItem) => {});
817
+ const thread = createThread(
818
+ {
819
+ async run() {
820
+ return { content: [toolCallPart("lookup_weather")] };
821
+ },
822
+ },
823
+ {
824
+ history: {
825
+ async load() {
826
+ return { messages: [] };
827
+ },
828
+ async append() {},
829
+ update,
830
+ },
831
+ },
832
+ );
833
+
834
+ await thread.append(userMessage("what is the weather"));
835
+ const assistantMessage = thread.messages.at(-1)!;
836
+ expect(assistantMessage.status?.type).toBe("complete");
837
+
838
+ thread.addToolResult({
839
+ messageId: assistantMessage.id,
840
+ toolCallId: "call-lookup_weather",
841
+ toolName: "lookup_weather",
842
+ result: { temperature: 21 },
843
+ isError: false,
844
+ });
845
+ await flush();
846
+
847
+ expect(update).toHaveBeenCalledOnce();
848
+ expect(update.mock.calls[0]?.[0].message.content).toEqual([
849
+ expect.objectContaining({
850
+ result: { temperature: 21 },
851
+ isError: false,
852
+ }),
853
+ ]);
854
+ });
855
+
856
+ it("persists a tool result added after an incomplete message", async () => {
857
+ const update = vi.fn(async (_item: ExportedMessageRepositoryItem) => {});
858
+ const thread = createThread(
859
+ {
860
+ async run() {
861
+ return {
862
+ content: [toolCallPart("lookup_weather")],
863
+ status: { type: "incomplete", reason: "cancelled" },
864
+ };
865
+ },
866
+ },
867
+ {
868
+ history: {
869
+ async load() {
870
+ return { messages: [] };
871
+ },
872
+ async append() {},
873
+ update,
874
+ },
875
+ },
876
+ );
877
+
878
+ await thread.append(userMessage("what is the weather"));
879
+ const assistantMessage = thread.messages.at(-1)!;
880
+ expect(assistantMessage.status?.type).toBe("incomplete");
881
+
882
+ thread.addToolResult({
883
+ messageId: assistantMessage.id,
884
+ toolCallId: "call-lookup_weather",
885
+ toolName: "lookup_weather",
886
+ result: { temperature: 21 },
887
+ isError: false,
888
+ });
889
+ await flush();
890
+
891
+ expect(update).toHaveBeenCalledOnce();
892
+ expect(update.mock.calls[0]?.[0].message.content).toEqual([
893
+ expect.objectContaining({ result: { temperature: 21 } }),
894
+ ]);
895
+ });
896
+
897
+ it("persists a tool result added after later turns follow its message", async () => {
898
+ const update = vi.fn(async (_item: ExportedMessageRepositoryItem) => {});
899
+ const thread = createThread(
900
+ {
901
+ async run() {
902
+ return { content: [toolCallPart("lookup_weather")] };
903
+ },
904
+ },
905
+ {
906
+ history: {
907
+ async load() {
908
+ return { messages: [] };
909
+ },
910
+ async append() {},
911
+ update,
912
+ },
913
+ },
914
+ );
915
+
916
+ await thread.append(userMessage("what is the weather"));
917
+ const [question, answer] = thread.messages;
918
+ await thread.append({
919
+ ...userMessage("and tomorrow?"),
920
+ parentId: answer!.id,
921
+ });
922
+ expect(thread.messages).toHaveLength(4);
923
+
924
+ thread.addToolResult({
925
+ messageId: answer!.id,
926
+ toolCallId: "call-lookup_weather",
927
+ toolName: "lookup_weather",
928
+ result: { temperature: 21 },
929
+ isError: false,
930
+ });
931
+ await flush();
932
+
933
+ expect(update).toHaveBeenCalledOnce();
934
+ expect(update.mock.calls[0]?.[0]).toMatchObject({
935
+ parentId: question!.id,
936
+ message: {
937
+ id: answer!.id,
938
+ content: [expect.objectContaining({ result: { temperature: 21 } })],
939
+ },
940
+ });
941
+ });
942
+
943
+ it("writes a result a subscriber adds in response after the late result", async () => {
944
+ const update = vi.fn(async (_item: ExportedMessageRepositoryItem) => {});
945
+ const thread = createThread(
946
+ {
947
+ async run() {
948
+ return {
949
+ content: [
950
+ toolCallPart("lookup_weather"),
951
+ toolCallPart("lookup_time"),
952
+ ],
953
+ };
954
+ },
955
+ },
956
+ {
957
+ history: {
958
+ async load() {
959
+ return { messages: [] };
960
+ },
961
+ async append() {},
962
+ update,
963
+ },
964
+ },
965
+ );
966
+
967
+ await thread.append(userMessage("weather and time"));
968
+ const assistantMessage = thread.messages.at(-1)!;
969
+ expect(assistantMessage.status?.type).toBe("complete");
970
+
971
+ const unsubscribe = thread.subscribe(() => {
972
+ const message = thread.messages.find((m) => m.id === assistantMessage.id);
973
+ const [weather, time] = message?.content ?? [];
974
+ if (
975
+ weather?.type === "tool-call" &&
976
+ weather.result !== undefined &&
977
+ time?.type === "tool-call" &&
978
+ time.result === undefined
979
+ ) {
980
+ thread.addToolResult({
981
+ messageId: assistantMessage.id,
982
+ toolCallId: "call-lookup_time",
983
+ toolName: "lookup_time",
984
+ result: { hour: 9 },
985
+ isError: false,
986
+ });
987
+ }
988
+ });
989
+ thread.addToolResult({
990
+ messageId: assistantMessage.id,
991
+ toolCallId: "call-lookup_weather",
992
+ toolName: "lookup_weather",
993
+ result: { temperature: 21 },
994
+ isError: false,
995
+ });
996
+ unsubscribe();
997
+ await flush();
998
+
999
+ expect(update).toHaveBeenCalledTimes(2);
1000
+ expect(update.mock.calls.at(-1)?.[0].message.content).toEqual([
1001
+ expect.objectContaining({ result: { temperature: 21 } }),
1002
+ expect.objectContaining({ result: { hour: 9 } }),
1003
+ ]);
1004
+ });
1005
+
1006
+ it("writes a decision a subscriber adds in response after the one it answers", async () => {
1007
+ const update = vi.fn(async (_item: ExportedMessageRepositoryItem) => {});
1008
+ let runs = 0;
1009
+ const thread = createThread(
1010
+ {
1011
+ async run() {
1012
+ runs++;
1013
+ if (runs > 1) return { content: [{ type: "text", text: "ok" }] };
1014
+ return {
1015
+ content: [
1016
+ {
1017
+ ...toolCallPart("send_email", { id: "a1" }),
1018
+ toolCallId: "call-1",
1019
+ },
1020
+ {
1021
+ ...toolCallPart("send_email", { id: "a2" }),
1022
+ toolCallId: "call-2",
1023
+ },
1024
+ ],
1025
+ status: { type: "requires-action", reason: "tool-calls" },
1026
+ };
1027
+ },
1028
+ },
1029
+ {
1030
+ history: {
1031
+ async load() {
1032
+ return { messages: [] };
1033
+ },
1034
+ async append() {},
1035
+ update,
1036
+ },
1037
+ },
1038
+ );
1039
+
1040
+ await thread.append(userMessage("send two emails"));
1041
+ const paused = thread.messages.at(-1)!;
1042
+ await thread.append({ ...userMessage("and then"), parentId: paused.id });
1043
+ await flush();
1044
+
1045
+ const unsubscribe = thread.subscribe(() => {
1046
+ const message = thread.messages.find((m) => m.id === paused.id);
1047
+ const [first, second] = message?.content ?? [];
1048
+ if (
1049
+ first?.type === "tool-call" &&
1050
+ first.approval?.approved !== undefined &&
1051
+ second?.type === "tool-call" &&
1052
+ second.approval?.approved === undefined
1053
+ ) {
1054
+ void thread.respondToToolApproval({ approvalId: "a2", approved: true });
1055
+ }
1056
+ });
1057
+ void thread.respondToToolApproval({ approvalId: "a1", approved: true });
1058
+ unsubscribe();
1059
+ await flush();
1060
+
1061
+ expect(update).toHaveBeenCalledTimes(2);
1062
+ expect(update.mock.calls.at(-1)?.[0].message.content).toEqual([
1063
+ expect.objectContaining({
1064
+ approval: expect.objectContaining({ approved: true }),
1065
+ }),
1066
+ expect.objectContaining({
1067
+ approval: expect.objectContaining({ approved: true }),
1068
+ }),
1069
+ ]);
1070
+ });
1071
+
1072
+ it("does not rewrite a message whose run is still streaming", async () => {
1073
+ const update = vi.fn(async (_item: ExportedMessageRepositoryItem) => {});
1074
+ let release!: () => void;
1075
+ const released = new Promise<void>((resolve) => {
1076
+ release = resolve;
1077
+ });
1078
+ const thread = createThread(
1079
+ {
1080
+ async *run() {
1081
+ yield { content: [toolCallPart("lookup_weather")] };
1082
+ await released;
1083
+ },
1084
+ },
1085
+ {
1086
+ history: {
1087
+ async load() {
1088
+ return { messages: [] };
1089
+ },
1090
+ async append() {},
1091
+ update,
1092
+ },
1093
+ },
1094
+ );
1095
+
1096
+ const appended = thread.append(userMessage("what is the weather"));
1097
+ await flush();
1098
+ const assistantMessage = thread.messages.at(-1)!;
1099
+ expect(assistantMessage.status?.type).toBe("running");
1100
+
1101
+ thread.addToolResult({
1102
+ messageId: assistantMessage.id,
1103
+ toolCallId: "call-lookup_weather",
1104
+ toolName: "lookup_weather",
1105
+ result: { temperature: 21 },
1106
+ isError: false,
1107
+ });
1108
+ await flush();
1109
+ release();
1110
+ await appended;
1111
+
1112
+ expect(update).not.toHaveBeenCalled();
1113
+ });
1114
+
1115
+ it("persists a final result that replaces a preliminary result", async () => {
1116
+ const update = vi.fn(async (_item: ExportedMessageRepositoryItem) => {});
1117
+ const thread = createThread(
1118
+ {
1119
+ async run() {
1120
+ return {
1121
+ content: [
1122
+ {
1123
+ ...toolCallPart("lookup_weather"),
1124
+ result: { temperature: 20 },
1125
+ isPreliminary: true,
1126
+ },
1127
+ ],
1128
+ };
1129
+ },
1130
+ },
1131
+ {
1132
+ history: {
1133
+ async load() {
1134
+ return { messages: [] };
1135
+ },
1136
+ async append() {},
1137
+ update,
1138
+ },
1139
+ },
1140
+ );
1141
+
1142
+ await thread.append(userMessage("what is the weather"));
1143
+ const assistantMessage = thread.messages.at(-1)!;
1144
+ expect(assistantMessage.status?.type).toBe("complete");
1145
+
1146
+ thread.addToolResult({
1147
+ messageId: assistantMessage.id,
1148
+ toolCallId: "call-lookup_weather",
1149
+ toolName: "lookup_weather",
1150
+ result: { temperature: 21 },
1151
+ isError: false,
1152
+ });
1153
+ await flush();
1154
+
1155
+ expect(update).toHaveBeenCalledOnce();
1156
+ const updatedToolCall = update.mock.calls[0]?.[0].message.content[0];
1157
+ expect(updatedToolCall).toMatchObject({ result: { temperature: 21 } });
1158
+ expect(updatedToolCall).not.toHaveProperty("isPreliminary");
1159
+ });
1160
+
1161
+ it("surfaces failed user persistence without abandoning the run", async () => {
1162
+ const persistenceError = new Error("history unavailable");
1163
+ const run = vi.fn(async () => ({
1164
+ content: [{ type: "text" as const, text: "done" }],
1165
+ }));
1166
+ const append = vi.fn(async (item: ExportedMessageRepositoryItem) => {
1167
+ if (item.message.role === "user") throw persistenceError;
1168
+ });
1169
+ const thread = createThread(
1170
+ { run },
1171
+ {
1172
+ history: {
1173
+ async load() {
1174
+ return { messages: [] };
1175
+ },
1176
+ append,
1177
+ },
1178
+ },
1179
+ );
1180
+
1181
+ await expect(thread.append(userMessage("hello"))).rejects.toBe(
1182
+ persistenceError,
1183
+ );
1184
+
1185
+ expect(append).toHaveBeenCalledTimes(2);
1186
+ expect(run).toHaveBeenCalledOnce();
1187
+ });
1188
+
1189
+ it("surfaces failed persistence for messages that do not start a run", async () => {
1190
+ const persistenceError = new Error("history unavailable");
1191
+ const run = vi.fn();
1192
+ const thread = createThread(
1193
+ { run },
1194
+ {
1195
+ history: {
1196
+ async load() {
1197
+ return { messages: [] };
1198
+ },
1199
+ async append() {
1200
+ throw persistenceError;
1201
+ },
1202
+ },
1203
+ },
1204
+ );
1205
+
1206
+ await expect(
1207
+ thread.append({ ...userMessage("hello"), startRun: false }),
1208
+ ).rejects.toBe(persistenceError);
1209
+
1210
+ expect(run).not.toHaveBeenCalled();
1211
+ });
1212
+
1213
+ it("handles failed persistence before notifying no-run subscribers", async () => {
1214
+ const persistenceError = new Error("history unavailable");
1215
+ const listenerError = new Error("listener unavailable");
1216
+ const thread = createThread(
1217
+ { run: vi.fn() },
1218
+ {
1219
+ history: {
1220
+ async load() {
1221
+ return { messages: [] };
1222
+ },
1223
+ async append() {
1224
+ throw persistenceError;
1225
+ },
1226
+ },
1227
+ },
1228
+ );
1229
+ thread.subscribe(() => {
1230
+ throw listenerError;
1231
+ });
1232
+
1233
+ await expect(
1234
+ thread.append({ ...userMessage("hello"), startRun: false }),
1235
+ ).rejects.toBe(listenerError);
1236
+ await flush();
1237
+ });
1238
+ });
1239
+
1240
+ describe("LocalThreadRuntimeCore - detach", () => {
1241
+ it("drops a pending append when detached", async () => {
1242
+ let resolveInitialization!: () => void;
1243
+ const initialization = new Promise<void>((resolve) => {
1244
+ resolveInitialization = resolve;
1245
+ });
1246
+ const run = vi.fn(async () => ({
1247
+ content: [{ type: "text" as const, text: "done" }],
1248
+ }));
1249
+ const thread = createThread({ run });
1250
+ thread.__internal_setGetInitializePromise(() => initialization);
1251
+
1252
+ const appendPromise = thread.append(userMessage("hello"));
1253
+ await Promise.resolve();
1254
+ thread.detach();
1255
+ resolveInitialization();
1256
+
1257
+ await appendPromise;
1258
+ expect(run).not.toHaveBeenCalled();
1259
+ expect(thread.messages).toEqual([]);
1260
+ });
1261
+ });
1262
+
1263
+ describe("LocalThreadRuntimeCore optimistic append", () => {
1264
+ it("paints the appended message before initialization resolves", async () => {
1265
+ let resolveInitialization!: () => void;
1266
+ const initialization = new Promise<void>((resolve) => {
1267
+ resolveInitialization = resolve;
1268
+ });
1269
+ const run = vi.fn(async () => ({
1270
+ content: [{ type: "text" as const, text: "done" }],
1271
+ }));
1272
+ const thread = createThread({ run });
1273
+ thread.__internal_setGetInitializePromise(() => initialization);
1274
+ const onUpdate = vi.fn();
1275
+ thread.subscribe(onUpdate);
1276
+
1277
+ const appendPromise = thread.append(userMessage("hello"));
1278
+ await Promise.resolve();
1279
+
1280
+ expect(thread.messages).toHaveLength(1);
1281
+ expect(thread.messages[0]?.role).toBe("user");
1282
+ expect(onUpdate).toHaveBeenCalled();
1283
+ expect(run).not.toHaveBeenCalled();
1284
+
1285
+ resolveInitialization();
1286
+ await appendPromise;
1287
+ expect(run).toHaveBeenCalledTimes(1);
1288
+ });
1289
+
1290
+ it("silently drops a detached append even when initialization rejects", async () => {
1291
+ let rejectInitialization!: (error: unknown) => void;
1292
+ const initialization = new Promise<void>((_, reject) => {
1293
+ rejectInitialization = reject;
1294
+ });
1295
+ const run = vi.fn(async () => ({
1296
+ content: [{ type: "text" as const, text: "done" }],
1297
+ }));
1298
+ const thread = createThread({ run });
1299
+ thread.__internal_setGetInitializePromise(() => initialization);
1300
+
1301
+ const appendPromise = thread.append(userMessage("hello"));
1302
+ await Promise.resolve();
1303
+ thread.detach();
1304
+ rejectInitialization(new Error("initialization failed"));
1305
+
1306
+ await expect(appendPromise).resolves.toBeUndefined();
1307
+ expect(thread.messages).toEqual([]);
1308
+ expect(run).not.toHaveBeenCalled();
1309
+ });
1310
+
1311
+ it("rolls the optimistic message back when initialization rejects", async () => {
1312
+ const initializationError = new Error("initialization failed");
1313
+ const run = vi.fn(async () => ({
1314
+ content: [{ type: "text" as const, text: "done" }],
1315
+ }));
1316
+ const thread = createThread({ run });
1317
+ thread.__internal_setGetInitializePromise(() =>
1318
+ Promise.reject(initializationError),
1319
+ );
1320
+
1321
+ const error = await thread.append(userMessage("hello")).then(
1322
+ () => {
1323
+ throw new Error("expected the append to reject");
1324
+ },
1325
+ (e: unknown) => e,
1326
+ );
1327
+ expect(isMessageNotSentError(error)).toBe(true);
1328
+ expect((error as Error).cause).toBe(initializationError);
1329
+ expect(thread.messages).toEqual([]);
1330
+ expect(run).not.toHaveBeenCalled();
1331
+ });
1332
+ });
1333
+
1334
+ describe("LocalThreadRuntimeCore append during a history load", () => {
1335
+ const persistedMessage = {
1336
+ id: "persisted",
1337
+ role: "user" as const,
1338
+ content: [{ type: "text" as const, text: "persisted" }],
1339
+ attachments: [],
1340
+ createdAt: new Date(0),
1341
+ metadata: { custom: {} },
1342
+ };
1343
+
1344
+ const createLoadingThread = (
1345
+ load: ThreadHistoryAdapter["load"],
1346
+ run: ChatModelAdapter["run"] = async () => ({
1347
+ content: [{ type: "text", text: "done" }],
1348
+ }),
1349
+ ) => {
1350
+ const appended: ExportedMessageRepositoryItem[] = [];
1351
+ const thread = createThread(
1352
+ { run },
1353
+ {
1354
+ history: {
1355
+ load,
1356
+ async append(item) {
1357
+ appended.push(item);
1358
+ },
1359
+ },
1360
+ },
1361
+ );
1362
+ return { thread, appended };
1363
+ };
1364
+
1365
+ it("lands a message typed during a load after the imported history", async () => {
1366
+ let releaseLoad!: () => void;
1367
+ const loadBarrier = new Promise<void>((resolve) => {
1368
+ releaseLoad = resolve;
1369
+ });
1370
+ const { thread, appended } = createLoadingThread(() =>
1371
+ loadBarrier.then(() => ({
1372
+ headId: "persisted",
1373
+ messages: [{ parentId: null, message: persistedMessage }],
1374
+ })),
1375
+ );
1376
+
1377
+ thread.__internal_load();
1378
+ expect(thread.isLoading).toBe(true);
1379
+
1380
+ const appendPromise = thread.append(userMessage("typed during load"));
1381
+ releaseLoad();
1382
+ await appendPromise;
1383
+ await flush();
1384
+
1385
+ expect(thread.messages.map((message) => message.content)).toEqual([
1386
+ [{ type: "text", text: "persisted" }],
1387
+ [{ type: "text", text: "typed during load" }],
1388
+ [{ type: "text", text: "done" }],
1389
+ ]);
1390
+ expect(appended.map((item) => [item.parentId, item.message.role])).toEqual([
1391
+ ["persisted", "user"],
1392
+ [thread.messages[1]!.id, "assistant"],
1393
+ ]);
1394
+ });
1395
+
1396
+ // The tail re-point must not move an append that named its parent, or an
1397
+ // edit issued during a load would silently reattach to the imported tail.
1398
+ it("keeps an explicit parent for a message appended during a load", async () => {
1399
+ const olderMessage = {
1400
+ ...persistedMessage,
1401
+ id: "older",
1402
+ content: [{ type: "text" as const, text: "older" }],
1403
+ };
1404
+ let releaseLoad!: () => void;
1405
+ const loadBarrier = new Promise<void>((resolve) => {
1406
+ releaseLoad = resolve;
1407
+ });
1408
+ const { thread, appended } = createLoadingThread(() =>
1409
+ loadBarrier.then(() => ({
1410
+ headId: "persisted",
1411
+ messages: [
1412
+ { parentId: null, message: olderMessage },
1413
+ { parentId: "older", message: persistedMessage },
1414
+ ],
1415
+ })),
1416
+ );
1417
+
1418
+ thread.__internal_load();
1419
+ expect(thread.isLoading).toBe(true);
1420
+
1421
+ const appendPromise = thread.append({
1422
+ ...userMessage("branched during load"),
1423
+ parentId: "older",
1424
+ });
1425
+ releaseLoad();
1426
+ await appendPromise;
1427
+ await flush();
1428
+
1429
+ expect(appended.map((item) => [item.parentId, item.message.role])).toEqual([
1430
+ ["older", "user"],
1431
+ [thread.messages.at(-2)!.id, "assistant"],
1432
+ ]);
1433
+ });
1434
+
1435
+ it("keeps a message typed during a rejected load", async () => {
1436
+ let rejectLoad!: (error: unknown) => void;
1437
+ const loadBarrier = new Promise<never>((_, reject) => {
1438
+ rejectLoad = reject;
287
1439
  });
288
- const run = vi.fn(async () => ({
289
- content: [{ type: "text" as const, text: "done" }],
290
- }));
291
- const thread = createThread({ run });
292
- thread.__internal_setGetInitializePromise(() => initialization);
1440
+ const { thread, appended } = createLoadingThread(() => loadBarrier);
293
1441
 
294
- const appendPromise = thread.append(userMessage("hello"));
295
- await Promise.resolve();
296
- thread.detach();
297
- rejectInitialization(new Error("initialization failed"));
1442
+ thread.__internal_load().catch(() => {});
1443
+ const appendPromise = thread.append(userMessage("typed during load"));
1444
+ rejectLoad(new Error("history unavailable"));
1445
+ await appendPromise;
1446
+ await flush();
298
1447
 
299
- await expect(appendPromise).resolves.toBeUndefined();
300
- expect(thread.messages).toEqual([]);
301
- expect(run).not.toHaveBeenCalled();
1448
+ expect(thread.messages.map((message) => message.content)).toEqual([
1449
+ [{ type: "text", text: "typed during load" }],
1450
+ [{ type: "text", text: "done" }],
1451
+ ]);
1452
+ expect(appended[0]?.parentId).toBe(null);
302
1453
  });
303
1454
 
304
- it("rolls the optimistic message back when initialization rejects", async () => {
305
- const initializationError = new Error("initialization failed");
1455
+ it("drops a message typed during a load on a detached thread", async () => {
1456
+ let releaseLoad!: () => void;
1457
+ const loadBarrier = new Promise<void>((resolve) => {
1458
+ releaseLoad = resolve;
1459
+ });
306
1460
  const run = vi.fn(async () => ({
307
1461
  content: [{ type: "text" as const, text: "done" }],
308
1462
  }));
309
- const thread = createThread({ run });
310
- thread.__internal_setGetInitializePromise(() =>
311
- Promise.reject(initializationError),
1463
+ const { thread, appended } = createLoadingThread(
1464
+ () =>
1465
+ loadBarrier.then(() => ({
1466
+ headId: "persisted",
1467
+ messages: [{ parentId: null, message: persistedMessage }],
1468
+ })),
1469
+ run,
312
1470
  );
313
1471
 
314
- const error = await thread.append(userMessage("hello")).then(
315
- () => {
316
- throw new Error("expected the append to reject");
317
- },
318
- (e: unknown) => e,
319
- );
320
- expect(isMessageNotSentError(error)).toBe(true);
321
- expect((error as Error).cause).toBe(initializationError);
322
- expect(thread.messages).toEqual([]);
1472
+ thread.__internal_load();
1473
+ const appendPromise = thread.append(userMessage("typed during load"));
1474
+ thread.detach();
1475
+ releaseLoad();
1476
+ await appendPromise;
1477
+ await flush();
1478
+
323
1479
  expect(run).not.toHaveBeenCalled();
1480
+ expect(appended).toEqual([]);
1481
+ expect(thread.messages.map((message) => message.id)).toEqual(["persisted"]);
324
1482
  });
325
1483
  });
326
1484
 
@@ -406,6 +1564,158 @@ describe("LocalThreadRuntimeCore human-in-the-loop tools", () => {
406
1564
  expect(toolCall?.result).toEqual(result);
407
1565
  expect(thread.messages.at(-1)?.status?.type).toBe("complete");
408
1566
  });
1567
+
1568
+ it.each(["tool result", "approval"])(
1569
+ "resumes with feedback a subscriber adds while the %s is notified",
1570
+ async (answer) => {
1571
+ const runs: ChatModelRunOptions[] = [];
1572
+ const thread = createThread({
1573
+ async run(options) {
1574
+ runs.push(options);
1575
+ if (runs.length === 1)
1576
+ return answer === "approval"
1577
+ ? toolCallResult("deploy", { id: "approval-1" })
1578
+ : toolCallResult("send_email");
1579
+ if (runs.length > 2) throw new Error("resumed more than once");
1580
+ return { content: [{ type: "text", text: "done" }] };
1581
+ },
1582
+ });
1583
+
1584
+ await thread.append(userMessage("send"));
1585
+ const messageId = thread.messages.at(-1)!.id;
1586
+ const unsubscribe = thread.subscribe(() => {
1587
+ const message = thread.messages.find((m) => m.id === messageId);
1588
+ if (
1589
+ message?.status?.type === "requires-action" &&
1590
+ message.metadata.submittedFeedback === undefined
1591
+ ) {
1592
+ thread.submitFeedback({ messageId, type: "positive" });
1593
+ }
1594
+ });
1595
+ if (answer === "approval") {
1596
+ void thread.respondToToolApproval({
1597
+ approvalId: "approval-1",
1598
+ approved: true,
1599
+ });
1600
+ } else {
1601
+ thread.addToolResult({
1602
+ messageId,
1603
+ toolCallId: "call-send_email",
1604
+ toolName: "send_email",
1605
+ result: { sent: true },
1606
+ isError: false,
1607
+ });
1608
+ }
1609
+ unsubscribe();
1610
+ await flush();
1611
+
1612
+ expect(runs).toHaveLength(2);
1613
+ const message = thread.messages.at(-1);
1614
+ expect(message?.status?.type).toBe("complete");
1615
+ expect(message?.metadata.submittedFeedback).toEqual({
1616
+ type: "positive",
1617
+ });
1618
+ },
1619
+ );
1620
+
1621
+ it("continues from feedback that lands before a paused roundtrip returns", async () => {
1622
+ let releaseTeardown!: () => void;
1623
+ const teardown = new Promise<void>((resolve) => {
1624
+ releaseTeardown = resolve;
1625
+ });
1626
+ const runs: ChatModelRunOptions[] = [];
1627
+ const thread = createThread({
1628
+ run(options) {
1629
+ runs.push(options);
1630
+ return (async function* () {
1631
+ if (runs.length > 2) throw new Error("continued more than once");
1632
+ if (runs.length === 2) {
1633
+ yield { content: [{ type: "text", text: "done" }] };
1634
+ return;
1635
+ }
1636
+ yield toolCallResult("lookup_weather");
1637
+ await teardown;
1638
+ })();
1639
+ },
1640
+ });
1641
+
1642
+ const send = thread.append(userMessage("weather"));
1643
+ await flush();
1644
+ const messageId = thread.messages.at(-1)!.id;
1645
+ thread.submitFeedback({ messageId, type: "positive" });
1646
+ releaseTeardown();
1647
+ await send;
1648
+
1649
+ expect(runs).toHaveLength(2);
1650
+ const message = thread.messages.at(-1);
1651
+ expect(message?.status?.type).toBe("complete");
1652
+ expect(message?.metadata.submittedFeedback).toEqual({ type: "positive" });
1653
+ });
1654
+
1655
+ it("continues from feedback that lands while a paused roundtrip returns", async () => {
1656
+ const runs: ChatModelRunOptions[] = [];
1657
+ const thread = createThread({
1658
+ run(options) {
1659
+ runs.push(options);
1660
+ return (async function* () {
1661
+ if (runs.length > 2) throw new Error("continued more than once");
1662
+ if (runs.length === 2) {
1663
+ yield { content: [{ type: "text", text: "done" }] };
1664
+ return;
1665
+ }
1666
+ yield toolCallResult("lookup_weather");
1667
+ const messageId = options.unstable_assistantMessageId!;
1668
+ let depth = 0;
1669
+ const submitFeedback = () => {
1670
+ thread.submitFeedback({ messageId, type: "positive" });
1671
+ if (++depth < 10) queueMicrotask(submitFeedback);
1672
+ };
1673
+ queueMicrotask(submitFeedback);
1674
+ })();
1675
+ },
1676
+ });
1677
+
1678
+ await thread.append(userMessage("weather"));
1679
+
1680
+ expect(runs).toHaveLength(2);
1681
+ const message = thread.messages.at(-1);
1682
+ expect(message?.status?.type).toBe("complete");
1683
+ expect(message?.metadata.submittedFeedback).toEqual({ type: "positive" });
1684
+ });
1685
+
1686
+ it("ends the run when anything but feedback replaces a paused message", async () => {
1687
+ let releaseTeardown!: () => void;
1688
+ const teardown = new Promise<void>((resolve) => {
1689
+ releaseTeardown = resolve;
1690
+ });
1691
+ const runs: ChatModelRunOptions[] = [];
1692
+ const thread = createThread({
1693
+ run(options) {
1694
+ runs.push(options);
1695
+ return (async function* () {
1696
+ if (runs.length > 1)
1697
+ throw new Error("continued after its message was replaced");
1698
+ yield toolCallResult("lookup_weather");
1699
+ await teardown;
1700
+ })();
1701
+ },
1702
+ });
1703
+
1704
+ const send = thread.append(userMessage("weather"));
1705
+ await flush();
1706
+ const exported = thread.export();
1707
+ thread.import({
1708
+ ...exported,
1709
+ messages: exported.messages.map((item) => ({
1710
+ ...item,
1711
+ message: { ...item.message },
1712
+ })),
1713
+ });
1714
+ releaseTeardown();
1715
+ await send;
1716
+
1717
+ expect(runs).toHaveLength(1);
1718
+ });
409
1719
  });
410
1720
 
411
1721
  describe("LocalThreadRuntimeCore addToolResult content", () => {
@@ -1002,6 +2312,75 @@ describe("LocalThreadRuntimeCore cancellation", () => {
1002
2312
  expect(message.status.type).toBe("requires-action");
1003
2313
  });
1004
2314
 
2315
+ it("keeps a paused run's later chunks after feedback", async () => {
2316
+ let releaseTeardown!: () => void;
2317
+ const teardown = new Promise<void>((resolve) => {
2318
+ releaseTeardown = resolve;
2319
+ });
2320
+ const thread = createThread({
2321
+ async *run() {
2322
+ yield toolCallResult("send_email");
2323
+ await teardown;
2324
+ yield {
2325
+ content: [
2326
+ toolCallPart("send_email"),
2327
+ { type: "text", text: "Waiting for approval." },
2328
+ ],
2329
+ } satisfies ChatModelRunResult;
2330
+ },
2331
+ });
2332
+
2333
+ const appendPromise = thread.append(userMessage("send"));
2334
+ await flush();
2335
+ thread.submitFeedback({
2336
+ messageId: thread.messages.at(-1)!.id,
2337
+ type: "positive",
2338
+ });
2339
+ releaseTeardown();
2340
+ await appendPromise;
2341
+
2342
+ const message = thread.messages.at(-1);
2343
+ expect(message?.status?.type).toBe("requires-action");
2344
+ expect(message?.content).toEqual([
2345
+ toolCallPart("send_email"),
2346
+ { type: "text", text: "Waiting for approval." },
2347
+ ]);
2348
+ expect(message?.metadata.submittedFeedback).toEqual({ type: "positive" });
2349
+ });
2350
+
2351
+ it("cancels a paused run that received feedback", async () => {
2352
+ const runs: ChatModelRunOptions[] = [];
2353
+ const thread = createThread({
2354
+ run(options) {
2355
+ runs.push(options);
2356
+ return (async function* () {
2357
+ if (runs.length > 1) throw new Error("continued after cancel");
2358
+ yield toolCallResult("lookup_weather");
2359
+ await new Promise((resolve) =>
2360
+ options.abortSignal.addEventListener("abort", resolve),
2361
+ );
2362
+ })();
2363
+ },
2364
+ });
2365
+
2366
+ const appendPromise = thread.append(userMessage("weather"));
2367
+ await flush();
2368
+ thread.submitFeedback({
2369
+ messageId: thread.messages.at(-1)!.id,
2370
+ type: "positive",
2371
+ });
2372
+ thread.cancelRun();
2373
+ await appendPromise;
2374
+
2375
+ expect(runs).toHaveLength(1);
2376
+ const message = thread.messages.at(-1);
2377
+ expect(message?.status).toEqual({
2378
+ type: "incomplete",
2379
+ reason: "cancelled",
2380
+ });
2381
+ expect(message?.metadata.submittedFeedback).toEqual({ type: "positive" });
2382
+ });
2383
+
1005
2384
  it("ignores a superseded result after its message is removed", async () => {
1006
2385
  let resolveFirst!: (result: ChatModelRunResult) => void;
1007
2386
  const firstResult = new Promise<ChatModelRunResult>((resolve) => {
@@ -1393,6 +2772,38 @@ describe("LocalThreadRuntimeCore tool approval persistence", () => {
1393
2772
  );
1394
2773
  };
1395
2774
 
2775
+ it("persists feedback submitted after the assistant run settles", async () => {
2776
+ const { history, appended, updated } = createHistory();
2777
+ const thread = createThread(
2778
+ {
2779
+ async run() {
2780
+ return { content: [{ type: "text", text: "hello" }] };
2781
+ },
2782
+ },
2783
+ { history },
2784
+ );
2785
+
2786
+ await thread.append(userMessage("hi"));
2787
+ await flush();
2788
+
2789
+ const assistant = appended.find(
2790
+ (item) => item.message.role === "assistant",
2791
+ );
2792
+ if (!assistant) throw new Error("expected persisted assistant message");
2793
+
2794
+ thread.submitFeedback({
2795
+ messageId: assistant.message.id,
2796
+ type: "positive",
2797
+ });
2798
+ await flush();
2799
+
2800
+ expect(updated).toHaveLength(1);
2801
+ expect(updated[0]?.message.id).toBe(assistant.message.id);
2802
+ expect(updated[0]?.message.metadata.submittedFeedback).toEqual({
2803
+ type: "positive",
2804
+ });
2805
+ });
2806
+
1396
2807
  it("persists a run paused for approval and rewrites it once the run finishes", async () => {
1397
2808
  const { history, appended, updated } = createHistory();
1398
2809
  const thread = createApprovalThreadWithHistory(history);
@@ -1414,6 +2825,39 @@ describe("LocalThreadRuntimeCore tool approval persistence", () => {
1414
2825
  expect(updated.at(-1)?.message.status?.type).toBe("complete");
1415
2826
  });
1416
2827
 
2828
+ it("persists an approval answer and feedback given after later turns follow the message", async () => {
2829
+ const { history, updated } = createHistory();
2830
+ const thread = createApprovalThreadWithHistory(history);
2831
+
2832
+ await thread.append(userMessage("send an email"));
2833
+ await flush();
2834
+ const [question, paused] = thread.messages;
2835
+ expect(paused?.status?.type).toBe("requires-action");
2836
+ await thread.append({
2837
+ ...userMessage("and cc my manager"),
2838
+ parentId: paused!.id,
2839
+ });
2840
+ await flush();
2841
+ expect(thread.messages).toHaveLength(4);
2842
+
2843
+ thread.respondToToolApproval({ approvalId: "a1", approved: true });
2844
+ thread.submitFeedback({ messageId: paused!.id, type: "positive" });
2845
+ await flush();
2846
+
2847
+ expect(updated.map((i) => i.message.id)).toEqual([paused!.id, paused!.id]);
2848
+ expect(updated.at(-1)).toMatchObject({
2849
+ parentId: question!.id,
2850
+ message: {
2851
+ content: [
2852
+ expect.objectContaining({
2853
+ approval: expect.objectContaining({ id: "a1", approved: true }),
2854
+ }),
2855
+ ],
2856
+ metadata: { submittedFeedback: { type: "positive" } },
2857
+ },
2858
+ });
2859
+ });
2860
+
1417
2861
  it("keeps the append-only behavior for adapters without update", async () => {
1418
2862
  const { history, appended } = createHistory({ update: false });
1419
2863
  const thread = createApprovalThreadWithHistory(history);
@@ -2296,6 +3740,103 @@ describe("LocalThreadRuntimeCore imported approvals", () => {
2296
3740
  expect(runs).toHaveLength(1);
2297
3741
  expect(thread.messages.at(-1)?.status?.type).toBe("complete");
2298
3742
  });
3743
+
3744
+ it("resumes an imported preliminary tool call and clears its marker", async () => {
3745
+ const { thread, runs } = createImportedThread([
3746
+ { role: "user", content: [{ type: "text", text: "send an email" }] },
3747
+ {
3748
+ role: "assistant",
3749
+ status: { type: "requires-action", reason: "tool-calls" },
3750
+ content: [
3751
+ {
3752
+ type: "tool-call",
3753
+ toolCallId: "call-send_email",
3754
+ toolName: "send_email",
3755
+ args: {},
3756
+ argsText: "{}",
3757
+ result: { preview: true },
3758
+ isPreliminary: true,
3759
+ },
3760
+ ],
3761
+ },
3762
+ ]);
3763
+
3764
+ thread.addToolResult({
3765
+ messageId: thread.messages.at(-1)!.id,
3766
+ toolCallId: "call-send_email",
3767
+ toolName: "send_email",
3768
+ result: { sent: true },
3769
+ isError: false,
3770
+ });
3771
+ await flush();
3772
+
3773
+ expect(runs).toHaveLength(1);
3774
+ const toolCall = runs[0]!
3775
+ .unstable_getMessage()
3776
+ .content.find((part) => part.type === "tool-call");
3777
+ expect(toolCall).toMatchObject({ result: { sent: true } });
3778
+ expect(toolCall?.type === "tool-call" && toolCall.isPreliminary).toBe(
3779
+ undefined,
3780
+ );
3781
+ });
3782
+
3783
+ it("persists a final result for a preliminary tool call that remains paused", async () => {
3784
+ const updated: ExportedMessageRepositoryItem[] = [];
3785
+ const { thread } = createImportedThread(
3786
+ [
3787
+ { role: "user", content: [{ type: "text", text: "send an email" }] },
3788
+ {
3789
+ role: "assistant",
3790
+ status: { type: "requires-action", reason: "tool-calls" },
3791
+ content: [
3792
+ {
3793
+ type: "tool-call",
3794
+ toolCallId: "call-send_email",
3795
+ toolName: "send_email",
3796
+ args: {},
3797
+ argsText: "{}",
3798
+ result: { preview: true },
3799
+ isPreliminary: true,
3800
+ },
3801
+ {
3802
+ type: "tool-call",
3803
+ toolCallId: "call-other",
3804
+ toolName: "send_email",
3805
+ args: {},
3806
+ argsText: "{}",
3807
+ },
3808
+ ],
3809
+ },
3810
+ ],
3811
+ {
3812
+ async load() {
3813
+ return { messages: [] };
3814
+ },
3815
+ async append() {},
3816
+ async update(item) {
3817
+ updated.push(item);
3818
+ },
3819
+ },
3820
+ );
3821
+
3822
+ thread.addToolResult({
3823
+ messageId: thread.messages.at(-1)!.id,
3824
+ toolCallId: "call-send_email",
3825
+ toolName: "send_email",
3826
+ result: { sent: true },
3827
+ isError: false,
3828
+ });
3829
+ await flush();
3830
+
3831
+ expect(updated).toHaveLength(1);
3832
+ const toolCall = updated[0]!.message.content.find(
3833
+ (part) => part.type === "tool-call",
3834
+ );
3835
+ expect(toolCall).toMatchObject({ result: { sent: true } });
3836
+ expect(toolCall?.type === "tool-call" && toolCall.isPreliminary).toBe(
3837
+ undefined,
3838
+ );
3839
+ });
2299
3840
  });
2300
3841
 
2301
3842
  describe("LocalThreadRuntimeCore message queue", () => {