@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
@@ -7,19 +7,33 @@ import type {
7
7
  MessageFormatRepository,
8
8
  } from "../../../adapters/thread-history";
9
9
  import type { ExportedMessageRepositoryItem } from "../../../runtime/utils/message-repository";
10
+ import type { ThreadMessage } from "../../../types";
10
11
  import {
11
12
  type AssistantCloud,
12
- type AssistantCloudRunReportToolCall,
13
+ type AssistantCloudEvent,
14
+ CloudEngagementReporter,
13
15
  CloudMessagePersistence,
16
+ CloudRunReporter,
14
17
  createFormattedPersistence,
15
18
  createRunTelemetryToolCall,
19
+ deriveRunOutcome,
20
+ describeRunError,
21
+ extractRunTelemetryModelId,
16
22
  normalizeRunTelemetryUsage,
23
+ type RunMessageTelemetry,
24
+ type RunReportOutcome,
25
+ type RunReportStepInit,
17
26
  type RunTelemetryUsageInit,
18
27
  truncateRunTelemetryText,
19
28
  } from "assistant-cloud";
29
+ import {
30
+ extractAISDKRunTelemetry,
31
+ type AISDKMessageLike,
32
+ } from "assistant-cloud/ai-sdk";
20
33
  import { auiV0Decode, auiV0Encode } from "./auiV0";
21
34
  import { type AssistantClient, getClientId, useAui } from "@assistant-ui/store";
22
35
  import type { ThreadListItemMethods } from "../../../store/scopes/thread-list-item";
36
+ import type { FeedbackAdapter } from "../../../adapters/feedback";
23
37
 
24
38
  type CloudThreadListItem = Pick<
25
39
  ThreadListItemMethods,
@@ -34,6 +48,8 @@ const globalPersistence = new WeakMap<
34
48
  class AssistantCloudThreadHistoryAdapter implements ThreadHistoryAdapter {
35
49
  private cloudRef: RefObject<AssistantCloud>;
36
50
  private getAui: () => AssistantClient;
51
+ private runReporter: CloudRunReporter;
52
+ public readonly engagementReporter: CloudEngagementReporter;
37
53
 
38
54
  constructor(
39
55
  cloudRef: RefObject<AssistantCloud>,
@@ -41,6 +57,12 @@ class AssistantCloudThreadHistoryAdapter implements ThreadHistoryAdapter {
41
57
  ) {
42
58
  this.cloudRef = cloudRef;
43
59
  this.getAui = getAui;
60
+ this.runReporter = new CloudRunReporter(() => this.cloudRef.current);
61
+ this.engagementReporter = new CloudEngagementReporter(
62
+ () => this.cloudRef.current,
63
+ (threadId, messageId, options) =>
64
+ this.resolveEngagementEventIds(threadId, messageId, options),
65
+ );
44
66
  }
45
67
 
46
68
  private get aui(): AssistantClient {
@@ -64,6 +86,72 @@ class AssistantCloudThreadHistoryAdapter implements ThreadHistoryAdapter {
64
86
  return this.getPersistence();
65
87
  }
66
88
 
89
+ /**
90
+ * A send is the moment the runtime creates the remote thread, so that one
91
+ * event waits for the id; every other event reads the id that already
92
+ * exists, because initializing a thread nobody has written to would create
93
+ * an empty remote thread just to attribute an event.
94
+ */
95
+ public async resolveEngagementEventIds(
96
+ threadId: string,
97
+ messageId?: string,
98
+ options?: { awaitThread?: boolean },
99
+ ): Promise<Pick<AssistantCloudEvent, "thread_id" | "message_id">> {
100
+ const threadListItem = this.getThreadListItem(threadId);
101
+ if (!threadListItem) return {};
102
+
103
+ let remoteThreadId = threadListItem.getState().remoteId;
104
+ if (!remoteThreadId && options?.awaitThread) {
105
+ remoteThreadId = await threadListItem
106
+ .initialize()
107
+ .then((result) => result.remoteId)
108
+ .catch(() => undefined);
109
+ }
110
+ const remoteMessageId = messageId
111
+ ? this.getPersistence(threadListItem).getResolvedRemoteId(messageId)
112
+ : undefined;
113
+ return {
114
+ ...(remoteThreadId ? { thread_id: remoteThreadId } : undefined),
115
+ ...(remoteMessageId ? { message_id: remoteMessageId } : undefined),
116
+ };
117
+ }
118
+
119
+ public readonly feedback: FeedbackAdapter = {
120
+ submit: ({ message, type }) => {
121
+ void (async () => {
122
+ const threadListItem = this.tryGetKeyedThreadListItem();
123
+ const remoteThreadId = threadListItem?.getState().remoteId;
124
+ if (!threadListItem || !remoteThreadId) {
125
+ console.warn(
126
+ `[assistant-ui] Skipping feedback for message ${message.id}: the thread has no remote id.`,
127
+ );
128
+ return;
129
+ }
130
+
131
+ const cloudMessageId = await this.getPersistence(
132
+ threadListItem,
133
+ ).getRemoteId(message.id);
134
+ if (!cloudMessageId) {
135
+ console.warn(
136
+ `[assistant-ui] Skipping feedback for message ${message.id}: no cloud message id is mapped.`,
137
+ );
138
+ return;
139
+ }
140
+
141
+ await this.cloudRef.current.threads.messages.feedback(
142
+ remoteThreadId,
143
+ cloudMessageId,
144
+ { type },
145
+ );
146
+ })().catch((error: unknown) => {
147
+ console.error(
148
+ "[assistant-ui] Cloud feedback submission failed:",
149
+ error,
150
+ );
151
+ });
152
+ },
153
+ };
154
+
67
155
  private tryGetKeyedThreadListItem(): CloudThreadListItem | undefined {
68
156
  const live = this.aui.threadListItem;
69
157
  if (!live.source) return undefined;
@@ -77,6 +165,21 @@ class AssistantCloudThreadHistoryAdapter implements ThreadHistoryAdapter {
77
165
  return listed ? this.aui.threads.item({ id }) : live;
78
166
  }
79
167
 
168
+ private getThreadListItem(threadId: string): CloudThreadListItem | undefined {
169
+ const current = this.aui.threadListItem;
170
+ const currentState = current.getState();
171
+ if (currentState.id === threadId || currentState.remoteId === threadId) {
172
+ return current;
173
+ }
174
+
175
+ const listed = this.aui.threads
176
+ .getState()
177
+ .threadItems.find(
178
+ (item) => item.id === threadId || item.remoteId === threadId,
179
+ );
180
+ return listed ? this.aui.threads.item({ id: listed.id }) : undefined;
181
+ }
182
+
80
183
  withFormat<TMessage, TStorageFormat extends Record<string, unknown>>(
81
184
  formatAdapter: MessageFormatAdapter<TMessage, TStorageFormat>,
82
185
  ): GenericThreadHistoryAdapter<TMessage> {
@@ -125,6 +228,7 @@ class AssistantCloudThreadHistoryAdapter implements ThreadHistoryAdapter {
125
228
  options?: {
126
229
  durationMs?: number;
127
230
  stepTimestamps?: StepTimestamp[];
231
+ message?: ThreadMessage;
128
232
  },
129
233
  ) {
130
234
  const encodedRunMessages = items.map((item) =>
@@ -135,6 +239,12 @@ class AssistantCloudThreadHistoryAdapter implements ThreadHistoryAdapter {
135
239
  encodedRunMessages,
136
240
  options,
137
241
  resolvePinned(),
242
+ mergeRunMessageInfo(
243
+ extractLastRunMessageInfo(items, formatAdapter),
244
+ options?.message
245
+ ? extractRunMessageInfo(options.message, "aui/v0")
246
+ : undefined,
247
+ ),
138
248
  );
139
249
  },
140
250
  async load(): Promise<MessageFormatRepository<TMessage>> {
@@ -162,7 +272,12 @@ class AssistantCloudThreadHistoryAdapter implements ThreadHistoryAdapter {
162
272
  );
163
273
 
164
274
  if (this.cloudRef.current.telemetry.enabled) {
165
- this._maybeReportRun(remoteId, "aui/v0", encoded);
275
+ this._maybeReportRun(
276
+ remoteId,
277
+ "aui/v0",
278
+ encoded,
279
+ extractRunMessageInfo(message, "aui/v0"),
280
+ );
166
281
  }
167
282
  }
168
283
 
@@ -177,7 +292,12 @@ class AssistantCloudThreadHistoryAdapter implements ThreadHistoryAdapter {
177
292
  await this._persistence.update(remoteId, message.id, "aui/v0", encoded);
178
293
 
179
294
  if (this.cloudRef.current.telemetry.enabled) {
180
- this._maybeReportRun(remoteId, "aui/v0", encoded);
295
+ this._maybeReportRun(
296
+ remoteId,
297
+ "aui/v0",
298
+ encoded,
299
+ extractRunMessageInfo(message, "aui/v0"),
300
+ );
181
301
  }
182
302
  }
183
303
 
@@ -209,14 +329,17 @@ class AssistantCloudThreadHistoryAdapter implements ThreadHistoryAdapter {
209
329
  stepTimestamps?: StepTimestamp[];
210
330
  },
211
331
  threadListItem?: CloudThreadListItem,
332
+ messageInfo?: RunMessageInfo,
212
333
  ) {
213
- if (!this.cloudRef.current.telemetry.enabled) return;
214
-
215
- const remoteId = (threadListItem ?? this.aui.threadListItem).getState()
216
- .remoteId;
334
+ const item = threadListItem ?? this.aui.threadListItem;
335
+ const remoteId = item.getState().remoteId;
217
336
  if (!remoteId) return;
218
337
 
219
- const extracted = extractRunTelemetry(format, runMessages);
338
+ const extracted =
339
+ extractRunTelemetry(format, runMessages) ??
340
+ (messageInfo?.status !== undefined
341
+ ? { status: "incomplete" as const }
342
+ : undefined);
220
343
  if (!extracted) return;
221
344
 
222
345
  this._sendReport(
@@ -224,107 +347,199 @@ class AssistantCloudThreadHistoryAdapter implements ThreadHistoryAdapter {
224
347
  extracted,
225
348
  options?.durationMs,
226
349
  options?.stepTimestamps,
350
+ messageInfo,
351
+ this.getPersistence(item),
227
352
  );
228
353
  }
229
354
 
230
- private _maybeReportRun<T>(remoteId: string, format: string, content: T) {
355
+ private _maybeReportRun<T>(
356
+ remoteId: string,
357
+ format: string,
358
+ content: T,
359
+ messageInfo?: RunMessageInfo,
360
+ ) {
231
361
  const extracted = extractTelemetry(format, content);
232
362
  if (!extracted) return;
233
363
 
234
- this._sendReport(remoteId, extracted);
364
+ this._sendReport(remoteId, extracted, undefined, undefined, messageInfo);
235
365
  }
236
366
 
237
367
  private _sendReport(
238
368
  remoteId: string,
239
- data: TelemetryData,
369
+ data: RunMessageTelemetry,
240
370
  durationMs?: number,
241
371
  stepTimestamps?: StepTimestamp[],
372
+ messageInfo?: RunMessageInfo,
373
+ persistence = this._persistence,
242
374
  ) {
243
375
  const mergedSteps = mergeStepTimestamps(data.steps, stepTimestamps);
244
- // Keep in sync with assistant-cloud createRunSchema
245
- // (apps/aui-cloud-api/src/endpoints/runs/create.ts).
246
- const initial: Parameters<typeof this.cloudRef.current.runs.report>[0] = {
247
- thread_id: remoteId,
248
- status: data.status,
249
- ...(data.totalSteps != null
250
- ? { total_steps: data.totalSteps }
251
- : undefined),
252
- ...(data.toolCalls ? { tool_calls: data.toolCalls } : undefined),
253
- ...(mergedSteps ? { steps: mergedSteps } : undefined),
254
- ...(data.inputTokens != null
255
- ? { input_tokens: data.inputTokens }
256
- : undefined),
257
- ...(data.outputTokens != null
258
- ? { output_tokens: data.outputTokens }
259
- : undefined),
260
- ...(data.reasoningTokens != null
261
- ? { reasoning_tokens: data.reasoningTokens }
262
- : undefined),
263
- ...(data.cachedInputTokens != null
264
- ? { cached_input_tokens: data.cachedInputTokens }
265
- : undefined),
266
- ...(durationMs != null ? { duration_ms: durationMs } : undefined),
267
- ...(data.outputText != null
268
- ? { output_text: data.outputText }
269
- : undefined),
270
- ...(data.metadata ? { metadata: data.metadata } : undefined),
271
- ...(data.modelId ? { model_id: data.modelId } : undefined),
272
- };
273
-
274
- const { beforeReport } = this.cloudRef.current.telemetry;
275
- const report = beforeReport ? beforeReport(initial) : initial;
276
- if (!report) return;
277
-
278
- this.cloudRef.current.runs.report(report).catch(() => {});
376
+ const messageId = messageInfo?.localMessageId
377
+ ? persistence.getResolvedRemoteId(messageInfo.localMessageId)
378
+ : undefined;
379
+ void this.runReporter.report({
380
+ threadId: remoteId,
381
+ status: messageInfo?.status ?? data.status,
382
+ outcome: messageInfo?.outcomeType,
383
+ error: messageInfo?.error,
384
+ errorCode: messageInfo?.errorCode,
385
+ messageId,
386
+ traceId: messageInfo?.traceId,
387
+ modelId: data.modelId,
388
+ provider: messageInfo?.provider,
389
+ usage: data.usage,
390
+ steps: mergedSteps,
391
+ totalSteps: data.totalSteps,
392
+ toolCalls: data.toolCalls,
393
+ durationMs,
394
+ firstTokenMs: messageInfo?.firstTokenMs,
395
+ outputText: data.outputText,
396
+ metadata: data.metadata,
397
+ });
279
398
  }
280
399
  }
281
400
 
282
- type TelemetryStepData = {
283
- input_tokens?: number;
284
- output_tokens?: number;
285
- reasoning_tokens?: number;
286
- cached_input_tokens?: number;
287
- tool_calls?: AssistantCloudRunReportToolCall[];
288
- start_ms?: number;
289
- end_ms?: number;
290
- };
291
-
292
401
  type StepTimestamp = { start_ms: number; end_ms: number };
293
402
 
294
403
  function mergeStepTimestamps(
295
- steps: TelemetryStepData[] | undefined,
404
+ steps: RunReportStepInit[] | undefined,
296
405
  timestamps: StepTimestamp[] | undefined,
297
- ): TelemetryStepData[] | undefined {
406
+ ): RunReportStepInit[] | undefined {
298
407
  if (!timestamps) return steps;
299
- if (!steps) return timestamps.map((t) => ({ ...t }));
408
+ if (!steps) {
409
+ return timestamps.map(({ start_ms, end_ms }) => ({
410
+ startMs: start_ms,
411
+ endMs: end_ms,
412
+ }));
413
+ }
300
414
 
301
415
  const len = Math.min(steps.length, timestamps.length);
302
- return steps.map((s, i) => ({
303
- ...s,
304
- ...(i < len ? timestamps[i] : undefined),
416
+ return steps.map((step, index) => ({
417
+ ...step,
418
+ ...(index < len
419
+ ? {
420
+ startMs: timestamps[index]!.start_ms,
421
+ endMs: timestamps[index]!.end_ms,
422
+ }
423
+ : undefined),
305
424
  }));
306
425
  }
307
426
 
308
- type TelemetryData = {
309
- status: "completed" | "incomplete" | "error";
310
- toolCalls?: AssistantCloudRunReportToolCall[];
311
- totalSteps?: number;
312
- inputTokens?: number;
313
- outputTokens?: number;
314
- reasoningTokens?: number;
315
- cachedInputTokens?: number;
316
- outputText?: string;
317
- metadata?: Record<string, unknown>;
318
- steps?: TelemetryStepData[];
319
- modelId?: string;
427
+ type RunMessageInfo = {
428
+ localMessageId?: string;
429
+ status?: "completed" | "incomplete" | "error";
430
+ outcomeType?: RunReportOutcome;
431
+ error?: string;
432
+ errorCode?: string;
433
+ firstTokenMs?: number;
434
+ traceId?: string;
435
+ provider?: string;
320
436
  };
321
437
 
322
- function extractTelemetry<T>(format: string, content: T): TelemetryData | null {
438
+ function extractLastRunMessageInfo<
439
+ TMessage,
440
+ TStorageFormat extends Record<string, unknown>,
441
+ >(
442
+ items: MessageFormatItem<TMessage>[],
443
+ formatAdapter: MessageFormatAdapter<TMessage, TStorageFormat>,
444
+ ): RunMessageInfo | undefined {
445
+ for (let i = items.length - 1; i >= 0; i--) {
446
+ const item = items[i]!;
447
+ const info = extractRunMessageInfo(
448
+ item.message,
449
+ formatAdapter.format,
450
+ formatAdapter.getId(item.message),
451
+ );
452
+ if (info) return info;
453
+ }
454
+ return undefined;
455
+ }
456
+
457
+ function mergeRunMessageInfo(
458
+ stored: RunMessageInfo | undefined,
459
+ observed: RunMessageInfo | undefined,
460
+ ): RunMessageInfo | undefined {
461
+ if (!observed) return stored;
462
+ const { localMessageId: _observedId, ...outcome } = observed;
463
+ return { ...stored, ...outcome };
464
+ }
465
+
466
+ function extractRunMessageInfo(
467
+ message: unknown,
468
+ format: string,
469
+ localMessageId?: string,
470
+ ): RunMessageInfo | undefined {
471
+ if (!isRecord(message) || message.role !== "assistant") return undefined;
472
+
473
+ const status = isRecord(message.status) ? message.status : undefined;
474
+ const metadata = isRecord(message.metadata) ? message.metadata : undefined;
475
+ const custom = isRecord(metadata?.custom) ? metadata.custom : undefined;
476
+ const timing = isRecord(metadata?.timing) ? metadata.timing : undefined;
477
+ const firstTokenTime = timing?.firstTokenTime;
478
+ const firstTokenMs =
479
+ typeof firstTokenTime === "number" && Number.isFinite(firstTokenTime)
480
+ ? Math.round(firstTokenTime)
481
+ : undefined;
482
+ const finishReason =
483
+ status?.type === "incomplete"
484
+ ? typeof status.reason === "string"
485
+ ? status.reason
486
+ : undefined
487
+ : typeof metadata?.finishReason === "string"
488
+ ? metadata.finishReason
489
+ : undefined;
490
+ const failed = status?.type === "incomplete" && status.reason === "error";
491
+ const outcome = deriveRunOutcome({ finishReason, isError: failed });
492
+ const outcomeType = outcome.outcome;
493
+ const runStatus =
494
+ outcome.status === "error"
495
+ ? "error"
496
+ : status?.type === "incomplete"
497
+ ? "incomplete"
498
+ : finishReason !== undefined
499
+ ? outcome.status
500
+ : undefined;
501
+ const failure = failed ? describeRunError(status.error) : {};
502
+ const messageId =
503
+ localMessageId ?? (typeof message.id === "string" ? message.id : undefined);
504
+ const traceId =
505
+ format === "aui/v0"
506
+ ? custom?.traceId
507
+ : format === "ai-sdk/v6"
508
+ ? metadata?.traceId
509
+ : undefined;
510
+ const provider =
511
+ typeof custom?.provider === "string"
512
+ ? custom.provider
513
+ : typeof metadata?.provider === "string"
514
+ ? metadata.provider
515
+ : undefined;
516
+
517
+ return {
518
+ ...(messageId ? { localMessageId: messageId } : undefined),
519
+ ...(runStatus !== undefined ? { status: runStatus } : undefined),
520
+ ...(outcomeType ? { outcomeType } : undefined),
521
+ ...failure,
522
+ ...(firstTokenMs != null && firstTokenMs >= 0
523
+ ? { firstTokenMs }
524
+ : undefined),
525
+ ...(typeof traceId === "string" ? { traceId } : undefined),
526
+ ...(provider !== undefined ? { provider } : undefined),
527
+ };
528
+ }
529
+
530
+ function isRecord(value: unknown): value is Record<string, unknown> {
531
+ return value !== null && typeof value === "object";
532
+ }
533
+
534
+ function extractTelemetry<T>(
535
+ format: string,
536
+ content: T,
537
+ ): RunMessageTelemetry | null {
323
538
  switch (format) {
324
539
  case "aui/v0":
325
540
  return extractAuiV0(content);
326
541
  case "ai-sdk/v6":
327
- return extractAiSdkV6(content);
542
+ return extractAISDKRunTelemetry([content as AISDKMessageLike]);
328
543
  default:
329
544
  return null;
330
545
  }
@@ -333,9 +548,9 @@ function extractTelemetry<T>(format: string, content: T): TelemetryData | null {
333
548
  function extractRunTelemetry<T>(
334
549
  format: string,
335
550
  runMessages: T[],
336
- ): TelemetryData | null {
551
+ ): RunMessageTelemetry | null {
337
552
  if (format === "ai-sdk/v6") {
338
- return aggregateAiSdkV6RunSteps(runMessages);
553
+ return extractAISDKRunTelemetry(runMessages as AISDKMessageLike[]);
339
554
  }
340
555
  for (let i = runMessages.length - 1; i >= 0; i--) {
341
556
  const result = extractTelemetry(format, runMessages[i]!);
@@ -344,15 +559,10 @@ function extractRunTelemetry<T>(
344
559
  return null;
345
560
  }
346
561
 
347
- const AUI_STATUS_MAP: Record<string, TelemetryData["status"]> = {
348
- error: "error",
349
- incomplete: "incomplete",
350
- };
351
-
352
- export function extractAuiV0<T>(content: T): TelemetryData | null {
562
+ export function extractAuiV0<T>(content: T): RunMessageTelemetry | null {
353
563
  const msg = content as {
354
564
  role?: string;
355
- status?: { type: string };
565
+ status?: { type: string; reason?: string };
356
566
  content?: readonly {
357
567
  type: string;
358
568
  text?: string;
@@ -433,340 +643,45 @@ export function extractAuiV0<T>(content: T): TelemetryData | null {
433
643
  cachedInputTokens = hasCachedInput ? totalCachedInput : undefined;
434
644
  }
435
645
 
436
- const statusType = msg.status?.type;
437
- const status: TelemetryData["status"] =
438
- (statusType && AUI_STATUS_MAP[statusType]) || "completed";
646
+ const status = msg.status?.type === "incomplete" ? "incomplete" : "completed";
439
647
 
440
648
  const metadata = msg.metadata?.custom as Record<string, unknown> | undefined;
441
- const modelId =
442
- msg.metadata?.modelId ??
443
- (typeof msg.metadata?.custom?.modelId === "string"
444
- ? msg.metadata.custom.modelId
445
- : undefined);
446
-
447
- const telemetrySteps: TelemetryStepData[] | undefined =
448
- steps && steps.length > 1
449
- ? steps.map((s) => {
450
- const usage = s.usage
451
- ? normalizeRunTelemetryUsage(s.usage)
452
- : undefined;
453
- return {
454
- ...(usage?.inputTokens != null
455
- ? { input_tokens: usage.inputTokens }
456
- : undefined),
457
- ...(usage?.outputTokens != null
458
- ? { output_tokens: usage.outputTokens }
459
- : undefined),
460
- ...(usage?.reasoningTokens != null
461
- ? { reasoning_tokens: usage.reasoningTokens }
462
- : undefined),
463
- ...(usage?.cachedInputTokens != null
464
- ? { cached_input_tokens: usage.cachedInputTokens }
465
- : undefined),
466
- };
467
- })
649
+ const modelId = extractRunTelemetryModelId(
650
+ msg.metadata as Record<string, unknown> | undefined,
651
+ );
652
+
653
+ const telemetrySteps: RunReportStepInit[] | undefined =
654
+ steps && steps.length > 0
655
+ ? steps.map((step) => (step.usage ? { usage: step.usage } : {}))
468
656
  : undefined;
469
657
 
470
658
  return {
471
659
  status,
472
660
  ...(toolCalls && toolCalls.length > 0 ? { toolCalls } : undefined),
473
661
  ...(steps?.length ? { totalSteps: steps.length } : undefined),
474
- ...(inputTokens != null ? { inputTokens } : undefined),
475
- ...(outputTokens != null ? { outputTokens } : undefined),
476
- ...(reasoningTokens != null ? { reasoningTokens } : undefined),
477
- ...(cachedInputTokens != null ? { cachedInputTokens } : undefined),
478
- ...(outputText != null ? { outputText } : undefined),
479
- ...(metadata ? { metadata } : undefined),
480
- ...(telemetrySteps ? { steps: telemetrySteps } : undefined),
481
- ...(modelId ? { modelId } : undefined),
482
- };
483
- }
484
-
485
- type AiSdkV6Part = {
486
- type: string;
487
- text?: string;
488
- toolName?: string;
489
- toolCallId?: string;
490
- args?: unknown;
491
- result?: unknown;
492
- input?: unknown;
493
- output?: unknown;
494
- };
495
-
496
- type AiSdkV6Message = {
497
- role?: string;
498
- parts?: readonly AiSdkV6Part[];
499
- metadata?: Record<string, unknown>;
500
- };
501
-
502
- function isToolCallPart(p: AiSdkV6Part): boolean {
503
- if (!p.toolCallId) return false;
504
- if (p.type === "tool-call" || p.type === "dynamic-tool") return !!p.toolName;
505
- return p.type.startsWith("tool-") || p.type.startsWith("dynamic-tool-");
506
- }
507
-
508
- function isDynamicToolPart(p: AiSdkV6Part): boolean {
509
- return p.type === "dynamic-tool" || p.type.startsWith("dynamic-tool-");
510
- }
511
-
512
- function partToToolCall(p: AiSdkV6Part): AssistantCloudRunReportToolCall {
513
- const toolSource: "mcp" | "frontend" = isDynamicToolPart(p)
514
- ? "mcp"
515
- : "frontend";
516
- return createRunTelemetryToolCall({
517
- toolName: p.toolName ?? p.type.slice(5),
518
- toolCallId: p.toolCallId!,
519
- args: p.args ?? p.input,
520
- result: p.result ?? p.output,
521
- toolSource,
522
- });
523
- }
524
-
525
- function collectAiSdkV6Parts(parts: readonly AiSdkV6Part[]): {
526
- textParts: string[];
527
- toolCalls: AssistantCloudRunReportToolCall[];
528
- stepsData: { tool_calls: AssistantCloudRunReportToolCall[] }[];
529
- } {
530
- const textParts: string[] = [];
531
- const toolCalls: AssistantCloudRunReportToolCall[] = [];
532
- const stepsData: { tool_calls: AssistantCloudRunReportToolCall[] }[] = [];
533
- let currentStepToolCalls: AssistantCloudRunReportToolCall[] | null = null;
534
-
535
- for (const p of parts) {
536
- if (p.type === "step-start") {
537
- if (currentStepToolCalls !== null) {
538
- stepsData.push({ tool_calls: currentStepToolCalls });
539
- }
540
- currentStepToolCalls = [];
541
- } else if (p.type === "text" && p.text) {
542
- textParts.push(p.text);
543
- } else if (isToolCallPart(p)) {
544
- const tc = partToToolCall(p);
545
- toolCalls.push(tc);
546
- if (currentStepToolCalls !== null) {
547
- currentStepToolCalls.push(tc);
548
- }
549
- }
550
- }
551
-
552
- if (currentStepToolCalls !== null) {
553
- stepsData.push({ tool_calls: currentStepToolCalls });
554
- }
555
-
556
- return { textParts, toolCalls, stepsData };
557
- }
558
-
559
- function extractModelId(
560
- metadata?: Record<string, unknown>,
561
- ): string | undefined {
562
- if (!metadata) return undefined;
563
- if (typeof metadata.modelId === "string") return metadata.modelId;
564
- const custom = metadata.custom as Record<string, unknown> | undefined;
565
- if (typeof custom?.modelId === "string") return custom.modelId;
566
- return undefined;
567
- }
568
-
569
- function buildAiSdkV6Result(
570
- textParts: string[],
571
- toolCalls: AssistantCloudRunReportToolCall[],
572
- totalSteps: number,
573
- metadata?: Record<string, unknown>,
574
- stepsData?: { tool_calls: AssistantCloudRunReportToolCall[] }[],
575
- usage?: {
576
- inputTokens?: number;
577
- outputTokens?: number;
578
- reasoningTokens?: number;
579
- cachedInputTokens?: number;
580
- },
581
- ): TelemetryData {
582
- const hasText = textParts.length > 0;
583
- const outputText = hasText
584
- ? truncateRunTelemetryText(textParts.join(""))
585
- : undefined;
586
- const modelId = extractModelId(metadata);
587
-
588
- const steps: TelemetryStepData[] | undefined =
589
- stepsData && stepsData.length > 1
590
- ? stepsData.map((s) => ({
591
- ...(s.tool_calls.length > 0
592
- ? { tool_calls: s.tool_calls }
593
- : undefined),
594
- }))
595
- : undefined;
596
-
597
- return {
598
- status: hasText ? "completed" : "incomplete",
599
- ...(toolCalls.length > 0 ? { toolCalls } : undefined),
600
- ...(totalSteps > 0 ? { totalSteps } : undefined),
601
- ...(usage?.inputTokens != null
602
- ? { inputTokens: usage.inputTokens }
603
- : undefined),
604
- ...(usage?.outputTokens != null
605
- ? { outputTokens: usage.outputTokens }
606
- : undefined),
607
- ...(usage?.reasoningTokens != null
608
- ? { reasoningTokens: usage.reasoningTokens }
609
- : undefined),
610
- ...(usage?.cachedInputTokens != null
611
- ? { cachedInputTokens: usage.cachedInputTokens }
662
+ ...(inputTokens != null ||
663
+ outputTokens != null ||
664
+ reasoningTokens != null ||
665
+ cachedInputTokens != null
666
+ ? {
667
+ usage: {
668
+ ...(inputTokens != null ? { inputTokens } : undefined),
669
+ ...(outputTokens != null ? { outputTokens } : undefined),
670
+ ...(reasoningTokens != null ? { reasoningTokens } : undefined),
671
+ ...(cachedInputTokens != null ? { cachedInputTokens } : undefined),
672
+ },
673
+ }
612
674
  : undefined),
613
675
  ...(outputText != null ? { outputText } : undefined),
614
676
  ...(metadata ? { metadata } : undefined),
615
- ...(steps ? { steps } : undefined),
677
+ ...(telemetrySteps ? { steps: telemetrySteps } : undefined),
616
678
  ...(modelId ? { modelId } : undefined),
617
679
  };
618
680
  }
619
681
 
620
- function extractAiSdkV6Usage(metadata?: Record<string, unknown>):
621
- | {
622
- inputTokens?: number;
623
- outputTokens?: number;
624
- reasoningTokens?: number;
625
- cachedInputTokens?: number;
626
- }
627
- | undefined {
628
- // Try top-level metadata.usage
629
- const usage = metadata?.usage as RunTelemetryUsageInit | undefined;
630
- if (usage) {
631
- const normalized = normalizeRunTelemetryUsage(usage);
632
- if (normalized) return normalized;
633
- }
634
-
635
- // Try aggregating from metadata.steps[].usage
636
- const steps = metadata?.steps as
637
- | readonly { usage?: RunTelemetryUsageInit }[]
638
- | undefined;
639
- if (steps && steps.length > 0) {
640
- let inputTokens = 0;
641
- let outputTokens = 0;
642
- let reasoningTokens = 0;
643
- let cachedInputTokens = 0;
644
- let hasInput = false;
645
- let hasOutput = false;
646
- let hasReasoning = false;
647
- let hasCachedInput = false;
648
- let hasAny = false;
649
- for (const s of steps) {
650
- if (!s.usage) continue;
651
- const n = normalizeRunTelemetryUsage(s.usage);
652
- if (n) {
653
- if (n.inputTokens != null) {
654
- inputTokens += n.inputTokens;
655
- hasInput = true;
656
- }
657
- if (n.outputTokens != null) {
658
- outputTokens += n.outputTokens;
659
- hasOutput = true;
660
- }
661
- if (n.reasoningTokens != null) {
662
- reasoningTokens += n.reasoningTokens;
663
- hasReasoning = true;
664
- }
665
- if (n.cachedInputTokens != null) {
666
- cachedInputTokens += n.cachedInputTokens;
667
- hasCachedInput = true;
668
- }
669
- hasAny = true;
670
- }
671
- }
672
- if (hasAny) {
673
- return {
674
- ...(hasInput ? { inputTokens } : undefined),
675
- ...(hasOutput ? { outputTokens } : undefined),
676
- ...(hasReasoning ? { reasoningTokens } : undefined),
677
- ...(hasCachedInput ? { cachedInputTokens } : undefined),
678
- };
679
- }
680
- }
681
-
682
- return undefined;
683
- }
684
-
685
- function extractAiSdkV6<T>(content: T): TelemetryData | null {
686
- const msg = content as AiSdkV6Message;
687
- if (msg.role !== "assistant") return null;
688
-
689
- const { textParts, toolCalls, stepsData } = collectAiSdkV6Parts(
690
- msg.parts ?? [],
691
- );
692
- return buildAiSdkV6Result(
693
- textParts,
694
- toolCalls,
695
- stepsData.length,
696
- msg.metadata,
697
- stepsData,
698
- extractAiSdkV6Usage(msg.metadata),
699
- );
700
- }
701
-
702
- function aggregateAiSdkV6RunSteps<T>(stepMessages: T[]): TelemetryData | null {
703
- const allTextParts: string[] = [];
704
- const allToolCalls: AssistantCloudRunReportToolCall[] = [];
705
- const allStepsData: { tool_calls: AssistantCloudRunReportToolCall[] }[] = [];
706
- let hasAssistant = false;
707
- let metadata: Record<string, unknown> | undefined;
708
- let inputTokens = 0;
709
- let outputTokens = 0;
710
- let reasoningTokens = 0;
711
- let cachedInputTokens = 0;
712
- let hasInput = false;
713
- let hasOutput = false;
714
- let hasReasoning = false;
715
- let hasCachedInput = false;
716
-
717
- for (const content of stepMessages) {
718
- const msg = content as AiSdkV6Message;
719
- if (msg.role !== "assistant") continue;
720
- hasAssistant = true;
721
-
722
- const { textParts, toolCalls, stepsData } = collectAiSdkV6Parts(
723
- msg.parts ?? [],
724
- );
725
- allTextParts.push(...textParts);
726
- allToolCalls.push(...toolCalls);
727
- allStepsData.push(...stepsData);
728
- if (msg.metadata) metadata = msg.metadata;
729
-
730
- const usage = extractAiSdkV6Usage(msg.metadata);
731
- if (usage) {
732
- if (usage.inputTokens != null) {
733
- inputTokens += usage.inputTokens;
734
- hasInput = true;
735
- }
736
- if (usage.outputTokens != null) {
737
- outputTokens += usage.outputTokens;
738
- hasOutput = true;
739
- }
740
- if (usage.reasoningTokens != null) {
741
- reasoningTokens += usage.reasoningTokens;
742
- hasReasoning = true;
743
- }
744
- if (usage.cachedInputTokens != null) {
745
- cachedInputTokens += usage.cachedInputTokens;
746
- hasCachedInput = true;
747
- }
748
- }
749
- }
750
-
751
- if (!hasAssistant) return null;
752
- return buildAiSdkV6Result(
753
- allTextParts,
754
- allToolCalls,
755
- allStepsData.length,
756
- metadata,
757
- allStepsData,
758
- {
759
- ...(hasInput ? { inputTokens } : undefined),
760
- ...(hasOutput ? { outputTokens } : undefined),
761
- ...(hasReasoning ? { reasoningTokens } : undefined),
762
- ...(hasCachedInput ? { cachedInputTokens } : undefined),
763
- },
764
- );
765
- }
766
-
767
682
  export function useAssistantCloudThreadHistoryAdapter(
768
683
  cloudRef: RefObject<AssistantCloud>,
769
- ): ThreadHistoryAdapter {
684
+ ): ThreadHistoryAdapter & { readonly feedback: FeedbackAdapter } {
770
685
  const aui = useAui();
771
686
  // Not useEffectEvent: history adapter methods run during render (SSR load).
772
687
  const auiRef = useRef(aui);
@@ -777,5 +692,180 @@ export function useAssistantCloudThreadHistoryAdapter(
777
692
  () =>
778
693
  new AssistantCloudThreadHistoryAdapter(cloudRef, () => auiRef.current),
779
694
  );
695
+ useAssistantCloudEngagementEvents(adapter, aui);
780
696
  return adapter;
781
697
  }
698
+
699
+ type RootEngagementTracker = {
700
+ count: number;
701
+ engagementReporter: CloudEngagementReporter;
702
+ dispose: () => void;
703
+ };
704
+
705
+ const rootEngagementTrackers = new WeakMap<
706
+ AssistantClient,
707
+ RootEngagementTracker
708
+ >();
709
+
710
+ /**
711
+ * Thread switches are a thread list event, so one subscription per assistant
712
+ * client reports them; every thread runtime mounted under it shares the
713
+ * subscription and the last one to unmount removes it.
714
+ */
715
+ const useRootEngagementEvents = (
716
+ adapter: AssistantCloudThreadHistoryAdapter,
717
+ aui: AssistantClient,
718
+ ) => {
719
+ useEffect(() => {
720
+ let tracker = rootEngagementTrackers.get(aui);
721
+ if (!tracker) {
722
+ const created: RootEngagementTracker = {
723
+ count: 0,
724
+ engagementReporter: adapter.engagementReporter,
725
+ dispose: () => {},
726
+ };
727
+ created.dispose = aui.on(
728
+ { scope: "*", event: "threads.selectionChanged" },
729
+ (payload) => {
730
+ created.engagementReporter.threadSwitched(payload.threadId);
731
+ },
732
+ );
733
+ rootEngagementTrackers.set(aui, created);
734
+ tracker = created;
735
+ }
736
+ const active = tracker;
737
+ active.engagementReporter = adapter.engagementReporter;
738
+ active.count += 1;
739
+ return () => {
740
+ active.count -= 1;
741
+ if (active.count === 0) {
742
+ active.dispose();
743
+ rootEngagementTrackers.delete(aui);
744
+ }
745
+ };
746
+ }, [adapter, aui]);
747
+ };
748
+
749
+ const useAssistantCloudEngagementEvents = (
750
+ adapter: AssistantCloudThreadHistoryAdapter,
751
+ aui: AssistantClient,
752
+ ) => {
753
+ useRootEngagementEvents(adapter, aui);
754
+
755
+ useEffect(() => {
756
+ const reporter = adapter.engagementReporter;
757
+
758
+ const unsubscribers = [
759
+ aui.on({ scope: "thread", event: "composer.send" }, (payload) => {
760
+ if (payload.messageId) {
761
+ reporter.messageEdited(payload.threadId, {
762
+ messageId: payload.messageId,
763
+ chars: payload.chars,
764
+ });
765
+ } else {
766
+ reporter.messageSent(payload.threadId, {
767
+ chars: payload.chars,
768
+ attachments: payload.attachments,
769
+ });
770
+ }
771
+ if (payload.suggestion) {
772
+ reporter.suggestionClicked(payload.threadId);
773
+ }
774
+ }),
775
+ aui.on(
776
+ { scope: "thread", event: "composer.attachmentAdd" },
777
+ (payload) => {
778
+ reporter.attachmentAdded(payload.threadId, {
779
+ messageId: payload.messageId,
780
+ contentType: payload.contentType,
781
+ });
782
+ },
783
+ ),
784
+ aui.on(
785
+ { scope: "thread", event: "composer.attachmentAddError" },
786
+ (payload) => {
787
+ reporter.attachmentFailed(payload.threadId, {
788
+ messageId: payload.messageId,
789
+ contentType: payload.contentType,
790
+ });
791
+ },
792
+ ),
793
+ aui.on({ scope: "thread", event: "composer.cancel" }, (payload) => {
794
+ reporter.runStopped(payload.threadId);
795
+ }),
796
+ aui.on({ scope: "thread", event: "thread.runStart" }, (payload) => {
797
+ reporter.runStarted(payload.threadId);
798
+ }),
799
+ aui.on({ scope: "thread", event: "thread.runEnd" }, (payload) => {
800
+ reporter.runEnded(payload.threadId);
801
+ }),
802
+ aui.on({ scope: "thread", event: "thread.cancelRun" }, (payload) => {
803
+ reporter.runStopped(payload.threadId);
804
+ }),
805
+ aui.on({ scope: "thread", event: "thread.voiceStarted" }, (payload) => {
806
+ reporter.voiceStarted(payload.threadId);
807
+ }),
808
+ aui.on({ scope: "thread", event: "message.reload" }, (payload) => {
809
+ reporter.messageRegenerated(payload.threadId, payload.messageId);
810
+ }),
811
+ aui.on(
812
+ { scope: "thread", event: "message.branchSwitched" },
813
+ (payload) => {
814
+ reporter.branchSwitched(payload.threadId, payload.messageId);
815
+ },
816
+ ),
817
+ aui.on({ scope: "thread", event: "message.copied" }, (payload) => {
818
+ reporter.messageCopied(payload.threadId, payload.messageId);
819
+ }),
820
+ aui.on(
821
+ { scope: "thread", event: "thread.toolApprovalAnswered" },
822
+ (payload) => {
823
+ if (payload.approved) {
824
+ reporter.toolApproved(
825
+ payload.threadId,
826
+ payload.messageId,
827
+ payload.toolCallId,
828
+ payload.toolName,
829
+ );
830
+ } else {
831
+ reporter.toolRejected(
832
+ payload.threadId,
833
+ payload.messageId,
834
+ payload.toolCallId,
835
+ payload.toolName,
836
+ );
837
+ }
838
+ },
839
+ ),
840
+ aui.on({ scope: "thread", event: "message.speak" }, (payload) => {
841
+ reporter.speechStarted(payload.threadId, payload.messageId);
842
+ }),
843
+ aui.on({ scope: "thread", event: "message.error" }, (payload) => {
844
+ reporter.errorShown(payload.threadId, {
845
+ messageId: payload.messageId,
846
+ reason: payload.reason,
847
+ });
848
+ }),
849
+ ];
850
+
851
+ return () => {
852
+ for (const unsubscribe of unsubscribers) unsubscribe();
853
+ };
854
+ }, [adapter, aui]);
855
+
856
+ useEffect(() => {
857
+ const reportSuggestions = () => {
858
+ const { mainThreadId } = aui.threads.getState();
859
+ if (aui.threadListItem.getState().id !== mainThreadId) return;
860
+ const { isEmpty, suggestions } = aui.thread.getState();
861
+ if (!isEmpty || suggestions.length === 0) return;
862
+ adapter.engagementReporter.suggestionsShown(
863
+ mainThreadId,
864
+ suggestions.length,
865
+ );
866
+ };
867
+
868
+ reportSuggestions();
869
+ return aui.subscribe(reportSuggestions);
870
+ }, [adapter, aui]);
871
+ };