@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,329 @@
1
+ export type ThreadTitleClaim = {
2
+ readonly title: string;
3
+ readonly order: number;
4
+ readonly settled: Promise<boolean>;
5
+ readonly settle: (renamed: boolean) => void;
6
+ };
7
+
8
+ type ThreadTitleGeneration = {
9
+ readonly automatic: boolean;
10
+ readonly order: number;
11
+ claim: ThreadTitleClaim | null;
12
+ beforeGenerationClaims: readonly ThreadTitleClaim[];
13
+ superseded: boolean;
14
+ readonly persisted: Promise<string | undefined>;
15
+ readonly settlePersisted: (title: string | undefined) => void;
16
+ };
17
+
18
+ export type ThreadTitleState = {
19
+ generations: Set<ThreadTitleGeneration>;
20
+ pendingClaim: ThreadTitleClaim | null;
21
+ inFlightClaims: Set<ThreadTitleClaim>;
22
+ manualTitle: string | undefined;
23
+ latestExplicit: ThreadTitleGeneration | null;
24
+ nextOrder: number;
25
+ };
26
+
27
+ export type ThreadTitleGenerationRun = {
28
+ states: Map<string, ThreadTitleState>;
29
+ threadId: string;
30
+ automatic: boolean;
31
+ generate: (
32
+ onTitle: (title: string | undefined) => Promise<void>,
33
+ ) => Promise<void>;
34
+ rename: (title: string) => Promise<void>;
35
+ applyTitle: (title: string | undefined) => Promise<void>;
36
+ };
37
+
38
+ function getThreadTitleState(
39
+ states: Map<string, ThreadTitleState>,
40
+ threadId: string,
41
+ ): ThreadTitleState {
42
+ let state = states.get(threadId);
43
+ if (state === undefined) {
44
+ state = {
45
+ generations: new Set(),
46
+ pendingClaim: null,
47
+ inFlightClaims: new Set(),
48
+ manualTitle: undefined,
49
+ latestExplicit: null,
50
+ nextOrder: 0,
51
+ };
52
+ states.set(threadId, state);
53
+ }
54
+ return state;
55
+ }
56
+
57
+ function pruneThreadTitleState(
58
+ states: Map<string, ThreadTitleState>,
59
+ threadId: string,
60
+ state: ThreadTitleState,
61
+ ): void {
62
+ if (
63
+ state.generations.size === 0 &&
64
+ state.pendingClaim === null &&
65
+ state.inFlightClaims.size === 0 &&
66
+ state.manualTitle === undefined &&
67
+ states.get(threadId) === state
68
+ ) {
69
+ states.delete(threadId);
70
+ }
71
+ }
72
+
73
+ function isCurrentGeneration(
74
+ state: ThreadTitleState,
75
+ generation: ThreadTitleGeneration,
76
+ ): boolean {
77
+ return (
78
+ !generation.superseded &&
79
+ (state.latestExplicit?.order ?? 0) <= generation.order
80
+ );
81
+ }
82
+
83
+ function isCurrentClaim(
84
+ state: ThreadTitleState,
85
+ claim: ThreadTitleClaim,
86
+ ): boolean {
87
+ return (state.latestExplicit?.order ?? 0) < claim.order;
88
+ }
89
+
90
+ export function startThreadTitleRename(
91
+ states: Map<string, ThreadTitleState>,
92
+ threadId: string,
93
+ title: string,
94
+ ): ThreadTitleClaim {
95
+ const state = getThreadTitleState(states, threadId);
96
+ let settle!: (renamed: boolean) => void;
97
+ const settled = new Promise<boolean>((resolve) => {
98
+ settle = resolve;
99
+ });
100
+ const claim: ThreadTitleClaim = {
101
+ title,
102
+ order: ++state.nextOrder,
103
+ settled,
104
+ settle,
105
+ };
106
+ state.pendingClaim = claim;
107
+ state.inFlightClaims.add(claim);
108
+ for (const generation of state.generations) {
109
+ if (generation.order < claim.order) generation.claim = claim;
110
+ }
111
+ return claim;
112
+ }
113
+
114
+ export function finishThreadTitleRename(
115
+ states: Map<string, ThreadTitleState>,
116
+ threadId: string,
117
+ claim: ThreadTitleClaim,
118
+ renamed: boolean,
119
+ ): void {
120
+ claim.settle(renamed);
121
+ const state = states.get(threadId);
122
+ if (state === undefined) return;
123
+ state.inFlightClaims.delete(claim);
124
+ if (state.pendingClaim === claim) {
125
+ state.pendingClaim = null;
126
+ if (renamed) {
127
+ state.manualTitle = claim.title;
128
+ }
129
+ }
130
+ pruneThreadTitleState(states, threadId, state);
131
+ }
132
+
133
+ function takeManualTitle(
134
+ states: Map<string, ThreadTitleState>,
135
+ threadId: string,
136
+ state: ThreadTitleState,
137
+ ): string | undefined {
138
+ const retained = state.manualTitle;
139
+ if (retained === undefined) return undefined;
140
+ state.manualTitle = undefined;
141
+ pruneThreadTitleState(states, threadId, state);
142
+ return retained;
143
+ }
144
+
145
+ export function clearThreadTitleState(
146
+ states: Map<string, ThreadTitleState>,
147
+ threadId: string,
148
+ ): void {
149
+ states.delete(threadId);
150
+ }
151
+
152
+ function startThreadTitleGeneration(
153
+ states: Map<string, ThreadTitleState>,
154
+ threadId: string,
155
+ automatic: boolean,
156
+ ): ThreadTitleGeneration | null {
157
+ const state = getThreadTitleState(states, threadId);
158
+ if (
159
+ automatic &&
160
+ state.pendingClaim === null &&
161
+ takeManualTitle(states, threadId, state) !== undefined
162
+ ) {
163
+ return null;
164
+ }
165
+
166
+ let settlePersisted!: (title: string | undefined) => void;
167
+ const persisted = new Promise<string | undefined>((resolve) => {
168
+ settlePersisted = resolve;
169
+ });
170
+ const generation: ThreadTitleGeneration = {
171
+ automatic,
172
+ order: ++state.nextOrder,
173
+ claim: automatic ? state.pendingClaim : null,
174
+ beforeGenerationClaims: automatic ? [] : [...state.inFlightClaims],
175
+ superseded: false,
176
+ persisted,
177
+ settlePersisted,
178
+ };
179
+ if (!automatic) {
180
+ state.pendingClaim = null;
181
+ state.manualTitle = undefined;
182
+ state.latestExplicit = generation;
183
+ for (const active of state.generations) {
184
+ if (active.automatic) active.superseded = true;
185
+ }
186
+ }
187
+ state.generations.add(generation);
188
+ return generation;
189
+ }
190
+
191
+ /**
192
+ * Runs one title generation under the per-thread rename claims and generation
193
+ * ordering, so the title that wins locally is also the title left on the
194
+ * server.
195
+ *
196
+ * A generated run persists the title itself, and the adapter exposes no
197
+ * compare-and-set, so the run that loses reasserts the winner through `rename`
198
+ * only after `generate` has resolved. Reasserting mid-stream would race the
199
+ * run's own write and lose the winning title on the server.
200
+ */
201
+ export async function runThreadTitleGeneration({
202
+ states,
203
+ threadId,
204
+ automatic,
205
+ generate,
206
+ rename,
207
+ applyTitle,
208
+ }: ThreadTitleGenerationRun): Promise<void> {
209
+ const state = getThreadTitleState(states, threadId);
210
+ const generation = startThreadTitleGeneration(states, threadId, automatic);
211
+ if (generation === null) return;
212
+
213
+ let persistedTitle: string | undefined;
214
+ let persistedOrder = generation.order;
215
+
216
+ const settleClaim = async (claim: ThreadTitleClaim) => {
217
+ const renamed = await claim.settled;
218
+ if (generation.claim !== claim) return undefined;
219
+ if (!renamed) {
220
+ generation.claim = null;
221
+ if (state.pendingClaim === claim) state.pendingClaim = null;
222
+ }
223
+ return renamed;
224
+ };
225
+
226
+ const retainManualTitle = () =>
227
+ automatic && takeManualTitle(states, threadId, state) !== undefined;
228
+
229
+ // `generate` resolves only once the run has persisted its title, so a run
230
+ // that lost the ordering race while it streamed has already overwritten the
231
+ // winner. It waits for the winning generation to persist its own title and
232
+ // writes that title back.
233
+ const repairLostRace = async () => {
234
+ if (persistedTitle === undefined) return;
235
+ while (true) {
236
+ const claim = generation.claim;
237
+ if (
238
+ claim !== null &&
239
+ claim.order > persistedOrder &&
240
+ isCurrentClaim(state, claim)
241
+ ) {
242
+ const renamed = await settleClaim(claim);
243
+ if (renamed === true) {
244
+ persistedTitle = claim.title;
245
+ persistedOrder = claim.order;
246
+ await rename(claim.title);
247
+ }
248
+ continue;
249
+ }
250
+ const winner = state.latestExplicit;
251
+ if (winner === null || winner.order <= persistedOrder) return;
252
+ const title = await winner.persisted;
253
+ if (state.latestExplicit !== winner) continue;
254
+ persistedOrder = winner.order;
255
+ if (title === undefined || title === persistedTitle) return;
256
+ persistedTitle = title;
257
+ await rename(title);
258
+ }
259
+ };
260
+
261
+ const runGeneration = async () => {
262
+ // An explicit generation waits for every earlier rename because any of
263
+ // those server writes may settle last.
264
+ if (generation.beforeGenerationClaims.length > 0) {
265
+ await Promise.all(
266
+ generation.beforeGenerationClaims.map((claim) => claim.settled),
267
+ );
268
+ }
269
+ if (generation.claim !== null) {
270
+ const renamed = await settleClaim(generation.claim);
271
+ if (renamed === true) {
272
+ generation.superseded = true;
273
+ return;
274
+ }
275
+ if (renamed === false && retainManualTitle()) return;
276
+ }
277
+ if (!isCurrentGeneration(state, generation)) return;
278
+
279
+ let sawTitle = false;
280
+ let lastTitle: string | undefined;
281
+ await generate(async (title) => {
282
+ sawTitle = true;
283
+ lastTitle = title;
284
+ if (title !== undefined) persistedTitle = title;
285
+ const claim = generation.claim;
286
+ if (claim !== null) {
287
+ const renamed = await settleClaim(claim);
288
+ if (renamed !== false) return;
289
+ if (retainManualTitle()) {
290
+ generation.superseded = true;
291
+ return;
292
+ }
293
+ }
294
+ if (isCurrentGeneration(state, generation)) await applyTitle(lastTitle);
295
+ });
296
+
297
+ while (true) {
298
+ const claim = generation.claim;
299
+ if (claim === null) return;
300
+ const renamed = await settleClaim(claim);
301
+ if (renamed === undefined) continue;
302
+ if (renamed === false) {
303
+ if (retainManualTitle()) return;
304
+ if (sawTitle && isCurrentGeneration(state, generation)) {
305
+ await applyTitle(lastTitle);
306
+ }
307
+ return;
308
+ }
309
+ if (!isCurrentClaim(state, claim)) return;
310
+ await rename(claim.title);
311
+ persistedTitle = claim.title;
312
+ persistedOrder = claim.order;
313
+ if (generation.claim !== claim) continue;
314
+ if (!isCurrentGeneration(state, generation)) return;
315
+ await applyTitle(claim.title);
316
+ generation.superseded = true;
317
+ return;
318
+ }
319
+ };
320
+
321
+ try {
322
+ await runGeneration();
323
+ await repairLostRace();
324
+ } finally {
325
+ generation.settlePersisted(persistedTitle);
326
+ state.generations.delete(generation);
327
+ pruneThreadTitleState(states, threadId, state);
328
+ }
329
+ }
@@ -0,0 +1,62 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { applyTitleStream } from "./title";
3
+
4
+ const createTextStream = (text: string) =>
5
+ new ReadableStream({
6
+ start(controller) {
7
+ controller.enqueue({
8
+ type: "part-start",
9
+ path: [0],
10
+ part: { type: "text" },
11
+ });
12
+ controller.enqueue({
13
+ type: "text-delta",
14
+ path: [0],
15
+ textDelta: text,
16
+ });
17
+ controller.enqueue({ type: "part-finish", path: [0] });
18
+ controller.close();
19
+ },
20
+ }) as never;
21
+
22
+ describe("applyTitleStream", () => {
23
+ it("ignores an empty stream", async () => {
24
+ const titles: (string | undefined)[] = [];
25
+
26
+ await applyTitleStream(
27
+ new ReadableStream({
28
+ start(controller) {
29
+ controller.close();
30
+ },
31
+ }) as never,
32
+ async (title) => {
33
+ titles.push(title);
34
+ },
35
+ );
36
+
37
+ expect(titles).toEqual([]);
38
+ });
39
+
40
+ it("ignores whitespace-only titles", async () => {
41
+ const titles: (string | undefined)[] = [];
42
+
43
+ await applyTitleStream(createTextStream(" "), async (title) => {
44
+ titles.push(title);
45
+ });
46
+
47
+ expect(titles).toEqual([]);
48
+ });
49
+
50
+ it("forwards titles containing text unchanged", async () => {
51
+ const titles: (string | undefined)[] = [];
52
+
53
+ await applyTitleStream(
54
+ createTextStream(" Generated title "),
55
+ async (title) => {
56
+ titles.push(title);
57
+ },
58
+ );
59
+
60
+ expect(titles).toContain(" Generated title ");
61
+ });
62
+ });
@@ -0,0 +1,16 @@
1
+ import { AssistantMessageStream } from "assistant-stream";
2
+
3
+ export const isTitleSourceMessage = (message: {
4
+ status?: { type: string } | undefined;
5
+ }) => message.status?.type !== "running";
6
+
7
+ export const applyTitleStream = async (
8
+ stream: Parameters<typeof AssistantMessageStream.fromAssistantStream>[0],
9
+ onTitle: (title: string | undefined) => Promise<void>,
10
+ ) => {
11
+ const messageStream = AssistantMessageStream.fromAssistantStream(stream);
12
+ for await (const result of messageStream) {
13
+ const title = result.parts.filter((part) => part.type === "text")[0]?.text;
14
+ if (title?.trim()) await onTitle(title);
15
+ }
16
+ };
@@ -3,6 +3,7 @@ import type { AssistantRuntime } from "../../runtime/api/assistant-runtime";
3
3
  import type { AssistantStream } from "assistant-stream";
4
4
  import type { ThreadHistoryAdapter } from "../../adapters/thread-history";
5
5
  import type { AttachmentAdapter } from "../../adapters/attachment";
6
+ import type { FeedbackAdapter } from "../../adapters/feedback";
6
7
  import type { ModelContextProvider } from "../../model-context/types";
7
8
 
8
9
  /* oxlint-disable typescript/no-explicit-any -- structural stand-in for ComponentType without depending on react types */
@@ -40,6 +41,7 @@ export type RuntimeAdapters = {
40
41
  modelContext?: ModelContextProvider | undefined;
41
42
  history?: ThreadHistoryAdapter | undefined;
42
43
  attachments?: AttachmentAdapter | undefined;
44
+ feedback?: FeedbackAdapter | undefined;
43
45
  };
44
46
 
45
47
  export type RemoteThreadListAdapter = {
@@ -54,6 +56,14 @@ export type RemoteThreadListAdapter = {
54
56
  unarchive(remoteId: string): Promise<void>;
55
57
  delete(remoteId: string): Promise<void>;
56
58
  initialize(threadId: string): Promise<RemoteThreadInitializeResponse>;
59
+ /**
60
+ * Generates a title for the thread and streams it back.
61
+ *
62
+ * When the generation persists the title itself, the returned stream must
63
+ * not complete before that write has landed. Concurrent generations are
64
+ * ordered by stream completion, and a run whose write outlives its stream
65
+ * can overwrite a newer title.
66
+ */
57
67
  generateTitle(
58
68
  remoteId: string,
59
69
  unstable_messages: readonly ThreadMessage[],
@@ -78,11 +88,12 @@ export type RemoteThreadListAdapter = {
78
88
  unstable_Provider?: RemoteThreadListProviderComponent | undefined;
79
89
 
80
90
  /**
81
- * Hook the `RemoteThreadList` store entry calls once for the main-thread
82
- * slot, then provides to the `thread` factory. This is not mounted per
83
- * listed thread. `useRemoteThreadListRuntime` also calls it when
84
- * `unstable_Provider` is omitted. Resolve `threadListItem` lazily on each
85
- * adapter call; do not capture it at hook mount. The hook must keep a
91
+ * Hook the `RemoteThreadList` store entry calls once per mounted thread
92
+ * body (the main-thread slot by default; every started thread with
93
+ * `backgroundThreads`), then provides to the `thread` factory. This is not
94
+ * mounted per listed thread. `useRemoteThreadListRuntime` also calls it
95
+ * when `unstable_Provider` is omitted. Resolve `threadListItem` lazily on
96
+ * each adapter call; do not capture it at hook mount. The hook must keep a
86
97
  * stable hook count across adapter swaps; a different count throws.
87
98
  * Memoize the returned object.
88
99
  *
@@ -11,8 +11,9 @@ observe via `setState(snapshot)` and what the current behavior is.
11
11
  > result is replaced, result is cleared, key order shuffles — the tracker
12
12
  > never invokes the host's tool callback a second time.
13
13
  >
14
- > `reset()` starts a new execution boundary. A reused `toolCallId` in the
15
- > next boundary gets a new execution identity and may fire once there.
14
+ > `reset()` starts a new execution boundary, and so does the single
15
+ > pipeline restart after a stream failure (F.4). A reused `toolCallId` in
16
+ > the next boundary gets a new execution identity and may fire once there.
16
17
 
17
18
  This guarantees host-side side effects (the typical reason `streamCall` /
18
19
  `execute` exists at all) can't double-run. The cost: post-completion
@@ -92,8 +93,102 @@ in-flight `execute` resolves, the execute runs to completion (its side
92
93
  effects happen) but its result chunk is dropped: `onResult` never fires,
93
94
  and the `executing` status, plus any `human()` interrupt the execution
94
95
  parked, stays up until the promise settles. Only a gate that lands after
95
- the result chunk has already been emitted is fully too late; the adapter
96
- must project the gate before that (#6285).
96
+ the result chunk has already been emitted is fully too late.
97
+
98
+ A call the adapter reports as client-owned reaches that window while the
99
+ run is still open; a call whose ownership is unknown reaches it when the
100
+ run settles before the gate arrives (A.10). Either way the client was
101
+ meant to run it, so nothing but the gate's arrival says otherwise and the
102
+ gate is late by construction.
103
+
104
+ ### A.9. Adapter reports the tool call as provider-owned
105
+ The entry is marked `skipExecute` at creation, exactly like a call
106
+ observed with a `result`. `unstable_isClientToolCall` is read once, when
107
+ the call is first observed live, and never re-read: ownership is fixed
108
+ when the provider emits the call, so a later snapshot cannot revoke it
109
+ and drop the result of an execute already in flight. `streamCall` still
110
+ fires once, as under A.8; only the wrapped `execute` and its result
111
+ chunk are withheld.
112
+
113
+ Outside production, a provider-owned call whose name resolves to a tool
114
+ with an `execute` logs a `console.warn`. The two together are a
115
+ misconfiguration: the provider answers the call, so the registered
116
+ `execute` would never run and the skip is otherwise silent.
117
+
118
+ The predicate is also what licenses running a frontend tool before the
119
+ provider's run ends, so an adapter that supplies it keeps the overlap
120
+ between a client tool's work and the run tail (A.10).
121
+
122
+ ### A.10. Args complete while the provider's run is still open
123
+ Closing the args stream hands the call to the client executor, so it may
124
+ only happen once the provider can no longer speak about that call. A
125
+ provider may still answer the call (A.5) or gate it (A.8) one or more
126
+ snapshots after its arguments complete, and a protocol can carry the
127
+ outcome no earlier: AG-UI projects an interrupt only from `RUN_FINISHED`.
128
+ The run ending is the only universal signal, so a call whose ownership is
129
+ unknown closes when the snapshot's `isRunning` goes false.
130
+
131
+ `ExternalStoreThreadRuntimeCore` feeds the tracker
132
+ `store.isRunning || _hasExecutingTools(store)`, so that condition also covers
133
+ a client tool mid-`execute`. That defers an unknown-ownership sibling only
134
+ while a client-owned call is actually executing, which no shipped adapter
135
+ produces: every entry pending in a snapshot closes in the same
136
+ `_processMessages` pass, an adapter that supplies the predicate classifies
137
+ every call, and an execution parked on `human()` reports `interrupt` rather
138
+ than `executing`, which `_hasExecutingTools` does not count.
139
+
140
+ That holds even when `execute` opens with `await human()`. The stream
141
+ fires `onExecutionStart` only after `execute` returns, because until then
142
+ it cannot tell a frontend tool from one without an `execute`, so by the
143
+ time the callback runs the body has already parked its resolver and set
144
+ `interrupt`. `_onExecutionStart` still registers the execution for
145
+ `_onExecutionEnd` but leaves the status alone when this execution's own
146
+ request is pending. The check is keyed on the request's execution id,
147
+ not on the status map: a pipeline restart (F.4) clears `_executing` but
148
+ keeps the status map, and a stale `interrupt` must not stop the fresh
149
+ execution from reporting `executing` (#6763).
150
+
151
+ A call the adapter reports as client-owned (A.9) closes as soon as its
152
+ arguments parse, because the adapter has already said the provider will
153
+ not answer it. Ownership says who answers a call, not whether it is
154
+ gated, so a gate on such a call is still late and A.8 governs it
155
+ (#6677).
156
+
157
+ ### A.11. The turn is discarded while a call waits on the run
158
+ `abort({ discardPending: true })` records every active entry that has
159
+ neither closed its args stream nor holds a result in
160
+ `_discardedToolCallIds`, and marks it `skipExecute`.
161
+
162
+ The abort signal alone does not cover these. A call waiting on the run to
163
+ settle (A.10) has not reached the executor, so there is nothing to signal,
164
+ and `abort()` installs a fresh `AbortController` before the settled
165
+ snapshot arrives; without the record, cancelling a run would be what
166
+ starts the call it was meant to stop.
167
+
168
+ `discardPending` is the caller's claim that the turn is over, not that it
169
+ is being interrupted, so only the three callers that end it pass it: a new
170
+ turn starting, a reload, and `cancelRun`. `deleteMessage` does not: its
171
+ `setMessages` fallback aborts while the provider run continues, and
172
+ discarding there would strand that run's remaining calls result-less,
173
+ while its `onDelete` path never aborts at all. `reset()` is unaffected
174
+ either way: it clears `_entries` and the recorded ids before aborting,
175
+ because it opens a new execution boundary.
176
+
177
+ An id is forgotten once a live snapshot observes the call with a result,
178
+ which bounds the set to the open calls of a discarded turn. That is all
179
+ it does: an id re-emitted inside the same execution boundary keeps its
180
+ existing entry, so A.4 and A.7 govern it rather than this set, and a new
181
+ boundary clears the set outright. The residual is a call that is
182
+ discarded, answered, and then loses its result (A.7) across a pipeline
183
+ restart; it is left to A.7 rather than pinned here, because holding the
184
+ id past the answer would trade a five-step chain for a set that grows for
185
+ the life of the tracker.
186
+
187
+ The record lives on the tracker rather than the entry because it is the one
188
+ reason to skip that no later snapshot carries: an `approval` is re-read at
189
+ each snapshot and provider ownership is recomputed, while a rebuilt entry
190
+ would otherwise come back clean. That is what lets F.4 drop a waiting entry
191
+ without a special case.
97
192
 
98
193
  ## B. Tool call disappears from snapshot
99
194
 
@@ -193,7 +288,17 @@ The `.pipeTo(...).catch(...)` handler logs and flips `_pipelineDead`.
193
288
  The next `setState` call recreates the pipeline once per tracker
194
289
  lifetime: existing active entries are *demoted to restored* (so the
195
290
  rebuilt pipeline does not re-fire `streamCall` for them) and the
196
- snapshot is processed against the fresh pipeline. Repeated failures
291
+ snapshot is processed against the fresh pipeline. An active entry that
292
+ neither closed its args stream nor holds a result is dropped instead of
293
+ demoted: a restored entry is promoted only when its signature changes,
294
+ and a call waiting on the run to settle (A.10) already holds its final
295
+ args, so demoting it would strand it unexecuted.
296
+
297
+ Starting it over re-fires `streamCall`, which the restart path already
298
+ does for any demoted entry whose signature later changes. The rebuilt
299
+ pipeline holds no part for the call, so nothing can reach the executor
300
+ without adding one; a restart is an execution boundary in the same sense
301
+ `reset()` is. Repeated failures
197
302
  keep the tracker dead with a visible error to avoid restart loops.
198
303
 
199
304
  ### F.5. Reset followed by same-id reuse