@assistant-ui/core 0.3.19 → 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 (934) hide show
  1. package/dist/adapters/attachment.d.ts +50 -43
  2. package/dist/adapters/attachment.d.ts.map +1 -1
  3. package/dist/adapters/attachment.js +52 -21
  4. package/dist/adapters/attachment.js.map +1 -1
  5. package/dist/adapters/directive-formatter.d.ts +2 -5
  6. package/dist/adapters/directive-formatter.d.ts.map +1 -1
  7. package/dist/adapters/feedback.d.ts +7 -8
  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 +63 -65
  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 +19 -22
  17. package/dist/adapters/suggestion.d.ts.map +1 -1
  18. package/dist/adapters/thread-history.d.ts +60 -58
  19. package/dist/adapters/thread-history.d.ts.map +1 -1
  20. package/dist/adapters/trigger.d.ts +8 -11
  21. package/dist/adapters/trigger.d.ts.map +1 -1
  22. package/dist/adapters/voice.d.ts +50 -45
  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 +1 -4
  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 +20 -23
  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 +28 -30
  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 +24 -27
  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 +51 -39
  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 +9 -12
  54. package/dist/model-context/registry-handles.d.ts.map +1 -1
  55. package/dist/model-context/registry.d.ts +15 -18
  56. package/dist/model-context/registry.d.ts.map +1 -1
  57. package/dist/model-context/tool-call-text.d.ts +15 -17
  58. package/dist/model-context/tool-call-text.d.ts.map +1 -1
  59. package/dist/model-context/tool.d.ts +12 -14
  60. package/dist/model-context/tool.d.ts.map +1 -1
  61. package/dist/model-context/types.d.ts +40 -43
  62. package/dist/model-context/types.d.ts.map +1 -1
  63. package/dist/react/AssistantProvider.d.ts +9 -12
  64. package/dist/react/AssistantProvider.d.ts.map +1 -1
  65. package/dist/react/AssistantRuntimeProvider.d.ts +23 -26
  66. package/dist/react/AssistantRuntimeProvider.d.ts.map +1 -1
  67. package/dist/react/RuntimeAdapter.d.ts +4 -8
  68. package/dist/react/RuntimeAdapter.d.ts.map +1 -1
  69. package/dist/react/adapters/LocalStorageThreadListAdapter.d.ts +28 -23
  70. package/dist/react/adapters/LocalStorageThreadListAdapter.d.ts.map +1 -1
  71. package/dist/react/adapters/LocalStorageThreadListAdapter.js +77 -58
  72. package/dist/react/adapters/LocalStorageThreadListAdapter.js.map +1 -1
  73. package/dist/react/adapters/TitleGenerationAdapter.d.ts +4 -7
  74. package/dist/react/adapters/TitleGenerationAdapter.d.ts.map +1 -1
  75. package/dist/react/client/AdaptedRemoteThread.d.ts +6 -9
  76. package/dist/react/client/AdaptedRemoteThread.d.ts.map +1 -1
  77. package/dist/react/client/DataRenderers.d.ts +2 -5
  78. package/dist/react/client/DataRenderers.d.ts.map +1 -1
  79. package/dist/react/client/InMemoryThreadList.d.ts +9 -12
  80. package/dist/react/client/InMemoryThreadList.d.ts.map +1 -1
  81. package/dist/react/client/Interactables.d.ts +3 -6
  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 +34 -37
  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 +10 -12
  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 -11
  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 +2 -5
  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 -9
  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 +42 -46
  109. package/dist/react/interactables-legacy/scopes.d.ts.map +1 -1
  110. package/dist/react/interactables-legacy/useAssistantInteractable.d.ts +8 -11
  111. package/dist/react/interactables-legacy/useAssistantInteractable.d.ts.map +1 -1
  112. package/dist/react/interactables-legacy/useInteractableState.d.ts +7 -9
  113. package/dist/react/interactables-legacy/useInteractableState.d.ts.map +1 -1
  114. package/dist/react/interactables-shared/useInteractablePersistenceQueue.d.ts +23 -14
  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 +15 -18
  119. package/dist/react/model-context/define-mcp-toolkit.d.ts.map +1 -1
  120. package/dist/react/model-context/define-toolkit.d.ts +13 -6
  121. package/dist/react/model-context/define-toolkit.d.ts.map +1 -1
  122. package/dist/react/model-context/external-tool.d.ts +1 -4
  123. package/dist/react/model-context/external-tool.d.ts.map +1 -1
  124. package/dist/react/model-context/human-tool.d.ts +3 -6
  125. package/dist/react/model-context/human-tool.d.ts.map +1 -1
  126. package/dist/react/model-context/interactableTool.d.ts +26 -29
  127. package/dist/react/model-context/interactableTool.d.ts.map +1 -1
  128. package/dist/react/model-context/makeAssistantDataUI.d.ts +6 -9
  129. package/dist/react/model-context/makeAssistantDataUI.d.ts.map +1 -1
  130. package/dist/react/model-context/makeAssistantTool.d.ts +6 -9
  131. package/dist/react/model-context/makeAssistantTool.d.ts.map +1 -1
  132. package/dist/react/model-context/makeAssistantToolUI.d.ts +6 -9
  133. package/dist/react/model-context/makeAssistantToolUI.d.ts.map +1 -1
  134. package/dist/react/model-context/provider-tool.d.ts +5 -7
  135. package/dist/react/model-context/provider-tool.d.ts.map +1 -1
  136. package/dist/react/model-context/stub-tool.d.ts +1 -4
  137. package/dist/react/model-context/stub-tool.d.ts.map +1 -1
  138. package/dist/react/model-context/toolbox.d.ts +44 -32
  139. package/dist/react/model-context/toolbox.d.ts.map +1 -1
  140. package/dist/react/model-context/useAssistantContext.d.ts +3 -6
  141. package/dist/react/model-context/useAssistantContext.d.ts.map +1 -1
  142. package/dist/react/model-context/useAssistantDataUI.d.ts +7 -10
  143. package/dist/react/model-context/useAssistantDataUI.d.ts.map +1 -1
  144. package/dist/react/model-context/useAssistantInstructions.d.ts +3 -6
  145. package/dist/react/model-context/useAssistantInstructions.d.ts.map +1 -1
  146. package/dist/react/model-context/useAssistantTool.d.ts +9 -13
  147. package/dist/react/model-context/useAssistantTool.d.ts.map +1 -1
  148. package/dist/react/model-context/useAssistantToolUI.d.ts +13 -16
  149. package/dist/react/model-context/useAssistantToolUI.d.ts.map +1 -1
  150. package/dist/react/model-context/useAuiToolOverrides.d.ts +3 -5
  151. package/dist/react/model-context/useAuiToolOverrides.d.ts.map +1 -1
  152. package/dist/react/model-context/useInlineRender.d.ts +3 -6
  153. package/dist/react/model-context/useInlineRender.d.ts.map +1 -1
  154. package/dist/react/model-context/useInteractable.d.ts +40 -40
  155. package/dist/react/model-context/useInteractable.d.ts.map +1 -1
  156. package/dist/react/model-context/useInteractableState.d.ts +10 -9
  157. package/dist/react/model-context/useInteractableState.d.ts.map +1 -1
  158. package/dist/react/model-context/useInteractableVersions.d.ts +4 -7
  159. package/dist/react/model-context/useInteractableVersions.d.ts.map +1 -1
  160. package/dist/react/model-context/useToolArgsStatus.d.ts +7 -9
  161. package/dist/react/model-context/useToolArgsStatus.d.ts.map +1 -1
  162. package/dist/react/primitive-hooks/useActionBarCopy.d.ts +7 -10
  163. package/dist/react/primitive-hooks/useActionBarCopy.d.ts.map +1 -1
  164. package/dist/react/primitive-hooks/useActionBarEdit.d.ts +3 -6
  165. package/dist/react/primitive-hooks/useActionBarEdit.d.ts.map +1 -1
  166. package/dist/react/primitive-hooks/useActionBarFeedback.d.ts +6 -9
  167. package/dist/react/primitive-hooks/useActionBarFeedback.d.ts.map +1 -1
  168. package/dist/react/primitive-hooks/useActionBarReload.d.ts +3 -6
  169. package/dist/react/primitive-hooks/useActionBarReload.d.ts.map +1 -1
  170. package/dist/react/primitive-hooks/useActionBarSpeak.d.ts +3 -6
  171. package/dist/react/primitive-hooks/useActionBarSpeak.d.ts.map +1 -1
  172. package/dist/react/primitive-hooks/useActionBarStopSpeaking.d.ts +3 -6
  173. package/dist/react/primitive-hooks/useActionBarStopSpeaking.d.ts.map +1 -1
  174. package/dist/react/primitive-hooks/useBranchPickerNext.d.ts +3 -6
  175. package/dist/react/primitive-hooks/useBranchPickerNext.d.ts.map +1 -1
  176. package/dist/react/primitive-hooks/useBranchPickerPrevious.d.ts +3 -6
  177. package/dist/react/primitive-hooks/useBranchPickerPrevious.d.ts.map +1 -1
  178. package/dist/react/primitive-hooks/useComposerAddAttachment.d.ts +4 -7
  179. package/dist/react/primitive-hooks/useComposerAddAttachment.d.ts.map +1 -1
  180. package/dist/react/primitive-hooks/useComposerCancel.d.ts +3 -6
  181. package/dist/react/primitive-hooks/useComposerCancel.d.ts.map +1 -1
  182. package/dist/react/primitive-hooks/useComposerDictate.d.ts +3 -6
  183. package/dist/react/primitive-hooks/useComposerDictate.d.ts.map +1 -1
  184. package/dist/react/primitive-hooks/useComposerSend.d.ts +4 -7
  185. package/dist/react/primitive-hooks/useComposerSend.d.ts.map +1 -1
  186. package/dist/react/primitive-hooks/useEditComposerCancel.d.ts +2 -5
  187. package/dist/react/primitive-hooks/useEditComposerCancel.d.ts.map +1 -1
  188. package/dist/react/primitive-hooks/useEditComposerSend.d.ts +3 -6
  189. package/dist/react/primitive-hooks/useEditComposerSend.d.ts.map +1 -1
  190. package/dist/react/primitive-hooks/useMessageBranching.d.ts +5 -8
  191. package/dist/react/primitive-hooks/useMessageBranching.d.ts.map +1 -1
  192. package/dist/react/primitive-hooks/useMessageError.d.ts +1 -4
  193. package/dist/react/primitive-hooks/useMessageError.d.ts.map +1 -1
  194. package/dist/react/primitive-hooks/useMessageReload.d.ts +3 -6
  195. package/dist/react/primitive-hooks/useMessageReload.d.ts.map +1 -1
  196. package/dist/react/primitive-hooks/useSuggestionTrigger.d.ts +7 -10
  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 +1 -4
  201. package/dist/react/primitive-hooks/useThreadIsEmpty.d.ts.map +1 -1
  202. package/dist/react/primitive-hooks/useThreadIsRunning.d.ts +1 -4
  203. package/dist/react/primitive-hooks/useThreadIsRunning.d.ts.map +1 -1
  204. package/dist/react/primitive-hooks/useThreadListItemArchive.d.ts +2 -5
  205. package/dist/react/primitive-hooks/useThreadListItemArchive.d.ts.map +1 -1
  206. package/dist/react/primitive-hooks/useThreadListItemDelete.d.ts +2 -5
  207. package/dist/react/primitive-hooks/useThreadListItemDelete.d.ts.map +1 -1
  208. package/dist/react/primitive-hooks/useThreadListItemTrigger.d.ts +2 -5
  209. package/dist/react/primitive-hooks/useThreadListItemTrigger.d.ts.map +1 -1
  210. package/dist/react/primitive-hooks/useThreadListItemUnarchive.d.ts +2 -5
  211. package/dist/react/primitive-hooks/useThreadListItemUnarchive.d.ts.map +1 -1
  212. package/dist/react/primitive-hooks/useThreadListLoadMore.d.ts +3 -6
  213. package/dist/react/primitive-hooks/useThreadListLoadMore.d.ts.map +1 -1
  214. package/dist/react/primitive-hooks/useThreadListNew.d.ts +2 -5
  215. package/dist/react/primitive-hooks/useThreadListNew.d.ts.map +1 -1
  216. package/dist/react/primitive-hooks/useThreadMessageIds.d.ts +1 -4
  217. package/dist/react/primitive-hooks/useThreadMessageIds.d.ts.map +1 -1
  218. package/dist/react/primitive-hooks/useThreadMessages.d.ts +2 -5
  219. package/dist/react/primitive-hooks/useThreadMessages.d.ts.map +1 -1
  220. package/dist/react/primitive-hooks/useVoice.d.ts +8 -11
  221. package/dist/react/primitive-hooks/useVoice.d.ts.map +1 -1
  222. package/dist/react/primitives/chainOfThought/ChainOfThoughtParts.d.ts +27 -29
  223. package/dist/react/primitives/chainOfThought/ChainOfThoughtParts.d.ts.map +1 -1
  224. package/dist/react/primitives/composer/ComposerAttachments.d.ts +26 -28
  225. package/dist/react/primitives/composer/ComposerAttachments.d.ts.map +1 -1
  226. package/dist/react/primitives/composer/ComposerIf.d.ts +13 -13
  227. package/dist/react/primitives/composer/ComposerIf.d.ts.map +1 -1
  228. package/dist/react/primitives/composer/ComposerQueue.d.ts +13 -17
  229. package/dist/react/primitives/composer/ComposerQueue.d.ts.map +1 -1
  230. package/dist/react/primitives/generativeUI/GenerativeUI.d.ts +30 -33
  231. package/dist/react/primitives/generativeUI/GenerativeUI.d.ts.map +1 -1
  232. package/dist/react/primitives/generativeUI/GenerativeUI.js +42 -33
  233. package/dist/react/primitives/generativeUI/GenerativeUI.js.map +1 -1
  234. package/dist/react/primitives/message/MessageAttachments.d.ts +30 -28
  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 +122 -120
  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 +208 -194
  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 +8 -11
  247. package/dist/react/primitives/message/MessageQuote.d.ts.map +1 -1
  248. package/dist/react/primitives/messagePart/MessagePartInProgress.d.ts +4 -7
  249. package/dist/react/primitives/messagePart/MessagePartInProgress.d.ts.map +1 -1
  250. package/dist/react/primitives/part/PartMessages.d.ts +16 -19
  251. package/dist/react/primitives/part/PartMessages.d.ts.map +1 -1
  252. package/dist/react/primitives/thread/ThreadMessages.d.ts +61 -64
  253. package/dist/react/primitives/thread/ThreadMessages.d.ts.map +1 -1
  254. package/dist/react/primitives/thread/ThreadSuggestions.d.ts +25 -27
  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 +26 -28
  259. package/dist/react/primitives/threadList/ThreadListItems.d.ts.map +1 -1
  260. package/dist/react/primitives/threadListItem/ThreadListItemTitle.d.ts +6 -9
  261. package/dist/react/primitives/threadListItem/ThreadListItemTitle.d.ts.map +1 -1
  262. package/dist/react/providers/AttachmentByIndexProvider.d.ts +5 -8
  263. package/dist/react/providers/AttachmentByIndexProvider.d.ts.map +1 -1
  264. package/dist/react/providers/ChainOfThoughtByIndicesProvider.d.ts +4 -7
  265. package/dist/react/providers/ChainOfThoughtByIndicesProvider.d.ts.map +1 -1
  266. package/dist/react/providers/ChainOfThoughtPartByIndexProvider.d.ts +3 -6
  267. package/dist/react/providers/ChainOfThoughtPartByIndexProvider.d.ts.map +1 -1
  268. package/dist/react/providers/MessageByIdProvider.d.ts +3 -6
  269. package/dist/react/providers/MessageByIdProvider.d.ts.map +1 -1
  270. package/dist/react/providers/MessageByIndexProvider.d.ts +3 -6
  271. package/dist/react/providers/MessageByIndexProvider.d.ts.map +1 -1
  272. package/dist/react/providers/PartByIndexProvider.d.ts +3 -6
  273. package/dist/react/providers/PartByIndexProvider.d.ts.map +1 -1
  274. package/dist/react/providers/QueueItemByIndexProvider.d.ts +4 -7
  275. package/dist/react/providers/QueueItemByIndexProvider.d.ts.map +1 -1
  276. package/dist/react/providers/ReadonlyThreadProvider.d.ts +7 -10
  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 +4 -7
  280. package/dist/react/providers/SuggestionByIndexProvider.d.ts.map +1 -1
  281. package/dist/react/providers/TextMessagePartProvider.d.ts +4 -7
  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 +4 -7
  286. package/dist/react/providers/ThreadListItemByIndexProvider.d.ts.map +1 -1
  287. package/dist/react/providers/ThreadListItemRuntimeProvider.d.ts +4 -8
  288. package/dist/react/providers/ThreadListItemRuntimeProvider.d.ts.map +1 -1
  289. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.d.ts +210 -219
  290. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.d.ts.map +1 -1
  291. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js +70 -46
  292. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js.map +1 -1
  293. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts +186 -198
  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 +17 -23
  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 +10 -11
  302. package/dist/react/runtimes/RuntimeAdapterProvider.d.ts.map +1 -1
  303. package/dist/react/runtimes/assistant-transport/commandQueue.d.ts +11 -14
  304. package/dist/react/runtimes/assistant-transport/commandQueue.d.ts.map +1 -1
  305. package/dist/react/runtimes/assistant-transport/replayBoundaryStream.d.ts +6 -8
  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 +9 -12
  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 +123 -118
  314. package/dist/react/runtimes/assistant-transport/types.d.ts.map +1 -1
  315. package/dist/react/runtimes/assistant-transport/useAssistantTransportRuntime.d.ts +7 -10
  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 +3 -6
  320. package/dist/react/runtimes/assistant-transport/useConvertedState.d.ts.map +1 -1
  321. package/dist/react/runtimes/assistant-transport/useLatestRef.d.ts +2 -5
  322. package/dist/react/runtimes/assistant-transport/useLatestRef.d.ts.map +1 -1
  323. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.d.ts +7 -10
  324. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.d.ts.map +1 -1
  325. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js +426 -216
  326. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js.map +1 -1
  327. package/dist/react/runtimes/cloud/CloudFileAttachmentAdapter.d.ts +15 -18
  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 +146 -125
  332. package/dist/react/runtimes/cloud/auiV0.d.ts.map +1 -1
  333. package/dist/react/runtimes/cloud/auiV0.js +120 -8
  334. package/dist/react/runtimes/cloud/auiV0.js.map +1 -1
  335. package/dist/react/runtimes/cloud/createCloudThreadListAdapter.d.ts +13 -15
  336. package/dist/react/runtimes/cloud/createCloudThreadListAdapter.d.ts.map +1 -1
  337. package/dist/react/runtimes/cloud/createCloudThreadListAdapterCreateFallback.d.ts +5 -7
  338. package/dist/react/runtimes/cloud/createCloudThreadListAdapterCreateFallback.d.ts.map +1 -1
  339. package/dist/react/runtimes/cloud/sdkIdentity.d.ts +2 -5
  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 +3 -6
  343. package/dist/react/runtimes/cloud/useCloudThreadListAdapter.d.ts.map +1 -1
  344. package/dist/react/runtimes/cloud/useCloudThreadListRuntime.d.ts +9 -11
  345. package/dist/react/runtimes/cloud/useCloudThreadListRuntime.d.ts.map +1 -1
  346. package/dist/react/runtimes/createMessageConverter.d.ts +15 -19
  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 +14 -17
  350. package/dist/react/runtimes/createRuntimeExtras.d.ts.map +1 -1
  351. package/dist/react/runtimes/external-message-converter.d.ts +17 -21
  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 +3 -6
  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 +2 -5
  362. package/dist/react/runtimes/useExternalStoreSharedOptions.d.ts.map +1 -1
  363. package/dist/react/runtimes/useLocalRuntime.d.ts +29 -41
  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 +3 -6
  368. package/dist/react/runtimes/useRemoteThreadListRuntime.d.ts.map +1 -1
  369. package/dist/react/runtimes/useRemoteThreadListRuntime.js +2 -2
  370. package/dist/react/runtimes/useRemoteThreadListRuntime.js.map +1 -1
  371. package/dist/react/runtimes/useRuntimeAdapters.d.ts +5 -7
  372. package/dist/react/runtimes/useRuntimeAdapters.d.ts.map +1 -1
  373. package/dist/react/runtimes/useStreamingTiming.d.ts +4 -6
  374. package/dist/react/runtimes/useStreamingTiming.d.ts.map +1 -1
  375. package/dist/react/types/MessagePartComponentTypes.d.ts +72 -70
  376. package/dist/react/types/MessagePartComponentTypes.d.ts.map +1 -1
  377. package/dist/react/types/scopes/dataRenderers.d.ts +11 -15
  378. package/dist/react/types/scopes/dataRenderers.d.ts.map +1 -1
  379. package/dist/react/types/scopes/interactables.d.ts +60 -60
  380. package/dist/react/types/scopes/interactables.d.ts.map +1 -1
  381. package/dist/react/types/scopes/tools.d.ts +21 -24
  382. package/dist/react/types/scopes/tools.d.ts.map +1 -1
  383. package/dist/react/types/store-augmentation.d.ts +10 -11
  384. package/dist/react/types/store-augmentation.d.ts.map +1 -1
  385. package/dist/react/utils/getMessageQuote.d.ts +6 -8
  386. package/dist/react/utils/getMessageQuote.d.ts.map +1 -1
  387. package/dist/react/utils/groupParts.d.ts +35 -37
  388. package/dist/react/utils/groupParts.d.ts.map +1 -1
  389. package/dist/react/utils/useJSONEqual.d.ts +3 -6
  390. package/dist/react/utils/useJSONEqual.d.ts.map +1 -1
  391. package/dist/react/utils/useJSONSchemaDependency.d.ts +3 -6
  392. package/dist/react/utils/useJSONSchemaDependency.d.ts.map +1 -1
  393. package/dist/runtime/api/assistant-runtime.d.ts +28 -31
  394. package/dist/runtime/api/assistant-runtime.d.ts.map +1 -1
  395. package/dist/runtime/api/attachment-runtime.d.ts +51 -49
  396. package/dist/runtime/api/attachment-runtime.d.ts.map +1 -1
  397. package/dist/runtime/api/attachment-runtime.js.map +1 -1
  398. package/dist/runtime/api/bindings.d.ts +35 -35
  399. package/dist/runtime/api/bindings.d.ts.map +1 -1
  400. package/dist/runtime/api/composer-runtime.d.ts +215 -201
  401. package/dist/runtime/api/composer-runtime.d.ts.map +1 -1
  402. package/dist/runtime/api/composer-runtime.js +4 -0
  403. package/dist/runtime/api/composer-runtime.js.map +1 -1
  404. package/dist/runtime/api/message-part-runtime.d.ts +31 -30
  405. package/dist/runtime/api/message-part-runtime.d.ts.map +1 -1
  406. package/dist/runtime/api/message-part-runtime.js +25 -2
  407. package/dist/runtime/api/message-part-runtime.js.map +1 -1
  408. package/dist/runtime/api/message-runtime.d.ts +87 -82
  409. package/dist/runtime/api/message-runtime.d.ts.map +1 -1
  410. package/dist/runtime/api/message-runtime.js +4 -3
  411. package/dist/runtime/api/message-runtime.js.map +1 -1
  412. package/dist/runtime/api/paths.d.ts +54 -57
  413. package/dist/runtime/api/paths.d.ts.map +1 -1
  414. package/dist/runtime/api/thread-list-item-runtime.d.ts +70 -72
  415. package/dist/runtime/api/thread-list-item-runtime.d.ts.map +1 -1
  416. package/dist/runtime/api/thread-list-item-runtime.js.map +1 -1
  417. package/dist/runtime/api/thread-list-runtime.d.ts +77 -81
  418. package/dist/runtime/api/thread-list-runtime.d.ts.map +1 -1
  419. package/dist/runtime/api/thread-list-runtime.js.map +1 -1
  420. package/dist/runtime/api/thread-runtime.d.ts +306 -308
  421. package/dist/runtime/api/thread-runtime.d.ts.map +1 -1
  422. package/dist/runtime/api/thread-runtime.js +2 -1
  423. package/dist/runtime/api/thread-runtime.js.map +1 -1
  424. package/dist/runtime/base/base-assistant-runtime-core.d.ts +9 -12
  425. package/dist/runtime/base/base-assistant-runtime-core.d.ts.map +1 -1
  426. package/dist/runtime/base/base-composer-runtime-core.d.ts +150 -101
  427. package/dist/runtime/base/base-composer-runtime-core.d.ts.map +1 -1
  428. package/dist/runtime/base/base-composer-runtime-core.js +400 -122
  429. package/dist/runtime/base/base-composer-runtime-core.js.map +1 -1
  430. package/dist/runtime/base/base-thread-runtime-core.d.ts +121 -104
  431. package/dist/runtime/base/base-thread-runtime-core.d.ts.map +1 -1
  432. package/dist/runtime/base/base-thread-runtime-core.js +174 -62
  433. package/dist/runtime/base/base-thread-runtime-core.js.map +1 -1
  434. package/dist/runtime/base/default-edit-composer-runtime-core.d.ts +30 -31
  435. package/dist/runtime/base/default-edit-composer-runtime-core.d.ts.map +1 -1
  436. package/dist/runtime/base/default-edit-composer-runtime-core.js +21 -2
  437. package/dist/runtime/base/default-edit-composer-runtime-core.js.map +1 -1
  438. package/dist/runtime/base/default-thread-composer-runtime-core.d.ts +28 -31
  439. package/dist/runtime/base/default-thread-composer-runtime-core.d.ts.map +1 -1
  440. package/dist/runtime/base/default-thread-composer-runtime-core.js +23 -2
  441. package/dist/runtime/base/default-thread-composer-runtime-core.js.map +1 -1
  442. package/dist/runtime/branch/external-thread-branch-adapter.d.ts +15 -18
  443. package/dist/runtime/branch/external-thread-branch-adapter.d.ts.map +1 -1
  444. package/dist/runtime/interfaces/assistant-runtime-core.d.ts +8 -11
  445. package/dist/runtime/interfaces/assistant-runtime-core.d.ts.map +1 -1
  446. package/dist/runtime/interfaces/composer-runtime-core.d.ts +98 -83
  447. package/dist/runtime/interfaces/composer-runtime-core.d.ts.map +1 -1
  448. package/dist/runtime/interfaces/thread-list-runtime-core.d.ts +67 -70
  449. package/dist/runtime/interfaces/thread-list-runtime-core.d.ts.map +1 -1
  450. package/dist/runtime/interfaces/thread-runtime-core.d.ts +225 -211
  451. package/dist/runtime/interfaces/thread-runtime-core.d.ts.map +1 -1
  452. package/dist/runtime/internal.d.ts +25 -20
  453. package/dist/runtime/internal.d.ts.map +1 -0
  454. package/dist/runtime/queue/external-thread-queue-adapter.d.ts +37 -40
  455. package/dist/runtime/queue/external-thread-queue-adapter.d.ts.map +1 -1
  456. package/dist/runtime/queue/message-queue.d.ts +32 -33
  457. package/dist/runtime/queue/message-queue.d.ts.map +1 -1
  458. package/dist/runtime/queue/message-queue.js +10 -2
  459. package/dist/runtime/queue/message-queue.js.map +1 -1
  460. package/dist/runtime/queue/queue-item.d.ts +7 -10
  461. package/dist/runtime/queue/queue-item.d.ts.map +1 -1
  462. package/dist/runtime/utils/abortable-thread-load.d.ts +9 -11
  463. package/dist/runtime/utils/abortable-thread-load.d.ts.map +1 -1
  464. package/dist/runtime/utils/attachment-add-operations.d.ts +16 -15
  465. package/dist/runtime/utils/attachment-add-operations.d.ts.map +1 -1
  466. package/dist/runtime/utils/attachment-add-operations.js +24 -2
  467. package/dist/runtime/utils/attachment-add-operations.js.map +1 -1
  468. package/dist/runtime/utils/attachment-send-operations.d.ts +12 -0
  469. package/dist/runtime/utils/attachment-send-operations.d.ts.map +1 -0
  470. package/dist/runtime/utils/attachment-send-operations.js +34 -0
  471. package/dist/runtime/utils/attachment-send-operations.js.map +1 -0
  472. package/dist/runtime/utils/auto-status.d.ts +11 -13
  473. package/dist/runtime/utils/auto-status.d.ts.map +1 -1
  474. package/dist/runtime/utils/auto-status.js +3 -5
  475. package/dist/runtime/utils/auto-status.js.map +1 -1
  476. package/dist/runtime/utils/chat-model-adapter.d.ts +31 -33
  477. package/dist/runtime/utils/chat-model-adapter.d.ts.map +1 -1
  478. package/dist/runtime/utils/external-message-conversion.d.ts +47 -48
  479. package/dist/runtime/utils/external-message-conversion.d.ts.map +1 -1
  480. package/dist/runtime/utils/external-message-conversion.js +6 -5
  481. package/dist/runtime/utils/external-message-conversion.js.map +1 -1
  482. package/dist/runtime/utils/external-store-message.d.ts +6 -9
  483. package/dist/runtime/utils/external-store-message.d.ts.map +1 -1
  484. package/dist/runtime/utils/message-repository-session.d.ts +24 -27
  485. package/dist/runtime/utils/message-repository-session.d.ts.map +1 -1
  486. package/dist/runtime/utils/message-repository.d.ts +52 -54
  487. package/dist/runtime/utils/message-repository.d.ts.map +1 -1
  488. package/dist/runtime/utils/message-repository.js +8 -7
  489. package/dist/runtime/utils/message-repository.js.map +1 -1
  490. package/dist/runtime/utils/pending-tool-calls.d.ts +12 -14
  491. package/dist/runtime/utils/pending-tool-calls.d.ts.map +1 -1
  492. package/dist/runtime/utils/resolveToolApprovalResponse.d.ts +7 -10
  493. package/dist/runtime/utils/resolveToolApprovalResponse.d.ts.map +1 -1
  494. package/dist/runtime/utils/runtime-extras-brand.d.ts +6 -9
  495. package/dist/runtime/utils/runtime-extras-brand.d.ts.map +1 -1
  496. package/dist/runtime/utils/stored-message-parts.d.ts +21 -0
  497. package/dist/runtime/utils/stored-message-parts.d.ts.map +1 -0
  498. package/dist/runtime/utils/stored-message-parts.js +103 -0
  499. package/dist/runtime/utils/stored-message-parts.js.map +1 -0
  500. package/dist/runtime/utils/streaming-timing.d.ts +23 -26
  501. package/dist/runtime/utils/streaming-timing.d.ts.map +1 -1
  502. package/dist/runtime/utils/thread-message-like.d.ts +38 -34
  503. package/dist/runtime/utils/thread-message-like.d.ts.map +1 -1
  504. package/dist/runtime/utils/thread-message-like.js +16 -6
  505. package/dist/runtime/utils/thread-message-like.js.map +1 -1
  506. package/dist/runtime/utils/thread-runtime-lifecycle.d.ts +5 -7
  507. package/dist/runtime/utils/thread-runtime-lifecycle.d.ts.map +1 -1
  508. package/dist/runtime/utils/thread-runtime-lifecycle.js +31 -4
  509. package/dist/runtime/utils/thread-runtime-lifecycle.js.map +1 -1
  510. package/dist/runtime/utils/tool-call-tree.d.ts +10 -13
  511. package/dist/runtime/utils/tool-call-tree.d.ts.map +1 -1
  512. package/dist/runtime/utils/tool-interactions.d.ts +10 -0
  513. package/dist/runtime/utils/tool-interactions.d.ts.map +1 -0
  514. package/dist/runtime/utils/tool-interactions.js +92 -0
  515. package/dist/runtime/utils/tool-interactions.js.map +1 -0
  516. package/dist/runtimes/assistant-transport/utils.d.ts +1 -4
  517. package/dist/runtimes/assistant-transport/utils.d.ts.map +1 -1
  518. package/dist/runtimes/external-store/external-store-adapter.d.ts +220 -200
  519. package/dist/runtimes/external-store/external-store-adapter.d.ts.map +1 -1
  520. package/dist/runtimes/external-store/external-store-runtime-core.d.ts +6 -8
  521. package/dist/runtimes/external-store/external-store-runtime-core.d.ts.map +1 -1
  522. package/dist/runtimes/external-store/external-store-runtime-core.js +4 -1
  523. package/dist/runtimes/external-store/external-store-runtime-core.js.map +1 -1
  524. package/dist/runtimes/external-store/external-store-shared-options.d.ts +3 -6
  525. package/dist/runtimes/external-store/external-store-shared-options.d.ts.map +1 -1
  526. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.d.ts +40 -43
  527. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.d.ts.map +1 -1
  528. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.js +2 -2
  529. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.js.map +1 -1
  530. package/dist/runtimes/external-store/external-store-thread-runtime-core.d.ts +119 -125
  531. package/dist/runtimes/external-store/external-store-thread-runtime-core.d.ts.map +1 -1
  532. package/dist/runtimes/external-store/external-store-thread-runtime-core.js +71 -18
  533. package/dist/runtimes/external-store/external-store-thread-runtime-core.js.map +1 -1
  534. package/dist/runtimes/external-store/thread-message-converter.d.ts +5 -8
  535. package/dist/runtimes/external-store/thread-message-converter.d.ts.map +1 -1
  536. package/dist/runtimes/inert/InertThreadRuntimeCore.d.ts +96 -98
  537. package/dist/runtimes/inert/InertThreadRuntimeCore.d.ts.map +1 -1
  538. package/dist/runtimes/inert/InertThreadRuntimeCore.js +3 -0
  539. package/dist/runtimes/inert/InertThreadRuntimeCore.js.map +1 -1
  540. package/dist/runtimes/internal.d.ts +20 -16
  541. package/dist/runtimes/internal.d.ts.map +1 -0
  542. package/dist/runtimes/local/local-runtime-core.d.ts +7 -10
  543. package/dist/runtimes/local/local-runtime-core.d.ts.map +1 -1
  544. package/dist/runtimes/local/local-runtime-options.d.ts +41 -44
  545. package/dist/runtimes/local/local-runtime-options.d.ts.map +1 -1
  546. package/dist/runtimes/local/local-thread-list-runtime-core.d.ts +42 -45
  547. package/dist/runtimes/local/local-thread-list-runtime-core.d.ts.map +1 -1
  548. package/dist/runtimes/local/local-thread-runtime-core.d.ts +94 -93
  549. package/dist/runtimes/local/local-thread-runtime-core.d.ts.map +1 -1
  550. package/dist/runtimes/local/local-thread-runtime-core.js +220 -29
  551. package/dist/runtimes/local/local-thread-runtime-core.js.map +1 -1
  552. package/dist/runtimes/local/should-continue.d.ts +2 -5
  553. package/dist/runtimes/local/should-continue.d.ts.map +1 -1
  554. package/dist/runtimes/readonly/ReadonlyThreadRuntimeCore.d.ts +77 -55
  555. package/dist/runtimes/readonly/ReadonlyThreadRuntimeCore.d.ts.map +1 -1
  556. package/dist/runtimes/readonly/ReadonlyThreadRuntimeCore.js +28 -1
  557. package/dist/runtimes/readonly/ReadonlyThreadRuntimeCore.js.map +1 -1
  558. package/dist/runtimes/remote-thread-list/adapter/in-memory.d.ts +12 -15
  559. package/dist/runtimes/remote-thread-list/adapter/in-memory.d.ts.map +1 -1
  560. package/dist/runtimes/remote-thread-list/adapter-changed.d.ts +3 -6
  561. package/dist/runtimes/remote-thread-list/adapter-changed.d.ts.map +1 -1
  562. package/dist/runtimes/remote-thread-list/empty-thread-core.d.ts +2 -5
  563. package/dist/runtimes/remote-thread-list/empty-thread-core.d.ts.map +1 -1
  564. package/dist/runtimes/remote-thread-list/optimistic-state.d.ts +34 -31
  565. package/dist/runtimes/remote-thread-list/optimistic-state.d.ts.map +1 -1
  566. package/dist/runtimes/remote-thread-list/optimistic-state.js +8 -3
  567. package/dist/runtimes/remote-thread-list/optimistic-state.js.map +1 -1
  568. package/dist/runtimes/remote-thread-list/remote-thread-state.d.ts +60 -63
  569. package/dist/runtimes/remote-thread-list/remote-thread-state.d.ts.map +1 -1
  570. package/dist/runtimes/remote-thread-list/remote-thread-state.js +1 -1
  571. package/dist/runtimes/remote-thread-list/title-generation.d.ts +31 -33
  572. package/dist/runtimes/remote-thread-list/title-generation.d.ts.map +1 -1
  573. package/dist/runtimes/remote-thread-list/title.d.ts +5 -8
  574. package/dist/runtimes/remote-thread-list/title.d.ts.map +1 -1
  575. package/dist/runtimes/remote-thread-list/types.d.ts +119 -121
  576. package/dist/runtimes/remote-thread-list/types.d.ts.map +1 -1
  577. package/dist/runtimes/tool-invocations/ToolInvocationTracker.d.ts +147 -149
  578. package/dist/runtimes/tool-invocations/ToolInvocationTracker.d.ts.map +1 -1
  579. package/dist/runtimes/tool-invocations/ToolInvocationTracker.js +22 -7
  580. package/dist/runtimes/tool-invocations/ToolInvocationTracker.js.map +1 -1
  581. package/dist/store/clients/chain-of-thought-client.d.ts +8 -11
  582. package/dist/store/clients/chain-of-thought-client.d.ts.map +1 -1
  583. package/dist/store/clients/external-thread.d.ts +56 -58
  584. package/dist/store/clients/external-thread.d.ts.map +1 -1
  585. package/dist/store/clients/external-thread.js +1270 -741
  586. package/dist/store/clients/external-thread.js.map +1 -1
  587. package/dist/store/clients/model-context-client.d.ts +2 -5
  588. package/dist/store/clients/model-context-client.d.ts.map +1 -1
  589. package/dist/store/clients/no-op-composer-client.d.ts +3 -6
  590. package/dist/store/clients/no-op-composer-client.d.ts.map +1 -1
  591. package/dist/store/clients/no-op-composer-client.js +3 -1
  592. package/dist/store/clients/no-op-composer-client.js.map +1 -1
  593. package/dist/store/clients/runtime-adapter.d.ts +2 -5
  594. package/dist/store/clients/runtime-adapter.d.ts.map +1 -1
  595. package/dist/store/clients/single-thread-list.d.ts +4 -6
  596. package/dist/store/clients/single-thread-list.d.ts.map +1 -1
  597. package/dist/store/clients/submission-message.d.ts +8 -0
  598. package/dist/store/clients/submission-message.d.ts.map +1 -0
  599. package/dist/store/clients/submission-message.js +22 -0
  600. package/dist/store/clients/submission-message.js.map +1 -0
  601. package/dist/store/clients/suggestions.d.ts +8 -11
  602. package/dist/store/clients/suggestions.d.ts.map +1 -1
  603. package/dist/store/clients/thread-message-client.d.ts +12 -12
  604. package/dist/store/clients/thread-message-client.d.ts.map +1 -1
  605. package/dist/store/clients/thread-message-client.js +81 -82
  606. package/dist/store/clients/thread-message-client.js.map +1 -1
  607. package/dist/store/clients/thread-selection-events.d.ts +1 -4
  608. package/dist/store/clients/thread-selection-events.d.ts.map +1 -1
  609. package/dist/store/clients/thread-tasks.d.ts +7 -10
  610. package/dist/store/clients/thread-tasks.d.ts.map +1 -1
  611. package/dist/store/env.d.ts +1 -4
  612. package/dist/store/env.d.ts.map +1 -1
  613. package/dist/store/index.d.ts +33 -36
  614. package/dist/store/index.d.ts.map +1 -0
  615. package/dist/store/index.js +5 -5
  616. package/dist/store/internal.d.ts +12 -12
  617. package/dist/store/internal.d.ts.map +1 -0
  618. package/dist/store/internal.js +2 -2
  619. package/dist/store/primitive-predicates.d.ts +14 -16
  620. package/dist/store/primitive-predicates.d.ts.map +1 -1
  621. package/dist/store/primitive-predicates.js +10 -5
  622. package/dist/store/primitive-predicates.js.map +1 -1
  623. package/dist/store/runtime-clients/attachment-runtime-client.d.ts +4 -7
  624. package/dist/store/runtime-clients/attachment-runtime-client.d.ts.map +1 -1
  625. package/dist/store/runtime-clients/composer-runtime-client.d.ts +11 -14
  626. package/dist/store/runtime-clients/composer-runtime-client.d.ts.map +1 -1
  627. package/dist/store/runtime-clients/composer-runtime-client.js +65 -62
  628. package/dist/store/runtime-clients/composer-runtime-client.js.map +1 -1
  629. package/dist/store/runtime-clients/handle-runtime-action.d.ts +1 -4
  630. package/dist/store/runtime-clients/handle-runtime-action.d.ts.map +1 -1
  631. package/dist/store/runtime-clients/handle-thread-list-action.d.ts +1 -4
  632. package/dist/store/runtime-clients/handle-thread-list-action.d.ts.map +1 -1
  633. package/dist/store/runtime-clients/liveRef.d.ts +2 -5
  634. package/dist/store/runtime-clients/liveRef.d.ts.map +1 -1
  635. package/dist/store/runtime-clients/message-part-runtime-client.d.ts +4 -7
  636. package/dist/store/runtime-clients/message-part-runtime-client.d.ts.map +1 -1
  637. package/dist/store/runtime-clients/message-part-runtime-client.js +26 -16
  638. package/dist/store/runtime-clients/message-part-runtime-client.js.map +1 -1
  639. package/dist/store/runtime-clients/message-runtime-client.d.ts +10 -11
  640. package/dist/store/runtime-clients/message-runtime-client.d.ts.map +1 -1
  641. package/dist/store/runtime-clients/message-runtime-client.js +121 -113
  642. package/dist/store/runtime-clients/message-runtime-client.js.map +1 -1
  643. package/dist/store/runtime-clients/thread-list-item-runtime-client.d.ts +5 -8
  644. package/dist/store/runtime-clients/thread-list-item-runtime-client.d.ts.map +1 -1
  645. package/dist/store/runtime-clients/thread-list-runtime-client.d.ts +6 -9
  646. package/dist/store/runtime-clients/thread-list-runtime-client.d.ts.map +1 -1
  647. package/dist/store/runtime-clients/thread-runtime-client.d.ts +4 -7
  648. package/dist/store/runtime-clients/thread-runtime-client.d.ts.map +1 -1
  649. package/dist/store/runtime-clients/thread-runtime-client.js +221 -158
  650. package/dist/store/runtime-clients/thread-runtime-client.js.map +1 -1
  651. package/dist/store/runtime-clients/useSubscribable.d.ts +3 -6
  652. package/dist/store/runtime-clients/useSubscribable.d.ts.map +1 -1
  653. package/dist/store/scope-registration.d.ts +28 -29
  654. package/dist/store/scope-registration.d.ts.map +1 -1
  655. package/dist/store/scopes/attachment.d.ts +19 -22
  656. package/dist/store/scopes/attachment.d.ts.map +1 -1
  657. package/dist/store/scopes/chain-of-thought.d.ts +33 -35
  658. package/dist/store/scopes/chain-of-thought.d.ts.map +1 -1
  659. package/dist/store/scopes/composer.d.ts +147 -135
  660. package/dist/store/scopes/composer.d.ts.map +1 -1
  661. package/dist/store/scopes/message.d.ts +119 -112
  662. package/dist/store/scopes/message.d.ts.map +1 -1
  663. package/dist/store/scopes/model-context.d.ts +10 -13
  664. package/dist/store/scopes/model-context.d.ts.map +1 -1
  665. package/dist/store/scopes/part.d.ts +53 -50
  666. package/dist/store/scopes/part.d.ts.map +1 -1
  667. package/dist/store/scopes/queue-item.d.ts +21 -23
  668. package/dist/store/scopes/queue-item.d.ts.map +1 -1
  669. package/dist/store/scopes/suggestion.d.ts +14 -17
  670. package/dist/store/scopes/suggestion.d.ts.map +1 -1
  671. package/dist/store/scopes/suggestions.d.ts +18 -21
  672. package/dist/store/scopes/suggestions.d.ts.map +1 -1
  673. package/dist/store/scopes/task.d.ts +27 -30
  674. package/dist/store/scopes/task.d.ts.map +1 -1
  675. package/dist/store/scopes/thread-list-item.d.ts +68 -71
  676. package/dist/store/scopes/thread-list-item.d.ts.map +1 -1
  677. package/dist/store/scopes/thread.d.ts +197 -200
  678. package/dist/store/scopes/thread.d.ts.map +1 -1
  679. package/dist/store/scopes/threads.d.ts +49 -52
  680. package/dist/store/scopes/threads.d.ts.map +1 -1
  681. package/dist/subscribable/subscribable.d.ts +73 -69
  682. package/dist/subscribable/subscribable.d.ts.map +1 -1
  683. package/dist/subscribable/subscribable.js +35 -7
  684. package/dist/subscribable/subscribable.js.map +1 -1
  685. package/dist/types/attachment.d.ts +35 -37
  686. package/dist/types/attachment.d.ts.map +1 -1
  687. package/dist/types/augmentations.d.ts +7 -8
  688. package/dist/types/augmentations.d.ts.map +1 -1
  689. package/dist/types/directive.d.ts +13 -16
  690. package/dist/types/directive.d.ts.map +1 -1
  691. package/dist/types/error.d.ts +15 -17
  692. package/dist/types/error.d.ts.map +1 -1
  693. package/dist/types/index.d.ts +7 -7
  694. package/dist/types/index.d.ts.map +1 -0
  695. package/dist/types/message.d.ts +358 -318
  696. package/dist/types/message.d.ts.map +1 -1
  697. package/dist/types/message.js.map +1 -1
  698. package/dist/types/quote.d.ts +3 -6
  699. package/dist/types/quote.d.ts.map +1 -1
  700. package/dist/types/trigger.d.ts +10 -13
  701. package/dist/types/trigger.d.ts.map +1 -1
  702. package/dist/types/unsubscribe.d.ts +1 -4
  703. package/dist/types/unsubscribe.d.ts.map +1 -1
  704. package/dist/utils/abortable-iterable.d.ts +2 -5
  705. package/dist/utils/abortable-iterable.d.ts.map +1 -1
  706. package/dist/utils/composite-context-provider.d.ts +9 -13
  707. package/dist/utils/composite-context-provider.d.ts.map +1 -1
  708. package/dist/utils/data-url.d.ts +16 -19
  709. package/dist/utils/data-url.d.ts.map +1 -1
  710. package/dist/utils/getGroupStatus.d.ts +19 -21
  711. package/dist/utils/getGroupStatus.d.ts.map +1 -1
  712. package/dist/utils/id.d.ts +3 -6
  713. package/dist/utils/id.d.ts.map +1 -1
  714. package/dist/utils/image-media-type.d.ts +1 -4
  715. package/dist/utils/image-media-type.d.ts.map +1 -1
  716. package/dist/utils/invoke-user-callback.d.ts +1 -4
  717. package/dist/utils/invoke-user-callback.d.ts.map +1 -1
  718. package/dist/utils/json/is-json-equal.d.ts +1 -4
  719. package/dist/utils/json/is-json-equal.d.ts.map +1 -1
  720. package/dist/utils/json/is-json.d.ts +5 -8
  721. package/dist/utils/json/is-json.d.ts.map +1 -1
  722. package/dist/utils/json/stable-stringify-tool-args.d.ts +3 -6
  723. package/dist/utils/json/stable-stringify-tool-args.d.ts.map +1 -1
  724. package/dist/utils/normalizePartStatus.d.ts +6 -8
  725. package/dist/utils/normalizePartStatus.d.ts.map +1 -1
  726. package/dist/utils/normalizePartStatus.js +3 -2
  727. package/dist/utils/normalizePartStatus.js.map +1 -1
  728. package/dist/utils/notify-event-listeners.d.ts +2 -4
  729. package/dist/utils/notify-event-listeners.d.ts.map +1 -1
  730. package/dist/utils/record.d.ts +1 -4
  731. package/dist/utils/record.d.ts.map +1 -1
  732. package/dist/utils/silent-runtime-action.d.ts +2 -5
  733. package/dist/utils/silent-runtime-action.d.ts.map +1 -1
  734. package/dist/utils/text.d.ts +2 -5
  735. package/dist/utils/text.d.ts.map +1 -1
  736. package/dist/utils/wire-media.d.ts +3 -6
  737. package/dist/utils/wire-media.d.ts.map +1 -1
  738. package/package.json +15 -13
  739. package/src/adapters/attachment.test.ts +98 -1
  740. package/src/adapters/attachment.ts +77 -20
  741. package/src/adapters/feedback.ts +1 -0
  742. package/src/adapters/speech.test.ts +94 -0
  743. package/src/adapters/speech.ts +16 -2
  744. package/src/adapters/thread-history.ts +16 -5
  745. package/src/adapters/voice.test.ts +169 -0
  746. package/src/adapters/voice.ts +25 -4
  747. package/src/index.ts +14 -2
  748. package/src/internal.ts +7 -0
  749. package/src/model-context/frame/host.test.ts +51 -1
  750. package/src/model-context/frame/host.ts +3 -1
  751. package/src/model-context/frame/provider.test.ts +106 -20
  752. package/src/model-context/frame/provider.ts +8 -1
  753. package/src/model-context/frame/validate.ts +57 -0
  754. package/src/model-context/interactable-composer-metadata.test.ts +82 -0
  755. package/src/model-context/interactable-composer-metadata.ts +45 -2
  756. package/src/react/RuntimeAdapter.test.ts +4 -1
  757. package/src/react/adapters/LocalStorageThreadListAdapter.test.ts +711 -0
  758. package/src/react/adapters/LocalStorageThreadListAdapter.tsx +125 -109
  759. package/src/react/client/Interactables.test.ts +865 -8
  760. package/src/react/client/Interactables.ts +325 -45
  761. package/src/react/client/RemoteThreadList.concurrent.test.tsx +9 -3
  762. package/src/react/client/RemoteThreadList.initialize-failure.test.ts +124 -0
  763. package/src/react/client/RemoteThreadList.test.ts +5 -6
  764. package/src/react/client/RemoteThreadList.ts +6 -0
  765. package/src/react/client/Tools.ts +2 -3
  766. package/src/react/client/interactable-model-context.test.ts +56 -13
  767. package/src/react/client/interactable-model-context.ts +48 -23
  768. package/src/react/index.ts +2 -2
  769. package/src/react/interactables-legacy/Interactables.test.ts +221 -4
  770. package/src/react/interactables-legacy/Interactables.ts +25 -14
  771. package/src/react/interactables-legacy/interactable-model-context.ts +26 -24
  772. package/src/react/interactables-legacy/useAssistantInteractable.test.tsx +40 -22
  773. package/src/react/interactables-shared/useInteractablePersistenceQueue.test.tsx +22 -2
  774. package/src/react/interactables-shared/useInteractablePersistenceQueue.ts +63 -11
  775. package/src/react/model-context/useInteractable.test.tsx +56 -29
  776. package/src/react/primitive-hooks/useActionBarCopy.test.ts +5 -5
  777. package/src/react/primitive-hooks/useSuggestionTrigger.test.ts +33 -0
  778. package/src/react/primitive-hooks/useSuggestionTrigger.ts +7 -4
  779. package/src/react/primitives/generativeUI/GenerativeUI.test.tsx +170 -0
  780. package/src/react/primitives/generativeUI/GenerativeUI.tsx +43 -28
  781. package/src/react/primitives/message/MessageAttachments.test.tsx +2 -6
  782. package/src/react/primitives/message/MessageAttachments.tsx +14 -8
  783. package/src/react/primitives/message/MessageGroupedParts.test.tsx +155 -8
  784. package/src/react/primitives/message/MessageGroupedParts.tsx +19 -6
  785. package/src/react/primitives/message/MessageParts.test.tsx +84 -0
  786. package/src/react/primitives/message/MessageParts.tool-interaction.test.tsx +69 -0
  787. package/src/react/primitives/message/MessageParts.tsx +29 -8
  788. package/src/react/primitives/thread/ThreadMessages.test.tsx +25 -7
  789. package/src/react/primitives/thread/ThreadSuggestions.test.tsx +97 -0
  790. package/src/react/primitives/thread/ThreadSuggestions.tsx +29 -6
  791. package/src/react/providers/ReadonlyThreadProvider.tsx +2 -2
  792. package/src/react/providers/TextMessagePartProvider.tsx +3 -0
  793. package/src/react/runtimes/RemoteThreadListHookInstanceManager.layout.test.tsx +62 -0
  794. package/src/react/runtimes/RemoteThreadListHookInstanceManager.retry.test.tsx +2 -2
  795. package/src/react/runtimes/RemoteThreadListHookInstanceManager.running.test.tsx +93 -0
  796. package/src/react/runtimes/RemoteThreadListHookInstanceManager.test.tsx +181 -21
  797. package/src/react/runtimes/RemoteThreadListHookInstanceManager.title.test.tsx +62 -17
  798. package/src/react/runtimes/RemoteThreadListHookInstanceManager.tsx +112 -77
  799. package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.tsx +19 -3
  800. package/src/react/runtimes/RemoteThreadResource.ts +5 -2
  801. package/src/react/runtimes/assistant-transport/replayBoundaryStream.test.ts +3 -1
  802. package/src/react/runtimes/assistant-transport/replayBoundaryStream.ts +10 -2
  803. package/src/react/runtimes/assistant-transport/runManager.ts +3 -0
  804. package/src/react/runtimes/assistant-transport/transport-parentid.test.tsx +57 -0
  805. package/src/react/runtimes/assistant-transport/transport-scheduling.test.ts +73 -46
  806. package/src/react/runtimes/assistant-transport/transport-thread-id.test.tsx +571 -0
  807. package/src/react/runtimes/assistant-transport/types.ts +9 -1
  808. package/src/react/runtimes/assistant-transport/useAssistantTransport.spec.md +10 -0
  809. package/src/react/runtimes/assistant-transport/useAssistantTransportRuntime.test.tsx +108 -1
  810. package/src/react/runtimes/assistant-transport/useAssistantTransportRuntime.ts +33 -9
  811. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.copy.integration.test.tsx +289 -0
  812. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.copy.test.tsx +471 -0
  813. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.engagement.test.tsx +330 -0
  814. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.test.ts +78 -2
  815. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.test.tsx +463 -2
  816. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.ts +595 -227
  817. package/src/react/runtimes/cloud/CloudFileAttachmentAdapter.test.ts +33 -4
  818. package/src/react/runtimes/cloud/CloudFileAttachmentAdapter.ts +7 -3
  819. package/src/react/runtimes/cloud/auiV0.ts +275 -6
  820. package/src/react/runtimes/cloud/auiV0DecodeSafely.test.ts +873 -0
  821. package/src/react/runtimes/createMessageConverter.ts +9 -3
  822. package/src/react/runtimes/external-message-converter.test.ts +7 -3
  823. package/src/react/runtimes/external-store-history-copy.ts +223 -0
  824. package/src/react/runtimes/remote-thread-hook-on-tap.test.ts +2 -1
  825. package/src/react/runtimes/useExternalStoreRuntime.history-copy.test.tsx +483 -0
  826. package/src/react/runtimes/useExternalStoreRuntime.lifecycle.test.tsx +84 -3
  827. package/src/react/runtimes/useExternalStoreRuntime.suggestions.test.tsx +6 -2
  828. package/src/react/runtimes/useExternalStoreRuntime.ts +25 -5
  829. package/src/react/runtimes/useLocalRuntime.test.tsx +145 -1
  830. package/src/react/runtimes/useLocalRuntime.ts +8 -5
  831. package/src/react/runtimes/useRemoteThreadListRuntime.concurrent.test.tsx +84 -4
  832. package/src/react/runtimes/useRemoteThreadListRuntime.ts +5 -1
  833. package/src/react/types/MessagePartComponentTypes.ts +10 -0
  834. package/src/react/types/scopes/interactables.ts +3 -0
  835. package/src/runtime/api/attachment-runtime.ts +10 -5
  836. package/src/runtime/api/bindings.ts +6 -1
  837. package/src/runtime/api/composer-runtime.ts +29 -6
  838. package/src/runtime/api/message-part-runtime.test.ts +215 -0
  839. package/src/runtime/api/message-part-runtime.ts +44 -1
  840. package/src/runtime/api/message-runtime.test.ts +112 -2
  841. package/src/runtime/api/message-runtime.ts +25 -7
  842. package/src/runtime/api/thread-list-item-runtime.ts +5 -5
  843. package/src/runtime/api/thread-list-runtime.ts +3 -3
  844. package/src/runtime/api/thread-runtime.test.ts +59 -0
  845. package/src/runtime/api/thread-runtime.ts +21 -11
  846. package/src/runtime/base/base-composer-runtime-core.ts +548 -153
  847. package/src/runtime/base/base-thread-runtime-core.test.ts +1614 -136
  848. package/src/runtime/base/base-thread-runtime-core.ts +236 -46
  849. package/src/runtime/base/default-edit-composer-runtime-core.ts +24 -2
  850. package/src/runtime/base/default-thread-composer-runtime-core.ts +33 -3
  851. package/src/runtime/interfaces/composer-runtime-core.ts +19 -0
  852. package/src/runtime/interfaces/thread-runtime-core.ts +24 -1
  853. package/src/runtime/queue/message-queue.ts +11 -1
  854. package/src/runtime/utils/attachment-add-operations.ts +45 -2
  855. package/src/runtime/utils/attachment-send-operations.ts +54 -0
  856. package/src/runtime/utils/auto-status.test.ts +54 -0
  857. package/src/runtime/utils/auto-status.ts +3 -10
  858. package/src/runtime/utils/external-message-conversion.test.ts +91 -0
  859. package/src/runtime/utils/external-message-conversion.ts +9 -6
  860. package/src/runtime/utils/message-repository.test.ts +69 -1
  861. package/src/runtime/utils/message-repository.ts +14 -7
  862. package/src/runtime/utils/stored-message-parts.ts +216 -0
  863. package/src/runtime/utils/thread-message-like.test.ts +58 -0
  864. package/src/runtime/utils/thread-message-like.ts +79 -48
  865. package/src/runtime/utils/thread-runtime-lifecycle.test.ts +67 -0
  866. package/src/runtime/utils/thread-runtime-lifecycle.ts +42 -8
  867. package/src/runtime/utils/tool-interactions.test.ts +193 -0
  868. package/src/runtime/utils/tool-interactions.ts +147 -0
  869. package/src/runtimes/external-store/external-store-adapter.ts +25 -0
  870. package/src/runtimes/external-store/external-store-runtime-core.ts +10 -2
  871. package/src/runtimes/external-store/external-store-thread-list-runtime-core.ts +2 -2
  872. package/src/runtimes/external-store/external-store-thread-runtime-core.test.ts +49 -0
  873. package/src/runtimes/external-store/external-store-thread-runtime-core.ts +114 -32
  874. package/src/runtimes/inert/InertThreadRuntimeCore.test.ts +127 -56
  875. package/src/runtimes/inert/InertThreadRuntimeCore.ts +10 -1
  876. package/src/runtimes/local/local-thread-runtime-core.edit-cancellation.test.ts +70 -0
  877. package/src/runtimes/local/local-thread-runtime-core.test.ts +1866 -169
  878. package/src/runtimes/local/local-thread-runtime-core.ts +365 -40
  879. package/src/runtimes/readonly/ReadonlyThreadRuntimeCore.ts +53 -1
  880. package/src/runtimes/remote-thread-list/optimistic-state.ts +8 -3
  881. package/src/runtimes/tool-invocations/EDGE_CASES.md +19 -4
  882. package/src/runtimes/tool-invocations/ToolInvocationTracker.test.ts +392 -39
  883. package/src/runtimes/tool-invocations/ToolInvocationTracker.ts +31 -8
  884. package/src/store/clients/external-thread.test.ts +145 -0
  885. package/src/store/clients/external-thread.ts +669 -144
  886. package/src/store/clients/model-context-client.test.ts +8 -8
  887. package/src/store/clients/no-op-composer-client.ts +2 -0
  888. package/src/store/clients/submission-message.ts +27 -0
  889. package/src/store/clients/thread-message-client.test.ts +39 -0
  890. package/src/store/clients/thread-message-client.ts +13 -6
  891. package/src/store/index.ts +2 -2
  892. package/src/store/internal.ts +1 -0
  893. package/src/store/primitive-predicates.test.ts +119 -0
  894. package/src/store/primitive-predicates.ts +19 -4
  895. package/src/store/runtime-clients/composer-runtime-client.ts +4 -3
  896. package/src/store/runtime-clients/message-part-runtime-client.test.ts +42 -0
  897. package/src/store/runtime-clients/message-part-runtime-client.ts +4 -0
  898. package/src/store/runtime-clients/message-runtime-client.ts +5 -0
  899. package/src/store/runtime-clients/thread-runtime-client.test.tsx +243 -0
  900. package/src/store/runtime-clients/thread-runtime-client.ts +46 -9
  901. package/src/store/scopes/composer.ts +22 -4
  902. package/src/store/scopes/message.ts +12 -1
  903. package/src/store/scopes/part.ts +8 -0
  904. package/src/subscribable/subscribable.test.ts +205 -3
  905. package/src/subscribable/subscribable.ts +49 -7
  906. package/src/tests/RemoteThreadListThreadListRuntimeCore-errors.test.ts +95 -1
  907. package/src/tests/attachment-adapters.test.ts +16 -1
  908. package/src/tests/auiV0Encode.test.ts +412 -1
  909. package/src/tests/base-composer-runtime-core-addAttachment.test.ts +6 -6
  910. package/src/tests/base-composer-runtime-core-send.test.ts +722 -29
  911. package/src/tests/base-composer-runtime-core.test.ts +212 -5
  912. package/src/tests/composer-can-cancel.test.ts +35 -6
  913. package/src/tests/composer-can-send.test.ts +120 -1
  914. package/src/tests/composer-submission.test.tsx +796 -0
  915. package/src/tests/default-edit-composer-runtime-core.test.ts +149 -2
  916. package/src/tests/external-store-thread-list-runtime-core.test.ts +111 -0
  917. package/src/tests/external-store-thread-runtime-core-adapter.test.ts +810 -28
  918. package/src/tests/external-store-thread-runtime-core.test.ts +217 -0
  919. package/src/tests/external-thread-attachments.test.tsx +763 -26
  920. package/src/tests/external-thread-feedback.test.tsx +20 -7
  921. package/src/tests/external-thread-isLast.test.tsx +69 -0
  922. package/src/tests/external-thread-parity.test.tsx +1 -1
  923. package/src/tests/get-thread-state-isRunning.test.ts +2 -2
  924. package/src/tests/message-queue.test.ts +45 -10
  925. package/src/tests/remote-thread-list-adapter-switch.test.ts +45 -1
  926. package/src/tests/thread-switch-events.test.tsx +11 -7
  927. package/src/tests/useRemoteThreadListRuntime-controlled.test.tsx +2 -1
  928. package/src/types/message.test.ts +19 -0
  929. package/src/types/message.ts +65 -10
  930. package/src/utils/normalizePartStatus.ts +14 -1
  931. package/dist/tests/remote-thread-list-test-helpers.d.ts +0 -16
  932. package/dist/tests/remote-thread-list-test-helpers.d.ts.map +0 -1
  933. package/dist/tests/remote-thread-list-test-helpers.js +0 -59
  934. package/dist/tests/remote-thread-list-test-helpers.js.map +0 -1
@@ -3,7 +3,9 @@ import type { FeedbackAdapter } from "../../adapters/feedback";
3
3
  import type { SpeechSynthesisAdapter } from "../../adapters/speech";
4
4
  import type { RealtimeVoiceAdapter } from "../../adapters/voice";
5
5
  import type { ModelContextProvider } from "../../model-context/types";
6
- import type { AppendMessage } from "../../types/message";
6
+ import type { AppendMessage, ThreadMessage } from "../../types/message";
7
+ import type { ChatModelRunResult } from "../../runtime/utils/chat-model-adapter";
8
+ import { isMessageNotSentError } from "../../types/error";
7
9
  import { CompositeContextProvider } from "../../utils/composite-context-provider";
8
10
  import type {
9
11
  AddToolResultOptions,
@@ -16,9 +18,15 @@ import type {
16
18
  } from "../interfaces/thread-runtime-core";
17
19
  import { BaseThreadRuntimeCore } from "./base-thread-runtime-core";
18
20
  import { LocalRuntimeCore } from "../../runtimes/local/local-runtime-core";
21
+ import { disposeThreadRuntime } from "../utils/thread-runtime-lifecycle";
19
22
 
20
- const createVoiceAdapter = () => {
23
+ const createVoiceAdapter = ({
24
+ sendText,
25
+ }: { sendText?: RealtimeVoiceAdapter.Session["sendText"] } = {}) => {
21
26
  let volumeCallback: ((volume: number) => void) | undefined;
27
+ let statusCallback:
28
+ | ((status: RealtimeVoiceAdapter.Status) => void)
29
+ | undefined;
22
30
  let transcriptCallback:
23
31
  | ((transcript: RealtimeVoiceAdapter.TranscriptItem) => void)
24
32
  | undefined;
@@ -28,7 +36,13 @@ const createVoiceAdapter = () => {
28
36
  disconnect: vi.fn(),
29
37
  mute: vi.fn(),
30
38
  unmute: vi.fn(),
31
- onStatusChange: () => () => {},
39
+ ...(sendText && { sendText }),
40
+ onStatusChange: (callback) => {
41
+ statusCallback = callback;
42
+ return () => {
43
+ statusCallback = undefined;
44
+ };
45
+ },
32
46
  onTranscript: (callback) => {
33
47
  transcriptCallback = callback;
34
48
  return () => {
@@ -47,12 +61,15 @@ const createVoiceAdapter = () => {
47
61
  return {
48
62
  adapter: { connect: () => session },
49
63
  emitVolume: (volume: number) => volumeCallback?.(volume),
64
+ emitStatus: (status: RealtimeVoiceAdapter.Status) =>
65
+ statusCallback?.(status),
50
66
  emitTranscript: (transcript: RealtimeVoiceAdapter.TranscriptItem) =>
51
67
  transcriptCallback?.(transcript),
52
68
  session,
53
69
  } satisfies {
54
70
  adapter: RealtimeVoiceAdapter;
55
71
  emitVolume: (volume: number) => void;
72
+ emitStatus: (status: RealtimeVoiceAdapter.Status) => void;
56
73
  emitTranscript: (transcript: RealtimeVoiceAdapter.TranscriptItem) => void;
57
74
  session: RealtimeVoiceAdapter.Session;
58
75
  };
@@ -1077,192 +1094,1652 @@ describe("BaseThreadRuntimeCore voice volume subscriptions", () => {
1077
1094
  });
1078
1095
 
1079
1096
  describe("BaseThreadRuntimeCore voice transcripts", () => {
1080
- it("speaks an assistant transcript", () => {
1097
+ const createLocalVoiceThread = async (
1098
+ sessionOptions: Parameters<typeof createVoiceAdapter>[0] = {},
1099
+ contextProvider?: ModelContextProvider,
1100
+ ) => {
1101
+ const voiceAdapter = createVoiceAdapter(sessionOptions);
1102
+ const history = {
1103
+ load: vi.fn(async () => ({ messages: [] })),
1104
+ append: vi.fn(async () => {}),
1105
+ };
1106
+ const run = vi.fn(async () => ({}));
1107
+ const runtime = new LocalRuntimeCore(
1108
+ {
1109
+ adapters: {
1110
+ chatModel: { run },
1111
+ history,
1112
+ voice: voiceAdapter.adapter,
1113
+ },
1114
+ },
1115
+ undefined,
1116
+ );
1117
+ if (contextProvider) runtime.registerModelContextProvider(contextProvider);
1118
+ const thread = runtime.threads.getMainThreadRuntimeCore();
1119
+ await thread.__internal_load();
1120
+ thread.connectVoice();
1121
+ return { history, run, thread, voiceAdapter };
1122
+ };
1123
+
1124
+ const typedMessage = (
1125
+ thread: { messages: readonly ThreadMessage[] },
1126
+ text = "Text message",
1127
+ ): AppendMessage => ({
1128
+ parentId: thread.messages.at(-1)?.id ?? null,
1129
+ sourceId: null,
1130
+ role: "user",
1131
+ content: [{ type: "text", text }],
1132
+ attachments: [],
1133
+ metadata: { custom: { quote: "context" } },
1134
+ createdAt: new Date(),
1135
+ runConfig: {},
1136
+ });
1137
+
1138
+ it("holds a queued send that becomes dispatchable while a voice session is connected", async () => {
1139
+ let resolveFirst!: (result: ChatModelRunResult) => void;
1140
+ const firstRun = new Promise<ChatModelRunResult>((resolve) => {
1141
+ resolveFirst = resolve;
1142
+ });
1143
+ let runCount = 0;
1144
+ const run = vi.fn(() =>
1145
+ ++runCount === 1 ? firstRun : Promise.resolve({}),
1146
+ );
1081
1147
  const voiceAdapter = createVoiceAdapter();
1082
- const speech = {
1083
- speak: vi.fn(() => ({
1084
- status: { type: "running" as const },
1085
- cancel: vi.fn(),
1086
- subscribe: () => () => {},
1087
- })),
1088
- } satisfies SpeechSynthesisAdapter;
1089
- const runtime = new TestRuntime(voiceAdapter, undefined, { speech });
1090
- runtime.connectVoice();
1148
+ const runtime = new LocalRuntimeCore(
1149
+ {
1150
+ unstable_enableMessageQueue: true,
1151
+ adapters: { chatModel: { run }, voice: voiceAdapter.adapter },
1152
+ },
1153
+ undefined,
1154
+ );
1155
+ const thread = runtime.threads.getMainThreadRuntimeCore();
1156
+ const firstMessage: AppendMessage = {
1157
+ parentId: null,
1158
+ sourceId: null,
1159
+ runConfig: {},
1160
+ role: "user",
1161
+ content: [{ type: "text", text: "first" }],
1162
+ attachments: [],
1163
+ metadata: { custom: {} },
1164
+ createdAt: new Date(),
1165
+ };
1166
+
1167
+ await thread.append(firstMessage);
1168
+ expect(run).toHaveBeenCalledOnce();
1169
+ await thread.append({
1170
+ ...firstMessage,
1171
+ parentId: thread.messages.at(-1)?.id ?? null,
1172
+ content: [{ type: "text", text: "second" }],
1173
+ steer: false,
1174
+ });
1175
+
1176
+ const deferred: Array<() => void> = [];
1177
+ const originalQueueMicrotask = globalThis.queueMicrotask;
1178
+ globalThis.queueMicrotask = (callback: () => void) => {
1179
+ deferred.push(callback);
1180
+ };
1181
+ try {
1182
+ resolveFirst({});
1183
+ await new Promise((resolve) => setTimeout(resolve, 0));
1184
+ } finally {
1185
+ globalThis.queueMicrotask = originalQueueMicrotask;
1186
+ }
1187
+ expect(thread.messages.at(-1)?.status?.type).toBe("complete");
1188
+
1189
+ thread.connectVoice();
1190
+ try {
1191
+ for (const callback of deferred) callback();
1192
+ await Promise.resolve();
1193
+ expect(run).toHaveBeenCalledOnce();
1194
+ expect(thread.getQueueItems()).toHaveLength(1);
1195
+ } finally {
1196
+ thread.disconnectVoice();
1197
+ }
1198
+ await Promise.resolve();
1199
+
1200
+ expect(run).toHaveBeenCalledTimes(2);
1201
+ expect(thread.getQueueItems()).toHaveLength(0);
1202
+ });
1203
+
1204
+ it("commits a final user transcript to the repository and history", async () => {
1205
+ const { history, thread, voiceAdapter } = await createLocalVoiceThread();
1091
1206
 
1092
1207
  try {
1093
1208
  voiceAdapter.emitTranscript({
1094
- role: "assistant",
1209
+ role: "user",
1095
1210
  text: "Hello",
1096
1211
  isFinal: true,
1097
1212
  });
1098
- const message = runtime.messages[0]!;
1213
+ const message = thread.messages[0]!;
1099
1214
 
1100
- expect(() => runtime.speak(message.id)).not.toThrow();
1101
- expect(speech.speak).toHaveBeenCalledExactlyOnceWith("Hello");
1215
+ expect(message.metadata.modality).toBe("voice");
1216
+ expect(history.append).toHaveBeenCalledExactlyOnceWith({
1217
+ parentId: null,
1218
+ message,
1219
+ });
1220
+ expect(thread.export().messages).toContainEqual({
1221
+ parentId: null,
1222
+ message,
1223
+ });
1102
1224
  } finally {
1103
- runtime.disconnectVoice();
1225
+ thread.disconnectVoice();
1104
1226
  }
1105
1227
  });
1106
1228
 
1107
- it("stops speaking a transcript when the session disconnects", () => {
1108
- const voiceAdapter = createVoiceAdapter();
1109
- const utterance = {
1110
- status: { type: "running" as const },
1111
- cancel: vi.fn(),
1112
- subscribe: () => () => {},
1113
- };
1114
- const speech = {
1115
- speak: vi.fn(() => utterance),
1116
- } satisfies SpeechSynthesisAdapter;
1117
- const runtime = new TestRuntime(voiceAdapter, undefined, { speech });
1118
- runtime.connectVoice();
1229
+ it("commits an assistant transcript when it finalizes", async () => {
1230
+ const { history, thread, voiceAdapter } = await createLocalVoiceThread();
1119
1231
 
1120
- voiceAdapter.emitTranscript({
1121
- role: "assistant",
1122
- text: "Hello",
1123
- isFinal: true,
1124
- });
1125
- runtime.speak(runtime.messages[0]!.id);
1126
- expect(runtime.speech?.messageId).toBe(runtime.messages[0]!.id);
1232
+ try {
1233
+ voiceAdapter.emitTranscript({ role: "assistant", text: "Partial" });
1127
1234
 
1128
- runtime.disconnectVoice();
1235
+ expect(thread.messages[0]?.status).toEqual({ type: "running" });
1236
+ expect(history.append).not.toHaveBeenCalled();
1129
1237
 
1130
- expect(utterance.cancel).toHaveBeenCalledOnce();
1131
- expect(runtime.speech).toBeUndefined();
1238
+ voiceAdapter.emitTranscript({
1239
+ role: "assistant",
1240
+ text: "Complete",
1241
+ isFinal: true,
1242
+ });
1243
+ const message = thread.messages[0]!;
1244
+
1245
+ expect(message.status).toEqual({ type: "complete", reason: "stop" });
1246
+ expect(history.append).toHaveBeenCalledExactlyOnceWith({
1247
+ parentId: null,
1248
+ message,
1249
+ });
1250
+ } finally {
1251
+ thread.disconnectVoice();
1252
+ }
1132
1253
  });
1133
1254
 
1134
- it("still disconnects the session when stopping a spoken transcript throws", () => {
1135
- const voiceAdapter = createVoiceAdapter();
1136
- const utterance = {
1137
- status: { type: "running" as const },
1138
- cancel: vi.fn(() => {
1139
- throw new Error("cancel failed");
1140
- }),
1141
- subscribe: () => () => {},
1142
- };
1143
- const speech = {
1144
- speak: vi.fn(() => utterance),
1145
- } satisfies SpeechSynthesisAdapter;
1146
- const runtime = new TestRuntime(voiceAdapter, undefined, { speech });
1147
- runtime.connectVoice();
1255
+ it("commits an in-flight assistant transcript when disconnecting", async () => {
1256
+ const { history, thread, voiceAdapter } = await createLocalVoiceThread();
1257
+
1258
+ voiceAdapter.emitTranscript({ role: "assistant", text: "Partial" });
1259
+ thread.disconnectVoice();
1260
+ const message = thread.messages[0]!;
1261
+
1262
+ expect(message.status).toEqual({ type: "complete", reason: "stop" });
1263
+ expect(history.append).toHaveBeenCalledExactlyOnceWith({
1264
+ parentId: null,
1265
+ message,
1266
+ });
1267
+ });
1268
+
1269
+ it("keeps committed transcripts after disconnect", async () => {
1270
+ const { thread, voiceAdapter } = await createLocalVoiceThread();
1148
1271
 
1149
1272
  voiceAdapter.emitTranscript({
1150
- role: "assistant",
1273
+ role: "user",
1151
1274
  text: "Hello",
1152
1275
  isFinal: true,
1153
1276
  });
1154
- runtime.speak(runtime.messages[0]!.id);
1277
+ const message = thread.messages[0]!;
1155
1278
 
1156
- expect(() => runtime.disconnectVoice()).toThrow("cancel failed");
1157
- expect(utterance.cancel).toHaveBeenCalledOnce();
1158
- expect(voiceAdapter.session.disconnect).toHaveBeenCalledOnce();
1159
- expect(runtime.messages).toHaveLength(0);
1160
- expect(runtime.speech).toBeUndefined();
1161
- expect(runtime.voice).toBeUndefined();
1279
+ thread.disconnectVoice();
1280
+
1281
+ expect(thread.messages).toEqual([message]);
1162
1282
  });
1163
1283
 
1164
- it("submits feedback for a transcript", () => {
1165
- const voiceAdapter = createVoiceAdapter();
1166
- const feedback = { submit: vi.fn() } satisfies FeedbackAdapter;
1167
- const runtime = new TestRuntime(voiceAdapter, undefined, { feedback });
1168
- runtime.connectVoice();
1284
+ it("links the second transcript to the first", async () => {
1285
+ const { thread, voiceAdapter } = await createLocalVoiceThread();
1169
1286
 
1170
1287
  try {
1171
1288
  voiceAdapter.emitTranscript({
1172
- role: "assistant",
1173
- text: "Hello",
1289
+ role: "user",
1290
+ text: "First",
1174
1291
  isFinal: true,
1175
1292
  });
1176
- const message = runtime.messages[0]!;
1177
-
1178
- expect(() =>
1179
- runtime.submitFeedback({ messageId: message.id, type: "positive" }),
1180
- ).not.toThrow();
1181
- expect(feedback.submit).toHaveBeenCalledExactlyOnceWith({
1182
- message,
1183
- type: "positive",
1293
+ const first = thread.messages[0]!;
1294
+ voiceAdapter.emitTranscript({
1295
+ role: "user",
1296
+ text: "Second",
1297
+ isFinal: true,
1184
1298
  });
1185
- expect(runtime.messages[0]?.metadata.submittedFeedback).toEqual({
1186
- type: "positive",
1299
+ const second = thread.messages[1]!;
1300
+
1301
+ expect(thread.export().messages).toContainEqual({
1302
+ parentId: first.id,
1303
+ message: second,
1187
1304
  });
1188
1305
  } finally {
1189
- runtime.disconnectVoice();
1306
+ thread.disconnectVoice();
1190
1307
  }
1191
1308
  });
1192
1309
 
1193
- it("rejects an edit resubmission of a transcript before any side effect", async () => {
1194
- const voiceAdapter = createVoiceAdapter();
1195
- const run = vi.fn(async () => ({}));
1196
- const runtime = new LocalRuntimeCore(
1197
- { adapters: { chatModel: { run }, voice: voiceAdapter.adapter } },
1198
- undefined,
1310
+ it("rejects a text send while a session is connected", async () => {
1311
+ const { thread, voiceAdapter } = await createLocalVoiceThread();
1312
+
1313
+ voiceAdapter.emitTranscript({
1314
+ role: "user",
1315
+ text: "Voice message",
1316
+ isFinal: true,
1317
+ });
1318
+ const transcript = thread.messages[0]!;
1319
+ const message: AppendMessage = {
1320
+ parentId: transcript.id,
1321
+ sourceId: null,
1322
+ role: "user",
1323
+ content: [{ type: "text", text: "Text message" }],
1324
+ attachments: [],
1325
+ metadata: { custom: {} },
1326
+ createdAt: new Date(),
1327
+ runConfig: {},
1328
+ startRun: false,
1329
+ };
1330
+
1331
+ expect(thread.voice?.canSendText).toBe(false);
1332
+ await expect(thread.append(message)).rejects.toThrow(
1333
+ "Cannot send a text message while a voice session is connected",
1334
+ );
1335
+ expect(thread.export().messages).toHaveLength(1);
1336
+
1337
+ thread.disconnectVoice();
1338
+
1339
+ await expect(thread.append(message)).resolves.toBeUndefined();
1340
+ expect(thread.export().messages).toContainEqual({
1341
+ parentId: transcript.id,
1342
+ message: expect.objectContaining({ role: "user" }),
1343
+ });
1344
+ });
1345
+
1346
+ it("sends a typed message into a session that takes text and commits it once as a typed turn", async () => {
1347
+ const sendText = vi.fn(async (_text: string) => {});
1348
+ const { history, run, thread, voiceAdapter } = await createLocalVoiceThread(
1349
+ { sendText },
1199
1350
  );
1200
- const thread = runtime.threads.getMainThreadRuntimeCore();
1201
- thread.connectVoice();
1202
1351
 
1203
1352
  try {
1204
1353
  voiceAdapter.emitTranscript({
1205
- role: "assistant",
1206
- text: "Hello",
1354
+ role: "user",
1355
+ text: "Voice message",
1207
1356
  isFinal: true,
1208
1357
  });
1209
- const edit = {
1210
- parentId: null,
1211
- sourceId: thread.messages[0]!.id,
1212
- role: "user" as const,
1213
- content: [{ type: "text" as const, text: "again" }],
1214
- attachments: [],
1215
- metadata: { custom: {} },
1216
- createdAt: new Date(),
1217
- runConfig: {},
1218
- };
1358
+ const transcript = thread.messages[0]!;
1359
+ expect(thread.voice?.canSendText).toBe(true);
1219
1360
 
1220
- await expect(thread.append(edit)).rejects.toThrow(
1221
- "Voice transcript messages cannot be edited",
1222
- );
1223
- await expect(thread.append({ ...edit, startRun: false })).rejects.toThrow(
1224
- "Voice transcript messages cannot be edited",
1225
- );
1361
+ await thread.append(typedMessage(thread));
1362
+
1363
+ expect(sendText).toHaveBeenCalledExactlyOnceWith("Text message");
1364
+ expect(thread.messages).toHaveLength(2);
1365
+ const typed = thread.messages[1]!;
1366
+ expect(typed).toMatchObject({
1367
+ role: "user",
1368
+ content: [{ type: "text", text: "Text message" }],
1369
+ metadata: { custom: { quote: "context" } },
1370
+ });
1371
+ expect(typed.metadata.modality).toBeUndefined();
1372
+ expect(history.append).toHaveBeenLastCalledWith({
1373
+ parentId: transcript.id,
1374
+ message: typed,
1375
+ });
1376
+ expect(thread.export().messages).toContainEqual({
1377
+ parentId: transcript.id,
1378
+ message: typed,
1379
+ });
1226
1380
  expect(run).not.toHaveBeenCalled();
1227
- expect(thread.messages).toHaveLength(1);
1228
1381
  } finally {
1229
1382
  thread.disconnectVoice();
1230
1383
  }
1231
1384
  });
1232
1385
 
1233
- it("rejects reloading a transcript", async () => {
1234
- const voiceAdapter = createVoiceAdapter();
1235
- const runtime = new LocalRuntimeCore(
1236
- {
1237
- adapters: {
1238
- chatModel: {
1239
- async run() {
1240
- return {};
1241
- },
1242
- },
1243
- voice: voiceAdapter.adapter,
1244
- },
1245
- },
1246
- undefined,
1247
- );
1248
- const thread = runtime.threads.getMainThreadRuntimeCore();
1249
- thread.connectVoice();
1386
+ it("finishes the reply being spoken before a typed turn", async () => {
1387
+ const sendText = vi.fn();
1388
+ const { thread, voiceAdapter } = await createLocalVoiceThread({
1389
+ sendText,
1390
+ });
1250
1391
 
1251
1392
  try {
1252
- voiceAdapter.emitTranscript({
1253
- role: "assistant",
1254
- text: "Hello",
1255
- isFinal: true,
1256
- });
1257
- const message = thread.messages[0]!;
1393
+ voiceAdapter.emitTranscript({ role: "assistant", text: "Hel" });
1394
+ expect(thread.messages[0]?.status?.type).toBe("running");
1258
1395
 
1259
- await expect(
1260
- thread.startRun({
1261
- parentId: null,
1262
- sourceId: message.id,
1263
- runConfig: {},
1396
+ await thread.append(typedMessage(thread));
1397
+
1398
+ expect(thread.messages.map((message) => message.role)).toEqual([
1399
+ "assistant",
1400
+ "user",
1401
+ ]);
1402
+ expect(thread.messages[0]?.status?.type).toBe("complete");
1403
+ } finally {
1404
+ thread.disconnectVoice();
1405
+ }
1406
+ });
1407
+
1408
+ it("hands the draft back when the session rejects the typed text", async () => {
1409
+ const failure = new Error("send failed");
1410
+ const sendText = vi.fn(async () => {
1411
+ throw failure;
1412
+ });
1413
+ const { history, thread } = await createLocalVoiceThread({ sendText });
1414
+
1415
+ try {
1416
+ const rejection = await thread.append(typedMessage(thread)).then(
1417
+ () => undefined,
1418
+ (error: unknown) => error,
1419
+ );
1420
+
1421
+ expect(isMessageNotSentError(rejection)).toBe(true);
1422
+ expect((rejection as Error).cause).toBe(failure);
1423
+ expect(thread.messages).toEqual([]);
1424
+ expect(history.append).not.toHaveBeenCalled();
1425
+ } finally {
1426
+ thread.disconnectVoice();
1427
+ }
1428
+ });
1429
+
1430
+ it("restores the composer draft when the session rejects a typed send", async () => {
1431
+ const sendText = vi.fn(async () => {
1432
+ throw new Error("send failed");
1433
+ });
1434
+ const { thread } = await createLocalVoiceThread({ sendText });
1435
+
1436
+ try {
1437
+ thread.composer.setText("Text message");
1438
+ expect(thread.composer.canSend).toBe(true);
1439
+
1440
+ await thread.composer.send();
1441
+ await new Promise((resolve) => setTimeout(resolve, 0));
1442
+
1443
+ expect(sendText).toHaveBeenCalledExactlyOnceWith("Text message");
1444
+ expect(thread.composer.text).toBe("Text message");
1445
+ expect(thread.messages).toEqual([]);
1446
+ } finally {
1447
+ thread.disconnectVoice();
1448
+ }
1449
+ });
1450
+
1451
+ it("hands the draft back when the session ends while the text is in flight", async () => {
1452
+ let resolveSend!: () => void;
1453
+ const sendText = vi.fn(
1454
+ () =>
1455
+ new Promise<void>((resolve) => {
1456
+ resolveSend = resolve;
1264
1457
  }),
1265
- ).rejects.toThrow("Voice transcript messages cannot be reloaded");
1458
+ );
1459
+ const { history, thread } = await createLocalVoiceThread({ sendText });
1460
+
1461
+ const pending = thread.append(typedMessage(thread));
1462
+ await Promise.resolve();
1463
+ thread.disconnectVoice();
1464
+ resolveSend();
1465
+
1466
+ const rejection = await pending.then(
1467
+ () => undefined,
1468
+ (error: unknown) => error,
1469
+ );
1470
+
1471
+ expect(isMessageNotSentError(rejection)).toBe(true);
1472
+ expect(thread.messages).toEqual([]);
1473
+ expect(history.append).not.toHaveBeenCalled();
1474
+ });
1475
+
1476
+ it("surfaces a history failure for a typed turn while keeping it in the thread", async () => {
1477
+ const failure = new Error("history down");
1478
+ const sendText = vi.fn();
1479
+ const { history, thread } = await createLocalVoiceThread({ sendText });
1480
+ history.append.mockRejectedValueOnce(failure);
1481
+
1482
+ try {
1483
+ await expect(thread.append(typedMessage(thread))).rejects.toBe(failure);
1484
+
1485
+ expect(thread.messages).toHaveLength(1);
1486
+ expect(thread.export().messages).toHaveLength(1);
1487
+ } finally {
1488
+ thread.disconnectVoice();
1489
+ }
1490
+ });
1491
+
1492
+ it("carries the composer metadata of a typed turn like a text send", async () => {
1493
+ const sendText = vi.fn();
1494
+ const { thread } = await createLocalVoiceThread(
1495
+ { sendText },
1496
+ {
1497
+ getModelContext: () => ({
1498
+ unstable_composerMetadata: { form: { answer: 42 } },
1499
+ }),
1500
+ },
1501
+ );
1502
+
1503
+ try {
1504
+ await thread.append(typedMessage(thread));
1505
+
1506
+ expect(thread.messages[0]?.metadata.custom).toEqual({
1507
+ quote: "context",
1508
+ form: { answer: 42 },
1509
+ });
1510
+ } finally {
1511
+ thread.disconnectVoice();
1512
+ }
1513
+ });
1514
+
1515
+ it("keeps rejecting a typed send until the session is running", async () => {
1516
+ const sendText = vi.fn();
1517
+ const { thread, voiceAdapter } = await createLocalVoiceThread({
1518
+ sendText,
1519
+ });
1520
+
1521
+ try {
1522
+ voiceAdapter.emitStatus({ type: "starting" });
1523
+ expect(thread.voice?.canSendText).toBe(false);
1524
+
1525
+ await expect(thread.append(typedMessage(thread))).rejects.toThrow(
1526
+ "Cannot send a text message while a voice session is connected",
1527
+ );
1528
+
1529
+ voiceAdapter.emitStatus({ type: "running" });
1530
+ expect(thread.voice?.canSendText).toBe(true);
1531
+
1532
+ await thread.append(typedMessage(thread));
1533
+
1534
+ expect(sendText).toHaveBeenCalledExactlyOnceWith("Text message");
1535
+ } finally {
1536
+ thread.disconnectVoice();
1537
+ }
1538
+ });
1539
+
1540
+ it("rejects anything but a plain text user message at the tail while a session takes text", async () => {
1541
+ const sendText = vi.fn();
1542
+ const { thread, voiceAdapter } = await createLocalVoiceThread({
1543
+ sendText,
1544
+ });
1545
+ const rejection =
1546
+ "Only a plain text user message can be sent while a voice session is connected";
1547
+
1548
+ try {
1549
+ voiceAdapter.emitTranscript({
1550
+ role: "user",
1551
+ text: "Voice message",
1552
+ isFinal: true,
1553
+ });
1554
+ const base = typedMessage(thread);
1555
+
1556
+ await expect(
1557
+ thread.append({
1558
+ ...base,
1559
+ attachments: [
1560
+ {
1561
+ id: "att-1",
1562
+ type: "document",
1563
+ name: "f.txt",
1564
+ contentType: "text/plain",
1565
+ content: [],
1566
+ status: { type: "complete" },
1567
+ },
1568
+ ],
1569
+ }),
1570
+ ).rejects.toThrow(rejection);
1571
+ await expect(
1572
+ thread.append({ ...base, sourceId: thread.messages[0]!.id }),
1573
+ ).rejects.toThrow(rejection);
1574
+ await expect(thread.append({ ...base, parentId: null })).rejects.toThrow(
1575
+ rejection,
1576
+ );
1577
+ await expect(
1578
+ thread.append({
1579
+ ...base,
1580
+ content: [{ type: "image", image: "data:image/png;base64," }],
1581
+ }),
1582
+ ).rejects.toThrow(rejection);
1583
+ await expect(
1584
+ thread.append({ ...base, content: [{ type: "text", text: " " }] }),
1585
+ ).rejects.toThrow(rejection);
1586
+ await expect(
1587
+ thread.append({
1588
+ ...base,
1589
+ role: "assistant",
1590
+ metadata: { custom: {} },
1591
+ } as AppendMessage),
1592
+ ).rejects.toThrow(rejection);
1593
+
1594
+ expect(sendText).not.toHaveBeenCalled();
1595
+ expect(thread.messages).toHaveLength(1);
1596
+ } finally {
1597
+ thread.disconnectVoice();
1598
+ }
1599
+ });
1600
+
1601
+ it("keeps the replacement session's hooks when a subscriber reconnects during disconnect", () => {
1602
+ class HookRuntime extends TestRuntime {
1603
+ connected = 0;
1604
+ disconnected = 0;
1605
+ protected override _onVoiceConnected() {
1606
+ this.connected += 1;
1607
+ }
1608
+ protected override _onVoiceDisconnected() {
1609
+ this.disconnected += 1;
1610
+ }
1611
+ }
1612
+ const runtime = new HookRuntime(createVoiceAdapter());
1613
+ runtime.connectVoice();
1614
+ expect(runtime.connected).toBe(1);
1615
+
1616
+ let reconnected = false;
1617
+ const unsubscribe = runtime.subscribe(() => {
1618
+ if (reconnected || runtime.voice !== undefined) return;
1619
+ reconnected = true;
1620
+ runtime.connectVoice();
1621
+ });
1622
+
1623
+ try {
1624
+ runtime.disconnectVoice();
1625
+ expect(runtime.voice).toBeDefined();
1626
+ expect(runtime.connected).toBe(2);
1627
+ expect(runtime.disconnected).toBe(0);
1628
+ } finally {
1629
+ unsubscribe();
1630
+ runtime.disconnectVoice();
1631
+ }
1632
+ expect(runtime.disconnected).toBe(1);
1633
+ });
1634
+
1635
+ it("keeps voice hooks held while replacing a session", () => {
1636
+ class HookRuntime extends TestRuntime {
1637
+ connected = 0;
1638
+ disconnected = 0;
1639
+ protected override _onVoiceConnected() {
1640
+ this.connected += 1;
1641
+ }
1642
+ protected override _onVoiceDisconnected() {
1643
+ this.disconnected += 1;
1644
+ }
1645
+ }
1646
+ const runtime = new HookRuntime(createVoiceAdapter());
1647
+
1648
+ runtime.connectVoice();
1649
+ runtime.connectVoice();
1650
+
1651
+ expect(runtime.connected).toBe(2);
1652
+ expect(runtime.disconnected).toBe(0);
1653
+
1654
+ runtime.disconnectVoice();
1655
+
1656
+ expect(runtime.disconnected).toBe(1);
1657
+ });
1658
+
1659
+ it("ignores an ended status from a replaced session", () => {
1660
+ class HookRuntime extends TestRuntime {
1661
+ disconnected = 0;
1662
+ protected override _onVoiceDisconnected() {
1663
+ this.disconnected += 1;
1664
+ }
1665
+ }
1666
+ const statusCallbacks: Array<
1667
+ (status: RealtimeVoiceAdapter.Status) => void
1668
+ > = [];
1669
+ const createSession = (): RealtimeVoiceAdapter.Session => ({
1670
+ status: { type: "running" },
1671
+ isMuted: false,
1672
+ disconnect: vi.fn(),
1673
+ mute: vi.fn(),
1674
+ unmute: vi.fn(),
1675
+ onStatusChange: (callback) => {
1676
+ statusCallbacks.push(callback);
1677
+ return () => {};
1678
+ },
1679
+ onTranscript: () => () => {},
1680
+ onModeChange: () => () => {},
1681
+ onVolumeChange: () => () => {},
1682
+ });
1683
+ const runtime = new HookRuntime({
1684
+ adapter: { connect: () => createSession() },
1685
+ emitVolume: () => {},
1686
+ emitStatus: () => {},
1687
+ emitTranscript: () => {},
1688
+ session: createSession(),
1689
+ });
1690
+
1691
+ runtime.connectVoice();
1692
+ runtime.connectVoice();
1693
+ statusCallbacks[0]!({ type: "ended", reason: "finished" });
1694
+
1695
+ try {
1696
+ expect(runtime.voice).toBeDefined();
1697
+ expect(runtime.disconnected).toBe(0);
1698
+ } finally {
1699
+ runtime.disconnectVoice();
1700
+ }
1701
+ expect(runtime.disconnected).toBe(1);
1702
+ });
1703
+
1704
+ it("releases voice hooks when a replacement cannot connect", () => {
1705
+ class HookRuntime extends TestRuntime {
1706
+ connected = 0;
1707
+ disconnected = 0;
1708
+ protected override _onVoiceConnected() {
1709
+ this.connected += 1;
1710
+ }
1711
+ protected override _onVoiceDisconnected() {
1712
+ this.disconnected += 1;
1713
+ }
1714
+ }
1715
+ const voice = createVoiceAdapter();
1716
+ const error = new Error("connection failed");
1717
+ voice.adapter.connect = vi
1718
+ .fn()
1719
+ .mockReturnValueOnce(voice.session)
1720
+ .mockImplementationOnce(() => {
1721
+ throw error;
1722
+ });
1723
+ const runtime = new HookRuntime(voice);
1724
+ runtime.connectVoice();
1725
+
1726
+ expect(() => runtime.connectVoice()).toThrow(error);
1727
+ expect(runtime.connected).toBe(1);
1728
+ expect(runtime.disconnected).toBe(1);
1729
+ });
1730
+
1731
+ it("rejects starting a voice session while a run is in progress", async () => {
1732
+ const voiceAdapter = createVoiceAdapter();
1733
+ let resolveRun!: (result: ChatModelRunResult) => void;
1734
+ const firstRun = new Promise<ChatModelRunResult>((resolve) => {
1735
+ resolveRun = resolve;
1736
+ });
1737
+ const run = vi.fn(() => firstRun);
1738
+ const runtime = new LocalRuntimeCore(
1739
+ { adapters: { chatModel: { run }, voice: voiceAdapter.adapter } },
1740
+ undefined,
1741
+ );
1742
+ const thread = runtime.threads.getMainThreadRuntimeCore();
1743
+ const pending = thread.append({
1744
+ parentId: null,
1745
+ sourceId: null,
1746
+ role: "user",
1747
+ content: [{ type: "text", text: "hello" }],
1748
+ attachments: [],
1749
+ metadata: { custom: {} },
1750
+ createdAt: new Date(),
1751
+ runConfig: {},
1752
+ });
1753
+ await new Promise((resolve) => setTimeout(resolve, 0));
1754
+ expect(run).toHaveBeenCalledOnce();
1755
+
1756
+ expect(() => thread.connectVoice()).toThrow(
1757
+ "Cannot start a voice session while a run is in progress or paused on a pending tool action",
1758
+ );
1759
+ expect(thread.voice).toBeUndefined();
1760
+
1761
+ resolveRun({});
1762
+ await pending;
1763
+
1764
+ thread.connectVoice();
1765
+ try {
1766
+ expect(thread.voice).toBeDefined();
1767
+ } finally {
1768
+ thread.disconnectVoice();
1769
+ }
1770
+ });
1771
+
1772
+ it("rejects starting a voice session while a run is paused on a tool action", () => {
1773
+ const voiceAdapter = createVoiceAdapter();
1774
+ const runtime = new LocalRuntimeCore(
1775
+ {
1776
+ adapters: {
1777
+ chatModel: {
1778
+ async run() {
1779
+ return {};
1780
+ },
1781
+ },
1782
+ voice: voiceAdapter.adapter,
1783
+ },
1784
+ },
1785
+ undefined,
1786
+ );
1787
+ const thread = runtime.threads.getMainThreadRuntimeCore();
1788
+ thread.reset([
1789
+ {
1790
+ id: "user",
1791
+ role: "user",
1792
+ content: [{ type: "text", text: "Use the tool" }],
1793
+ },
1794
+ {
1795
+ id: "assistant",
1796
+ role: "assistant",
1797
+ content: [
1798
+ {
1799
+ type: "tool-call",
1800
+ toolCallId: "tool-call",
1801
+ toolName: "tool",
1802
+ args: {},
1803
+ argsText: "{}",
1804
+ },
1805
+ ],
1806
+ status: { type: "requires-action", reason: "tool-calls" },
1807
+ },
1808
+ ]);
1809
+
1810
+ expect(() => thread.connectVoice()).toThrow(
1811
+ "Cannot start a voice session while a run is in progress or paused on a pending tool action",
1812
+ );
1813
+ expect(thread.voice).toBeUndefined();
1814
+ });
1815
+
1816
+ it("reloads committed transcripts from history into a fresh runtime", async () => {
1817
+ const items: Array<{ parentId: string | null; message: ThreadMessage }> =
1818
+ [];
1819
+ const history = {
1820
+ load: async () => ({
1821
+ messages: items,
1822
+ headId: items.at(-1)?.message.id ?? null,
1823
+ }),
1824
+ append: async (item: {
1825
+ parentId: string | null;
1826
+ message: ThreadMessage;
1827
+ }) => {
1828
+ items.push(item);
1829
+ },
1830
+ };
1831
+ const chatModel = {
1832
+ async run() {
1833
+ return {};
1834
+ },
1835
+ };
1836
+ const voiceAdapter = createVoiceAdapter();
1837
+ const first = new LocalRuntimeCore(
1838
+ { adapters: { chatModel, history, voice: voiceAdapter.adapter } },
1839
+ undefined,
1840
+ );
1841
+ const thread = first.threads.getMainThreadRuntimeCore();
1842
+ await new Promise((resolve) => setTimeout(resolve, 0));
1843
+ thread.connectVoice();
1844
+ voiceAdapter.emitTranscript({ role: "user", text: "Hi", isFinal: true });
1845
+ voiceAdapter.emitTranscript({
1846
+ role: "assistant",
1847
+ text: "Hello",
1848
+ isFinal: true,
1849
+ });
1850
+ thread.disconnectVoice();
1851
+ await new Promise((resolve) => setTimeout(resolve, 0));
1852
+ expect(items).toHaveLength(2);
1853
+
1854
+ const second = new LocalRuntimeCore(
1855
+ { adapters: { chatModel, history } },
1856
+ undefined,
1857
+ );
1858
+ const reloaded = second.threads.getMainThreadRuntimeCore();
1859
+ await reloaded.__internal_load();
1860
+
1861
+ expect(reloaded.messages.map((message) => message.role)).toEqual([
1862
+ "user",
1863
+ "assistant",
1864
+ ]);
1865
+ expect(reloaded.messages[1]?.metadata.modality).toBe("voice");
1866
+ });
1867
+
1868
+ it("rejects starting a voice session while a send awaits initialization", async () => {
1869
+ const voiceAdapter = createVoiceAdapter();
1870
+ const run = vi.fn(async () => ({}));
1871
+ const runtime = new LocalRuntimeCore(
1872
+ { adapters: { chatModel: { run }, voice: voiceAdapter.adapter } },
1873
+ undefined,
1874
+ );
1875
+ const thread = runtime.threads.getMainThreadRuntimeCore();
1876
+ let release!: () => void;
1877
+ const barrier = new Promise<void>((resolve) => {
1878
+ release = resolve;
1879
+ });
1880
+ thread.__internal_setGetInitializePromise(() => barrier);
1881
+ const pending = thread.append({
1882
+ parentId: null,
1883
+ sourceId: null,
1884
+ role: "user",
1885
+ content: [{ type: "text", text: "hello" }],
1886
+ attachments: [],
1887
+ metadata: { custom: {} },
1888
+ createdAt: new Date(),
1889
+ runConfig: {},
1890
+ });
1891
+ await new Promise((resolve) => setTimeout(resolve, 0));
1892
+ expect(run).not.toHaveBeenCalled();
1893
+
1894
+ expect(() => thread.connectVoice()).toThrow(
1895
+ "Cannot start a voice session while a run is in progress or paused on a pending tool action",
1896
+ );
1897
+
1898
+ release();
1899
+ await pending;
1900
+ expect(run).toHaveBeenCalledOnce();
1901
+
1902
+ thread.connectVoice();
1903
+ try {
1904
+ expect(thread.voice).toBeDefined();
1905
+ } finally {
1906
+ thread.disconnectVoice();
1907
+ }
1908
+ });
1909
+
1910
+ it("waits for history already loading before committing a final transcript", async () => {
1911
+ const voiceAdapter = createVoiceAdapter();
1912
+ let release!: () => void;
1913
+ const barrier = new Promise<void>((resolve) => {
1914
+ release = resolve;
1915
+ });
1916
+ const history = {
1917
+ load: vi.fn(() => barrier.then(() => ({ messages: [] }))),
1918
+ append: vi.fn(async () => {}),
1919
+ };
1920
+ const runtime = new LocalRuntimeCore(
1921
+ {
1922
+ adapters: {
1923
+ chatModel: {
1924
+ async run() {
1925
+ return {};
1926
+ },
1927
+ },
1928
+ history,
1929
+ voice: voiceAdapter.adapter,
1930
+ },
1931
+ },
1932
+ undefined,
1933
+ );
1934
+ const thread = runtime.threads.getMainThreadRuntimeCore();
1935
+ const load = thread.__internal_load();
1936
+
1937
+ expect(thread.isLoading).toBe(true);
1938
+ thread.connectVoice();
1939
+ expect(thread.voice).toBeDefined();
1940
+
1941
+ voiceAdapter.emitTranscript({
1942
+ role: "user",
1943
+ text: "Hello",
1944
+ isFinal: true,
1945
+ });
1946
+ await Promise.resolve();
1947
+ expect(history.append).not.toHaveBeenCalled();
1948
+
1949
+ release();
1950
+ await load;
1951
+ await vi.waitFor(() => {
1952
+ expect(history.append).toHaveBeenCalledOnce();
1953
+ });
1954
+ expect(history.append).toHaveBeenCalledWith(
1955
+ expect.objectContaining({ parentId: null }),
1956
+ );
1957
+
1958
+ thread.disconnectVoice();
1959
+ });
1960
+
1961
+ it("waits for a history load started after connection before committing a final transcript", async () => {
1962
+ const voiceAdapter = createVoiceAdapter();
1963
+ let release!: () => void;
1964
+ const loadBarrier = new Promise<void>((resolve) => {
1965
+ release = resolve;
1966
+ });
1967
+ const persistedMessage: ThreadMessage = {
1968
+ id: "persisted",
1969
+ role: "user",
1970
+ content: [{ type: "text", text: "Persisted" }],
1971
+ metadata: { custom: {} },
1972
+ createdAt: new Date(),
1973
+ attachments: [],
1974
+ status: { type: "complete", reason: "unknown" },
1975
+ };
1976
+ const history = {
1977
+ load: vi.fn(() =>
1978
+ loadBarrier.then(() => ({
1979
+ messages: [{ parentId: null, message: persistedMessage }],
1980
+ })),
1981
+ ),
1982
+ append: vi.fn(async () => {}),
1983
+ };
1984
+ const chatModel = {
1985
+ async run() {
1986
+ return {};
1987
+ },
1988
+ };
1989
+ const runtime = new LocalRuntimeCore(
1990
+ { adapters: { chatModel, voice: voiceAdapter.adapter } },
1991
+ undefined,
1992
+ );
1993
+ const thread = runtime.threads.getMainThreadRuntimeCore();
1994
+ await thread.__internal_load();
1995
+ thread.connectVoice();
1996
+ thread.__internal_setOptions({
1997
+ adapters: { chatModel, history, voice: voiceAdapter.adapter },
1998
+ });
1999
+
2000
+ expect(thread.isLoading).toBe(true);
2001
+ voiceAdapter.emitTranscript({
2002
+ role: "user",
2003
+ text: "Hello",
2004
+ isFinal: true,
2005
+ });
2006
+ await Promise.resolve();
2007
+ expect(history.append).not.toHaveBeenCalled();
2008
+
2009
+ release();
2010
+ await vi.waitFor(() => {
2011
+ expect(history.append).toHaveBeenCalledOnce();
2012
+ });
2013
+ expect(history.append).toHaveBeenCalledWith(
2014
+ expect.objectContaining({ parentId: persistedMessage.id }),
2015
+ );
2016
+ expect(thread.messages.map((message) => message.id)).toEqual([
2017
+ persistedMessage.id,
2018
+ expect.any(String),
2019
+ ]);
2020
+
2021
+ thread.disconnectVoice();
2022
+ });
2023
+
2024
+ it("waits for a history load started after connection before committing a typed turn", async () => {
2025
+ const sendText = vi.fn(async () => {});
2026
+ const voiceAdapter = createVoiceAdapter({ sendText });
2027
+ let release!: () => void;
2028
+ const loadBarrier = new Promise<void>((resolve) => {
2029
+ release = resolve;
2030
+ });
2031
+ const persistedMessage: ThreadMessage = {
2032
+ id: "persisted",
2033
+ role: "user",
2034
+ content: [{ type: "text", text: "Persisted" }],
2035
+ metadata: { custom: {} },
2036
+ createdAt: new Date(),
2037
+ attachments: [],
2038
+ status: { type: "complete", reason: "unknown" },
2039
+ };
2040
+ const history = {
2041
+ load: vi.fn(() =>
2042
+ loadBarrier.then(() => ({
2043
+ messages: [{ parentId: null, message: persistedMessage }],
2044
+ })),
2045
+ ),
2046
+ append: vi.fn(async () => {}),
2047
+ };
2048
+ const chatModel = {
2049
+ async run() {
2050
+ return {};
2051
+ },
2052
+ };
2053
+ const runtime = new LocalRuntimeCore(
2054
+ { adapters: { chatModel, voice: voiceAdapter.adapter } },
2055
+ undefined,
2056
+ );
2057
+ const thread = runtime.threads.getMainThreadRuntimeCore();
2058
+ await thread.__internal_load();
2059
+ thread.connectVoice();
2060
+ thread.__internal_setOptions({
2061
+ adapters: { chatModel, history, voice: voiceAdapter.adapter },
2062
+ });
2063
+
2064
+ let settled = false;
2065
+ const append = thread.append({
2066
+ ...typedMessage(thread, "Typed"),
2067
+ });
2068
+ void append.finally(() => {
2069
+ settled = true;
2070
+ });
2071
+ await vi.waitFor(() => {
2072
+ expect(sendText).toHaveBeenCalledExactlyOnceWith("Typed");
2073
+ });
2074
+ expect(settled).toBe(false);
2075
+ expect(history.append).not.toHaveBeenCalled();
2076
+
2077
+ release();
2078
+ await append;
2079
+
2080
+ expect(history.append).toHaveBeenCalledWith(
2081
+ expect.objectContaining({ parentId: persistedMessage.id }),
2082
+ );
2083
+ expect(thread.messages.map((message) => message.id)).toEqual([
2084
+ persistedMessage.id,
2085
+ expect.any(String),
2086
+ ]);
2087
+
2088
+ thread.disconnectVoice();
2089
+ });
2090
+
2091
+ it("notifies subscribers when a commit lands after the session ended", async () => {
2092
+ const voiceAdapter = createVoiceAdapter();
2093
+ let release!: () => void;
2094
+ const loadBarrier = new Promise<void>((resolve) => {
2095
+ release = resolve;
2096
+ });
2097
+ const history = {
2098
+ load: vi.fn(() => loadBarrier.then(() => ({ messages: [] }))),
2099
+ append: vi.fn(async () => {}),
2100
+ };
2101
+ const chatModel = {
2102
+ async run() {
2103
+ return {};
2104
+ },
2105
+ };
2106
+ const runtime = new LocalRuntimeCore(
2107
+ { adapters: { chatModel, voice: voiceAdapter.adapter } },
2108
+ undefined,
2109
+ );
2110
+ const thread = runtime.threads.getMainThreadRuntimeCore();
2111
+ await thread.__internal_load();
2112
+ thread.connectVoice();
2113
+ thread.__internal_setOptions({
2114
+ adapters: { chatModel, history, voice: voiceAdapter.adapter },
2115
+ });
2116
+
2117
+ voiceAdapter.emitTranscript({
2118
+ role: "user",
2119
+ text: "Hello",
2120
+ isFinal: true,
2121
+ });
2122
+ thread.disconnectVoice();
2123
+
2124
+ const seen: number[] = [];
2125
+ const unsubscribe = thread.subscribe(() => {
2126
+ seen.push(thread.messages.length);
2127
+ });
2128
+ release();
2129
+ await vi.waitFor(() => {
2130
+ expect(history.append).toHaveBeenCalledOnce();
2131
+ });
2132
+ await new Promise((resolve) => setImmediate(resolve));
2133
+
2134
+ expect(thread.messages).toHaveLength(1);
2135
+ expect(seen).toContain(1);
2136
+
2137
+ unsubscribe();
2138
+ });
2139
+
2140
+ it("drops a deferred voice commit at detach without waiting for the load", async () => {
2141
+ const voiceAdapter = createVoiceAdapter();
2142
+ let release!: () => void;
2143
+ const loadBarrier = new Promise<void>((resolve) => {
2144
+ release = resolve;
2145
+ });
2146
+ const history = {
2147
+ load: vi.fn(() => loadBarrier.then(() => ({ messages: [] }))),
2148
+ append: vi.fn(async () => {}),
2149
+ };
2150
+ const runtime = new LocalRuntimeCore(
2151
+ {
2152
+ adapters: {
2153
+ chatModel: {
2154
+ async run() {
2155
+ return {};
2156
+ },
2157
+ },
2158
+ history,
2159
+ voice: voiceAdapter.adapter,
2160
+ },
2161
+ },
2162
+ undefined,
2163
+ );
2164
+ const thread = runtime.threads.getMainThreadRuntimeCore();
2165
+ const load = thread.__internal_load();
2166
+ thread.connectVoice();
2167
+
2168
+ voiceAdapter.emitTranscript({
2169
+ role: "user",
2170
+ text: "Hello",
2171
+ isFinal: true,
2172
+ });
2173
+ expect(thread.messages).toHaveLength(1);
2174
+ thread.detach();
2175
+
2176
+ await vi.waitFor(() => {
2177
+ expect(thread.messages).toEqual([]);
2178
+ });
2179
+ expect(thread.isLoading).toBe(true);
2180
+
2181
+ release();
2182
+ await load;
2183
+ await Promise.resolve();
2184
+
2185
+ expect(history.append).not.toHaveBeenCalled();
2186
+ expect(thread.messages).toEqual([]);
2187
+
2188
+ thread.disconnectVoice();
2189
+ });
2190
+
2191
+ it("drops a typed turn still sending when the thread detaches", async () => {
2192
+ let finishSend!: () => void;
2193
+ const sendText = vi.fn(
2194
+ (_text: string) =>
2195
+ new Promise<void>((resolve) => {
2196
+ finishSend = resolve;
2197
+ }),
2198
+ );
2199
+ const voiceAdapter = createVoiceAdapter({ sendText });
2200
+ const history = {
2201
+ load: vi.fn(async () => ({ messages: [] })),
2202
+ append: vi.fn(async () => {}),
2203
+ };
2204
+ const runtime = new LocalRuntimeCore(
2205
+ {
2206
+ adapters: {
2207
+ chatModel: {
2208
+ async run() {
2209
+ return {};
2210
+ },
2211
+ },
2212
+ history,
2213
+ voice: voiceAdapter.adapter,
2214
+ },
2215
+ },
2216
+ undefined,
2217
+ );
2218
+ const thread = runtime.threads.getMainThreadRuntimeCore();
2219
+ await thread.__internal_load();
2220
+ thread.connectVoice();
2221
+
2222
+ const append = thread.append(typedMessage(thread, "Typed"));
2223
+ expect(sendText).toHaveBeenCalledExactlyOnceWith("Typed");
2224
+ thread.detach();
2225
+ finishSend();
2226
+
2227
+ await expect(append).resolves.toBeUndefined();
2228
+ expect(history.append).not.toHaveBeenCalled();
2229
+ expect(thread.messages).toEqual([]);
2230
+
2231
+ thread.disconnectVoice();
2232
+ });
2233
+
2234
+ it("disconnects without committing the unfinished reply when the thread is disposed", async () => {
2235
+ const voiceAdapter = createVoiceAdapter();
2236
+ const onTranscript = voiceAdapter.session.onTranscript;
2237
+ let staleTranscript!: (
2238
+ transcript: RealtimeVoiceAdapter.TranscriptItem,
2239
+ ) => void;
2240
+ voiceAdapter.session.onTranscript = (callback) => {
2241
+ staleTranscript = callback;
2242
+ return onTranscript(callback);
2243
+ };
2244
+ const history = {
2245
+ load: vi.fn(async () => ({ messages: [] })),
2246
+ append: vi.fn(async () => {}),
2247
+ };
2248
+ const runtime = new LocalRuntimeCore(
2249
+ {
2250
+ adapters: {
2251
+ chatModel: {
2252
+ async run() {
2253
+ return {};
2254
+ },
2255
+ },
2256
+ history,
2257
+ voice: voiceAdapter.adapter,
2258
+ },
2259
+ },
2260
+ undefined,
2261
+ );
2262
+ const thread = runtime.threads.getMainThreadRuntimeCore();
2263
+ await thread.__internal_load();
2264
+ thread.connectVoice();
2265
+ voiceAdapter.emitTranscript({ role: "assistant", text: "Unfinished" });
2266
+ expect(thread.messages).toHaveLength(1);
2267
+
2268
+ disposeThreadRuntime(thread);
2269
+
2270
+ expect(voiceAdapter.session.disconnect).toHaveBeenCalledOnce();
2271
+ expect(thread.voice).toBeUndefined();
2272
+ expect(thread.messages).toEqual([]);
2273
+
2274
+ staleTranscript({ role: "assistant", text: "Late" });
2275
+ await Promise.resolve();
2276
+
2277
+ expect(thread.messages).toEqual([]);
2278
+ expect(history.append).not.toHaveBeenCalled();
2279
+ });
2280
+
2281
+ it("propagates a typed turn history rejection after the barrier", async () => {
2282
+ const sendText = vi.fn(async () => {});
2283
+ const voiceAdapter = createVoiceAdapter({ sendText });
2284
+ let release!: () => void;
2285
+ const loadBarrier = new Promise<void>((resolve) => {
2286
+ release = resolve;
2287
+ });
2288
+ const historyError = new Error("history append failed");
2289
+ const history = {
2290
+ load: vi.fn(() => loadBarrier.then(() => ({ messages: [] }))),
2291
+ append: vi.fn(async () => {
2292
+ throw historyError;
2293
+ }),
2294
+ };
2295
+ const runtime = new LocalRuntimeCore(
2296
+ {
2297
+ adapters: {
2298
+ chatModel: {
2299
+ async run() {
2300
+ return {};
2301
+ },
2302
+ },
2303
+ history,
2304
+ voice: voiceAdapter.adapter,
2305
+ },
2306
+ },
2307
+ undefined,
2308
+ );
2309
+ const thread = runtime.threads.getMainThreadRuntimeCore();
2310
+ const load = thread.__internal_load();
2311
+ thread.connectVoice();
2312
+
2313
+ const append = thread.append(typedMessage(thread, "Typed"));
2314
+ await vi.waitFor(() => {
2315
+ expect(sendText).toHaveBeenCalledExactlyOnceWith("Typed");
2316
+ });
2317
+ release();
2318
+ await load;
2319
+ await expect(append).rejects.toBe(historyError);
2320
+
2321
+ thread.disconnectVoice();
2322
+ });
2323
+
2324
+ it("reports a background transcript history rejection", async () => {
2325
+ const voiceAdapter = createVoiceAdapter();
2326
+ const historyError = new Error("history append failed");
2327
+ const history = {
2328
+ load: vi.fn(async () => ({ messages: [] })),
2329
+ append: vi.fn(async () => {
2330
+ throw historyError;
2331
+ }),
2332
+ };
2333
+ const consoleError = vi
2334
+ .spyOn(console, "error")
2335
+ .mockImplementation(() => {});
2336
+ const runtime = new LocalRuntimeCore(
2337
+ {
2338
+ adapters: {
2339
+ chatModel: {
2340
+ async run() {
2341
+ return {};
2342
+ },
2343
+ },
2344
+ history,
2345
+ voice: voiceAdapter.adapter,
2346
+ },
2347
+ },
2348
+ undefined,
2349
+ );
2350
+ const thread = runtime.threads.getMainThreadRuntimeCore();
2351
+ await thread.__internal_load();
2352
+ thread.connectVoice();
2353
+
2354
+ voiceAdapter.emitTranscript({
2355
+ role: "user",
2356
+ text: "Hello",
2357
+ isFinal: true,
2358
+ });
2359
+ await vi.waitFor(() => {
2360
+ expect(consoleError).toHaveBeenCalledWith(
2361
+ "[assistant-ui] Voice message commit failed",
2362
+ historyError,
2363
+ );
2364
+ });
2365
+
2366
+ thread.disconnectVoice();
2367
+ });
2368
+
2369
+ it("rejects opening an edit while connected", async () => {
2370
+ const { thread, voiceAdapter } = await createLocalVoiceThread();
2371
+
2372
+ try {
2373
+ voiceAdapter.emitTranscript({
2374
+ role: "user",
2375
+ text: "Hello",
2376
+ isFinal: true,
2377
+ });
2378
+ const transcript = thread.messages[0]!;
2379
+
2380
+ expect(() => thread.beginEdit(transcript.id)).toThrow(
2381
+ "Cannot edit a message while a voice session is connected",
2382
+ );
2383
+
2384
+ thread.disconnectVoice();
2385
+
2386
+ expect(() => thread.beginEdit(transcript.id)).not.toThrow();
2387
+ } finally {
2388
+ thread.disconnectVoice();
2389
+ }
2390
+ });
2391
+
2392
+ it("speaks an assistant transcript", () => {
2393
+ const voiceAdapter = createVoiceAdapter();
2394
+ const speech = {
2395
+ speak: vi.fn(() => ({
2396
+ status: { type: "running" as const },
2397
+ cancel: vi.fn(),
2398
+ subscribe: () => () => {},
2399
+ })),
2400
+ } satisfies SpeechSynthesisAdapter;
2401
+ const runtime = new TestRuntime(voiceAdapter, undefined, { speech });
2402
+ runtime.connectVoice();
2403
+
2404
+ try {
2405
+ voiceAdapter.emitTranscript({
2406
+ role: "assistant",
2407
+ text: "Hello",
2408
+ isFinal: true,
2409
+ });
2410
+ const message = runtime.messages[0]!;
2411
+
2412
+ expect(() => runtime.speak(message.id)).not.toThrow();
2413
+ expect(speech.speak).toHaveBeenCalledExactlyOnceWith("Hello");
2414
+ } finally {
2415
+ runtime.disconnectVoice();
2416
+ }
2417
+ });
2418
+
2419
+ it("stops speaking a transcript when the session disconnects", () => {
2420
+ const voiceAdapter = createVoiceAdapter();
2421
+ const utterance = {
2422
+ status: { type: "running" as const },
2423
+ cancel: vi.fn(),
2424
+ subscribe: () => () => {},
2425
+ };
2426
+ const speech = {
2427
+ speak: vi.fn(() => utterance),
2428
+ } satisfies SpeechSynthesisAdapter;
2429
+ const runtime = new TestRuntime(voiceAdapter, undefined, { speech });
2430
+ runtime.connectVoice();
2431
+
2432
+ voiceAdapter.emitTranscript({
2433
+ role: "assistant",
2434
+ text: "Hello",
2435
+ isFinal: true,
2436
+ });
2437
+ runtime.speak(runtime.messages[0]!.id);
2438
+ expect(runtime.speech?.messageId).toBe(runtime.messages[0]!.id);
2439
+
2440
+ runtime.disconnectVoice();
2441
+
2442
+ expect(utterance.cancel).toHaveBeenCalledOnce();
2443
+ expect(runtime.speech).toBeUndefined();
2444
+ });
2445
+
2446
+ it("still disconnects the session when stopping a spoken transcript throws", () => {
2447
+ const voiceAdapter = createVoiceAdapter();
2448
+ const utterance = {
2449
+ status: { type: "running" as const },
2450
+ cancel: vi.fn(() => {
2451
+ throw new Error("cancel failed");
2452
+ }),
2453
+ subscribe: () => () => {},
2454
+ };
2455
+ const speech = {
2456
+ speak: vi.fn(() => utterance),
2457
+ } satisfies SpeechSynthesisAdapter;
2458
+ const runtime = new TestRuntime(voiceAdapter, undefined, { speech });
2459
+ runtime.connectVoice();
2460
+
2461
+ voiceAdapter.emitTranscript({
2462
+ role: "assistant",
2463
+ text: "Hello",
2464
+ isFinal: true,
2465
+ });
2466
+ runtime.speak(runtime.messages[0]!.id);
2467
+
2468
+ expect(() => runtime.disconnectVoice()).toThrow("cancel failed");
2469
+ expect(utterance.cancel).toHaveBeenCalledOnce();
2470
+ expect(voiceAdapter.session.disconnect).toHaveBeenCalledOnce();
2471
+ expect(runtime.messages).toHaveLength(0);
2472
+ expect(runtime.speech).toBeUndefined();
2473
+ expect(runtime.voice).toBeUndefined();
2474
+ });
2475
+
2476
+ it("submits feedback for a transcript", () => {
2477
+ const voiceAdapter = createVoiceAdapter();
2478
+ const feedback = { submit: vi.fn() } satisfies FeedbackAdapter;
2479
+ const runtime = new TestRuntime(voiceAdapter, undefined, { feedback });
2480
+ runtime.connectVoice();
2481
+
2482
+ try {
2483
+ voiceAdapter.emitTranscript({
2484
+ role: "assistant",
2485
+ text: "Hello",
2486
+ isFinal: true,
2487
+ });
2488
+ const message = runtime.messages[0]!;
2489
+
2490
+ expect(() =>
2491
+ runtime.submitFeedback({
2492
+ messageId: message.id,
2493
+ type: "positive",
2494
+ comment: "Helpful summary",
2495
+ }),
2496
+ ).not.toThrow();
2497
+ expect(feedback.submit).toHaveBeenCalledExactlyOnceWith({
2498
+ message,
2499
+ type: "positive",
2500
+ comment: "Helpful summary",
2501
+ });
2502
+ expect(runtime.messages[0]?.metadata.submittedFeedback).toEqual({
2503
+ type: "positive",
2504
+ comment: "Helpful summary",
2505
+ });
2506
+
2507
+ const rated = runtime.messages[0]!;
2508
+ runtime.submitFeedback({
2509
+ messageId: message.id,
2510
+ type: "negative",
2511
+ comment: " ",
2512
+ });
2513
+ expect(feedback.submit).toHaveBeenLastCalledWith({
2514
+ message: rated,
2515
+ type: "negative",
2516
+ });
2517
+ expect(runtime.messages[0]?.metadata.submittedFeedback).toEqual({
2518
+ type: "negative",
2519
+ });
2520
+
2521
+ const rerated = runtime.messages[0]!;
2522
+ runtime.submitFeedback({ messageId: message.id, type: "positive" });
2523
+ expect(feedback.submit).toHaveBeenLastCalledWith({
2524
+ message: rerated,
2525
+ type: "positive",
2526
+ });
2527
+ expect(runtime.messages[0]?.metadata.submittedFeedback).toEqual({
2528
+ type: "positive",
2529
+ });
2530
+ } finally {
2531
+ runtime.disconnectVoice();
2532
+ }
2533
+ });
2534
+
2535
+ it("marks transcript feedback locally without an adapter", () => {
2536
+ const voiceAdapter = createVoiceAdapter();
2537
+ const runtime = new TestRuntime(voiceAdapter);
2538
+ runtime.connectVoice();
2539
+
2540
+ try {
2541
+ voiceAdapter.emitTranscript({
2542
+ role: "assistant",
2543
+ text: "Hello",
2544
+ isFinal: true,
2545
+ });
2546
+ const message = runtime.messages[0]!;
2547
+
2548
+ expect(() =>
2549
+ runtime.submitFeedback({ messageId: message.id, type: "negative" }),
2550
+ ).not.toThrow();
2551
+ expect(runtime.messages[0]?.metadata.submittedFeedback).toEqual({
2552
+ type: "negative",
2553
+ });
2554
+ } finally {
2555
+ runtime.disconnectVoice();
2556
+ }
2557
+ });
2558
+
2559
+ it("blocks an edit while connected and accepts it once the session ends", async () => {
2560
+ const voiceAdapter = createVoiceAdapter();
2561
+ const run = vi.fn(async () => ({}));
2562
+ const runtime = new LocalRuntimeCore(
2563
+ { adapters: { chatModel: { run }, voice: voiceAdapter.adapter } },
2564
+ undefined,
2565
+ );
2566
+ const thread = runtime.threads.getMainThreadRuntimeCore();
2567
+ thread.connectVoice();
2568
+
2569
+ try {
2570
+ voiceAdapter.emitTranscript({
2571
+ role: "assistant",
2572
+ text: "Hello",
2573
+ isFinal: true,
2574
+ });
2575
+ const edit = {
2576
+ parentId: null,
2577
+ sourceId: thread.messages[0]!.id,
2578
+ role: "user" as const,
2579
+ content: [{ type: "text" as const, text: "again" }],
2580
+ attachments: [],
2581
+ metadata: { custom: {} },
2582
+ createdAt: new Date(),
2583
+ runConfig: {},
2584
+ };
2585
+
2586
+ await expect(thread.append(edit)).rejects.toThrow(
2587
+ "Cannot send a text message while a voice session is connected",
2588
+ );
2589
+ await expect(thread.append({ ...edit, startRun: false })).rejects.toThrow(
2590
+ "Cannot send a text message while a voice session is connected",
2591
+ );
2592
+ expect(run).not.toHaveBeenCalled();
2593
+ expect(thread.messages).toHaveLength(1);
2594
+
2595
+ thread.disconnectVoice();
2596
+
2597
+ await thread.append(edit);
2598
+ expect(run).toHaveBeenCalledOnce();
2599
+ } finally {
2600
+ thread.disconnectVoice();
2601
+ }
2602
+ });
2603
+
2604
+ it("blocks a reload while connected and allows it once the session ends", async () => {
2605
+ const voiceAdapter = createVoiceAdapter();
2606
+ const run = vi.fn(async () => ({}));
2607
+ const runtime = new LocalRuntimeCore(
2608
+ { adapters: { chatModel: { run }, voice: voiceAdapter.adapter } },
2609
+ undefined,
2610
+ );
2611
+ const thread = runtime.threads.getMainThreadRuntimeCore();
2612
+ thread.connectVoice();
2613
+
2614
+ try {
2615
+ voiceAdapter.emitTranscript({
2616
+ role: "assistant",
2617
+ text: "Hello",
2618
+ isFinal: true,
2619
+ });
2620
+ const message = thread.messages[0]!;
2621
+
2622
+ await expect(
2623
+ thread.startRun({
2624
+ parentId: null,
2625
+ sourceId: message.id,
2626
+ runConfig: {},
2627
+ }),
2628
+ ).rejects.toThrow(
2629
+ "Cannot start a run while a voice session is connected",
2630
+ );
2631
+ expect(run).not.toHaveBeenCalled();
2632
+
2633
+ thread.disconnectVoice();
2634
+
2635
+ await thread.startRun({
2636
+ parentId: null,
2637
+ sourceId: message.id,
2638
+ runConfig: {},
2639
+ });
2640
+ expect(run).toHaveBeenCalledOnce();
2641
+ } finally {
2642
+ thread.disconnectVoice();
2643
+ }
2644
+ });
2645
+
2646
+ it("blocks an approval response while connected before deciding it", () => {
2647
+ const voiceAdapter = createVoiceAdapter();
2648
+ const run = vi.fn(async () => ({}));
2649
+ const runtime = new LocalRuntimeCore(
2650
+ { adapters: { chatModel: { run }, voice: voiceAdapter.adapter } },
2651
+ undefined,
2652
+ );
2653
+ const thread = runtime.threads.getMainThreadRuntimeCore();
2654
+ thread.connectVoice();
2655
+ thread.reset([
2656
+ {
2657
+ id: "user",
2658
+ role: "user",
2659
+ content: [{ type: "text", text: "Use the tool" }],
2660
+ },
2661
+ {
2662
+ id: "assistant",
2663
+ role: "assistant",
2664
+ content: [
2665
+ {
2666
+ type: "tool-call",
2667
+ toolCallId: "tool-call",
2668
+ toolName: "tool",
2669
+ args: {},
2670
+ argsText: "{}",
2671
+ approval: { id: "approval-1" },
2672
+ },
2673
+ ],
2674
+ status: { type: "requires-action", reason: "interrupt" },
2675
+ },
2676
+ ]);
2677
+
2678
+ try {
2679
+ expect(() =>
2680
+ thread.respondToToolApproval({
2681
+ approvalId: "approval-1",
2682
+ approved: true,
2683
+ }),
2684
+ ).toThrow(
2685
+ "Cannot respond to a tool approval while a voice session is connected",
2686
+ );
2687
+ const part = thread.messages[1]!.content[0]!;
2688
+ expect(
2689
+ part.type === "tool-call" && part.approval?.approved,
2690
+ ).toBeUndefined();
2691
+ expect(run).not.toHaveBeenCalled();
2692
+ } finally {
2693
+ thread.disconnectVoice();
2694
+ }
2695
+ });
2696
+
2697
+ it("blocks a tool result continuation while connected", () => {
2698
+ const voiceAdapter = createVoiceAdapter();
2699
+ const run = vi.fn(async () => ({}));
2700
+ const runtime = new LocalRuntimeCore(
2701
+ { adapters: { chatModel: { run }, voice: voiceAdapter.adapter } },
2702
+ undefined,
2703
+ );
2704
+ const thread = runtime.threads.getMainThreadRuntimeCore();
2705
+ const messageId = "assistant";
2706
+ const toolCallId = "tool-call";
2707
+ thread.connectVoice();
2708
+ thread.reset([
2709
+ {
2710
+ id: "user",
2711
+ role: "user",
2712
+ content: [{ type: "text", text: "Use the tool" }],
2713
+ },
2714
+ {
2715
+ id: messageId,
2716
+ role: "assistant",
2717
+ content: [
2718
+ {
2719
+ type: "tool-call",
2720
+ toolCallId,
2721
+ toolName: "tool",
2722
+ args: {},
2723
+ argsText: "{}",
2724
+ },
2725
+ ],
2726
+ status: { type: "requires-action", reason: "tool-calls" },
2727
+ },
2728
+ ]);
2729
+
2730
+ try {
2731
+ expect(() =>
2732
+ thread.addToolResult({
2733
+ messageId,
2734
+ toolCallId,
2735
+ toolName: "tool",
2736
+ result: "done",
2737
+ isError: false,
2738
+ }),
2739
+ ).toThrow("Cannot add a tool result while a voice session is connected");
2740
+ expect(run).not.toHaveBeenCalled();
2741
+ const part = thread.messages[1]!.content[0]!;
2742
+ expect(part).not.toHaveProperty("result");
1266
2743
  } finally {
1267
2744
  thread.disconnectVoice();
1268
2745
  }
@@ -1279,6 +2756,7 @@ describe("BaseThreadRuntimeCore voice transcripts", () => {
1279
2756
  text: "Hello",
1280
2757
  isFinal: true,
1281
2758
  });
2759
+ voiceAdapter.emitStatus({ type: "ended", reason: "finished" });
1282
2760
 
1283
2761
  expect(() => runtime.beginEdit(runtime.messages[0]!.id)).toThrow(
1284
2762
  "Voice transcript messages cannot be edited",