@assistant-ui/core 0.2.10 → 0.2.12

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 (362) hide show
  1. package/dist/adapters/attachment.d.ts.map +1 -1
  2. package/dist/adapters/attachment.js +22 -12
  3. package/dist/adapters/attachment.js.map +1 -1
  4. package/dist/adapters/directive-formatter.js.map +1 -1
  5. package/dist/adapters/speech.js.map +1 -1
  6. package/dist/index.d.ts +3 -1
  7. package/dist/index.js +2 -1
  8. package/dist/index.js.map +1 -1
  9. package/dist/internal.js +1 -1
  10. package/dist/model-context/frame/host.js.map +1 -1
  11. package/dist/model-context/frame/provider.js.map +1 -1
  12. package/dist/model-context/registry.js.map +1 -1
  13. package/dist/model-context/types.js.map +1 -1
  14. package/dist/react/AssistantProvider.js +1 -1
  15. package/dist/react/AssistantRuntimeProvider.d.ts +1 -1
  16. package/dist/react/AssistantRuntimeProvider.d.ts.map +1 -1
  17. package/dist/react/AssistantRuntimeProvider.js +1 -1
  18. package/dist/react/RuntimeAdapter.js +4 -2
  19. package/dist/react/RuntimeAdapter.js.map +1 -1
  20. package/dist/react/adapters/LocalStorageThreadListAdapter.js +1 -1
  21. package/dist/react/adapters/LocalStorageThreadListAdapter.js.map +1 -1
  22. package/dist/react/client/DataRenderers.d.ts.map +1 -1
  23. package/dist/react/client/DataRenderers.js +6 -5
  24. package/dist/react/client/DataRenderers.js.map +1 -1
  25. package/dist/react/client/Interactables.d.ts.map +1 -1
  26. package/dist/react/client/Interactables.js +30 -29
  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/Tools.js +10 -9
  30. package/dist/react/client/Tools.js.map +1 -1
  31. package/dist/react/index.d.ts +5 -4
  32. package/dist/react/index.js +3 -2
  33. package/dist/react/model-context/define-mcp-toolkit.js.map +1 -1
  34. package/dist/react/model-context/define-toolkit.d.ts +9 -12
  35. package/dist/react/model-context/define-toolkit.d.ts.map +1 -1
  36. package/dist/react/model-context/define-toolkit.js +1 -1
  37. package/dist/react/model-context/define-toolkit.js.map +1 -1
  38. package/dist/react/model-context/external-tool.d.ts +14 -0
  39. package/dist/react/model-context/external-tool.d.ts.map +1 -0
  40. package/dist/react/model-context/external-tool.js +17 -0
  41. package/dist/react/model-context/external-tool.js.map +1 -0
  42. package/dist/react/model-context/human-tool.d.ts +27 -0
  43. package/dist/react/model-context/human-tool.d.ts.map +1 -0
  44. package/dist/react/model-context/human-tool.js +30 -0
  45. package/dist/react/model-context/human-tool.js.map +1 -0
  46. package/dist/react/model-context/toolbox.d.ts +3 -3
  47. package/dist/react/model-context/toolbox.js.map +1 -1
  48. package/dist/react/model-context/useAssistantContext.js +1 -1
  49. package/dist/react/model-context/useAssistantContext.js.map +1 -1
  50. package/dist/react/model-context/useAssistantDataUI.js +1 -1
  51. package/dist/react/model-context/useAssistantDataUI.js.map +1 -1
  52. package/dist/react/model-context/useAssistantInstructions.js +1 -1
  53. package/dist/react/model-context/useAssistantInstructions.js.map +1 -1
  54. package/dist/react/model-context/useAssistantInteractable.js +1 -1
  55. package/dist/react/model-context/useAssistantInteractable.js.map +1 -1
  56. package/dist/react/model-context/useAssistantTool.js +1 -1
  57. package/dist/react/model-context/useAssistantTool.js.map +1 -1
  58. package/dist/react/model-context/useAssistantToolUI.js +1 -1
  59. package/dist/react/model-context/useAssistantToolUI.js.map +1 -1
  60. package/dist/react/model-context/useAuiToolOverrides.js +1 -1
  61. package/dist/react/model-context/useAuiToolOverrides.js.map +1 -1
  62. package/dist/react/model-context/useInlineRender.js +1 -1
  63. package/dist/react/model-context/useInlineRender.js.map +1 -1
  64. package/dist/react/model-context/useInteractableState.js +1 -1
  65. package/dist/react/model-context/useInteractableState.js.map +1 -1
  66. package/dist/react/model-context/useToolArgsStatus.js +1 -1
  67. package/dist/react/primitive-hooks/useActionBarCopy.js +1 -1
  68. package/dist/react/primitive-hooks/useActionBarCopy.js.map +1 -1
  69. package/dist/react/primitive-hooks/useActionBarEdit.js +1 -1
  70. package/dist/react/primitive-hooks/useActionBarEdit.js.map +1 -1
  71. package/dist/react/primitive-hooks/useActionBarFeedback.js +1 -1
  72. package/dist/react/primitive-hooks/useActionBarFeedback.js.map +1 -1
  73. package/dist/react/primitive-hooks/useActionBarReload.js +1 -1
  74. package/dist/react/primitive-hooks/useActionBarReload.js.map +1 -1
  75. package/dist/react/primitive-hooks/useActionBarSpeak.js +1 -1
  76. package/dist/react/primitive-hooks/useActionBarSpeak.js.map +1 -1
  77. package/dist/react/primitive-hooks/useActionBarStopSpeaking.js +1 -1
  78. package/dist/react/primitive-hooks/useActionBarStopSpeaking.js.map +1 -1
  79. package/dist/react/primitive-hooks/useBranchPickerNext.js +1 -1
  80. package/dist/react/primitive-hooks/useBranchPickerNext.js.map +1 -1
  81. package/dist/react/primitive-hooks/useBranchPickerPrevious.js +1 -1
  82. package/dist/react/primitive-hooks/useBranchPickerPrevious.js.map +1 -1
  83. package/dist/react/primitive-hooks/useComposerAddAttachment.js +1 -1
  84. package/dist/react/primitive-hooks/useComposerAddAttachment.js.map +1 -1
  85. package/dist/react/primitive-hooks/useComposerCancel.js +1 -1
  86. package/dist/react/primitive-hooks/useComposerCancel.js.map +1 -1
  87. package/dist/react/primitive-hooks/useComposerDictate.js +1 -1
  88. package/dist/react/primitive-hooks/useComposerDictate.js.map +1 -1
  89. package/dist/react/primitive-hooks/useComposerSend.js +1 -1
  90. package/dist/react/primitive-hooks/useComposerSend.js.map +1 -1
  91. package/dist/react/primitive-hooks/useEditComposerCancel.js +1 -1
  92. package/dist/react/primitive-hooks/useEditComposerCancel.js.map +1 -1
  93. package/dist/react/primitive-hooks/useEditComposerSend.js +1 -1
  94. package/dist/react/primitive-hooks/useEditComposerSend.js.map +1 -1
  95. package/dist/react/primitive-hooks/useMessageBranching.js +1 -1
  96. package/dist/react/primitive-hooks/useMessageBranching.js.map +1 -1
  97. package/dist/react/primitive-hooks/useMessageReload.js +1 -1
  98. package/dist/react/primitive-hooks/useMessageReload.js.map +1 -1
  99. package/dist/react/primitive-hooks/useSuggestionTrigger.js +1 -1
  100. package/dist/react/primitive-hooks/useSuggestionTrigger.js.map +1 -1
  101. package/dist/react/primitive-hooks/useThreadListItemArchive.js +1 -1
  102. package/dist/react/primitive-hooks/useThreadListItemArchive.js.map +1 -1
  103. package/dist/react/primitive-hooks/useThreadListItemDelete.js +1 -1
  104. package/dist/react/primitive-hooks/useThreadListItemDelete.js.map +1 -1
  105. package/dist/react/primitive-hooks/useThreadListItemTrigger.js +1 -1
  106. package/dist/react/primitive-hooks/useThreadListItemTrigger.js.map +1 -1
  107. package/dist/react/primitive-hooks/useThreadListItemUnarchive.js +1 -1
  108. package/dist/react/primitive-hooks/useThreadListItemUnarchive.js.map +1 -1
  109. package/dist/react/primitive-hooks/useThreadListLoadMore.js +1 -1
  110. package/dist/react/primitive-hooks/useThreadListLoadMore.js.map +1 -1
  111. package/dist/react/primitive-hooks/useThreadListNew.js +1 -1
  112. package/dist/react/primitive-hooks/useThreadListNew.js.map +1 -1
  113. package/dist/react/primitive-hooks/useVoice.js +1 -1
  114. package/dist/react/primitive-hooks/useVoice.js.map +1 -1
  115. package/dist/react/primitives/chainOfThought/ChainOfThoughtParts.d.ts +1 -1
  116. package/dist/react/primitives/chainOfThought/ChainOfThoughtParts.js +1 -1
  117. package/dist/react/primitives/chainOfThought/ChainOfThoughtParts.js.map +1 -1
  118. package/dist/react/primitives/composer/ComposerAttachments.js +1 -1
  119. package/dist/react/primitives/composer/ComposerAttachments.js.map +1 -1
  120. package/dist/react/primitives/composer/ComposerQueue.js +1 -1
  121. package/dist/react/primitives/composer/ComposerQueue.js.map +1 -1
  122. package/dist/react/primitives/generativeUI/GenerativeUI.js +1 -1
  123. package/dist/react/primitives/generativeUI/GenerativeUI.js.map +1 -1
  124. package/dist/react/primitives/message/MessageAttachments.js +1 -1
  125. package/dist/react/primitives/message/MessageAttachments.js.map +1 -1
  126. package/dist/react/primitives/message/MessageGroupedParts.js +1 -1
  127. package/dist/react/primitives/message/MessageGroupedParts.js.map +1 -1
  128. package/dist/react/primitives/message/MessageParts.d.ts +1 -1
  129. package/dist/react/primitives/message/MessageParts.js +1 -1
  130. package/dist/react/primitives/message/MessageParts.js.map +1 -1
  131. package/dist/react/primitives/message/MessageQuote.js +1 -1
  132. package/dist/react/primitives/part/PartMessages.d.ts +2 -2
  133. package/dist/react/primitives/part/PartMessages.js +3 -3
  134. package/dist/react/primitives/part/PartMessages.js.map +1 -1
  135. package/dist/react/primitives/thread/ThreadMessages.js +1 -1
  136. package/dist/react/primitives/thread/ThreadMessages.js.map +1 -1
  137. package/dist/react/primitives/thread/ThreadSuggestions.js +1 -1
  138. package/dist/react/primitives/thread/ThreadSuggestions.js.map +1 -1
  139. package/dist/react/primitives/threadList/ThreadListItems.js +1 -1
  140. package/dist/react/primitives/threadList/ThreadListItems.js.map +1 -1
  141. package/dist/react/providers/AttachmentByIndexProvider.js.map +1 -1
  142. package/dist/react/providers/ChainOfThoughtByIndicesProvider.js.map +1 -1
  143. package/dist/react/providers/ChainOfThoughtPartByIndexProvider.js.map +1 -1
  144. package/dist/react/providers/MessageByIndexProvider.js.map +1 -1
  145. package/dist/react/providers/PartByIndexProvider.js.map +1 -1
  146. package/dist/react/providers/QueueItemByIndexProvider.js.map +1 -1
  147. package/dist/react/providers/ReadonlyThreadProvider.js +1 -1
  148. package/dist/react/providers/ReadonlyThreadProvider.js.map +1 -1
  149. package/dist/react/providers/SuggestionByIndexProvider.js.map +1 -1
  150. package/dist/react/providers/TextMessagePartProvider.d.ts.map +1 -1
  151. package/dist/react/providers/TextMessagePartProvider.js +4 -3
  152. package/dist/react/providers/TextMessagePartProvider.js.map +1 -1
  153. package/dist/react/providers/ThreadListItemByIndexProvider.js.map +1 -1
  154. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.d.ts +7 -0
  155. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.d.ts.map +1 -1
  156. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js +1 -1
  157. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js.map +1 -1
  158. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts +7 -0
  159. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts.map +1 -1
  160. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js +1 -1
  161. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js.map +1 -1
  162. package/dist/react/runtimes/RuntimeAdapterProvider.js +1 -1
  163. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js +1 -1
  164. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js.map +1 -1
  165. package/dist/react/runtimes/cloud/useCloudThreadListAdapter.js +1 -1
  166. package/dist/react/runtimes/cloud/useCloudThreadListAdapter.js.map +1 -1
  167. package/dist/react/runtimes/createMessageConverter.d.ts +2 -2
  168. package/dist/react/runtimes/createMessageConverter.d.ts.map +1 -1
  169. package/dist/react/runtimes/createMessageConverter.js.map +1 -1
  170. package/dist/react/runtimes/external-message-converter.d.ts +4 -3
  171. package/dist/react/runtimes/external-message-converter.d.ts.map +1 -1
  172. package/dist/react/runtimes/external-message-converter.js +1 -1
  173. package/dist/react/runtimes/external-message-converter.js.map +1 -1
  174. package/dist/react/runtimes/useExternalStoreRuntime.js +1 -1
  175. package/dist/react/runtimes/useExternalStoreSharedOptions.js +1 -1
  176. package/dist/react/runtimes/useLocalRuntime.d.ts +2 -1
  177. package/dist/react/runtimes/useLocalRuntime.d.ts.map +1 -1
  178. package/dist/react/runtimes/useLocalRuntime.js +4 -3
  179. package/dist/react/runtimes/useLocalRuntime.js.map +1 -1
  180. package/dist/react/runtimes/useRemoteThreadListRuntime.js +1 -1
  181. package/dist/react/runtimes/useRemoteThreadListRuntime.js.map +1 -1
  182. package/dist/react/utils/groupParts.d.ts +1 -1
  183. package/dist/react/utils/groupParts.js.map +1 -1
  184. package/dist/runtime/api/attachment-runtime.js.map +1 -1
  185. package/dist/runtime/api/composer-runtime.d.ts +8 -2
  186. package/dist/runtime/api/composer-runtime.d.ts.map +1 -1
  187. package/dist/runtime/api/composer-runtime.js +14 -0
  188. package/dist/runtime/api/composer-runtime.js.map +1 -1
  189. package/dist/runtime/api/message-part-runtime.js.map +1 -1
  190. package/dist/runtime/api/message-runtime.js +1 -1
  191. package/dist/runtime/api/message-runtime.js.map +1 -1
  192. package/dist/runtime/api/thread-list-item-runtime.js.map +1 -1
  193. package/dist/runtime/api/thread-runtime.d.ts +7 -0
  194. package/dist/runtime/api/thread-runtime.d.ts.map +1 -1
  195. package/dist/runtime/api/thread-runtime.js.map +1 -1
  196. package/dist/runtime/base/base-composer-runtime-core.d.ts +4 -0
  197. package/dist/runtime/base/base-composer-runtime-core.d.ts.map +1 -1
  198. package/dist/runtime/base/base-composer-runtime-core.js +7 -1
  199. package/dist/runtime/base/base-composer-runtime-core.js.map +1 -1
  200. package/dist/runtime/base/base-thread-runtime-core.js.map +1 -1
  201. package/dist/runtime/base/default-thread-composer-runtime-core.d.ts +4 -0
  202. package/dist/runtime/base/default-thread-composer-runtime-core.d.ts.map +1 -1
  203. package/dist/runtime/base/default-thread-composer-runtime-core.js +17 -1
  204. package/dist/runtime/base/default-thread-composer-runtime-core.js.map +1 -1
  205. package/dist/runtime/interfaces/composer-runtime-core.d.ts +6 -1
  206. package/dist/runtime/interfaces/composer-runtime-core.d.ts.map +1 -1
  207. package/dist/runtime/interfaces/thread-runtime-core.d.ts +4 -0
  208. package/dist/runtime/interfaces/thread-runtime-core.d.ts.map +1 -1
  209. package/dist/runtime/queue/external-thread-queue-adapter.d.ts +20 -0
  210. package/dist/runtime/queue/external-thread-queue-adapter.d.ts.map +1 -0
  211. package/dist/runtime/queue/external-thread-queue-adapter.js +0 -0
  212. package/dist/runtime/queue/message-queue.d.ts +20 -0
  213. package/dist/runtime/queue/message-queue.d.ts.map +1 -0
  214. package/dist/runtime/queue/message-queue.js +97 -0
  215. package/dist/runtime/queue/message-queue.js.map +1 -0
  216. package/dist/runtime/utils/message-repository.js.map +1 -1
  217. package/dist/runtime/utils/thread-message-like.js.map +1 -1
  218. package/dist/runtimes/external-store/external-store-adapter.d.ts +3 -1
  219. package/dist/runtimes/external-store/external-store-adapter.d.ts.map +1 -1
  220. package/dist/runtimes/external-store/external-store-runtime-core.js.map +1 -1
  221. package/dist/runtimes/external-store/external-store-thread-runtime-core.d.ts +4 -0
  222. package/dist/runtimes/external-store/external-store-thread-runtime-core.d.ts.map +1 -1
  223. package/dist/runtimes/external-store/external-store-thread-runtime-core.js +20 -2
  224. package/dist/runtimes/external-store/external-store-thread-runtime-core.js.map +1 -1
  225. package/dist/runtimes/local/local-runtime-core.js.map +1 -1
  226. package/dist/runtimes/local/local-runtime-options.d.ts +5 -0
  227. package/dist/runtimes/local/local-runtime-options.d.ts.map +1 -1
  228. package/dist/runtimes/local/local-thread-runtime-core.d.ts +7 -0
  229. package/dist/runtimes/local/local-thread-runtime-core.d.ts.map +1 -1
  230. package/dist/runtimes/local/local-thread-runtime-core.js +45 -0
  231. package/dist/runtimes/local/local-thread-runtime-core.js.map +1 -1
  232. package/dist/runtimes/readonly/ReadonlyThreadRuntimeCore.d.ts +3 -0
  233. package/dist/runtimes/readonly/ReadonlyThreadRuntimeCore.d.ts.map +1 -1
  234. package/dist/runtimes/readonly/ReadonlyThreadRuntimeCore.js +3 -0
  235. package/dist/runtimes/readonly/ReadonlyThreadRuntimeCore.js.map +1 -1
  236. package/dist/runtimes/remote-thread-list/empty-thread-core.d.ts.map +1 -1
  237. package/dist/runtimes/remote-thread-list/empty-thread-core.js +3 -0
  238. package/dist/runtimes/remote-thread-list/empty-thread-core.js.map +1 -1
  239. package/dist/runtimes/remote-thread-list/remote-thread-state.js.map +1 -1
  240. package/dist/runtimes/tool-invocations/ToolInvocationTracker.js.map +1 -1
  241. package/dist/store/clients/chain-of-thought-client.d.ts.map +1 -1
  242. package/dist/store/clients/chain-of-thought-client.js +6 -5
  243. package/dist/store/clients/chain-of-thought-client.js.map +1 -1
  244. package/dist/store/clients/model-context-client.d.ts.map +1 -1
  245. package/dist/store/clients/model-context-client.js +6 -5
  246. package/dist/store/clients/model-context-client.js.map +1 -1
  247. package/dist/store/clients/no-op-composer-client.d.ts.map +1 -1
  248. package/dist/store/clients/no-op-composer-client.js +4 -3
  249. package/dist/store/clients/no-op-composer-client.js.map +1 -1
  250. package/dist/store/clients/runtime-adapter.d.ts.map +1 -1
  251. package/dist/store/clients/runtime-adapter.js +7 -6
  252. package/dist/store/clients/runtime-adapter.js.map +1 -1
  253. package/dist/store/clients/suggestions.d.ts.map +1 -1
  254. package/dist/store/clients/suggestions.js +7 -6
  255. package/dist/store/clients/suggestions.js.map +1 -1
  256. package/dist/store/clients/thread-message-client.d.ts.map +1 -1
  257. package/dist/store/clients/thread-message-client.js +13 -12
  258. package/dist/store/clients/thread-message-client.js.map +1 -1
  259. package/dist/store/index.d.ts +1 -1
  260. package/dist/store/runtime-clients/attachment-runtime-client.d.ts.map +1 -1
  261. package/dist/store/runtime-clients/attachment-runtime-client.js +3 -3
  262. package/dist/store/runtime-clients/attachment-runtime-client.js.map +1 -1
  263. package/dist/store/runtime-clients/composer-runtime-client.d.ts +12 -5
  264. package/dist/store/runtime-clients/composer-runtime-client.d.ts.map +1 -1
  265. package/dist/store/runtime-clients/composer-runtime-client.js +32 -16
  266. package/dist/store/runtime-clients/composer-runtime-client.js.map +1 -1
  267. package/dist/store/runtime-clients/message-part-runtime-client.d.ts.map +1 -1
  268. package/dist/store/runtime-clients/message-part-runtime-client.js +3 -3
  269. package/dist/store/runtime-clients/message-part-runtime-client.js.map +1 -1
  270. package/dist/store/runtime-clients/message-runtime-client.d.ts +6 -3
  271. package/dist/store/runtime-clients/message-runtime-client.d.ts.map +1 -1
  272. package/dist/store/runtime-clients/message-runtime-client.js +17 -16
  273. package/dist/store/runtime-clients/message-runtime-client.js.map +1 -1
  274. package/dist/store/runtime-clients/thread-list-item-runtime-client.d.ts.map +1 -1
  275. package/dist/store/runtime-clients/thread-list-item-runtime-client.js +8 -7
  276. package/dist/store/runtime-clients/thread-list-item-runtime-client.js.map +1 -1
  277. package/dist/store/runtime-clients/thread-list-runtime-client.d.ts.map +1 -1
  278. package/dist/store/runtime-clients/thread-list-runtime-client.js +11 -10
  279. package/dist/store/runtime-clients/thread-list-runtime-client.js.map +1 -1
  280. package/dist/store/runtime-clients/thread-runtime-client.d.ts.map +1 -1
  281. package/dist/store/runtime-clients/thread-runtime-client.js +15 -14
  282. package/dist/store/runtime-clients/thread-runtime-client.js.map +1 -1
  283. package/dist/store/runtime-clients/useSubscribable.d.ts +7 -0
  284. package/dist/store/runtime-clients/useSubscribable.d.ts.map +1 -0
  285. package/dist/store/runtime-clients/useSubscribable.js +16 -0
  286. package/dist/store/runtime-clients/useSubscribable.js.map +1 -0
  287. package/dist/store/scope-registration.d.ts +1 -1
  288. package/dist/store/scopes/composer.d.ts +1 -1
  289. package/dist/store/scopes/queue-item.d.ts +2 -1
  290. package/dist/store/scopes/queue-item.d.ts.map +1 -1
  291. package/dist/store/scopes/queue-item.js +6 -0
  292. package/dist/store/scopes/queue-item.js.map +1 -0
  293. package/dist/store/scopes/thread.d.ts +1 -1
  294. package/dist/store/scopes/threads.d.ts +1 -1
  295. package/dist/types/message.d.ts +2 -1
  296. package/dist/types/message.d.ts.map +1 -1
  297. package/dist/types/message.js.map +1 -1
  298. package/dist/utils/composite-context-provider.d.ts +1 -1
  299. package/dist/utils/composite-context-provider.js +3 -1
  300. package/dist/utils/composite-context-provider.js.map +1 -1
  301. package/dist/utils/json/is-json.js.map +1 -1
  302. package/dist/utils/text.js.map +1 -1
  303. package/package.json +14 -10
  304. package/src/adapters/attachment.ts +24 -4
  305. package/src/index.ts +8 -0
  306. package/src/react/RuntimeAdapter.ts +6 -4
  307. package/src/react/client/DataRenderers.ts +48 -42
  308. package/src/react/client/Interactables.ts +263 -267
  309. package/src/react/client/Tools.ts +122 -129
  310. package/src/react/index.ts +3 -1
  311. package/src/react/model-context/define-toolkit.test.ts +14 -7
  312. package/src/react/model-context/define-toolkit.ts +18 -19
  313. package/src/react/model-context/external-tool.ts +16 -0
  314. package/src/react/model-context/{hitl.ts → human-tool.ts} +13 -8
  315. package/src/react/model-context/toolbox.ts +3 -3
  316. package/src/react/primitives/message/MessageGroupedParts.tsx +1 -1
  317. package/src/react/primitives/part/PartMessages.tsx +2 -2
  318. package/src/react/providers/TextMessagePartProvider.tsx +31 -32
  319. package/src/react/runtimes/createMessageConverter.ts +2 -1
  320. package/src/react/runtimes/external-message-converter.ts +5 -3
  321. package/src/react/runtimes/useLocalRuntime.ts +2 -0
  322. package/src/runtime/api/composer-runtime.ts +26 -0
  323. package/src/runtime/base/base-composer-runtime-core.ts +11 -0
  324. package/src/runtime/base/default-thread-composer-runtime-core.ts +22 -0
  325. package/src/runtime/interfaces/composer-runtime-core.ts +7 -0
  326. package/src/runtime/interfaces/thread-runtime-core.ts +5 -0
  327. package/src/runtime/queue/external-thread-queue-adapter.ts +14 -0
  328. package/src/runtime/queue/message-queue.ts +128 -0
  329. package/src/runtimes/external-store/external-store-adapter.ts +3 -0
  330. package/src/runtimes/external-store/external-store-thread-runtime-core.ts +31 -2
  331. package/src/runtimes/local/local-runtime-options.ts +6 -0
  332. package/src/runtimes/local/local-thread-runtime-core.ts +70 -0
  333. package/src/runtimes/readonly/ReadonlyThreadRuntimeCore.ts +4 -0
  334. package/src/runtimes/remote-thread-list/empty-thread-core.ts +4 -0
  335. package/src/store/clients/chain-of-thought-client.ts +24 -25
  336. package/src/store/clients/model-context-client.ts +22 -19
  337. package/src/store/clients/no-op-composer-client.ts +69 -66
  338. package/src/store/clients/runtime-adapter.ts +9 -6
  339. package/src/store/clients/suggestions.ts +41 -40
  340. package/src/store/clients/thread-message-client.ts +127 -132
  341. package/src/store/runtime-clients/attachment-runtime-client.ts +7 -3
  342. package/src/store/runtime-clients/composer-runtime-client.ts +142 -113
  343. package/src/store/runtime-clients/message-part-runtime-client.ts +16 -14
  344. package/src/store/runtime-clients/message-runtime-client.ts +124 -122
  345. package/src/store/runtime-clients/thread-list-item-runtime-client.ts +44 -45
  346. package/src/store/runtime-clients/thread-list-runtime-client.ts +83 -80
  347. package/src/store/runtime-clients/thread-runtime-client.ts +113 -119
  348. package/src/store/runtime-clients/{tap-subscribable.ts → useSubscribable.ts} +4 -4
  349. package/src/store/scopes/queue-item.ts +2 -0
  350. package/src/tests/attachment-adapters.test.ts +86 -0
  351. package/src/tests/external-store-thread-runtime-core.test.ts +125 -0
  352. package/src/tests/message-queue.test.ts +168 -0
  353. package/src/types/message.ts +2 -0
  354. package/src/utils/composite-context-provider.ts +3 -1
  355. package/dist/react/model-context/hitl.d.ts +0 -23
  356. package/dist/react/model-context/hitl.d.ts.map +0 -1
  357. package/dist/react/model-context/hitl.js +0 -26
  358. package/dist/react/model-context/hitl.js.map +0 -1
  359. package/dist/store/runtime-clients/tap-subscribable.d.ts +0 -7
  360. package/dist/store/runtime-clients/tap-subscribable.d.ts.map +0 -1
  361. package/dist/store/runtime-clients/tap-subscribable.js +0 -16
  362. package/dist/store/runtime-clients/tap-subscribable.js.map +0 -1
@@ -3,6 +3,7 @@ import type { MessageRole } from "../../types/message";
3
3
  import type { QuoteInfo } from "../../types/quote";
4
4
  import type { Unsubscribe } from "../../types/unsubscribe";
5
5
  import type { RunConfig } from "../../types/message";
6
+ import type { QueueItemState } from "../../store/scopes/queue-item";
6
7
  import {
7
8
  LazyMemoizeSubject,
8
9
  EventSubscriptionSubject,
@@ -63,6 +64,9 @@ type BaseComposerState = {
63
64
  * Undefined when no quote is set.
64
65
  */
65
66
  readonly quote: QuoteInfo | undefined;
67
+
68
+ /** Messages waiting to be processed. Empty unless the `queue` capability is set. */
69
+ readonly queue: readonly QueueItemState[];
66
70
  };
67
71
 
68
72
  export type ThreadComposerState = BaseComposerState & {
@@ -97,6 +101,7 @@ const getThreadComposerState = (
97
101
  attachmentAccept: runtime?.attachmentAccept ?? "",
98
102
  dictation: runtime?.dictation,
99
103
  quote: runtime?.quote,
104
+ queue: runtime?.queue ?? EMPTY_ARRAY,
100
105
 
101
106
  value: runtime?.text ?? "",
102
107
  });
@@ -120,6 +125,7 @@ const getEditComposerState = (
120
125
  attachmentAccept: runtime?.attachmentAccept ?? "",
121
126
  dictation: runtime?.dictation,
122
127
  quote: runtime?.quote,
128
+ queue: runtime?.queue ?? EMPTY_ARRAY,
123
129
 
124
130
  parentId: runtime?.parentId ?? null,
125
131
  sourceId: runtime?.sourceId ?? null,
@@ -194,6 +200,12 @@ export type ComposerRuntime = {
194
200
  */
195
201
  cancel(): void;
196
202
 
203
+ /** Promote a queued message so it processes next. */
204
+ steerQueueItem(queueItemId: string): void;
205
+
206
+ /** Remove a queued message. */
207
+ removeQueueItem(queueItemId: string): void;
208
+
197
209
  /**
198
210
  * Listens for changes to the composer state.
199
211
  * @param callback The callback to call when the composer state changes.
@@ -251,6 +263,8 @@ export abstract class ComposerRuntimeImpl implements ComposerRuntime {
251
263
  this.clearAttachments = this.clearAttachments.bind(this);
252
264
  this.send = this.send.bind(this);
253
265
  this.cancel = this.cancel.bind(this);
266
+ this.steerQueueItem = this.steerQueueItem.bind(this);
267
+ this.removeQueueItem = this.removeQueueItem.bind(this);
254
268
  this.setRole = this.setRole.bind(this);
255
269
  this.getAttachmentByIndex = this.getAttachmentByIndex.bind(this);
256
270
  this.startDictation = this.startDictation.bind(this);
@@ -303,6 +317,18 @@ export abstract class ComposerRuntimeImpl implements ComposerRuntime {
303
317
  core.cancel();
304
318
  }
305
319
 
320
+ public steerQueueItem(queueItemId: string) {
321
+ const core = this._core.getState();
322
+ if (!core) throw new Error("Composer is not available");
323
+ core.steerQueueItem(queueItemId);
324
+ }
325
+
326
+ public removeQueueItem(queueItemId: string) {
327
+ const core = this._core.getState();
328
+ if (!core) throw new Error("Composer is not available");
329
+ core.removeQueueItem(queueItemId);
330
+ }
331
+
306
332
  public setRole(role: MessageRole) {
307
333
  const core = this._core.getState();
308
334
  if (!core) throw new Error("Composer is not available");
@@ -23,6 +23,10 @@ import type {
23
23
  SendOptions,
24
24
  } from "../interfaces/composer-runtime-core";
25
25
  import type { DictationAdapter } from "../../adapters/speech";
26
+ import {
27
+ EMPTY_QUEUE_ITEMS,
28
+ type QueueItemState,
29
+ } from "../../store/scopes/queue-item";
26
30
  import { generateId } from "../../utils/id";
27
31
 
28
32
  const isAttachmentComplete = (a: Attachment): a is CompleteAttachment =>
@@ -200,6 +204,13 @@ export abstract class BaseComposerRuntimeCore
200
204
  this.handleCancel();
201
205
  }
202
206
 
207
+ public get queue(): readonly QueueItemState[] {
208
+ return EMPTY_QUEUE_ITEMS;
209
+ }
210
+
211
+ public steerQueueItem(_queueItemId: string): void {}
212
+ public removeQueueItem(_queueItemId: string): void {}
213
+
203
214
  protected abstract handleSend(
204
215
  message: Omit<AppendMessage, "parentId" | "sourceId">,
205
216
  options?: SendOptions,
@@ -6,6 +6,10 @@ import type {
6
6
  ThreadComposerRuntimeCore,
7
7
  } from "../interfaces/composer-runtime-core";
8
8
  import type { ThreadRuntimeCore } from "../interfaces/thread-runtime-core";
9
+ import {
10
+ EMPTY_QUEUE_ITEMS,
11
+ type QueueItemState,
12
+ } from "../../store/scopes/queue-item";
9
13
  import { BaseComposerRuntimeCore } from "./base-composer-runtime-core";
10
14
 
11
15
  export class DefaultThreadComposerRuntimeCore
@@ -21,6 +25,18 @@ export class DefaultThreadComposerRuntimeCore
21
25
  return !this.isEmpty && !this.runtime.isSendDisabled;
22
26
  }
23
27
 
28
+ public override get queue(): readonly QueueItemState[] {
29
+ return this.runtime.getQueueItems?.() ?? EMPTY_QUEUE_ITEMS;
30
+ }
31
+
32
+ public override steerQueueItem(queueItemId: string): void {
33
+ this.runtime.steerQueueItem?.(queueItemId);
34
+ }
35
+
36
+ public override removeQueueItem(queueItemId: string): void {
37
+ this.runtime.removeQueueItem?.(queueItemId);
38
+ }
39
+
24
40
  protected getAttachmentAdapter() {
25
41
  return this.runtime.adapters?.attachments;
26
42
  }
@@ -45,6 +61,7 @@ export class DefaultThreadComposerRuntimeCore
45
61
 
46
62
  public connect() {
47
63
  let lastIsSendDisabled = this.runtime.isSendDisabled;
64
+ let lastQueue = this.queue;
48
65
  return this.runtime.subscribe(() => {
49
66
  let changed = false;
50
67
  if (this.canCancel !== this.runtime.capabilities.cancel) {
@@ -55,6 +72,10 @@ export class DefaultThreadComposerRuntimeCore
55
72
  lastIsSendDisabled = this.runtime.isSendDisabled;
56
73
  changed = true;
57
74
  }
75
+ if (lastQueue !== this.queue) {
76
+ lastQueue = this.queue;
77
+ changed = true;
78
+ }
58
79
  if (changed) this._notifySubscribers();
59
80
  });
60
81
  }
@@ -68,6 +89,7 @@ export class DefaultThreadComposerRuntimeCore
68
89
  parentId: this.runtime.messages.at(-1)?.id ?? null,
69
90
  sourceId: null,
70
91
  startRun: options?.startRun,
92
+ steer: options?.steer,
71
93
  });
72
94
  }
73
95
 
@@ -4,6 +4,7 @@ import type { Attachment, CreateAttachment } from "../../types/attachment";
4
4
  import type { Unsubscribe } from "../../types/unsubscribe";
5
5
  import type { RunConfig } from "../../types/message";
6
6
  import type { DictationAdapter } from "../../adapters/speech";
7
+ import type { QueueItemState } from "../../store/scopes/queue-item";
7
8
 
8
9
  export type AttachmentAddErrorReason =
9
10
  | "no-adapter"
@@ -45,6 +46,8 @@ export type DictationState = {
45
46
 
46
47
  export type SendOptions = {
47
48
  startRun?: boolean;
49
+ /** Process this message next; only meaningful with the `queue` capability. */
50
+ steer?: boolean;
48
51
  };
49
52
 
50
53
  export type ComposerRuntimeCore = Readonly<{
@@ -78,6 +81,10 @@ export type ComposerRuntimeCore = Readonly<{
78
81
  send: (options?: SendOptions) => void;
79
82
  cancel: () => void;
80
83
 
84
+ queue: readonly QueueItemState[];
85
+ steerQueueItem: (queueItemId: string) => void;
86
+ removeQueueItem: (queueItemId: string) => void;
87
+
81
88
  dictation: DictationState | undefined;
82
89
  startDictation: () => void;
83
90
  stopDictation: () => void;
@@ -16,6 +16,7 @@ import type {
16
16
  EditComposerRuntimeCore,
17
17
  ThreadComposerRuntimeCore,
18
18
  } from "./composer-runtime-core";
19
+ import type { QueueItemState } from "../../store/scopes/queue-item";
19
20
 
20
21
  export type RuntimeCapabilities = {
21
22
  readonly switchToBranch: boolean;
@@ -169,6 +170,10 @@ export type ThreadRuntimeCore = Readonly<{
169
170
  getEditComposer: (messageId: string) => EditComposerRuntimeCore | undefined;
170
171
  beginEdit: (messageId: string) => void;
171
172
 
173
+ getQueueItems?: () => readonly QueueItemState[];
174
+ steerQueueItem?: (queueItemId: string) => void;
175
+ removeQueueItem?: (queueItemId: string) => void;
176
+
172
177
  speech: SpeechState | undefined;
173
178
  voice: VoiceSessionState | undefined;
174
179
 
@@ -0,0 +1,14 @@
1
+ import type { AppendMessage } from "../../types/message";
2
+ import type { QueueItemState } from "../../store/scopes/queue-item";
3
+
4
+ /**
5
+ * The queue surface a runtime exposes so the composer can stay usable during a
6
+ * run and render the pending messages.
7
+ */
8
+ export type ExternalThreadQueueAdapter = {
9
+ items: readonly QueueItemState[];
10
+ enqueue: (message: AppendMessage, options: { steer: boolean }) => void;
11
+ steer: (queueItemId: string) => void;
12
+ remove: (queueItemId: string) => void;
13
+ clear: (reason: "edit" | "reload" | "cancel-run") => void;
14
+ };
@@ -0,0 +1,128 @@
1
+ import type { AppendMessage } from "../../types/message";
2
+ import {
3
+ EMPTY_QUEUE_ITEMS,
4
+ type QueueItemState,
5
+ } from "../../store/scopes/queue-item";
6
+ import { generateId } from "../../utils/id";
7
+ import { getThreadMessageText } from "../../utils/text";
8
+ import type { ExternalThreadQueueAdapter } from "./external-thread-queue-adapter";
9
+
10
+ export type MessageQueueDriver = {
11
+ run: (message: AppendMessage, options: { steer: boolean }) => void;
12
+ /** When omitted, `steer` degrades to "process next" instead of interrupting. */
13
+ cancel?: (() => void) | undefined;
14
+ };
15
+
16
+ export type MessageQueueController = {
17
+ readonly adapter: ExternalThreadQueueAdapter;
18
+ /** Mark a run as in flight so concurrent sends buffer; call on the rising edge. */
19
+ notifyBusy: () => void;
20
+ /** Advances to the next pending message; call on the run's falling edge. */
21
+ notifyIdle: () => void;
22
+ subscribe: (callback: () => void) => () => void;
23
+ };
24
+
25
+ export const createMessageQueue = (
26
+ driver: MessageQueueDriver,
27
+ ): MessageQueueController => {
28
+ let items: readonly QueueItemState[] = EMPTY_QUEUE_ITEMS;
29
+ const messages = new Map<string, AppendMessage>();
30
+ const subscribers = new Set<() => void>();
31
+
32
+ let running = false;
33
+ // swallow the cancelled run's settle when steering so it does not double-advance
34
+ let suppressIdle = 0;
35
+
36
+ const notify = () => {
37
+ for (const callback of subscribers) callback();
38
+ };
39
+
40
+ const setItems = (next: readonly QueueItemState[]) => {
41
+ items = next;
42
+ adapter.items = next;
43
+ notify();
44
+ };
45
+
46
+ const advance = () => {
47
+ if (running || items.length === 0) return;
48
+ const head = items[0]!;
49
+ const message = messages.get(head.id);
50
+ messages.delete(head.id);
51
+ setItems(items.slice(1));
52
+ if (!message) return;
53
+ running = true;
54
+ driver.run(message, { steer: false });
55
+ };
56
+
57
+ const enqueue = (message: AppendMessage, { steer }: { steer: boolean }) => {
58
+ const id = generateId();
59
+ const prompt = getThreadMessageText(message);
60
+ messages.set(id, message);
61
+ setItems([...items, { id, prompt }]);
62
+ if (steer) {
63
+ steerItem(id);
64
+ } else {
65
+ advance();
66
+ }
67
+ };
68
+
69
+ const steerItem = (queueItemId: string) => {
70
+ if (!messages.has(queueItemId)) return;
71
+
72
+ if (driver.cancel && running) {
73
+ const message = messages.get(queueItemId)!;
74
+ messages.delete(queueItemId);
75
+ setItems(items.filter((item) => item.id !== queueItemId));
76
+ suppressIdle++;
77
+ driver.cancel();
78
+ running = true;
79
+ driver.run(message, { steer: true });
80
+ return;
81
+ }
82
+
83
+ const target = items.find((item) => item.id === queueItemId);
84
+ if (!target) return;
85
+ setItems([target, ...items.filter((item) => item.id !== queueItemId)]);
86
+ advance();
87
+ };
88
+
89
+ const remove = (queueItemId: string) => {
90
+ if (!messages.delete(queueItemId)) return;
91
+ setItems(items.filter((item) => item.id !== queueItemId));
92
+ };
93
+
94
+ const clear = () => {
95
+ if (items.length === 0) return;
96
+ messages.clear();
97
+ setItems(EMPTY_QUEUE_ITEMS);
98
+ };
99
+
100
+ const adapter: ExternalThreadQueueAdapter = {
101
+ items,
102
+ enqueue,
103
+ steer: steerItem,
104
+ remove,
105
+ clear,
106
+ };
107
+
108
+ return {
109
+ adapter,
110
+ notifyBusy: () => {
111
+ running = true;
112
+ },
113
+ notifyIdle: () => {
114
+ if (suppressIdle > 0) {
115
+ suppressIdle--;
116
+ return;
117
+ }
118
+ running = false;
119
+ advance();
120
+ },
121
+ subscribe: (callback) => {
122
+ subscribers.add(callback);
123
+ return () => {
124
+ subscribers.delete(callback);
125
+ };
126
+ },
127
+ };
128
+ };
@@ -17,6 +17,7 @@ import type {
17
17
  import type { ExportedMessageRepository } from "../../runtime/utils/message-repository";
18
18
  import type { ReadonlyJSONValue } from "assistant-stream/utils";
19
19
  import type { ToolExecutionStatus } from "../tool-invocations/ToolInvocationTracker";
20
+ import type { ExternalThreadQueueAdapter } from "../../runtime/queue/external-thread-queue-adapter";
20
21
 
21
22
  export type ExternalStoreThreadData<TState extends "regular" | "archived"> = {
22
23
  status: TState;
@@ -105,6 +106,8 @@ type ExternalStoreAdapterBase<T> = {
105
106
  onExportExternalState?: (() => any) | undefined;
106
107
  onLoadExternalState?: ((state: any) => void) | undefined;
107
108
  onNew: (message: AppendMessage) => Promise<void>;
109
+ /** Opt in to message queuing. Typically produced by `createMessageQueue`. */
110
+ queue?: ExternalThreadQueueAdapter | undefined;
108
111
  onEdit?: ((message: AppendMessage) => Promise<void>) | undefined;
109
112
  onReload?: // TODO: remove parentId in 0.12.0
110
113
  | ((parentId: string | null, config: StartRunConfig) => Promise<void>)
@@ -32,6 +32,7 @@ import {
32
32
  } from "../../runtime/utils/message-repository";
33
33
  import { generateId } from "../../utils/id";
34
34
  import { ToolInvocationTracker } from "../tool-invocations/ToolInvocationTracker";
35
+ import { EMPTY_QUEUE_ITEMS } from "../../store/scopes/queue-item";
35
36
 
36
37
  const EMPTY_ARRAY: readonly ThreadSuggestion[] = Object.freeze([]);
37
38
 
@@ -155,7 +156,7 @@ export class ExternalStoreThreadRuntimeCore
155
156
  unstable_copy: this._store.unstable_capabilities?.copy !== false,
156
157
  attachments: !!this._store.adapters?.attachments,
157
158
  feedback: !!this._store.adapters?.feedback,
158
- queue: false,
159
+ queue: this._store.queue !== undefined,
159
160
  };
160
161
  if (!shallowEqual(this._capabilities, newCapabilities)) {
161
162
  this._capabilities = newCapabilities;
@@ -389,6 +390,15 @@ export class ExternalStoreThreadRuntimeCore
389
390
  }
390
391
 
391
392
  public async append(message: AppendMessage): Promise<void> {
393
+ const isEdit = message.parentId !== (this.messages.at(-1)?.id ?? null);
394
+
395
+ // Buffering does not start a run, so the tool-abort below must wait until
396
+ // the queue flushes. By then the prior run (and its tools) has settled.
397
+ if (!isEdit && this._store.queue) {
398
+ this._store.queue.enqueue(message, { steer: message.steer ?? false });
399
+ return;
400
+ }
401
+
392
402
  // Auto-abort in-flight client-side tool executions when a new run is
393
403
  // about to start. Without this, a tool that finishes after the new turn
394
404
  // begins would feed a stale result into `onAddToolResult`, racing with
@@ -399,19 +409,36 @@ export class ExternalStoreThreadRuntimeCore
399
409
  await this._toolInvocations?.abort();
400
410
  }
401
411
 
402
- if (message.parentId !== (this.messages.at(-1)?.id ?? null)) {
412
+ if (isEdit) {
403
413
  if (!this._store.onEdit)
404
414
  throw new Error("Runtime does not support editing messages.");
415
+ this._store.queue?.clear("edit");
405
416
  await this._store.onEdit(message);
406
417
  } else {
407
418
  await this._store.onNew(message);
408
419
  }
409
420
  }
410
421
 
422
+ public getQueueItems() {
423
+ // The composer reads this during base-thread construction, before the
424
+ // constructor assigns `_store`, so guard against the unset field.
425
+ return this._store?.queue?.items ?? EMPTY_QUEUE_ITEMS;
426
+ }
427
+
428
+ public steerQueueItem(queueItemId: string) {
429
+ this._store?.queue?.steer(queueItemId);
430
+ }
431
+
432
+ public removeQueueItem(queueItemId: string) {
433
+ this._store?.queue?.remove(queueItemId);
434
+ }
435
+
411
436
  public async startRun(config: StartRunConfig): Promise<void> {
412
437
  if (!this._store.onReload)
413
438
  throw new Error("Runtime does not support reloading messages.");
414
439
 
440
+ this._store.queue?.clear("reload");
441
+
415
442
  // Auto-abort in-flight client-side tool executions when a run reloads;
416
443
  // any results that land afterward would target a turn that no longer
417
444
  // exists. See `append` above for full rationale.
@@ -457,6 +484,8 @@ export class ExternalStoreThreadRuntimeCore
457
484
  if (!this._store.onCancel)
458
485
  throw new Error("Runtime does not support cancelling runs.");
459
486
 
487
+ this._store.queue?.clear("cancel-run");
488
+
460
489
  // Abort any in-flight client-side tool executions. Fire-and-forget —
461
490
  // the abort resolves once executions settle, but we don't gate the
462
491
  // cancel on it.
@@ -26,4 +26,10 @@ export type LocalRuntimeOptionsBase = {
26
26
  * Names of tools that are allowed to interrupt the run in order to wait for human/external approval.
27
27
  */
28
28
  unstable_humanToolNames?: string[] | undefined;
29
+
30
+ /**
31
+ * Opt in to message queuing: a message sent during a run is held in
32
+ * `composer.queue` and sent once the run settles. Steering runs it next.
33
+ */
34
+ unstable_enableMessageQueue?: boolean | undefined;
29
35
  };
@@ -22,6 +22,14 @@ import type {
22
22
  } from "../../types/message";
23
23
  import type { RunConfig } from "../../types/message";
24
24
  import type { ModelContextProvider } from "../../model-context/types";
25
+ import {
26
+ createMessageQueue,
27
+ type MessageQueueController,
28
+ } from "../../runtime/queue/message-queue";
29
+ import {
30
+ EMPTY_QUEUE_ITEMS,
31
+ type QueueItemState,
32
+ } from "../../store/scopes/queue-item";
25
33
 
26
34
  class AbortError extends Error {
27
35
  override name = "AbortError";
@@ -54,6 +62,9 @@ export class LocalThreadRuntimeCore
54
62
 
55
63
  private abortController: AbortController | null = null;
56
64
 
65
+ private _queue: MessageQueueController | null = null;
66
+ private _queueRunInFlight = false;
67
+
57
68
  public readonly isDisabled = false;
58
69
  public readonly isSendDisabled = false;
59
70
 
@@ -139,6 +150,31 @@ export class LocalThreadRuntimeCore
139
150
  hasUpdates = true;
140
151
  }
141
152
 
153
+ const canQueue = options.unstable_enableMessageQueue === true;
154
+ if (canQueue && !this._queue) {
155
+ this._queue = createMessageQueue({
156
+ run: (message) => {
157
+ // release the queue when the dispatch settles, even if it rejects
158
+ // before reaching startRun's finally, so a failure can't deadlock it
159
+ this._queueRunInFlight = true;
160
+ void this._runAppend(message)
161
+ .finally(() => {
162
+ this._queueRunInFlight = false;
163
+ this._queue?.notifyIdle();
164
+ })
165
+ .catch(() => {});
166
+ },
167
+ });
168
+ this._queue.subscribe(() => this._notifySubscribers());
169
+ } else if (!canQueue && this._queue) {
170
+ this._queue.adapter.clear("cancel-run");
171
+ this._queue = null;
172
+ }
173
+ if (this.capabilities.queue !== canQueue) {
174
+ this.capabilities.queue = canQueue;
175
+ hasUpdates = true;
176
+ }
177
+
142
178
  if (hasUpdates) this._notifySubscribers();
143
179
  }
144
180
 
@@ -183,6 +219,31 @@ export class LocalThreadRuntimeCore
183
219
  }
184
220
 
185
221
  public async append(message: AppendMessage): Promise<void> {
222
+ const isTail = message.parentId === (this.messages.at(-1)?.id ?? null);
223
+ const willRun = message.startRun ?? message.role === "user";
224
+ if (this._queue && willRun && isTail) {
225
+ this._queue.adapter.enqueue(message, { steer: message.steer ?? false });
226
+ return;
227
+ }
228
+ if (this._queue && !isTail) this._queue.adapter.clear("edit");
229
+ return this._runAppend(message);
230
+ }
231
+
232
+ public getQueueItems(): readonly QueueItemState[] {
233
+ // Reads can arrive during base-thread construction, before the queue field
234
+ // is assigned, so guard against the unset field.
235
+ return this._queue?.adapter.items ?? EMPTY_QUEUE_ITEMS;
236
+ }
237
+
238
+ public steerQueueItem(queueItemId: string): void {
239
+ this._queue?.adapter.steer(queueItemId);
240
+ }
241
+
242
+ public removeQueueItem(queueItemId: string): void {
243
+ this._queue?.adapter.remove(queueItemId);
244
+ }
245
+
246
+ private async _runAppend(message: AppendMessage): Promise<void> {
186
247
  this.ensureInitialized();
187
248
 
188
249
  const initPromise = this._getInitializePromise?.();
@@ -254,6 +315,8 @@ export class LocalThreadRuntimeCore
254
315
  this._notifyEventSubscribers("runStart", {});
255
316
 
256
317
  try {
318
+ // mark busy for runs not started through the queue (regenerate, resume)
319
+ this._queue?.notifyBusy();
257
320
  this._suggestions = [];
258
321
  this._suggestionsController?.abort();
259
322
  this._suggestionsController = null;
@@ -270,6 +333,11 @@ export class LocalThreadRuntimeCore
270
333
  } while (shouldContinue(message, this._options.unstable_humanToolNames));
271
334
  } finally {
272
335
  this._notifyEventSubscribers("runEnd", {});
336
+ // queue-driven runs release from the driver settle handler; a direct
337
+ // run (regenerate, resume) releases here
338
+ if (!this._queueRunInFlight) {
339
+ queueMicrotask(() => this._queue?.notifyIdle());
340
+ }
273
341
  }
274
342
 
275
343
  this._suggestionsController = new AbortController();
@@ -474,12 +542,14 @@ export class LocalThreadRuntimeCore
474
542
  }
475
543
 
476
544
  public detach() {
545
+ this._queue?.adapter.clear("cancel-run");
477
546
  const error = new AbortError(true);
478
547
  this.abortController?.abort(error);
479
548
  this.abortController = null;
480
549
  }
481
550
 
482
551
  public cancelRun() {
552
+ this._queue?.adapter.clear("cancel-run");
483
553
  const error = new AbortError(false);
484
554
  this.abortController?.abort(error);
485
555
  this.abortController = null;
@@ -158,6 +158,10 @@ export class ReadonlyThreadRuntimeCore
158
158
  // noop
159
159
  },
160
160
 
161
+ queue: [] as never[],
162
+ steerQueueItem() {},
163
+ removeQueueItem() {},
164
+
161
165
  dictation: undefined,
162
166
 
163
167
  startDictation() {
@@ -139,6 +139,10 @@ export const EMPTY_THREAD_CORE: ThreadRuntimeCore = {
139
139
  // noop
140
140
  },
141
141
 
142
+ queue: [] as never[],
143
+ steerQueueItem() {},
144
+ removeQueueItem() {},
145
+
142
146
  dictation: undefined,
143
147
 
144
148
  startDictation() {
@@ -1,4 +1,5 @@
1
- import { resource, tapMemo, tapState } from "@assistant-ui/tap";
1
+ import { useMemo, useState } from "react";
2
+ import { resource } from "@assistant-ui/tap";
2
3
  import type { ClientOutput } from "@assistant-ui/store";
3
4
  import type {
4
5
  ChainOfThoughtState,
@@ -11,30 +12,28 @@ const COMPLETE_STATUS: MessagePartStatus = Object.freeze({
11
12
  type: "complete",
12
13
  });
13
14
 
14
- export const ChainOfThoughtClient = resource(
15
- ({
16
- parts,
17
- getMessagePart,
18
- }: {
19
- parts: readonly ChainOfThoughtPart[];
20
- getMessagePart: (selector: { index: number }) => PartMethods;
21
- }): ClientOutput<"chainOfThought"> => {
22
- const [collapsed, setCollapsed] = tapState(true);
15
+ export const ChainOfThoughtClient = resource(function ChainOfThoughtClient({
16
+ parts,
17
+ getMessagePart,
18
+ }: {
19
+ parts: readonly ChainOfThoughtPart[];
20
+ getMessagePart: (selector: { index: number }) => PartMethods;
21
+ }): ClientOutput<"chainOfThought"> {
22
+ const [collapsed, setCollapsed] = useState(true);
23
23
 
24
- const status = tapMemo(() => {
25
- const lastPart = parts[parts.length - 1];
26
- return lastPart?.status ?? COMPLETE_STATUS;
27
- }, [parts]);
24
+ const status = useMemo(() => {
25
+ const lastPart = parts[parts.length - 1];
26
+ return lastPart?.status ?? COMPLETE_STATUS;
27
+ }, [parts]);
28
28
 
29
- const state = tapMemo<ChainOfThoughtState>(
30
- () => ({ parts, collapsed, status }),
31
- [parts, collapsed, status],
32
- );
29
+ const state = useMemo<ChainOfThoughtState>(
30
+ () => ({ parts, collapsed, status }),
31
+ [parts, collapsed, status],
32
+ );
33
33
 
34
- return {
35
- getState: () => state,
36
- setCollapsed,
37
- part: getMessagePart,
38
- };
39
- },
40
- );
34
+ return {
35
+ getState: () => state,
36
+ setCollapsed,
37
+ part: getMessagePart,
38
+ };
39
+ });