@assistant-ui/core 0.3.16 → 0.3.18

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 (474) hide show
  1. package/dist/adapters/attachment.d.ts.map +1 -1
  2. package/dist/adapters/attachment.js +5 -4
  3. package/dist/adapters/attachment.js.map +1 -1
  4. package/dist/index.d.ts +3 -3
  5. package/dist/index.js +2 -2
  6. package/dist/index.js.map +1 -1
  7. package/dist/internal.d.ts +4 -2
  8. package/dist/internal.js +4 -2
  9. package/dist/model-context/frame/host.d.ts.map +1 -1
  10. package/dist/model-context/frame/host.js +10 -4
  11. package/dist/model-context/frame/host.js.map +1 -1
  12. package/dist/model-context/frame/provider.d.ts +5 -0
  13. package/dist/model-context/frame/provider.d.ts.map +1 -1
  14. package/dist/model-context/frame/provider.js +101 -28
  15. package/dist/model-context/frame/provider.js.map +1 -1
  16. package/dist/model-context/interactable-composer-metadata.d.ts.map +1 -1
  17. package/dist/model-context/interactable-composer-metadata.js +16 -9
  18. package/dist/model-context/interactable-composer-metadata.js.map +1 -1
  19. package/dist/model-context/registry.d.ts.map +1 -1
  20. package/dist/model-context/registry.js +3 -5
  21. package/dist/model-context/registry.js.map +1 -1
  22. package/dist/model-context/tool-call-text.d.ts +26 -0
  23. package/dist/model-context/tool-call-text.d.ts.map +1 -0
  24. package/dist/model-context/tool-call-text.js +18 -0
  25. package/dist/model-context/tool-call-text.js.map +1 -0
  26. package/dist/model-context/types.d.ts.map +1 -1
  27. package/dist/model-context/types.js +5 -4
  28. package/dist/model-context/types.js.map +1 -1
  29. package/dist/react/RuntimeAdapter.js +1 -1
  30. package/dist/react/adapters/LocalStorageThreadListAdapter.d.ts.map +1 -1
  31. package/dist/react/adapters/LocalStorageThreadListAdapter.js +1 -1
  32. package/dist/react/adapters/LocalStorageThreadListAdapter.js.map +1 -1
  33. package/dist/react/client/AdaptedRemoteThread.js +1 -1
  34. package/dist/react/client/InMemoryThreadList.d.ts.map +1 -1
  35. package/dist/react/client/InMemoryThreadList.js +63 -57
  36. package/dist/react/client/InMemoryThreadList.js.map +1 -1
  37. package/dist/react/client/Interactables.d.ts.map +1 -1
  38. package/dist/react/client/Interactables.js +25 -28
  39. package/dist/react/client/Interactables.js.map +1 -1
  40. package/dist/react/client/RemoteThreadList.d.ts +16 -0
  41. package/dist/react/client/RemoteThreadList.d.ts.map +1 -1
  42. package/dist/react/client/RemoteThreadList.js +354 -242
  43. package/dist/react/client/RemoteThreadList.js.map +1 -1
  44. package/dist/react/client/Tools.d.ts.map +1 -1
  45. package/dist/react/client/Tools.js +24 -18
  46. package/dist/react/client/Tools.js.map +1 -1
  47. package/dist/react/index.d.ts +2 -1
  48. package/dist/react/index.js +2 -1
  49. package/dist/react/interactables-legacy/Interactables.js +246 -125
  50. package/dist/react/interactables-legacy/Interactables.js.map +1 -1
  51. package/dist/react/interactables-shared/useInteractablePersistenceQueue.d.ts.map +1 -1
  52. package/dist/react/interactables-shared/useInteractablePersistenceQueue.js +11 -9
  53. package/dist/react/interactables-shared/useInteractablePersistenceQueue.js.map +1 -1
  54. package/dist/react/model-context/makeAssistantTool.d.ts +2 -2
  55. package/dist/react/model-context/makeAssistantTool.js +1 -1
  56. package/dist/react/model-context/makeAssistantTool.js.map +1 -1
  57. package/dist/react/model-context/toolbox.d.ts +2 -14
  58. package/dist/react/model-context/toolbox.d.ts.map +1 -1
  59. package/dist/react/model-context/toolbox.js +1 -13
  60. package/dist/react/model-context/toolbox.js.map +1 -1
  61. package/dist/react/model-context/useAssistantContext.js +37 -7
  62. package/dist/react/model-context/useAssistantContext.js.map +1 -1
  63. package/dist/react/model-context/useAssistantTool.d.ts +2 -2
  64. package/dist/react/model-context/useAssistantTool.js +1 -1
  65. package/dist/react/model-context/useAssistantTool.js.map +1 -1
  66. package/dist/react/model-context/useAuiToolOverrides.js +31 -5
  67. package/dist/react/model-context/useAuiToolOverrides.js.map +1 -1
  68. package/dist/react/model-context/useInlineRender.d.ts.map +1 -1
  69. package/dist/react/model-context/useInlineRender.js +10 -36
  70. package/dist/react/model-context/useInlineRender.js.map +1 -1
  71. package/dist/react/model-context/useToolArgsStatus.d.ts.map +1 -1
  72. package/dist/react/model-context/useToolArgsStatus.js +2 -1
  73. package/dist/react/model-context/useToolArgsStatus.js.map +1 -1
  74. package/dist/react/primitive-hooks/useMessageError.d.ts.map +1 -1
  75. package/dist/react/primitive-hooks/useMessageError.js +2 -8
  76. package/dist/react/primitive-hooks/useMessageError.js.map +1 -1
  77. package/dist/react/primitive-hooks/useThreadListLoadMore.d.ts.map +1 -1
  78. package/dist/react/primitive-hooks/useThreadListLoadMore.js +2 -4
  79. package/dist/react/primitive-hooks/useThreadListLoadMore.js.map +1 -1
  80. package/dist/react/primitives/composer/ComposerAttachments.d.ts.map +1 -1
  81. package/dist/react/primitives/composer/ComposerAttachments.js +4 -3
  82. package/dist/react/primitives/composer/ComposerAttachments.js.map +1 -1
  83. package/dist/react/primitives/composer/ComposerQueue.d.ts.map +1 -1
  84. package/dist/react/primitives/composer/ComposerQueue.js +4 -3
  85. package/dist/react/primitives/composer/ComposerQueue.js.map +1 -1
  86. package/dist/react/primitives/message/MessageAttachments.d.ts.map +1 -1
  87. package/dist/react/primitives/message/MessageAttachments.js +7 -6
  88. package/dist/react/primitives/message/MessageAttachments.js.map +1 -1
  89. package/dist/react/primitives/message/MessageGroupedParts.js +2 -2
  90. package/dist/react/primitives/message/MessageGroupedParts.js.map +1 -1
  91. package/dist/react/primitives/message/MessageParts.js +3 -3
  92. package/dist/react/primitives/message/MessageParts.js.map +1 -1
  93. package/dist/react/primitives/threadList/ThreadListItems.d.ts.map +1 -1
  94. package/dist/react/primitives/threadList/ThreadListItems.js +5 -4
  95. package/dist/react/primitives/threadList/ThreadListItems.js.map +1 -1
  96. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.d.ts +6 -4
  97. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.d.ts.map +1 -1
  98. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js +59 -29
  99. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js.map +1 -1
  100. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts +11 -6
  101. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts.map +1 -1
  102. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js +160 -143
  103. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js.map +1 -1
  104. package/dist/react/runtimes/RemoteThreadResource.d.ts +2 -6
  105. package/dist/react/runtimes/RemoteThreadResource.d.ts.map +1 -1
  106. package/dist/react/runtimes/RemoteThreadResource.js +15 -13
  107. package/dist/react/runtimes/RemoteThreadResource.js.map +1 -1
  108. package/dist/react/runtimes/assistant-transport/useLatestRef.js +2 -2
  109. package/dist/react/runtimes/assistant-transport/useLatestRef.js.map +1 -1
  110. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.d.ts +4 -1
  111. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.d.ts.map +1 -1
  112. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js +424 -52
  113. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js.map +1 -1
  114. package/dist/react/runtimes/cloud/auiV0.d.ts +5 -1
  115. package/dist/react/runtimes/cloud/auiV0.d.ts.map +1 -1
  116. package/dist/react/runtimes/cloud/auiV0.js.map +1 -1
  117. package/dist/react/runtimes/cloud/createCloudThreadListAdapter.d.ts +29 -0
  118. package/dist/react/runtimes/cloud/createCloudThreadListAdapter.d.ts.map +1 -0
  119. package/dist/react/runtimes/cloud/createCloudThreadListAdapter.js +160 -0
  120. package/dist/react/runtimes/cloud/createCloudThreadListAdapter.js.map +1 -0
  121. package/dist/react/runtimes/cloud/useCloudThreadListAdapter.d.ts +1 -9
  122. package/dist/react/runtimes/cloud/useCloudThreadListAdapter.d.ts.map +1 -1
  123. package/dist/react/runtimes/cloud/useCloudThreadListAdapter.js +16 -153
  124. package/dist/react/runtimes/cloud/useCloudThreadListAdapter.js.map +1 -1
  125. package/dist/react/runtimes/external-message-converter.js +1 -1
  126. package/dist/react/runtimes/external-message-converter.js.map +1 -1
  127. package/dist/react/runtimes/useExternalStoreRuntime.js +75 -46
  128. package/dist/react/runtimes/useExternalStoreRuntime.js.map +1 -1
  129. package/dist/react/runtimes/useRemoteThreadListRuntime.d.ts.map +1 -1
  130. package/dist/react/runtimes/useRemoteThreadListRuntime.js +77 -29
  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/message-part-runtime.d.ts +2 -2
  137. package/dist/runtime/api/message-part-runtime.d.ts.map +1 -1
  138. package/dist/runtime/api/message-part-runtime.js +1 -1
  139. package/dist/runtime/api/message-part-runtime.js.map +1 -1
  140. package/dist/runtime/api/thread-list-item-runtime.d.ts +7 -3
  141. package/dist/runtime/api/thread-list-item-runtime.d.ts.map +1 -1
  142. package/dist/runtime/api/thread-list-item-runtime.js +2 -2
  143. package/dist/runtime/api/thread-list-item-runtime.js.map +1 -1
  144. package/dist/runtime/api/thread-list-runtime.d.ts +12 -1
  145. package/dist/runtime/api/thread-list-runtime.d.ts.map +1 -1
  146. package/dist/runtime/api/thread-list-runtime.js +23 -14
  147. package/dist/runtime/api/thread-list-runtime.js.map +1 -1
  148. package/dist/runtime/api/thread-runtime.d.ts +5 -1
  149. package/dist/runtime/api/thread-runtime.d.ts.map +1 -1
  150. package/dist/runtime/api/thread-runtime.js +1 -1
  151. package/dist/runtime/api/thread-runtime.js.map +1 -1
  152. package/dist/runtime/base/base-composer-runtime-core.d.ts.map +1 -1
  153. package/dist/runtime/base/base-composer-runtime-core.js +16 -12
  154. package/dist/runtime/base/base-composer-runtime-core.js.map +1 -1
  155. package/dist/runtime/base/base-thread-runtime-core.d.ts +2 -2
  156. package/dist/runtime/base/base-thread-runtime-core.d.ts.map +1 -1
  157. package/dist/runtime/base/base-thread-runtime-core.js +8 -7
  158. package/dist/runtime/base/base-thread-runtime-core.js.map +1 -1
  159. package/dist/runtime/interfaces/composer-runtime-core.d.ts +12 -5
  160. package/dist/runtime/interfaces/composer-runtime-core.d.ts.map +1 -1
  161. package/dist/runtime/interfaces/thread-list-runtime-core.d.ts +18 -3
  162. package/dist/runtime/interfaces/thread-list-runtime-core.d.ts.map +1 -1
  163. package/dist/runtime/interfaces/thread-runtime-core.d.ts +15 -1
  164. package/dist/runtime/interfaces/thread-runtime-core.d.ts.map +1 -1
  165. package/dist/runtime/utils/auto-status.d.ts +1 -1
  166. package/dist/runtime/utils/auto-status.d.ts.map +1 -1
  167. package/dist/runtime/utils/auto-status.js +6 -2
  168. package/dist/runtime/utils/auto-status.js.map +1 -1
  169. package/dist/runtime/utils/external-message-conversion.d.ts +3 -2
  170. package/dist/runtime/utils/external-message-conversion.d.ts.map +1 -1
  171. package/dist/runtime/utils/external-message-conversion.js +5 -4
  172. package/dist/runtime/utils/external-message-conversion.js.map +1 -1
  173. package/dist/runtime/utils/message-repository.d.ts +1 -0
  174. package/dist/runtime/utils/message-repository.d.ts.map +1 -1
  175. package/dist/runtime/utils/message-repository.js +15 -6
  176. package/dist/runtime/utils/message-repository.js.map +1 -1
  177. package/dist/runtime/utils/resolveToolApprovalResponse.d.ts +6 -3
  178. package/dist/runtime/utils/resolveToolApprovalResponse.d.ts.map +1 -1
  179. package/dist/runtime/utils/resolveToolApprovalResponse.js +12 -3
  180. package/dist/runtime/utils/resolveToolApprovalResponse.js.map +1 -1
  181. package/dist/runtime/utils/thread-message-like.d.ts +2 -10
  182. package/dist/runtime/utils/thread-message-like.d.ts.map +1 -1
  183. package/dist/runtime/utils/thread-message-like.js +1 -1
  184. package/dist/runtime/utils/thread-message-like.js.map +1 -1
  185. package/dist/runtime/utils/tool-call-tree.d.ts +39 -0
  186. package/dist/runtime/utils/tool-call-tree.d.ts.map +1 -0
  187. package/dist/runtime/utils/tool-call-tree.js +85 -0
  188. package/dist/runtime/utils/tool-call-tree.js.map +1 -0
  189. package/dist/runtimes/external-store/external-store-adapter.d.ts +29 -2
  190. package/dist/runtimes/external-store/external-store-adapter.d.ts.map +1 -1
  191. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.d.ts.map +1 -1
  192. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.js +21 -27
  193. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.js.map +1 -1
  194. package/dist/runtimes/external-store/external-store-thread-runtime-core.d.ts +2 -1
  195. package/dist/runtimes/external-store/external-store-thread-runtime-core.d.ts.map +1 -1
  196. package/dist/runtimes/external-store/external-store-thread-runtime-core.js +34 -28
  197. package/dist/runtimes/external-store/external-store-thread-runtime-core.js.map +1 -1
  198. package/dist/runtimes/inert/InertThreadRuntimeCore.d.ts +1 -1
  199. package/dist/runtimes/inert/InertThreadRuntimeCore.d.ts.map +1 -1
  200. package/dist/runtimes/inert/InertThreadRuntimeCore.js.map +1 -1
  201. package/dist/runtimes/internal.d.ts +2 -2
  202. package/dist/runtimes/internal.js +2 -2
  203. package/dist/runtimes/local/local-thread-runtime-core.d.ts +1 -1
  204. package/dist/runtimes/local/local-thread-runtime-core.d.ts.map +1 -1
  205. package/dist/runtimes/local/local-thread-runtime-core.js +3 -1
  206. package/dist/runtimes/local/local-thread-runtime-core.js.map +1 -1
  207. package/dist/runtimes/remote-thread-list/remote-thread-state.d.ts +18 -1
  208. package/dist/runtimes/remote-thread-list/remote-thread-state.d.ts.map +1 -1
  209. package/dist/runtimes/remote-thread-list/remote-thread-state.js +148 -43
  210. package/dist/runtimes/remote-thread-list/remote-thread-state.js.map +1 -1
  211. package/dist/runtimes/remote-thread-list/title-generation.d.ts +49 -0
  212. package/dist/runtimes/remote-thread-list/title-generation.d.ts.map +1 -0
  213. package/dist/runtimes/remote-thread-list/title-generation.js +197 -0
  214. package/dist/runtimes/remote-thread-list/title-generation.js.map +1 -0
  215. package/dist/runtimes/remote-thread-list/title.d.ts +11 -0
  216. package/dist/runtimes/remote-thread-list/title.d.ts.map +1 -0
  217. package/dist/runtimes/remote-thread-list/title.js +14 -0
  218. package/dist/runtimes/remote-thread-list/title.js.map +1 -0
  219. package/dist/runtimes/remote-thread-list/types.d.ts +16 -5
  220. package/dist/runtimes/remote-thread-list/types.d.ts.map +1 -1
  221. package/dist/runtimes/tool-invocations/ToolInvocationTracker.d.ts +31 -8
  222. package/dist/runtimes/tool-invocations/ToolInvocationTracker.d.ts.map +1 -1
  223. package/dist/runtimes/tool-invocations/ToolInvocationTracker.js +97 -60
  224. package/dist/runtimes/tool-invocations/ToolInvocationTracker.js.map +1 -1
  225. package/dist/store/clients/external-thread.d.ts +1 -1
  226. package/dist/store/clients/external-thread.d.ts.map +1 -1
  227. package/dist/store/clients/external-thread.js +30 -7
  228. package/dist/store/clients/external-thread.js.map +1 -1
  229. package/dist/store/clients/model-context-client.d.ts.map +1 -1
  230. package/dist/store/clients/model-context-client.js +2 -1
  231. package/dist/store/clients/model-context-client.js.map +1 -1
  232. package/dist/store/clients/single-thread-list.d.ts.map +1 -1
  233. package/dist/store/clients/single-thread-list.js +2 -1
  234. package/dist/store/clients/single-thread-list.js.map +1 -1
  235. package/dist/store/clients/suggestions.d.ts.map +1 -1
  236. package/dist/store/clients/suggestions.js +19 -6
  237. package/dist/store/clients/suggestions.js.map +1 -1
  238. package/dist/store/clients/thread-message-client.js +1 -1
  239. package/dist/store/clients/thread-selection-events.js +1 -1
  240. package/dist/store/index.d.ts +2 -1
  241. package/dist/store/index.js +2 -1
  242. package/dist/store/internal.d.ts +2 -2
  243. package/dist/store/internal.js +2 -2
  244. package/dist/store/primitive-predicates.d.ts +4 -1
  245. package/dist/store/primitive-predicates.d.ts.map +1 -1
  246. package/dist/store/primitive-predicates.js +9 -1
  247. package/dist/store/primitive-predicates.js.map +1 -1
  248. package/dist/store/runtime-clients/composer-runtime-client.d.ts +1 -0
  249. package/dist/store/runtime-clients/composer-runtime-client.d.ts.map +1 -1
  250. package/dist/store/runtime-clients/composer-runtime-client.js +98 -62
  251. package/dist/store/runtime-clients/composer-runtime-client.js.map +1 -1
  252. package/dist/store/runtime-clients/message-runtime-client.d.ts +1 -0
  253. package/dist/store/runtime-clients/message-runtime-client.d.ts.map +1 -1
  254. package/dist/store/runtime-clients/message-runtime-client.js +221 -144
  255. package/dist/store/runtime-clients/message-runtime-client.js.map +1 -1
  256. package/dist/store/runtime-clients/thread-list-item-runtime-client.d.ts.map +1 -1
  257. package/dist/store/runtime-clients/thread-list-item-runtime-client.js +2 -2
  258. package/dist/store/runtime-clients/thread-list-item-runtime-client.js.map +1 -1
  259. package/dist/store/runtime-clients/thread-list-runtime-client.d.ts.map +1 -1
  260. package/dist/store/runtime-clients/thread-list-runtime-client.js +128 -107
  261. package/dist/store/runtime-clients/thread-list-runtime-client.js.map +1 -1
  262. package/dist/store/runtime-clients/thread-runtime-client.d.ts.map +1 -1
  263. package/dist/store/runtime-clients/thread-runtime-client.js +194 -122
  264. package/dist/store/runtime-clients/thread-runtime-client.js.map +1 -1
  265. package/dist/store/runtime-clients/useSubscribable.d.ts +3 -1
  266. package/dist/store/runtime-clients/useSubscribable.d.ts.map +1 -1
  267. package/dist/store/runtime-clients/useSubscribable.js +1 -1
  268. package/dist/store/runtime-clients/useSubscribable.js.map +1 -1
  269. package/dist/store/scopes/composer.d.ts +10 -2
  270. package/dist/store/scopes/composer.d.ts.map +1 -1
  271. package/dist/store/scopes/message.d.ts +30 -1
  272. package/dist/store/scopes/message.d.ts.map +1 -1
  273. package/dist/store/scopes/part.d.ts +6 -3
  274. package/dist/store/scopes/part.d.ts.map +1 -1
  275. package/dist/store/scopes/thread-list-item.d.ts +2 -2
  276. package/dist/store/scopes/thread-list-item.d.ts.map +1 -1
  277. package/dist/store/scopes/thread.d.ts +8 -0
  278. package/dist/store/scopes/thread.d.ts.map +1 -1
  279. package/dist/store/scopes/threads.d.ts +1 -0
  280. package/dist/store/scopes/threads.d.ts.map +1 -1
  281. package/dist/subscribable/subscribable.d.ts +8 -1
  282. package/dist/subscribable/subscribable.d.ts.map +1 -1
  283. package/dist/subscribable/subscribable.js +24 -16
  284. package/dist/subscribable/subscribable.js.map +1 -1
  285. package/dist/types/attachment.d.ts +2 -1
  286. package/dist/types/attachment.d.ts.map +1 -1
  287. package/dist/types/attachment.js +2 -1
  288. package/dist/types/attachment.js.map +1 -1
  289. package/dist/types/message.d.ts +40 -1
  290. package/dist/types/message.d.ts.map +1 -1
  291. package/dist/types/message.js +7 -1
  292. package/dist/types/message.js.map +1 -1
  293. package/dist/utils/abortable-iterable.d.ts +19 -0
  294. package/dist/utils/abortable-iterable.d.ts.map +1 -0
  295. package/dist/utils/abortable-iterable.js +72 -0
  296. package/dist/utils/abortable-iterable.js.map +1 -0
  297. package/dist/utils/composite-context-provider.d.ts.map +1 -1
  298. package/dist/utils/composite-context-provider.js +20 -2
  299. package/dist/utils/composite-context-provider.js.map +1 -1
  300. package/dist/utils/record.d.ts +11 -0
  301. package/dist/utils/record.d.ts.map +1 -0
  302. package/dist/utils/record.js +12 -0
  303. package/dist/utils/record.js.map +1 -0
  304. package/package.json +12 -17
  305. package/src/adapters/attachment.test.ts +50 -1
  306. package/src/adapters/attachment.ts +7 -4
  307. package/src/index.ts +7 -1
  308. package/src/internal.ts +12 -0
  309. package/src/model-context/frame/host.test.ts +24 -0
  310. package/src/model-context/frame/host.ts +10 -4
  311. package/src/model-context/frame/provider.test.ts +288 -1
  312. package/src/model-context/frame/provider.ts +148 -30
  313. package/src/model-context/interactable-composer-metadata.test.ts +28 -0
  314. package/src/model-context/interactable-composer-metadata.ts +9 -3
  315. package/src/model-context/registry.test.ts +12 -0
  316. package/src/model-context/registry.ts +3 -2
  317. package/src/model-context/tool-call-text.test.ts +67 -0
  318. package/src/model-context/tool-call-text.ts +67 -0
  319. package/src/model-context/types.test.ts +25 -0
  320. package/src/model-context/types.ts +9 -4
  321. package/src/react/AssistantRuntimeProvider.test.tsx +66 -2
  322. package/src/react/RuntimeAdapter.test.ts +34 -0
  323. package/src/react/adapters/LocalStorageThreadListAdapter.tsx +1 -3
  324. package/src/react/client/InMemoryThreadList.ts +1 -0
  325. package/src/react/client/Interactables.test.ts +28 -0
  326. package/src/react/client/Interactables.ts +15 -13
  327. package/src/react/client/RemoteThreadList.background.test.ts +526 -0
  328. package/src/react/client/RemoteThreadList.concurrent.test.tsx +315 -0
  329. package/src/react/client/RemoteThreadList.test.ts +617 -3
  330. package/src/react/client/RemoteThreadList.ts +433 -167
  331. package/src/react/client/Tools.ts +29 -12
  332. package/src/react/index.ts +4 -0
  333. package/src/react/interactables-legacy/Interactables.ts +16 -16
  334. package/src/react/interactables-shared/useInteractablePersistenceQueue.ts +9 -10
  335. package/src/react/model-context/makeAssistantTool.ts +2 -2
  336. package/src/react/model-context/modelContextHooks.test.tsx +131 -0
  337. package/src/react/model-context/toolbox.test.ts +9 -9
  338. package/src/react/model-context/toolbox.ts +9 -36
  339. package/src/react/model-context/useAssistantContext.ts +4 -2
  340. package/src/react/model-context/useAssistantTool.ts +2 -2
  341. package/src/react/model-context/useAuiToolOverrides.ts +4 -2
  342. package/src/react/model-context/useInlineRender.test.tsx +48 -0
  343. package/src/react/model-context/useInlineRender.ts +14 -10
  344. package/src/react/model-context/useToolArgsStatus.test.tsx +30 -0
  345. package/src/react/model-context/useToolArgsStatus.ts +2 -1
  346. package/src/react/primitive-hooks/useMessageError.ts +2 -19
  347. package/src/react/primitive-hooks/useThreadListLoadMore.ts +2 -3
  348. package/src/react/primitives/composer/ComposerAttachments.test.tsx +76 -0
  349. package/src/react/primitives/composer/ComposerAttachments.tsx +9 -4
  350. package/src/react/primitives/composer/ComposerQueue.test.tsx +72 -0
  351. package/src/react/primitives/composer/ComposerQueue.tsx +7 -4
  352. package/src/react/primitives/message/MessageAttachments.test.tsx +107 -37
  353. package/src/react/primitives/message/MessageAttachments.tsx +10 -7
  354. package/src/react/primitives/message/MessageGroupedParts.tsx +2 -2
  355. package/src/react/primitives/message/MessageParts.tsx +3 -3
  356. package/src/react/primitives/threadList/ThreadListItems.test.tsx +99 -0
  357. package/src/react/primitives/threadList/ThreadListItems.tsx +8 -5
  358. package/src/react/runtimes/RemoteThreadListHookInstanceManager.destroy.test.tsx +172 -0
  359. package/src/react/runtimes/RemoteThreadListHookInstanceManager.running.test.tsx +127 -1
  360. package/src/react/runtimes/RemoteThreadListHookInstanceManager.test.tsx +7 -1
  361. package/src/react/runtimes/RemoteThreadListHookInstanceManager.tsx +99 -35
  362. package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.load-error.test.ts +132 -0
  363. package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.switch-delete.test.ts +274 -0
  364. package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.tsx +223 -152
  365. package/src/react/runtimes/RemoteThreadResource.test.ts +9 -0
  366. package/src/react/runtimes/RemoteThreadResource.ts +20 -16
  367. package/src/react/runtimes/assistant-transport/transport-scheduling.test.ts +51 -2
  368. package/src/react/runtimes/assistant-transport/useLatestRef.ts +2 -2
  369. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.test.ts +45 -0
  370. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.test.tsx +546 -5
  371. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.ts +546 -103
  372. package/src/react/runtimes/cloud/auiV0.ts +5 -0
  373. package/src/react/runtimes/cloud/createCloudThreadListAdapter.test.ts +103 -0
  374. package/src/react/runtimes/cloud/createCloudThreadListAdapter.ts +237 -0
  375. package/src/react/runtimes/cloud/useCloudThreadListAdapter.test.tsx +90 -1
  376. package/src/react/runtimes/cloud/useCloudThreadListAdapter.tsx +33 -238
  377. package/src/react/runtimes/external-message-converter.ts +1 -0
  378. package/src/react/runtimes/useExternalStoreRuntime.lifecycle.test.tsx +41 -0
  379. package/src/react/runtimes/useExternalStoreRuntime.ts +10 -4
  380. package/src/react/runtimes/useLocalRuntime.test.tsx +20 -0
  381. package/src/react/runtimes/useRemoteThreadListRuntime.concurrent.test.tsx +216 -0
  382. package/src/react/runtimes/useRemoteThreadListRuntime.load-error.test.tsx +108 -0
  383. package/src/react/runtimes/useRemoteThreadListRuntime.ts +57 -6
  384. package/src/react/types/MessagePartComponentTypes.ts +7 -3
  385. package/src/react/types/scopes/tools.ts +2 -0
  386. package/src/runtime/api/message-part-runtime.ts +3 -3
  387. package/src/runtime/api/message-runtime.test.ts +75 -0
  388. package/src/runtime/api/thread-list-item-runtime.ts +10 -3
  389. package/src/runtime/api/thread-list-runtime.ts +46 -12
  390. package/src/runtime/api/thread-runtime.test.ts +78 -0
  391. package/src/runtime/api/thread-runtime.ts +8 -1
  392. package/src/runtime/base/base-composer-runtime-core.ts +36 -8
  393. package/src/runtime/base/base-thread-runtime-core.test.ts +149 -4
  394. package/src/runtime/base/base-thread-runtime-core.ts +16 -8
  395. package/src/runtime/interfaces/composer-runtime-core.ts +10 -5
  396. package/src/runtime/interfaces/thread-list-runtime-core.ts +25 -2
  397. package/src/runtime/interfaces/thread-runtime-core.ts +17 -1
  398. package/src/runtime/utils/auto-status.test.ts +57 -0
  399. package/src/runtime/utils/auto-status.ts +13 -1
  400. package/src/runtime/utils/external-message-conversion.test.ts +165 -0
  401. package/src/runtime/utils/external-message-conversion.ts +18 -2
  402. package/src/runtime/utils/message-repository.ts +29 -17
  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 +3 -11
  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 +18 -13
  410. package/src/runtimes/external-store/external-store-thread-runtime-core.ts +56 -33
  411. package/src/runtimes/inert/InertThreadRuntimeCore.ts +1 -1
  412. package/src/runtimes/internal.ts +1 -0
  413. package/src/runtimes/local/local-thread-runtime-core.test.ts +29 -3
  414. package/src/runtimes/local/local-thread-runtime-core.ts +5 -1
  415. package/src/runtimes/remote-thread-list/remote-thread-state.test.ts +238 -0
  416. package/src/runtimes/remote-thread-list/remote-thread-state.ts +223 -44
  417. package/src/runtimes/remote-thread-list/title-generation.test.ts +602 -0
  418. package/src/runtimes/remote-thread-list/title-generation.ts +329 -0
  419. package/src/runtimes/remote-thread-list/title.test.ts +62 -0
  420. package/src/runtimes/remote-thread-list/title.ts +16 -0
  421. package/src/runtimes/remote-thread-list/types.ts +16 -5
  422. package/src/runtimes/tool-invocations/EDGE_CASES.md +110 -5
  423. package/src/runtimes/tool-invocations/ToolInvocationTracker.test.ts +750 -136
  424. package/src/runtimes/tool-invocations/ToolInvocationTracker.ts +144 -91
  425. package/src/store/clients/external-thread.ts +62 -11
  426. package/src/store/clients/model-context-client.ts +5 -1
  427. package/src/store/clients/single-thread-list.ts +1 -0
  428. package/src/store/clients/suggestions.test.ts +195 -1
  429. package/src/store/clients/suggestions.ts +45 -34
  430. package/src/store/index.ts +4 -0
  431. package/src/store/internal.ts +2 -0
  432. package/src/store/primitive-predicates.ts +35 -0
  433. package/src/store/runtime-clients/composer-runtime-client.ts +42 -9
  434. package/src/store/runtime-clients/message-runtime-client.ts +54 -6
  435. package/src/store/runtime-clients/thread-list-item-runtime-client.ts +4 -2
  436. package/src/store/runtime-clients/thread-list-runtime-client.ts +17 -2
  437. package/src/store/runtime-clients/thread-runtime-client.ts +58 -10
  438. package/src/store/runtime-clients/useSubscribable.ts +8 -2
  439. package/src/store/scopes/composer.ts +16 -4
  440. package/src/store/scopes/message.ts +14 -0
  441. package/src/store/scopes/part.ts +6 -3
  442. package/src/store/scopes/thread-list-item.ts +5 -2
  443. package/src/store/scopes/thread.ts +4 -0
  444. package/src/store/scopes/threads.ts +1 -0
  445. package/src/subscribable/subscribable.test.ts +50 -0
  446. package/src/subscribable/subscribable.ts +33 -21
  447. package/src/tests/MessageRepository.test.ts +116 -0
  448. package/src/tests/OptimisticState-list-race.test.ts +1 -0
  449. package/src/tests/RemoteThreadListThreadListRuntimeCore-initialize-adapter-change.test.ts +38 -0
  450. package/src/tests/RemoteThreadListThreadListRuntimeCore-load-race.test.ts +158 -0
  451. package/src/tests/RemoteThreadListThreadListRuntimeCore-loadMore.test.ts +78 -0
  452. package/src/tests/RemoteThreadListThreadListRuntimeCore-reload.test.ts +52 -0
  453. package/src/tests/RemoteThreadListThreadListRuntimeCore-title-race.test.ts +89 -0
  454. package/src/tests/attachment-adapters.test.ts +14 -0
  455. package/src/tests/base-composer-runtime-core.test.ts +39 -0
  456. package/src/tests/external-store-optimistic-id.test.ts +79 -0
  457. package/src/tests/external-store-repository-instance.test.ts +166 -0
  458. package/src/tests/external-store-thread-list-runtime-core.test.ts +83 -0
  459. package/src/tests/external-store-thread-runtime-core-adapter.test.ts +49 -34
  460. package/src/tests/external-thread-attachments.test.tsx +224 -1
  461. package/src/tests/external-thread-parity.test.tsx +23 -0
  462. package/src/tests/remote-thread-list-adapter-switch.test.ts +66 -1
  463. package/src/tests/remote-thread-list-isLoading.test.ts +1 -0
  464. package/src/tests/thread-list-runtime-getLoadThreadsPromise.test.ts +1 -0
  465. package/src/tests/thread-switch-events.test.tsx +475 -2
  466. package/src/tests/tools-scope-migration.test.tsx +28 -0
  467. package/src/types/attachment.ts +4 -0
  468. package/src/types/message.ts +50 -2
  469. package/src/utils/abortable-iterable.test.ts +213 -0
  470. package/src/utils/abortable-iterable.ts +93 -0
  471. package/src/utils/composite-context-provider.test.ts +25 -0
  472. package/src/utils/composite-context-provider.ts +23 -2
  473. package/src/utils/record.test.ts +48 -0
  474. package/src/utils/record.ts +11 -0
@@ -0,0 +1,108 @@
1
+ // @vitest-environment jsdom
2
+
3
+ import { act, cleanup, render, waitFor } from "@testing-library/react";
4
+ import { afterEach, describe, expect, it, vi } from "vitest";
5
+ import type { AssistantRuntime } from "../../runtime/api/assistant-runtime";
6
+ import { makeAdapter } from "../../tests/remote-thread-list-test-helpers";
7
+ import { AssistantRuntimeProvider } from "../AssistantRuntimeProvider";
8
+ import { useExternalStoreRuntime } from "./useExternalStoreRuntime";
9
+ import { useRemoteThreadListRuntime } from "./useRemoteThreadListRuntime";
10
+
11
+ const EMPTY_MESSAGES: readonly never[] = [];
12
+
13
+ afterEach(() => {
14
+ cleanup();
15
+ vi.restoreAllMocks();
16
+ });
17
+
18
+ describe("useRemoteThreadListRuntime load recovery", () => {
19
+ it("reloads once when the browser comes online after a failed load", async () => {
20
+ const error = new Error("offline");
21
+ vi.spyOn(console, "error").mockImplementation(() => {});
22
+ const list = vi
23
+ .fn()
24
+ .mockRejectedValueOnce(error)
25
+ .mockResolvedValueOnce({ threads: [] });
26
+ const adapter = makeAdapter({ list });
27
+ const runtimeRef: { current: AssistantRuntime | null } = { current: null };
28
+ const useThreadRuntime = () =>
29
+ useExternalStoreRuntime({
30
+ messages: EMPTY_MESSAGES,
31
+ onNew: async () => {},
32
+ } as never);
33
+
34
+ const App = () => {
35
+ const runtime = useRemoteThreadListRuntime({
36
+ adapter,
37
+ runtimeHook: useThreadRuntime,
38
+ });
39
+ runtimeRef.current = runtime;
40
+ return (
41
+ <AssistantRuntimeProvider runtime={runtime}>
42
+ {null}
43
+ </AssistantRuntimeProvider>
44
+ );
45
+ };
46
+
47
+ render(<App />);
48
+ await waitFor(() => expect(list).toHaveBeenCalledTimes(1));
49
+ await waitFor(() =>
50
+ expect(runtimeRef.current!.threads.getState().loadError).toBe(error),
51
+ );
52
+
53
+ act(() => window.dispatchEvent(new Event("online")));
54
+
55
+ await waitFor(() => expect(list).toHaveBeenCalledTimes(2));
56
+ await act(async () => {});
57
+ expect(list).toHaveBeenCalledTimes(2);
58
+ expect(runtimeRef.current!.threads.getState().loadError).toBeUndefined();
59
+ });
60
+
61
+ it("reloads once when the page becomes visible after a failed load", async () => {
62
+ const error = new Error("offline");
63
+ vi.spyOn(console, "error").mockImplementation(() => {});
64
+ vi.spyOn(document, "visibilityState", "get").mockReturnValue("visible");
65
+ const list = vi
66
+ .fn()
67
+ .mockRejectedValueOnce(error)
68
+ .mockResolvedValueOnce({ threads: [] });
69
+ const adapter = makeAdapter({ list });
70
+ const runtimeRef: { current: AssistantRuntime | null } = { current: null };
71
+ const useThreadRuntime = () =>
72
+ useExternalStoreRuntime({
73
+ messages: EMPTY_MESSAGES,
74
+ onNew: async () => {},
75
+ } as never);
76
+
77
+ const App = () => {
78
+ const runtime = useRemoteThreadListRuntime({
79
+ adapter,
80
+ runtimeHook: useThreadRuntime,
81
+ });
82
+ runtimeRef.current = runtime;
83
+ return (
84
+ <AssistantRuntimeProvider runtime={runtime}>
85
+ {null}
86
+ </AssistantRuntimeProvider>
87
+ );
88
+ };
89
+
90
+ render(<App />);
91
+ await waitFor(() => expect(list).toHaveBeenCalledTimes(1));
92
+ await waitFor(() =>
93
+ expect(runtimeRef.current!.threads.getState().loadError).toBe(error),
94
+ );
95
+
96
+ act(() => document.dispatchEvent(new Event("visibilitychange")));
97
+
98
+ await waitFor(() => expect(list).toHaveBeenCalledTimes(2));
99
+ await waitFor(() =>
100
+ expect(runtimeRef.current!.threads.getState().loadError).toBeUndefined(),
101
+ );
102
+
103
+ act(() => document.dispatchEvent(new Event("visibilitychange")));
104
+ await act(async () => {});
105
+
106
+ expect(list).toHaveBeenCalledTimes(2);
107
+ });
108
+ });
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  useState,
3
3
  useEffect,
4
+ useInsertionEffect,
4
5
  useMemo,
5
6
  useRef,
6
7
  useCallback,
@@ -12,6 +13,8 @@ import type { RemoteThreadListOptions } from "../../runtimes/remote-thread-list/
12
13
  import type { AssistantRuntimeCore } from "../../runtime/interfaces/assistant-runtime-core";
13
14
  import type { AssistantRuntime } from "../../runtime/api/assistant-runtime";
14
15
  import { RemoteThreadListThreadListRuntimeCore } from "./RemoteThreadListThreadListRuntimeCore";
16
+ import { WritableSubscribable } from "../../subscribable/subscribable";
17
+ import { useSubscribable } from "../../store/runtime-clients/useSubscribable";
15
18
  import { useAui } from "@assistant-ui/store";
16
19
 
17
20
  class RemoteThreadListRuntimeCore
@@ -37,25 +40,73 @@ const useRemoteThreadListRuntimeImpl = (
37
40
  options: RemoteThreadListOptions,
38
41
  ): AssistantRuntime => {
39
42
  const [runtime] = useState(() => new RemoteThreadListRuntimeCore(options));
43
+
44
+ // Insertion-effect cleanup runs only when React deletes the fiber, so a
45
+ // hidden <Activity> or a re-suspended boundary keeps the threads alive; the
46
+ // disposal is deferred to a microtask because it notifies subscribers and
47
+ // React forbids scheduling updates from an insertion effect.
48
+ useInsertionEffect(
49
+ () => () => queueMicrotask(() => runtime.threads.__internal_dispose()),
50
+ [runtime],
51
+ );
52
+
40
53
  useEffect(() => {
41
54
  runtime.threads.__internal_setOptions(options);
42
55
  runtime.threads.__internal_load();
43
56
  }, [runtime, options]);
44
57
 
58
+ useEffect(() => {
59
+ if (typeof window === "undefined" || typeof document === "undefined") {
60
+ return;
61
+ }
62
+
63
+ const reloadAfterError = () => {
64
+ if (runtime.threads.loadError !== undefined) {
65
+ void runtime.threads.reload();
66
+ }
67
+ };
68
+ const reloadAfterVisible = () => {
69
+ if (document.visibilityState === "visible") reloadAfterError();
70
+ };
71
+
72
+ window.addEventListener("online", reloadAfterError);
73
+ document.addEventListener("visibilitychange", reloadAfterVisible);
74
+ return () => {
75
+ window.removeEventListener("online", reloadAfterError);
76
+ document.removeEventListener("visibilitychange", reloadAfterVisible);
77
+ };
78
+ }, [runtime]);
79
+
45
80
  return useMemo(() => new AssistantRuntimeImpl(runtime), [runtime]);
46
81
  };
47
82
 
48
83
  export const useRemoteThreadListRuntime = (
49
84
  options: RemoteThreadListOptions,
50
85
  ): AssistantRuntime => {
51
- const runtimeHookRef = useRef(options.runtimeHook);
52
- runtimeHookRef.current = options.runtimeHook;
86
+ const [runtimeHookStore] = useState(
87
+ () => new WritableSubscribable(options.runtimeHook),
88
+ );
89
+ useEffect(() => {
90
+ runtimeHookStore.setState(options.runtimeHook);
91
+ }, [runtimeHookStore, options.runtimeHook]);
53
92
 
54
93
  const initialThreadIdRef = useRef(options.initialThreadId);
55
94
 
56
- const stableRuntimeHook = useCallback(() => {
57
- return runtimeHookRef.current();
58
- }, []);
95
+ // Thread resources subscribe to the store rather than reading a ref, so a
96
+ // hook published at commit reaches exactly the resources that use it and an
97
+ // abandoned render publishes nothing. The store pins its server snapshot to
98
+ // the constructor value for hydration, which tap reads on any never-mounted
99
+ // fiber, so the live state serves as the server snapshot here.
100
+ const stableRuntimeHook = useCallback(
101
+ function useCommittedRuntimeHook() {
102
+ return useSubscribable({
103
+ subscribe: runtimeHookStore.subscribe,
104
+ getState: runtimeHookStore.getState,
105
+ getServerSnapshot: runtimeHookStore.getState,
106
+ })();
107
+ },
108
+ [runtimeHookStore],
109
+ );
59
110
 
60
111
  const onThreadIdChange = useEffectEvent((threadId: string | undefined) => {
61
112
  options.onThreadIdChange?.(threadId);
@@ -91,7 +142,7 @@ export const useRemoteThreadListRuntime = (
91
142
 
92
143
  // If allowNesting is true and already inside a thread list context,
93
144
  // just call the runtimeHook directly (no-op behavior)
94
- return stableRuntimeHook();
145
+ return options.runtimeHook();
95
146
  }
96
147
 
97
148
  const runtime = useRemoteThreadListRuntimeImpl(stableOptions);
@@ -83,10 +83,14 @@ export type ToolCallMessagePartProps<
83
83
  /**
84
84
  * Responds to a server-side tool approval gate. Only valid while
85
85
  * `approval` is set on the part, `approval.approved === undefined`, and
86
- * no `approval.resolution` is recorded. Accepts a boolean decision or the
87
- * id of one of `approval.options`; option kinds resolve to the boolean.
86
+ * no `approval.resolution` is recorded. Accepts a boolean decision, the
87
+ * id of one of `approval.options` (option kinds resolve to the boolean),
88
+ * or a free-form answer when the request accepts one.
89
+ *
90
+ * Resolves once the runtime has accepted the response, and rejects when
91
+ * it could not be recorded, so the controls can stay retryable.
88
92
  */
89
- respondToApproval: (response: ToolApprovalResponse) => void;
93
+ respondToApproval: (response: ToolApprovalResponse) => Promise<void>;
90
94
  };
91
95
 
92
96
  /** Component used to render a tool-call message part. */
@@ -1,5 +1,6 @@
1
1
  import type { ToolCallMessagePartComponent } from "../MessagePartComponentTypes";
2
2
  import type { Unsubscribe } from "../../..";
3
+ import type { ToolCallText } from "../../../model-context/tool-call-text";
3
4
 
4
5
  export type McpAppResourceOutput = {
5
6
  readonly render: ToolCallMessagePartComponent;
@@ -12,6 +13,7 @@ export type McpAppResourceOutput = {
12
13
  */
13
14
  type ToolRegistration = {
14
15
  readonly render: ToolCallMessagePartComponent;
16
+ readonly renderText?: ToolCallText<any, any, unknown> | undefined;
15
17
  /** Whether this UI renders standalone, outside the chain-of-thought trace. */
16
18
  readonly standalone: boolean;
17
19
  };
@@ -28,7 +28,7 @@ type MessagePartSnapshotBinding = SubscribableWithState<
28
28
  export type MessagePartRuntime = {
29
29
  addToolResult(result: any | ToolResponse<any>): void;
30
30
  resumeToolCall(payload: unknown): void;
31
- respondToToolApproval(response: ToolApprovalResponse): void;
31
+ respondToToolApproval(response: ToolApprovalResponse): Promise<void>;
32
32
 
33
33
  readonly path: MessagePartRuntimePath;
34
34
  getState(): MessagePartState;
@@ -113,7 +113,7 @@ export class MessagePartRuntimeImpl implements MessagePartRuntime {
113
113
  });
114
114
  }
115
115
 
116
- public respondToToolApproval(response: ToolApprovalResponse) {
116
+ public respondToToolApproval(response: ToolApprovalResponse): Promise<void> {
117
117
  const state = this.contentBinding.getState();
118
118
  if (!state) throw new Error("Message part is not available");
119
119
 
@@ -131,7 +131,7 @@ export class MessagePartRuntimeImpl implements MessagePartRuntime {
131
131
 
132
132
  if (!this.threadApi) throw new Error("Thread API is not available");
133
133
 
134
- this.threadApi
134
+ return this.threadApi
135
135
  .getState()
136
136
  .respondToToolApproval(
137
137
  resolveToolApprovalResponse(state.approval, response),
@@ -11,6 +11,7 @@ import {
11
11
  type MessageStateBinding,
12
12
  } from "./message-runtime";
13
13
  import { toMessagePartStatus } from "../../utils/normalizePartStatus";
14
+ import { convertExternalMessageChunk } from "../utils/external-message-conversion";
14
15
 
15
16
  const messagePath = {
16
17
  ref: "threads.main.messages[0]",
@@ -256,6 +257,80 @@ describe("toMessagePartStatus", () => {
256
257
  });
257
258
 
258
259
  describe("MessageRuntimeImpl paths", () => {
260
+ it.each([undefined, ""])(
261
+ "looks up separate tool calls with ID %s without breaking provider result matching",
262
+ (toolCallId) => {
263
+ const id = toolCallId === undefined ? {} : { toolCallId };
264
+ const converted = convertExternalMessageChunk(
265
+ {
266
+ inputs: [{}],
267
+ outputs: [
268
+ {
269
+ role: "assistant",
270
+ content: [
271
+ { type: "tool-call", ...id, toolName: "weather", args: {} },
272
+ { type: "tool-call", ...id, toolName: "search", args: {} },
273
+ {
274
+ type: "tool-call",
275
+ toolCallId: "provider-call",
276
+ toolName: "calendar",
277
+ args: { day: "Monday" },
278
+ },
279
+ ],
280
+ },
281
+ {
282
+ role: "assistant",
283
+ content: [
284
+ {
285
+ type: "tool-call",
286
+ toolCallId: "provider-call",
287
+ toolName: "calendar",
288
+ args: { day: "Tuesday" },
289
+ },
290
+ ],
291
+ },
292
+ {
293
+ role: "tool",
294
+ toolCallId: "provider-call",
295
+ toolName: "calendar",
296
+ result: "Meeting at noon",
297
+ },
298
+ ],
299
+ },
300
+ 0,
301
+ 1,
302
+ false,
303
+ undefined,
304
+ );
305
+ const runtime = new MessageRuntimeImpl(
306
+ {
307
+ ...messageBinding,
308
+ getState: () => ({ ...message, ...converted }),
309
+ },
310
+ threadBinding,
311
+ );
312
+ const calls = converted.content.filter(
313
+ (part) => part.type === "tool-call",
314
+ );
315
+
316
+ expect(
317
+ calls.map((call) =>
318
+ runtime.getMessagePartByToolCallId(call.toolCallId).getState(),
319
+ ),
320
+ ).toMatchObject([
321
+ { type: "tool-call", toolName: "weather" },
322
+ { type: "tool-call", toolName: "search" },
323
+ {
324
+ type: "tool-call",
325
+ toolCallId: "provider-call",
326
+ toolName: "calendar",
327
+ args: { day: "Tuesday" },
328
+ result: "Meeting at noon",
329
+ },
330
+ ]);
331
+ },
332
+ );
333
+
259
334
  it("appends nested selectors to the message path", () => {
260
335
  const runtime = new MessageRuntimeImpl(messageBinding, threadBinding);
261
336
 
@@ -30,12 +30,17 @@ import type { ThreadListItemStatus } from "../interfaces/thread-list-runtime-cor
30
30
 
31
31
  export type { ThreadListItemState, ThreadListItemStatus };
32
32
 
33
+ export type ThreadListItemGenerateTitleOptions = {
34
+ /** Marks a generation started by the automatic title trigger. */
35
+ automatic?: boolean;
36
+ };
37
+
33
38
  export type ThreadListItemRuntime = {
34
39
  readonly path: ThreadListItemRuntimePath;
35
40
  getState(): ThreadListItemState;
36
41
 
37
42
  initialize(): Promise<{ remoteId: string; externalId: string | undefined }>;
38
- generateTitle(): Promise<void>;
43
+ generateTitle(options?: ThreadListItemGenerateTitleOptions): Promise<void>;
39
44
 
40
45
  switchTo(options?: { unarchive?: boolean }): Promise<void>;
41
46
  rename(newTitle: string): Promise<void>;
@@ -147,9 +152,11 @@ export class ThreadListItemRuntimeImpl implements ThreadListItemRuntime {
147
152
  return this._threadListBinding.initialize(state.id);
148
153
  }
149
154
 
150
- public generateTitle(): Promise<void> {
155
+ public generateTitle(
156
+ options?: ThreadListItemGenerateTitleOptions,
157
+ ): Promise<void> {
151
158
  const state = this._core.getState();
152
- return this._threadListBinding.generateTitle(state.id);
159
+ return this._threadListBinding.generateTitle(state.id, options);
153
160
  }
154
161
 
155
162
  public unstable_on<E extends ThreadListItemEventType>(
@@ -7,11 +7,15 @@ import {
7
7
  SKIP_UPDATE,
8
8
  ShallowMemoizeSubject,
9
9
  } from "../../subscribable/subscribable";
10
- import type { ThreadListRuntimeCore } from "../interfaces/thread-list-runtime-core";
10
+ import type {
11
+ ThreadListRuntimeCore,
12
+ ThreadListRuntimeEvent,
13
+ } from "../interfaces/thread-list-runtime-core";
11
14
  import {
12
15
  type ThreadListItemRuntime,
13
16
  ThreadListItemRuntimeImpl,
14
17
  type ThreadListItemState,
18
+ type ThreadListItemStateBinding,
15
19
  } from "./thread-list-item-runtime";
16
20
  import {
17
21
  type ThreadListItemRuntimeBinding,
@@ -21,6 +25,7 @@ import {
21
25
  } from "./thread-runtime";
22
26
 
23
27
  const RESOLVED_PROMISE = Promise.resolve();
28
+ const NOOP_UNSUBSCRIBE = () => {};
24
29
 
25
30
  export type ThreadListState = {
26
31
  readonly mainThreadId: string;
@@ -28,6 +33,8 @@ export type ThreadListState = {
28
33
  readonly threadIds: readonly string[];
29
34
  readonly archivedThreadIds: readonly string[];
30
35
  readonly isLoading: boolean;
36
+ /** The error thrown by the most recent thread list load that failed, cleared when a later load starts. */
37
+ readonly loadError: unknown;
31
38
  readonly isLoadingMore: boolean;
32
39
  readonly hasMore: boolean;
33
40
  readonly threadItems: Readonly<
@@ -57,6 +64,16 @@ export type ThreadListRuntime = {
57
64
  ): Promise<void>;
58
65
  switchToNewThread(): Promise<void>;
59
66
 
67
+ /**
68
+ * Observes lifecycle events on every thread this list keeps alive, so an
69
+ * event that fires while its thread is not selected stays observable. Thread
70
+ * lists that mount only the main thread never emit; their main thread's
71
+ * runtime is observed directly.
72
+ */
73
+ unstable_subscribeThreadEvents(
74
+ callback: (event: ThreadListRuntimeEvent) => void,
75
+ ): Unsubscribe;
76
+
60
77
  getLoadThreadsPromise(): Promise<void>;
61
78
  reload(): Promise<void>;
62
79
  /**
@@ -83,6 +100,7 @@ const getThreadListState = (
83
100
  threadIds: threadList.threadIds,
84
101
  archivedThreadIds: threadList.archivedThreadIds,
85
102
  isLoading: threadList.isLoading,
103
+ loadError: threadList.loadError,
86
104
  isLoadingMore: threadList.isLoadingMore ?? false,
87
105
  hasMore: threadList.hasMore ?? false,
88
106
  threadItems: threadList.threadItems,
@@ -171,6 +189,8 @@ export class ThreadListRuntimeImpl implements ThreadListRuntime {
171
189
  protected __internal_bindMethods() {
172
190
  this.switchToThread = this.switchToThread.bind(this);
173
191
  this.switchToNewThread = this.switchToNewThread.bind(this);
192
+ this.unstable_subscribeThreadEvents =
193
+ this.unstable_subscribeThreadEvents.bind(this);
174
194
  this.getLoadThreadsPromise = this.getLoadThreadsPromise.bind(this);
175
195
  this.reload = this.reload.bind(this);
176
196
  this.reloadMainThread = this.reloadMainThread.bind(this);
@@ -194,6 +214,14 @@ export class ThreadListRuntimeImpl implements ThreadListRuntime {
194
214
  return this._core.switchToNewThread();
195
215
  }
196
216
 
217
+ public unstable_subscribeThreadEvents(
218
+ callback: (event: ThreadListRuntimeEvent) => void,
219
+ ): Unsubscribe {
220
+ return (
221
+ this._core.unstable_subscribeThreadEvents?.(callback) ?? NOOP_UNSUBSCRIBE
222
+ );
223
+ }
224
+
197
225
  public getLoadThreadsPromise(): Promise<void> {
198
226
  return this._core.getLoadThreadsPromise();
199
227
  }
@@ -226,6 +254,21 @@ export class ThreadListRuntimeImpl implements ThreadListRuntime {
226
254
  return this._mainThreadListItemRuntime;
227
255
  }
228
256
 
257
+ private _createItemStateBinding(
258
+ threadId: string,
259
+ ): ThreadListItemStateBinding {
260
+ return new ShallowMemoizeSubject({
261
+ path: {
262
+ ref: `threadItems[threadId=${threadId}]`,
263
+ threadSelector: { type: "threadId", threadId },
264
+ },
265
+ getState: () => {
266
+ return getThreadListItemState(this._core, threadId);
267
+ },
268
+ subscribe: (callback) => this._core.subscribe(callback),
269
+ });
270
+ }
271
+
229
272
  public getById(threadId: string) {
230
273
  return new this._runtimeFactory(
231
274
  new NestedSubscriptionSubject({
@@ -236,7 +279,7 @@ export class ThreadListRuntimeImpl implements ThreadListRuntime {
236
279
  getState: () => this._core.getThreadRuntimeCore(threadId),
237
280
  subscribe: (callback) => this._core.subscribe(callback),
238
281
  }),
239
- this.mainItem,
282
+ this._createItemStateBinding(threadId),
240
283
  );
241
284
  }
242
285
 
@@ -277,16 +320,7 @@ export class ThreadListRuntimeImpl implements ThreadListRuntime {
277
320
 
278
321
  public getItemById(threadId: string) {
279
322
  return new ThreadListItemRuntimeImpl(
280
- new ShallowMemoizeSubject({
281
- path: {
282
- ref: `threadItems[threadId=${threadId}]`,
283
- threadSelector: { type: "threadId", threadId },
284
- },
285
- getState: () => {
286
- return getThreadListItemState(this._core, threadId);
287
- },
288
- subscribe: (callback) => this._core.subscribe(callback),
289
- }),
323
+ this._createItemStateBinding(threadId),
290
324
  this._core,
291
325
  );
292
326
  }
@@ -0,0 +1,78 @@
1
+ import { describe, expect, it, vi } from "vitest";
2
+ import { LocalRuntimeCore } from "../../runtimes/local/local-runtime-core";
3
+ import { ExternalStoreRuntimeCore } from "../../runtimes/external-store/external-store-runtime-core";
4
+ import { AssistantRuntimeImpl } from "./assistant-runtime";
5
+
6
+ describe("ThreadRuntime.append", () => {
7
+ it.each([
8
+ { parent: { parentId: null }, expectedParent: null, visible: ["new"] },
9
+ {
10
+ parent: {},
11
+ expectedParent: "tail",
12
+ visible: ["root", "tail", "new"],
13
+ },
14
+ {
15
+ parent: { parentId: "root" },
16
+ expectedParent: "root",
17
+ visible: ["root", "new"],
18
+ },
19
+ ])(
20
+ "selects the branch under $expectedParent",
21
+ ({ parent, expectedParent, visible }) => {
22
+ const core = new LocalRuntimeCore(
23
+ { adapters: { chatModel: { run: async () => ({ content: [] }) } } },
24
+ [
25
+ { id: "root", role: "user", content: "root" },
26
+ { id: "tail", role: "assistant", content: "tail" },
27
+ ],
28
+ );
29
+ const thread = new AssistantRuntimeImpl(core).thread;
30
+
31
+ thread.append({
32
+ ...parent,
33
+ content: [{ type: "text", text: "new" }],
34
+ startRun: false,
35
+ });
36
+
37
+ expect(
38
+ thread.getState().messages.map((message) => message.content),
39
+ ).toEqual(visible.map((text) => [{ type: "text", text }]));
40
+ expect(thread.export().messages.at(-1)?.parentId).toBe(expectedParent);
41
+ },
42
+ );
43
+ });
44
+
45
+ describe("ThreadRuntime.append with an external store", () => {
46
+ it("routes an explicit root parent to onEdit instead of onNew", async () => {
47
+ const onNew = vi.fn(async () => {});
48
+ const onEdit = vi.fn(async () => {});
49
+ const core = new ExternalStoreRuntimeCore({
50
+ messages: [
51
+ {
52
+ id: "old",
53
+ role: "user",
54
+ content: [{ type: "text", text: "old" }],
55
+ createdAt: new Date(0),
56
+ attachments: [],
57
+ metadata: { custom: {} },
58
+ },
59
+ ],
60
+ onNew,
61
+ onEdit,
62
+ });
63
+ const thread = new AssistantRuntimeImpl(core).thread;
64
+
65
+ thread.append({
66
+ parentId: null,
67
+ content: [{ type: "text", text: "new root" }],
68
+ startRun: false,
69
+ });
70
+
71
+ await vi.waitFor(() =>
72
+ expect(onEdit).toHaveBeenCalledExactlyOnceWith(
73
+ expect.objectContaining({ parentId: null }),
74
+ ),
75
+ );
76
+ expect(onNew).not.toHaveBeenCalled();
77
+ });
78
+ });
@@ -77,6 +77,10 @@ const toStartRunConfig = (message: CreateStartRunConfig): StartRunConfig => {
77
77
  export type CreateAppendMessage =
78
78
  | string
79
79
  | {
80
+ /**
81
+ * An omitted value or `undefined` selects the current tail.
82
+ * `null` selects a root branch.
83
+ */
80
84
  parentId?: string | null | undefined;
81
85
  sourceId?: string | null | undefined;
82
86
  role?: AppendMessage["role"] | undefined;
@@ -107,7 +111,10 @@ const toAppendMessage = (
107
111
 
108
112
  return {
109
113
  createdAt: message.createdAt ?? new Date(),
110
- parentId: message.parentId ?? messages.at(-1)?.id ?? null,
114
+ parentId:
115
+ message.parentId === undefined
116
+ ? (messages.at(-1)?.id ?? null)
117
+ : message.parentId,
111
118
  sourceId: message.sourceId ?? null,
112
119
  role: message.role ?? "user",
113
120
  content: message.content,