@assistant-ui/core 0.2.21 → 0.2.23

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 (345) hide show
  1. package/dist/adapters/attachment.d.ts.map +1 -1
  2. package/dist/adapters/speech.d.ts.map +1 -1
  3. package/dist/adapters/speech.js +9 -2
  4. package/dist/adapters/speech.js.map +1 -1
  5. package/dist/adapters/thread-history.d.ts +8 -0
  6. package/dist/adapters/thread-history.d.ts.map +1 -1
  7. package/dist/adapters/voice.d.ts.map +1 -1
  8. package/dist/adapters/voice.js +13 -5
  9. package/dist/adapters/voice.js.map +1 -1
  10. package/dist/internal.d.ts +5 -2
  11. package/dist/internal.js +5 -2
  12. package/dist/model-context/frame/host.d.ts +1 -0
  13. package/dist/model-context/frame/host.d.ts.map +1 -1
  14. package/dist/model-context/frame/host.js +41 -21
  15. package/dist/model-context/frame/host.js.map +1 -1
  16. package/dist/model-context/frame/provider.d.ts.map +1 -1
  17. package/dist/model-context/frame/provider.js +1 -0
  18. package/dist/model-context/frame/provider.js.map +1 -1
  19. package/dist/model-context/interactable-composer-metadata.js.map +1 -1
  20. package/dist/model-context/types.d.ts.map +1 -1
  21. package/dist/model-context/types.js +13 -4
  22. package/dist/model-context/types.js.map +1 -1
  23. package/dist/react/AssistantProvider.js.map +1 -1
  24. package/dist/react/adapters/LocalStorageThreadListAdapter.d.ts.map +1 -1
  25. package/dist/react/adapters/LocalStorageThreadListAdapter.js +78 -50
  26. package/dist/react/adapters/LocalStorageThreadListAdapter.js.map +1 -1
  27. package/dist/react/client/Interactables.js.map +1 -1
  28. package/dist/react/client/Tools.d.ts.map +1 -1
  29. package/dist/react/client/interactable-model-context.d.ts.map +1 -1
  30. package/dist/react/client/interactable-model-context.js +1 -0
  31. package/dist/react/client/interactable-model-context.js.map +1 -1
  32. package/dist/react/interactables-legacy/useInteractableState.js +1 -0
  33. package/dist/react/interactables-legacy/useInteractableState.js.map +1 -1
  34. package/dist/react/model-context/useInteractable.js.map +1 -1
  35. package/dist/react/model-context/useInteractableState.js +1 -0
  36. package/dist/react/model-context/useInteractableState.js.map +1 -1
  37. package/dist/react/model-context/useInteractableVersions.js +1 -0
  38. package/dist/react/model-context/useInteractableVersions.js.map +1 -1
  39. package/dist/react/model-context/useToolArgsStatus.js +1 -0
  40. package/dist/react/model-context/useToolArgsStatus.js.map +1 -1
  41. package/dist/react/primitive-hooks/useActionBarCopy.js +14 -13
  42. package/dist/react/primitive-hooks/useActionBarCopy.js.map +1 -1
  43. package/dist/react/primitive-hooks/useActionBarEdit.js +1 -0
  44. package/dist/react/primitive-hooks/useActionBarEdit.js.map +1 -1
  45. package/dist/react/primitive-hooks/useActionBarFeedback.js +1 -0
  46. package/dist/react/primitive-hooks/useActionBarFeedback.js.map +1 -1
  47. package/dist/react/primitive-hooks/useActionBarReload.js +1 -0
  48. package/dist/react/primitive-hooks/useActionBarReload.js.map +1 -1
  49. package/dist/react/primitive-hooks/useActionBarSpeak.js +1 -0
  50. package/dist/react/primitive-hooks/useActionBarSpeak.js.map +1 -1
  51. package/dist/react/primitive-hooks/useActionBarStopSpeaking.js +1 -0
  52. package/dist/react/primitive-hooks/useActionBarStopSpeaking.js.map +1 -1
  53. package/dist/react/primitive-hooks/useBranchPickerNext.js +1 -0
  54. package/dist/react/primitive-hooks/useBranchPickerNext.js.map +1 -1
  55. package/dist/react/primitive-hooks/useBranchPickerPrevious.js +1 -0
  56. package/dist/react/primitive-hooks/useBranchPickerPrevious.js.map +1 -1
  57. package/dist/react/primitive-hooks/useComposerAddAttachment.d.ts.map +1 -1
  58. package/dist/react/primitive-hooks/useComposerAddAttachment.js +1 -0
  59. package/dist/react/primitive-hooks/useComposerAddAttachment.js.map +1 -1
  60. package/dist/react/primitive-hooks/useComposerCancel.js +1 -0
  61. package/dist/react/primitive-hooks/useComposerCancel.js.map +1 -1
  62. package/dist/react/primitive-hooks/useComposerDictate.js +1 -0
  63. package/dist/react/primitive-hooks/useComposerDictate.js.map +1 -1
  64. package/dist/react/primitive-hooks/useComposerSend.d.ts.map +1 -1
  65. package/dist/react/primitive-hooks/useComposerSend.js +1 -0
  66. package/dist/react/primitive-hooks/useComposerSend.js.map +1 -1
  67. package/dist/react/primitive-hooks/useEditComposerCancel.js +1 -0
  68. package/dist/react/primitive-hooks/useEditComposerCancel.js.map +1 -1
  69. package/dist/react/primitive-hooks/useEditComposerSend.js +1 -0
  70. package/dist/react/primitive-hooks/useEditComposerSend.js.map +1 -1
  71. package/dist/react/primitive-hooks/useMessageBranching.js +1 -0
  72. package/dist/react/primitive-hooks/useMessageBranching.js.map +1 -1
  73. package/dist/react/primitive-hooks/useMessageReload.js +1 -0
  74. package/dist/react/primitive-hooks/useMessageReload.js.map +1 -1
  75. package/dist/react/primitive-hooks/useSuggestionTrigger.js +1 -0
  76. package/dist/react/primitive-hooks/useSuggestionTrigger.js.map +1 -1
  77. package/dist/react/primitive-hooks/useThreadListItemArchive.js +1 -0
  78. package/dist/react/primitive-hooks/useThreadListItemArchive.js.map +1 -1
  79. package/dist/react/primitive-hooks/useThreadListItemDelete.js +1 -0
  80. package/dist/react/primitive-hooks/useThreadListItemDelete.js.map +1 -1
  81. package/dist/react/primitive-hooks/useThreadListItemTrigger.js +1 -0
  82. package/dist/react/primitive-hooks/useThreadListItemTrigger.js.map +1 -1
  83. package/dist/react/primitive-hooks/useThreadListItemUnarchive.js +1 -0
  84. package/dist/react/primitive-hooks/useThreadListItemUnarchive.js.map +1 -1
  85. package/dist/react/primitive-hooks/useThreadListLoadMore.js +1 -0
  86. package/dist/react/primitive-hooks/useThreadListLoadMore.js.map +1 -1
  87. package/dist/react/primitive-hooks/useThreadListNew.js +1 -0
  88. package/dist/react/primitive-hooks/useThreadListNew.js.map +1 -1
  89. package/dist/react/primitives/chainOfThought/ChainOfThoughtParts.d.ts.map +1 -1
  90. package/dist/react/primitives/chainOfThought/ChainOfThoughtParts.js +1 -1
  91. package/dist/react/primitives/chainOfThought/ChainOfThoughtParts.js.map +1 -1
  92. package/dist/react/primitives/composer/ComposerAttachments.js.map +1 -1
  93. package/dist/react/primitives/composer/ComposerQueue.d.ts.map +1 -1
  94. package/dist/react/primitives/composer/ComposerQueue.js.map +1 -1
  95. package/dist/react/primitives/generativeUI/GenerativeUI.d.ts.map +1 -1
  96. package/dist/react/primitives/message/MessageAttachments.js.map +1 -1
  97. package/dist/react/primitives/message/MessageGroupedParts.js.map +1 -1
  98. package/dist/react/primitives/message/MessageParts.d.ts +2 -1
  99. package/dist/react/primitives/message/MessageParts.d.ts.map +1 -1
  100. package/dist/react/primitives/message/MessageParts.js +46 -36
  101. package/dist/react/primitives/message/MessageParts.js.map +1 -1
  102. package/dist/react/primitives/part/PartMessages.js.map +1 -1
  103. package/dist/react/primitives/thread/ThreadMessages.js.map +1 -1
  104. package/dist/react/primitives/thread/ThreadSuggestions.js.map +1 -1
  105. package/dist/react/primitives/threadList/ThreadListItems.js.map +1 -1
  106. package/dist/react/providers/ChainOfThoughtPartByIndexProvider.d.ts.map +1 -1
  107. package/dist/react/providers/ChainOfThoughtPartByIndexProvider.js +18 -30
  108. package/dist/react/providers/ChainOfThoughtPartByIndexProvider.js.map +1 -1
  109. package/dist/react/providers/PartByIndexProvider.d.ts.map +1 -1
  110. package/dist/react/providers/PartByIndexProvider.js +18 -30
  111. package/dist/react/providers/PartByIndexProvider.js.map +1 -1
  112. package/dist/react/providers/TextMessagePartProvider.js +1 -0
  113. package/dist/react/providers/TextMessagePartProvider.js.map +1 -1
  114. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.d.ts.map +1 -1
  115. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js +1 -0
  116. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js.map +1 -1
  117. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts +9 -2
  118. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts.map +1 -1
  119. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js +55 -12
  120. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js.map +1 -1
  121. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.d.ts +31 -1
  122. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.d.ts.map +1 -1
  123. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js +12 -2
  124. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js.map +1 -1
  125. package/dist/react/runtimes/cloud/CloudFileAttachmentAdapter.d.ts +1 -1
  126. package/dist/react/runtimes/cloud/CloudFileAttachmentAdapter.d.ts.map +1 -1
  127. package/dist/react/runtimes/cloud/CloudFileAttachmentAdapter.js +3 -2
  128. package/dist/react/runtimes/cloud/CloudFileAttachmentAdapter.js.map +1 -1
  129. package/dist/react/runtimes/cloud/auiV0.d.ts +12 -1
  130. package/dist/react/runtimes/cloud/auiV0.d.ts.map +1 -1
  131. package/dist/react/runtimes/cloud/auiV0.js +2 -1
  132. package/dist/react/runtimes/cloud/auiV0.js.map +1 -1
  133. package/dist/react/runtimes/cloud/useCloudThreadListAdapter.js +75 -73
  134. package/dist/react/runtimes/cloud/useCloudThreadListAdapter.js.map +1 -1
  135. package/dist/react/runtimes/createMessageConverter.d.ts.map +1 -1
  136. package/dist/react/runtimes/external-message-converter.js +2 -2
  137. package/dist/react/runtimes/external-message-converter.js.map +1 -1
  138. package/dist/react/runtimes/useExternalStoreSharedOptions.js +1 -0
  139. package/dist/react/runtimes/useExternalStoreSharedOptions.js.map +1 -1
  140. package/dist/react/runtimes/useLocalRuntime.d.ts.map +1 -1
  141. package/dist/react/runtimes/useLocalRuntime.js +9 -1
  142. package/dist/react/runtimes/useLocalRuntime.js.map +1 -1
  143. package/dist/runtime/api/assistant-runtime.d.ts +1 -1
  144. package/dist/runtime/api/assistant-runtime.d.ts.map +1 -1
  145. package/dist/runtime/api/assistant-runtime.js +1 -1
  146. package/dist/runtime/api/assistant-runtime.js.map +1 -1
  147. package/dist/runtime/api/attachment-runtime.d.ts +1 -1
  148. package/dist/runtime/api/attachment-runtime.d.ts.map +1 -1
  149. package/dist/runtime/api/attachment-runtime.js +1 -1
  150. package/dist/runtime/api/attachment-runtime.js.map +1 -1
  151. package/dist/runtime/api/composer-runtime.d.ts +2 -2
  152. package/dist/runtime/api/composer-runtime.d.ts.map +1 -1
  153. package/dist/runtime/api/composer-runtime.js +2 -2
  154. package/dist/runtime/api/composer-runtime.js.map +1 -1
  155. package/dist/runtime/api/message-part-runtime.d.ts +4 -4
  156. package/dist/runtime/api/message-part-runtime.d.ts.map +1 -1
  157. package/dist/runtime/api/message-part-runtime.js +3 -3
  158. package/dist/runtime/api/message-part-runtime.js.map +1 -1
  159. package/dist/runtime/api/message-runtime.d.ts +1 -1
  160. package/dist/runtime/api/message-runtime.d.ts.map +1 -1
  161. package/dist/runtime/api/message-runtime.js +6 -6
  162. package/dist/runtime/api/message-runtime.js.map +1 -1
  163. package/dist/runtime/api/thread-list-item-runtime.d.ts +1 -1
  164. package/dist/runtime/api/thread-list-item-runtime.d.ts.map +1 -1
  165. package/dist/runtime/api/thread-list-item-runtime.js +4 -3
  166. package/dist/runtime/api/thread-list-item-runtime.js.map +1 -1
  167. package/dist/runtime/api/thread-list-runtime.d.ts +1 -1
  168. package/dist/runtime/api/thread-list-runtime.d.ts.map +1 -1
  169. package/dist/runtime/api/thread-list-runtime.js +1 -1
  170. package/dist/runtime/api/thread-list-runtime.js.map +1 -1
  171. package/dist/runtime/api/thread-runtime.d.ts.map +1 -1
  172. package/dist/runtime/base/base-composer-runtime-core.d.ts +4 -1
  173. package/dist/runtime/base/base-composer-runtime-core.d.ts.map +1 -1
  174. package/dist/runtime/base/base-composer-runtime-core.js +41 -14
  175. package/dist/runtime/base/base-composer-runtime-core.js.map +1 -1
  176. package/dist/runtime/base/base-thread-runtime-core.d.ts +1 -1
  177. package/dist/runtime/base/base-thread-runtime-core.d.ts.map +1 -1
  178. package/dist/runtime/base/base-thread-runtime-core.js +3 -2
  179. package/dist/runtime/base/base-thread-runtime-core.js.map +1 -1
  180. package/dist/runtime/base/default-edit-composer-runtime-core.d.ts +2 -2
  181. package/dist/runtime/base/default-edit-composer-runtime-core.d.ts.map +1 -1
  182. package/dist/runtime/base/default-edit-composer-runtime-core.js +6 -4
  183. package/dist/runtime/base/default-edit-composer-runtime-core.js.map +1 -1
  184. package/dist/runtime/base/default-thread-composer-runtime-core.d.ts +1 -1
  185. package/dist/runtime/base/default-thread-composer-runtime-core.d.ts.map +1 -1
  186. package/dist/runtime/base/default-thread-composer-runtime-core.js +3 -3
  187. package/dist/runtime/base/default-thread-composer-runtime-core.js.map +1 -1
  188. package/dist/runtime/utils/message-repository.d.ts.map +1 -1
  189. package/dist/runtime/utils/message-repository.js +4 -2
  190. package/dist/runtime/utils/message-repository.js.map +1 -1
  191. package/dist/runtime/utils/thread-message-like.js +4 -1
  192. package/dist/runtime/utils/thread-message-like.js.map +1 -1
  193. package/dist/runtimes/external-store/external-store-runtime-core.d.ts.map +1 -1
  194. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.d.ts +1 -1
  195. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.d.ts.map +1 -1
  196. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.js +1 -1
  197. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.js.map +1 -1
  198. package/dist/runtimes/external-store/external-store-thread-runtime-core.d.ts.map +1 -1
  199. package/dist/runtimes/external-store/external-store-thread-runtime-core.js +12 -0
  200. package/dist/runtimes/external-store/external-store-thread-runtime-core.js.map +1 -1
  201. package/dist/runtimes/internal.d.ts +2 -1
  202. package/dist/runtimes/internal.js +2 -1
  203. package/dist/runtimes/local/local-runtime-core.d.ts.map +1 -1
  204. package/dist/runtimes/local/local-thread-list-runtime-core.d.ts.map +1 -1
  205. package/dist/runtimes/local/local-thread-runtime-core.d.ts +3 -0
  206. package/dist/runtimes/local/local-thread-runtime-core.d.ts.map +1 -1
  207. package/dist/runtimes/local/local-thread-runtime-core.js +49 -15
  208. package/dist/runtimes/local/local-thread-runtime-core.js.map +1 -1
  209. package/dist/runtimes/readonly/ReadonlyThreadRuntimeCore.d.ts.map +1 -1
  210. package/dist/runtimes/remote-thread-list/optimistic-state.d.ts +4 -4
  211. package/dist/runtimes/remote-thread-list/optimistic-state.d.ts.map +1 -1
  212. package/dist/runtimes/remote-thread-list/optimistic-state.js +22 -9
  213. package/dist/runtimes/remote-thread-list/optimistic-state.js.map +1 -1
  214. package/dist/runtimes/remote-thread-list/types.d.ts +1 -1
  215. package/dist/runtimes/tool-invocations/ToolInvocationTracker.d.ts +32 -27
  216. package/dist/runtimes/tool-invocations/ToolInvocationTracker.d.ts.map +1 -1
  217. package/dist/runtimes/tool-invocations/ToolInvocationTracker.js +3 -2
  218. package/dist/runtimes/tool-invocations/ToolInvocationTracker.js.map +1 -1
  219. package/dist/store/clients/chain-of-thought-client.d.ts.map +1 -1
  220. package/dist/store/clients/no-op-composer-client.d.ts.map +1 -1
  221. package/dist/store/clients/no-op-composer-client.js +1 -0
  222. package/dist/store/clients/no-op-composer-client.js.map +1 -1
  223. package/dist/store/runtime-clients/attachment-runtime-client.d.ts.map +1 -1
  224. package/dist/store/runtime-clients/composer-runtime-client.d.ts.map +1 -1
  225. package/dist/store/runtime-clients/message-part-runtime-client.d.ts.map +1 -1
  226. package/dist/store/runtime-clients/message-runtime-client.d.ts.map +1 -1
  227. package/dist/store/runtime-clients/thread-list-item-runtime-client.d.ts.map +1 -1
  228. package/dist/store/runtime-clients/thread-list-runtime-client.d.ts.map +1 -1
  229. package/dist/store/runtime-clients/thread-list-runtime-client.js +1 -0
  230. package/dist/store/runtime-clients/thread-list-runtime-client.js.map +1 -1
  231. package/dist/store/runtime-clients/thread-runtime-client.d.ts.map +1 -1
  232. package/dist/store/runtime-clients/thread-runtime-client.js +17 -15
  233. package/dist/store/runtime-clients/thread-runtime-client.js.map +1 -1
  234. package/dist/store/scopes/thread.d.ts +1 -0
  235. package/dist/store/scopes/thread.d.ts.map +1 -1
  236. package/dist/subscribable/subscribable.d.ts +4 -4
  237. package/dist/subscribable/subscribable.d.ts.map +1 -1
  238. package/dist/subscribable/subscribable.js +14 -6
  239. package/dist/subscribable/subscribable.js.map +1 -1
  240. package/dist/tests/remote-thread-list-test-helpers.d.ts +2 -1
  241. package/dist/tests/remote-thread-list-test-helpers.d.ts.map +1 -1
  242. package/dist/tests/remote-thread-list-test-helpers.js +5 -2
  243. package/dist/tests/remote-thread-list-test-helpers.js.map +1 -1
  244. package/dist/types/attachment.d.ts +2 -1
  245. package/dist/types/attachment.d.ts.map +1 -1
  246. package/dist/types/attachment.js +6 -0
  247. package/dist/types/attachment.js.map +1 -0
  248. package/dist/types/message.d.ts +1 -0
  249. package/dist/types/message.d.ts.map +1 -1
  250. package/dist/types/message.js.map +1 -1
  251. package/dist/utils/data-url.d.ts +9 -0
  252. package/dist/utils/data-url.d.ts.map +1 -0
  253. package/dist/utils/data-url.js +14 -0
  254. package/dist/utils/data-url.js.map +1 -0
  255. package/dist/utils/notify-event-listeners.d.ts +6 -0
  256. package/dist/utils/notify-event-listeners.d.ts.map +1 -0
  257. package/dist/utils/notify-event-listeners.js +16 -0
  258. package/dist/utils/notify-event-listeners.js.map +1 -0
  259. package/package.json +10 -9
  260. package/src/adapters/speech.test.ts +88 -0
  261. package/src/adapters/speech.ts +17 -2
  262. package/src/adapters/thread-history.ts +8 -0
  263. package/src/adapters/voice.test.ts +130 -0
  264. package/src/adapters/voice.ts +26 -6
  265. package/src/internal.ts +6 -1
  266. package/src/model-context/frame/host.test.ts +147 -0
  267. package/src/model-context/frame/host.ts +56 -19
  268. package/src/model-context/frame/provider.test.ts +83 -0
  269. package/src/model-context/frame/provider.ts +1 -0
  270. package/src/model-context/types.ts +16 -6
  271. package/src/react/adapters/LocalStorageThreadListAdapter.test.ts +82 -0
  272. package/src/react/adapters/LocalStorageThreadListAdapter.tsx +108 -58
  273. package/src/react/primitive-hooks/useActionBarCopy.test.ts +79 -0
  274. package/src/react/primitive-hooks/useActionBarCopy.ts +4 -3
  275. package/src/react/primitives/chainOfThought/ChainOfThoughtParts.tsx +1 -3
  276. package/src/react/primitives/message/MessageParts.tsx +48 -35
  277. package/src/react/providers/ChainOfThoughtPartByIndexProvider.tsx +19 -2
  278. package/src/react/providers/PartByIndexProvider.tsx +16 -2
  279. package/src/react/runtimes/RemoteThreadListHookInstanceManager.test.tsx +18 -0
  280. package/src/react/runtimes/RemoteThreadListHookInstanceManager.tsx +1 -0
  281. package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.tsx +68 -13
  282. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.test.ts +34 -0
  283. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.test.tsx +69 -0
  284. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.ts +27 -6
  285. package/src/react/runtimes/cloud/CloudFileAttachmentAdapter.test.ts +16 -0
  286. package/src/react/runtimes/cloud/CloudFileAttachmentAdapter.ts +7 -2
  287. package/src/react/runtimes/cloud/auiV0.ts +14 -0
  288. package/src/react/runtimes/cloud/useCloudThreadListAdapter.tsx +90 -88
  289. package/src/react/runtimes/external-message-converter.test.ts +346 -0
  290. package/src/react/runtimes/external-message-converter.test.tsx +21 -0
  291. package/src/react/runtimes/external-message-converter.ts +2 -2
  292. package/src/react/runtimes/useLocalRuntime.test.tsx +99 -0
  293. package/src/react/runtimes/useLocalRuntime.ts +13 -1
  294. package/src/runtime/api/assistant-runtime.ts +4 -1
  295. package/src/runtime/api/attachment-runtime.ts +8 -2
  296. package/src/runtime/api/composer-runtime.ts +9 -5
  297. package/src/runtime/api/message-part-runtime.ts +10 -3
  298. package/src/runtime/api/message-runtime.test.ts +81 -0
  299. package/src/runtime/api/message-runtime.ts +11 -8
  300. package/src/runtime/api/thread-list-item-runtime.ts +13 -3
  301. package/src/runtime/api/thread-list-runtime.ts +10 -2
  302. package/src/runtime/base/base-composer-runtime-core.ts +77 -31
  303. package/src/runtime/base/base-thread-runtime-core.ts +7 -2
  304. package/src/runtime/base/default-edit-composer-runtime-core.ts +18 -4
  305. package/src/runtime/base/default-thread-composer-runtime-core.ts +13 -3
  306. package/src/runtime/utils/message-repository.ts +19 -13
  307. package/src/runtime/utils/thread-message-like.ts +1 -0
  308. package/src/runtimes/external-store/external-store-thread-list-runtime-core.ts +4 -1
  309. package/src/runtimes/external-store/external-store-thread-runtime-core.ts +15 -0
  310. package/src/runtimes/internal.ts +3 -0
  311. package/src/runtimes/local/local-thread-runtime-core.test.ts +746 -1
  312. package/src/runtimes/local/local-thread-runtime-core.ts +89 -23
  313. package/src/runtimes/local/should-continue.test.ts +122 -0
  314. package/src/runtimes/remote-thread-list/optimistic-state.test.ts +63 -0
  315. package/src/runtimes/remote-thread-list/optimistic-state.ts +49 -17
  316. package/src/runtimes/remote-thread-list/types.ts +1 -1
  317. package/src/runtimes/tool-invocations/EDGE_CASES.md +13 -19
  318. package/src/runtimes/tool-invocations/ToolInvocationTracker.test.ts +128 -8
  319. package/src/runtimes/tool-invocations/ToolInvocationTracker.ts +42 -34
  320. package/src/store/clients/model-context-client.test.ts +21 -1
  321. package/src/store/runtime-clients/thread-runtime-client.ts +2 -1
  322. package/src/store/scopes/thread.ts +1 -0
  323. package/src/subscribable/subscribable.test.ts +58 -0
  324. package/src/subscribable/subscribable.ts +29 -11
  325. package/src/tests/MessageRepository.test.ts +12 -0
  326. package/src/tests/OptimisticState-list-race.test.ts +33 -0
  327. package/src/tests/RemoteThreadListThreadListRuntimeCore-switchToThread-order.test.ts +223 -0
  328. package/src/tests/RemoteThreadListThreadListRuntimeCore-switchToThread-position.test.ts +121 -0
  329. package/src/tests/auiV0Encode.test.ts +74 -0
  330. package/src/tests/base-composer-runtime-core-addAttachment.test.ts +44 -0
  331. package/src/tests/base-composer-runtime-core-send.test.ts +433 -5
  332. package/src/tests/base-composer-runtime-core.test.ts +422 -0
  333. package/src/tests/default-edit-composer-runtime-core.test.ts +43 -5
  334. package/src/tests/event-subscription-listener-errors.test.ts +205 -0
  335. package/src/tests/external-store-thread-list-runtime-core.test.ts +6 -6
  336. package/src/tests/external-store-thread-runtime-core-adapter.test.ts +575 -0
  337. package/src/tests/external-store-thread-runtime-core.test.ts +55 -1
  338. package/src/tests/remote-thread-list-test-helpers.ts +14 -7
  339. package/src/tests/thread-message-like.test.ts +57 -0
  340. package/src/types/attachment.ts +5 -0
  341. package/src/types/message.ts +1 -0
  342. package/src/utils/data-url.test.ts +50 -0
  343. package/src/utils/data-url.ts +9 -0
  344. package/src/utils/notify-event-listeners.ts +30 -0
  345. package/src/tests/OptimisticState-delete-crash.test.ts +0 -208
@@ -0,0 +1,83 @@
1
+ /**
2
+ * @vitest-environment jsdom
3
+ */
4
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
5
+ import { AssistantFrameProvider } from "./provider";
6
+ import { FRAME_MESSAGE_CHANNEL } from "./types";
7
+
8
+ describe("AssistantFrameProvider", () => {
9
+ let messageHandler: ((event: MessageEvent) => void) | undefined;
10
+ let parentWindow: Window;
11
+
12
+ beforeEach(() => {
13
+ parentWindow = {
14
+ postMessage: vi.fn(),
15
+ } as unknown as Window;
16
+
17
+ Object.defineProperty(window, "parent", {
18
+ value: parentWindow,
19
+ configurable: true,
20
+ });
21
+
22
+ vi.spyOn(window, "addEventListener").mockImplementation(
23
+ (event, listener) => {
24
+ if (event === "message" && typeof listener === "function") {
25
+ messageHandler = listener as (event: MessageEvent) => void;
26
+ }
27
+ },
28
+ );
29
+ vi.spyOn(window, "removeEventListener").mockImplementation(() => {});
30
+ });
31
+
32
+ afterEach(() => {
33
+ AssistantFrameProvider.dispose();
34
+ vi.restoreAllMocks();
35
+ });
36
+
37
+ it("only accepts tool calls from the parent window", async () => {
38
+ const execute = vi.fn(async () => "result");
39
+ AssistantFrameProvider.addModelContextProvider(
40
+ {
41
+ getModelContext: () => ({
42
+ tools: {
43
+ sensitiveTool: { execute },
44
+ },
45
+ }),
46
+ },
47
+ "https://parent.example",
48
+ );
49
+
50
+ const toolCall = {
51
+ channel: FRAME_MESSAGE_CHANNEL,
52
+ message: {
53
+ type: "tool-call",
54
+ id: "tool-call-1",
55
+ toolName: "sensitiveTool",
56
+ args: {},
57
+ },
58
+ };
59
+ const otherWindow = {
60
+ postMessage: vi.fn(),
61
+ } as unknown as Window;
62
+
63
+ messageHandler?.(
64
+ new MessageEvent("message", {
65
+ data: toolCall,
66
+ origin: "https://parent.example",
67
+ source: otherWindow,
68
+ }),
69
+ );
70
+
71
+ expect(execute).not.toHaveBeenCalled();
72
+
73
+ messageHandler?.(
74
+ new MessageEvent("message", {
75
+ data: toolCall,
76
+ origin: "https://parent.example",
77
+ source: parentWindow,
78
+ }),
79
+ );
80
+
81
+ await vi.waitFor(() => expect(execute).toHaveBeenCalledOnce());
82
+ });
83
+ });
@@ -59,6 +59,7 @@ export class AssistantFrameProvider {
59
59
  private handleMessage(event: MessageEvent) {
60
60
  if (this._targetOrigin !== "*" && event.origin !== this._targetOrigin)
61
61
  return;
62
+ if (event.source !== window.parent) return;
62
63
  if (event.data?.channel !== FRAME_MESSAGE_CHANNEL) return;
63
64
 
64
65
  const message = event.data.message as FrameMessage;
@@ -55,6 +55,12 @@ export type AssistantContextConfig = {
55
55
  disabled?: boolean | undefined;
56
56
  };
57
57
 
58
+ const stripOverwrite = (tool: Tool<any, any>): Tool<any, any> => {
59
+ if (!tool.overwrite) return tool;
60
+ const { overwrite: _, ...rest } = tool;
61
+ return rest as Tool<any, any>;
62
+ };
63
+
58
64
  export const mergeModelContexts = (
59
65
  configSet: Set<ModelContextProvider>,
60
66
  ): ModelContext => {
@@ -79,25 +85,29 @@ export const mergeModelContexts = (
79
85
  if (existing && existing !== tool) {
80
86
  const existingPriority = toolPriorities[name]!;
81
87
  if (existingPriority === priority) {
82
- throw new Error(
83
- `You tried to define a tool with the name ${name}, but it already exists.`,
84
- );
88
+ if (!tool.overwrite) {
89
+ throw new Error(
90
+ `You tried to define a tool with the name ${name}, but it already exists.`,
91
+ );
92
+ }
93
+ acc.tools![name] = stripOverwrite(tool);
94
+ continue;
85
95
  }
86
96
 
87
97
  const higherPriorityTool =
88
98
  existingPriority > priority ? existing : tool;
89
99
  const lowerPriorityTool =
90
100
  existingPriority > priority ? tool : existing;
91
- acc.tools![name] = {
101
+ acc.tools![name] = stripOverwrite({
92
102
  ...lowerPriorityTool,
93
103
  ...higherPriorityTool,
94
- } as Tool<any, any>;
104
+ } as Tool<any, any>);
95
105
  toolPriorities[name] = Math.max(existingPriority, priority);
96
106
  continue;
97
107
  }
98
108
 
99
109
  if (!acc.tools) acc.tools = {};
100
- acc.tools[name] = tool;
110
+ acc.tools[name] = stripOverwrite(tool);
101
111
  toolPriorities[name] ??= priority;
102
112
  }
103
113
  }
@@ -207,6 +207,88 @@ describe("createLocalStorageAdapter", () => {
207
207
  ]);
208
208
  });
209
209
 
210
+ it("preserves concurrent metadata mutations across adapters", async () => {
211
+ const threadsKey = "@assistant-ui:threads";
212
+ const values = new Map<string, string>();
213
+ let metadataReads = 0;
214
+ let metadataWrites = 0;
215
+ let markFirstWriteStarted!: () => void;
216
+ let releaseFirstWrite!: () => void;
217
+ const firstWriteStarted = new Promise<void>((resolve) => {
218
+ markFirstWriteStarted = resolve;
219
+ });
220
+ const firstWriteCanFinish = new Promise<void>((resolve) => {
221
+ releaseFirstWrite = resolve;
222
+ });
223
+ const storage: AsyncStorageLike = {
224
+ getItem: async (key) => {
225
+ if (key === threadsKey) metadataReads += 1;
226
+ return values.get(key) ?? null;
227
+ },
228
+ setItem: async (key, value) => {
229
+ if (key === threadsKey) {
230
+ metadataWrites += 1;
231
+ if (metadataWrites === 1) {
232
+ markFirstWriteStarted();
233
+ await firstWriteCanFinish;
234
+ }
235
+ }
236
+ values.set(key, value);
237
+ },
238
+ removeItem: async (key) => {
239
+ values.delete(key);
240
+ },
241
+ };
242
+ const firstAdapter = createLocalStorageAdapter({ storage });
243
+ const secondAdapter = createLocalStorageAdapter({ storage });
244
+
245
+ const firstInitialization = firstAdapter.initialize("thread-1");
246
+ await firstWriteStarted;
247
+ const secondInitialization = secondAdapter.initialize("thread-2");
248
+ const readsWhileFirstWritePending = metadataReads;
249
+
250
+ releaseFirstWrite();
251
+ await Promise.all([firstInitialization, secondInitialization]);
252
+
253
+ expect(readsWhileFirstWritePending).toBe(1);
254
+ expect(JSON.parse(values.get(threadsKey) ?? "")).toEqual([
255
+ { remoteId: "thread-2", status: "regular" },
256
+ { remoteId: "thread-1", status: "regular" },
257
+ ]);
258
+ });
259
+
260
+ it("continues processing mutations after a storage failure", async () => {
261
+ const threadsKey = "@assistant-ui:threads";
262
+ const values = new Map<string, string>();
263
+ let shouldFail = true;
264
+ const storage: AsyncStorageLike = {
265
+ getItem: async (key) => values.get(key) ?? null,
266
+ setItem: async (key, value) => {
267
+ if (shouldFail) {
268
+ shouldFail = false;
269
+ throw new Error("Storage unavailable");
270
+ }
271
+ values.set(key, value);
272
+ },
273
+ removeItem: async (key) => {
274
+ values.delete(key);
275
+ },
276
+ };
277
+ const adapter = createLocalStorageAdapter({ storage });
278
+
279
+ await expect(adapter.initialize("thread-1")).rejects.toThrow(
280
+ "Storage unavailable",
281
+ );
282
+ await expect(adapter.initialize("thread-2")).resolves.toEqual({
283
+ remoteId: "thread-2",
284
+ externalId: undefined,
285
+ });
286
+
287
+ expect(JSON.parse(values.get(threadsKey) ?? "")).toEqual([
288
+ { remoteId: "thread-2", status: "regular" },
289
+ ]);
290
+ });
291
+
210
292
  it("includes the thread id when a stored thread cannot be fetched", async () => {
211
293
  const storage = createStorage({
212
294
  "@assistant-ui:threads": JSON.stringify([
@@ -23,6 +23,37 @@ export type AsyncStorageLike = {
23
23
  removeItem(key: string): Promise<void>;
24
24
  };
25
25
 
26
+ class KeyedMutationQueue {
27
+ private readonly tails = new Map<string, Promise<void>>();
28
+
29
+ run<T>(key: string, mutation: () => Promise<T>): Promise<T> {
30
+ const previous = this.tails.get(key);
31
+ const result = previous ? previous.then(mutation) : mutation();
32
+ const tail = result.then(
33
+ () => undefined,
34
+ () => undefined,
35
+ );
36
+
37
+ this.tails.set(key, tail);
38
+ void tail.then(() => {
39
+ if (this.tails.get(key) === tail) this.tails.delete(key);
40
+ });
41
+
42
+ return result;
43
+ }
44
+ }
45
+
46
+ const mutationQueues = new WeakMap<AsyncStorageLike, KeyedMutationQueue>();
47
+
48
+ const getMutationQueue = (storage: AsyncStorageLike): KeyedMutationQueue => {
49
+ let queue = mutationQueues.get(storage);
50
+ if (!queue) {
51
+ queue = new KeyedMutationQueue();
52
+ mutationQueues.set(storage, queue);
53
+ }
54
+ return queue;
55
+ };
56
+
26
57
  type LocalStorageAdapterOptions = {
27
58
  storage: AsyncStorageLike;
28
59
  prefix?: string | undefined;
@@ -244,11 +275,22 @@ export const parseStoredMessageRepository = (
244
275
  };
245
276
 
246
277
  class AsyncStorageHistoryAdapter implements ThreadHistoryAdapter {
278
+ private storage: AsyncStorageLike;
279
+ private aui: ReturnType<typeof useAui>;
280
+ private prefix: string;
281
+ private mutationQueue: KeyedMutationQueue;
282
+
247
283
  constructor(
248
- private storage: AsyncStorageLike,
249
- private aui: ReturnType<typeof useAui>,
250
- private prefix: string,
251
- ) {}
284
+ storage: AsyncStorageLike,
285
+ aui: ReturnType<typeof useAui>,
286
+ prefix: string,
287
+ mutationQueue: KeyedMutationQueue,
288
+ ) {
289
+ this.storage = storage;
290
+ this.aui = aui;
291
+ this.prefix = prefix;
292
+ this.mutationQueue = mutationQueue;
293
+ }
252
294
 
253
295
  private _messagesKey(remoteId: string) {
254
296
  return `${this.prefix}messages:${remoteId}`;
@@ -266,31 +308,34 @@ class AsyncStorageHistoryAdapter implements ThreadHistoryAdapter {
266
308
  const { remoteId } = await this.aui.threadListItem().initialize();
267
309
 
268
310
  const key = this._messagesKey(remoteId);
269
- const raw = await this.storage.getItem(key);
270
- const repo = parseStoredMessageRepository(raw);
311
+ await this.mutationQueue.run(key, async () => {
312
+ const raw = await this.storage.getItem(key);
313
+ const repo = parseStoredMessageRepository(raw);
314
+
315
+ const idx = repo.messages.findIndex(
316
+ (m) => m.message.id === item.message.id,
317
+ );
318
+ if (idx >= 0) {
319
+ repo.messages[idx] = item;
320
+ } else {
321
+ repo.messages.push(item);
322
+ }
323
+ repo.headId = item.message.id;
271
324
 
272
- const idx = repo.messages.findIndex(
273
- (m) => m.message.id === item.message.id,
274
- );
275
- if (idx >= 0) {
276
- repo.messages[idx] = item;
277
- } else {
278
- repo.messages.push(item);
279
- }
280
- repo.headId = item.message.id;
281
-
282
- await this.storage.setItem(key, JSON.stringify(repo));
325
+ await this.storage.setItem(key, JSON.stringify(repo));
326
+ });
283
327
  }
284
328
  }
285
329
 
286
330
  const createHistoryProvider = (
287
331
  storage: AsyncStorageLike,
288
332
  prefix: string,
333
+ mutationQueue: KeyedMutationQueue,
289
334
  ): FC<PropsWithChildren> => {
290
335
  const Provider: FC<PropsWithChildren> = ({ children }) => {
291
336
  const aui = useAui();
292
337
  const history = useMemo(
293
- () => new AsyncStorageHistoryAdapter(storage, aui, prefix),
338
+ () => new AsyncStorageHistoryAdapter(storage, aui, prefix, mutationQueue),
294
339
  [aui],
295
340
  );
296
341
  const adapters = useMemo(() => ({ history }), [history]);
@@ -311,6 +356,7 @@ export const createLocalStorageAdapter = (
311
356
 
312
357
  const threadsKey = `${prefix}threads`;
313
358
  const messagesKey = (threadId: string) => `${prefix}messages:${threadId}`;
359
+ const mutationQueue = getMutationQueue(storage);
314
360
 
315
361
  const loadThreadMetadata = async (): Promise<StoredThreadMetadata[]> => {
316
362
  const raw = await storage.getItem(threadsKey);
@@ -323,8 +369,22 @@ export const createLocalStorageAdapter = (
323
369
  await storage.setItem(threadsKey, JSON.stringify(threads));
324
370
  };
325
371
 
372
+ const updateThreadMetadata = async (
373
+ remoteId: string,
374
+ update: (thread: StoredThreadMetadata) => void,
375
+ ): Promise<void> => {
376
+ await mutationQueue.run(threadsKey, async () => {
377
+ const threads = await loadThreadMetadata();
378
+ const thread = threads.find((item) => item.remoteId === remoteId);
379
+ if (thread) {
380
+ update(thread);
381
+ await saveThreadMetadata(threads);
382
+ }
383
+ });
384
+ };
385
+
326
386
  const adapter: RemoteThreadListAdapter = {
327
- unstable_Provider: createHistoryProvider(storage, prefix),
387
+ unstable_Provider: createHistoryProvider(storage, prefix, mutationQueue),
328
388
 
329
389
  async list(): Promise<RemoteThreadListResponse> {
330
390
  const threads = await loadThreadMetadata();
@@ -343,64 +403,57 @@ export const createLocalStorageAdapter = (
343
403
  threadId: string,
344
404
  ): Promise<RemoteThreadInitializeResponse> {
345
405
  const remoteId = threadId;
346
- const threads = await loadThreadMetadata();
406
+ return mutationQueue.run(threadsKey, async () => {
407
+ const threads = await loadThreadMetadata();
347
408
 
348
- // Only add if not already present
349
- if (!threads.some((t) => t.remoteId === remoteId)) {
350
- threads.unshift({
351
- remoteId,
352
- status: "regular",
353
- });
354
- await saveThreadMetadata(threads);
355
- }
409
+ // Only add if not already present
410
+ if (!threads.some((t) => t.remoteId === remoteId)) {
411
+ threads.unshift({
412
+ remoteId,
413
+ status: "regular",
414
+ });
415
+ await saveThreadMetadata(threads);
416
+ }
356
417
 
357
- return { remoteId, externalId: undefined };
418
+ return { remoteId, externalId: undefined };
419
+ });
358
420
  },
359
421
 
360
422
  async rename(remoteId: string, newTitle: string): Promise<void> {
361
- const threads = await loadThreadMetadata();
362
- const thread = threads.find((t) => t.remoteId === remoteId);
363
- if (thread) {
423
+ await updateThreadMetadata(remoteId, (thread) => {
364
424
  thread.title = newTitle;
365
- await saveThreadMetadata(threads);
366
- }
425
+ });
367
426
  },
368
427
 
369
428
  async updateCustom(
370
429
  remoteId: string,
371
430
  custom: Record<string, unknown> | undefined,
372
431
  ): Promise<void> {
373
- const threads = await loadThreadMetadata();
374
- const thread = threads.find((t) => t.remoteId === remoteId);
375
- if (thread) {
432
+ await updateThreadMetadata(remoteId, (thread) => {
376
433
  thread.custom = custom;
377
- await saveThreadMetadata(threads);
378
- }
434
+ });
379
435
  },
380
436
 
381
437
  async archive(remoteId: string): Promise<void> {
382
- const threads = await loadThreadMetadata();
383
- const thread = threads.find((t) => t.remoteId === remoteId);
384
- if (thread) {
438
+ await updateThreadMetadata(remoteId, (thread) => {
385
439
  thread.status = "archived";
386
- await saveThreadMetadata(threads);
387
- }
440
+ });
388
441
  },
389
442
 
390
443
  async unarchive(remoteId: string): Promise<void> {
391
- const threads = await loadThreadMetadata();
392
- const thread = threads.find((t) => t.remoteId === remoteId);
393
- if (thread) {
444
+ await updateThreadMetadata(remoteId, (thread) => {
394
445
  thread.status = "regular";
395
- await saveThreadMetadata(threads);
396
- }
446
+ });
397
447
  },
398
448
 
399
449
  async delete(remoteId: string): Promise<void> {
400
- const threads = await loadThreadMetadata();
401
- const filtered = threads.filter((t) => t.remoteId !== remoteId);
402
- await saveThreadMetadata(filtered);
403
- await storage.removeItem(messagesKey(remoteId));
450
+ await mutationQueue.run(threadsKey, async () => {
451
+ const threads = await loadThreadMetadata();
452
+ const filtered = threads.filter((t) => t.remoteId !== remoteId);
453
+ await saveThreadMetadata(filtered);
454
+ });
455
+ const key = messagesKey(remoteId);
456
+ await mutationQueue.run(key, () => storage.removeItem(key));
404
457
  },
405
458
 
406
459
  async fetch(threadId: string): Promise<RemoteThreadMetadata> {
@@ -427,12 +480,9 @@ export const createLocalStorageAdapter = (
427
480
  const title = await titleGenerator.generateTitle(messages);
428
481
 
429
482
  // Update the stored title
430
- const threads = await loadThreadMetadata();
431
- const thread = threads.find((t) => t.remoteId === remoteId);
432
- if (thread) {
483
+ await updateThreadMetadata(remoteId, (thread) => {
433
484
  thread.title = title;
434
- await saveThreadMetadata(threads);
435
- }
485
+ });
436
486
 
437
487
  // Return a stream with a single text part
438
488
  return createAssistantStream((controller) => {
@@ -0,0 +1,79 @@
1
+ import { afterEach, describe, expect, it, vi } from "vitest";
2
+
3
+ const mocks = vi.hoisted(() => {
4
+ const setIsCopied = vi.fn();
5
+
6
+ return {
7
+ setIsCopied,
8
+ state: {
9
+ message: {
10
+ role: "assistant",
11
+ status: { type: "complete", reason: "stop" },
12
+ parts: [{ type: "text", text: "Hello" }],
13
+ isCopied: false,
14
+ },
15
+ composer: {
16
+ isEditing: false,
17
+ text: "",
18
+ },
19
+ },
20
+ aui: {
21
+ message: () => ({
22
+ getCopyText: () => "Hello",
23
+ setIsCopied,
24
+ }),
25
+ },
26
+ };
27
+ });
28
+
29
+ vi.mock("react", async (importOriginal) => ({
30
+ ...(await importOriginal<typeof import("react")>()),
31
+ useCallback: ((callback: unknown) =>
32
+ callback) as typeof import("react").useCallback,
33
+ }));
34
+
35
+ vi.mock("@assistant-ui/store", async (importOriginal) => ({
36
+ ...(await importOriginal<typeof import("@assistant-ui/store")>()),
37
+ useAui: () => mocks.aui,
38
+ useAuiState: ((selector: (state: typeof mocks.state) => unknown) =>
39
+ selector(mocks.state)) as typeof import("@assistant-ui/store").useAuiState,
40
+ }));
41
+
42
+ import { useActionBarCopy } from "./useActionBarCopy";
43
+
44
+ afterEach(() => {
45
+ vi.clearAllMocks();
46
+ });
47
+
48
+ describe("useActionBarCopy", () => {
49
+ it("does not report copy success without a clipboard handler", async () => {
50
+ const { copy, disabled } = useActionBarCopy();
51
+
52
+ copy();
53
+ await Promise.resolve();
54
+
55
+ expect(disabled).toBe(true);
56
+ expect(mocks.setIsCopied).not.toHaveBeenCalled();
57
+ });
58
+
59
+ it("reports copy success after the clipboard handler resolves", async () => {
60
+ const copyToClipboard = vi.fn();
61
+ const { copy } = useActionBarCopy({ copyToClipboard });
62
+
63
+ copy();
64
+ await Promise.resolve();
65
+
66
+ expect(copyToClipboard).toHaveBeenCalledWith("Hello");
67
+ expect(mocks.setIsCopied).toHaveBeenCalledWith(true);
68
+ });
69
+
70
+ it("does not report copy success when the clipboard handler rejects", async () => {
71
+ const copyToClipboard = vi.fn().mockRejectedValue(new Error("denied"));
72
+ const { copy } = useActionBarCopy({ copyToClipboard });
73
+
74
+ copy();
75
+ await Promise.resolve();
76
+
77
+ expect(mocks.setIsCopied).not.toHaveBeenCalled();
78
+ });
79
+ });
@@ -23,13 +23,14 @@ export const useActionBarCopy = ({
23
23
  const composerValue = useAuiState((s) => s.composer.text);
24
24
 
25
25
  const copy = useCallback(() => {
26
+ if (!copyToClipboard) return;
27
+
26
28
  const valueToCopy = isEditing ? composerValue : aui.message().getCopyText();
27
29
  if (!valueToCopy) return;
28
30
 
29
- const write = copyToClipboard ?? (() => {});
30
31
  // The rejection handler swallows clipboard write failures (permission denied,
31
32
  // API unavailable) so they don't surface as unhandled promise rejections.
32
- Promise.resolve(write(valueToCopy)).then(
33
+ Promise.resolve(copyToClipboard(valueToCopy)).then(
33
34
  () => {
34
35
  aui.message().setIsCopied(true);
35
36
  setTimeout(() => aui.message().setIsCopied(false), copiedDuration);
@@ -38,5 +39,5 @@ export const useActionBarCopy = ({
38
39
  );
39
40
  }, [aui, isEditing, composerValue, copiedDuration, copyToClipboard]);
40
41
 
41
- return { copy, disabled, isCopied };
42
+ return { copy, disabled: disabled || !copyToClipboard, isCopied };
42
43
  };
@@ -51,9 +51,7 @@ const ChainOfThoughtPrimitivePartsInner: FC<{
51
51
  Array.from({ length: partsLength }, (_, index) => (
52
52
  <ChainOfThoughtPartByIndexProvider key={index} index={index}>
53
53
  <RenderChildrenWithAccessor
54
- getItemState={(aui) =>
55
- aui.chainOfThought().part({ index }).getState()
56
- }
54
+ getItemState={(aui) => aui.part().getState()}
57
55
  >
58
56
  {(getItem) =>
59
57
  children({