@assistant-ui/core 0.2.20 → 0.2.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 (509) hide show
  1. package/dist/adapters/attachment.d.ts +2 -2
  2. package/dist/adapters/attachment.d.ts.map +1 -1
  3. package/dist/adapters/attachment.js +14 -7
  4. package/dist/adapters/attachment.js.map +1 -1
  5. package/dist/adapters/directive-formatter.d.ts +0 -1
  6. package/dist/adapters/directive-formatter.d.ts.map +1 -1
  7. package/dist/adapters/feedback.d.ts +0 -1
  8. package/dist/adapters/feedback.d.ts.map +1 -1
  9. package/dist/adapters/index.d.ts +2 -2
  10. package/dist/adapters/index.js +2 -1
  11. package/dist/adapters/speech.d.ts +0 -1
  12. package/dist/adapters/speech.d.ts.map +1 -1
  13. package/dist/adapters/suggestion.d.ts +16 -2
  14. package/dist/adapters/suggestion.d.ts.map +1 -1
  15. package/dist/adapters/suggestion.js +36 -0
  16. package/dist/adapters/suggestion.js.map +1 -0
  17. package/dist/adapters/thread-history.d.ts +2 -2
  18. package/dist/adapters/thread-history.d.ts.map +1 -1
  19. package/dist/adapters/trigger.d.ts +5 -3
  20. package/dist/adapters/trigger.d.ts.map +1 -1
  21. package/dist/adapters/voice.d.ts +0 -1
  22. package/dist/adapters/voice.d.ts.map +1 -1
  23. package/dist/index.d.ts +4 -3
  24. package/dist/index.js +3 -1
  25. package/dist/index.js.map +1 -1
  26. package/dist/internal/duplicate-detection.d.ts.map +1 -1
  27. package/dist/internal.d.ts +7 -1
  28. package/dist/internal.js +6 -2
  29. package/dist/model-context/frame/host.d.ts +0 -1
  30. package/dist/model-context/frame/host.d.ts.map +1 -1
  31. package/dist/model-context/frame/provider.d.ts +0 -1
  32. package/dist/model-context/frame/provider.d.ts.map +1 -1
  33. package/dist/model-context/frame/types.d.ts.map +1 -1
  34. package/dist/model-context/interactable-composer-metadata.d.ts +4 -2
  35. package/dist/model-context/interactable-composer-metadata.d.ts.map +1 -1
  36. package/dist/model-context/registry-handles.d.ts +0 -1
  37. package/dist/model-context/registry-handles.d.ts.map +1 -1
  38. package/dist/model-context/registry.d.ts +0 -1
  39. package/dist/model-context/registry.d.ts.map +1 -1
  40. package/dist/model-context/tool.d.ts +23 -9
  41. package/dist/model-context/tool.d.ts.map +1 -1
  42. package/dist/model-context/tool.js +0 -25
  43. package/dist/model-context/tool.js.map +1 -1
  44. package/dist/model-context/types.d.ts +0 -1
  45. package/dist/model-context/types.d.ts.map +1 -1
  46. package/dist/react/AssistantProvider.d.ts +0 -1
  47. package/dist/react/AssistantProvider.d.ts.map +1 -1
  48. package/dist/react/AssistantRuntimeProvider.d.ts +1 -6
  49. package/dist/react/AssistantRuntimeProvider.d.ts.map +1 -1
  50. package/dist/react/RuntimeAdapter.d.ts +1 -0
  51. package/dist/react/RuntimeAdapter.d.ts.map +1 -1
  52. package/dist/react/adapters/LocalStorageThreadListAdapter.d.ts +13 -2
  53. package/dist/react/adapters/LocalStorageThreadListAdapter.d.ts.map +1 -1
  54. package/dist/react/adapters/LocalStorageThreadListAdapter.js +122 -7
  55. package/dist/react/adapters/LocalStorageThreadListAdapter.js.map +1 -1
  56. package/dist/react/adapters/TitleGenerationAdapter.d.ts +0 -1
  57. package/dist/react/adapters/TitleGenerationAdapter.d.ts.map +1 -1
  58. package/dist/react/client/DataRenderers.d.ts +0 -1
  59. package/dist/react/client/DataRenderers.d.ts.map +1 -1
  60. package/dist/react/client/Interactables.d.ts +0 -1
  61. package/dist/react/client/Interactables.d.ts.map +1 -1
  62. package/dist/react/client/Tools.d.ts +3 -2
  63. package/dist/react/client/Tools.d.ts.map +1 -1
  64. package/dist/react/client/interactable-model-context.d.ts +0 -1
  65. package/dist/react/client/interactable-model-context.d.ts.map +1 -1
  66. package/dist/react/index.d.ts +2 -2
  67. package/dist/react/interactables-legacy/Interactables.d.ts +0 -1
  68. package/dist/react/interactables-legacy/Interactables.d.ts.map +1 -1
  69. package/dist/react/interactables-legacy/interactable-model-context.d.ts +0 -1
  70. package/dist/react/interactables-legacy/interactable-model-context.d.ts.map +1 -1
  71. package/dist/react/interactables-legacy/scopes.d.ts +4 -2
  72. package/dist/react/interactables-legacy/scopes.d.ts.map +1 -1
  73. package/dist/react/interactables-legacy/useAssistantInteractable.d.ts +0 -1
  74. package/dist/react/interactables-legacy/useAssistantInteractable.d.ts.map +1 -1
  75. package/dist/react/interactables-legacy/useInteractableState.d.ts.map +1 -1
  76. package/dist/react/model-context/define-mcp-toolkit.d.ts +19 -4
  77. package/dist/react/model-context/define-mcp-toolkit.d.ts.map +1 -1
  78. package/dist/react/model-context/define-mcp-toolkit.js +17 -5
  79. package/dist/react/model-context/define-mcp-toolkit.js.map +1 -1
  80. package/dist/react/model-context/define-toolkit.d.ts +1 -2
  81. package/dist/react/model-context/define-toolkit.d.ts.map +1 -1
  82. package/dist/react/model-context/external-tool.d.ts.map +1 -1
  83. package/dist/react/model-context/human-tool.d.ts.map +1 -1
  84. package/dist/react/model-context/interactableTool.d.ts +2 -2
  85. package/dist/react/model-context/interactableTool.d.ts.map +1 -1
  86. package/dist/react/model-context/makeAssistantDataUI.d.ts +2 -2
  87. package/dist/react/model-context/makeAssistantDataUI.d.ts.map +1 -1
  88. package/dist/react/model-context/makeAssistantTool.d.ts +2 -2
  89. package/dist/react/model-context/makeAssistantTool.d.ts.map +1 -1
  90. package/dist/react/model-context/makeAssistantToolUI.d.ts +2 -2
  91. package/dist/react/model-context/makeAssistantToolUI.d.ts.map +1 -1
  92. package/dist/react/model-context/provider-tool.d.ts +0 -1
  93. package/dist/react/model-context/provider-tool.d.ts.map +1 -1
  94. package/dist/react/model-context/stub-tool.d.ts.map +1 -1
  95. package/dist/react/model-context/toolbox.d.ts +5 -5
  96. package/dist/react/model-context/toolbox.d.ts.map +1 -1
  97. package/dist/react/model-context/useAssistantContext.d.ts +1 -0
  98. package/dist/react/model-context/useAssistantContext.d.ts.map +1 -1
  99. package/dist/react/model-context/useAssistantDataUI.d.ts +3 -2
  100. package/dist/react/model-context/useAssistantDataUI.d.ts.map +1 -1
  101. package/dist/react/model-context/useAssistantInstructions.d.ts +1 -0
  102. package/dist/react/model-context/useAssistantInstructions.d.ts.map +1 -1
  103. package/dist/react/model-context/useAssistantTool.d.ts +4 -2
  104. package/dist/react/model-context/useAssistantTool.d.ts.map +1 -1
  105. package/dist/react/model-context/useAssistantToolUI.d.ts +3 -2
  106. package/dist/react/model-context/useAssistantToolUI.d.ts.map +1 -1
  107. package/dist/react/model-context/useAuiToolOverrides.d.ts +0 -1
  108. package/dist/react/model-context/useAuiToolOverrides.d.ts.map +1 -1
  109. package/dist/react/model-context/useInlineRender.d.ts +0 -1
  110. package/dist/react/model-context/useInlineRender.d.ts.map +1 -1
  111. package/dist/react/model-context/useInteractable.d.ts +7 -4
  112. package/dist/react/model-context/useInteractable.d.ts.map +1 -1
  113. package/dist/react/model-context/useInteractableState.d.ts.map +1 -1
  114. package/dist/react/model-context/useInteractableVersions.d.ts +0 -1
  115. package/dist/react/model-context/useInteractableVersions.d.ts.map +1 -1
  116. package/dist/react/model-context/useToolArgsStatus.d.ts +3 -1
  117. package/dist/react/model-context/useToolArgsStatus.d.ts.map +1 -1
  118. package/dist/react/primitive-hooks/useActionBarCopy.d.ts +1 -4
  119. package/dist/react/primitive-hooks/useActionBarCopy.d.ts.map +1 -1
  120. package/dist/react/primitive-hooks/useActionBarEdit.d.ts.map +1 -1
  121. package/dist/react/primitive-hooks/useActionBarFeedback.d.ts.map +1 -1
  122. package/dist/react/primitive-hooks/useActionBarReload.d.ts.map +1 -1
  123. package/dist/react/primitive-hooks/useActionBarSpeak.d.ts.map +1 -1
  124. package/dist/react/primitive-hooks/useActionBarStopSpeaking.d.ts.map +1 -1
  125. package/dist/react/primitive-hooks/useBranchPickerNext.d.ts.map +1 -1
  126. package/dist/react/primitive-hooks/useBranchPickerPrevious.d.ts.map +1 -1
  127. package/dist/react/primitive-hooks/useComposerAddAttachment.d.ts +0 -1
  128. package/dist/react/primitive-hooks/useComposerAddAttachment.d.ts.map +1 -1
  129. package/dist/react/primitive-hooks/useComposerCancel.d.ts.map +1 -1
  130. package/dist/react/primitive-hooks/useComposerDictate.d.ts.map +1 -1
  131. package/dist/react/primitive-hooks/useComposerSend.d.ts +0 -1
  132. package/dist/react/primitive-hooks/useComposerSend.d.ts.map +1 -1
  133. package/dist/react/primitive-hooks/useEditComposerCancel.d.ts.map +1 -1
  134. package/dist/react/primitive-hooks/useEditComposerSend.d.ts.map +1 -1
  135. package/dist/react/primitive-hooks/useMessageBranching.d.ts.map +1 -1
  136. package/dist/react/primitive-hooks/useMessageError.d.ts.map +1 -1
  137. package/dist/react/primitive-hooks/useMessageError.js +5 -1
  138. package/dist/react/primitive-hooks/useMessageError.js.map +1 -1
  139. package/dist/react/primitive-hooks/useMessageReload.d.ts.map +1 -1
  140. package/dist/react/primitive-hooks/useSuggestionTrigger.d.ts +1 -5
  141. package/dist/react/primitive-hooks/useSuggestionTrigger.d.ts.map +1 -1
  142. package/dist/react/primitive-hooks/useThreadIsEmpty.d.ts.map +1 -1
  143. package/dist/react/primitive-hooks/useThreadIsRunning.d.ts.map +1 -1
  144. package/dist/react/primitive-hooks/useThreadListItemArchive.d.ts.map +1 -1
  145. package/dist/react/primitive-hooks/useThreadListItemDelete.d.ts.map +1 -1
  146. package/dist/react/primitive-hooks/useThreadListItemTrigger.d.ts.map +1 -1
  147. package/dist/react/primitive-hooks/useThreadListItemUnarchive.d.ts.map +1 -1
  148. package/dist/react/primitive-hooks/useThreadListLoadMore.d.ts.map +1 -1
  149. package/dist/react/primitive-hooks/useThreadListNew.d.ts.map +1 -1
  150. package/dist/react/primitive-hooks/useThreadMessageIds.d.ts.map +1 -1
  151. package/dist/react/primitive-hooks/useThreadMessages.d.ts +0 -1
  152. package/dist/react/primitive-hooks/useThreadMessages.d.ts.map +1 -1
  153. package/dist/react/primitive-hooks/useVoice.d.ts +0 -1
  154. package/dist/react/primitive-hooks/useVoice.d.ts.map +1 -1
  155. package/dist/react/primitives/chainOfThought/ChainOfThoughtParts.d.ts +7 -4
  156. package/dist/react/primitives/chainOfThought/ChainOfThoughtParts.d.ts.map +1 -1
  157. package/dist/react/primitives/composer/ComposerAttachments.d.ts +4 -3
  158. package/dist/react/primitives/composer/ComposerAttachments.d.ts.map +1 -1
  159. package/dist/react/primitives/composer/ComposerIf.d.ts +4 -3
  160. package/dist/react/primitives/composer/ComposerIf.d.ts.map +1 -1
  161. package/dist/react/primitives/composer/ComposerQueue.d.ts +2 -2
  162. package/dist/react/primitives/composer/ComposerQueue.d.ts.map +1 -1
  163. package/dist/react/primitives/generativeUI/GenerativeUI.d.ts +2 -2
  164. package/dist/react/primitives/generativeUI/GenerativeUI.d.ts.map +1 -1
  165. package/dist/react/primitives/message/MessageAttachments.d.ts +4 -3
  166. package/dist/react/primitives/message/MessageAttachments.d.ts.map +1 -1
  167. package/dist/react/primitives/message/MessageGroupedParts.d.ts +1 -6
  168. package/dist/react/primitives/message/MessageGroupedParts.d.ts.map +1 -1
  169. package/dist/react/primitives/message/MessageParts.d.ts +42 -24
  170. package/dist/react/primitives/message/MessageParts.d.ts.map +1 -1
  171. package/dist/react/primitives/message/MessageParts.js +2 -1
  172. package/dist/react/primitives/message/MessageParts.js.map +1 -1
  173. package/dist/react/primitives/message/MessageQuote.d.ts +2 -2
  174. package/dist/react/primitives/message/MessageQuote.d.ts.map +1 -1
  175. package/dist/react/primitives/messagePart/MessagePartInProgress.d.ts +0 -1
  176. package/dist/react/primitives/messagePart/MessagePartInProgress.d.ts.map +1 -1
  177. package/dist/react/primitives/part/PartMessages.d.ts +2 -2
  178. package/dist/react/primitives/part/PartMessages.d.ts.map +1 -1
  179. package/dist/react/primitives/thread/ThreadMessages.d.ts +35 -17
  180. package/dist/react/primitives/thread/ThreadMessages.d.ts.map +1 -1
  181. package/dist/react/primitives/thread/ThreadSuggestions.d.ts +6 -4
  182. package/dist/react/primitives/thread/ThreadSuggestions.d.ts.map +1 -1
  183. package/dist/react/primitives/threadList/ThreadListItems.d.ts +4 -3
  184. package/dist/react/primitives/threadList/ThreadListItems.d.ts.map +1 -1
  185. package/dist/react/primitives/threadListItem/ThreadListItemTitle.d.ts +0 -1
  186. package/dist/react/primitives/threadListItem/ThreadListItemTitle.d.ts.map +1 -1
  187. package/dist/react/providers/AttachmentByIndexProvider.d.ts +0 -1
  188. package/dist/react/providers/AttachmentByIndexProvider.d.ts.map +1 -1
  189. package/dist/react/providers/ChainOfThoughtByIndicesProvider.d.ts +0 -1
  190. package/dist/react/providers/ChainOfThoughtByIndicesProvider.d.ts.map +1 -1
  191. package/dist/react/providers/ChainOfThoughtPartByIndexProvider.d.ts +0 -1
  192. package/dist/react/providers/ChainOfThoughtPartByIndexProvider.d.ts.map +1 -1
  193. package/dist/react/providers/MessageByIdProvider.d.ts +0 -1
  194. package/dist/react/providers/MessageByIdProvider.d.ts.map +1 -1
  195. package/dist/react/providers/MessageByIndexProvider.d.ts +0 -1
  196. package/dist/react/providers/MessageByIndexProvider.d.ts.map +1 -1
  197. package/dist/react/providers/PartByIndexProvider.d.ts +0 -1
  198. package/dist/react/providers/PartByIndexProvider.d.ts.map +1 -1
  199. package/dist/react/providers/QueueItemByIndexProvider.d.ts +0 -1
  200. package/dist/react/providers/QueueItemByIndexProvider.d.ts.map +1 -1
  201. package/dist/react/providers/ReadonlyThreadProvider.d.ts +0 -1
  202. package/dist/react/providers/ReadonlyThreadProvider.d.ts.map +1 -1
  203. package/dist/react/providers/SuggestionByIndexProvider.d.ts +0 -1
  204. package/dist/react/providers/SuggestionByIndexProvider.d.ts.map +1 -1
  205. package/dist/react/providers/TextMessagePartProvider.d.ts +0 -1
  206. package/dist/react/providers/TextMessagePartProvider.d.ts.map +1 -1
  207. package/dist/react/providers/ThreadListItemByIndexProvider.d.ts +0 -1
  208. package/dist/react/providers/ThreadListItemByIndexProvider.d.ts.map +1 -1
  209. package/dist/react/providers/ThreadListItemRuntimeProvider.d.ts +1 -1
  210. package/dist/react/providers/ThreadListItemRuntimeProvider.d.ts.map +1 -1
  211. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.d.ts +2 -1
  212. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.d.ts.map +1 -1
  213. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js +1 -1
  214. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js.map +1 -1
  215. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts +3 -1
  216. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts.map +1 -1
  217. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js +21 -19
  218. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js.map +1 -1
  219. package/dist/react/runtimes/RuntimeAdapterProvider.d.ts +0 -1
  220. package/dist/react/runtimes/RuntimeAdapterProvider.d.ts.map +1 -1
  221. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.d.ts +0 -1
  222. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.d.ts.map +1 -1
  223. package/dist/react/runtimes/cloud/CloudFileAttachmentAdapter.d.ts +1 -4
  224. package/dist/react/runtimes/cloud/CloudFileAttachmentAdapter.d.ts.map +1 -1
  225. package/dist/react/runtimes/cloud/CloudFileAttachmentAdapter.js +6 -3
  226. package/dist/react/runtimes/cloud/CloudFileAttachmentAdapter.js.map +1 -1
  227. package/dist/react/runtimes/cloud/auiV0.d.ts +12 -2
  228. package/dist/react/runtimes/cloud/auiV0.d.ts.map +1 -1
  229. package/dist/react/runtimes/cloud/auiV0.js +30 -1
  230. package/dist/react/runtimes/cloud/auiV0.js.map +1 -1
  231. package/dist/react/runtimes/cloud/useCloudThreadListAdapter.d.ts +0 -1
  232. package/dist/react/runtimes/cloud/useCloudThreadListAdapter.d.ts.map +1 -1
  233. package/dist/react/runtimes/createMessageConverter.d.ts +1 -7
  234. package/dist/react/runtimes/createMessageConverter.d.ts.map +1 -1
  235. package/dist/react/runtimes/createRuntimeExtras.d.ts +0 -1
  236. package/dist/react/runtimes/createRuntimeExtras.d.ts.map +1 -1
  237. package/dist/react/runtimes/external-message-converter.d.ts +1 -8
  238. package/dist/react/runtimes/external-message-converter.d.ts.map +1 -1
  239. package/dist/react/runtimes/external-message-converter.js +42 -44
  240. package/dist/react/runtimes/external-message-converter.js.map +1 -1
  241. package/dist/react/runtimes/useExternalStoreRuntime.d.ts +0 -1
  242. package/dist/react/runtimes/useExternalStoreRuntime.d.ts.map +1 -1
  243. package/dist/react/runtimes/useExternalStoreSharedOptions.d.ts +0 -1
  244. package/dist/react/runtimes/useExternalStoreSharedOptions.d.ts.map +1 -1
  245. package/dist/react/runtimes/useLocalRuntime.d.ts +2 -5
  246. package/dist/react/runtimes/useLocalRuntime.d.ts.map +1 -1
  247. package/dist/react/runtimes/useRemoteThreadListRuntime.d.ts +0 -1
  248. package/dist/react/runtimes/useRemoteThreadListRuntime.d.ts.map +1 -1
  249. package/dist/react/runtimes/useStreamingTiming.d.ts +0 -1
  250. package/dist/react/runtimes/useStreamingTiming.d.ts.map +1 -1
  251. package/dist/react/types/MessagePartComponentTypes.d.ts +6 -3
  252. package/dist/react/types/MessagePartComponentTypes.d.ts.map +1 -1
  253. package/dist/react/types/scopes/dataRenderers.d.ts +1 -1
  254. package/dist/react/types/scopes/dataRenderers.d.ts.map +1 -1
  255. package/dist/react/types/scopes/interactables.d.ts +4 -2
  256. package/dist/react/types/scopes/interactables.d.ts.map +1 -1
  257. package/dist/react/types/scopes/tools.d.ts +5 -3
  258. package/dist/react/types/scopes/tools.d.ts.map +1 -1
  259. package/dist/react/types/store-augmentation.d.ts +1 -2
  260. package/dist/react/types/store-augmentation.d.ts.map +1 -1
  261. package/dist/react/utils/getMessageQuote.d.ts +0 -1
  262. package/dist/react/utils/getMessageQuote.d.ts.map +1 -1
  263. package/dist/react/utils/groupParts.d.ts +2 -2
  264. package/dist/react/utils/groupParts.d.ts.map +1 -1
  265. package/dist/react/utils/useJSONEqual.d.ts.map +1 -1
  266. package/dist/runtime/api/assistant-runtime.d.ts +0 -1
  267. package/dist/runtime/api/assistant-runtime.d.ts.map +1 -1
  268. package/dist/runtime/api/attachment-runtime.d.ts +0 -1
  269. package/dist/runtime/api/attachment-runtime.d.ts.map +1 -1
  270. package/dist/runtime/api/bindings.d.ts +0 -1
  271. package/dist/runtime/api/bindings.d.ts.map +1 -1
  272. package/dist/runtime/api/composer-runtime.d.ts +6 -4
  273. package/dist/runtime/api/composer-runtime.d.ts.map +1 -1
  274. package/dist/runtime/api/message-part-runtime.d.ts +0 -1
  275. package/dist/runtime/api/message-part-runtime.d.ts.map +1 -1
  276. package/dist/runtime/api/message-runtime.d.ts +6 -16
  277. package/dist/runtime/api/message-runtime.d.ts.map +1 -1
  278. package/dist/runtime/api/message-runtime.js +1 -1
  279. package/dist/runtime/api/paths.d.ts.map +1 -1
  280. package/dist/runtime/api/thread-list-item-runtime.d.ts +0 -1
  281. package/dist/runtime/api/thread-list-item-runtime.d.ts.map +1 -1
  282. package/dist/runtime/api/thread-list-runtime.d.ts +0 -1
  283. package/dist/runtime/api/thread-list-runtime.d.ts.map +1 -1
  284. package/dist/runtime/api/thread-runtime.d.ts +3 -1
  285. package/dist/runtime/api/thread-runtime.d.ts.map +1 -1
  286. package/dist/runtime/base/base-assistant-runtime-core.d.ts +0 -1
  287. package/dist/runtime/base/base-assistant-runtime-core.d.ts.map +1 -1
  288. package/dist/runtime/base/base-composer-runtime-core.d.ts +0 -1
  289. package/dist/runtime/base/base-composer-runtime-core.d.ts.map +1 -1
  290. package/dist/runtime/base/base-composer-runtime-core.js +17 -3
  291. package/dist/runtime/base/base-composer-runtime-core.js.map +1 -1
  292. package/dist/runtime/base/base-thread-runtime-core.d.ts +2 -4
  293. package/dist/runtime/base/base-thread-runtime-core.d.ts.map +1 -1
  294. package/dist/runtime/base/base-thread-runtime-core.js +1 -1
  295. package/dist/runtime/base/default-edit-composer-runtime-core.d.ts +1 -5
  296. package/dist/runtime/base/default-edit-composer-runtime-core.d.ts.map +1 -1
  297. package/dist/runtime/base/default-thread-composer-runtime-core.d.ts +1 -0
  298. package/dist/runtime/base/default-thread-composer-runtime-core.d.ts.map +1 -1
  299. package/dist/runtime/branch/external-thread-branch-adapter.d.ts.map +1 -1
  300. package/dist/runtime/interfaces/assistant-runtime-core.d.ts +0 -1
  301. package/dist/runtime/interfaces/assistant-runtime-core.d.ts.map +1 -1
  302. package/dist/runtime/interfaces/composer-runtime-core.d.ts +2 -2
  303. package/dist/runtime/interfaces/composer-runtime-core.d.ts.map +1 -1
  304. package/dist/runtime/interfaces/thread-list-runtime-core.d.ts +0 -1
  305. package/dist/runtime/interfaces/thread-list-runtime-core.d.ts.map +1 -1
  306. package/dist/runtime/interfaces/thread-runtime-core.d.ts +2 -2
  307. package/dist/runtime/interfaces/thread-runtime-core.d.ts.map +1 -1
  308. package/dist/runtime/queue/external-thread-queue-adapter.d.ts +0 -1
  309. package/dist/runtime/queue/external-thread-queue-adapter.d.ts.map +1 -1
  310. package/dist/runtime/queue/message-queue.d.ts +6 -4
  311. package/dist/runtime/queue/message-queue.d.ts.map +1 -1
  312. package/dist/runtime/queue/message-queue.js +1 -1
  313. package/dist/runtime/utils/auto-status.d.ts +0 -1
  314. package/dist/runtime/utils/auto-status.d.ts.map +1 -1
  315. package/dist/runtime/utils/chat-model-adapter.d.ts +0 -1
  316. package/dist/runtime/utils/chat-model-adapter.d.ts.map +1 -1
  317. package/dist/runtime/utils/external-store-message.d.ts +0 -1
  318. package/dist/runtime/utils/external-store-message.d.ts.map +1 -1
  319. package/dist/runtime/utils/message-repository.d.ts +1 -5
  320. package/dist/runtime/utils/message-repository.d.ts.map +1 -1
  321. package/dist/runtime/utils/message-repository.js +3 -1
  322. package/dist/runtime/utils/message-repository.js.map +1 -1
  323. package/dist/runtime/utils/resolveToolApprovalResponse.d.ts +0 -1
  324. package/dist/runtime/utils/resolveToolApprovalResponse.d.ts.map +1 -1
  325. package/dist/runtime/utils/streaming-timing.d.ts +5 -3
  326. package/dist/runtime/utils/streaming-timing.d.ts.map +1 -1
  327. package/dist/runtime/utils/thread-message-like.d.ts +2 -2
  328. package/dist/runtime/utils/thread-message-like.d.ts.map +1 -1
  329. package/dist/runtime/utils/thread-message-like.js.map +1 -1
  330. package/dist/runtimes/assistant-transport/utils.d.ts.map +1 -1
  331. package/dist/runtimes/external-store/external-store-adapter.d.ts +2 -2
  332. package/dist/runtimes/external-store/external-store-adapter.d.ts.map +1 -1
  333. package/dist/runtimes/external-store/external-store-runtime-core.d.ts +0 -1
  334. package/dist/runtimes/external-store/external-store-runtime-core.d.ts.map +1 -1
  335. package/dist/runtimes/external-store/external-store-shared-options.d.ts +0 -1
  336. package/dist/runtimes/external-store/external-store-shared-options.d.ts.map +1 -1
  337. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.d.ts +0 -1
  338. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.d.ts.map +1 -1
  339. package/dist/runtimes/external-store/external-store-thread-runtime-core.d.ts +2 -0
  340. package/dist/runtimes/external-store/external-store-thread-runtime-core.d.ts.map +1 -1
  341. package/dist/runtimes/external-store/external-store-thread-runtime-core.js +1 -1
  342. package/dist/runtimes/external-store/thread-message-converter.d.ts +0 -1
  343. package/dist/runtimes/external-store/thread-message-converter.d.ts.map +1 -1
  344. package/dist/runtimes/local/local-runtime-core.d.ts +0 -1
  345. package/dist/runtimes/local/local-runtime-core.d.ts.map +1 -1
  346. package/dist/runtimes/local/local-runtime-options.d.ts +0 -1
  347. package/dist/runtimes/local/local-runtime-options.d.ts.map +1 -1
  348. package/dist/runtimes/local/local-thread-list-runtime-core.d.ts +0 -1
  349. package/dist/runtimes/local/local-thread-list-runtime-core.d.ts.map +1 -1
  350. package/dist/runtimes/local/local-thread-runtime-core.d.ts +5 -21
  351. package/dist/runtimes/local/local-thread-runtime-core.d.ts.map +1 -1
  352. package/dist/runtimes/local/local-thread-runtime-core.js +24 -15
  353. package/dist/runtimes/local/local-thread-runtime-core.js.map +1 -1
  354. package/dist/runtimes/local/should-continue.d.ts +0 -1
  355. package/dist/runtimes/local/should-continue.d.ts.map +1 -1
  356. package/dist/runtimes/local/should-continue.js +2 -2
  357. package/dist/runtimes/local/should-continue.js.map +1 -1
  358. package/dist/runtimes/readonly/ReadonlyThreadRuntimeCore.d.ts +0 -1
  359. package/dist/runtimes/readonly/ReadonlyThreadRuntimeCore.d.ts.map +1 -1
  360. package/dist/runtimes/remote-thread-list/adapter/in-memory.d.ts +1 -2
  361. package/dist/runtimes/remote-thread-list/adapter/in-memory.d.ts.map +1 -1
  362. package/dist/runtimes/remote-thread-list/adapter/in-memory.js +5 -3
  363. package/dist/runtimes/remote-thread-list/adapter/in-memory.js.map +1 -1
  364. package/dist/runtimes/remote-thread-list/empty-thread-core.d.ts +0 -1
  365. package/dist/runtimes/remote-thread-list/empty-thread-core.d.ts.map +1 -1
  366. package/dist/runtimes/remote-thread-list/optimistic-state.d.ts +6 -4
  367. package/dist/runtimes/remote-thread-list/optimistic-state.d.ts.map +1 -1
  368. package/dist/runtimes/remote-thread-list/remote-thread-state.d.ts +0 -1
  369. package/dist/runtimes/remote-thread-list/remote-thread-state.d.ts.map +1 -1
  370. package/dist/runtimes/remote-thread-list/types.d.ts +0 -1
  371. package/dist/runtimes/remote-thread-list/types.d.ts.map +1 -1
  372. package/dist/runtimes/tool-invocations/ToolInvocationTracker.d.ts +2 -2
  373. package/dist/runtimes/tool-invocations/ToolInvocationTracker.d.ts.map +1 -1
  374. package/dist/store/clients/chain-of-thought-client.d.ts +0 -1
  375. package/dist/store/clients/chain-of-thought-client.d.ts.map +1 -1
  376. package/dist/store/clients/model-context-client.d.ts +0 -1
  377. package/dist/store/clients/model-context-client.d.ts.map +1 -1
  378. package/dist/store/clients/no-op-composer-client.d.ts +0 -1
  379. package/dist/store/clients/no-op-composer-client.d.ts.map +1 -1
  380. package/dist/store/clients/runtime-adapter.d.ts +0 -1
  381. package/dist/store/clients/runtime-adapter.d.ts.map +1 -1
  382. package/dist/store/clients/suggestions.d.ts +0 -1
  383. package/dist/store/clients/suggestions.d.ts.map +1 -1
  384. package/dist/store/clients/thread-message-client.d.ts +0 -1
  385. package/dist/store/clients/thread-message-client.d.ts.map +1 -1
  386. package/dist/store/runtime-clients/attachment-runtime-client.d.ts +0 -1
  387. package/dist/store/runtime-clients/attachment-runtime-client.d.ts.map +1 -1
  388. package/dist/store/runtime-clients/composer-runtime-client.d.ts +0 -1
  389. package/dist/store/runtime-clients/composer-runtime-client.d.ts.map +1 -1
  390. package/dist/store/runtime-clients/liveRef.d.ts.map +1 -1
  391. package/dist/store/runtime-clients/message-part-runtime-client.d.ts +0 -1
  392. package/dist/store/runtime-clients/message-part-runtime-client.d.ts.map +1 -1
  393. package/dist/store/runtime-clients/message-runtime-client.d.ts +0 -1
  394. package/dist/store/runtime-clients/message-runtime-client.d.ts.map +1 -1
  395. package/dist/store/runtime-clients/thread-list-item-runtime-client.d.ts +0 -1
  396. package/dist/store/runtime-clients/thread-list-item-runtime-client.d.ts.map +1 -1
  397. package/dist/store/runtime-clients/thread-list-runtime-client.d.ts +0 -1
  398. package/dist/store/runtime-clients/thread-list-runtime-client.d.ts.map +1 -1
  399. package/dist/store/runtime-clients/thread-runtime-client.d.ts +0 -1
  400. package/dist/store/runtime-clients/thread-runtime-client.d.ts.map +1 -1
  401. package/dist/store/runtime-clients/useSubscribable.d.ts +0 -1
  402. package/dist/store/runtime-clients/useSubscribable.d.ts.map +1 -1
  403. package/dist/store/scope-registration.d.ts +1 -2
  404. package/dist/store/scope-registration.d.ts.map +1 -1
  405. package/dist/store/scopes/attachment.d.ts +0 -1
  406. package/dist/store/scopes/attachment.d.ts.map +1 -1
  407. package/dist/store/scopes/chain-of-thought.d.ts +0 -1
  408. package/dist/store/scopes/chain-of-thought.d.ts.map +1 -1
  409. package/dist/store/scopes/composer.d.ts +0 -1
  410. package/dist/store/scopes/composer.d.ts.map +1 -1
  411. package/dist/store/scopes/message.d.ts +6 -4
  412. package/dist/store/scopes/message.d.ts.map +1 -1
  413. package/dist/store/scopes/model-context.d.ts +0 -1
  414. package/dist/store/scopes/model-context.d.ts.map +1 -1
  415. package/dist/store/scopes/part.d.ts +0 -1
  416. package/dist/store/scopes/part.d.ts.map +1 -1
  417. package/dist/store/scopes/queue-item.d.ts.map +1 -1
  418. package/dist/store/scopes/suggestion.d.ts.map +1 -1
  419. package/dist/store/scopes/suggestions.d.ts +0 -1
  420. package/dist/store/scopes/suggestions.d.ts.map +1 -1
  421. package/dist/store/scopes/thread-list-item.d.ts +0 -1
  422. package/dist/store/scopes/thread-list-item.d.ts.map +1 -1
  423. package/dist/store/scopes/thread.d.ts +4 -3
  424. package/dist/store/scopes/thread.d.ts.map +1 -1
  425. package/dist/store/scopes/threads.d.ts +0 -1
  426. package/dist/store/scopes/threads.d.ts.map +1 -1
  427. package/dist/subscribable/subscribable.d.ts +0 -1
  428. package/dist/subscribable/subscribable.d.ts.map +1 -1
  429. package/dist/tests/remote-thread-list-test-helpers.d.ts +0 -1
  430. package/dist/tests/remote-thread-list-test-helpers.d.ts.map +1 -1
  431. package/dist/types/attachment.d.ts +1 -1
  432. package/dist/types/attachment.d.ts.map +1 -1
  433. package/dist/types/directive.d.ts +3 -2
  434. package/dist/types/directive.d.ts.map +1 -1
  435. package/dist/types/error.d.ts +15 -0
  436. package/dist/types/error.d.ts.map +1 -0
  437. package/dist/types/error.js +46 -0
  438. package/dist/types/error.js.map +1 -0
  439. package/dist/types/index.d.ts +2 -2
  440. package/dist/types/message.d.ts +70 -33
  441. package/dist/types/message.d.ts.map +1 -1
  442. package/dist/types/message.js.map +1 -1
  443. package/dist/types/quote.d.ts.map +1 -1
  444. package/dist/types/trigger.d.ts +0 -1
  445. package/dist/types/trigger.d.ts.map +1 -1
  446. package/dist/types/unsubscribe.d.ts.map +1 -1
  447. package/dist/utils/composite-context-provider.d.ts +1 -0
  448. package/dist/utils/composite-context-provider.d.ts.map +1 -1
  449. package/dist/utils/id.d.ts.map +1 -1
  450. package/dist/utils/json/is-json-equal.d.ts.map +1 -1
  451. package/dist/utils/json/is-json.d.ts +0 -1
  452. package/dist/utils/json/is-json.d.ts.map +1 -1
  453. package/dist/utils/json/is-json.js +1 -1
  454. package/dist/utils/json/is-json.js.map +1 -1
  455. package/dist/utils/json/stable-stringify-tool-args.d.ts +7 -0
  456. package/dist/utils/json/stable-stringify-tool-args.d.ts.map +1 -0
  457. package/dist/utils/json/stable-stringify-tool-args.js +32 -0
  458. package/dist/utils/json/stable-stringify-tool-args.js.map +1 -0
  459. package/dist/utils/text.d.ts +0 -1
  460. package/dist/utils/text.d.ts.map +1 -1
  461. package/package.json +12 -9
  462. package/src/adapters/attachment.test.ts +107 -0
  463. package/src/adapters/attachment.ts +20 -10
  464. package/src/adapters/index.ts +6 -1
  465. package/src/adapters/suggestion.test.ts +187 -0
  466. package/src/adapters/suggestion.ts +75 -1
  467. package/src/index.ts +15 -1
  468. package/src/internal.ts +16 -0
  469. package/src/model-context/tool.test.ts +110 -0
  470. package/src/model-context/tool.ts +31 -9
  471. package/src/react/adapters/LocalStorageThreadListAdapter.test.ts +222 -0
  472. package/src/react/adapters/LocalStorageThreadListAdapter.tsx +214 -7
  473. package/src/react/index.ts +2 -0
  474. package/src/react/model-context/define-mcp-toolkit.ts +39 -6
  475. package/src/react/model-context/define-toolkit.test.ts +107 -0
  476. package/src/react/primitive-hooks/useMessageError.test.ts +64 -0
  477. package/src/react/primitive-hooks/useMessageError.ts +19 -6
  478. package/src/react/runtimes/RemoteThreadListHookInstanceManager.tsx +3 -1
  479. package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.tsx +48 -19
  480. package/src/react/runtimes/cloud/CloudFileAttachmentAdapter.test.ts +111 -0
  481. package/src/react/runtimes/cloud/CloudFileAttachmentAdapter.ts +13 -3
  482. package/src/react/runtimes/cloud/auiV0.ts +71 -2
  483. package/src/react/runtimes/external-message-converter.test.tsx +102 -0
  484. package/src/react/runtimes/external-message-converter.ts +35 -10
  485. package/src/runtime/base/base-composer-runtime-core.ts +26 -7
  486. package/src/runtime/utils/message-repository.ts +8 -3
  487. package/src/runtime/utils/thread-message-like.ts +2 -0
  488. package/src/runtimes/local/local-thread-runtime-core.test.ts +127 -3
  489. package/src/runtimes/local/local-thread-runtime-core.ts +25 -23
  490. package/src/runtimes/local/should-continue.ts +5 -2
  491. package/src/runtimes/remote-thread-list/adapter/in-memory.test.ts +14 -0
  492. package/src/runtimes/remote-thread-list/adapter/in-memory.ts +13 -5
  493. package/src/tests/MessageRepository.test.ts +26 -0
  494. package/src/tests/RemoteThreadListThreadListRuntimeCore-errors.test.ts +42 -0
  495. package/src/tests/attachment-adapters.test.ts +14 -2
  496. package/src/tests/auiV0Encode.test.ts +134 -1
  497. package/src/tests/base-composer-runtime-core-addAttachment.test.ts +79 -5
  498. package/src/tests/base-composer-runtime-core-send.test.ts +145 -0
  499. package/src/tests/thread-message-like.test.ts +40 -0
  500. package/src/types/attachment.ts +1 -0
  501. package/src/types/error.test.ts +122 -0
  502. package/src/types/error.ts +74 -0
  503. package/src/types/index.ts +1 -0
  504. package/src/types/message.ts +11 -3
  505. package/src/utils/json/is-json-equal.test.ts +84 -0
  506. package/src/utils/json/is-json.test.ts +123 -0
  507. package/src/utils/json/is-json.ts +1 -1
  508. package/src/utils/json/stable-stringify-tool-args.test.ts +105 -0
  509. package/src/utils/json/stable-stringify-tool-args.ts +70 -0
@@ -0,0 +1,102 @@
1
+ // @vitest-environment jsdom
2
+
3
+ import { renderHook } from "@testing-library/react";
4
+ import { describe, expect, it } from "vitest";
5
+ import { useExternalMessageConverter } from "./external-message-converter";
6
+
7
+ type TestMessage = {
8
+ id: string;
9
+ role: "user" | "assistant";
10
+ text: string;
11
+ };
12
+
13
+ type TestMetadata = useExternalMessageConverter.Metadata & {
14
+ optimisticMessageId?: string;
15
+ };
16
+
17
+ const convert: useExternalMessageConverter.Callback<TestMessage> = (
18
+ message,
19
+ metadata,
20
+ ) => ({
21
+ role: message.role,
22
+ id: message.id,
23
+ content: [{ type: "text", text: message.text }],
24
+ metadata: {
25
+ ...(message.role === "assistant" &&
26
+ message.id === (metadata as TestMetadata).optimisticMessageId && {
27
+ isOptimistic: true,
28
+ }),
29
+ },
30
+ });
31
+
32
+ const MESSAGES: TestMessage[] = [
33
+ { id: "u1", role: "user", text: "hi" },
34
+ { id: "a1", role: "assistant", text: "hello" },
35
+ ];
36
+
37
+ const EMPTY: TestMetadata = {};
38
+
39
+ type Props = {
40
+ callback?: useExternalMessageConverter.Callback<TestMessage>;
41
+ metadata?: TestMetadata;
42
+ };
43
+
44
+ const renderConverter = (initialProps: Props = {}) =>
45
+ renderHook(
46
+ ({ callback = convert, metadata = EMPTY }: Props) =>
47
+ useExternalMessageConverter<TestMessage>({
48
+ callback,
49
+ messages: MESSAGES,
50
+ isRunning: false,
51
+ metadata,
52
+ }),
53
+ { initialProps },
54
+ );
55
+
56
+ describe("useExternalMessageConverter", () => {
57
+ it("reuses converted messages across rerenders when inputs are unchanged", () => {
58
+ const { result, rerender } = renderConverter();
59
+
60
+ const first = result.current;
61
+ rerender({});
62
+
63
+ expect(result.current[0]).toBe(first[0]);
64
+ expect(result.current[1]).toBe(first[1]);
65
+ });
66
+
67
+ it("re-converts cached messages when metadata changes", () => {
68
+ const { result, rerender } = renderConverter({
69
+ metadata: { optimisticMessageId: "a1" },
70
+ });
71
+
72
+ expect(result.current.at(-1)?.metadata.isOptimistic).toBe(true);
73
+
74
+ rerender({ metadata: {} });
75
+
76
+ expect(result.current.at(-1)?.metadata.isOptimistic).toBeUndefined();
77
+ });
78
+
79
+ it("re-converts cached messages when the callback changes", () => {
80
+ const upper: useExternalMessageConverter.Callback<TestMessage> = (
81
+ message,
82
+ ) => ({
83
+ role: message.role,
84
+ id: message.id,
85
+ content: [{ type: "text", text: message.text.toUpperCase() }],
86
+ });
87
+
88
+ const { result, rerender } = renderConverter();
89
+
90
+ expect(result.current[1]?.content[0]).toMatchObject({
91
+ type: "text",
92
+ text: "hello",
93
+ });
94
+
95
+ rerender({ callback: upper });
96
+
97
+ expect(result.current[1]?.content[0]).toMatchObject({
98
+ type: "text",
99
+ text: "HELLO",
100
+ });
101
+ });
102
+ });
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
 
3
- import { useMemo } from "react";
3
+ import { useMemo, useState } from "react";
4
4
  import { ThreadMessageConverter } from "../../runtimes/external-store/thread-message-converter";
5
5
  import {
6
6
  getExternalStoreMessages,
@@ -76,6 +76,11 @@ type CallbackResult<T> = {
76
76
  outputs: useExternalMessageConverter.Message[];
77
77
  };
78
78
 
79
+ type CallbackCacheEntry<T> = CallbackResult<T> & {
80
+ metadata: useExternalMessageConverter.Metadata;
81
+ callback: useExternalMessageConverter.Callback<T>;
82
+ };
83
+
79
84
  type ChunkResult<T> = {
80
85
  inputs: T[];
81
86
  outputs: useExternalMessageConverter.Message[];
@@ -404,28 +409,48 @@ export const useExternalMessageConverter = <T extends WeakKey>({
404
409
  joinStrategy?: JoinStrategy | undefined;
405
410
  metadata?: useExternalMessageConverter.Metadata | undefined;
406
411
  }) => {
412
+ // The caches live for the component lifetime; React Compiler hoists
413
+ // allocations without reactive dependencies out of useMemo, so re-creating
414
+ // them on dependency change would not survive compilation. Staleness is
415
+ // instead tracked per entry via the metadata/callback that produced it,
416
+ // keeping correctness independent of memoization (which React treats as
417
+ // droppable). A "use no memo" opt-out would restore the useMemo semantics
418
+ // but leave cache flushing coupled to memo firing.
419
+ const [caches] = useState(() => ({
420
+ callbackCache: new WeakMap<T, CallbackCacheEntry<T>>(),
421
+ chunkCache: new WeakMap<
422
+ useExternalMessageConverter.Message,
423
+ ChunkResult<T>
424
+ >(),
425
+ converterCache: new ThreadMessageConverter(),
426
+ }));
427
+
407
428
  const state = useMemo(
408
429
  () => ({
409
430
  metadata: metadata ?? {},
410
431
  callback,
411
- callbackCache: new WeakMap<T, CallbackResult<T>>(),
412
- chunkCache: new WeakMap<
413
- useExternalMessageConverter.Message,
414
- ChunkResult<T>
415
- >(),
416
- converterCache: new ThreadMessageConverter(),
432
+ ...caches,
417
433
  }),
418
- [callback, metadata],
434
+ [callback, metadata, caches],
419
435
  );
420
436
 
421
437
  return useMemo(() => {
422
438
  const callbackResults: CallbackResult<T>[] = [];
423
439
  for (const message of messages) {
424
440
  let result = state.callbackCache.get(message);
425
- if (!result) {
441
+ if (
442
+ !result ||
443
+ result.metadata !== state.metadata ||
444
+ result.callback !== state.callback
445
+ ) {
426
446
  const output = state.callback(message, state.metadata);
427
447
  const outputs = Array.isArray(output) ? output : [output];
428
- result = { input: message, outputs };
448
+ result = {
449
+ input: message,
450
+ outputs,
451
+ metadata: state.metadata,
452
+ callback: state.callback,
453
+ };
429
454
  state.callbackCache.set(message, result);
430
455
  }
431
456
  callbackResults.push(result);
@@ -195,16 +195,30 @@ export abstract class BaseComposerRuntimeCore
195
195
  )
196
196
  : [];
197
197
 
198
+ const originalAttachments = this.attachments;
198
199
  const text = this.text;
199
200
  const quote = this._quote;
200
201
  this._quote = undefined;
201
202
  this._emptyTextAndAttachments();
202
203
 
204
+ let resolvedAttachments: Awaited<typeof attachments>;
205
+ try {
206
+ resolvedAttachments = await attachments;
207
+ } catch (e) {
208
+ if (this.isEmpty && this._quote === undefined) {
209
+ this._attachments = originalAttachments;
210
+ this._text = text;
211
+ this._quote = quote;
212
+ this._notifySubscribers();
213
+ }
214
+ throw e;
215
+ }
216
+
203
217
  const message: Omit<AppendMessage, "parentId" | "sourceId"> = {
204
218
  createdAt: new Date(),
205
219
  role: this.role,
206
220
  content: text ? [{ type: "text", text }] : [],
207
- attachments: await attachments,
221
+ attachments: resolvedAttachments,
208
222
  runConfig: this.runConfig,
209
223
  metadata: { custom: { ...(quote ? { quote } : {}) } },
210
224
  };
@@ -321,7 +335,11 @@ export abstract class BaseComposerRuntimeCore
321
335
  if (lastAttachment) {
322
336
  upsertAttachment({
323
337
  ...lastAttachment,
324
- status: { type: "incomplete", reason: "error" },
338
+ status: {
339
+ type: "incomplete",
340
+ reason: "error",
341
+ message: e instanceof Error ? e.message : String(e),
342
+ },
325
343
  });
326
344
  }
327
345
  this._safeEmitAttachmentAddError(
@@ -333,14 +351,15 @@ export abstract class BaseComposerRuntimeCore
333
351
  throw e;
334
352
  }
335
353
 
336
- const hasError =
354
+ if (
337
355
  lastAttachment?.status.type === "incomplete" &&
338
- lastAttachment.status.reason === "error";
339
- if (hasError) {
356
+ lastAttachment.status.reason === "error"
357
+ ) {
340
358
  this._safeEmitAttachmentAddError(
341
359
  "adapter-error",
342
- "Attachment upload did not complete successfully.",
343
- lastAttachment?.id,
360
+ lastAttachment.status.message ??
361
+ "Attachment upload did not complete successfully.",
362
+ lastAttachment.id,
344
363
  );
345
364
  } else {
346
365
  this._notifyEventSubscribers("attachmentAdd", {});
@@ -436,13 +436,18 @@ export class MessageRepository {
436
436
  export(): ExportedMessageRepository {
437
437
  const exportItems: ExportedMessageRepository["messages"] = [];
438
438
 
439
- // Optimistic messages are ephemeral and never persisted. They're always
440
- // leaf nodes, so skipping them can't orphan a persisted child.
439
+ // Optimistic messages are ephemeral and never persisted. A persisted child
440
+ // of an optimistic node is re-parented onto its nearest persisted ancestor
441
+ // so the exported tree never references a skipped id.
441
442
  for (const [, message] of this.messages) {
442
443
  if (message.current.metadata?.isOptimistic) continue;
444
+ let prev = message.prev;
445
+ while (prev && prev.current.metadata?.isOptimistic) {
446
+ prev = prev.prev;
447
+ }
443
448
  exportItems.push({
444
449
  message: message.current,
445
- parentId: message.prev?.current.id ?? null,
450
+ parentId: prev?.current.id ?? null,
446
451
  });
447
452
  }
448
453
 
@@ -20,6 +20,7 @@ import type {
20
20
  import type { CompleteAttachment } from "../../types/attachment";
21
21
  import type {
22
22
  MessageTiming,
23
+ PartProviderMetadata,
23
24
  TextMessagePart,
24
25
  ToolApprovalOption,
25
26
  ToolCallTiming,
@@ -61,6 +62,7 @@ export type ThreadMessageLike = {
61
62
  readonly messages?: readonly ThreadMessage[] | undefined;
62
63
  readonly interrupt?: { type: "human"; payload: unknown };
63
64
  readonly timing?: ToolCallTiming;
65
+ readonly providerMetadata?: PartProviderMetadata;
64
66
  readonly approval?: {
65
67
  readonly id: string;
66
68
  readonly approved?: boolean;
@@ -1,4 +1,4 @@
1
- import { describe, expect, it } from "vitest";
1
+ import { describe, expect, it, vi } from "vitest";
2
2
  import { LocalRuntimeCore } from "./local-runtime-core";
3
3
  import type {
4
4
  ChatModelAdapter,
@@ -6,13 +6,24 @@ import type {
6
6
  ChatModelRunResult,
7
7
  } from "../../runtime/utils/chat-model-adapter";
8
8
  import type { AppendMessage } from "../../types/message";
9
+ import type { LocalRuntimeOptionsBase } from "./local-runtime-options";
9
10
 
10
11
  const flush = () => new Promise((resolve) => setTimeout(resolve, 10));
11
12
 
12
- const createThread = (adapter: ChatModelAdapter) => {
13
+ const createThread = (
14
+ adapter: ChatModelAdapter,
15
+ options?: {
16
+ suggestion?: LocalRuntimeOptionsBase["adapters"]["suggestion"];
17
+ },
18
+ ) => {
13
19
  const core = new LocalRuntimeCore(
14
20
  {
15
- adapters: { chatModel: adapter },
21
+ adapters: {
22
+ chatModel: adapter,
23
+ ...(options?.suggestion !== undefined && {
24
+ suggestion: options.suggestion,
25
+ }),
26
+ },
16
27
  unstable_humanToolNames: ["send_email"],
17
28
  },
18
29
  undefined,
@@ -117,6 +128,34 @@ describe("LocalThreadRuntimeCore human-in-the-loop tools", () => {
117
128
  "text",
118
129
  ]);
119
130
  });
131
+
132
+ it.each([
133
+ ["false", false],
134
+ ["zero", 0],
135
+ ["an empty string", ""],
136
+ ["null", null],
137
+ ])("resumes when the tool result is %s", async (_label, result) => {
138
+ const { thread, runs } = createApprovalThread(toolCallResult("send_email"));
139
+
140
+ await thread.append(userMessage("send an email"));
141
+ await flush();
142
+
143
+ thread.addToolResult({
144
+ messageId: thread.messages.at(-1)!.id,
145
+ toolCallId: "call-send_email",
146
+ toolName: "send_email",
147
+ result,
148
+ isError: false,
149
+ });
150
+ await flush();
151
+
152
+ expect(runs).toHaveLength(2);
153
+ const toolCall = runs[1]!
154
+ .unstable_getMessage()
155
+ .content.find((part) => part.type === "tool-call");
156
+ expect(toolCall?.result).toEqual(result);
157
+ expect(thread.messages.at(-1)?.status?.type).toBe("complete");
158
+ });
120
159
  });
121
160
 
122
161
  describe("LocalThreadRuntimeCore tool approvals", () => {
@@ -368,3 +407,88 @@ describe("LocalThreadRuntimeCore tool approvals", () => {
368
407
  ).toThrowError(/unstable_humanToolNames/);
369
408
  });
370
409
  });
410
+
411
+ describe("LocalThreadRuntimeCore suggestions", () => {
412
+ it("cancelRun aborts pending suggestion generation", async () => {
413
+ const generate = vi.fn().mockImplementation(
414
+ ({ signal }: { signal?: AbortSignal }) =>
415
+ new Promise<readonly { prompt: string }[]>((resolve) => {
416
+ signal?.addEventListener("abort", () => {
417
+ resolve([{ prompt: "stale" }]);
418
+ });
419
+ }),
420
+ );
421
+
422
+ const thread = createThread(
423
+ {
424
+ async run() {
425
+ return { content: [{ type: "text", text: "hello" }] };
426
+ },
427
+ },
428
+ { suggestion: { generate } },
429
+ );
430
+
431
+ const appendPromise = thread.append(userMessage("hi"));
432
+ await appendPromise;
433
+ await new Promise((r) => setTimeout(r, 0));
434
+
435
+ expect(generate).toHaveBeenCalledTimes(1);
436
+ const signal = generate.mock.calls[0]![0].signal as AbortSignal;
437
+ expect(signal.aborted).toBe(false);
438
+
439
+ thread.cancelRun();
440
+ expect(signal.aborted).toBe(true);
441
+ expect(thread.suggestions).toEqual([]);
442
+ });
443
+
444
+ it("completes the run when suggestion generation rejects", async () => {
445
+ const generate = vi.fn().mockRejectedValue(new Error("suggestion failed"));
446
+
447
+ const thread = createThread(
448
+ {
449
+ async run() {
450
+ return { content: [{ type: "text", text: "hello" }] };
451
+ },
452
+ },
453
+ { suggestion: { generate } },
454
+ );
455
+
456
+ await thread.append(userMessage("hi"));
457
+ await new Promise((r) => setTimeout(r, 0));
458
+
459
+ expect(generate).toHaveBeenCalledTimes(1);
460
+ expect(thread.messages.at(-1)?.status?.type).toBe("complete");
461
+ expect(thread.suggestions).toEqual([]);
462
+ });
463
+
464
+ it("resolves append before suggestion generation completes", async () => {
465
+ let resolveSuggestions!: (value: readonly { prompt: string }[]) => void;
466
+ const suggestionsDeferred = new Promise<readonly { prompt: string }[]>(
467
+ (resolve) => {
468
+ resolveSuggestions = resolve;
469
+ },
470
+ );
471
+ const generate = vi.fn().mockReturnValue(suggestionsDeferred);
472
+
473
+ const thread = createThread(
474
+ {
475
+ async run() {
476
+ return { content: [{ type: "text", text: "hello" }] };
477
+ },
478
+ },
479
+ { suggestion: { generate } },
480
+ );
481
+
482
+ await thread.append(userMessage("hi"));
483
+ expect(thread.messages.at(-1)?.status?.type).toBe("complete");
484
+ expect(thread.suggestions).toEqual([]);
485
+
486
+ await new Promise((r) => setTimeout(r, 0));
487
+ expect(generate).toHaveBeenCalledTimes(1);
488
+ expect(thread.suggestions).toEqual([]);
489
+
490
+ resolveSuggestions([{ prompt: "follow up" }]);
491
+ await new Promise((r) => setTimeout(r, 0));
492
+ expect(thread.suggestions).toEqual([{ prompt: "follow up" }]);
493
+ });
494
+ });
@@ -6,6 +6,7 @@ import type {
6
6
  } from "../../runtime/utils/chat-model-adapter";
7
7
  import { shouldContinue } from "./should-continue";
8
8
  import type { LocalRuntimeOptionsBase } from "./local-runtime-options";
9
+ import { consumeSuggestionResult } from "../../adapters/suggestion";
9
10
  import type {
10
11
  AddToolResultOptions,
11
12
  ResumeToolCallOptions,
@@ -21,6 +22,7 @@ import type {
21
22
  ThreadAssistantMessage,
22
23
  } from "../../types/message";
23
24
  import type { RunConfig } from "../../types/message";
25
+ import { toAssistantError } from "../../types/error";
24
26
  import type { ModelContextProvider } from "../../model-context/types";
25
27
  import {
26
28
  createMessageQueue,
@@ -375,28 +377,28 @@ export class LocalThreadRuntimeCore
375
377
  }
376
378
  }
377
379
 
378
- this._suggestionsController = new AbortController();
379
- const signal = this._suggestionsController.signal;
380
380
  if (
381
381
  this.adapters.suggestion &&
382
382
  message.status?.type !== "requires-action"
383
383
  ) {
384
- const promiseOrGenerator = this.adapters.suggestion?.generate({
385
- messages: this.messages,
386
- });
387
-
388
- if (Symbol.asyncIterator in promiseOrGenerator) {
389
- for await (const r of promiseOrGenerator) {
390
- if (signal.aborted) break;
391
- this._suggestions = r;
392
- this._notifySubscribers();
393
- }
394
- } else {
395
- const result = await promiseOrGenerator;
396
- if (signal.aborted) return;
397
- this._suggestions = result;
398
- this._notifySubscribers();
399
- }
384
+ this._suggestionsController = new AbortController();
385
+ const signal = this._suggestionsController.signal;
386
+ const adapter = this.adapters.suggestion;
387
+ void (async () => {
388
+ try {
389
+ const promiseOrGenerator = adapter.generate({
390
+ messages: this.messages,
391
+ signal,
392
+ });
393
+ await consumeSuggestionResult(promiseOrGenerator, {
394
+ signal,
395
+ onUpdate: (r) => {
396
+ this._suggestions = r;
397
+ this._notifySubscribers();
398
+ },
399
+ });
400
+ } catch {}
401
+ })();
400
402
  }
401
403
  }
402
404
 
@@ -539,7 +541,6 @@ export class LocalThreadRuntimeCore
539
541
  });
540
542
  }
541
543
  } catch (e) {
542
- // TODO this should be handled by the run result stream
543
544
  if (e instanceof AbortError) {
544
545
  updateMessage({
545
546
  status: { type: "incomplete", reason: "cancelled" },
@@ -553,10 +554,7 @@ export class LocalThreadRuntimeCore
553
554
  status: {
554
555
  type: "incomplete",
555
556
  reason: "error",
556
- error:
557
- e instanceof Error
558
- ? e.message
559
- : `[${typeof e}] ${new String(e).toString()}`,
557
+ error: toAssistantError(e),
560
558
  },
561
559
  });
562
560
 
@@ -584,6 +582,8 @@ export class LocalThreadRuntimeCore
584
582
  const error = new AbortError(true);
585
583
  this.abortController?.abort(error);
586
584
  this.abortController = null;
585
+ this._suggestionsController?.abort();
586
+ this._suggestionsController = null;
587
587
  }
588
588
 
589
589
  public cancelRun() {
@@ -591,6 +591,8 @@ export class LocalThreadRuntimeCore
591
591
  const error = new AbortError(false);
592
592
  this.abortController?.abort(error);
593
593
  this.abortController = null;
594
+ this._suggestionsController?.abort();
595
+ this._suggestionsController = null;
594
596
  }
595
597
 
596
598
  public addToolResult({
@@ -23,14 +23,17 @@ export const shouldContinue = (
23
23
  // TODO legacy behavior -- make specifying human tool names required
24
24
  if (humanToolNames === undefined) {
25
25
  return result.content.every(
26
- (c) => c.type !== "tool-call" || !!c.result || c.approval !== undefined,
26
+ (c) =>
27
+ c.type !== "tool-call" ||
28
+ c.result !== undefined ||
29
+ c.approval !== undefined,
27
30
  );
28
31
  }
29
32
 
30
33
  return result.content.every(
31
34
  (c) =>
32
35
  c.type !== "tool-call" ||
33
- !!c.result ||
36
+ c.result !== undefined ||
34
37
  c.approval !== undefined ||
35
38
  !humanToolNames.includes(c.toolName),
36
39
  );
@@ -0,0 +1,14 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { InMemoryThreadListAdapter } from "./in-memory";
3
+
4
+ describe("InMemoryThreadListAdapter", () => {
5
+ it("closes its title stream when title generation is unsupported", async () => {
6
+ const adapter = new InMemoryThreadListAdapter();
7
+ const stream = await adapter.generateTitle();
8
+
9
+ await expect(stream.getReader().read()).resolves.toEqual({
10
+ done: true,
11
+ value: undefined,
12
+ });
13
+ });
14
+ });
@@ -38,10 +38,18 @@ export class InMemoryThreadListAdapter implements RemoteThreadListAdapter {
38
38
  }
39
39
 
40
40
  generateTitle(): Promise<AssistantStream> {
41
- return Promise.resolve(new ReadableStream<AssistantStreamChunk>());
42
- }
43
-
44
- fetch(_threadId: string): Promise<RemoteThreadMetadata> {
45
- return Promise.reject(new Error("Thread not found"));
41
+ return Promise.resolve(
42
+ new ReadableStream<AssistantStreamChunk>({
43
+ start(controller) {
44
+ controller.close();
45
+ },
46
+ }),
47
+ );
48
+ }
49
+
50
+ fetch(threadId: string): Promise<RemoteThreadMetadata> {
51
+ return Promise.reject(
52
+ new Error(`Thread "${threadId}" not found in in-memory thread list.`),
53
+ );
46
54
  }
47
55
  }
@@ -309,6 +309,32 @@ describe("MessageRepository", () => {
309
309
  expect(exported.headId).toBe("u");
310
310
  });
311
311
 
312
+ it("re-parents children of a skipped optimistic message in export()", () => {
313
+ repository.addOrUpdateMessage(null, createTestMessage({ id: "u1" }));
314
+ repository.addOrUpdateMessage("u1", optimistic({ id: "a1" }));
315
+ repository.addOrUpdateMessage(
316
+ "a1",
317
+ createTestMessage({ id: "u2", role: "user" }),
318
+ );
319
+ repository.addOrUpdateMessage("u2", createTestMessage({ id: "a2" }));
320
+ repository.resetHead("a2");
321
+
322
+ const exported = repository.export();
323
+ const byId = new Map(exported.messages.map((m) => [m.message.id, m]));
324
+
325
+ expect([...byId.keys()].sort()).toEqual(["a2", "u1", "u2"]);
326
+ expect(byId.get("u2")?.parentId).toBe("u1");
327
+ expect(byId.get("a2")?.parentId).toBe("u2");
328
+
329
+ const restored = new MessageRepository();
330
+ restored.import(exported);
331
+ expect(restored.getMessages().map((m) => m.id)).toEqual([
332
+ "u1",
333
+ "u2",
334
+ "a2",
335
+ ]);
336
+ });
337
+
312
338
  it("round-trips through export/import without resurrecting the placeholder", () => {
313
339
  const parent = createTestMessage({ id: "u" });
314
340
  const real = createTestMessage({ id: "a" });
@@ -0,0 +1,42 @@
1
+ import { describe, it, expect, vi } from "vitest";
2
+ import { createCore, makeAdapter } from "./remote-thread-list-test-helpers";
3
+ import { InMemoryThreadListAdapter } from "../runtimes/remote-thread-list/adapter/in-memory";
4
+
5
+ describe("RemoteThreadListThreadListRuntimeCore errors", () => {
6
+ it("includes the requested thread id when a thread is missing", () => {
7
+ const core = createCore(makeAdapter());
8
+
9
+ expect(() => core.rename("missing-thread", "New title")).toThrow(
10
+ 'Thread "missing-thread" not found while renaming it.',
11
+ );
12
+ });
13
+
14
+ it("includes the requested thread id and status when an operation is invalid", async () => {
15
+ const adapter = makeAdapter({
16
+ list: vi.fn(async () => ({
17
+ threads: [
18
+ {
19
+ status: "archived" as const,
20
+ remoteId: "archived-thread",
21
+ externalId: "archived-thread",
22
+ title: "Archived",
23
+ },
24
+ ],
25
+ })),
26
+ });
27
+ const core = createCore(adapter);
28
+ await core.getLoadThreadsPromise();
29
+
30
+ await expect(core.archive("archived-thread")).rejects.toThrow(
31
+ 'Thread "archived-thread" has status "archived", so it cannot be archived.',
32
+ );
33
+ });
34
+
35
+ it("includes the requested thread id when the in-memory adapter cannot fetch it", async () => {
36
+ const adapter = new InMemoryThreadListAdapter();
37
+
38
+ await expect(adapter.fetch("missing-thread")).rejects.toThrow(
39
+ 'Thread "missing-thread" not found in in-memory thread list.',
40
+ );
41
+ });
42
+ });