@assistant-ui/core 0.3.18 → 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 (274) hide show
  1. package/dist/adapters/speech.d.ts.map +1 -1
  2. package/dist/adapters/speech.js +13 -3
  3. package/dist/adapters/speech.js.map +1 -1
  4. package/dist/adapters/thread-history.d.ts +4 -0
  5. package/dist/adapters/thread-history.d.ts.map +1 -1
  6. package/dist/index.d.ts +2 -2
  7. package/dist/index.js.map +1 -1
  8. package/dist/internal.d.ts +1 -1
  9. package/dist/model-context/frame/host.d.ts.map +1 -1
  10. package/dist/model-context/frame/host.js +26 -11
  11. package/dist/model-context/frame/host.js.map +1 -1
  12. package/dist/model-context/interactable-composer-metadata.js +11 -9
  13. package/dist/model-context/interactable-composer-metadata.js.map +1 -1
  14. package/dist/react/adapters/LocalStorageThreadListAdapter.d.ts.map +1 -1
  15. package/dist/react/adapters/LocalStorageThreadListAdapter.js +45 -7
  16. package/dist/react/adapters/LocalStorageThreadListAdapter.js.map +1 -1
  17. package/dist/react/client/DataRenderers.d.ts.map +1 -1
  18. package/dist/react/client/DataRenderers.js +20 -15
  19. package/dist/react/client/DataRenderers.js.map +1 -1
  20. package/dist/react/client/Interactables.d.ts.map +1 -1
  21. package/dist/react/client/Interactables.js +11 -5
  22. package/dist/react/client/Interactables.js.map +1 -1
  23. package/dist/react/client/RemoteThreadList.d.ts.map +1 -1
  24. package/dist/react/client/RemoteThreadList.js +3 -2
  25. package/dist/react/client/RemoteThreadList.js.map +1 -1
  26. package/dist/react/client/interactable-model-context.d.ts.map +1 -1
  27. package/dist/react/client/interactable-model-context.js +3 -2
  28. package/dist/react/client/interactable-model-context.js.map +1 -1
  29. package/dist/react/index.d.ts +2 -2
  30. package/dist/react/index.js +2 -2
  31. package/dist/react/interactables-legacy/useAssistantInteractable.d.ts.map +1 -1
  32. package/dist/react/interactables-legacy/useAssistantInteractable.js +3 -0
  33. package/dist/react/interactables-legacy/useAssistantInteractable.js.map +1 -1
  34. package/dist/react/model-context/useInteractable.d.ts.map +1 -1
  35. package/dist/react/model-context/useInteractable.js +4 -1
  36. package/dist/react/model-context/useInteractable.js.map +1 -1
  37. package/dist/react/model-context/useInteractableVersions.js +1 -1
  38. package/dist/react/primitives/message/MessageGroupedParts.d.ts +11 -2
  39. package/dist/react/primitives/message/MessageGroupedParts.d.ts.map +1 -1
  40. package/dist/react/primitives/message/MessageGroupedParts.js +3 -2
  41. package/dist/react/primitives/message/MessageGroupedParts.js.map +1 -1
  42. package/dist/react/primitives/part/PartMessages.d.ts +1 -0
  43. package/dist/react/primitives/part/PartMessages.d.ts.map +1 -1
  44. package/dist/react/primitives/part/PartMessages.js +1 -0
  45. package/dist/react/primitives/part/PartMessages.js.map +1 -1
  46. package/dist/react/primitives/thread/ThreadMessages.d.ts.map +1 -1
  47. package/dist/react/primitives/thread/ThreadMessages.js +6 -5
  48. package/dist/react/primitives/thread/ThreadMessages.js.map +1 -1
  49. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts.map +1 -1
  50. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js +2 -1
  51. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js.map +1 -1
  52. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.d.ts +2 -24
  53. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.d.ts.map +1 -1
  54. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js +135 -374
  55. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js.map +1 -1
  56. package/dist/react/runtimes/cloud/CloudFileAttachmentAdapter.d.ts +1 -0
  57. package/dist/react/runtimes/cloud/CloudFileAttachmentAdapter.d.ts.map +1 -1
  58. package/dist/react/runtimes/cloud/CloudFileAttachmentAdapter.js +22 -2
  59. package/dist/react/runtimes/cloud/CloudFileAttachmentAdapter.js.map +1 -1
  60. package/dist/react/runtimes/cloud/auiV0.d.ts +40 -15
  61. package/dist/react/runtimes/cloud/auiV0.d.ts.map +1 -1
  62. package/dist/react/runtimes/cloud/auiV0.js +62 -15
  63. package/dist/react/runtimes/cloud/auiV0.js.map +1 -1
  64. package/dist/react/runtimes/cloud/createCloudThreadListAdapter.d.ts +2 -1
  65. package/dist/react/runtimes/cloud/createCloudThreadListAdapter.d.ts.map +1 -1
  66. package/dist/react/runtimes/cloud/createCloudThreadListAdapter.js +4 -0
  67. package/dist/react/runtimes/cloud/createCloudThreadListAdapter.js.map +1 -1
  68. package/dist/react/runtimes/cloud/sdkIdentity.d.ts +6 -0
  69. package/dist/react/runtimes/cloud/sdkIdentity.d.ts.map +1 -0
  70. package/dist/react/runtimes/cloud/sdkIdentity.js +9 -0
  71. package/dist/react/runtimes/cloud/sdkIdentity.js.map +1 -0
  72. package/dist/react/runtimes/external-message-converter.d.ts +12 -2
  73. package/dist/react/runtimes/external-message-converter.d.ts.map +1 -1
  74. package/dist/react/runtimes/external-message-converter.js +26 -61
  75. package/dist/react/runtimes/external-message-converter.js.map +1 -1
  76. package/dist/react/runtimes/useRemoteThreadListRuntime.js +1 -1
  77. package/dist/react/runtimes/useRemoteThreadListRuntime.js.map +1 -1
  78. package/dist/react/utils/groupParts.d.ts +6 -1
  79. package/dist/react/utils/groupParts.d.ts.map +1 -1
  80. package/dist/react/utils/groupParts.js +6 -0
  81. package/dist/react/utils/groupParts.js.map +1 -1
  82. package/dist/react/utils/useJSONEqual.d.ts +3 -1
  83. package/dist/react/utils/useJSONEqual.d.ts.map +1 -1
  84. package/dist/react/utils/useJSONEqual.js +6 -1
  85. package/dist/react/utils/useJSONEqual.js.map +1 -1
  86. package/dist/react/utils/useJSONSchemaDependency.d.ts +7 -0
  87. package/dist/react/utils/useJSONSchemaDependency.d.ts.map +1 -0
  88. package/dist/react/utils/useJSONSchemaDependency.js +20 -0
  89. package/dist/react/utils/useJSONSchemaDependency.js.map +1 -0
  90. package/dist/runtime/api/thread-runtime.d.ts.map +1 -1
  91. package/dist/runtime/api/thread-runtime.js +2 -5
  92. package/dist/runtime/api/thread-runtime.js.map +1 -1
  93. package/dist/runtime/base/base-composer-runtime-core.d.ts +1 -0
  94. package/dist/runtime/base/base-composer-runtime-core.d.ts.map +1 -1
  95. package/dist/runtime/base/base-composer-runtime-core.js +46 -10
  96. package/dist/runtime/base/base-composer-runtime-core.js.map +1 -1
  97. package/dist/runtime/base/base-thread-runtime-core.d.ts +2 -0
  98. package/dist/runtime/base/base-thread-runtime-core.d.ts.map +1 -1
  99. package/dist/runtime/base/base-thread-runtime-core.js +155 -64
  100. package/dist/runtime/base/base-thread-runtime-core.js.map +1 -1
  101. package/dist/runtime/interfaces/thread-runtime-core.d.ts +6 -0
  102. package/dist/runtime/interfaces/thread-runtime-core.d.ts.map +1 -1
  103. package/dist/runtime/queue/message-queue.d.ts +5 -0
  104. package/dist/runtime/queue/message-queue.d.ts.map +1 -1
  105. package/dist/runtime/queue/message-queue.js +61 -6
  106. package/dist/runtime/queue/message-queue.js.map +1 -1
  107. package/dist/runtime/utils/auto-status.d.ts +6 -2
  108. package/dist/runtime/utils/auto-status.d.ts.map +1 -1
  109. package/dist/runtime/utils/auto-status.js +12 -4
  110. package/dist/runtime/utils/auto-status.js.map +1 -1
  111. package/dist/runtime/utils/external-message-conversion.d.ts +16 -4
  112. package/dist/runtime/utils/external-message-conversion.d.ts.map +1 -1
  113. package/dist/runtime/utils/external-message-conversion.js +48 -12
  114. package/dist/runtime/utils/external-message-conversion.js.map +1 -1
  115. package/dist/runtime/utils/message-repository.js +3 -3
  116. package/dist/runtime/utils/message-repository.js.map +1 -1
  117. package/dist/runtime/utils/thread-message-like.d.ts +2 -1
  118. package/dist/runtime/utils/thread-message-like.d.ts.map +1 -1
  119. package/dist/runtime/utils/thread-message-like.js +4 -2
  120. package/dist/runtime/utils/thread-message-like.js.map +1 -1
  121. package/dist/runtimes/external-store/external-store-thread-runtime-core.d.ts.map +1 -1
  122. package/dist/runtimes/external-store/external-store-thread-runtime-core.js +8 -1
  123. package/dist/runtimes/external-store/external-store-thread-runtime-core.js.map +1 -1
  124. package/dist/runtimes/internal.d.ts +1 -1
  125. package/dist/runtimes/local/local-thread-runtime-core.d.ts +1 -1
  126. package/dist/runtimes/local/local-thread-runtime-core.d.ts.map +1 -1
  127. package/dist/runtimes/local/local-thread-runtime-core.js +4 -1
  128. package/dist/runtimes/local/local-thread-runtime-core.js.map +1 -1
  129. package/dist/runtimes/tool-invocations/ToolInvocationTracker.d.ts.map +1 -1
  130. package/dist/runtimes/tool-invocations/ToolInvocationTracker.js +2 -1
  131. package/dist/runtimes/tool-invocations/ToolInvocationTracker.js.map +1 -1
  132. package/dist/store/clients/external-thread.d.ts.map +1 -1
  133. package/dist/store/clients/external-thread.js +241 -200
  134. package/dist/store/clients/external-thread.js.map +1 -1
  135. package/dist/store/clients/thread-tasks.d.ts +13 -0
  136. package/dist/store/clients/thread-tasks.d.ts.map +1 -0
  137. package/dist/store/clients/thread-tasks.js +69 -0
  138. package/dist/store/clients/thread-tasks.js.map +1 -0
  139. package/dist/store/index.d.ts +3 -2
  140. package/dist/store/index.js +3 -3
  141. package/dist/store/primitive-predicates.d.ts.map +1 -1
  142. package/dist/store/primitive-predicates.js +4 -4
  143. package/dist/store/primitive-predicates.js.map +1 -1
  144. package/dist/store/runtime-clients/thread-runtime-client.d.ts.map +1 -1
  145. package/dist/store/runtime-clients/thread-runtime-client.js +176 -128
  146. package/dist/store/runtime-clients/thread-runtime-client.js.map +1 -1
  147. package/dist/store/scope-registration.d.ts +2 -0
  148. package/dist/store/scope-registration.d.ts.map +1 -1
  149. package/dist/store/scopes/task.d.ts +36 -0
  150. package/dist/store/scopes/task.d.ts.map +1 -0
  151. package/dist/store/scopes/task.js +0 -0
  152. package/dist/store/scopes/thread.d.ts +20 -0
  153. package/dist/store/scopes/thread.d.ts.map +1 -1
  154. package/dist/subscribable/subscribable.d.ts +2 -1
  155. package/dist/subscribable/subscribable.d.ts.map +1 -1
  156. package/dist/subscribable/subscribable.js +23 -15
  157. package/dist/subscribable/subscribable.js.map +1 -1
  158. package/dist/types/index.d.ts +2 -2
  159. package/dist/types/message.d.ts +8 -1
  160. package/dist/types/message.d.ts.map +1 -1
  161. package/dist/types/message.js.map +1 -1
  162. package/dist/utils/abortable-iterable.d.ts.map +1 -1
  163. package/dist/utils/abortable-iterable.js +30 -14
  164. package/dist/utils/abortable-iterable.js.map +1 -1
  165. package/dist/utils/getGroupStatus.d.ts +18 -2
  166. package/dist/utils/getGroupStatus.d.ts.map +1 -1
  167. package/dist/utils/getGroupStatus.js +32 -7
  168. package/dist/utils/getGroupStatus.js.map +1 -1
  169. package/dist/utils/wire-media.d.ts.map +1 -1
  170. package/dist/utils/wire-media.js +2 -2
  171. package/dist/utils/wire-media.js.map +1 -1
  172. package/package.json +6 -6
  173. package/src/adapters/speech.test.ts +236 -0
  174. package/src/adapters/speech.ts +17 -10
  175. package/src/adapters/thread-history.ts +3 -0
  176. package/src/index.ts +1 -0
  177. package/src/model-context/frame/host.test.ts +113 -1
  178. package/src/model-context/frame/host.ts +39 -12
  179. package/src/model-context/interactable-composer-metadata.test.ts +30 -0
  180. package/src/model-context/interactable-composer-metadata.ts +10 -10
  181. package/src/react/adapters/LocalStorageThreadListAdapter.test.ts +244 -0
  182. package/src/react/adapters/LocalStorageThreadListAdapter.tsx +104 -7
  183. package/src/react/client/DataRenderers.test.tsx +45 -0
  184. package/src/react/client/DataRenderers.ts +13 -9
  185. package/src/react/client/Interactables.test.ts +44 -1
  186. package/src/react/client/Interactables.ts +7 -1
  187. package/src/react/client/RemoteThreadList.test.ts +41 -1
  188. package/src/react/client/RemoteThreadList.ts +15 -2
  189. package/src/react/client/interactable-model-context.test.ts +44 -0
  190. package/src/react/client/interactable-model-context.ts +3 -2
  191. package/src/react/index.ts +5 -1
  192. package/src/react/interactables-legacy/useAssistantInteractable.test.tsx +132 -0
  193. package/src/react/interactables-legacy/useAssistantInteractable.ts +10 -1
  194. package/src/react/model-context/useInteractable.test.tsx +177 -0
  195. package/src/react/model-context/useInteractable.ts +3 -0
  196. package/src/react/primitive-hooks/useActionBarCopy.test.ts +0 -1
  197. package/src/react/primitive-hooks/useSuggestionTrigger.test.ts +0 -1
  198. package/src/react/primitives/message/MessageGroupedParts.test.tsx +96 -0
  199. package/src/react/primitives/message/MessageGroupedParts.tsx +15 -4
  200. package/src/react/primitives/part/PartMessages.test.tsx +104 -1
  201. package/src/react/primitives/part/PartMessages.tsx +1 -0
  202. package/src/react/primitives/thread/ThreadMessages.test.tsx +142 -0
  203. package/src/react/primitives/thread/ThreadMessages.tsx +8 -5
  204. package/src/react/providers/ReadonlyThreadProvider.test.tsx +56 -0
  205. package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.tsx +7 -1
  206. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.test.ts +20 -14
  207. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.test.tsx +221 -1
  208. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.ts +166 -512
  209. package/src/react/runtimes/cloud/CloudFileAttachmentAdapter.test.ts +77 -0
  210. package/src/react/runtimes/cloud/CloudFileAttachmentAdapter.ts +27 -1
  211. package/src/react/runtimes/cloud/auiV0.ts +160 -25
  212. package/src/react/runtimes/cloud/createCloudThreadListAdapter.test.ts +21 -0
  213. package/src/react/runtimes/cloud/createCloudThreadListAdapter.ts +7 -1
  214. package/src/react/runtimes/cloud/sdkIdentity.ts +9 -0
  215. package/src/react/runtimes/cloud/useCloudThreadListAdapter.test.tsx +18 -4
  216. package/src/react/runtimes/external-message-converter.test.ts +136 -1
  217. package/src/react/runtimes/external-message-converter.ts +39 -93
  218. package/src/react/runtimes/useRemoteThreadListRuntime.ts +1 -1
  219. package/src/react/utils/groupParts.ts +12 -1
  220. package/src/react/utils/useJSONEqual.ts +9 -0
  221. package/src/react/utils/useJSONSchemaDependency.ts +22 -0
  222. package/src/runtime/api/thread-runtime.test.ts +58 -0
  223. package/src/runtime/api/thread-runtime.ts +2 -4
  224. package/src/runtime/base/base-composer-runtime-core.ts +69 -16
  225. package/src/runtime/base/base-thread-runtime-core.test.ts +1008 -1
  226. package/src/runtime/base/base-thread-runtime-core.ts +222 -83
  227. package/src/runtime/interfaces/thread-runtime-core.ts +6 -0
  228. package/src/runtime/queue/message-queue.ts +68 -6
  229. package/src/runtime/utils/auto-status.test.ts +112 -1
  230. package/src/runtime/utils/auto-status.ts +31 -5
  231. package/src/runtime/utils/external-message-conversion.test.ts +134 -0
  232. package/src/runtime/utils/external-message-conversion.ts +122 -31
  233. package/src/runtime/utils/message-repository.test.ts +49 -0
  234. package/src/runtime/utils/message-repository.ts +3 -3
  235. package/src/runtime/utils/thread-message-like.test.ts +52 -0
  236. package/src/runtime/utils/thread-message-like.ts +4 -0
  237. package/src/runtimes/external-store/external-store-thread-runtime-core.ts +31 -1
  238. package/src/runtimes/local/local-thread-runtime-core.test.ts +21 -0
  239. package/src/runtimes/local/local-thread-runtime-core.ts +11 -1
  240. package/src/runtimes/tool-invocations/EDGE_CASES.md +2 -6
  241. package/src/runtimes/tool-invocations/ToolInvocationTracker.test.ts +133 -0
  242. package/src/runtimes/tool-invocations/ToolInvocationTracker.ts +10 -6
  243. package/src/store/clients/external-thread.ts +19 -1
  244. package/src/store/clients/thread-tasks.test.ts +274 -0
  245. package/src/store/clients/thread-tasks.ts +138 -0
  246. package/src/store/index.ts +8 -0
  247. package/src/store/primitive-predicates.test.ts +48 -5
  248. package/src/store/primitive-predicates.ts +7 -2
  249. package/src/store/runtime-clients/thread-runtime-client.ts +31 -1
  250. package/src/store/scope-registration.ts +2 -0
  251. package/src/store/scopes/task.ts +34 -0
  252. package/src/store/scopes/thread.ts +16 -0
  253. package/src/store/thread-tasks.test.tsx +209 -0
  254. package/src/subscribable/subscribable.test.ts +156 -1
  255. package/src/subscribable/subscribable.ts +25 -15
  256. package/src/tests/MessageRepository.test.ts +1 -5
  257. package/src/tests/auiV0Encode.test.ts +300 -0
  258. package/src/tests/base-composer-runtime-core-send.test.ts +51 -0
  259. package/src/tests/base-composer-runtime-core.test.ts +314 -2
  260. package/src/tests/external-store-thread-runtime-core-adapter.test.ts +151 -0
  261. package/src/tests/external-thread-attachments.test.tsx +69 -0
  262. package/src/tests/external-thread-parity.test.tsx +34 -0
  263. package/src/tests/groupParts.test.ts +71 -0
  264. package/src/tests/message-queue.test.ts +253 -0
  265. package/src/tests/remote-thread-list-reactive-threadId.test.ts +51 -0
  266. package/src/tests/useRemoteThreadListRuntime-controlled.test.tsx +43 -0
  267. package/src/types/index.ts +1 -0
  268. package/src/types/message.ts +8 -0
  269. package/src/utils/abortable-iterable.test.ts +115 -0
  270. package/src/utils/abortable-iterable.ts +34 -23
  271. package/src/utils/getGroupStatus.test.ts +70 -4
  272. package/src/utils/getGroupStatus.ts +35 -12
  273. package/src/utils/wire-media.test.ts +10 -0
  274. package/src/utils/wire-media.ts +6 -2
@@ -7,24 +7,29 @@ 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
13
  type AssistantCloudEvent,
13
- type AssistantCloudRunReportToolCall,
14
+ CloudEngagementReporter,
14
15
  CloudMessagePersistence,
16
+ CloudRunReporter,
15
17
  createFormattedPersistence,
16
- createRunReport,
17
18
  createRunTelemetryToolCall,
18
19
  deriveRunOutcome,
19
20
  describeRunError,
20
21
  extractRunTelemetryModelId,
21
22
  normalizeRunTelemetryUsage,
23
+ type RunMessageTelemetry,
22
24
  type RunReportOutcome,
23
25
  type RunReportStepInit,
24
- type RunTelemetryUsage,
25
26
  type RunTelemetryUsageInit,
26
27
  truncateRunTelemetryText,
27
28
  } from "assistant-cloud";
29
+ import {
30
+ extractAISDKRunTelemetry,
31
+ type AISDKMessageLike,
32
+ } from "assistant-cloud/ai-sdk";
28
33
  import { auiV0Decode, auiV0Encode } from "./auiV0";
29
34
  import { type AssistantClient, getClientId, useAui } from "@assistant-ui/store";
30
35
  import type { ThreadListItemMethods } from "../../../store/scopes/thread-list-item";
@@ -43,6 +48,8 @@ const globalPersistence = new WeakMap<
43
48
  class AssistantCloudThreadHistoryAdapter implements ThreadHistoryAdapter {
44
49
  private cloudRef: RefObject<AssistantCloud>;
45
50
  private getAui: () => AssistantClient;
51
+ private runReporter: CloudRunReporter;
52
+ public readonly engagementReporter: CloudEngagementReporter;
46
53
 
47
54
  constructor(
48
55
  cloudRef: RefObject<AssistantCloud>,
@@ -50,6 +57,12 @@ class AssistantCloudThreadHistoryAdapter implements ThreadHistoryAdapter {
50
57
  ) {
51
58
  this.cloudRef = cloudRef;
52
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
+ );
53
66
  }
54
67
 
55
68
  private get aui(): AssistantClient {
@@ -215,6 +228,7 @@ class AssistantCloudThreadHistoryAdapter implements ThreadHistoryAdapter {
215
228
  options?: {
216
229
  durationMs?: number;
217
230
  stepTimestamps?: StepTimestamp[];
231
+ message?: ThreadMessage;
218
232
  },
219
233
  ) {
220
234
  const encodedRunMessages = items.map((item) =>
@@ -225,7 +239,12 @@ class AssistantCloudThreadHistoryAdapter implements ThreadHistoryAdapter {
225
239
  encodedRunMessages,
226
240
  options,
227
241
  resolvePinned(),
228
- extractLastRunMessageInfo(items, formatAdapter),
242
+ mergeRunMessageInfo(
243
+ extractLastRunMessageInfo(items, formatAdapter),
244
+ options?.message
245
+ ? extractRunMessageInfo(options.message, "aui/v0")
246
+ : undefined,
247
+ ),
229
248
  );
230
249
  },
231
250
  async load(): Promise<MessageFormatRepository<TMessage>> {
@@ -312,13 +331,15 @@ class AssistantCloudThreadHistoryAdapter implements ThreadHistoryAdapter {
312
331
  threadListItem?: CloudThreadListItem,
313
332
  messageInfo?: RunMessageInfo,
314
333
  ) {
315
- if (!this.cloudRef.current.telemetry.enabled) return;
316
-
317
334
  const item = threadListItem ?? this.aui.threadListItem;
318
335
  const remoteId = item.getState().remoteId;
319
336
  if (!remoteId) return;
320
337
 
321
- const extracted = extractRunTelemetry(format, runMessages);
338
+ const extracted =
339
+ extractRunTelemetry(format, runMessages) ??
340
+ (messageInfo?.status !== undefined
341
+ ? { status: "incomplete" as const }
342
+ : undefined);
322
343
  if (!extracted) return;
323
344
 
324
345
  this._sendReport(
@@ -345,7 +366,7 @@ class AssistantCloudThreadHistoryAdapter implements ThreadHistoryAdapter {
345
366
 
346
367
  private _sendReport(
347
368
  remoteId: string,
348
- data: TelemetryData,
369
+ data: RunMessageTelemetry,
349
370
  durationMs?: number,
350
371
  stepTimestamps?: StepTimestamp[],
351
372
  messageInfo?: RunMessageInfo,
@@ -355,7 +376,7 @@ class AssistantCloudThreadHistoryAdapter implements ThreadHistoryAdapter {
355
376
  const messageId = messageInfo?.localMessageId
356
377
  ? persistence.getResolvedRemoteId(messageInfo.localMessageId)
357
378
  : undefined;
358
- const initial = createRunReport({
379
+ void this.runReporter.report({
359
380
  threadId: remoteId,
360
381
  status: messageInfo?.status ?? data.status,
361
382
  outcome: messageInfo?.outcomeType,
@@ -365,90 +386,47 @@ class AssistantCloudThreadHistoryAdapter implements ThreadHistoryAdapter {
365
386
  traceId: messageInfo?.traceId,
366
387
  modelId: data.modelId,
367
388
  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),
389
+ usage: data.usage,
390
+ steps: mergedSteps,
375
391
  totalSteps: data.totalSteps,
376
392
  toolCalls: data.toolCalls,
377
393
  durationMs,
378
394
  firstTokenMs: messageInfo?.firstTokenMs,
379
395
  outputText: data.outputText,
380
396
  metadata: data.metadata,
381
- telemetry: this.cloudRef.current.telemetry,
382
397
  });
383
-
384
- const { beforeReport } = this.cloudRef.current.telemetry;
385
- const report = beforeReport ? beforeReport(initial) : initial;
386
- if (!report) return;
387
-
388
- this.cloudRef.current.runs.report(report).catch(() => {});
389
398
  }
390
399
  }
391
400
 
392
- type TelemetryStepData = {
393
- input_tokens?: number;
394
- output_tokens?: number;
395
- reasoning_tokens?: number;
396
- cached_input_tokens?: number;
397
- tool_calls?: AssistantCloudRunReportToolCall[];
398
- start_ms?: number;
399
- end_ms?: number;
400
- };
401
-
402
401
  type StepTimestamp = { start_ms: number; end_ms: number };
403
402
 
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
-
421
403
  function mergeStepTimestamps(
422
- steps: TelemetryStepData[] | undefined,
404
+ steps: RunReportStepInit[] | undefined,
423
405
  timestamps: StepTimestamp[] | undefined,
424
- ): TelemetryStepData[] | undefined {
406
+ ): RunReportStepInit[] | undefined {
425
407
  if (!timestamps) return steps;
426
- 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
+ }
427
414
 
428
415
  const len = Math.min(steps.length, timestamps.length);
429
- return steps.map((s, i) => ({
430
- ...s,
431
- ...(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),
432
424
  }));
433
425
  }
434
426
 
435
- type TelemetryData = {
436
- status: "completed" | "incomplete" | "error";
437
- toolCalls?: AssistantCloudRunReportToolCall[];
438
- totalSteps?: number;
439
- inputTokens?: number;
440
- outputTokens?: number;
441
- reasoningTokens?: number;
442
- cachedInputTokens?: number;
443
- outputText?: string;
444
- metadata?: Record<string, unknown>;
445
- steps?: TelemetryStepData[];
446
- modelId?: string;
447
- };
448
-
449
427
  type RunMessageInfo = {
450
428
  localMessageId?: string;
451
- status?: "error";
429
+ status?: "completed" | "incomplete" | "error";
452
430
  outcomeType?: RunReportOutcome;
453
431
  error?: string;
454
432
  errorCode?: string;
@@ -476,6 +454,15 @@ function extractLastRunMessageInfo<
476
454
  return undefined;
477
455
  }
478
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
+
479
466
  function extractRunMessageInfo(
480
467
  message: unknown,
481
468
  format: string,
@@ -487,27 +474,30 @@ function extractRunMessageInfo(
487
474
  const metadata = isRecord(message.metadata) ? message.metadata : undefined;
488
475
  const custom = isRecord(metadata?.custom) ? metadata.custom : undefined;
489
476
  const timing = isRecord(metadata?.timing) ? metadata.timing : undefined;
490
- const streamStartTime = timing?.streamStartTime;
491
477
  const firstTokenTime = timing?.firstTokenTime;
492
478
  const firstTokenMs =
493
- typeof streamStartTime === "number" &&
494
- Number.isFinite(streamStartTime) &&
495
- typeof firstTokenTime === "number" &&
496
- Number.isFinite(firstTokenTime)
497
- ? Math.round(firstTokenTime - streamStartTime)
479
+ typeof firstTokenTime === "number" && Number.isFinite(firstTokenTime)
480
+ ? Math.round(firstTokenTime)
498
481
  : undefined;
499
482
  const finishReason =
500
483
  status?.type === "incomplete"
501
- ? status.reason
484
+ ? typeof status.reason === "string"
485
+ ? status.reason
486
+ : undefined
502
487
  : typeof metadata?.finishReason === "string"
503
488
  ? metadata.finishReason
504
489
  : undefined;
505
490
  const failed = status?.type === "incomplete" && status.reason === "error";
506
- const outcome = deriveRunOutcome({
507
- finishReason: typeof finishReason === "string" ? finishReason : undefined,
508
- isError: failed,
509
- });
491
+ const outcome = deriveRunOutcome({ finishReason, isError: failed });
510
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;
511
501
  const failure = failed ? describeRunError(status.error) : {};
512
502
  const messageId =
513
503
  localMessageId ?? (typeof message.id === "string" ? message.id : undefined);
@@ -526,7 +516,7 @@ function extractRunMessageInfo(
526
516
 
527
517
  return {
528
518
  ...(messageId ? { localMessageId: messageId } : undefined),
529
- ...(outcome.status === "error" ? { status: "error" as const } : undefined),
519
+ ...(runStatus !== undefined ? { status: runStatus } : undefined),
530
520
  ...(outcomeType ? { outcomeType } : undefined),
531
521
  ...failure,
532
522
  ...(firstTokenMs != null && firstTokenMs >= 0
@@ -541,12 +531,15 @@ function isRecord(value: unknown): value is Record<string, unknown> {
541
531
  return value !== null && typeof value === "object";
542
532
  }
543
533
 
544
- function extractTelemetry<T>(format: string, content: T): TelemetryData | null {
534
+ function extractTelemetry<T>(
535
+ format: string,
536
+ content: T,
537
+ ): RunMessageTelemetry | null {
545
538
  switch (format) {
546
539
  case "aui/v0":
547
540
  return extractAuiV0(content);
548
541
  case "ai-sdk/v6":
549
- return extractAiSdkV6(content);
542
+ return extractAISDKRunTelemetry([content as AISDKMessageLike]);
550
543
  default:
551
544
  return null;
552
545
  }
@@ -555,9 +548,9 @@ function extractTelemetry<T>(format: string, content: T): TelemetryData | null {
555
548
  function extractRunTelemetry<T>(
556
549
  format: string,
557
550
  runMessages: T[],
558
- ): TelemetryData | null {
551
+ ): RunMessageTelemetry | null {
559
552
  if (format === "ai-sdk/v6") {
560
- return aggregateAiSdkV6RunSteps(runMessages);
553
+ return extractAISDKRunTelemetry(runMessages as AISDKMessageLike[]);
561
554
  }
562
555
  for (let i = runMessages.length - 1; i >= 0; i--) {
563
556
  const result = extractTelemetry(format, runMessages[i]!);
@@ -566,12 +559,7 @@ function extractRunTelemetry<T>(
566
559
  return null;
567
560
  }
568
561
 
569
- const AUI_STATUS_MAP: Record<string, TelemetryData["status"]> = {
570
- error: "error",
571
- incomplete: "incomplete",
572
- };
573
-
574
- export function extractAuiV0<T>(content: T): TelemetryData | null {
562
+ export function extractAuiV0<T>(content: T): RunMessageTelemetry | null {
575
563
  const msg = content as {
576
564
  role?: string;
577
565
  status?: { type: string; reason?: string };
@@ -655,317 +643,42 @@ export function extractAuiV0<T>(content: T): TelemetryData | null {
655
643
  cachedInputTokens = hasCachedInput ? totalCachedInput : undefined;
656
644
  }
657
645
 
658
- const statusType = msg.status?.type;
659
- const status: TelemetryData["status"] =
660
- statusType === "incomplete" && msg.status?.reason === "error"
661
- ? "error"
662
- : (statusType && AUI_STATUS_MAP[statusType]) || "completed";
646
+ const status = msg.status?.type === "incomplete" ? "incomplete" : "completed";
663
647
 
664
648
  const metadata = msg.metadata?.custom as Record<string, unknown> | undefined;
665
649
  const modelId = extractRunTelemetryModelId(
666
650
  msg.metadata as Record<string, unknown> | undefined,
667
651
  );
668
652
 
669
- const telemetrySteps: TelemetryStepData[] | undefined =
670
- steps && steps.length > 1
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
- })
653
+ const telemetrySteps: RunReportStepInit[] | undefined =
654
+ steps && steps.length > 0
655
+ ? steps.map((step) => (step.usage ? { usage: step.usage } : {}))
690
656
  : undefined;
691
657
 
692
658
  return {
693
659
  status,
694
660
  ...(toolCalls && toolCalls.length > 0 ? { toolCalls } : undefined),
695
661
  ...(steps?.length ? { totalSteps: steps.length } : undefined),
696
- ...(inputTokens != null ? { inputTokens } : undefined),
697
- ...(outputTokens != null ? { outputTokens } : undefined),
698
- ...(reasoningTokens != null ? { reasoningTokens } : undefined),
699
- ...(cachedInputTokens != null ? { cachedInputTokens } : undefined),
700
- ...(outputText != null ? { outputText } : undefined),
701
- ...(metadata ? { metadata } : undefined),
702
- ...(telemetrySteps ? { steps: telemetrySteps } : undefined),
703
- ...(modelId ? { modelId } : undefined),
704
- };
705
- }
706
-
707
- type AiSdkV6Part = {
708
- type: string;
709
- text?: string;
710
- toolName?: string;
711
- toolCallId?: string;
712
- args?: unknown;
713
- result?: unknown;
714
- input?: unknown;
715
- output?: unknown;
716
- };
717
-
718
- type AiSdkV6Message = {
719
- role?: string;
720
- parts?: readonly AiSdkV6Part[];
721
- metadata?: Record<string, unknown>;
722
- };
723
-
724
- function isToolCallPart(p: AiSdkV6Part): boolean {
725
- if (!p.toolCallId) return false;
726
- if (p.type === "tool-call" || p.type === "dynamic-tool") return !!p.toolName;
727
- return p.type.startsWith("tool-") || p.type.startsWith("dynamic-tool-");
728
- }
729
-
730
- function isDynamicToolPart(p: AiSdkV6Part): boolean {
731
- return p.type === "dynamic-tool" || p.type.startsWith("dynamic-tool-");
732
- }
733
-
734
- function partToToolCall(p: AiSdkV6Part): AssistantCloudRunReportToolCall {
735
- const toolSource: "mcp" | "frontend" = isDynamicToolPart(p)
736
- ? "mcp"
737
- : "frontend";
738
- return createRunTelemetryToolCall({
739
- toolName: p.toolName ?? p.type.slice(5),
740
- toolCallId: p.toolCallId!,
741
- args: p.args ?? p.input,
742
- result: p.result ?? p.output,
743
- toolSource,
744
- });
745
- }
746
-
747
- function collectAiSdkV6Parts(parts: readonly AiSdkV6Part[]): {
748
- textParts: string[];
749
- toolCalls: AssistantCloudRunReportToolCall[];
750
- stepsData: { tool_calls: AssistantCloudRunReportToolCall[] }[];
751
- } {
752
- const textParts: string[] = [];
753
- const toolCalls: AssistantCloudRunReportToolCall[] = [];
754
- const stepsData: { tool_calls: AssistantCloudRunReportToolCall[] }[] = [];
755
- let currentStepToolCalls: AssistantCloudRunReportToolCall[] | null = null;
756
-
757
- for (const p of parts) {
758
- if (p.type === "step-start") {
759
- if (currentStepToolCalls !== null) {
760
- stepsData.push({ tool_calls: currentStepToolCalls });
761
- }
762
- currentStepToolCalls = [];
763
- } else if (p.type === "text" && p.text) {
764
- textParts.push(p.text);
765
- } else if (isToolCallPart(p)) {
766
- const tc = partToToolCall(p);
767
- toolCalls.push(tc);
768
- if (currentStepToolCalls !== null) {
769
- currentStepToolCalls.push(tc);
770
- }
771
- }
772
- }
773
-
774
- if (currentStepToolCalls !== null) {
775
- stepsData.push({ tool_calls: currentStepToolCalls });
776
- }
777
-
778
- return { textParts, toolCalls, stepsData };
779
- }
780
-
781
- function buildAiSdkV6Result(
782
- textParts: string[],
783
- toolCalls: AssistantCloudRunReportToolCall[],
784
- totalSteps: number,
785
- metadata?: Record<string, unknown>,
786
- stepsData?: { tool_calls: AssistantCloudRunReportToolCall[] }[],
787
- usage?: RunTelemetryUsage,
788
- ): TelemetryData {
789
- const hasText = textParts.length > 0;
790
- const outputText = hasText
791
- ? truncateRunTelemetryText(textParts.join(""))
792
- : undefined;
793
- const modelId = extractRunTelemetryModelId(metadata);
794
-
795
- const steps: TelemetryStepData[] | undefined =
796
- stepsData && stepsData.length > 1
797
- ? stepsData.map((s) => ({
798
- ...(s.tool_calls.length > 0
799
- ? { tool_calls: s.tool_calls }
800
- : undefined),
801
- }))
802
- : undefined;
803
-
804
- return {
805
- status: hasText ? "completed" : "incomplete",
806
- ...(toolCalls.length > 0 ? { toolCalls } : undefined),
807
- ...(totalSteps > 0 ? { totalSteps } : undefined),
808
- ...(usage?.inputTokens != null
809
- ? { inputTokens: usage.inputTokens }
810
- : undefined),
811
- ...(usage?.outputTokens != null
812
- ? { outputTokens: usage.outputTokens }
813
- : undefined),
814
- ...(usage?.reasoningTokens != null
815
- ? { reasoningTokens: usage.reasoningTokens }
816
- : undefined),
817
- ...(usage?.cachedInputTokens != null
818
- ? { 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
+ }
819
674
  : undefined),
820
675
  ...(outputText != null ? { outputText } : undefined),
821
676
  ...(metadata ? { metadata } : undefined),
822
- ...(steps ? { steps } : undefined),
677
+ ...(telemetrySteps ? { steps: telemetrySteps } : undefined),
823
678
  ...(modelId ? { modelId } : undefined),
824
679
  };
825
680
  }
826
681
 
827
- function extractAiSdkV6Usage(
828
- metadata?: Record<string, unknown>,
829
- ): RunTelemetryUsage | undefined {
830
- // Try top-level metadata.usage
831
- const usage = metadata?.usage as RunTelemetryUsageInit | undefined;
832
- if (usage) {
833
- const normalized = normalizeRunTelemetryUsage(usage);
834
- if (normalized) return normalized;
835
- }
836
-
837
- // Try aggregating from metadata.steps[].usage
838
- const steps = metadata?.steps as
839
- | readonly { usage?: RunTelemetryUsageInit }[]
840
- | undefined;
841
- if (steps && steps.length > 0) {
842
- let inputTokens = 0;
843
- let outputTokens = 0;
844
- let reasoningTokens = 0;
845
- let cachedInputTokens = 0;
846
- let hasInput = false;
847
- let hasOutput = false;
848
- let hasReasoning = false;
849
- let hasCachedInput = false;
850
- let hasAny = false;
851
- for (const s of steps) {
852
- if (!s.usage) continue;
853
- const n = normalizeRunTelemetryUsage(s.usage);
854
- if (n) {
855
- if (n.inputTokens != null) {
856
- inputTokens += n.inputTokens;
857
- hasInput = true;
858
- }
859
- if (n.outputTokens != null) {
860
- outputTokens += n.outputTokens;
861
- hasOutput = true;
862
- }
863
- if (n.reasoningTokens != null) {
864
- reasoningTokens += n.reasoningTokens;
865
- hasReasoning = true;
866
- }
867
- if (n.cachedInputTokens != null) {
868
- cachedInputTokens += n.cachedInputTokens;
869
- hasCachedInput = true;
870
- }
871
- hasAny = true;
872
- }
873
- }
874
- if (hasAny) {
875
- return {
876
- ...(hasInput ? { inputTokens } : undefined),
877
- ...(hasOutput ? { outputTokens } : undefined),
878
- ...(hasReasoning ? { reasoningTokens } : undefined),
879
- ...(hasCachedInput ? { cachedInputTokens } : undefined),
880
- };
881
- }
882
- }
883
-
884
- return undefined;
885
- }
886
-
887
- function extractAiSdkV6<T>(content: T): TelemetryData | null {
888
- const msg = content as AiSdkV6Message;
889
- if (msg.role !== "assistant") return null;
890
-
891
- const { textParts, toolCalls, stepsData } = collectAiSdkV6Parts(
892
- msg.parts ?? [],
893
- );
894
- return buildAiSdkV6Result(
895
- textParts,
896
- toolCalls,
897
- stepsData.length,
898
- msg.metadata,
899
- stepsData,
900
- extractAiSdkV6Usage(msg.metadata),
901
- );
902
- }
903
-
904
- function aggregateAiSdkV6RunSteps<T>(stepMessages: T[]): TelemetryData | null {
905
- const allTextParts: string[] = [];
906
- const allToolCalls: AssistantCloudRunReportToolCall[] = [];
907
- const allStepsData: { tool_calls: AssistantCloudRunReportToolCall[] }[] = [];
908
- let hasAssistant = false;
909
- let metadata: Record<string, unknown> | undefined;
910
- let inputTokens = 0;
911
- let outputTokens = 0;
912
- let reasoningTokens = 0;
913
- let cachedInputTokens = 0;
914
- let hasInput = false;
915
- let hasOutput = false;
916
- let hasReasoning = false;
917
- let hasCachedInput = false;
918
-
919
- for (const content of stepMessages) {
920
- const msg = content as AiSdkV6Message;
921
- if (msg.role !== "assistant") continue;
922
- hasAssistant = true;
923
-
924
- const { textParts, toolCalls, stepsData } = collectAiSdkV6Parts(
925
- msg.parts ?? [],
926
- );
927
- allTextParts.push(...textParts);
928
- allToolCalls.push(...toolCalls);
929
- allStepsData.push(...stepsData);
930
- if (msg.metadata) metadata = msg.metadata;
931
-
932
- const usage = extractAiSdkV6Usage(msg.metadata);
933
- if (usage) {
934
- if (usage.inputTokens != null) {
935
- inputTokens += usage.inputTokens;
936
- hasInput = true;
937
- }
938
- if (usage.outputTokens != null) {
939
- outputTokens += usage.outputTokens;
940
- hasOutput = true;
941
- }
942
- if (usage.reasoningTokens != null) {
943
- reasoningTokens += usage.reasoningTokens;
944
- hasReasoning = true;
945
- }
946
- if (usage.cachedInputTokens != null) {
947
- cachedInputTokens += usage.cachedInputTokens;
948
- hasCachedInput = true;
949
- }
950
- }
951
- }
952
-
953
- if (!hasAssistant) return null;
954
- return buildAiSdkV6Result(
955
- allTextParts,
956
- allToolCalls,
957
- allStepsData.length,
958
- metadata,
959
- allStepsData,
960
- {
961
- ...(hasInput ? { inputTokens } : undefined),
962
- ...(hasOutput ? { outputTokens } : undefined),
963
- ...(hasReasoning ? { reasoningTokens } : undefined),
964
- ...(hasCachedInput ? { cachedInputTokens } : undefined),
965
- },
966
- );
967
- }
968
-
969
682
  export function useAssistantCloudThreadHistoryAdapter(
970
683
  cloudRef: RefObject<AssistantCloud>,
971
684
  ): ThreadHistoryAdapter & { readonly feedback: FeedbackAdapter } {
@@ -979,14 +692,13 @@ export function useAssistantCloudThreadHistoryAdapter(
979
692
  () =>
980
693
  new AssistantCloudThreadHistoryAdapter(cloudRef, () => auiRef.current),
981
694
  );
982
- useAssistantCloudEngagementEvents(cloudRef, adapter, aui);
695
+ useAssistantCloudEngagementEvents(adapter, aui);
983
696
  return adapter;
984
697
  }
985
698
 
986
699
  type RootEngagementTracker = {
987
700
  count: number;
988
- adapter: AssistantCloudThreadHistoryAdapter;
989
- cloudRef: RefObject<AssistantCloud>;
701
+ engagementReporter: CloudEngagementReporter;
990
702
  dispose: () => void;
991
703
  };
992
704
 
@@ -1001,7 +713,6 @@ const rootEngagementTrackers = new WeakMap<
1001
713
  * subscription and the last one to unmount removes it.
1002
714
  */
1003
715
  const useRootEngagementEvents = (
1004
- cloudRef: RefObject<AssistantCloud>,
1005
716
  adapter: AssistantCloudThreadHistoryAdapter,
1006
717
  aui: AssistantClient,
1007
718
  ) => {
@@ -1010,31 +721,20 @@ const useRootEngagementEvents = (
1010
721
  if (!tracker) {
1011
722
  const created: RootEngagementTracker = {
1012
723
  count: 0,
1013
- adapter,
1014
- cloudRef,
724
+ engagementReporter: adapter.engagementReporter,
1015
725
  dispose: () => {},
1016
726
  };
1017
727
  created.dispose = aui.on(
1018
728
  { scope: "*", event: "threads.selectionChanged" },
1019
729
  (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(() => {});
730
+ created.engagementReporter.threadSwitched(payload.threadId);
1030
731
  },
1031
732
  );
1032
733
  rootEngagementTrackers.set(aui, created);
1033
734
  tracker = created;
1034
735
  }
1035
736
  const active = tracker;
1036
- active.adapter = adapter;
1037
- active.cloudRef = cloudRef;
737
+ active.engagementReporter = adapter.engagementReporter;
1038
738
  active.count += 1;
1039
739
  return () => {
1040
740
  active.count -= 1;
@@ -1043,175 +743,129 @@ const useRootEngagementEvents = (
1043
743
  rootEngagementTrackers.delete(aui);
1044
744
  }
1045
745
  };
1046
- }, [adapter, aui, cloudRef]);
746
+ }, [adapter, aui]);
1047
747
  };
1048
748
 
1049
749
  const useAssistantCloudEngagementEvents = (
1050
- cloudRef: RefObject<AssistantCloud>,
1051
750
  adapter: AssistantCloudThreadHistoryAdapter,
1052
751
  aui: AssistantClient,
1053
752
  ) => {
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>());
753
+ useRootEngagementEvents(adapter, aui);
1058
754
 
1059
755
  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
- };
756
+ const reporter = adapter.engagementReporter;
1082
757
 
1083
758
  const unsubscribers = [
1084
759
  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
- );
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
+ }
1105
771
  if (payload.suggestion) {
1106
- track({ kind: "suggestion_clicked" }, payload.threadId);
772
+ reporter.suggestionClicked(payload.threadId);
1107
773
  }
1108
774
  }),
1109
775
  aui.on(
1110
776
  { scope: "thread", event: "composer.attachmentAdd" },
1111
777
  (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
- );
778
+ reporter.attachmentAdded(payload.threadId, {
779
+ messageId: payload.messageId,
780
+ contentType: payload.contentType,
781
+ });
1122
782
  },
1123
783
  ),
1124
784
  aui.on(
1125
785
  { scope: "thread", event: "composer.attachmentAddError" },
1126
786
  (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
- );
787
+ reporter.attachmentFailed(payload.threadId, {
788
+ messageId: payload.messageId,
789
+ contentType: payload.contentType,
790
+ });
1137
791
  },
1138
792
  ),
1139
793
  aui.on({ scope: "thread", event: "composer.cancel" }, (payload) => {
1140
- trackRunStopped(payload.threadId);
794
+ reporter.runStopped(payload.threadId);
1141
795
  }),
1142
796
  aui.on({ scope: "thread", event: "thread.runStart" }, (payload) => {
1143
- runStartedAt.current.set(payload.threadId, Date.now());
797
+ reporter.runStarted(payload.threadId);
1144
798
  }),
1145
799
  aui.on({ scope: "thread", event: "thread.runEnd" }, (payload) => {
1146
- runStartedAt.current.delete(payload.threadId);
1147
- runEndedAt.current.set(payload.threadId, Date.now());
800
+ reporter.runEnded(payload.threadId);
1148
801
  }),
1149
802
  aui.on({ scope: "thread", event: "thread.cancelRun" }, (payload) => {
1150
- trackRunStopped(payload.threadId);
803
+ reporter.runStopped(payload.threadId);
1151
804
  }),
1152
805
  aui.on({ scope: "thread", event: "thread.voiceStarted" }, (payload) => {
1153
- track({ kind: "voice_started" }, payload.threadId);
806
+ reporter.voiceStarted(payload.threadId);
1154
807
  }),
1155
808
  aui.on({ scope: "thread", event: "message.reload" }, (payload) => {
1156
- track(
1157
- { kind: "message_regenerated" },
1158
- payload.threadId,
1159
- payload.messageId,
1160
- );
809
+ reporter.messageRegenerated(payload.threadId, payload.messageId);
1161
810
  }),
1162
811
  aui.on(
1163
812
  { scope: "thread", event: "message.branchSwitched" },
1164
813
  (payload) => {
1165
- track(
1166
- { kind: "branch_switched" },
1167
- payload.threadId,
1168
- payload.messageId,
1169
- );
814
+ reporter.branchSwitched(payload.threadId, payload.messageId);
1170
815
  },
1171
816
  ),
1172
817
  aui.on({ scope: "thread", event: "message.copied" }, (payload) => {
1173
- track({ kind: "message_copied" }, payload.threadId, payload.messageId);
818
+ reporter.messageCopied(payload.threadId, payload.messageId);
1174
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
+ ),
1175
840
  aui.on({ scope: "thread", event: "message.speak" }, (payload) => {
1176
- track({ kind: "speech_started" }, payload.threadId, payload.messageId);
841
+ reporter.speechStarted(payload.threadId, payload.messageId);
1177
842
  }),
1178
843
  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
- );
844
+ reporter.errorShown(payload.threadId, {
845
+ messageId: payload.messageId,
846
+ reason: payload.reason,
847
+ });
1184
848
  }),
1185
849
  ];
1186
850
 
1187
851
  return () => {
1188
852
  for (const unsubscribe of unsubscribers) unsubscribe();
1189
853
  };
1190
- }, [adapter, aui, cloudRef]);
854
+ }, [adapter, aui]);
1191
855
 
1192
856
  useEffect(() => {
1193
857
  const reportSuggestions = () => {
1194
858
  const { mainThreadId } = aui.threads.getState();
1195
859
  if (aui.threadListItem.getState().id !== mainThreadId) return;
1196
860
  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
- });
861
+ if (!isEmpty || suggestions.length === 0) return;
862
+ adapter.engagementReporter.suggestionsShown(
863
+ mainThreadId,
864
+ suggestions.length,
865
+ );
1212
866
  };
1213
867
 
1214
868
  reportSuggestions();
1215
869
  return aui.subscribe(reportSuggestions);
1216
- }, [adapter, aui, cloudRef]);
870
+ }, [adapter, aui]);
1217
871
  };