@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
@@ -9,17 +9,26 @@ import type {
9
9
  import type { ExportedMessageRepositoryItem } from "../../../runtime/utils/message-repository";
10
10
  import {
11
11
  type AssistantCloud,
12
+ type AssistantCloudEvent,
12
13
  type AssistantCloudRunReportToolCall,
13
14
  CloudMessagePersistence,
14
15
  createFormattedPersistence,
16
+ createRunReport,
15
17
  createRunTelemetryToolCall,
18
+ deriveRunOutcome,
19
+ describeRunError,
20
+ extractRunTelemetryModelId,
16
21
  normalizeRunTelemetryUsage,
22
+ type RunReportOutcome,
23
+ type RunReportStepInit,
24
+ type RunTelemetryUsage,
17
25
  type RunTelemetryUsageInit,
18
26
  truncateRunTelemetryText,
19
27
  } from "assistant-cloud";
20
28
  import { auiV0Decode, auiV0Encode } from "./auiV0";
21
29
  import { type AssistantClient, getClientId, useAui } from "@assistant-ui/store";
22
30
  import type { ThreadListItemMethods } from "../../../store/scopes/thread-list-item";
31
+ import type { FeedbackAdapter } from "../../../adapters/feedback";
23
32
 
24
33
  type CloudThreadListItem = Pick<
25
34
  ThreadListItemMethods,
@@ -64,12 +73,98 @@ class AssistantCloudThreadHistoryAdapter implements ThreadHistoryAdapter {
64
73
  return this.getPersistence();
65
74
  }
66
75
 
76
+ /**
77
+ * A send is the moment the runtime creates the remote thread, so that one
78
+ * event waits for the id; every other event reads the id that already
79
+ * exists, because initializing a thread nobody has written to would create
80
+ * an empty remote thread just to attribute an event.
81
+ */
82
+ public async resolveEngagementEventIds(
83
+ threadId: string,
84
+ messageId?: string,
85
+ options?: { awaitThread?: boolean },
86
+ ): Promise<Pick<AssistantCloudEvent, "thread_id" | "message_id">> {
87
+ const threadListItem = this.getThreadListItem(threadId);
88
+ if (!threadListItem) return {};
89
+
90
+ let remoteThreadId = threadListItem.getState().remoteId;
91
+ if (!remoteThreadId && options?.awaitThread) {
92
+ remoteThreadId = await threadListItem
93
+ .initialize()
94
+ .then((result) => result.remoteId)
95
+ .catch(() => undefined);
96
+ }
97
+ const remoteMessageId = messageId
98
+ ? this.getPersistence(threadListItem).getResolvedRemoteId(messageId)
99
+ : undefined;
100
+ return {
101
+ ...(remoteThreadId ? { thread_id: remoteThreadId } : undefined),
102
+ ...(remoteMessageId ? { message_id: remoteMessageId } : undefined),
103
+ };
104
+ }
105
+
106
+ public readonly feedback: FeedbackAdapter = {
107
+ submit: ({ message, type }) => {
108
+ void (async () => {
109
+ const threadListItem = this.tryGetKeyedThreadListItem();
110
+ const remoteThreadId = threadListItem?.getState().remoteId;
111
+ if (!threadListItem || !remoteThreadId) {
112
+ console.warn(
113
+ `[assistant-ui] Skipping feedback for message ${message.id}: the thread has no remote id.`,
114
+ );
115
+ return;
116
+ }
117
+
118
+ const cloudMessageId = await this.getPersistence(
119
+ threadListItem,
120
+ ).getRemoteId(message.id);
121
+ if (!cloudMessageId) {
122
+ console.warn(
123
+ `[assistant-ui] Skipping feedback for message ${message.id}: no cloud message id is mapped.`,
124
+ );
125
+ return;
126
+ }
127
+
128
+ await this.cloudRef.current.threads.messages.feedback(
129
+ remoteThreadId,
130
+ cloudMessageId,
131
+ { type },
132
+ );
133
+ })().catch((error: unknown) => {
134
+ console.error(
135
+ "[assistant-ui] Cloud feedback submission failed:",
136
+ error,
137
+ );
138
+ });
139
+ },
140
+ };
141
+
67
142
  private tryGetKeyedThreadListItem(): CloudThreadListItem | undefined {
68
143
  const live = this.aui.threadListItem;
69
144
  if (!live.source) return undefined;
70
145
  const id = live.getState().id;
71
146
  if (id === undefined) return undefined;
72
- return this.aui.threads.item({ id });
147
+ // A body can resolve before the list's committed items include its
148
+ // thread; the live item is already the per-thread anchor in that window.
149
+ const listed = this.aui.threads
150
+ .getState()
151
+ .threadItems.some((item) => item.id === id || item.remoteId === id);
152
+ return listed ? this.aui.threads.item({ id }) : live;
153
+ }
154
+
155
+ private getThreadListItem(threadId: string): CloudThreadListItem | undefined {
156
+ const current = this.aui.threadListItem;
157
+ const currentState = current.getState();
158
+ if (currentState.id === threadId || currentState.remoteId === threadId) {
159
+ return current;
160
+ }
161
+
162
+ const listed = this.aui.threads
163
+ .getState()
164
+ .threadItems.find(
165
+ (item) => item.id === threadId || item.remoteId === threadId,
166
+ );
167
+ return listed ? this.aui.threads.item({ id: listed.id }) : undefined;
73
168
  }
74
169
 
75
170
  withFormat<TMessage, TStorageFormat extends Record<string, unknown>>(
@@ -83,8 +178,6 @@ class AssistantCloudThreadHistoryAdapter implements ThreadHistoryAdapter {
83
178
  return threadListItem;
84
179
  };
85
180
  const resolvePinned = () => threadListItem ?? pinCurrent();
86
- const getFormatted = () =>
87
- createFormattedPersistence(adapter._persistence, formatAdapter);
88
181
  const getTargetFormatted = (item: CloudThreadListItem) =>
89
182
  createFormattedPersistence(adapter.getPersistence(item), formatAdapter);
90
183
  return {
@@ -132,14 +225,18 @@ class AssistantCloudThreadHistoryAdapter implements ThreadHistoryAdapter {
132
225
  encodedRunMessages,
133
226
  options,
134
227
  resolvePinned(),
228
+ extractLastRunMessageInfo(items, formatAdapter),
135
229
  );
136
230
  },
137
231
  async load(): Promise<MessageFormatRepository<TMessage>> {
138
- pinCurrent();
232
+ // Loads re-pin and resolve through the pinned item, so the id mapping
233
+ // they populate lives on the same persistence instance later writes
234
+ // resolve, whichever of the list item or the live graft won the pin.
235
+ const pinned = pinCurrent();
139
236
  const live = adapter.aui.threadListItem;
140
237
  const remoteId = live.source ? live.getState().remoteId : undefined;
141
238
  if (!remoteId) return { messages: [] };
142
- return getFormatted().load(remoteId);
239
+ return getTargetFormatted(pinned ?? live).load(remoteId);
143
240
  },
144
241
  };
145
242
  }
@@ -156,7 +253,12 @@ class AssistantCloudThreadHistoryAdapter implements ThreadHistoryAdapter {
156
253
  );
157
254
 
158
255
  if (this.cloudRef.current.telemetry.enabled) {
159
- this._maybeReportRun(remoteId, "aui/v0", encoded);
256
+ this._maybeReportRun(
257
+ remoteId,
258
+ "aui/v0",
259
+ encoded,
260
+ extractRunMessageInfo(message, "aui/v0"),
261
+ );
160
262
  }
161
263
  }
162
264
 
@@ -171,7 +273,12 @@ class AssistantCloudThreadHistoryAdapter implements ThreadHistoryAdapter {
171
273
  await this._persistence.update(remoteId, message.id, "aui/v0", encoded);
172
274
 
173
275
  if (this.cloudRef.current.telemetry.enabled) {
174
- this._maybeReportRun(remoteId, "aui/v0", encoded);
276
+ this._maybeReportRun(
277
+ remoteId,
278
+ "aui/v0",
279
+ encoded,
280
+ extractRunMessageInfo(message, "aui/v0"),
281
+ );
175
282
  }
176
283
  }
177
284
 
@@ -203,11 +310,12 @@ class AssistantCloudThreadHistoryAdapter implements ThreadHistoryAdapter {
203
310
  stepTimestamps?: StepTimestamp[];
204
311
  },
205
312
  threadListItem?: CloudThreadListItem,
313
+ messageInfo?: RunMessageInfo,
206
314
  ) {
207
315
  if (!this.cloudRef.current.telemetry.enabled) return;
208
316
 
209
- const remoteId = (threadListItem ?? this.aui.threadListItem).getState()
210
- .remoteId;
317
+ const item = threadListItem ?? this.aui.threadListItem;
318
+ const remoteId = item.getState().remoteId;
211
319
  if (!remoteId) return;
212
320
 
213
321
  const extracted = extractRunTelemetry(format, runMessages);
@@ -218,14 +326,21 @@ class AssistantCloudThreadHistoryAdapter implements ThreadHistoryAdapter {
218
326
  extracted,
219
327
  options?.durationMs,
220
328
  options?.stepTimestamps,
329
+ messageInfo,
330
+ this.getPersistence(item),
221
331
  );
222
332
  }
223
333
 
224
- private _maybeReportRun<T>(remoteId: string, format: string, content: T) {
334
+ private _maybeReportRun<T>(
335
+ remoteId: string,
336
+ format: string,
337
+ content: T,
338
+ messageInfo?: RunMessageInfo,
339
+ ) {
225
340
  const extracted = extractTelemetry(format, content);
226
341
  if (!extracted) return;
227
342
 
228
- this._sendReport(remoteId, extracted);
343
+ this._sendReport(remoteId, extracted, undefined, undefined, messageInfo);
229
344
  }
230
345
 
231
346
  private _sendReport(
@@ -233,37 +348,38 @@ class AssistantCloudThreadHistoryAdapter implements ThreadHistoryAdapter {
233
348
  data: TelemetryData,
234
349
  durationMs?: number,
235
350
  stepTimestamps?: StepTimestamp[],
351
+ messageInfo?: RunMessageInfo,
352
+ persistence = this._persistence,
236
353
  ) {
237
354
  const mergedSteps = mergeStepTimestamps(data.steps, stepTimestamps);
238
- // Keep in sync with assistant-cloud createRunSchema
239
- // (apps/aui-cloud-api/src/endpoints/runs/create.ts).
240
- const initial: Parameters<typeof this.cloudRef.current.runs.report>[0] = {
241
- thread_id: remoteId,
242
- status: data.status,
243
- ...(data.totalSteps != null
244
- ? { total_steps: data.totalSteps }
245
- : undefined),
246
- ...(data.toolCalls ? { tool_calls: data.toolCalls } : undefined),
247
- ...(mergedSteps ? { steps: mergedSteps } : undefined),
248
- ...(data.inputTokens != null
249
- ? { input_tokens: data.inputTokens }
250
- : undefined),
251
- ...(data.outputTokens != null
252
- ? { output_tokens: data.outputTokens }
253
- : undefined),
254
- ...(data.reasoningTokens != null
255
- ? { reasoning_tokens: data.reasoningTokens }
256
- : undefined),
257
- ...(data.cachedInputTokens != null
258
- ? { cached_input_tokens: data.cachedInputTokens }
259
- : undefined),
260
- ...(durationMs != null ? { duration_ms: durationMs } : undefined),
261
- ...(data.outputText != null
262
- ? { output_text: data.outputText }
263
- : undefined),
264
- ...(data.metadata ? { metadata: data.metadata } : undefined),
265
- ...(data.modelId ? { model_id: data.modelId } : undefined),
266
- };
355
+ const messageId = messageInfo?.localMessageId
356
+ ? persistence.getResolvedRemoteId(messageInfo.localMessageId)
357
+ : undefined;
358
+ const initial = createRunReport({
359
+ threadId: remoteId,
360
+ status: messageInfo?.status ?? data.status,
361
+ outcome: messageInfo?.outcomeType,
362
+ error: messageInfo?.error,
363
+ errorCode: messageInfo?.errorCode,
364
+ messageId,
365
+ traceId: messageInfo?.traceId,
366
+ modelId: data.modelId,
367
+ provider: messageInfo?.provider,
368
+ usage: {
369
+ inputTokens: data.inputTokens,
370
+ outputTokens: data.outputTokens,
371
+ reasoningTokens: data.reasoningTokens,
372
+ cachedInputTokens: data.cachedInputTokens,
373
+ },
374
+ steps: toRunReportSteps(mergedSteps),
375
+ totalSteps: data.totalSteps,
376
+ toolCalls: data.toolCalls,
377
+ durationMs,
378
+ firstTokenMs: messageInfo?.firstTokenMs,
379
+ outputText: data.outputText,
380
+ metadata: data.metadata,
381
+ telemetry: this.cloudRef.current.telemetry,
382
+ });
267
383
 
268
384
  const { beforeReport } = this.cloudRef.current.telemetry;
269
385
  const report = beforeReport ? beforeReport(initial) : initial;
@@ -285,6 +401,23 @@ type TelemetryStepData = {
285
401
 
286
402
  type StepTimestamp = { start_ms: number; end_ms: number };
287
403
 
404
+ function toRunReportSteps(
405
+ steps: TelemetryStepData[] | undefined,
406
+ ): RunReportStepInit[] | undefined {
407
+ if (!steps) return undefined;
408
+ return steps.map((step) => ({
409
+ usage: {
410
+ inputTokens: step.input_tokens,
411
+ outputTokens: step.output_tokens,
412
+ reasoningTokens: step.reasoning_tokens,
413
+ cachedInputTokens: step.cached_input_tokens,
414
+ },
415
+ toolCalls: step.tool_calls,
416
+ startMs: step.start_ms,
417
+ endMs: step.end_ms,
418
+ }));
419
+ }
420
+
288
421
  function mergeStepTimestamps(
289
422
  steps: TelemetryStepData[] | undefined,
290
423
  timestamps: StepTimestamp[] | undefined,
@@ -313,6 +446,101 @@ type TelemetryData = {
313
446
  modelId?: string;
314
447
  };
315
448
 
449
+ type RunMessageInfo = {
450
+ localMessageId?: string;
451
+ status?: "error";
452
+ outcomeType?: RunReportOutcome;
453
+ error?: string;
454
+ errorCode?: string;
455
+ firstTokenMs?: number;
456
+ traceId?: string;
457
+ provider?: string;
458
+ };
459
+
460
+ function extractLastRunMessageInfo<
461
+ TMessage,
462
+ TStorageFormat extends Record<string, unknown>,
463
+ >(
464
+ items: MessageFormatItem<TMessage>[],
465
+ formatAdapter: MessageFormatAdapter<TMessage, TStorageFormat>,
466
+ ): RunMessageInfo | undefined {
467
+ for (let i = items.length - 1; i >= 0; i--) {
468
+ const item = items[i]!;
469
+ const info = extractRunMessageInfo(
470
+ item.message,
471
+ formatAdapter.format,
472
+ formatAdapter.getId(item.message),
473
+ );
474
+ if (info) return info;
475
+ }
476
+ return undefined;
477
+ }
478
+
479
+ function extractRunMessageInfo(
480
+ message: unknown,
481
+ format: string,
482
+ localMessageId?: string,
483
+ ): RunMessageInfo | undefined {
484
+ if (!isRecord(message) || message.role !== "assistant") return undefined;
485
+
486
+ const status = isRecord(message.status) ? message.status : undefined;
487
+ const metadata = isRecord(message.metadata) ? message.metadata : undefined;
488
+ const custom = isRecord(metadata?.custom) ? metadata.custom : undefined;
489
+ const timing = isRecord(metadata?.timing) ? metadata.timing : undefined;
490
+ const streamStartTime = timing?.streamStartTime;
491
+ const firstTokenTime = timing?.firstTokenTime;
492
+ const firstTokenMs =
493
+ typeof streamStartTime === "number" &&
494
+ Number.isFinite(streamStartTime) &&
495
+ typeof firstTokenTime === "number" &&
496
+ Number.isFinite(firstTokenTime)
497
+ ? Math.round(firstTokenTime - streamStartTime)
498
+ : undefined;
499
+ const finishReason =
500
+ status?.type === "incomplete"
501
+ ? status.reason
502
+ : typeof metadata?.finishReason === "string"
503
+ ? metadata.finishReason
504
+ : undefined;
505
+ const failed = status?.type === "incomplete" && status.reason === "error";
506
+ const outcome = deriveRunOutcome({
507
+ finishReason: typeof finishReason === "string" ? finishReason : undefined,
508
+ isError: failed,
509
+ });
510
+ const outcomeType = outcome.outcome;
511
+ const failure = failed ? describeRunError(status.error) : {};
512
+ const messageId =
513
+ localMessageId ?? (typeof message.id === "string" ? message.id : undefined);
514
+ const traceId =
515
+ format === "aui/v0"
516
+ ? custom?.traceId
517
+ : format === "ai-sdk/v6"
518
+ ? metadata?.traceId
519
+ : undefined;
520
+ const provider =
521
+ typeof custom?.provider === "string"
522
+ ? custom.provider
523
+ : typeof metadata?.provider === "string"
524
+ ? metadata.provider
525
+ : undefined;
526
+
527
+ return {
528
+ ...(messageId ? { localMessageId: messageId } : undefined),
529
+ ...(outcome.status === "error" ? { status: "error" as const } : undefined),
530
+ ...(outcomeType ? { outcomeType } : undefined),
531
+ ...failure,
532
+ ...(firstTokenMs != null && firstTokenMs >= 0
533
+ ? { firstTokenMs }
534
+ : undefined),
535
+ ...(typeof traceId === "string" ? { traceId } : undefined),
536
+ ...(provider !== undefined ? { provider } : undefined),
537
+ };
538
+ }
539
+
540
+ function isRecord(value: unknown): value is Record<string, unknown> {
541
+ return value !== null && typeof value === "object";
542
+ }
543
+
316
544
  function extractTelemetry<T>(format: string, content: T): TelemetryData | null {
317
545
  switch (format) {
318
546
  case "aui/v0":
@@ -346,7 +574,7 @@ const AUI_STATUS_MAP: Record<string, TelemetryData["status"]> = {
346
574
  export function extractAuiV0<T>(content: T): TelemetryData | null {
347
575
  const msg = content as {
348
576
  role?: string;
349
- status?: { type: string };
577
+ status?: { type: string; reason?: string };
350
578
  content?: readonly {
351
579
  type: string;
352
580
  text?: string;
@@ -358,14 +586,7 @@ export function extractAuiV0<T>(content: T): TelemetryData | null {
358
586
  }[];
359
587
  metadata?: {
360
588
  modelId?: string;
361
- steps?: readonly {
362
- usage?: {
363
- inputTokens?: number;
364
- outputTokens?: number;
365
- reasoningTokens?: number;
366
- cachedInputTokens?: number;
367
- };
368
- }[];
589
+ steps?: readonly { usage?: RunTelemetryUsageInit }[];
369
590
  custom?: Record<string, unknown> & { modelId?: string };
370
591
  };
371
592
  };
@@ -408,20 +629,23 @@ export function extractAuiV0<T>(content: T): TelemetryData | null {
408
629
  let hasReasoning = false;
409
630
  let hasCachedInput = false;
410
631
  for (const step of steps) {
411
- if (step.usage?.inputTokens != null) {
412
- totalInput += step.usage.inputTokens;
632
+ if (!step.usage) continue;
633
+ const usage = normalizeRunTelemetryUsage(step.usage);
634
+ if (!usage) continue;
635
+ if (usage.inputTokens != null) {
636
+ totalInput += usage.inputTokens;
413
637
  hasInput = true;
414
638
  }
415
- if (step.usage?.outputTokens != null) {
416
- totalOutput += step.usage.outputTokens;
639
+ if (usage.outputTokens != null) {
640
+ totalOutput += usage.outputTokens;
417
641
  hasOutput = true;
418
642
  }
419
- if (step.usage?.reasoningTokens != null) {
420
- totalReasoning += step.usage.reasoningTokens;
643
+ if (usage.reasoningTokens != null) {
644
+ totalReasoning += usage.reasoningTokens;
421
645
  hasReasoning = true;
422
646
  }
423
- if (step.usage?.cachedInputTokens != null) {
424
- totalCachedInput += step.usage.cachedInputTokens;
647
+ if (usage.cachedInputTokens != null) {
648
+ totalCachedInput += usage.cachedInputTokens;
425
649
  hasCachedInput = true;
426
650
  }
427
651
  }
@@ -433,31 +657,36 @@ export function extractAuiV0<T>(content: T): TelemetryData | null {
433
657
 
434
658
  const statusType = msg.status?.type;
435
659
  const status: TelemetryData["status"] =
436
- (statusType && AUI_STATUS_MAP[statusType]) || "completed";
660
+ statusType === "incomplete" && msg.status?.reason === "error"
661
+ ? "error"
662
+ : (statusType && AUI_STATUS_MAP[statusType]) || "completed";
437
663
 
438
664
  const metadata = msg.metadata?.custom as Record<string, unknown> | undefined;
439
- const modelId =
440
- msg.metadata?.modelId ??
441
- (typeof msg.metadata?.custom?.modelId === "string"
442
- ? msg.metadata.custom.modelId
443
- : undefined);
665
+ const modelId = extractRunTelemetryModelId(
666
+ msg.metadata as Record<string, unknown> | undefined,
667
+ );
444
668
 
445
669
  const telemetrySteps: TelemetryStepData[] | undefined =
446
670
  steps && steps.length > 1
447
- ? steps.map((s) => ({
448
- ...(s.usage?.inputTokens != null
449
- ? { input_tokens: s.usage.inputTokens }
450
- : undefined),
451
- ...(s.usage?.outputTokens != null
452
- ? { output_tokens: s.usage.outputTokens }
453
- : undefined),
454
- ...(s.usage?.reasoningTokens != null
455
- ? { reasoning_tokens: s.usage.reasoningTokens }
456
- : undefined),
457
- ...(s.usage?.cachedInputTokens != null
458
- ? { cached_input_tokens: s.usage.cachedInputTokens }
459
- : undefined),
460
- }))
671
+ ? steps.map((s) => {
672
+ const usage = s.usage
673
+ ? normalizeRunTelemetryUsage(s.usage)
674
+ : undefined;
675
+ return {
676
+ ...(usage?.inputTokens != null
677
+ ? { input_tokens: usage.inputTokens }
678
+ : undefined),
679
+ ...(usage?.outputTokens != null
680
+ ? { output_tokens: usage.outputTokens }
681
+ : undefined),
682
+ ...(usage?.reasoningTokens != null
683
+ ? { reasoning_tokens: usage.reasoningTokens }
684
+ : undefined),
685
+ ...(usage?.cachedInputTokens != null
686
+ ? { cached_input_tokens: usage.cachedInputTokens }
687
+ : undefined),
688
+ };
689
+ })
461
690
  : undefined;
462
691
 
463
692
  return {
@@ -549,34 +778,19 @@ function collectAiSdkV6Parts(parts: readonly AiSdkV6Part[]): {
549
778
  return { textParts, toolCalls, stepsData };
550
779
  }
551
780
 
552
- function extractModelId(
553
- metadata?: Record<string, unknown>,
554
- ): string | undefined {
555
- if (!metadata) return undefined;
556
- if (typeof metadata.modelId === "string") return metadata.modelId;
557
- const custom = metadata.custom as Record<string, unknown> | undefined;
558
- if (typeof custom?.modelId === "string") return custom.modelId;
559
- return undefined;
560
- }
561
-
562
781
  function buildAiSdkV6Result(
563
782
  textParts: string[],
564
783
  toolCalls: AssistantCloudRunReportToolCall[],
565
784
  totalSteps: number,
566
785
  metadata?: Record<string, unknown>,
567
786
  stepsData?: { tool_calls: AssistantCloudRunReportToolCall[] }[],
568
- usage?: {
569
- inputTokens?: number;
570
- outputTokens?: number;
571
- reasoningTokens?: number;
572
- cachedInputTokens?: number;
573
- },
787
+ usage?: RunTelemetryUsage,
574
788
  ): TelemetryData {
575
789
  const hasText = textParts.length > 0;
576
790
  const outputText = hasText
577
791
  ? truncateRunTelemetryText(textParts.join(""))
578
792
  : undefined;
579
- const modelId = extractModelId(metadata);
793
+ const modelId = extractRunTelemetryModelId(metadata);
580
794
 
581
795
  const steps: TelemetryStepData[] | undefined =
582
796
  stepsData && stepsData.length > 1
@@ -610,14 +824,9 @@ function buildAiSdkV6Result(
610
824
  };
611
825
  }
612
826
 
613
- function extractAiSdkV6Usage(metadata?: Record<string, unknown>):
614
- | {
615
- inputTokens?: number;
616
- outputTokens?: number;
617
- reasoningTokens?: number;
618
- cachedInputTokens?: number;
619
- }
620
- | undefined {
827
+ function extractAiSdkV6Usage(
828
+ metadata?: Record<string, unknown>,
829
+ ): RunTelemetryUsage | undefined {
621
830
  // Try top-level metadata.usage
622
831
  const usage = metadata?.usage as RunTelemetryUsageInit | undefined;
623
832
  if (usage) {
@@ -759,7 +968,7 @@ function aggregateAiSdkV6RunSteps<T>(stepMessages: T[]): TelemetryData | null {
759
968
 
760
969
  export function useAssistantCloudThreadHistoryAdapter(
761
970
  cloudRef: RefObject<AssistantCloud>,
762
- ): ThreadHistoryAdapter {
971
+ ): ThreadHistoryAdapter & { readonly feedback: FeedbackAdapter } {
763
972
  const aui = useAui();
764
973
  // Not useEffectEvent: history adapter methods run during render (SSR load).
765
974
  const auiRef = useRef(aui);
@@ -770,5 +979,239 @@ export function useAssistantCloudThreadHistoryAdapter(
770
979
  () =>
771
980
  new AssistantCloudThreadHistoryAdapter(cloudRef, () => auiRef.current),
772
981
  );
982
+ useAssistantCloudEngagementEvents(cloudRef, adapter, aui);
773
983
  return adapter;
774
984
  }
985
+
986
+ type RootEngagementTracker = {
987
+ count: number;
988
+ adapter: AssistantCloudThreadHistoryAdapter;
989
+ cloudRef: RefObject<AssistantCloud>;
990
+ dispose: () => void;
991
+ };
992
+
993
+ const rootEngagementTrackers = new WeakMap<
994
+ AssistantClient,
995
+ RootEngagementTracker
996
+ >();
997
+
998
+ /**
999
+ * Thread switches are a thread list event, so one subscription per assistant
1000
+ * client reports them; every thread runtime mounted under it shares the
1001
+ * subscription and the last one to unmount removes it.
1002
+ */
1003
+ const useRootEngagementEvents = (
1004
+ cloudRef: RefObject<AssistantCloud>,
1005
+ adapter: AssistantCloudThreadHistoryAdapter,
1006
+ aui: AssistantClient,
1007
+ ) => {
1008
+ useEffect(() => {
1009
+ let tracker = rootEngagementTrackers.get(aui);
1010
+ if (!tracker) {
1011
+ const created: RootEngagementTracker = {
1012
+ count: 0,
1013
+ adapter,
1014
+ cloudRef,
1015
+ dispose: () => {},
1016
+ };
1017
+ created.dispose = aui.on(
1018
+ { scope: "*", event: "threads.selectionChanged" },
1019
+ (payload) => {
1020
+ void created.adapter
1021
+ .resolveEngagementEventIds(payload.threadId)
1022
+ .then((ids) => {
1023
+ if (!ids.thread_id) return;
1024
+ created.cloudRef.current.events?.track({
1025
+ kind: "thread_switched",
1026
+ ...ids,
1027
+ });
1028
+ })
1029
+ .catch(() => {});
1030
+ },
1031
+ );
1032
+ rootEngagementTrackers.set(aui, created);
1033
+ tracker = created;
1034
+ }
1035
+ const active = tracker;
1036
+ active.adapter = adapter;
1037
+ active.cloudRef = cloudRef;
1038
+ active.count += 1;
1039
+ return () => {
1040
+ active.count -= 1;
1041
+ if (active.count === 0) {
1042
+ active.dispose();
1043
+ rootEngagementTrackers.delete(aui);
1044
+ }
1045
+ };
1046
+ }, [adapter, aui, cloudRef]);
1047
+ };
1048
+
1049
+ const useAssistantCloudEngagementEvents = (
1050
+ cloudRef: RefObject<AssistantCloud>,
1051
+ adapter: AssistantCloudThreadHistoryAdapter,
1052
+ aui: AssistantClient,
1053
+ ) => {
1054
+ useRootEngagementEvents(cloudRef, adapter, aui);
1055
+ const runStartedAt = useRef(new Map<string, number>());
1056
+ const runEndedAt = useRef(new Map<string, number>());
1057
+ const shownSuggestions = useRef(new Set<string>());
1058
+
1059
+ useEffect(() => {
1060
+ const track = (
1061
+ event: AssistantCloudEvent,
1062
+ threadId: string,
1063
+ messageId?: string,
1064
+ options?: { awaitThread?: boolean },
1065
+ ) => {
1066
+ void adapter
1067
+ .resolveEngagementEventIds(threadId, messageId, options)
1068
+ .then((ids) => {
1069
+ cloudRef.current.events?.track({ ...event, ...ids });
1070
+ })
1071
+ .catch(() => {});
1072
+ };
1073
+ const trackRunStopped = (threadId: string) => {
1074
+ const startedAt = runStartedAt.current.get(threadId);
1075
+ if (startedAt === undefined) return;
1076
+ runStartedAt.current.delete(threadId);
1077
+ track(
1078
+ { kind: "run_stopped", value: Math.max(0, Date.now() - startedAt) },
1079
+ threadId,
1080
+ );
1081
+ };
1082
+
1083
+ const unsubscribers = [
1084
+ aui.on({ scope: "thread", event: "composer.send" }, (payload) => {
1085
+ const previousRunEndedAt = runEndedAt.current.get(payload.threadId);
1086
+ track(
1087
+ {
1088
+ kind: payload.messageId ? "message_edited" : "message_sent",
1089
+ ...(payload.messageId
1090
+ ? { props: { chars: payload.chars } }
1091
+ : {
1092
+ props: {
1093
+ chars: payload.chars,
1094
+ attachments: payload.attachments,
1095
+ },
1096
+ }),
1097
+ ...(!payload.messageId && previousRunEndedAt !== undefined
1098
+ ? { value: Math.max(0, Date.now() - previousRunEndedAt) }
1099
+ : undefined),
1100
+ },
1101
+ payload.threadId,
1102
+ payload.messageId,
1103
+ { awaitThread: !payload.messageId },
1104
+ );
1105
+ if (payload.suggestion) {
1106
+ track({ kind: "suggestion_clicked" }, payload.threadId);
1107
+ }
1108
+ }),
1109
+ aui.on(
1110
+ { scope: "thread", event: "composer.attachmentAdd" },
1111
+ (payload) => {
1112
+ track(
1113
+ {
1114
+ kind: "attachment_added",
1115
+ ...(payload.contentType
1116
+ ? { props: { type: payload.contentType } }
1117
+ : undefined),
1118
+ },
1119
+ payload.threadId,
1120
+ payload.messageId,
1121
+ );
1122
+ },
1123
+ ),
1124
+ aui.on(
1125
+ { scope: "thread", event: "composer.attachmentAddError" },
1126
+ (payload) => {
1127
+ track(
1128
+ {
1129
+ kind: "attachment_failed",
1130
+ ...(payload.contentType
1131
+ ? { props: { type: payload.contentType } }
1132
+ : undefined),
1133
+ },
1134
+ payload.threadId,
1135
+ payload.messageId,
1136
+ );
1137
+ },
1138
+ ),
1139
+ aui.on({ scope: "thread", event: "composer.cancel" }, (payload) => {
1140
+ trackRunStopped(payload.threadId);
1141
+ }),
1142
+ aui.on({ scope: "thread", event: "thread.runStart" }, (payload) => {
1143
+ runStartedAt.current.set(payload.threadId, Date.now());
1144
+ }),
1145
+ aui.on({ scope: "thread", event: "thread.runEnd" }, (payload) => {
1146
+ runStartedAt.current.delete(payload.threadId);
1147
+ runEndedAt.current.set(payload.threadId, Date.now());
1148
+ }),
1149
+ aui.on({ scope: "thread", event: "thread.cancelRun" }, (payload) => {
1150
+ trackRunStopped(payload.threadId);
1151
+ }),
1152
+ aui.on({ scope: "thread", event: "thread.voiceStarted" }, (payload) => {
1153
+ track({ kind: "voice_started" }, payload.threadId);
1154
+ }),
1155
+ aui.on({ scope: "thread", event: "message.reload" }, (payload) => {
1156
+ track(
1157
+ { kind: "message_regenerated" },
1158
+ payload.threadId,
1159
+ payload.messageId,
1160
+ );
1161
+ }),
1162
+ aui.on(
1163
+ { scope: "thread", event: "message.branchSwitched" },
1164
+ (payload) => {
1165
+ track(
1166
+ { kind: "branch_switched" },
1167
+ payload.threadId,
1168
+ payload.messageId,
1169
+ );
1170
+ },
1171
+ ),
1172
+ aui.on({ scope: "thread", event: "message.copied" }, (payload) => {
1173
+ track({ kind: "message_copied" }, payload.threadId, payload.messageId);
1174
+ }),
1175
+ aui.on({ scope: "thread", event: "message.speak" }, (payload) => {
1176
+ track({ kind: "speech_started" }, payload.threadId, payload.messageId);
1177
+ }),
1178
+ aui.on({ scope: "thread", event: "message.error" }, (payload) => {
1179
+ track(
1180
+ { kind: "error_shown", props: { reason: payload.reason } },
1181
+ payload.threadId,
1182
+ payload.messageId,
1183
+ );
1184
+ }),
1185
+ ];
1186
+
1187
+ return () => {
1188
+ for (const unsubscribe of unsubscribers) unsubscribe();
1189
+ };
1190
+ }, [adapter, aui, cloudRef]);
1191
+
1192
+ useEffect(() => {
1193
+ const reportSuggestions = () => {
1194
+ const { mainThreadId } = aui.threads.getState();
1195
+ if (aui.threadListItem.getState().id !== mainThreadId) return;
1196
+ const { isEmpty, suggestions } = aui.thread.getState();
1197
+ if (
1198
+ !isEmpty ||
1199
+ suggestions.length === 0 ||
1200
+ shownSuggestions.current.has(mainThreadId)
1201
+ ) {
1202
+ return;
1203
+ }
1204
+ shownSuggestions.current.add(mainThreadId);
1205
+ void adapter.resolveEngagementEventIds(mainThreadId).then((ids) => {
1206
+ cloudRef.current.events?.track({
1207
+ kind: "suggestions_shown",
1208
+ value: suggestions.length,
1209
+ ...ids,
1210
+ });
1211
+ });
1212
+ };
1213
+
1214
+ reportSuggestions();
1215
+ return aui.subscribe(reportSuggestions);
1216
+ }, [adapter, aui, cloudRef]);
1217
+ };