@assistant-ui/core 0.3.15 → 0.3.17

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 (455) hide show
  1. package/dist/index.d.ts +2 -2
  2. package/dist/index.js +2 -2
  3. package/dist/index.js.map +1 -1
  4. package/dist/internal.d.ts +8 -2
  5. package/dist/internal.js +9 -3
  6. package/dist/model-context/frame/host.d.ts.map +1 -1
  7. package/dist/model-context/frame/host.js +3 -2
  8. package/dist/model-context/frame/host.js.map +1 -1
  9. package/dist/model-context/frame/provider.d.ts +5 -0
  10. package/dist/model-context/frame/provider.d.ts.map +1 -1
  11. package/dist/model-context/frame/provider.js +141 -28
  12. package/dist/model-context/frame/provider.js.map +1 -1
  13. package/dist/model-context/interactable-composer-metadata.d.ts +1 -1
  14. package/dist/model-context/interactable-composer-metadata.js +1 -1
  15. package/dist/model-context/interactable-composer-metadata.js.map +1 -1
  16. package/dist/model-context/registry.d.ts +2 -2
  17. package/dist/model-context/registry.d.ts.map +1 -1
  18. package/dist/model-context/registry.js +9 -28
  19. package/dist/model-context/registry.js.map +1 -1
  20. package/dist/model-context/tool-call-text.d.ts +26 -0
  21. package/dist/model-context/tool-call-text.d.ts.map +1 -0
  22. package/dist/model-context/tool-call-text.js +18 -0
  23. package/dist/model-context/tool-call-text.js.map +1 -0
  24. package/dist/react/RuntimeAdapter.js +1 -1
  25. package/dist/react/adapters/LocalStorageThreadListAdapter.d.ts.map +1 -1
  26. package/dist/react/adapters/LocalStorageThreadListAdapter.js +1 -1
  27. package/dist/react/adapters/LocalStorageThreadListAdapter.js.map +1 -1
  28. package/dist/react/client/AdaptedRemoteThread.js +1 -1
  29. package/dist/react/client/InMemoryThreadList.d.ts.map +1 -1
  30. package/dist/react/client/InMemoryThreadList.js +25 -21
  31. package/dist/react/client/InMemoryThreadList.js.map +1 -1
  32. package/dist/react/client/Interactables.d.ts.map +1 -1
  33. package/dist/react/client/Interactables.js +60 -178
  34. package/dist/react/client/Interactables.js.map +1 -1
  35. package/dist/react/client/RemoteThreadList.d.ts +15 -0
  36. package/dist/react/client/RemoteThreadList.d.ts.map +1 -1
  37. package/dist/react/client/RemoteThreadList.js +263 -194
  38. package/dist/react/client/RemoteThreadList.js.map +1 -1
  39. package/dist/react/client/Tools.d.ts.map +1 -1
  40. package/dist/react/client/Tools.js +6 -2
  41. package/dist/react/client/Tools.js.map +1 -1
  42. package/dist/react/index.d.ts +4 -2
  43. package/dist/react/index.js +2 -1
  44. package/dist/react/interactables-legacy/Interactables.d.ts.map +1 -1
  45. package/dist/react/interactables-legacy/Interactables.js +60 -178
  46. package/dist/react/interactables-legacy/Interactables.js.map +1 -1
  47. package/dist/react/interactables-shared/useInteractablePersistenceQueue.d.ts +24 -0
  48. package/dist/react/interactables-shared/useInteractablePersistenceQueue.d.ts.map +1 -0
  49. package/dist/react/interactables-shared/useInteractablePersistenceQueue.js +138 -0
  50. package/dist/react/interactables-shared/useInteractablePersistenceQueue.js.map +1 -0
  51. package/dist/react/model-context/toolbox.d.ts +2 -14
  52. package/dist/react/model-context/toolbox.d.ts.map +1 -1
  53. package/dist/react/model-context/toolbox.js +1 -13
  54. package/dist/react/model-context/toolbox.js.map +1 -1
  55. package/dist/react/model-context/useAssistantContext.js +37 -7
  56. package/dist/react/model-context/useAssistantContext.js.map +1 -1
  57. package/dist/react/model-context/useAuiToolOverrides.js +31 -5
  58. package/dist/react/model-context/useAuiToolOverrides.js.map +1 -1
  59. package/dist/react/model-context/useInlineRender.d.ts.map +1 -1
  60. package/dist/react/model-context/useInlineRender.js +10 -36
  61. package/dist/react/model-context/useInlineRender.js.map +1 -1
  62. package/dist/react/primitive-hooks/useMessageError.d.ts.map +1 -1
  63. package/dist/react/primitive-hooks/useMessageError.js +2 -8
  64. package/dist/react/primitive-hooks/useMessageError.js.map +1 -1
  65. package/dist/react/primitive-hooks/useThreadListLoadMore.d.ts.map +1 -1
  66. package/dist/react/primitive-hooks/useThreadListLoadMore.js +2 -4
  67. package/dist/react/primitive-hooks/useThreadListLoadMore.js.map +1 -1
  68. package/dist/react/primitives/composer/ComposerAttachments.d.ts.map +1 -1
  69. package/dist/react/primitives/composer/ComposerAttachments.js +4 -3
  70. package/dist/react/primitives/composer/ComposerAttachments.js.map +1 -1
  71. package/dist/react/primitives/composer/ComposerIf.js.map +1 -1
  72. package/dist/react/primitives/composer/ComposerQueue.d.ts +2 -1
  73. package/dist/react/primitives/composer/ComposerQueue.d.ts.map +1 -1
  74. package/dist/react/primitives/composer/ComposerQueue.js +4 -3
  75. package/dist/react/primitives/composer/ComposerQueue.js.map +1 -1
  76. package/dist/react/primitives/message/MessageAttachments.d.ts.map +1 -1
  77. package/dist/react/primitives/message/MessageAttachments.js +7 -6
  78. package/dist/react/primitives/message/MessageAttachments.js.map +1 -1
  79. package/dist/react/primitives/message/MessageGroupedParts.js +2 -2
  80. package/dist/react/primitives/message/MessageGroupedParts.js.map +1 -1
  81. package/dist/react/primitives/message/MessageParts.js +3 -3
  82. package/dist/react/primitives/message/MessageParts.js.map +1 -1
  83. package/dist/react/primitives/threadList/ThreadListItems.d.ts.map +1 -1
  84. package/dist/react/primitives/threadList/ThreadListItems.js +5 -4
  85. package/dist/react/primitives/threadList/ThreadListItems.js.map +1 -1
  86. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.d.ts +7 -5
  87. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.d.ts.map +1 -1
  88. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js +44 -22
  89. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js.map +1 -1
  90. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts +7 -6
  91. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts.map +1 -1
  92. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js +102 -117
  93. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js.map +1 -1
  94. package/dist/react/runtimes/RemoteThreadResource.d.ts +1 -6
  95. package/dist/react/runtimes/RemoteThreadResource.d.ts.map +1 -1
  96. package/dist/react/runtimes/RemoteThreadResource.js +3 -3
  97. package/dist/react/runtimes/RemoteThreadResource.js.map +1 -1
  98. package/dist/react/runtimes/assistant-transport/runManager.d.ts.map +1 -1
  99. package/dist/react/runtimes/assistant-transport/runManager.js +2 -12
  100. package/dist/react/runtimes/assistant-transport/runManager.js.map +1 -1
  101. package/dist/react/runtimes/assistant-transport/useLatestRef.js +2 -2
  102. package/dist/react/runtimes/assistant-transport/useLatestRef.js.map +1 -1
  103. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.d.ts +3 -10
  104. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.d.ts.map +1 -1
  105. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js +43 -85
  106. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js.map +1 -1
  107. package/dist/react/runtimes/cloud/auiV0.d.ts +5 -1
  108. package/dist/react/runtimes/cloud/auiV0.d.ts.map +1 -1
  109. package/dist/react/runtimes/cloud/auiV0.js.map +1 -1
  110. package/dist/react/runtimes/cloud/createCloudThreadListAdapter.d.ts +29 -0
  111. package/dist/react/runtimes/cloud/createCloudThreadListAdapter.d.ts.map +1 -0
  112. package/dist/react/runtimes/cloud/createCloudThreadListAdapter.js +159 -0
  113. package/dist/react/runtimes/cloud/createCloudThreadListAdapter.js.map +1 -0
  114. package/dist/react/runtimes/cloud/createCloudThreadListAdapterCreateFallback.d.ts +12 -0
  115. package/dist/react/runtimes/cloud/createCloudThreadListAdapterCreateFallback.d.ts.map +1 -0
  116. package/dist/react/runtimes/cloud/createCloudThreadListAdapterCreateFallback.js +10 -0
  117. package/dist/react/runtimes/cloud/createCloudThreadListAdapterCreateFallback.js.map +1 -0
  118. package/dist/react/runtimes/cloud/useCloudThreadListAdapter.d.ts +1 -9
  119. package/dist/react/runtimes/cloud/useCloudThreadListAdapter.d.ts.map +1 -1
  120. package/dist/react/runtimes/cloud/useCloudThreadListAdapter.js +16 -153
  121. package/dist/react/runtimes/cloud/useCloudThreadListAdapter.js.map +1 -1
  122. package/dist/react/runtimes/cloud/useCloudThreadListRuntime.js.map +1 -1
  123. package/dist/react/runtimes/createMessageConverter.d.ts +2 -1
  124. package/dist/react/runtimes/createMessageConverter.d.ts.map +1 -1
  125. package/dist/react/runtimes/external-message-converter.d.ts +6 -25
  126. package/dist/react/runtimes/external-message-converter.d.ts.map +1 -1
  127. package/dist/react/runtimes/external-message-converter.js +33 -233
  128. package/dist/react/runtimes/external-message-converter.js.map +1 -1
  129. package/dist/react/runtimes/useRemoteThreadListRuntime.d.ts.map +1 -1
  130. package/dist/react/runtimes/useRemoteThreadListRuntime.js +14 -6
  131. package/dist/react/runtimes/useRemoteThreadListRuntime.js.map +1 -1
  132. package/dist/react/types/MessagePartComponentTypes.d.ts +7 -3
  133. package/dist/react/types/MessagePartComponentTypes.d.ts.map +1 -1
  134. package/dist/react/types/scopes/tools.d.ts +2 -0
  135. package/dist/react/types/scopes/tools.d.ts.map +1 -1
  136. package/dist/runtime/api/composer-runtime.d.ts +1 -1
  137. package/dist/runtime/api/composer-runtime.js.map +1 -1
  138. package/dist/runtime/api/message-part-runtime.d.ts +2 -2
  139. package/dist/runtime/api/message-part-runtime.d.ts.map +1 -1
  140. package/dist/runtime/api/message-part-runtime.js +1 -1
  141. package/dist/runtime/api/message-part-runtime.js.map +1 -1
  142. package/dist/runtime/api/thread-list-runtime.d.ts +9 -1
  143. package/dist/runtime/api/thread-list-runtime.d.ts.map +1 -1
  144. package/dist/runtime/api/thread-list-runtime.js +5 -0
  145. package/dist/runtime/api/thread-list-runtime.js.map +1 -1
  146. package/dist/runtime/api/thread-runtime.d.ts +2 -3
  147. package/dist/runtime/api/thread-runtime.d.ts.map +1 -1
  148. package/dist/runtime/base/base-composer-runtime-core.d.ts +1 -1
  149. package/dist/runtime/base/base-composer-runtime-core.d.ts.map +1 -1
  150. package/dist/runtime/base/base-composer-runtime-core.js +14 -29
  151. package/dist/runtime/base/base-composer-runtime-core.js.map +1 -1
  152. package/dist/runtime/base/base-thread-runtime-core.d.ts +4 -6
  153. package/dist/runtime/base/base-thread-runtime-core.d.ts.map +1 -1
  154. package/dist/runtime/base/base-thread-runtime-core.js +5 -11
  155. package/dist/runtime/base/base-thread-runtime-core.js.map +1 -1
  156. package/dist/runtime/base/default-thread-composer-runtime-core.d.ts +1 -1
  157. package/dist/runtime/base/default-thread-composer-runtime-core.d.ts.map +1 -1
  158. package/dist/runtime/base/default-thread-composer-runtime-core.js +1 -1
  159. package/dist/runtime/base/default-thread-composer-runtime-core.js.map +1 -1
  160. package/dist/runtime/interfaces/composer-runtime-core.d.ts +1 -1
  161. package/dist/runtime/interfaces/thread-list-runtime-core.d.ts +14 -2
  162. package/dist/runtime/interfaces/thread-list-runtime-core.d.ts.map +1 -1
  163. package/dist/runtime/interfaces/thread-runtime-core.d.ts +16 -2
  164. package/dist/runtime/interfaces/thread-runtime-core.d.ts.map +1 -1
  165. package/dist/runtime/internal.d.ts +2 -1
  166. package/dist/runtime/internal.js +2 -1
  167. package/dist/runtime/queue/external-thread-queue-adapter.d.ts +1 -1
  168. package/dist/runtime/queue/message-queue.d.ts.map +1 -1
  169. package/dist/runtime/queue/message-queue.js +1 -1
  170. package/dist/runtime/queue/message-queue.js.map +1 -1
  171. package/dist/runtime/queue/queue-item.d.ts +12 -0
  172. package/dist/runtime/queue/queue-item.d.ts.map +1 -0
  173. package/dist/runtime/queue/queue-item.js +6 -0
  174. package/dist/runtime/queue/queue-item.js.map +1 -0
  175. package/dist/runtime/utils/abortable-thread-load.d.ts +15 -0
  176. package/dist/runtime/utils/abortable-thread-load.d.ts.map +1 -0
  177. package/dist/runtime/utils/abortable-thread-load.js +35 -0
  178. package/dist/runtime/utils/abortable-thread-load.js.map +1 -0
  179. package/dist/runtime/utils/attachment-add-operations.d.ts +18 -0
  180. package/dist/runtime/utils/attachment-add-operations.d.ts.map +1 -0
  181. package/dist/runtime/utils/attachment-add-operations.js +43 -0
  182. package/dist/runtime/utils/attachment-add-operations.js.map +1 -0
  183. package/dist/runtime/utils/auto-status.d.ts +7 -2
  184. package/dist/runtime/utils/auto-status.d.ts.map +1 -1
  185. package/dist/runtime/utils/auto-status.js +13 -3
  186. package/dist/runtime/utils/auto-status.js.map +1 -1
  187. package/dist/runtime/utils/external-message-conversion.d.ts +48 -0
  188. package/dist/runtime/utils/external-message-conversion.d.ts.map +1 -0
  189. package/dist/runtime/utils/external-message-conversion.js +222 -0
  190. package/dist/runtime/utils/external-message-conversion.js.map +1 -0
  191. package/dist/runtime/utils/external-store-message.d.ts +2 -1
  192. package/dist/runtime/utils/external-store-message.d.ts.map +1 -1
  193. package/dist/runtime/utils/external-store-message.js +2 -1
  194. package/dist/runtime/utils/external-store-message.js.map +1 -1
  195. package/dist/runtime/utils/message-repository-session.d.ts +30 -0
  196. package/dist/runtime/utils/message-repository-session.d.ts.map +1 -0
  197. package/dist/runtime/utils/message-repository-session.js +130 -0
  198. package/dist/runtime/utils/message-repository-session.js.map +1 -0
  199. package/dist/runtime/utils/message-repository.d.ts.map +1 -1
  200. package/dist/runtime/utils/message-repository.js +3 -4
  201. package/dist/runtime/utils/message-repository.js.map +1 -1
  202. package/dist/runtime/utils/pending-tool-calls.d.ts +20 -0
  203. package/dist/runtime/utils/pending-tool-calls.d.ts.map +1 -0
  204. package/dist/runtime/utils/pending-tool-calls.js +22 -0
  205. package/dist/runtime/utils/pending-tool-calls.js.map +1 -0
  206. package/dist/runtime/utils/resolveToolApprovalResponse.d.ts +6 -3
  207. package/dist/runtime/utils/resolveToolApprovalResponse.d.ts.map +1 -1
  208. package/dist/runtime/utils/resolveToolApprovalResponse.js +12 -3
  209. package/dist/runtime/utils/resolveToolApprovalResponse.js.map +1 -1
  210. package/dist/runtime/utils/thread-message-like.d.ts +3 -10
  211. package/dist/runtime/utils/thread-message-like.d.ts.map +1 -1
  212. package/dist/runtime/utils/thread-message-like.js.map +1 -1
  213. package/dist/runtime/utils/tool-call-tree.d.ts +39 -0
  214. package/dist/runtime/utils/tool-call-tree.d.ts.map +1 -0
  215. package/dist/runtime/utils/tool-call-tree.js +85 -0
  216. package/dist/runtime/utils/tool-call-tree.js.map +1 -0
  217. package/dist/runtimes/external-store/external-store-adapter.d.ts +29 -2
  218. package/dist/runtimes/external-store/external-store-adapter.d.ts.map +1 -1
  219. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.d.ts +2 -5
  220. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.d.ts.map +1 -1
  221. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.js +3 -10
  222. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.js.map +1 -1
  223. package/dist/runtimes/external-store/external-store-thread-runtime-core.d.ts +21 -2
  224. package/dist/runtimes/external-store/external-store-thread-runtime-core.d.ts.map +1 -1
  225. package/dist/runtimes/external-store/external-store-thread-runtime-core.js +154 -46
  226. package/dist/runtimes/external-store/external-store-thread-runtime-core.js.map +1 -1
  227. package/dist/runtimes/inert/InertThreadRuntimeCore.d.ts +1 -1
  228. package/dist/runtimes/inert/InertThreadRuntimeCore.d.ts.map +1 -1
  229. package/dist/runtimes/inert/InertThreadRuntimeCore.js.map +1 -1
  230. package/dist/runtimes/local/local-thread-runtime-core.d.ts +4 -2
  231. package/dist/runtimes/local/local-thread-runtime-core.d.ts.map +1 -1
  232. package/dist/runtimes/local/local-thread-runtime-core.js +23 -3
  233. package/dist/runtimes/local/local-thread-runtime-core.js.map +1 -1
  234. package/dist/runtimes/remote-thread-list/remote-thread-state.d.ts +12 -1
  235. package/dist/runtimes/remote-thread-list/remote-thread-state.d.ts.map +1 -1
  236. package/dist/runtimes/remote-thread-list/remote-thread-state.js +107 -14
  237. package/dist/runtimes/remote-thread-list/remote-thread-state.js.map +1 -1
  238. package/dist/runtimes/remote-thread-list/title.d.ts +9 -0
  239. package/dist/runtimes/remote-thread-list/title.d.ts.map +1 -0
  240. package/dist/runtimes/remote-thread-list/title.js +6 -0
  241. package/dist/runtimes/remote-thread-list/title.js.map +1 -0
  242. package/dist/runtimes/remote-thread-list/types.d.ts +6 -5
  243. package/dist/runtimes/remote-thread-list/types.d.ts.map +1 -1
  244. package/dist/runtimes/tool-invocations/ToolInvocationTracker.d.ts +31 -8
  245. package/dist/runtimes/tool-invocations/ToolInvocationTracker.d.ts.map +1 -1
  246. package/dist/runtimes/tool-invocations/ToolInvocationTracker.js +98 -59
  247. package/dist/runtimes/tool-invocations/ToolInvocationTracker.js.map +1 -1
  248. package/dist/store/clients/external-thread.d.ts +1 -1
  249. package/dist/store/clients/external-thread.d.ts.map +1 -1
  250. package/dist/store/clients/external-thread.js +85 -103
  251. package/dist/store/clients/external-thread.js.map +1 -1
  252. package/dist/store/clients/single-thread-list.d.ts.map +1 -1
  253. package/dist/store/clients/single-thread-list.js +2 -2
  254. package/dist/store/clients/single-thread-list.js.map +1 -1
  255. package/dist/store/clients/suggestions.d.ts.map +1 -1
  256. package/dist/store/clients/suggestions.js +33 -12
  257. package/dist/store/clients/suggestions.js.map +1 -1
  258. package/dist/store/clients/thread-message-client.js +1 -1
  259. package/dist/store/clients/thread-selection-events.js +1 -1
  260. package/dist/store/index.d.ts +6 -3
  261. package/dist/store/index.js +2 -1
  262. package/dist/store/internal.d.ts +2 -2
  263. package/dist/store/internal.js +2 -2
  264. package/dist/store/primitive-predicates.d.ts +4 -1
  265. package/dist/store/primitive-predicates.d.ts.map +1 -1
  266. package/dist/store/primitive-predicates.js +9 -1
  267. package/dist/store/primitive-predicates.js.map +1 -1
  268. package/dist/store/runtime-clients/composer-runtime-client.js +1 -1
  269. package/dist/store/runtime-clients/message-runtime-client.js +1 -1
  270. package/dist/store/runtime-clients/thread-list-item-runtime-client.js +1 -1
  271. package/dist/store/runtime-clients/thread-list-runtime-client.d.ts.map +1 -1
  272. package/dist/store/runtime-clients/thread-list-runtime-client.js +124 -105
  273. package/dist/store/runtime-clients/thread-list-runtime-client.js.map +1 -1
  274. package/dist/store/runtime-clients/thread-runtime-client.js +1 -1
  275. package/dist/store/runtime-clients/useSubscribable.d.ts +3 -1
  276. package/dist/store/runtime-clients/useSubscribable.d.ts.map +1 -1
  277. package/dist/store/runtime-clients/useSubscribable.js +1 -1
  278. package/dist/store/runtime-clients/useSubscribable.js.map +1 -1
  279. package/dist/store/scope-registration.d.ts +1 -1
  280. package/dist/store/scopes/composer.d.ts +2 -1
  281. package/dist/store/scopes/composer.d.ts.map +1 -1
  282. package/dist/store/scopes/message.d.ts +1 -1
  283. package/dist/store/scopes/part.d.ts +6 -3
  284. package/dist/store/scopes/part.d.ts.map +1 -1
  285. package/dist/store/scopes/queue-item.d.ts +2 -9
  286. package/dist/store/scopes/queue-item.d.ts.map +1 -1
  287. package/dist/store/scopes/queue-item.js +0 -6
  288. package/dist/subscribable/subscribable.d.ts +8 -1
  289. package/dist/subscribable/subscribable.d.ts.map +1 -1
  290. package/dist/subscribable/subscribable.js +24 -16
  291. package/dist/subscribable/subscribable.js.map +1 -1
  292. package/dist/types/message.d.ts +47 -4
  293. package/dist/types/message.d.ts.map +1 -1
  294. package/dist/types/message.js +7 -1
  295. package/dist/types/message.js.map +1 -1
  296. package/dist/utils/data-url.d.ts +14 -1
  297. package/dist/utils/data-url.d.ts.map +1 -1
  298. package/dist/utils/data-url.js +13 -1
  299. package/dist/utils/data-url.js.map +1 -1
  300. package/dist/utils/invoke-user-callback.d.ts +5 -0
  301. package/dist/utils/invoke-user-callback.d.ts.map +1 -0
  302. package/dist/utils/invoke-user-callback.js +19 -0
  303. package/dist/utils/invoke-user-callback.js.map +1 -0
  304. package/dist/utils/normalizePartStatus.js.map +1 -1
  305. package/package.json +12 -17
  306. package/src/index.ts +6 -1
  307. package/src/internal.ts +20 -0
  308. package/src/model-context/frame/host.test.ts +91 -7
  309. package/src/model-context/frame/host.ts +7 -2
  310. package/src/model-context/frame/provider.test.ts +484 -13
  311. package/src/model-context/frame/provider.ts +205 -35
  312. package/src/model-context/interactable-composer-metadata.ts +1 -1
  313. package/src/model-context/registry.test.ts +44 -0
  314. package/src/model-context/registry.ts +13 -35
  315. package/src/model-context/tool-call-text.test.ts +67 -0
  316. package/src/model-context/tool-call-text.ts +67 -0
  317. package/src/react/AssistantRuntimeProvider.test.tsx +66 -2
  318. package/src/react/adapters/LocalStorageThreadListAdapter.tsx +1 -3
  319. package/src/react/client/InMemoryThreadList.test.tsx +49 -0
  320. package/src/react/client/InMemoryThreadList.ts +7 -1
  321. package/src/react/client/Interactables.ts +19 -178
  322. package/src/react/client/RemoteThreadList.background.test.ts +384 -0
  323. package/src/react/client/RemoteThreadList.concurrent.test.tsx +274 -0
  324. package/src/react/client/RemoteThreadList.test.ts +369 -1
  325. package/src/react/client/RemoteThreadList.ts +320 -142
  326. package/src/react/client/Tools.ts +7 -1
  327. package/src/react/index.ts +4 -0
  328. package/src/react/interactables-legacy/Interactables.ts +20 -177
  329. package/src/react/interactables-shared/useInteractablePersistenceQueue.test.tsx +161 -0
  330. package/src/react/interactables-shared/useInteractablePersistenceQueue.ts +206 -0
  331. package/src/react/model-context/modelContextHooks.test.tsx +131 -0
  332. package/src/react/model-context/toolbox.test.ts +9 -9
  333. package/src/react/model-context/toolbox.ts +9 -36
  334. package/src/react/model-context/useAssistantContext.ts +4 -2
  335. package/src/react/model-context/useAuiToolOverrides.ts +4 -2
  336. package/src/react/model-context/useInlineRender.test.tsx +48 -0
  337. package/src/react/model-context/useInlineRender.ts +14 -10
  338. package/src/react/primitive-hooks/useMessageError.ts +2 -19
  339. package/src/react/primitive-hooks/useThreadListLoadMore.ts +2 -3
  340. package/src/react/primitives/composer/ComposerAttachments.test.tsx +76 -0
  341. package/src/react/primitives/composer/ComposerAttachments.tsx +9 -4
  342. package/src/react/primitives/composer/ComposerQueue.test.tsx +72 -0
  343. package/src/react/primitives/composer/ComposerQueue.tsx +7 -4
  344. package/src/react/primitives/message/MessageAttachments.test.tsx +107 -37
  345. package/src/react/primitives/message/MessageAttachments.tsx +10 -7
  346. package/src/react/primitives/message/MessageGroupedParts.tsx +2 -2
  347. package/src/react/primitives/message/MessageParts.tsx +3 -3
  348. package/src/react/primitives/threadList/ThreadListItems.test.tsx +99 -0
  349. package/src/react/primitives/threadList/ThreadListItems.tsx +8 -5
  350. package/src/react/runtimes/RemoteThreadListHookInstanceManager.running.test.tsx +94 -1
  351. package/src/react/runtimes/RemoteThreadListHookInstanceManager.tsx +72 -31
  352. package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.switch-delete.test.ts +169 -0
  353. package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.tsx +140 -124
  354. package/src/react/runtimes/RemoteThreadResource.ts +1 -4
  355. package/src/react/runtimes/assistant-transport/runManager.ts +4 -17
  356. package/src/react/runtimes/assistant-transport/transport-scheduling.test.ts +51 -2
  357. package/src/react/runtimes/assistant-transport/useLatestRef.ts +2 -2
  358. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.test.ts +45 -0
  359. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.test.tsx +157 -1
  360. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.ts +82 -176
  361. package/src/react/runtimes/cloud/auiV0.ts +5 -0
  362. package/src/react/runtimes/cloud/createCloudThreadListAdapter.test.ts +88 -0
  363. package/src/react/runtimes/cloud/createCloudThreadListAdapter.ts +236 -0
  364. package/src/react/runtimes/cloud/createCloudThreadListAdapterCreateFallback.test.ts +37 -0
  365. package/src/react/runtimes/cloud/createCloudThreadListAdapterCreateFallback.ts +18 -0
  366. package/src/react/runtimes/cloud/useCloudThreadListAdapter.test.tsx +90 -1
  367. package/src/react/runtimes/cloud/useCloudThreadListAdapter.tsx +33 -238
  368. package/src/react/runtimes/external-message-converter.test.ts +0 -14
  369. package/src/react/runtimes/external-message-converter.test.tsx +77 -0
  370. package/src/react/runtimes/external-message-converter.ts +59 -481
  371. package/src/react/runtimes/useRemoteThreadListRuntime.concurrent.test.tsx +216 -0
  372. package/src/react/runtimes/useRemoteThreadListRuntime.ts +24 -6
  373. package/src/react/types/MessagePartComponentTypes.ts +7 -3
  374. package/src/react/types/scopes/tools.ts +2 -0
  375. package/src/runtime/api/composer-runtime.ts +1 -1
  376. package/src/runtime/api/message-part-runtime.ts +3 -3
  377. package/src/runtime/api/message-runtime.test.ts +28 -1
  378. package/src/runtime/api/thread-list-runtime.ts +25 -1
  379. package/src/runtime/base/base-composer-runtime-core.ts +20 -40
  380. package/src/runtime/base/base-thread-runtime-core.test.ts +110 -3
  381. package/src/runtime/base/base-thread-runtime-core.ts +16 -15
  382. package/src/runtime/base/default-thread-composer-runtime-core.ts +1 -4
  383. package/src/runtime/interfaces/composer-runtime-core.ts +1 -1
  384. package/src/runtime/interfaces/thread-list-runtime-core.ts +20 -1
  385. package/src/runtime/interfaces/thread-runtime-core.ts +18 -2
  386. package/src/runtime/internal.ts +5 -0
  387. package/src/runtime/queue/external-thread-queue-adapter.ts +1 -1
  388. package/src/runtime/queue/message-queue.ts +1 -4
  389. package/src/runtime/queue/queue-item.ts +10 -0
  390. package/src/runtime/utils/abortable-thread-load.test.ts +87 -0
  391. package/src/runtime/utils/abortable-thread-load.ts +53 -0
  392. package/src/runtime/utils/attachment-add-operations.ts +59 -0
  393. package/src/runtime/utils/auto-status.test.ts +57 -0
  394. package/src/runtime/utils/auto-status.ts +46 -1
  395. package/src/runtime/utils/external-message-conversion.test.ts +210 -0
  396. package/src/runtime/utils/external-message-conversion.ts +510 -0
  397. package/src/runtime/utils/external-store-message.ts +2 -0
  398. package/src/runtime/utils/message-repository-session.test.ts +195 -0
  399. package/src/runtime/utils/message-repository-session.ts +176 -0
  400. package/src/runtime/utils/message-repository.ts +7 -4
  401. package/src/runtime/utils/pending-tool-calls.test.ts +57 -0
  402. package/src/runtime/utils/pending-tool-calls.ts +35 -0
  403. package/src/runtime/utils/resolveToolApprovalResponse.test.ts +73 -0
  404. package/src/runtime/utils/resolveToolApprovalResponse.ts +27 -6
  405. package/src/runtime/utils/thread-message-like.ts +4 -10
  406. package/src/runtime/utils/tool-call-tree.test.ts +191 -0
  407. package/src/runtime/utils/tool-call-tree.ts +97 -0
  408. package/src/runtimes/external-store/external-store-adapter.ts +38 -2
  409. package/src/runtimes/external-store/external-store-thread-list-runtime-core.ts +6 -14
  410. package/src/runtimes/external-store/external-store-thread-runtime-core.ts +231 -61
  411. package/src/runtimes/inert/InertThreadRuntimeCore.ts +1 -1
  412. package/src/runtimes/local/local-thread-runtime-core.test.ts +201 -4
  413. package/src/runtimes/local/local-thread-runtime-core.ts +53 -4
  414. package/src/runtimes/remote-thread-list/remote-thread-state.test.ts +149 -0
  415. package/src/runtimes/remote-thread-list/remote-thread-state.ts +178 -16
  416. package/src/runtimes/remote-thread-list/title.ts +3 -0
  417. package/src/runtimes/remote-thread-list/types.ts +6 -5
  418. package/src/runtimes/tool-invocations/EDGE_CASES.md +125 -3
  419. package/src/runtimes/tool-invocations/ToolInvocationTracker.test.ts +856 -102
  420. package/src/runtimes/tool-invocations/ToolInvocationTracker.ts +145 -89
  421. package/src/store/clients/external-thread.ts +30 -69
  422. package/src/store/clients/single-thread-list.ts +4 -1
  423. package/src/store/clients/suggestions.test.ts +185 -0
  424. package/src/store/clients/suggestions.ts +42 -35
  425. package/src/store/index.ts +4 -0
  426. package/src/store/internal.ts +2 -0
  427. package/src/store/primitive-predicates.ts +35 -0
  428. package/src/store/runtime-clients/thread-list-runtime-client.ts +16 -2
  429. package/src/store/runtime-clients/useSubscribable.ts +8 -2
  430. package/src/store/scopes/message.ts +1 -1
  431. package/src/store/scopes/part.ts +6 -3
  432. package/src/store/scopes/queue-item.ts +2 -9
  433. package/src/subscribable/subscribable.test.ts +50 -0
  434. package/src/subscribable/subscribable.ts +33 -21
  435. package/src/tests/MessageRepository.test.ts +77 -0
  436. package/src/tests/RemoteThreadListThreadListRuntimeCore-load-race.test.ts +305 -0
  437. package/src/tests/RemoteThreadListThreadListRuntimeCore-loadMore.test.ts +78 -0
  438. package/src/tests/RemoteThreadListThreadListRuntimeCore-reload.test.ts +52 -0
  439. package/src/tests/RemoteThreadListThreadListRuntimeCore-title-race.test.ts +158 -0
  440. package/src/tests/external-store-optimistic-id.test.ts +79 -0
  441. package/src/tests/external-store-repository-instance.test.ts +166 -0
  442. package/src/tests/external-store-thread-runtime-core-adapter.test.ts +186 -16
  443. package/src/tests/external-store-thread-runtime-core.test.ts +642 -0
  444. package/src/tests/external-thread-attachments.test.tsx +258 -1
  445. package/src/tests/remote-thread-list-adapter-switch.test.ts +66 -1
  446. package/src/tests/single-thread-list-item.test.tsx +47 -0
  447. package/src/tests/thread-message-like.test.ts +39 -1
  448. package/src/tests/thread-switch-events.test.tsx +397 -2
  449. package/src/types/message.ts +58 -5
  450. package/src/utils/data-url.test.ts +52 -1
  451. package/src/utils/data-url.ts +21 -0
  452. package/src/utils/invoke-user-callback.test.ts +82 -0
  453. package/src/utils/invoke-user-callback.ts +31 -0
  454. package/src/utils/normalizePartStatus.ts +1 -1
  455. package/dist/store/scopes/queue-item.js.map +0 -1
@@ -0,0 +1,185 @@
1
+ import { useState } from "react";
2
+ import { createTapRoot, flushTapSync, useResource } from "@assistant-ui/tap";
3
+ import { describe, expect, it } from "vitest";
4
+ import type { ThreadSuggestion } from "../../runtime/interfaces/thread-runtime-core";
5
+ import {
6
+ Suggestions,
7
+ ThreadSuggestions,
8
+ type SuggestionConfig,
9
+ } from "./suggestions";
10
+
11
+ describe("Suggestions", () => {
12
+ it("keeps equal inline string suggestions stable across owner renders", () => {
13
+ let rerender!: () => void;
14
+ const root = createTapRoot(function SuggestionsRoot() {
15
+ const [, setTick] = useState(0);
16
+ rerender = () => setTick((tick) => tick + 1);
17
+ return useResource(Suggestions(["account-a", "account-b"]));
18
+ });
19
+
20
+ try {
21
+ const firstState = root.getValue().getState();
22
+ const firstSuggestion = root
23
+ .getValue()
24
+ .suggestion({ index: 0 })
25
+ .getState();
26
+
27
+ flushTapSync(rerender);
28
+
29
+ expect(root.getValue().getState()).toBe(firstState);
30
+ expect(root.getValue().suggestion({ index: 0 }).getState()).toBe(
31
+ firstSuggestion,
32
+ );
33
+ } finally {
34
+ root.unmount();
35
+ }
36
+ });
37
+
38
+ it("keeps equal inline object suggestions stable across owner renders", () => {
39
+ let rerender!: () => void;
40
+ const root = createTapRoot(function SuggestionsRoot() {
41
+ const [, setTick] = useState(0);
42
+ rerender = () => setTick((tick) => tick + 1);
43
+ return useResource(
44
+ Suggestions([{ title: "Title", label: "Label", prompt: "Prompt" }]),
45
+ );
46
+ });
47
+
48
+ try {
49
+ const firstState = root.getValue().getState();
50
+ const firstSuggestion = root
51
+ .getValue()
52
+ .suggestion({ index: 0 })
53
+ .getState();
54
+
55
+ flushTapSync(rerender);
56
+
57
+ expect(root.getValue().getState()).toBe(firstState);
58
+ expect(root.getValue().suggestion({ index: 0 }).getState()).toBe(
59
+ firstSuggestion,
60
+ );
61
+ } finally {
62
+ root.unmount();
63
+ }
64
+ });
65
+
66
+ it("updates when the configured suggestions change", () => {
67
+ let setSuggestions!: (suggestions: SuggestionConfig[]) => void;
68
+ const root = createTapRoot(function SuggestionsRoot() {
69
+ const [suggestions, setValue] = useState<SuggestionConfig[]>([
70
+ "account-a",
71
+ ]);
72
+ setSuggestions = setValue;
73
+ return useResource(Suggestions(suggestions));
74
+ });
75
+
76
+ try {
77
+ expect(root.getValue().getState().suggestions[0]?.prompt).toBe(
78
+ "account-a",
79
+ );
80
+
81
+ flushTapSync(() => setSuggestions(["account-b"]));
82
+
83
+ expect(root.getValue().getState().suggestions[0]?.prompt).toBe(
84
+ "account-b",
85
+ );
86
+ } finally {
87
+ root.unmount();
88
+ }
89
+ });
90
+
91
+ it("reuses unchanged suggestion state when one entry changes", () => {
92
+ let setSuggestions!: (suggestions: SuggestionConfig[]) => void;
93
+ const root = createTapRoot(function SuggestionsRoot() {
94
+ const [suggestions, setValue] = useState<SuggestionConfig[]>([
95
+ { title: "Title A", label: "Label A", prompt: "Prompt A" },
96
+ { title: "Title B", label: "Label B", prompt: "Prompt B" },
97
+ ]);
98
+ setSuggestions = setValue;
99
+ return useResource(Suggestions(suggestions));
100
+ });
101
+
102
+ try {
103
+ const firstState = root.getValue().getState();
104
+ const unchangedSuggestion = root
105
+ .getValue()
106
+ .suggestion({ index: 1 })
107
+ .getState();
108
+
109
+ flushTapSync(() =>
110
+ setSuggestions([
111
+ { title: "Changed", label: "Label A", prompt: "Prompt A" },
112
+ { title: "Title B", label: "Label B", prompt: "Prompt B" },
113
+ ]),
114
+ );
115
+
116
+ expect(root.getValue().getState()).not.toBe(firstState);
117
+ expect(root.getValue().getState().suggestions[0]?.title).toBe("Changed");
118
+ expect(root.getValue().suggestion({ index: 1 }).getState()).toBe(
119
+ unchangedSuggestion,
120
+ );
121
+ } finally {
122
+ root.unmount();
123
+ }
124
+ });
125
+ });
126
+
127
+ describe("ThreadSuggestions", () => {
128
+ it("keeps equal runtime suggestions stable across owner renders", () => {
129
+ let rerender!: () => void;
130
+ const root = createTapRoot(function ThreadSuggestionsRoot() {
131
+ const [, setTick] = useState(0);
132
+ rerender = () => setTick((tick) => tick + 1);
133
+ return useResource(ThreadSuggestions([{ prompt: "Prompt" }]));
134
+ });
135
+
136
+ try {
137
+ const firstState = root.getValue().getState();
138
+ const firstSuggestion = root
139
+ .getValue()
140
+ .suggestion({ index: 0 })
141
+ .getState();
142
+
143
+ flushTapSync(rerender);
144
+
145
+ expect(root.getValue().getState()).toBe(firstState);
146
+ expect(root.getValue().suggestion({ index: 0 }).getState()).toBe(
147
+ firstSuggestion,
148
+ );
149
+ } finally {
150
+ root.unmount();
151
+ }
152
+ });
153
+
154
+ it("reuses unchanged suggestion state when one entry changes", () => {
155
+ let setSuggestions!: (suggestions: ThreadSuggestion[]) => void;
156
+ const root = createTapRoot(function ThreadSuggestionsRoot() {
157
+ const [suggestions, setValue] = useState<ThreadSuggestion[]>([
158
+ { prompt: "Prompt A" },
159
+ { prompt: "Prompt B" },
160
+ ]);
161
+ setSuggestions = setValue;
162
+ return useResource(ThreadSuggestions(suggestions));
163
+ });
164
+
165
+ try {
166
+ const firstState = root.getValue().getState();
167
+ const unchangedSuggestion = root
168
+ .getValue()
169
+ .suggestion({ index: 1 })
170
+ .getState();
171
+
172
+ flushTapSync(() =>
173
+ setSuggestions([{ prompt: "Changed" }, { prompt: "Prompt B" }]),
174
+ );
175
+
176
+ expect(root.getValue().getState()).not.toBe(firstState);
177
+ expect(root.getValue().getState().suggestions[0]?.prompt).toBe("Changed");
178
+ expect(root.getValue().suggestion({ index: 1 }).getState()).toBe(
179
+ unchangedSuggestion,
180
+ );
181
+ } finally {
182
+ root.unmount();
183
+ }
184
+ });
185
+ });
@@ -1,7 +1,8 @@
1
- import { useMemo, useState } from "react";
1
+ import { useMemo } from "react";
2
2
  import { resource, withKey } from "@assistant-ui/tap";
3
3
  import type { ClientOutput } from "@assistant-ui/store";
4
4
  import { useClientLookup } from "@assistant-ui/store/client";
5
+ import { shallowEqual } from "@assistant-ui/store/internal";
5
6
  import type { SuggestionsState } from "../scopes/suggestions";
6
7
  import type { SuggestionState } from "../scopes/suggestion";
7
8
  import type { ThreadSuggestion } from "../../runtime/interfaces/thread-runtime-core";
@@ -10,6 +11,31 @@ export type SuggestionConfig =
10
11
  | string
11
12
  | { title: string; label: string; prompt: string };
12
13
 
14
+ const useStableSuggestionsState = (
15
+ next: SuggestionsState,
16
+ ): SuggestionsState => {
17
+ const cell = useMemo(() => ({}) as { state?: SuggestionsState }, []);
18
+ const previous = cell.state;
19
+
20
+ const suggestions = next.suggestions.map((suggestion, index) => {
21
+ const previousSuggestion = previous?.suggestions[index];
22
+ return previousSuggestion && shallowEqual(previousSuggestion, suggestion)
23
+ ? previousSuggestion
24
+ : suggestion;
25
+ });
26
+ const state =
27
+ previous &&
28
+ previous.suggestions.length === suggestions.length &&
29
+ suggestions.every(
30
+ (suggestion, index) => suggestion === previous.suggestions[index],
31
+ )
32
+ ? previous
33
+ : { suggestions };
34
+
35
+ cell.state = state;
36
+ return state;
37
+ };
38
+
13
39
  const useSuggestionClient = (
14
40
  state: SuggestionState,
15
41
  ): ClientOutput<"suggestion"> => {
@@ -21,8 +47,9 @@ const useSuggestionClient = (
21
47
  const SuggestionClient = resource(useSuggestionClient);
22
48
 
23
49
  const useSuggestionsClient = (
24
- state: SuggestionsState,
50
+ nextState: SuggestionsState,
25
51
  ): ClientOutput<"suggestions"> => {
52
+ const state = useStableSuggestionsState(nextState);
26
53
  const suggestionClients = useClientLookup(
27
54
  state.suggestions.map((suggestion, index) =>
28
55
  withKey(index, SuggestionClient(suggestion), [suggestion]),
@@ -40,28 +67,13 @@ const useSuggestionsClient = (
40
67
  const useStaticSuggestions = (
41
68
  suggestions?: SuggestionConfig[],
42
69
  ): ClientOutput<"suggestions"> => {
43
- const [state] = useState<SuggestionsState>(() => {
44
- const normalizedSuggestions = (suggestions ?? []).map((s) => {
45
- if (typeof s === "string") {
46
- return {
47
- title: s,
48
- label: "",
49
- prompt: s,
50
- };
51
- }
52
- return {
53
- title: s.title,
54
- label: s.label,
55
- prompt: s.prompt,
56
- };
57
- });
58
-
59
- return {
60
- suggestions: normalizedSuggestions,
61
- };
70
+ return useSuggestionsClient({
71
+ suggestions: (suggestions ?? []).map((s) =>
72
+ typeof s === "string"
73
+ ? { title: s, label: "", prompt: s }
74
+ : { title: s.title, label: s.label, prompt: s.prompt },
75
+ ),
62
76
  });
63
-
64
- return useSuggestionsClient(state);
65
77
  };
66
78
 
67
79
  export const Suggestions = resource(useStaticSuggestions);
@@ -69,18 +81,13 @@ export const Suggestions = resource(useStaticSuggestions);
69
81
  const useThreadSuggestions = (
70
82
  suggestions: readonly ThreadSuggestion[],
71
83
  ): ClientOutput<"suggestions"> => {
72
- const state = useMemo<SuggestionsState>(
73
- () => ({
74
- suggestions: suggestions.map((s) => ({
75
- title: s.title ?? s.prompt,
76
- label: s.label ?? "",
77
- prompt: s.prompt,
78
- })),
79
- }),
80
- [suggestions],
81
- );
82
-
83
- return useSuggestionsClient(state);
84
+ return useSuggestionsClient({
85
+ suggestions: suggestions.map((s) => ({
86
+ title: s.title ?? s.prompt,
87
+ label: s.label ?? "",
88
+ prompt: s.prompt,
89
+ })),
90
+ });
84
91
  };
85
92
 
86
93
  export const ThreadSuggestions = resource(useThreadSuggestions);
@@ -108,6 +108,10 @@ export {
108
108
  createRuntimeExtrasBrand,
109
109
  type RuntimeExtrasBrand,
110
110
  } from "../runtime/utils/runtime-extras-brand";
111
+ export {
112
+ resolveToolCallText,
113
+ type ToolCallText,
114
+ } from "../model-context/tool-call-text";
111
115
  export { defineToolkit } from "../react/model-context/define-toolkit";
112
116
  export {
113
117
  defineMcpToolkit,
@@ -15,7 +15,9 @@ export {
15
15
  composerCancelDisabled,
16
16
  composerInputDisabled,
17
17
  composerSendDisabled,
18
+ messageErrorText,
18
19
  suggestionTriggerDisabled,
20
+ threadListLoadMoreDisabled,
19
21
  } from "./primitive-predicates";
20
22
  export { isDevelopment } from "./env";
21
23
  export { useThreadSelectionEvents } from "./clients/thread-selection-events";
@@ -1,3 +1,7 @@
1
+ import type {
2
+ ReadonlyJSONArray,
3
+ ReadonlyJSONObject,
4
+ } from "assistant-stream/utils";
1
5
  import type { AssistantState } from "@assistant-ui/store";
2
6
 
3
7
  /**
@@ -41,3 +45,34 @@ export const suggestionTriggerDisabled = (
41
45
  ): boolean =>
42
46
  s.thread.isDisabled ||
43
47
  (send && s.thread.isRunning && !s.thread.capabilities.queue);
48
+
49
+ export const messageErrorText = (
50
+ s: AssistantState,
51
+ ):
52
+ | string
53
+ | number
54
+ | boolean
55
+ | ReadonlyJSONObject
56
+ | ReadonlyJSONArray
57
+ | undefined => {
58
+ if (
59
+ s.message.status?.type !== "incomplete" ||
60
+ s.message.status.reason !== "error"
61
+ ) {
62
+ return undefined;
63
+ }
64
+ const error = s.message.status.error;
65
+ if (typeof error === "string") return error;
66
+ if (
67
+ typeof error === "object" &&
68
+ error !== null &&
69
+ "message" in error &&
70
+ typeof error.message === "string"
71
+ ) {
72
+ return error.message;
73
+ }
74
+ return error ?? "An error occurred";
75
+ };
76
+
77
+ export const threadListLoadMoreDisabled = (s: AssistantState): boolean =>
78
+ !s.threads.hasMore || s.threads.isLoading || s.threads.isLoadingMore;
@@ -1,7 +1,11 @@
1
- import { useMemo } from "react";
1
+ import { useEffect, useMemo } from "react";
2
2
  import { useResource, withKey, resource } from "@assistant-ui/tap";
3
3
  import type { ClientOutput } from "@assistant-ui/store";
4
- import { useClientLookup, useClientResource } from "@assistant-ui/store/client";
4
+ import {
5
+ useAssistantEmit,
6
+ useClientLookup,
7
+ useClientResource,
8
+ } from "@assistant-ui/store/client";
5
9
  import { useThreadSelectionEvents } from "../clients/thread-selection-events";
6
10
  import type { ThreadListRuntime } from "../../runtime/api/thread-list-runtime";
7
11
  import type { AssistantRuntime } from "../../runtime/api/assistant-runtime";
@@ -44,6 +48,16 @@ const useThreadListClient = ({
44
48
  const runtimeState = useSubscribable(runtime);
45
49
  useThreadSelectionEvents(runtimeState.mainThreadId);
46
50
 
51
+ const emit = useAssistantEmit();
52
+ useEffect(
53
+ () =>
54
+ runtime.unstable_subscribeThreadEvents(({ threadId, type }) => {
55
+ if (threadId === runtime.getState().mainThreadId) return;
56
+ emit(`thread.${type}`, { threadId });
57
+ }),
58
+ [runtime, emit],
59
+ );
60
+
47
61
  const main = useClientResource(
48
62
  ThreadClient({
49
63
  runtime: runtime.main,
@@ -2,7 +2,13 @@ import { useSyncExternalStore } from "react";
2
2
  import type { SubscribableWithState } from "../../subscribable/subscribable";
3
3
 
4
4
  export const useSubscribable = <T>(
5
- subscribable: Omit<SubscribableWithState<T, any>, "path">,
5
+ subscribable: Omit<SubscribableWithState<T, any>, "path"> & {
6
+ getServerSnapshot?: () => T;
7
+ },
6
8
  ) => {
7
- return useSyncExternalStore(subscribable.subscribe, subscribable.getState);
9
+ return useSyncExternalStore(
10
+ subscribable.subscribe,
11
+ subscribable.getState,
12
+ subscribable.getServerSnapshot,
13
+ );
8
14
  };
@@ -19,7 +19,7 @@ export type MessageState = ThreadMessage & {
19
19
  * @example
20
20
  * ```ts
21
21
  * import { WebSpeechSynthesisAdapter } from "@assistant-ui/react";
22
- * import { useChatRuntime } from "@assistant-ui/react-ai-sdk";
22
+ * import { useChatRuntime } from "@assistant-ui/ai-sdk";
23
23
  *
24
24
  * const runtime = useChatRuntime({
25
25
  * adapters: {
@@ -29,10 +29,13 @@ export type PartMethods = {
29
29
  resumeToolCall(payload: unknown): void;
30
30
  /**
31
31
  * Respond to a server-side tool approval gate. The approval id is read from
32
- * the part. Accepts a boolean decision or the id of one of the approval's
33
- * options.
32
+ * the part. Accepts a boolean decision, the id of one of the approval's
33
+ * options, or a free-form answer when the request asks for one.
34
+ *
35
+ * Resolves once the runtime has accepted the response, and rejects when it
36
+ * could not be recorded, so a renderer can leave its controls retryable.
34
37
  */
35
- respondToToolApproval(response: ToolApprovalResponse): void;
38
+ respondToToolApproval(response: ToolApprovalResponse): Promise<void>;
36
39
  __internal_getRuntime?(): MessagePartRuntime;
37
40
  };
38
41
 
@@ -1,14 +1,7 @@
1
- import type { FileMessagePart, TextMessagePart } from "../../types/message";
2
1
  import type { QueuePlacement } from "../../runtime/queue/external-thread-queue-adapter";
2
+ import type { QueueItemState } from "../../runtime/queue/queue-item";
3
3
 
4
- export type QueueItemState = {
5
- readonly id: string;
6
- /** @deprecated Derive from the text parts of `parts` instead. Removal after 2026-11-05. */
7
- readonly prompt: string;
8
- readonly parts: readonly (FileMessagePart | TextMessagePart)[];
9
- };
10
-
11
- export const EMPTY_QUEUE_ITEMS: readonly QueueItemState[] = Object.freeze([]);
4
+ export type { QueueItemState };
12
5
 
13
6
  export type QueueItemMethods = {
14
7
  getState(): QueueItemState;
@@ -32,6 +32,30 @@ const createBinding = (initialState: TestState) => {
32
32
  };
33
33
  };
34
34
 
35
+ // Mirrors runtime bindings such as `getThreadListState`, which assemble a new
36
+ // object on every read while the underlying values keep their identity.
37
+ const createRebuildingBinding = (initialState: TestState) => {
38
+ let state = initialState;
39
+ const subscribers = new Set<() => void>();
40
+
41
+ const binding: SubscribableWithState<TestState, null> = {
42
+ path: null,
43
+ getState: () => ({ ...state }),
44
+ subscribe: (callback) => {
45
+ subscribers.add(callback);
46
+ return () => subscribers.delete(callback);
47
+ },
48
+ };
49
+
50
+ return {
51
+ binding,
52
+ update(nextState: TestState) {
53
+ state = nextState;
54
+ for (const callback of subscribers) callback();
55
+ },
56
+ };
57
+ };
58
+
35
59
  describe("ShallowMemoizeSubject", () => {
36
60
  it("notifies subscribers when a state key is removed", () => {
37
61
  const source = createBinding({
@@ -131,4 +155,30 @@ describe("LazyMemoizeSubject", () => {
131
155
  expect(subject.getState()).toEqual({ status: "ready" });
132
156
  reconnect();
133
157
  });
158
+
159
+ it("keeps one reference while unconnected and the state is unchanged", () => {
160
+ // A consumer that reads without subscribing (React's useSyncExternalStore
161
+ // calls getSnapshot outside the subscription) must not see a new object on
162
+ // every call: React compares snapshots with Object.is, so a fresh reference
163
+ // reads as a change and re-renders forever.
164
+ const source = createRebuildingBinding({ status: "ready" });
165
+ const subject = new LazyMemoizeSubject(source.binding);
166
+
167
+ const first = subject.getState();
168
+
169
+ expect(subject.getState()).toBe(first);
170
+ expect(subject.getState()).toBe(first);
171
+ });
172
+
173
+ it("returns a new reference once the unchanged state actually changes", () => {
174
+ const source = createRebuildingBinding({ status: "ready" });
175
+ const subject = new LazyMemoizeSubject(source.binding);
176
+
177
+ const first = subject.getState();
178
+ source.update({ status: "done" });
179
+
180
+ const second = subject.getState();
181
+ expect(second).not.toBe(first);
182
+ expect(second).toEqual({ status: "done" });
183
+ });
134
184
  });
@@ -1,3 +1,4 @@
1
+ import { shallowEqual } from "@assistant-ui/store/client";
1
2
  import type { Unsubscribe } from "../types/unsubscribe";
2
3
  import { notifyEventListeners } from "../utils/notify-event-listeners";
3
4
 
@@ -57,25 +58,10 @@ export const notifySubscribers = <TArgs extends unknown[]>(
57
58
  }
58
59
  };
59
60
 
60
- function shallowEqual<T extends object>(
61
- objA: T | undefined,
62
- objB: T | undefined,
63
- ) {
64
- if (objA === undefined && objB === undefined) return true;
65
- if (objA === undefined) return false;
66
- if (objB === undefined) return false;
67
-
68
- const keysA = Object.keys(objA);
69
- if (keysA.length !== Object.keys(objB).length) return false;
70
-
71
- for (const key of keysA) {
72
- const valueA = objA[key as keyof T];
73
- const valueB = objB[key as keyof T];
74
- if (!Object.is(valueA, valueB)) return false;
75
- }
76
-
77
- return true;
78
- }
61
+ const shallowEqualOrUndefined = <T extends object>(
62
+ a: T | undefined,
63
+ b: T | undefined,
64
+ ) => (a === undefined || b === undefined ? a === b : shallowEqual(a, b));
79
65
 
80
66
  export class BaseSubscribable {
81
67
  private _subscribers = new Set<() => void>();
@@ -99,6 +85,32 @@ export class BaseSubscribable {
99
85
  }
100
86
  }
101
87
 
88
+ export class WritableSubscribable<TState> extends BaseSubscribable {
89
+ private _state: TState;
90
+
91
+ constructor(state: TState) {
92
+ super();
93
+ this._state = state;
94
+ this.subscribe = this.subscribe.bind(this);
95
+ this.getState = this.getState.bind(this);
96
+ // Hydration has to agree with what the server rendered, so the server
97
+ // snapshot stays at the creation-time state rather than following writes.
98
+ this.getServerSnapshot = () => state;
99
+ }
100
+
101
+ public getState(): TState {
102
+ return this._state;
103
+ }
104
+
105
+ public getServerSnapshot: () => TState;
106
+
107
+ public setState(state: TState): void {
108
+ if (Object.is(state, this._state)) return;
109
+ this._state = state;
110
+ this._notifySubscribers();
111
+ }
112
+ }
113
+
102
114
  // lazy connect/disconnect: only opens upstream subscription while it has subscribers
103
115
  export abstract class BaseSubject {
104
116
  private _subscriptions = new Set<(payload?: unknown) => void>();
@@ -168,7 +180,7 @@ export class ShallowMemoizeSubject<TState extends object, TPath>
168
180
  private _syncState() {
169
181
  const state = this.binding.getState();
170
182
  if (state === SKIP_UPDATE) return false;
171
- if (shallowEqual(state, this._previousState)) return false;
183
+ if (shallowEqualOrUndefined(state, this._previousState)) return false;
172
184
  this._previousState = state;
173
185
  return true;
174
186
  }
@@ -209,7 +221,7 @@ export class LazyMemoizeSubject<TState extends object, TPath>
209
221
  if (
210
222
  newState !== SKIP_UPDATE &&
211
223
  (this._previousState === undefined ||
212
- !shallowEqual(newState, this._previousState))
224
+ !shallowEqualOrUndefined(newState, this._previousState))
213
225
  ) {
214
226
  this._previousState = newState;
215
227
  }
@@ -576,6 +576,83 @@ describe("MessageRepository", () => {
576
576
  });
577
577
  });
578
578
 
579
+ describe("ExportedMessageRepository auto status", () => {
580
+ const pendingToolCall: ThreadMessageLike = {
581
+ id: "a1",
582
+ role: "assistant",
583
+ content: [
584
+ {
585
+ type: "tool-call",
586
+ toolCallId: "t1",
587
+ toolName: "search",
588
+ args: {},
589
+ argsText: "{}",
590
+ approval: { id: "a1" },
591
+ },
592
+ ],
593
+ };
594
+
595
+ it("fromArray reports requires-action for an unresolved approval", () => {
596
+ const result = ExportedMessageRepository.fromArray([pendingToolCall]);
597
+ expect(result.messages[0]!.message.status).toMatchObject({
598
+ type: "requires-action",
599
+ reason: "interrupt",
600
+ });
601
+ });
602
+
603
+ it("fromBranchableArray reports requires-action for an unresolved approval", () => {
604
+ const result = ExportedMessageRepository.fromBranchableArray([
605
+ { message: pendingToolCall, parentId: null },
606
+ ]);
607
+ expect(result.messages[0]!.message.status).toMatchObject({
608
+ type: "requires-action",
609
+ reason: "interrupt",
610
+ });
611
+ });
612
+
613
+ it("fromArray reports requires-action with reason tool-calls for a resultless tool call", () => {
614
+ const result = ExportedMessageRepository.fromArray([
615
+ {
616
+ ...pendingToolCall,
617
+ content: [
618
+ {
619
+ type: "tool-call",
620
+ toolCallId: "t1",
621
+ toolName: "search",
622
+ args: {},
623
+ argsText: "{}",
624
+ },
625
+ ],
626
+ },
627
+ ]);
628
+ expect(result.messages[0]!.message.status).toMatchObject({
629
+ type: "requires-action",
630
+ reason: "tool-calls",
631
+ });
632
+ });
633
+
634
+ it("keeps complete for a resolved tool call", () => {
635
+ const result = ExportedMessageRepository.fromArray([
636
+ {
637
+ ...pendingToolCall,
638
+ content: [
639
+ {
640
+ type: "tool-call",
641
+ toolCallId: "t1",
642
+ toolName: "search",
643
+ args: {},
644
+ argsText: "{}",
645
+ result: "ok",
646
+ },
647
+ ],
648
+ },
649
+ ]);
650
+ expect(result.messages[0]!.message.status).toMatchObject({
651
+ type: "complete",
652
+ });
653
+ });
654
+ });
655
+
579
656
  describe("ExportedMessageRepository.fromBranchableArray", () => {
580
657
  it("should create a branching tree structure", () => {
581
658
  const items = [