@assistant-ui/core 0.3.17 → 0.3.19

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 (470) 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/adapters/speech.d.ts.map +1 -1
  5. package/dist/adapters/speech.js +13 -3
  6. package/dist/adapters/speech.js.map +1 -1
  7. package/dist/adapters/thread-history.d.ts +4 -0
  8. package/dist/adapters/thread-history.d.ts.map +1 -1
  9. package/dist/index.d.ts +3 -3
  10. package/dist/index.js.map +1 -1
  11. package/dist/internal.d.ts +4 -3
  12. package/dist/internal.js +3 -2
  13. package/dist/model-context/frame/host.d.ts.map +1 -1
  14. package/dist/model-context/frame/host.js +35 -14
  15. package/dist/model-context/frame/host.js.map +1 -1
  16. package/dist/model-context/frame/provider.d.ts +2 -0
  17. package/dist/model-context/frame/provider.d.ts.map +1 -1
  18. package/dist/model-context/frame/provider.js +30 -7
  19. package/dist/model-context/frame/provider.js.map +1 -1
  20. package/dist/model-context/interactable-composer-metadata.d.ts.map +1 -1
  21. package/dist/model-context/interactable-composer-metadata.js +27 -18
  22. package/dist/model-context/interactable-composer-metadata.js.map +1 -1
  23. package/dist/model-context/registry.d.ts.map +1 -1
  24. package/dist/model-context/registry.js +3 -5
  25. package/dist/model-context/registry.js.map +1 -1
  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/adapters/LocalStorageThreadListAdapter.d.ts.map +1 -1
  30. package/dist/react/adapters/LocalStorageThreadListAdapter.js +45 -7
  31. package/dist/react/adapters/LocalStorageThreadListAdapter.js.map +1 -1
  32. package/dist/react/client/DataRenderers.d.ts.map +1 -1
  33. package/dist/react/client/DataRenderers.js +20 -15
  34. package/dist/react/client/DataRenderers.js.map +1 -1
  35. package/dist/react/client/InMemoryThreadList.d.ts.map +1 -1
  36. package/dist/react/client/InMemoryThreadList.js +62 -56
  37. package/dist/react/client/InMemoryThreadList.js.map +1 -1
  38. package/dist/react/client/Interactables.d.ts.map +1 -1
  39. package/dist/react/client/Interactables.js +35 -32
  40. package/dist/react/client/Interactables.js.map +1 -1
  41. package/dist/react/client/RemoteThreadList.d.ts +1 -0
  42. package/dist/react/client/RemoteThreadList.d.ts.map +1 -1
  43. package/dist/react/client/RemoteThreadList.js +154 -143
  44. package/dist/react/client/RemoteThreadList.js.map +1 -1
  45. package/dist/react/client/Tools.d.ts.map +1 -1
  46. package/dist/react/client/Tools.js +18 -16
  47. package/dist/react/client/Tools.js.map +1 -1
  48. package/dist/react/client/interactable-model-context.d.ts.map +1 -1
  49. package/dist/react/client/interactable-model-context.js +3 -2
  50. package/dist/react/client/interactable-model-context.js.map +1 -1
  51. package/dist/react/index.d.ts +2 -2
  52. package/dist/react/index.js +2 -2
  53. package/dist/react/interactables-legacy/Interactables.js +242 -121
  54. package/dist/react/interactables-legacy/Interactables.js.map +1 -1
  55. package/dist/react/interactables-legacy/useAssistantInteractable.d.ts.map +1 -1
  56. package/dist/react/interactables-legacy/useAssistantInteractable.js +3 -0
  57. package/dist/react/interactables-legacy/useAssistantInteractable.js.map +1 -1
  58. package/dist/react/interactables-shared/useInteractablePersistenceQueue.d.ts.map +1 -1
  59. package/dist/react/interactables-shared/useInteractablePersistenceQueue.js +11 -9
  60. package/dist/react/interactables-shared/useInteractablePersistenceQueue.js.map +1 -1
  61. package/dist/react/model-context/makeAssistantTool.d.ts +2 -2
  62. package/dist/react/model-context/makeAssistantTool.js +1 -1
  63. package/dist/react/model-context/makeAssistantTool.js.map +1 -1
  64. package/dist/react/model-context/useAssistantTool.d.ts +2 -2
  65. package/dist/react/model-context/useAssistantTool.js +1 -1
  66. package/dist/react/model-context/useAssistantTool.js.map +1 -1
  67. package/dist/react/model-context/useInteractable.d.ts.map +1 -1
  68. package/dist/react/model-context/useInteractable.js +4 -1
  69. package/dist/react/model-context/useInteractable.js.map +1 -1
  70. package/dist/react/model-context/useInteractableVersions.js +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/primitives/message/MessageGroupedParts.d.ts +11 -2
  75. package/dist/react/primitives/message/MessageGroupedParts.d.ts.map +1 -1
  76. package/dist/react/primitives/message/MessageGroupedParts.js +3 -2
  77. package/dist/react/primitives/message/MessageGroupedParts.js.map +1 -1
  78. package/dist/react/primitives/part/PartMessages.d.ts +1 -0
  79. package/dist/react/primitives/part/PartMessages.d.ts.map +1 -1
  80. package/dist/react/primitives/part/PartMessages.js +1 -0
  81. package/dist/react/primitives/part/PartMessages.js.map +1 -1
  82. package/dist/react/primitives/thread/ThreadMessages.d.ts.map +1 -1
  83. package/dist/react/primitives/thread/ThreadMessages.js +6 -5
  84. package/dist/react/primitives/thread/ThreadMessages.js.map +1 -1
  85. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.d.ts.map +1 -1
  86. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js +16 -8
  87. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js.map +1 -1
  88. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts +5 -1
  89. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts.map +1 -1
  90. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js +105 -104
  91. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js.map +1 -1
  92. package/dist/react/runtimes/RemoteThreadResource.d.ts +1 -0
  93. package/dist/react/runtimes/RemoteThreadResource.d.ts.map +1 -1
  94. package/dist/react/runtimes/RemoteThreadResource.js +13 -11
  95. package/dist/react/runtimes/RemoteThreadResource.js.map +1 -1
  96. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.d.ts +5 -24
  97. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.d.ts.map +1 -1
  98. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js +354 -226
  99. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js.map +1 -1
  100. package/dist/react/runtimes/cloud/CloudFileAttachmentAdapter.d.ts +1 -0
  101. package/dist/react/runtimes/cloud/CloudFileAttachmentAdapter.d.ts.map +1 -1
  102. package/dist/react/runtimes/cloud/CloudFileAttachmentAdapter.js +22 -2
  103. package/dist/react/runtimes/cloud/CloudFileAttachmentAdapter.js.map +1 -1
  104. package/dist/react/runtimes/cloud/auiV0.d.ts +40 -15
  105. package/dist/react/runtimes/cloud/auiV0.d.ts.map +1 -1
  106. package/dist/react/runtimes/cloud/auiV0.js +62 -15
  107. package/dist/react/runtimes/cloud/auiV0.js.map +1 -1
  108. package/dist/react/runtimes/cloud/createCloudThreadListAdapter.d.ts +2 -1
  109. package/dist/react/runtimes/cloud/createCloudThreadListAdapter.d.ts.map +1 -1
  110. package/dist/react/runtimes/cloud/createCloudThreadListAdapter.js +6 -1
  111. package/dist/react/runtimes/cloud/createCloudThreadListAdapter.js.map +1 -1
  112. package/dist/react/runtimes/cloud/sdkIdentity.d.ts +6 -0
  113. package/dist/react/runtimes/cloud/sdkIdentity.d.ts.map +1 -0
  114. package/dist/react/runtimes/cloud/sdkIdentity.js +9 -0
  115. package/dist/react/runtimes/cloud/sdkIdentity.js.map +1 -0
  116. package/dist/react/runtimes/external-message-converter.d.ts +12 -2
  117. package/dist/react/runtimes/external-message-converter.d.ts.map +1 -1
  118. package/dist/react/runtimes/external-message-converter.js +26 -61
  119. package/dist/react/runtimes/external-message-converter.js.map +1 -1
  120. package/dist/react/runtimes/useExternalStoreRuntime.js +75 -46
  121. package/dist/react/runtimes/useExternalStoreRuntime.js.map +1 -1
  122. package/dist/react/runtimes/useRemoteThreadListRuntime.d.ts.map +1 -1
  123. package/dist/react/runtimes/useRemoteThreadListRuntime.js +64 -24
  124. package/dist/react/runtimes/useRemoteThreadListRuntime.js.map +1 -1
  125. package/dist/react/utils/groupParts.d.ts +6 -1
  126. package/dist/react/utils/groupParts.d.ts.map +1 -1
  127. package/dist/react/utils/groupParts.js +6 -0
  128. package/dist/react/utils/groupParts.js.map +1 -1
  129. package/dist/react/utils/useJSONEqual.d.ts +3 -1
  130. package/dist/react/utils/useJSONEqual.d.ts.map +1 -1
  131. package/dist/react/utils/useJSONEqual.js +6 -1
  132. package/dist/react/utils/useJSONEqual.js.map +1 -1
  133. package/dist/react/utils/useJSONSchemaDependency.d.ts +7 -0
  134. package/dist/react/utils/useJSONSchemaDependency.d.ts.map +1 -0
  135. package/dist/react/utils/useJSONSchemaDependency.js +20 -0
  136. package/dist/react/utils/useJSONSchemaDependency.js.map +1 -0
  137. package/dist/runtime/api/thread-list-item-runtime.d.ts +7 -3
  138. package/dist/runtime/api/thread-list-item-runtime.d.ts.map +1 -1
  139. package/dist/runtime/api/thread-list-item-runtime.js +2 -2
  140. package/dist/runtime/api/thread-list-item-runtime.js.map +1 -1
  141. package/dist/runtime/api/thread-list-runtime.d.ts +3 -0
  142. package/dist/runtime/api/thread-list-runtime.d.ts.map +1 -1
  143. package/dist/runtime/api/thread-list-runtime.js +18 -14
  144. package/dist/runtime/api/thread-list-runtime.js.map +1 -1
  145. package/dist/runtime/api/thread-runtime.d.ts +4 -0
  146. package/dist/runtime/api/thread-runtime.d.ts.map +1 -1
  147. package/dist/runtime/api/thread-runtime.js +3 -6
  148. package/dist/runtime/api/thread-runtime.js.map +1 -1
  149. package/dist/runtime/base/base-composer-runtime-core.d.ts +1 -0
  150. package/dist/runtime/base/base-composer-runtime-core.d.ts.map +1 -1
  151. package/dist/runtime/base/base-composer-runtime-core.js +62 -22
  152. package/dist/runtime/base/base-composer-runtime-core.js.map +1 -1
  153. package/dist/runtime/base/base-thread-runtime-core.d.ts +2 -0
  154. package/dist/runtime/base/base-thread-runtime-core.d.ts.map +1 -1
  155. package/dist/runtime/base/base-thread-runtime-core.js +161 -69
  156. package/dist/runtime/base/base-thread-runtime-core.js.map +1 -1
  157. package/dist/runtime/interfaces/composer-runtime-core.d.ts +12 -5
  158. package/dist/runtime/interfaces/composer-runtime-core.d.ts.map +1 -1
  159. package/dist/runtime/interfaces/thread-list-runtime-core.d.ts +4 -1
  160. package/dist/runtime/interfaces/thread-list-runtime-core.d.ts.map +1 -1
  161. package/dist/runtime/interfaces/thread-runtime-core.d.ts +6 -0
  162. package/dist/runtime/interfaces/thread-runtime-core.d.ts.map +1 -1
  163. package/dist/runtime/queue/message-queue.d.ts +5 -0
  164. package/dist/runtime/queue/message-queue.d.ts.map +1 -1
  165. package/dist/runtime/queue/message-queue.js +61 -6
  166. package/dist/runtime/queue/message-queue.js.map +1 -1
  167. package/dist/runtime/utils/auto-status.d.ts +6 -2
  168. package/dist/runtime/utils/auto-status.d.ts.map +1 -1
  169. package/dist/runtime/utils/auto-status.js +12 -4
  170. package/dist/runtime/utils/auto-status.js.map +1 -1
  171. package/dist/runtime/utils/external-message-conversion.d.ts +16 -4
  172. package/dist/runtime/utils/external-message-conversion.d.ts.map +1 -1
  173. package/dist/runtime/utils/external-message-conversion.js +49 -13
  174. package/dist/runtime/utils/external-message-conversion.js.map +1 -1
  175. package/dist/runtime/utils/message-repository.d.ts +1 -0
  176. package/dist/runtime/utils/message-repository.d.ts.map +1 -1
  177. package/dist/runtime/utils/message-repository.js +18 -9
  178. package/dist/runtime/utils/message-repository.js.map +1 -1
  179. package/dist/runtime/utils/thread-message-like.d.ts +2 -1
  180. package/dist/runtime/utils/thread-message-like.d.ts.map +1 -1
  181. package/dist/runtime/utils/thread-message-like.js +5 -3
  182. package/dist/runtime/utils/thread-message-like.js.map +1 -1
  183. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.d.ts.map +1 -1
  184. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.js +21 -27
  185. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.js.map +1 -1
  186. package/dist/runtimes/external-store/external-store-thread-runtime-core.d.ts.map +1 -1
  187. package/dist/runtimes/external-store/external-store-thread-runtime-core.js +8 -1
  188. package/dist/runtimes/external-store/external-store-thread-runtime-core.js.map +1 -1
  189. package/dist/runtimes/internal.d.ts +3 -3
  190. package/dist/runtimes/internal.js +2 -2
  191. package/dist/runtimes/local/local-thread-runtime-core.d.ts +1 -1
  192. package/dist/runtimes/local/local-thread-runtime-core.d.ts.map +1 -1
  193. package/dist/runtimes/local/local-thread-runtime-core.js +4 -1
  194. package/dist/runtimes/local/local-thread-runtime-core.js.map +1 -1
  195. package/dist/runtimes/remote-thread-list/remote-thread-state.d.ts +18 -1
  196. package/dist/runtimes/remote-thread-list/remote-thread-state.d.ts.map +1 -1
  197. package/dist/runtimes/remote-thread-list/remote-thread-state.js +130 -42
  198. package/dist/runtimes/remote-thread-list/remote-thread-state.js.map +1 -1
  199. package/dist/runtimes/remote-thread-list/title-generation.d.ts +49 -0
  200. package/dist/runtimes/remote-thread-list/title-generation.d.ts.map +1 -0
  201. package/dist/runtimes/remote-thread-list/title-generation.js +197 -0
  202. package/dist/runtimes/remote-thread-list/title-generation.js.map +1 -0
  203. package/dist/runtimes/remote-thread-list/title.d.ts +3 -1
  204. package/dist/runtimes/remote-thread-list/title.d.ts.map +1 -1
  205. package/dist/runtimes/remote-thread-list/title.js +9 -1
  206. package/dist/runtimes/remote-thread-list/title.js.map +1 -1
  207. package/dist/runtimes/remote-thread-list/types.d.ts +10 -0
  208. package/dist/runtimes/remote-thread-list/types.d.ts.map +1 -1
  209. package/dist/runtimes/tool-invocations/ToolInvocationTracker.d.ts.map +1 -1
  210. package/dist/runtimes/tool-invocations/ToolInvocationTracker.js +2 -1
  211. package/dist/runtimes/tool-invocations/ToolInvocationTracker.js.map +1 -1
  212. package/dist/store/clients/external-thread.d.ts.map +1 -1
  213. package/dist/store/clients/external-thread.js +260 -203
  214. package/dist/store/clients/external-thread.js.map +1 -1
  215. package/dist/store/clients/model-context-client.d.ts.map +1 -1
  216. package/dist/store/clients/model-context-client.js +2 -1
  217. package/dist/store/clients/model-context-client.js.map +1 -1
  218. package/dist/store/clients/single-thread-list.d.ts.map +1 -1
  219. package/dist/store/clients/single-thread-list.js +1 -0
  220. package/dist/store/clients/single-thread-list.js.map +1 -1
  221. package/dist/store/clients/suggestions.d.ts.map +1 -1
  222. package/dist/store/clients/suggestions.js +5 -4
  223. package/dist/store/clients/suggestions.js.map +1 -1
  224. package/dist/store/clients/thread-tasks.d.ts +13 -0
  225. package/dist/store/clients/thread-tasks.d.ts.map +1 -0
  226. package/dist/store/clients/thread-tasks.js +69 -0
  227. package/dist/store/clients/thread-tasks.js.map +1 -0
  228. package/dist/store/index.d.ts +3 -2
  229. package/dist/store/index.js +3 -3
  230. package/dist/store/primitive-predicates.d.ts.map +1 -1
  231. package/dist/store/primitive-predicates.js +4 -4
  232. package/dist/store/primitive-predicates.js.map +1 -1
  233. package/dist/store/runtime-clients/composer-runtime-client.d.ts +1 -0
  234. package/dist/store/runtime-clients/composer-runtime-client.d.ts.map +1 -1
  235. package/dist/store/runtime-clients/composer-runtime-client.js +97 -61
  236. package/dist/store/runtime-clients/composer-runtime-client.js.map +1 -1
  237. package/dist/store/runtime-clients/message-runtime-client.d.ts +1 -0
  238. package/dist/store/runtime-clients/message-runtime-client.d.ts.map +1 -1
  239. package/dist/store/runtime-clients/message-runtime-client.js +221 -144
  240. package/dist/store/runtime-clients/message-runtime-client.js.map +1 -1
  241. package/dist/store/runtime-clients/thread-list-item-runtime-client.d.ts.map +1 -1
  242. package/dist/store/runtime-clients/thread-list-item-runtime-client.js +1 -1
  243. package/dist/store/runtime-clients/thread-list-item-runtime-client.js.map +1 -1
  244. package/dist/store/runtime-clients/thread-list-runtime-client.d.ts.map +1 -1
  245. package/dist/store/runtime-clients/thread-list-runtime-client.js +54 -52
  246. package/dist/store/runtime-clients/thread-list-runtime-client.js.map +1 -1
  247. package/dist/store/runtime-clients/thread-runtime-client.d.ts.map +1 -1
  248. package/dist/store/runtime-clients/thread-runtime-client.js +246 -126
  249. package/dist/store/runtime-clients/thread-runtime-client.js.map +1 -1
  250. package/dist/store/scope-registration.d.ts +2 -0
  251. package/dist/store/scope-registration.d.ts.map +1 -1
  252. package/dist/store/scopes/composer.d.ts +10 -2
  253. package/dist/store/scopes/composer.d.ts.map +1 -1
  254. package/dist/store/scopes/message.d.ts +30 -1
  255. package/dist/store/scopes/message.d.ts.map +1 -1
  256. package/dist/store/scopes/task.d.ts +36 -0
  257. package/dist/store/scopes/task.d.ts.map +1 -0
  258. package/dist/store/scopes/task.js +0 -0
  259. package/dist/store/scopes/thread-list-item.d.ts +2 -2
  260. package/dist/store/scopes/thread-list-item.d.ts.map +1 -1
  261. package/dist/store/scopes/thread.d.ts +28 -0
  262. package/dist/store/scopes/thread.d.ts.map +1 -1
  263. package/dist/store/scopes/threads.d.ts +1 -0
  264. package/dist/store/scopes/threads.d.ts.map +1 -1
  265. package/dist/subscribable/subscribable.d.ts +2 -1
  266. package/dist/subscribable/subscribable.d.ts.map +1 -1
  267. package/dist/subscribable/subscribable.js +23 -15
  268. package/dist/subscribable/subscribable.js.map +1 -1
  269. package/dist/types/attachment.d.ts +2 -1
  270. package/dist/types/attachment.d.ts.map +1 -1
  271. package/dist/types/attachment.js +2 -1
  272. package/dist/types/attachment.js.map +1 -1
  273. package/dist/types/index.d.ts +2 -2
  274. package/dist/types/message.d.ts +8 -1
  275. package/dist/types/message.d.ts.map +1 -1
  276. package/dist/types/message.js.map +1 -1
  277. package/dist/utils/abortable-iterable.d.ts +19 -0
  278. package/dist/utils/abortable-iterable.d.ts.map +1 -0
  279. package/dist/utils/abortable-iterable.js +88 -0
  280. package/dist/utils/abortable-iterable.js.map +1 -0
  281. package/dist/utils/composite-context-provider.d.ts.map +1 -1
  282. package/dist/utils/composite-context-provider.js +20 -2
  283. package/dist/utils/composite-context-provider.js.map +1 -1
  284. package/dist/utils/getGroupStatus.d.ts +18 -2
  285. package/dist/utils/getGroupStatus.d.ts.map +1 -1
  286. package/dist/utils/getGroupStatus.js +32 -7
  287. package/dist/utils/getGroupStatus.js.map +1 -1
  288. package/dist/utils/record.d.ts +11 -0
  289. package/dist/utils/record.d.ts.map +1 -0
  290. package/dist/utils/record.js +12 -0
  291. package/dist/utils/record.js.map +1 -0
  292. package/dist/utils/wire-media.d.ts.map +1 -1
  293. package/dist/utils/wire-media.js +2 -2
  294. package/dist/utils/wire-media.js.map +1 -1
  295. package/package.json +11 -11
  296. package/src/adapters/attachment.test.ts +50 -1
  297. package/src/adapters/attachment.ts +7 -4
  298. package/src/adapters/speech.test.ts +236 -0
  299. package/src/adapters/speech.ts +17 -10
  300. package/src/adapters/thread-history.ts +3 -0
  301. package/src/index.ts +2 -0
  302. package/src/internal.ts +4 -0
  303. package/src/model-context/frame/host.test.ts +137 -1
  304. package/src/model-context/frame/host.ts +49 -16
  305. package/src/model-context/frame/provider.test.ts +71 -0
  306. package/src/model-context/frame/provider.ts +37 -7
  307. package/src/model-context/interactable-composer-metadata.test.ts +58 -0
  308. package/src/model-context/interactable-composer-metadata.ts +19 -13
  309. package/src/model-context/registry.test.ts +12 -0
  310. package/src/model-context/registry.ts +3 -2
  311. package/src/model-context/types.test.ts +25 -0
  312. package/src/model-context/types.ts +9 -4
  313. package/src/react/RuntimeAdapter.test.ts +34 -0
  314. package/src/react/adapters/LocalStorageThreadListAdapter.test.ts +244 -0
  315. package/src/react/adapters/LocalStorageThreadListAdapter.tsx +104 -7
  316. package/src/react/client/DataRenderers.test.tsx +45 -0
  317. package/src/react/client/DataRenderers.ts +13 -9
  318. package/src/react/client/InMemoryThreadList.ts +1 -0
  319. package/src/react/client/Interactables.test.ts +72 -1
  320. package/src/react/client/Interactables.ts +22 -14
  321. package/src/react/client/RemoteThreadList.background.test.ts +148 -6
  322. package/src/react/client/RemoteThreadList.concurrent.test.tsx +42 -1
  323. package/src/react/client/RemoteThreadList.test.ts +386 -4
  324. package/src/react/client/RemoteThreadList.ts +181 -112
  325. package/src/react/client/Tools.ts +22 -11
  326. package/src/react/client/interactable-model-context.test.ts +44 -0
  327. package/src/react/client/interactable-model-context.ts +3 -2
  328. package/src/react/index.ts +5 -1
  329. package/src/react/interactables-legacy/Interactables.ts +16 -16
  330. package/src/react/interactables-legacy/useAssistantInteractable.test.tsx +132 -0
  331. package/src/react/interactables-legacy/useAssistantInteractable.ts +10 -1
  332. package/src/react/interactables-shared/useInteractablePersistenceQueue.ts +9 -10
  333. package/src/react/model-context/makeAssistantTool.ts +2 -2
  334. package/src/react/model-context/useAssistantTool.ts +2 -2
  335. package/src/react/model-context/useInteractable.test.tsx +177 -0
  336. package/src/react/model-context/useInteractable.ts +3 -0
  337. package/src/react/model-context/useToolArgsStatus.test.tsx +30 -0
  338. package/src/react/model-context/useToolArgsStatus.ts +2 -1
  339. package/src/react/primitive-hooks/useActionBarCopy.test.ts +0 -1
  340. package/src/react/primitive-hooks/useSuggestionTrigger.test.ts +0 -1
  341. package/src/react/primitives/message/MessageGroupedParts.test.tsx +96 -0
  342. package/src/react/primitives/message/MessageGroupedParts.tsx +15 -4
  343. package/src/react/primitives/part/PartMessages.test.tsx +104 -1
  344. package/src/react/primitives/part/PartMessages.tsx +1 -0
  345. package/src/react/primitives/thread/ThreadMessages.test.tsx +142 -0
  346. package/src/react/primitives/thread/ThreadMessages.tsx +8 -5
  347. package/src/react/providers/ReadonlyThreadProvider.test.tsx +56 -0
  348. package/src/react/runtimes/RemoteThreadListHookInstanceManager.destroy.test.tsx +172 -0
  349. package/src/react/runtimes/RemoteThreadListHookInstanceManager.running.test.tsx +33 -0
  350. package/src/react/runtimes/RemoteThreadListHookInstanceManager.test.tsx +7 -1
  351. package/src/react/runtimes/RemoteThreadListHookInstanceManager.tsx +28 -5
  352. package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.load-error.test.ts +132 -0
  353. package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.switch-delete.test.ts +105 -0
  354. package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.tsx +143 -112
  355. package/src/react/runtimes/RemoteThreadResource.test.ts +9 -0
  356. package/src/react/runtimes/RemoteThreadResource.ts +19 -12
  357. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.test.ts +20 -14
  358. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.test.tsx +703 -6
  359. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.ts +492 -402
  360. package/src/react/runtimes/cloud/CloudFileAttachmentAdapter.test.ts +77 -0
  361. package/src/react/runtimes/cloud/CloudFileAttachmentAdapter.ts +27 -1
  362. package/src/react/runtimes/cloud/auiV0.ts +160 -25
  363. package/src/react/runtimes/cloud/createCloudThreadListAdapter.test.ts +36 -0
  364. package/src/react/runtimes/cloud/createCloudThreadListAdapter.ts +8 -1
  365. package/src/react/runtimes/cloud/sdkIdentity.ts +9 -0
  366. package/src/react/runtimes/cloud/useCloudThreadListAdapter.test.tsx +18 -4
  367. package/src/react/runtimes/external-message-converter.test.ts +136 -1
  368. package/src/react/runtimes/external-message-converter.ts +39 -93
  369. package/src/react/runtimes/useExternalStoreRuntime.lifecycle.test.tsx +41 -0
  370. package/src/react/runtimes/useExternalStoreRuntime.ts +10 -4
  371. package/src/react/runtimes/useLocalRuntime.test.tsx +20 -0
  372. package/src/react/runtimes/useRemoteThreadListRuntime.load-error.test.tsx +108 -0
  373. package/src/react/runtimes/useRemoteThreadListRuntime.ts +34 -1
  374. package/src/react/utils/groupParts.ts +12 -1
  375. package/src/react/utils/useJSONEqual.ts +9 -0
  376. package/src/react/utils/useJSONSchemaDependency.ts +22 -0
  377. package/src/runtime/api/message-runtime.test.ts +75 -0
  378. package/src/runtime/api/thread-list-item-runtime.ts +10 -3
  379. package/src/runtime/api/thread-list-runtime.ts +21 -11
  380. package/src/runtime/api/thread-runtime.test.ts +136 -0
  381. package/src/runtime/api/thread-runtime.ts +10 -5
  382. package/src/runtime/base/base-composer-runtime-core.ts +105 -24
  383. package/src/runtime/base/base-thread-runtime-core.test.ts +1063 -2
  384. package/src/runtime/base/base-thread-runtime-core.ts +228 -87
  385. package/src/runtime/interfaces/composer-runtime-core.ts +10 -5
  386. package/src/runtime/interfaces/thread-list-runtime-core.ts +5 -1
  387. package/src/runtime/interfaces/thread-runtime-core.ts +6 -0
  388. package/src/runtime/queue/message-queue.ts +68 -6
  389. package/src/runtime/utils/auto-status.test.ts +112 -1
  390. package/src/runtime/utils/auto-status.ts +31 -5
  391. package/src/runtime/utils/external-message-conversion.test.ts +202 -0
  392. package/src/runtime/utils/external-message-conversion.ts +123 -32
  393. package/src/runtime/utils/message-repository.test.ts +49 -0
  394. package/src/runtime/utils/message-repository.ts +32 -20
  395. package/src/runtime/utils/thread-message-like.test.ts +52 -0
  396. package/src/runtime/utils/thread-message-like.ts +5 -1
  397. package/src/runtimes/external-store/external-store-thread-list-runtime-core.ts +18 -13
  398. package/src/runtimes/external-store/external-store-thread-runtime-core.ts +31 -1
  399. package/src/runtimes/internal.ts +1 -0
  400. package/src/runtimes/local/local-thread-runtime-core.test.ts +21 -0
  401. package/src/runtimes/local/local-thread-runtime-core.ts +11 -1
  402. package/src/runtimes/remote-thread-list/remote-thread-state.test.ts +128 -2
  403. package/src/runtimes/remote-thread-list/remote-thread-state.ts +182 -42
  404. package/src/runtimes/remote-thread-list/title-generation.test.ts +602 -0
  405. package/src/runtimes/remote-thread-list/title-generation.ts +329 -0
  406. package/src/runtimes/remote-thread-list/title.test.ts +62 -0
  407. package/src/runtimes/remote-thread-list/title.ts +13 -0
  408. package/src/runtimes/remote-thread-list/types.ts +10 -0
  409. package/src/runtimes/tool-invocations/EDGE_CASES.md +2 -6
  410. package/src/runtimes/tool-invocations/ToolInvocationTracker.test.ts +133 -0
  411. package/src/runtimes/tool-invocations/ToolInvocationTracker.ts +10 -6
  412. package/src/store/clients/external-thread.ts +67 -4
  413. package/src/store/clients/model-context-client.ts +5 -1
  414. package/src/store/clients/single-thread-list.ts +1 -0
  415. package/src/store/clients/suggestions.test.ts +40 -0
  416. package/src/store/clients/suggestions.ts +13 -10
  417. package/src/store/clients/thread-tasks.test.ts +274 -0
  418. package/src/store/clients/thread-tasks.ts +138 -0
  419. package/src/store/index.ts +8 -0
  420. package/src/store/primitive-predicates.test.ts +48 -5
  421. package/src/store/primitive-predicates.ts +7 -2
  422. package/src/store/runtime-clients/composer-runtime-client.ts +42 -9
  423. package/src/store/runtime-clients/message-runtime-client.ts +54 -6
  424. package/src/store/runtime-clients/thread-list-item-runtime-client.ts +4 -2
  425. package/src/store/runtime-clients/thread-list-runtime-client.ts +1 -0
  426. package/src/store/runtime-clients/thread-runtime-client.ts +89 -11
  427. package/src/store/scope-registration.ts +2 -0
  428. package/src/store/scopes/composer.ts +16 -4
  429. package/src/store/scopes/message.ts +14 -0
  430. package/src/store/scopes/task.ts +34 -0
  431. package/src/store/scopes/thread-list-item.ts +5 -2
  432. package/src/store/scopes/thread.ts +20 -0
  433. package/src/store/scopes/threads.ts +1 -0
  434. package/src/store/thread-tasks.test.tsx +209 -0
  435. package/src/subscribable/subscribable.test.ts +156 -1
  436. package/src/subscribable/subscribable.ts +25 -15
  437. package/src/tests/MessageRepository.test.ts +117 -5
  438. package/src/tests/OptimisticState-list-race.test.ts +1 -0
  439. package/src/tests/RemoteThreadListThreadListRuntimeCore-initialize-adapter-change.test.ts +38 -0
  440. package/src/tests/RemoteThreadListThreadListRuntimeCore-load-race.test.ts +47 -0
  441. package/src/tests/RemoteThreadListThreadListRuntimeCore-title-race.test.ts +89 -0
  442. package/src/tests/attachment-adapters.test.ts +14 -0
  443. package/src/tests/auiV0Encode.test.ts +300 -0
  444. package/src/tests/base-composer-runtime-core-send.test.ts +51 -0
  445. package/src/tests/base-composer-runtime-core.test.ts +353 -2
  446. package/src/tests/external-store-thread-list-runtime-core.test.ts +83 -0
  447. package/src/tests/external-store-thread-runtime-core-adapter.test.ts +178 -18
  448. package/src/tests/external-thread-attachments.test.tsx +208 -0
  449. package/src/tests/external-thread-parity.test.tsx +57 -0
  450. package/src/tests/groupParts.test.ts +71 -0
  451. package/src/tests/message-queue.test.ts +253 -0
  452. package/src/tests/remote-thread-list-isLoading.test.ts +1 -0
  453. package/src/tests/remote-thread-list-reactive-threadId.test.ts +51 -0
  454. package/src/tests/thread-list-runtime-getLoadThreadsPromise.test.ts +1 -0
  455. package/src/tests/thread-switch-events.test.tsx +78 -0
  456. package/src/tests/tools-scope-migration.test.tsx +28 -0
  457. package/src/tests/useRemoteThreadListRuntime-controlled.test.tsx +43 -0
  458. package/src/types/attachment.ts +4 -0
  459. package/src/types/index.ts +1 -0
  460. package/src/types/message.ts +8 -0
  461. package/src/utils/abortable-iterable.test.ts +328 -0
  462. package/src/utils/abortable-iterable.ts +104 -0
  463. package/src/utils/composite-context-provider.test.ts +25 -0
  464. package/src/utils/composite-context-provider.ts +23 -2
  465. package/src/utils/getGroupStatus.test.ts +70 -4
  466. package/src/utils/getGroupStatus.ts +35 -12
  467. package/src/utils/record.test.ts +48 -0
  468. package/src/utils/record.ts +11 -0
  469. package/src/utils/wire-media.test.ts +10 -0
  470. package/src/utils/wire-media.ts +6 -2
@@ -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
+ });
@@ -1,3 +1,16 @@
1
+ import { AssistantMessageStream } from "assistant-stream";
2
+
1
3
  export const isTitleSourceMessage = (message: {
2
4
  status?: { type: string } | undefined;
3
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[],
@@ -72,9 +72,7 @@ If the tool also had a frontend `execute`, the executor is short-circuited
72
72
  via the entry's per-execution skip marker. Single fire.
73
73
 
74
74
  ### A.6. Previously-resolved tool's `result` is replaced
75
- Silently ignored — `entry.hasResult` short-circuits both the
76
- re-`setResponse` path and the downstream result-chunk handler. The host
77
- sees only the first result.
75
+ Silently ignored. A restored entry with a result remains historical even when a live snapshot reserializes its `argsText` or replaces its result. For active entries, `entry.hasResult` short-circuits both the re-`setResponse` path and the downstream result-chunk handler. The host sees only the first result.
78
76
 
79
77
  ### A.7. Previously-resolved tool loses its `result` (back to undefined)
80
78
  Silently ignored. The entry stays in the resolved phase internally.
@@ -213,9 +211,7 @@ Silently kept as restored. Recursion into `content.messages` still
213
211
  happens so any nested live tool calls are processed.
214
212
 
215
213
  ### C.3. Restored entry observed in a live snapshot, signature changed
216
- The restored entry is deleted and a new active entry starts via
217
- `_startActiveEntry`. This is PR #4057's promotion path. `streamCall`
218
- fires once — its first and only fire for this `toolCallId`.
214
+ A restored entry with a result remains historical regardless of later `argsText` or result changes. An unresolved restored entry is promoted only when a result lands or its `argsText` changes. Complete `argsText` values that parse to equivalent JSON are unchanged. Promotion deletes the restored entry and starts a new active entry via `_startActiveEntry`. `streamCall` fires once, its first and only fire for this `toolCallId`.
219
215
 
220
216
  ### C.4. `isLoading` transitions `true → false` while messages are stable
221
217
  The next `setState` call sees `isLoading === false` and processes
@@ -1465,6 +1465,139 @@ describe("ToolInvocationTracker", () => {
1465
1465
  expect(onResult).not.toHaveBeenCalled();
1466
1466
  });
1467
1467
 
1468
+ it("keeps a resolved restored tool call historical when its snapshot is reserialized", async () => {
1469
+ const execute = vi.fn(async () => ({ forecast: "ok" }));
1470
+ const streamCall = vi.fn();
1471
+ const getTools = () => ({
1472
+ weatherSearch: {
1473
+ parameters: { type: "object", properties: {} },
1474
+ execute,
1475
+ streamCall,
1476
+ } satisfies Tool,
1477
+ });
1478
+ const onResult = vi.fn();
1479
+ const tracker = new ToolInvocationTracker(getTools, {
1480
+ onResult,
1481
+ onStatusesChange: () => {},
1482
+ });
1483
+
1484
+ tracker.setState(
1485
+ createState([
1486
+ createAssistantMessage(
1487
+ '{"query":"London","page":1}',
1488
+ { query: "London", page: 1 },
1489
+ { result: { source: "history", revision: 1 } },
1490
+ ),
1491
+ ]),
1492
+ );
1493
+
1494
+ tracker.setState(
1495
+ createState([
1496
+ createAssistantMessage(
1497
+ '{ "page": 1, "query": "London" }',
1498
+ { query: "London", page: 1 },
1499
+ { result: { source: "history", revision: 2 } },
1500
+ ),
1501
+ ]),
1502
+ );
1503
+
1504
+ await new Promise((r) => setTimeout(r, 0));
1505
+
1506
+ expect(streamCall).not.toHaveBeenCalled();
1507
+ expect(execute).not.toHaveBeenCalled();
1508
+ expect(onResult).not.toHaveBeenCalled();
1509
+ });
1510
+
1511
+ it("promotes unresolved restored tool calls only for real args changes or a landed result", async () => {
1512
+ const equivalentStreamCall = vi.fn();
1513
+ const growingStreamCall = vi.fn();
1514
+ const resolvedStreamCall = vi.fn();
1515
+ const getTools = () => ({
1516
+ equivalentTool: {
1517
+ parameters: { type: "object", properties: {} },
1518
+ streamCall: equivalentStreamCall,
1519
+ } satisfies Tool,
1520
+ growingTool: {
1521
+ parameters: { type: "object", properties: {} },
1522
+ streamCall: growingStreamCall,
1523
+ } satisfies Tool,
1524
+ resolvedTool: {
1525
+ parameters: { type: "object", properties: {} },
1526
+ streamCall: resolvedStreamCall,
1527
+ } satisfies Tool,
1528
+ });
1529
+ const tracker = new ToolInvocationTracker(getTools, {
1530
+ onResult: vi.fn(),
1531
+ onStatusesChange: () => {},
1532
+ });
1533
+
1534
+ tracker.setState(
1535
+ createState([
1536
+ createAssistantMessage(
1537
+ '{"a":1,"b":2}',
1538
+ { a: 1, b: 2 },
1539
+ {
1540
+ toolCallId: "equivalent",
1541
+ toolName: "equivalentTool",
1542
+ },
1543
+ ),
1544
+ createAssistantMessage(
1545
+ '{"query":"Lon',
1546
+ { query: "Lon" },
1547
+ {
1548
+ toolCallId: "growing",
1549
+ toolName: "growingTool",
1550
+ },
1551
+ ),
1552
+ createAssistantMessage(
1553
+ '{"city":"London"}',
1554
+ { city: "London" },
1555
+ {
1556
+ toolCallId: "resolved",
1557
+ toolName: "resolvedTool",
1558
+ },
1559
+ ),
1560
+ ]),
1561
+ );
1562
+
1563
+ tracker.setState(
1564
+ createState([
1565
+ createAssistantMessage(
1566
+ '{ "b": 2, "a": 1 }',
1567
+ { a: 1, b: 2 },
1568
+ {
1569
+ toolCallId: "equivalent",
1570
+ toolName: "equivalentTool",
1571
+ },
1572
+ ),
1573
+ createAssistantMessage(
1574
+ '{"query":"London"}',
1575
+ { query: "London" },
1576
+ {
1577
+ toolCallId: "growing",
1578
+ toolName: "growingTool",
1579
+ },
1580
+ ),
1581
+ createAssistantMessage(
1582
+ '{"city":"London"}',
1583
+ { city: "London" },
1584
+ {
1585
+ toolCallId: "resolved",
1586
+ toolName: "resolvedTool",
1587
+ result: { source: "history" },
1588
+ },
1589
+ ),
1590
+ ]),
1591
+ );
1592
+
1593
+ await waitFor(() => {
1594
+ expect(growingStreamCall).toHaveBeenCalledTimes(1);
1595
+ expect(resolvedStreamCall).toHaveBeenCalledTimes(1);
1596
+ });
1597
+
1598
+ expect(equivalentStreamCall).not.toHaveBeenCalled();
1599
+ });
1600
+
1468
1601
  it("promotes an in-progress tool call from the initial snapshot when it changes", async () => {
1469
1602
  const execute = vi.fn(async () => ({ forecast: "ok" }));
1470
1603
  const streamCall = vi.fn();
@@ -825,12 +825,16 @@ export class ToolInvocationTracker {
825
825
  this._discardedToolCallIds.delete(content.toolCallId);
826
826
 
827
827
  if (entry && !entry.controller) {
828
- // Restored entry observed in a live snapshot. Promote if its
829
- // signature has changed; otherwise treat as still-historical.
830
- const signatureChanged =
831
- content.argsText !== entry.argsText ||
832
- (content.result !== undefined) !== entry.hasResult;
833
- if (!signatureChanged) continue;
828
+ // A restored entry with a result remains historical.
829
+ if (entry.hasResult) continue;
830
+ const argsChanged =
831
+ content.argsText !== entry.argsText &&
832
+ !(
833
+ isArgsTextComplete(entry.argsText) &&
834
+ isArgsTextComplete(content.argsText) &&
835
+ isEquivalentCompleteArgsText(entry.argsText, content.argsText)
836
+ );
837
+ if (!argsChanged && content.result === undefined) continue;
834
838
  this._entries.delete(content.toolCallId);
835
839
  entry = undefined;
836
840
  }