assistant-cloud 0.2.0 → 0.2.2

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 (109) hide show
  1. package/README.md +48 -6
  2. package/dist/AssistantCloud.d.ts +3 -3
  3. package/dist/AssistantCloud.d.ts.map +1 -1
  4. package/dist/AssistantCloud.js +2 -0
  5. package/dist/AssistantCloud.js.map +1 -1
  6. package/dist/AssistantCloudAPI.d.ts +14 -6
  7. package/dist/AssistantCloudAPI.d.ts.map +1 -1
  8. package/dist/AssistantCloudAPI.js +17 -1
  9. package/dist/AssistantCloudAPI.js.map +1 -1
  10. package/dist/AssistantCloudAuthStrategy.d.ts +6 -7
  11. package/dist/AssistantCloudAuthStrategy.d.ts.map +1 -1
  12. package/dist/AssistantCloudAuthStrategy.js +4 -2
  13. package/dist/AssistantCloudAuthStrategy.js.map +1 -1
  14. package/dist/AssistantCloudAuthTokens.d.ts +1 -2
  15. package/dist/AssistantCloudAuthTokens.d.ts.map +1 -1
  16. package/dist/AssistantCloudEvents.d.ts +3 -4
  17. package/dist/AssistantCloudEvents.d.ts.map +1 -1
  18. package/dist/AssistantCloudEvents.js +5 -1
  19. package/dist/AssistantCloudEvents.js.map +1 -1
  20. package/dist/AssistantCloudFiles.d.ts +2 -3
  21. package/dist/AssistantCloudFiles.d.ts.map +1 -1
  22. package/dist/AssistantCloudProjectThreadMessages.d.ts +1 -2
  23. package/dist/AssistantCloudProjectThreadMessages.d.ts.map +1 -1
  24. package/dist/AssistantCloudProjectThreads.d.ts +1 -2
  25. package/dist/AssistantCloudProjectThreads.d.ts.map +1 -1
  26. package/dist/AssistantCloudProjects.d.ts +1 -2
  27. package/dist/AssistantCloudProjects.d.ts.map +1 -1
  28. package/dist/AssistantCloudRuns.d.ts +3 -3
  29. package/dist/AssistantCloudRuns.d.ts.map +1 -1
  30. package/dist/AssistantCloudRuns.js +2 -1
  31. package/dist/AssistantCloudRuns.js.map +1 -1
  32. package/dist/AssistantCloudScores.d.ts +3 -4
  33. package/dist/AssistantCloudScores.d.ts.map +1 -1
  34. package/dist/AssistantCloudThreadMessages.d.ts +7 -6
  35. package/dist/AssistantCloudThreadMessages.d.ts.map +1 -1
  36. package/dist/AssistantCloudThreadMessages.js +7 -2
  37. package/dist/AssistantCloudThreadMessages.js.map +1 -1
  38. package/dist/AssistantCloudThreads.d.ts +3 -4
  39. package/dist/AssistantCloudThreads.d.ts.map +1 -1
  40. package/dist/CloudEngagementReporter.d.ts +73 -0
  41. package/dist/CloudEngagementReporter.d.ts.map +1 -0
  42. package/dist/CloudEngagementReporter.js +132 -0
  43. package/dist/CloudEngagementReporter.js.map +1 -0
  44. package/dist/CloudMessagePersistence.d.ts +9 -11
  45. package/dist/CloudMessagePersistence.d.ts.map +1 -1
  46. package/dist/CloudMessagePersistence.js +18 -11
  47. package/dist/CloudMessagePersistence.js.map +1 -1
  48. package/dist/CloudRunReporter.d.ts +19 -0
  49. package/dist/CloudRunReporter.d.ts.map +1 -0
  50. package/dist/CloudRunReporter.js +47 -0
  51. package/dist/CloudRunReporter.js.map +1 -0
  52. package/dist/FormattedCloudPersistence.d.ts +5 -11
  53. package/dist/FormattedCloudPersistence.d.ts.map +1 -1
  54. package/dist/FormattedCloudPersistence.js +3 -8
  55. package/dist/FormattedCloudPersistence.js.map +1 -1
  56. package/dist/ai-sdk/index.d.ts +32 -0
  57. package/dist/ai-sdk/index.d.ts.map +1 -0
  58. package/dist/ai-sdk/index.js +147 -0
  59. package/dist/ai-sdk/index.js.map +1 -0
  60. package/dist/cloudResponse.d.ts +12 -13
  61. package/dist/cloudResponse.d.ts.map +1 -1
  62. package/dist/generateThreadTitle.d.ts +1 -2
  63. package/dist/generateThreadTitle.d.ts.map +1 -1
  64. package/dist/generateThreadTitle.js +1 -0
  65. package/dist/generateThreadTitle.js.map +1 -1
  66. package/dist/index.d.ts +5 -3
  67. package/dist/index.js +4 -2
  68. package/dist/instrumentMcpSampling.d.ts +6 -7
  69. package/dist/instrumentMcpSampling.d.ts.map +1 -1
  70. package/dist/instrumentMcpSampling.js +12 -2
  71. package/dist/instrumentMcpSampling.js.map +1 -1
  72. package/dist/runTelemetry.d.ts +30 -15
  73. package/dist/runTelemetry.d.ts.map +1 -1
  74. package/dist/runTelemetry.js.map +1 -1
  75. package/dist/telemetry/index.d.ts +8 -9
  76. package/dist/telemetry/index.d.ts.map +1 -1
  77. package/dist/version.d.ts +4 -0
  78. package/dist/version.d.ts.map +1 -0
  79. package/dist/version.js +6 -0
  80. package/dist/version.js.map +1 -0
  81. package/package.json +24 -10
  82. package/src/AssistantCloud.ts +3 -0
  83. package/src/AssistantCloudAPI.ts +31 -1
  84. package/src/AssistantCloudAuthStrategy.ts +4 -2
  85. package/src/AssistantCloudEvents.test.ts +68 -2
  86. package/src/AssistantCloudEvents.ts +6 -0
  87. package/src/AssistantCloudRuns.ts +1 -0
  88. package/src/AssistantCloudThreadMessages.test.ts +53 -2
  89. package/src/AssistantCloudThreadMessages.ts +13 -1
  90. package/src/CloudEngagementReporter.ts +231 -0
  91. package/src/CloudMessagePersistence.ts +24 -13
  92. package/src/CloudRunReporter.ts +50 -0
  93. package/src/FormattedCloudPersistence.ts +3 -8
  94. package/src/ai-sdk/index.test.ts +258 -0
  95. package/src/ai-sdk/index.ts +222 -0
  96. package/src/generateThreadTitle.test.ts +32 -0
  97. package/src/generateThreadTitle.ts +1 -0
  98. package/src/index.ts +11 -1
  99. package/src/instrumentMcpSampling.test.ts +102 -0
  100. package/src/instrumentMcpSampling.ts +16 -2
  101. package/src/runTelemetry.ts +17 -0
  102. package/src/tests/AssistantCloud.test.ts +33 -1
  103. package/src/tests/AssistantCloudAPI.test.ts +51 -0
  104. package/src/tests/AssistantCloudAuthStrategy.test.ts +22 -0
  105. package/src/tests/AssistantCloudProjects.test.ts +1 -5
  106. package/src/tests/CloudEngagementReporter.test.ts +184 -0
  107. package/src/tests/CloudMessagePersistence.test.ts +235 -0
  108. package/src/tests/CloudRunReporter.test.ts +135 -0
  109. package/src/version.ts +4 -0
@@ -1 +1 @@
1
- {"version":3,"file":"instrumentMcpSampling.js","names":[],"sources":["../src/instrumentMcpSampling.ts"],"sourcesContent":["/**\n * MCP sampling instrumentation utility.\n *\n * Wraps an MCP client's sampling handler to capture nested LLM calls\n * (sampling/createMessage requests) made during tool execution.\n * The captured data can be reported as child generation spans.\n */\n\nexport type SamplingCallData = {\n model_id?: string;\n input_tokens?: number;\n output_tokens?: number;\n reasoning_tokens?: number;\n cached_input_tokens?: number;\n duration_ms?: number;\n};\n\nexport type McpSamplingHandler = (\n request: McpSamplingRequest,\n) => Promise<McpSamplingResponse>;\n\nexport type McpSamplingRequest = {\n method: \"sampling/createMessage\";\n params: {\n messages: unknown[];\n modelPreferences?: { hints?: { name?: string }[] };\n maxTokens?: number;\n [key: string]: unknown;\n };\n};\n\nexport type McpSamplingResponse = {\n model?: string;\n content: unknown;\n usage?: {\n inputTokens?: number;\n outputTokens?: number;\n promptTokens?: number;\n completionTokens?: number;\n reasoningTokens?: number;\n cachedInputTokens?: number;\n };\n [key: string]: unknown;\n};\n\n/**\n * Wraps an MCP sampling handler to intercept and measure sampling calls.\n *\n * @param handler - The original sampling handler from the MCP client\n * @param onSamplingCall - Callback invoked with metrics for each sampling call\n * @returns A wrapped handler that transparently captures sampling metrics\n *\n * @example\n * ```ts\n * const samplingCalls: SamplingCallData[] = [];\n * const wrapped = wrapSamplingHandler(\n * originalHandler,\n * (data) => samplingCalls.push(data),\n * );\n * // Use `wrapped` as the MCP client's sampling handler\n * // After tool execution, `samplingCalls` contains metrics for all nested LLM calls\n * ```\n */\nexport function wrapSamplingHandler(\n handler: McpSamplingHandler,\n onSamplingCall: (data: SamplingCallData) => void,\n): McpSamplingHandler {\n return async (request) => {\n const startTime = Date.now();\n const response = await handler(request);\n const durationMs = Date.now() - startTime;\n\n const modelId =\n response.model ?? request.params.modelPreferences?.hints?.[0]?.name;\n\n const inputTokens =\n response.usage?.inputTokens ?? response.usage?.promptTokens;\n const outputTokens =\n response.usage?.outputTokens ?? response.usage?.completionTokens;\n const reasoningTokens = response.usage?.reasoningTokens;\n const cachedInputTokens = response.usage?.cachedInputTokens;\n\n onSamplingCall({\n ...(modelId ? { model_id: modelId } : undefined),\n ...(inputTokens != null ? { input_tokens: inputTokens } : undefined),\n ...(outputTokens != null ? { output_tokens: outputTokens } : undefined),\n ...(reasoningTokens != null\n ? { reasoning_tokens: reasoningTokens }\n : undefined),\n ...(cachedInputTokens != null\n ? { cached_input_tokens: cachedInputTokens }\n : undefined),\n duration_ms: durationMs,\n });\n\n return response;\n };\n}\n\n/**\n * Creates a collector that accumulates sampling call data during tool execution.\n * Use with `wrapSamplingHandler` to capture all sampling calls for a tool invocation.\n *\n * @example\n * ```ts\n * const collector = createSamplingCollector();\n * const wrappedHandler = wrapSamplingHandler(handler, collector.collect);\n * // ... execute MCP tool ...\n * const calls = collector.getCalls(); // SamplingCallData[]\n * ```\n */\nexport function createSamplingCollector() {\n const calls: SamplingCallData[] = [];\n return {\n collect: (data: SamplingCallData) => calls.push(data),\n getCalls: () => [...calls],\n reset: () => {\n calls.length = 0;\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AA+DA,SAAgB,oBACd,SACA,gBACoB;CACpB,OAAO,OAAO,YAAY;EACxB,MAAM,YAAY,KAAK,IAAI;EAC3B,MAAM,WAAW,MAAM,QAAQ,OAAO;EACtC,MAAM,aAAa,KAAK,IAAI,IAAI;EAEhC,MAAM,UACJ,SAAS,SAAS,QAAQ,OAAO,kBAAkB,QAAQ,EAAE,EAAE;EAEjE,MAAM,cACJ,SAAS,OAAO,eAAe,SAAS,OAAO;EACjD,MAAM,eACJ,SAAS,OAAO,gBAAgB,SAAS,OAAO;EAClD,MAAM,kBAAkB,SAAS,OAAO;EACxC,MAAM,oBAAoB,SAAS,OAAO;EAE1C,eAAe;GACb,GAAI,UAAU,EAAE,UAAU,QAAQ,IAAI,KAAA;GACtC,GAAI,eAAe,OAAO,EAAE,cAAc,YAAY,IAAI,KAAA;GAC1D,GAAI,gBAAgB,OAAO,EAAE,eAAe,aAAa,IAAI,KAAA;GAC7D,GAAI,mBAAmB,OACnB,EAAE,kBAAkB,gBAAgB,IACpC,KAAA;GACJ,GAAI,qBAAqB,OACrB,EAAE,qBAAqB,kBAAkB,IACzC,KAAA;GACJ,aAAa;EACf,CAAC;EAED,OAAO;CACT;AACF;;;;;;;;;;;;;AAcA,SAAgB,0BAA0B;CACxC,MAAM,QAA4B,CAAC;CACnC,OAAO;EACL,UAAU,SAA2B,MAAM,KAAK,IAAI;EACpD,gBAAgB,CAAC,GAAG,KAAK;EACzB,aAAa;GACX,MAAM,SAAS;EACjB;CACF;AACF"}
1
+ {"version":3,"file":"instrumentMcpSampling.js","names":[],"sources":["../src/instrumentMcpSampling.ts"],"sourcesContent":["/**\n * MCP sampling instrumentation utility.\n *\n * Wraps an MCP client's sampling handler to capture nested LLM calls\n * (sampling/createMessage requests) made during tool execution.\n * The captured data can be reported as child generation spans.\n */\n\nexport type SamplingCallData = {\n model_id?: string;\n input_tokens?: number;\n output_tokens?: number;\n reasoning_tokens?: number;\n cached_input_tokens?: number;\n duration_ms?: number;\n};\n\nexport type McpSamplingHandler = (\n request: McpSamplingRequest,\n) => Promise<McpSamplingResponse>;\n\nexport type McpSamplingRequest = {\n method: \"sampling/createMessage\";\n params: {\n messages: unknown[];\n modelPreferences?: { hints?: { name?: string }[] };\n maxTokens?: number;\n [key: string]: unknown;\n };\n};\n\nexport type McpSamplingResponse = {\n model?: string;\n content: unknown;\n usage?: {\n inputTokens?: number;\n outputTokens?: number;\n promptTokens?: number;\n completionTokens?: number;\n reasoningTokens?: number;\n cachedInputTokens?: number;\n };\n [key: string]: unknown;\n};\n\nconst reportSamplingError = (error: unknown): void => {\n try {\n console.error(\n \"[assistant-cloud] onSamplingCall callback threw an error\",\n error,\n );\n } catch {}\n};\n\n/**\n * Wraps an MCP sampling handler to intercept and measure sampling calls.\n *\n * @param handler - The original sampling handler from the MCP client\n * @param onSamplingCall - Callback invoked with metrics for each sampling call\n * @returns A wrapped handler that transparently captures sampling metrics\n *\n * @example\n * ```ts\n * const samplingCalls: SamplingCallData[] = [];\n * const wrapped = wrapSamplingHandler(\n * originalHandler,\n * (data) => samplingCalls.push(data),\n * );\n * // Use `wrapped` as the MCP client's sampling handler\n * // After tool execution, `samplingCalls` contains metrics for all nested LLM calls\n * ```\n */\nexport function wrapSamplingHandler(\n handler: McpSamplingHandler,\n onSamplingCall: (data: SamplingCallData) => void,\n): McpSamplingHandler {\n return async (request) => {\n const startTime = Date.now();\n const response = await handler(request);\n const durationMs = Date.now() - startTime;\n\n const modelId =\n response.model ?? request.params.modelPreferences?.hints?.[0]?.name;\n\n const inputTokens =\n response.usage?.inputTokens ?? response.usage?.promptTokens;\n const outputTokens =\n response.usage?.outputTokens ?? response.usage?.completionTokens;\n const reasoningTokens = response.usage?.reasoningTokens;\n const cachedInputTokens = response.usage?.cachedInputTokens;\n\n const data: SamplingCallData = {\n ...(modelId ? { model_id: modelId } : undefined),\n ...(inputTokens != null ? { input_tokens: inputTokens } : undefined),\n ...(outputTokens != null ? { output_tokens: outputTokens } : undefined),\n ...(reasoningTokens != null\n ? { reasoning_tokens: reasoningTokens }\n : undefined),\n ...(cachedInputTokens != null\n ? { cached_input_tokens: cachedInputTokens }\n : undefined),\n duration_ms: durationMs,\n };\n try {\n void Promise.resolve(onSamplingCall(data)).catch(reportSamplingError);\n } catch (error) {\n reportSamplingError(error);\n }\n\n return response;\n };\n}\n\n/**\n * Creates a collector that accumulates sampling call data during tool execution.\n * Use with `wrapSamplingHandler` to capture all sampling calls for a tool invocation.\n *\n * @example\n * ```ts\n * const collector = createSamplingCollector();\n * const wrappedHandler = wrapSamplingHandler(handler, collector.collect);\n * // ... execute MCP tool ...\n * const calls = collector.getCalls(); // SamplingCallData[]\n * ```\n */\nexport function createSamplingCollector() {\n const calls: SamplingCallData[] = [];\n return {\n collect: (data: SamplingCallData) => calls.push(data),\n getCalls: () => [...calls],\n reset: () => {\n calls.length = 0;\n },\n };\n}\n"],"mappings":";AA6CA,MAAM,uBAAuB,UAAyB;CACpD,IAAI;EACF,QAAQ,MACN,4DACA,KACF;CACF,QAAQ,CAAC;AACX;;;;;;;;;;;;;;;;;;;AAoBA,SAAgB,oBACd,SACA,gBACoB;CACpB,OAAO,OAAO,YAAY;EACxB,MAAM,YAAY,KAAK,IAAI;EAC3B,MAAM,WAAW,MAAM,QAAQ,OAAO;EACtC,MAAM,aAAa,KAAK,IAAI,IAAI;EAEhC,MAAM,UACJ,SAAS,SAAS,QAAQ,OAAO,kBAAkB,QAAQ,EAAE,EAAE;EAEjE,MAAM,cACJ,SAAS,OAAO,eAAe,SAAS,OAAO;EACjD,MAAM,eACJ,SAAS,OAAO,gBAAgB,SAAS,OAAO;EAClD,MAAM,kBAAkB,SAAS,OAAO;EACxC,MAAM,oBAAoB,SAAS,OAAO;EAE1C,MAAM,OAAyB;GAC7B,GAAI,UAAU,EAAE,UAAU,QAAQ,IAAI,KAAA;GACtC,GAAI,eAAe,OAAO,EAAE,cAAc,YAAY,IAAI,KAAA;GAC1D,GAAI,gBAAgB,OAAO,EAAE,eAAe,aAAa,IAAI,KAAA;GAC7D,GAAI,mBAAmB,OACnB,EAAE,kBAAkB,gBAAgB,IACpC,KAAA;GACJ,GAAI,qBAAqB,OACrB,EAAE,qBAAqB,kBAAkB,IACzC,KAAA;GACJ,aAAa;EACf;EACA,IAAI;GACF,QAAa,QAAQ,eAAe,IAAI,CAAC,CAAC,CAAC,MAAM,mBAAmB;EACtE,SAAS,OAAO;GACd,oBAAoB,KAAK;EAC3B;EAEA,OAAO;CACT;AACF;;;;;;;;;;;;;AAcA,SAAgB,0BAA0B;CACxC,MAAM,QAA4B,CAAC;CACnC,OAAO;EACL,UAAU,SAA2B,MAAM,KAAK,IAAI;EACpD,gBAAgB,CAAC,GAAG,KAAK;EACzB,aAAa;GACX,MAAM,SAAS;EACjB;CACF;AACF"}
@@ -1,7 +1,7 @@
1
1
  import { SamplingCallData } from "./instrumentMcpSampling.js";
2
2
  import { AssistantCloudRunReport } from "./AssistantCloudRuns.js";
3
3
  //#region src/runTelemetry.d.ts
4
- type AssistantCloudRunReportToolCall = {
4
+ export type AssistantCloudRunReportToolCall = {
5
5
  tool_name: string;
6
6
  tool_call_id: string;
7
7
  tool_args?: string;
@@ -11,12 +11,12 @@ type AssistantCloudRunReportToolCall = {
11
11
  end_ms?: number;
12
12
  sampling_calls?: SamplingCallData[];
13
13
  };
14
- type RunReportOutcome = "aborted" | "disconnected" | "length" | "content_filter";
14
+ export type RunReportOutcome = "aborted" | "disconnected" | "length" | "content_filter";
15
15
  /**
16
16
  * Maps a finish event to the report status and outcome. `fallbackStatus`
17
17
  * applies when the event carries neither a finish reason nor a failure flag.
18
18
  */
19
- declare function deriveRunOutcome(input: {
19
+ export declare function deriveRunOutcome(input: {
20
20
  finishReason?: string | undefined;
21
21
  isAbort?: boolean | undefined;
22
22
  isDisconnect?: boolean | undefined;
@@ -29,7 +29,7 @@ declare function deriveRunOutcome(input: {
29
29
  * Reads the message and code the runs endpoint stores for a failed run. The
30
30
  * code is the error's `code` when it has one, else its class name.
31
31
  */
32
- declare function describeRunError(error: unknown): {
32
+ export declare function describeRunError(error: unknown): {
33
33
  error?: string;
34
34
  errorCode?: string;
35
35
  };
@@ -37,8 +37,8 @@ declare function describeRunError(error: unknown): {
37
37
  * Clamps a string to the size the runs endpoint accepts for a single span
38
38
  * field.
39
39
  */
40
- declare function truncateRunTelemetryText(value: string): string;
41
- type RunTelemetryToolCallInit = {
40
+ export declare function truncateRunTelemetryText(value: string): string;
41
+ export type RunTelemetryToolCallInit = {
42
42
  toolName: string;
43
43
  toolCallId: string;
44
44
  args?: unknown;
@@ -55,7 +55,7 @@ type RunTelemetryToolCallInit = {
55
55
  * source has its result summarized, because MCP content blocks carry inline
56
56
  * base64 image and audio payloads that would otherwise dominate the report.
57
57
  */
58
- declare function createRunTelemetryToolCall(init: RunTelemetryToolCallInit): AssistantCloudRunReportToolCall;
58
+ export declare function createRunTelemetryToolCall(init: RunTelemetryToolCallInit): AssistantCloudRunReportToolCall;
59
59
  /**
60
60
  * Resolves the model ID a run reports, in the order an app can supply it: an
61
61
  * explicit `modelId`, the `custom` bag, then the per-step `response.modelId`
@@ -63,14 +63,14 @@ declare function createRunTelemetryToolCall(init: RunTelemetryToolCallInit): Ass
63
63
  * The AI SDK puts no model ID on a UI message part, so message metadata is the
64
64
  * only channel one arrives on.
65
65
  */
66
- declare function extractRunTelemetryModelId(metadata: Record<string, unknown> | undefined): string | undefined;
67
- type RunTelemetryUsage = {
66
+ export declare function extractRunTelemetryModelId(metadata: Record<string, unknown> | undefined): string | undefined;
67
+ export type RunTelemetryUsage = {
68
68
  inputTokens?: number | undefined;
69
69
  outputTokens?: number | undefined;
70
70
  reasoningTokens?: number | undefined;
71
71
  cachedInputTokens?: number | undefined;
72
72
  };
73
- type RunTelemetryUsageInit = RunTelemetryUsage & {
73
+ export type RunTelemetryUsageInit = RunTelemetryUsage & {
74
74
  promptTokens?: number | undefined;
75
75
  completionTokens?: number | undefined;
76
76
  inputTokenDetails?: {
@@ -80,14 +80,30 @@ type RunTelemetryUsageInit = RunTelemetryUsage & {
80
80
  reasoningTokens?: number;
81
81
  };
82
82
  };
83
- type RunReportStepInit = {
83
+ export type RunReportStepInit = {
84
84
  usage?: RunTelemetryUsageInit | undefined;
85
85
  toolCalls?: AssistantCloudRunReportToolCall[] | undefined;
86
86
  startMs?: number | undefined;
87
87
  endMs?: number | undefined;
88
88
  finishReason?: string | undefined;
89
89
  };
90
- type RunReportInit = {
90
+ /**
91
+ * The run report fields read from the messages of one run, in whichever
92
+ * format they were stored: the status the messages imply, the tool calls, the
93
+ * steps, the text, the usage and the model.
94
+ */
95
+ export type RunMessageTelemetry = {
96
+ assistantMessageId?: string;
97
+ status: "completed" | "incomplete";
98
+ toolCalls?: AssistantCloudRunReportToolCall[];
99
+ steps?: RunReportStepInit[];
100
+ totalSteps?: number;
101
+ outputText?: string;
102
+ usage?: RunTelemetryUsage;
103
+ modelId?: string;
104
+ metadata?: Record<string, unknown>;
105
+ };
106
+ export type RunReportInit = {
91
107
  threadId: string;
92
108
  status: AssistantCloudRunReport["status"];
93
109
  outcome?: RunReportOutcome | undefined;
@@ -111,14 +127,13 @@ type RunReportInit = {
111
127
  tags?: readonly string[] | undefined;
112
128
  };
113
129
  };
114
- declare function createRunReport(init: RunReportInit): AssistantCloudRunReport;
130
+ export declare function createRunReport(init: RunReportInit): AssistantCloudRunReport;
115
131
  /**
116
132
  * Resolves the token counts a provider reports under any of the names the AI
117
133
  * SDK has used: the current top-level ones, the legacy prompt/completion pair,
118
134
  * and the v7 token detail objects. Returns undefined when no count is present,
119
135
  * so callers can tell an empty usage object from a zeroed one.
120
136
  */
121
- declare function normalizeRunTelemetryUsage(usage: RunTelemetryUsageInit): RunTelemetryUsage | undefined;
137
+ export declare function normalizeRunTelemetryUsage(usage: RunTelemetryUsageInit): RunTelemetryUsage | undefined;
122
138
  //#endregion
123
- export { AssistantCloudRunReportToolCall, RunReportInit, RunReportOutcome, RunReportStepInit, RunTelemetryToolCallInit, RunTelemetryUsage, RunTelemetryUsageInit, createRunReport, createRunTelemetryToolCall, deriveRunOutcome, describeRunError, extractRunTelemetryModelId, normalizeRunTelemetryUsage, truncateRunTelemetryText };
124
139
  //# sourceMappingURL=runTelemetry.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"runTelemetry.d.ts","names":[],"sources":["../src/runTelemetry.ts"],"mappings":";;;KAOY;EACV;EACA;EACA;EACA;EACA;EACA;EACA;EACA,iBAAiB;;KAGP;;;;;iBAUI,iBACd;EACE;EACA;EACA;EACA;GAEF;EAEA;EACA,UAAU;;;;;;iBA+BI,iBAAiB;EAC/B;EACA;;;;;;iBA+Bc,yBAAyB;KAwC7B;EACV;EACA;EACA;;;;;EAKA;EACA;EACA;;;;;;;iBAQc,2BACd,MAAM,2BACL;;;;;;;;iBAuBa,2BACd,UAAU;KAmBA;EACV;EACA;EACA;EACA;;KAGU,wBAAwB;EAClC;EACA;EACA;IAAsB;;EACtB;IAAuB;;;KAGb;EACV,QAAQ;EACR,YAAY;EACZ;EACA;EACA;;KAGU;EACV;EACA,QAAQ;EACR,UAAU;EACV;EACA;EACA;EACA;EACA;EACA;EACA,QAAQ;EACR,QAAQ;EACR;EACA,YAAY;EACZ;EACA;EACA;EACA,WAAW;EACX;IACE;IACA;IACA;;;iBAuEY,gBAAgB,MAAM,gBAAgB;;;;;;;iBAiDtC,2BACd,OAAO,wBACN"}
1
+ {"version":3,"file":"runTelemetry.d.ts","names":[],"sources":["../src/runTelemetry.ts"],"mappings":";;;YAOY;EACV;EACA;EACA;EACA;EACA;EACA;EACA;EACA,iBAAiB;;YAGP;;;;;wBAUI,iBACd;EACE;EACA;EACA;EACA;GAEF;EAEA;EACA,UAAU;;;;;;wBA+BI,iBAAiB;EAC/B;EACA;;;;;;wBA+Bc,yBAAyB;YAwC7B;EACV;EACA;EACA;;;;;EAKA;EACA;EACA;;;;;;;wBAQc,2BACd,MAAM,2BACL;;;;;;;;wBAuBa,2BACd,UAAU;YAmBA;EACV;EACA;EACA;EACA;;YAGU,wBAAwB;EAClC;EACA;EACA;IAAsB;;EACtB;IAAuB;;;YAGb;EACV,QAAQ;EACR,YAAY;EACZ;EACA;EACA;;;;;;;YAQU;EACV;EACA;EACA,YAAY;EACZ,QAAQ;EACR;EACA;EACA,QAAQ;EACR;EACA,WAAW;;YAGD;EACV;EACA,QAAQ;EACR,UAAU;EACV;EACA;EACA;EACA;EACA;EACA;EACA,QAAQ;EACR,QAAQ;EACR;EACA,YAAY;EACZ;EACA;EACA;EACA,WAAW;EACX;IACE;IACA;IACA;;;wBAuEY,gBAAgB,MAAM,gBAAgB;;;;;;;wBAiDtC,2BACd,OAAO,wBACN"}
@@ -1 +1 @@
1
- {"version":3,"file":"runTelemetry.js","names":[],"sources":["../src/runTelemetry.ts"],"sourcesContent":["import type { SamplingCallData } from \"./instrumentMcpSampling\";\nimport type { AssistantCloudRunReport } from \"./AssistantCloudRuns\";\n\nconst MAX_TELEMETRY_TEXT_LENGTH = 50_000;\n\nconst BASE64_PATTERN = /^[A-Za-z0-9+/]{100,}={0,2}$/;\n\nexport type AssistantCloudRunReportToolCall = {\n tool_name: string;\n tool_call_id: string;\n tool_args?: string;\n tool_result?: string;\n tool_source?: \"mcp\" | \"frontend\" | \"backend\";\n start_ms?: number;\n end_ms?: number;\n sampling_calls?: SamplingCallData[];\n};\n\nexport type RunReportOutcome =\n | \"aborted\"\n | \"disconnected\"\n | \"length\"\n | \"content_filter\";\n\n/**\n * Maps a finish event to the report status and outcome. `fallbackStatus`\n * applies when the event carries neither a finish reason nor a failure flag.\n */\nexport function deriveRunOutcome(\n input: {\n finishReason?: string | undefined;\n isAbort?: boolean | undefined;\n isDisconnect?: boolean | undefined;\n isError?: boolean | undefined;\n },\n fallbackStatus: \"completed\" | \"incomplete\" = \"completed\",\n): {\n status: \"completed\" | \"incomplete\" | \"error\";\n outcome?: RunReportOutcome;\n} {\n if (input.isError) return { status: \"error\" };\n if (input.isAbort) return { status: \"incomplete\", outcome: \"aborted\" };\n if (input.isDisconnect) {\n return { status: \"incomplete\", outcome: \"disconnected\" };\n }\n switch (input.finishReason) {\n case \"length\":\n return { status: \"incomplete\", outcome: \"length\" };\n case \"content-filter\":\n case \"content_filter\":\n return { status: \"incomplete\", outcome: \"content_filter\" };\n case \"cancelled\":\n return { status: \"incomplete\", outcome: \"aborted\" };\n case \"error\":\n return { status: \"error\" };\n default:\n return {\n status: input.finishReason === undefined ? fallbackStatus : \"completed\",\n };\n }\n}\n\nconst MAX_RUN_ERROR_CODE_LENGTH = 64;\nconst MAX_RUN_ERROR_LENGTH = 2048;\n\n/**\n * Reads the message and code the runs endpoint stores for a failed run. The\n * code is the error's `code` when it has one, else its class name.\n */\nexport function describeRunError(error: unknown): {\n error?: string;\n errorCode?: string;\n} {\n if (error == null) return {};\n const record =\n typeof error === \"object\" ? (error as Record<string, unknown>) : undefined;\n const message =\n typeof error === \"string\"\n ? error\n : typeof record?.message === \"string\"\n ? record.message\n : undefined;\n const code =\n typeof record?.code === \"string\"\n ? record.code\n : typeof record?.name === \"string\" && record.name !== \"Error\"\n ? record.name\n : undefined;\n return {\n ...(message\n ? { error: message.slice(0, MAX_RUN_ERROR_LENGTH) }\n : undefined),\n ...(code\n ? { errorCode: code.slice(0, MAX_RUN_ERROR_CODE_LENGTH) }\n : undefined),\n };\n}\n\n/**\n * Clamps a string to the size the runs endpoint accepts for a single span\n * field.\n */\nexport function truncateRunTelemetryText(value: string): string {\n if (value.length <= MAX_TELEMETRY_TEXT_LENGTH) return value;\n return value.slice(0, MAX_TELEMETRY_TEXT_LENGTH);\n}\n\nfunction safeStringify(value: unknown): string | undefined {\n if (value == null) return undefined;\n try {\n return truncateRunTelemetryText(JSON.stringify(value));\n } catch {\n return undefined;\n }\n}\n\nfunction summarizeMcpResult(value: unknown): string | undefined {\n if (value == null) return undefined;\n try {\n const parsed = typeof value === \"string\" ? JSON.parse(value) : value;\n if (Array.isArray(parsed)) {\n const summarized = parsed.map((item) => {\n if (item && typeof item === \"object\" && item.type) {\n if (\n (item.type === \"image\" || item.type === \"audio\") &&\n typeof item.data === \"string\" &&\n BASE64_PATTERN.test(item.data.slice(0, 200))\n ) {\n const sizeKB = ((item.data.length * 3) / 4 / 1024).toFixed(1);\n return { ...item, data: `[${item.type}: ${sizeKB}KB]` };\n }\n }\n return item;\n });\n return truncateRunTelemetryText(JSON.stringify(summarized));\n }\n } catch {\n // not JSON array, fall through\n }\n return safeStringify(value);\n}\n\nexport type RunTelemetryToolCallInit = {\n toolName: string;\n toolCallId: string;\n args?: unknown;\n /**\n * Pre-serialized arguments, used in place of serializing `args`. Values over\n * the span size are clamped before they are included in the report.\n */\n argsText?: string | undefined;\n result?: unknown;\n toolSource?: \"mcp\" | \"frontend\" | \"backend\" | undefined;\n};\n\n/**\n * Serializes one tool call into the shape the runs endpoint accepts. An `mcp`\n * source has its result summarized, because MCP content blocks carry inline\n * base64 image and audio payloads that would otherwise dominate the report.\n */\nexport function createRunTelemetryToolCall(\n init: RunTelemetryToolCallInit,\n): AssistantCloudRunReportToolCall {\n const { toolName, toolCallId, args, argsText, result, toolSource } = init;\n const call: AssistantCloudRunReportToolCall = {\n tool_name: toolName,\n tool_call_id: toolCallId,\n };\n const toolArgs =\n argsText != null ? truncateRunTelemetryText(argsText) : safeStringify(args);\n if (toolArgs !== undefined) call.tool_args = toolArgs;\n const toolResult =\n toolSource === \"mcp\" ? summarizeMcpResult(result) : safeStringify(result);\n if (toolResult !== undefined) call.tool_result = toolResult;\n if (toolSource) call.tool_source = toolSource;\n return call;\n}\n\n/**\n * Resolves the model ID a run reports, in the order an app can supply it: an\n * explicit `modelId`, the `custom` bag, then the per-step `response.modelId`\n * that a `messageMetadata` callback copies off the AI SDK's finish-step part.\n * The AI SDK puts no model ID on a UI message part, so message metadata is the\n * only channel one arrives on.\n */\nexport function extractRunTelemetryModelId(\n metadata: Record<string, unknown> | undefined,\n): string | undefined {\n if (!metadata) return undefined;\n if (typeof metadata.modelId === \"string\") return metadata.modelId;\n const custom = metadata.custom as Record<string, unknown> | undefined;\n if (typeof custom?.modelId === \"string\") return custom.modelId;\n\n const steps: unknown = metadata.steps;\n if (!Array.isArray(steps)) return undefined;\n for (const step of steps as unknown[]) {\n if (!step || typeof step !== \"object\") continue;\n const response = (step as Record<string, unknown>).response;\n if (!response || typeof response !== \"object\") continue;\n const modelId = (response as Record<string, unknown>).modelId;\n if (typeof modelId === \"string\") return modelId;\n }\n return undefined;\n}\n\nexport type RunTelemetryUsage = {\n inputTokens?: number | undefined;\n outputTokens?: number | undefined;\n reasoningTokens?: number | undefined;\n cachedInputTokens?: number | undefined;\n};\n\nexport type RunTelemetryUsageInit = RunTelemetryUsage & {\n promptTokens?: number | undefined;\n completionTokens?: number | undefined;\n inputTokenDetails?: { cacheReadTokens?: number };\n outputTokenDetails?: { reasoningTokens?: number };\n};\n\nexport type RunReportStepInit = {\n usage?: RunTelemetryUsageInit | undefined;\n toolCalls?: AssistantCloudRunReportToolCall[] | undefined;\n startMs?: number | undefined;\n endMs?: number | undefined;\n finishReason?: string | undefined;\n};\n\nexport type RunReportInit = {\n threadId: string;\n status: AssistantCloudRunReport[\"status\"];\n outcome?: RunReportOutcome | undefined;\n errorCode?: string | undefined;\n error?: string | undefined;\n messageId?: string | undefined;\n traceId?: string | undefined;\n modelId?: string | undefined;\n provider?: string | undefined;\n usage?: RunTelemetryUsageInit | undefined;\n steps?: RunReportStepInit[] | undefined;\n totalSteps?: number | undefined;\n toolCalls?: AssistantCloudRunReportToolCall[] | undefined;\n durationMs?: number | undefined;\n firstTokenMs?: number | undefined;\n outputText?: string | undefined;\n metadata?: Record<string, unknown> | undefined;\n telemetry?: {\n environment?: string | undefined;\n release?: string | undefined;\n tags?: readonly string[] | undefined;\n };\n};\n\nfunction assignUsage(\n report: AssistantCloudRunReport,\n usage: RunTelemetryUsageInit | undefined,\n): void {\n if (!usage) return;\n const normalized = normalizeRunTelemetryUsage(usage);\n if (!normalized) return;\n if (normalized.inputTokens !== undefined) {\n report.input_tokens = normalized.inputTokens;\n }\n if (normalized.outputTokens !== undefined) {\n report.output_tokens = normalized.outputTokens;\n }\n if (normalized.reasoningTokens !== undefined) {\n report.reasoning_tokens = normalized.reasoningTokens;\n }\n if (normalized.cachedInputTokens !== undefined) {\n report.cached_input_tokens = normalized.cachedInputTokens;\n }\n}\n\nfunction createRunReportStep(\n init: RunReportStepInit,\n): NonNullable<AssistantCloudRunReport[\"steps\"]>[number] {\n const step: NonNullable<AssistantCloudRunReport[\"steps\"]>[number] = {};\n const usage = init.usage ? normalizeRunTelemetryUsage(init.usage) : undefined;\n if (usage?.inputTokens !== undefined) step.input_tokens = usage.inputTokens;\n if (usage?.outputTokens !== undefined)\n step.output_tokens = usage.outputTokens;\n if (usage?.reasoningTokens !== undefined) {\n step.reasoning_tokens = usage.reasoningTokens;\n }\n if (usage?.cachedInputTokens !== undefined) {\n step.cached_input_tokens = usage.cachedInputTokens;\n }\n if (init.toolCalls !== undefined) step.tool_calls = init.toolCalls;\n if (init.startMs !== undefined) step.start_ms = init.startMs;\n if (init.endMs !== undefined) step.end_ms = init.endMs;\n if (init.finishReason !== undefined) {\n step.finish_reason = init.finishReason.slice(0, 32);\n }\n return step;\n}\n\nfunction normalizeRunReportTags(\n tags: readonly string[] | undefined,\n): string[] | undefined {\n if (!tags) return undefined;\n const result: string[] = [];\n const seen = new Set<string>();\n for (const tag of tags) {\n const normalized = tag.trim().slice(0, 64).trim();\n if (!normalized || seen.has(normalized)) continue;\n seen.add(normalized);\n result.push(normalized);\n if (result.length === 20) break;\n }\n return result.length > 0 ? result : undefined;\n}\n\nfunction normalizeRunReportMilliseconds(\n value: number | undefined,\n): number | undefined {\n if (value === undefined || !Number.isFinite(value)) return undefined;\n return Math.max(0, Math.round(value));\n}\n\nexport function createRunReport(init: RunReportInit): AssistantCloudRunReport {\n const report: AssistantCloudRunReport = {\n thread_id: init.threadId,\n status: init.status,\n };\n const traceId = init.traceId?.toLowerCase();\n if (traceId && /^[0-9a-f]{32}$/.test(traceId)) report.trace_id = traceId;\n if (init.outcome !== undefined) report.outcome_type = init.outcome;\n if (init.errorCode !== undefined) report.error_code = init.errorCode;\n if (init.error !== undefined) report.error = init.error;\n if (init.messageId !== undefined) report.message_id = init.messageId;\n if (init.modelId !== undefined) report.model_id = init.modelId;\n if (init.provider !== undefined) {\n report.provider = init.provider;\n report.provider_type = init.provider;\n }\n assignUsage(report, init.usage);\n if (init.steps !== undefined) {\n report.steps = init.steps.map(createRunReportStep);\n report.total_steps = init.steps.length;\n } else if (init.totalSteps !== undefined) {\n report.total_steps = init.totalSteps;\n }\n if (init.toolCalls !== undefined) report.tool_calls = init.toolCalls;\n const durationMs = normalizeRunReportMilliseconds(init.durationMs);\n if (durationMs !== undefined) report.duration_ms = durationMs;\n const firstTokenMs = normalizeRunReportMilliseconds(init.firstTokenMs);\n if (firstTokenMs !== undefined) report.first_token_ms = firstTokenMs;\n if (init.outputText !== undefined) {\n report.output_text = truncateRunTelemetryText(init.outputText);\n }\n if (init.metadata !== undefined) report.metadata = init.metadata;\n if (init.telemetry?.environment !== undefined) {\n report.environment = init.telemetry.environment;\n }\n if (init.telemetry?.release !== undefined) {\n report.release = init.telemetry.release;\n }\n const tags = normalizeRunReportTags(init.telemetry?.tags);\n if (tags !== undefined) report.tags = tags;\n return report;\n}\n\n/**\n * Resolves the token counts a provider reports under any of the names the AI\n * SDK has used: the current top-level ones, the legacy prompt/completion pair,\n * and the v7 token detail objects. Returns undefined when no count is present,\n * so callers can tell an empty usage object from a zeroed one.\n */\nexport function normalizeRunTelemetryUsage(\n usage: RunTelemetryUsageInit,\n): RunTelemetryUsage | undefined {\n const inputTokens = usage.inputTokens ?? usage.promptTokens;\n const outputTokens = usage.outputTokens ?? usage.completionTokens;\n // AI SDK v7 moved these under token detail objects; v6 kept them top-level.\n const reasoningTokens =\n usage.reasoningTokens ?? usage.outputTokenDetails?.reasoningTokens;\n const cachedInputTokens =\n usage.cachedInputTokens ?? usage.inputTokenDetails?.cacheReadTokens;\n\n if (\n inputTokens == null &&\n outputTokens == null &&\n reasoningTokens == null &&\n cachedInputTokens == null\n ) {\n return undefined;\n }\n\n return {\n ...(inputTokens != null ? { inputTokens } : undefined),\n ...(outputTokens != null ? { outputTokens } : undefined),\n ...(reasoningTokens != null ? { reasoningTokens } : undefined),\n ...(cachedInputTokens != null ? { cachedInputTokens } : undefined),\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"runTelemetry.js","names":[],"sources":["../src/runTelemetry.ts"],"sourcesContent":["import type { SamplingCallData } from \"./instrumentMcpSampling\";\nimport type { AssistantCloudRunReport } from \"./AssistantCloudRuns\";\n\nconst MAX_TELEMETRY_TEXT_LENGTH = 50_000;\n\nconst BASE64_PATTERN = /^[A-Za-z0-9+/]{100,}={0,2}$/;\n\nexport type AssistantCloudRunReportToolCall = {\n tool_name: string;\n tool_call_id: string;\n tool_args?: string;\n tool_result?: string;\n tool_source?: \"mcp\" | \"frontend\" | \"backend\";\n start_ms?: number;\n end_ms?: number;\n sampling_calls?: SamplingCallData[];\n};\n\nexport type RunReportOutcome =\n | \"aborted\"\n | \"disconnected\"\n | \"length\"\n | \"content_filter\";\n\n/**\n * Maps a finish event to the report status and outcome. `fallbackStatus`\n * applies when the event carries neither a finish reason nor a failure flag.\n */\nexport function deriveRunOutcome(\n input: {\n finishReason?: string | undefined;\n isAbort?: boolean | undefined;\n isDisconnect?: boolean | undefined;\n isError?: boolean | undefined;\n },\n fallbackStatus: \"completed\" | \"incomplete\" = \"completed\",\n): {\n status: \"completed\" | \"incomplete\" | \"error\";\n outcome?: RunReportOutcome;\n} {\n if (input.isError) return { status: \"error\" };\n if (input.isAbort) return { status: \"incomplete\", outcome: \"aborted\" };\n if (input.isDisconnect) {\n return { status: \"incomplete\", outcome: \"disconnected\" };\n }\n switch (input.finishReason) {\n case \"length\":\n return { status: \"incomplete\", outcome: \"length\" };\n case \"content-filter\":\n case \"content_filter\":\n return { status: \"incomplete\", outcome: \"content_filter\" };\n case \"cancelled\":\n return { status: \"incomplete\", outcome: \"aborted\" };\n case \"error\":\n return { status: \"error\" };\n default:\n return {\n status: input.finishReason === undefined ? fallbackStatus : \"completed\",\n };\n }\n}\n\nconst MAX_RUN_ERROR_CODE_LENGTH = 64;\nconst MAX_RUN_ERROR_LENGTH = 2048;\n\n/**\n * Reads the message and code the runs endpoint stores for a failed run. The\n * code is the error's `code` when it has one, else its class name.\n */\nexport function describeRunError(error: unknown): {\n error?: string;\n errorCode?: string;\n} {\n if (error == null) return {};\n const record =\n typeof error === \"object\" ? (error as Record<string, unknown>) : undefined;\n const message =\n typeof error === \"string\"\n ? error\n : typeof record?.message === \"string\"\n ? record.message\n : undefined;\n const code =\n typeof record?.code === \"string\"\n ? record.code\n : typeof record?.name === \"string\" && record.name !== \"Error\"\n ? record.name\n : undefined;\n return {\n ...(message\n ? { error: message.slice(0, MAX_RUN_ERROR_LENGTH) }\n : undefined),\n ...(code\n ? { errorCode: code.slice(0, MAX_RUN_ERROR_CODE_LENGTH) }\n : undefined),\n };\n}\n\n/**\n * Clamps a string to the size the runs endpoint accepts for a single span\n * field.\n */\nexport function truncateRunTelemetryText(value: string): string {\n if (value.length <= MAX_TELEMETRY_TEXT_LENGTH) return value;\n return value.slice(0, MAX_TELEMETRY_TEXT_LENGTH);\n}\n\nfunction safeStringify(value: unknown): string | undefined {\n if (value == null) return undefined;\n try {\n return truncateRunTelemetryText(JSON.stringify(value));\n } catch {\n return undefined;\n }\n}\n\nfunction summarizeMcpResult(value: unknown): string | undefined {\n if (value == null) return undefined;\n try {\n const parsed = typeof value === \"string\" ? JSON.parse(value) : value;\n if (Array.isArray(parsed)) {\n const summarized = parsed.map((item) => {\n if (item && typeof item === \"object\" && item.type) {\n if (\n (item.type === \"image\" || item.type === \"audio\") &&\n typeof item.data === \"string\" &&\n BASE64_PATTERN.test(item.data.slice(0, 200))\n ) {\n const sizeKB = ((item.data.length * 3) / 4 / 1024).toFixed(1);\n return { ...item, data: `[${item.type}: ${sizeKB}KB]` };\n }\n }\n return item;\n });\n return truncateRunTelemetryText(JSON.stringify(summarized));\n }\n } catch {\n // not JSON array, fall through\n }\n return safeStringify(value);\n}\n\nexport type RunTelemetryToolCallInit = {\n toolName: string;\n toolCallId: string;\n args?: unknown;\n /**\n * Pre-serialized arguments, used in place of serializing `args`. Values over\n * the span size are clamped before they are included in the report.\n */\n argsText?: string | undefined;\n result?: unknown;\n toolSource?: \"mcp\" | \"frontend\" | \"backend\" | undefined;\n};\n\n/**\n * Serializes one tool call into the shape the runs endpoint accepts. An `mcp`\n * source has its result summarized, because MCP content blocks carry inline\n * base64 image and audio payloads that would otherwise dominate the report.\n */\nexport function createRunTelemetryToolCall(\n init: RunTelemetryToolCallInit,\n): AssistantCloudRunReportToolCall {\n const { toolName, toolCallId, args, argsText, result, toolSource } = init;\n const call: AssistantCloudRunReportToolCall = {\n tool_name: toolName,\n tool_call_id: toolCallId,\n };\n const toolArgs =\n argsText != null ? truncateRunTelemetryText(argsText) : safeStringify(args);\n if (toolArgs !== undefined) call.tool_args = toolArgs;\n const toolResult =\n toolSource === \"mcp\" ? summarizeMcpResult(result) : safeStringify(result);\n if (toolResult !== undefined) call.tool_result = toolResult;\n if (toolSource) call.tool_source = toolSource;\n return call;\n}\n\n/**\n * Resolves the model ID a run reports, in the order an app can supply it: an\n * explicit `modelId`, the `custom` bag, then the per-step `response.modelId`\n * that a `messageMetadata` callback copies off the AI SDK's finish-step part.\n * The AI SDK puts no model ID on a UI message part, so message metadata is the\n * only channel one arrives on.\n */\nexport function extractRunTelemetryModelId(\n metadata: Record<string, unknown> | undefined,\n): string | undefined {\n if (!metadata) return undefined;\n if (typeof metadata.modelId === \"string\") return metadata.modelId;\n const custom = metadata.custom as Record<string, unknown> | undefined;\n if (typeof custom?.modelId === \"string\") return custom.modelId;\n\n const steps: unknown = metadata.steps;\n if (!Array.isArray(steps)) return undefined;\n for (const step of steps as unknown[]) {\n if (!step || typeof step !== \"object\") continue;\n const response = (step as Record<string, unknown>).response;\n if (!response || typeof response !== \"object\") continue;\n const modelId = (response as Record<string, unknown>).modelId;\n if (typeof modelId === \"string\") return modelId;\n }\n return undefined;\n}\n\nexport type RunTelemetryUsage = {\n inputTokens?: number | undefined;\n outputTokens?: number | undefined;\n reasoningTokens?: number | undefined;\n cachedInputTokens?: number | undefined;\n};\n\nexport type RunTelemetryUsageInit = RunTelemetryUsage & {\n promptTokens?: number | undefined;\n completionTokens?: number | undefined;\n inputTokenDetails?: { cacheReadTokens?: number };\n outputTokenDetails?: { reasoningTokens?: number };\n};\n\nexport type RunReportStepInit = {\n usage?: RunTelemetryUsageInit | undefined;\n toolCalls?: AssistantCloudRunReportToolCall[] | undefined;\n startMs?: number | undefined;\n endMs?: number | undefined;\n finishReason?: string | undefined;\n};\n\n/**\n * The run report fields read from the messages of one run, in whichever\n * format they were stored: the status the messages imply, the tool calls, the\n * steps, the text, the usage and the model.\n */\nexport type RunMessageTelemetry = {\n assistantMessageId?: string;\n status: \"completed\" | \"incomplete\";\n toolCalls?: AssistantCloudRunReportToolCall[];\n steps?: RunReportStepInit[];\n totalSteps?: number;\n outputText?: string;\n usage?: RunTelemetryUsage;\n modelId?: string;\n metadata?: Record<string, unknown>;\n};\n\nexport type RunReportInit = {\n threadId: string;\n status: AssistantCloudRunReport[\"status\"];\n outcome?: RunReportOutcome | undefined;\n errorCode?: string | undefined;\n error?: string | undefined;\n messageId?: string | undefined;\n traceId?: string | undefined;\n modelId?: string | undefined;\n provider?: string | undefined;\n usage?: RunTelemetryUsageInit | undefined;\n steps?: RunReportStepInit[] | undefined;\n totalSteps?: number | undefined;\n toolCalls?: AssistantCloudRunReportToolCall[] | undefined;\n durationMs?: number | undefined;\n firstTokenMs?: number | undefined;\n outputText?: string | undefined;\n metadata?: Record<string, unknown> | undefined;\n telemetry?: {\n environment?: string | undefined;\n release?: string | undefined;\n tags?: readonly string[] | undefined;\n };\n};\n\nfunction assignUsage(\n report: AssistantCloudRunReport,\n usage: RunTelemetryUsageInit | undefined,\n): void {\n if (!usage) return;\n const normalized = normalizeRunTelemetryUsage(usage);\n if (!normalized) return;\n if (normalized.inputTokens !== undefined) {\n report.input_tokens = normalized.inputTokens;\n }\n if (normalized.outputTokens !== undefined) {\n report.output_tokens = normalized.outputTokens;\n }\n if (normalized.reasoningTokens !== undefined) {\n report.reasoning_tokens = normalized.reasoningTokens;\n }\n if (normalized.cachedInputTokens !== undefined) {\n report.cached_input_tokens = normalized.cachedInputTokens;\n }\n}\n\nfunction createRunReportStep(\n init: RunReportStepInit,\n): NonNullable<AssistantCloudRunReport[\"steps\"]>[number] {\n const step: NonNullable<AssistantCloudRunReport[\"steps\"]>[number] = {};\n const usage = init.usage ? normalizeRunTelemetryUsage(init.usage) : undefined;\n if (usage?.inputTokens !== undefined) step.input_tokens = usage.inputTokens;\n if (usage?.outputTokens !== undefined)\n step.output_tokens = usage.outputTokens;\n if (usage?.reasoningTokens !== undefined) {\n step.reasoning_tokens = usage.reasoningTokens;\n }\n if (usage?.cachedInputTokens !== undefined) {\n step.cached_input_tokens = usage.cachedInputTokens;\n }\n if (init.toolCalls !== undefined) step.tool_calls = init.toolCalls;\n if (init.startMs !== undefined) step.start_ms = init.startMs;\n if (init.endMs !== undefined) step.end_ms = init.endMs;\n if (init.finishReason !== undefined) {\n step.finish_reason = init.finishReason.slice(0, 32);\n }\n return step;\n}\n\nfunction normalizeRunReportTags(\n tags: readonly string[] | undefined,\n): string[] | undefined {\n if (!tags) return undefined;\n const result: string[] = [];\n const seen = new Set<string>();\n for (const tag of tags) {\n const normalized = tag.trim().slice(0, 64).trim();\n if (!normalized || seen.has(normalized)) continue;\n seen.add(normalized);\n result.push(normalized);\n if (result.length === 20) break;\n }\n return result.length > 0 ? result : undefined;\n}\n\nfunction normalizeRunReportMilliseconds(\n value: number | undefined,\n): number | undefined {\n if (value === undefined || !Number.isFinite(value)) return undefined;\n return Math.max(0, Math.round(value));\n}\n\nexport function createRunReport(init: RunReportInit): AssistantCloudRunReport {\n const report: AssistantCloudRunReport = {\n thread_id: init.threadId,\n status: init.status,\n };\n const traceId = init.traceId?.toLowerCase();\n if (traceId && /^[0-9a-f]{32}$/.test(traceId)) report.trace_id = traceId;\n if (init.outcome !== undefined) report.outcome_type = init.outcome;\n if (init.errorCode !== undefined) report.error_code = init.errorCode;\n if (init.error !== undefined) report.error = init.error;\n if (init.messageId !== undefined) report.message_id = init.messageId;\n if (init.modelId !== undefined) report.model_id = init.modelId;\n if (init.provider !== undefined) {\n report.provider = init.provider;\n report.provider_type = init.provider;\n }\n assignUsage(report, init.usage);\n if (init.steps !== undefined) {\n report.steps = init.steps.map(createRunReportStep);\n report.total_steps = init.steps.length;\n } else if (init.totalSteps !== undefined) {\n report.total_steps = init.totalSteps;\n }\n if (init.toolCalls !== undefined) report.tool_calls = init.toolCalls;\n const durationMs = normalizeRunReportMilliseconds(init.durationMs);\n if (durationMs !== undefined) report.duration_ms = durationMs;\n const firstTokenMs = normalizeRunReportMilliseconds(init.firstTokenMs);\n if (firstTokenMs !== undefined) report.first_token_ms = firstTokenMs;\n if (init.outputText !== undefined) {\n report.output_text = truncateRunTelemetryText(init.outputText);\n }\n if (init.metadata !== undefined) report.metadata = init.metadata;\n if (init.telemetry?.environment !== undefined) {\n report.environment = init.telemetry.environment;\n }\n if (init.telemetry?.release !== undefined) {\n report.release = init.telemetry.release;\n }\n const tags = normalizeRunReportTags(init.telemetry?.tags);\n if (tags !== undefined) report.tags = tags;\n return report;\n}\n\n/**\n * Resolves the token counts a provider reports under any of the names the AI\n * SDK has used: the current top-level ones, the legacy prompt/completion pair,\n * and the v7 token detail objects. Returns undefined when no count is present,\n * so callers can tell an empty usage object from a zeroed one.\n */\nexport function normalizeRunTelemetryUsage(\n usage: RunTelemetryUsageInit,\n): RunTelemetryUsage | undefined {\n const inputTokens = usage.inputTokens ?? usage.promptTokens;\n const outputTokens = usage.outputTokens ?? usage.completionTokens;\n // AI SDK v7 moved these under token detail objects; v6 kept them top-level.\n const reasoningTokens =\n usage.reasoningTokens ?? usage.outputTokenDetails?.reasoningTokens;\n const cachedInputTokens =\n usage.cachedInputTokens ?? usage.inputTokenDetails?.cacheReadTokens;\n\n if (\n inputTokens == null &&\n outputTokens == null &&\n reasoningTokens == null &&\n cachedInputTokens == null\n ) {\n return undefined;\n }\n\n return {\n ...(inputTokens != null ? { inputTokens } : undefined),\n ...(outputTokens != null ? { outputTokens } : undefined),\n ...(reasoningTokens != null ? { reasoningTokens } : undefined),\n ...(cachedInputTokens != null ? { cachedInputTokens } : undefined),\n };\n}\n"],"mappings":"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"}
@@ -1,24 +1,24 @@
1
1
  import { ReadableSpan, SpanExporter, SpanProcessor } from "@opentelemetry/sdk-trace-base";
2
2
  //#region src/telemetry/index.d.ts
3
- type AssistantCloudTraceExportOptions = {
3
+ export type AssistantCloudTraceExportOptions = {
4
4
  apiKey: string;
5
5
  baseUrl?: string;
6
6
  headers?: Record<string, string>;
7
7
  };
8
- type AssistantCloudSpanProcessorOptions = {
8
+ export type AssistantCloudSpanProcessorOptions = {
9
9
  filter?: (span: ReadableSpan) => boolean;
10
10
  };
11
- declare function assistantCloudTraceExportOptions({ apiKey, baseUrl, headers }: AssistantCloudTraceExportOptions): {
11
+ export declare function assistantCloudTraceExportOptions({ apiKey, baseUrl, headers }: AssistantCloudTraceExportOptions): {
12
12
  url: string;
13
13
  headers: Record<string, string>;
14
14
  };
15
- declare function createAssistantCloudTraceExporter(options: AssistantCloudTraceExportOptions): SpanExporter;
16
- declare function isAssistantCloudSpan(span: ReadableSpan): boolean;
17
- declare function createAssistantCloudSpanProcessor(exporter: SpanExporter, options?: AssistantCloudSpanProcessorOptions): SpanProcessor;
18
- declare function assistantCloudTraceMetadata(): {
15
+ export declare function createAssistantCloudTraceExporter(options: AssistantCloudTraceExportOptions): SpanExporter;
16
+ export declare function isAssistantCloudSpan(span: ReadableSpan): boolean;
17
+ export declare function createAssistantCloudSpanProcessor(exporter: SpanExporter, options?: AssistantCloudSpanProcessorOptions): SpanProcessor;
18
+ export declare function assistantCloudTraceMetadata(): {
19
19
  traceId?: string;
20
20
  };
21
- declare function withAssistantCloudTraceMetadata<Part extends {
21
+ export declare function withAssistantCloudTraceMetadata<Part extends {
22
22
  type: string;
23
23
  } = {
24
24
  type: string;
@@ -28,5 +28,4 @@ declare function withAssistantCloudTraceMetadata<Part extends {
28
28
  part: Part;
29
29
  }) => Record<string, unknown> | undefined;
30
30
  //#endregion
31
- export { AssistantCloudSpanProcessorOptions, AssistantCloudTraceExportOptions, assistantCloudTraceExportOptions, assistantCloudTraceMetadata, createAssistantCloudSpanProcessor, createAssistantCloudTraceExporter, isAssistantCloudSpan, withAssistantCloudTraceMetadata };
32
31
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/telemetry/index.ts"],"mappings":";;KAYY;EACV;EACA;EACA,UAAU;;KAGA;EACV,UAAU,MAAM;;iBAGF,mCACd,QACA,SACA,WACC;EACD;EACA,SAAS;;iBAmBK,kCACd,SAAS,mCACR;iBAIa,qBAAqB,MAAM;iBAO3B,kCACd,UAAU,cACV,UAAS,qCACR;iBAca;EAAiC;;iBAOjC,gCACd;EAAe;;EAAmB;GAElC,mBAAmB;EACjB,MAAM;MACF,uCACJ;EAAW,MAAM;MAAW"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/telemetry/index.ts"],"mappings":";;YAYY;EACV;EACA;EACA,UAAU;;YAGA;EACV,UAAU,MAAM;;wBAGF,mCACd,QACA,SACA,WACC;EACD;EACA,SAAS;;wBAmBK,kCACd,SAAS,mCACR;wBAIa,qBAAqB,MAAM;wBAO3B,kCACd,UAAU,cACV,UAAS,qCACR;wBAca;EAAiC;;wBAOjC,gCACd;EAAe;;EAAmB;GAElC,mBAAmB;EACjB,MAAM;MACF,uCACJ;EAAW,MAAM;MAAW"}
@@ -0,0 +1,4 @@
1
+ //#region src/version.d.ts
2
+ export declare const ASSISTANT_CLOUD_VERSION: string;
3
+ //#endregion
4
+ //# sourceMappingURL=version.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.d.ts","names":[],"sources":["../src/version.ts"],"mappings":";qBAAa"}
@@ -0,0 +1,6 @@
1
+ //#region src/version.ts
2
+ const ASSISTANT_CLOUD_VERSION = "0.2.2";
3
+ //#endregion
4
+ export { ASSISTANT_CLOUD_VERSION };
5
+
6
+ //# sourceMappingURL=version.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.js","names":[],"sources":["../src/version.ts"],"sourcesContent":["export const ASSISTANT_CLOUD_VERSION: string =\n typeof __AUI_PACKAGE_VERSION__ === \"string\"\n ? __AUI_PACKAGE_VERSION__\n : \"0.0.0\";\n"],"mappings":";AAAA,MAAa,0BAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "assistant-cloud",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "Cloud integration for assistant-ui",
5
5
  "keywords": [
6
6
  "assistant",
@@ -20,6 +20,10 @@
20
20
  "./telemetry": {
21
21
  "types": "./dist/telemetry/index.d.ts",
22
22
  "default": "./dist/telemetry/index.js"
23
+ },
24
+ "./ai-sdk": {
25
+ "types": "./dist/ai-sdk/index.d.ts",
26
+ "default": "./dist/ai-sdk/index.js"
23
27
  }
24
28
  },
25
29
  "main": "./dist/index.js",
@@ -31,12 +35,13 @@
31
35
  ],
32
36
  "sideEffects": false,
33
37
  "dependencies": {
34
- "assistant-stream": "^0.3.42"
38
+ "assistant-stream": "^0.3.44"
35
39
  },
36
40
  "peerDependencies": {
37
41
  "@opentelemetry/api": "^1.9.0",
38
42
  "@opentelemetry/exporter-trace-otlp-http": ">=0.200.0",
39
- "@opentelemetry/sdk-trace-base": "^2.1.0"
43
+ "@opentelemetry/sdk-trace-base": "^2.1.0",
44
+ "ai": "^6.0.0 || ^7.0.0"
40
45
  },
41
46
  "peerDependenciesMeta": {
42
47
  "@opentelemetry/api": {
@@ -47,16 +52,21 @@
47
52
  },
48
53
  "@opentelemetry/sdk-trace-base": {
49
54
  "optional": true
55
+ },
56
+ "ai": {
57
+ "optional": true
50
58
  }
51
59
  },
52
60
  "devDependencies": {
53
- "@assistant-ui/x-buildutils": "0.0.26",
54
- "@opentelemetry/api": "^1.9.0",
55
- "@opentelemetry/exporter-trace-otlp-http": "^0.205.0",
56
- "@opentelemetry/sdk-trace-base": "^2.1.0",
57
- "@types/node": "^26.4.1",
61
+ "@assistant-ui/x-buildutils": "0.0.28",
62
+ "@opentelemetry/api": "^1.9.1",
63
+ "@opentelemetry/exporter-trace-otlp-http": "^0.222.0",
64
+ "@opentelemetry/sdk-trace-base": "^2.11.0",
65
+ "@types/node": "^26.5.1",
66
+ "ai": "^7.0.101",
67
+ "ai-v6": "npm:ai@^6.0.277",
58
68
  "jsdom": "^30.0.1",
59
- "vitest": "^4.1.11"
69
+ "vitest": "^5.0.1"
60
70
  },
61
71
  "publishConfig": {
62
72
  "access": "public",
@@ -73,7 +83,11 @@
73
83
  },
74
84
  "scripts": {
75
85
  "build": "aui-build",
76
- "test": "vitest run",
86
+ "test": "pnpm run test:v7 && pnpm run test:peer-v6 && pnpm run test:types:peer-v6",
87
+ "typecheck": "tsc --noEmit",
88
+ "test:v7": "vitest run",
89
+ "test:peer-v6": "vitest run --config vitest.peer-v6.config.ts",
90
+ "test:types:peer-v6": "tsc --noEmit --project tsconfig.peer-v6.json",
77
91
  "test:watch": "vitest"
78
92
  }
79
93
  }
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  AssistantCloudAPI,
3
3
  type AssistantCloudConfig,
4
+ type SdkIdentity,
4
5
  type AssistantCloudTelemetryConfig,
5
6
  } from "./AssistantCloudAPI";
6
7
  import { AssistantCloudAuthTokens } from "./AssistantCloudAuthTokens";
@@ -20,9 +21,11 @@ export class AssistantCloud {
20
21
  public readonly events;
21
22
  public readonly scores;
22
23
  public readonly telemetry: AssistantCloudTelemetryConfig;
24
+ public readonly registerSdk: (sdk: SdkIdentity) => void;
23
25
 
24
26
  constructor(config: AssistantCloudConfig) {
25
27
  const api = new AssistantCloudAPI(config);
28
+ this.registerSdk = api.registerSdk;
26
29
  const t = config.telemetry;
27
30
  this.telemetry =
28
31
  t === false
@@ -6,6 +6,12 @@ import {
6
6
  normalizeBaseUrl,
7
7
  } from "./AssistantCloudAuthStrategy";
8
8
  import type { AssistantCloudRunReport } from "./AssistantCloudRuns";
9
+ import { ASSISTANT_CLOUD_VERSION } from "./version";
10
+
11
+ export type SdkIdentity = {
12
+ name: string;
13
+ version: string;
14
+ };
9
15
 
10
16
  export type AssistantCloudTelemetryConfig = {
11
17
  /**
@@ -53,7 +59,10 @@ export type AssistantCloudConfig = (
53
59
  *
54
60
  * When enabled, the SDK automatically reports run metadata (status, step
55
61
  * count, tool calls, and token usage) to Assistant Cloud after each
56
- * assistant message is saved. No message content is sent.
62
+ * assistant message is saved. Reports can also include assistant output,
63
+ * tool arguments and results, errors, and metadata, which may contain
64
+ * sensitive content. Use `beforeReport` to redact or drop reports, or
65
+ * `telemetry: false` to disable reporting.
57
66
  *
58
67
  * - `true` / `undefined` — enabled with defaults
59
68
  * - `false` — disabled
@@ -89,11 +98,31 @@ type MakeRequestOptions = {
89
98
  keepalive?: boolean | undefined;
90
99
  };
91
100
 
101
+ const HEADER_TOKEN = /^[\x21-\x7e]+$/;
102
+
92
103
  export class AssistantCloudAPI {
93
104
  public _auth: AssistantCloudAuthStrategy;
94
105
  public _baseUrl;
106
+ public readonly registerSdk: (sdk: SdkIdentity) => void;
107
+ public readonly sdkHeader: () => string;
95
108
 
96
109
  constructor(config: AssistantCloudConfig) {
110
+ const sdks = new Map<string, SdkIdentity>();
111
+ this.registerSdk = (sdk) => {
112
+ const name = sdk.name.trim();
113
+ const version = sdk.version.trim();
114
+ if (!HEADER_TOKEN.test(name) || !HEADER_TOKEN.test(version)) return;
115
+ sdks.set(`${name}/${version}`, { name, version });
116
+ };
117
+ this.sdkHeader = () =>
118
+ [
119
+ `assistant-cloud/${ASSISTANT_CLOUD_VERSION}`,
120
+ ...Array.from(
121
+ sdks.values(),
122
+ ({ name, version }) => `${name}/${version}`,
123
+ ),
124
+ ].join(" ");
125
+
97
126
  if ("authToken" in config) {
98
127
  this._baseUrl = normalizeBaseUrl(config.baseUrl);
99
128
  this._auth = new AssistantCloudJWTAuthStrategy(config.authToken);
@@ -131,6 +160,7 @@ export class AssistantCloudAPI {
131
160
  ...authHeaders,
132
161
  ...options.headers,
133
162
  "Content-Type": "application/json",
163
+ "Aui-Sdk": this.sdkHeader(),
134
164
  };
135
165
 
136
166
  const queryParams = new URLSearchParams();
@@ -160,8 +160,9 @@ export class AssistantCloudJWTAuthStrategy implements AssistantCloudAuthStrategy
160
160
  const token = await this.#authTokenCallback();
161
161
  if (!token) return false;
162
162
 
163
+ const tokenExpiry = getJwtExpiry(token);
163
164
  this.cachedToken = token;
164
- this.tokenExpiry = getJwtExpiry(token);
165
+ this.tokenExpiry = tokenExpiry;
165
166
 
166
167
  return { Authorization: `Bearer ${token}` };
167
168
  }
@@ -175,8 +176,9 @@ export class AssistantCloudJWTAuthStrategy implements AssistantCloudAuthStrategy
175
176
  throw new Error("Invalid auth header received");
176
177
  }
177
178
 
179
+ const tokenExpiry = getJwtExpiry(token);
178
180
  this.cachedToken = token;
179
- this.tokenExpiry = getJwtExpiry(token);
181
+ this.tokenExpiry = tokenExpiry;
180
182
  }
181
183
  }
182
184
 
@@ -12,11 +12,11 @@ const event = (index: number): AssistantCloudEvent => ({
12
12
  thread_id: `thread-${index}`,
13
13
  });
14
14
 
15
- const createEvents = (enabled = true) => {
15
+ const createEvents = (enabled: boolean | (() => boolean) = true) => {
16
16
  const makeRequest = vi.fn().mockResolvedValue({ accepted: 1 });
17
17
  const events = new AssistantCloudEvents(
18
18
  { makeRequest } as unknown as AssistantCloudAPI,
19
- () => enabled,
19
+ typeof enabled === "function" ? enabled : () => enabled,
20
20
  );
21
21
  return { events, makeRequest };
22
22
  };
@@ -106,6 +106,31 @@ describe("AssistantCloudEvents", () => {
106
106
  expect(makeRequest).not.toHaveBeenCalled();
107
107
  });
108
108
 
109
+ it("does not send queued batches after telemetry is disabled", async () => {
110
+ let enabled = true;
111
+ let resolveFirstRequest!: (value: { accepted: number }) => void;
112
+ const { events, makeRequest } = createEvents(() => enabled);
113
+ makeRequest.mockImplementationOnce(
114
+ () =>
115
+ new Promise((resolve) => {
116
+ resolveFirstRequest = resolve;
117
+ }),
118
+ );
119
+
120
+ for (let index = 0; index < 51; index++) events.track(event(index));
121
+ await vi.waitFor(() => expect(makeRequest).toHaveBeenCalledOnce());
122
+
123
+ enabled = false;
124
+ resolveFirstRequest({ accepted: 50 });
125
+
126
+ await vi.waitFor(() =>
127
+ expect(makeRequest.mock.results[0]?.value).resolves.toEqual({
128
+ accepted: 50,
129
+ }),
130
+ );
131
+ expect(makeRequest).toHaveBeenCalledOnce();
132
+ });
133
+
109
134
  it("keeps every request below the server batch limit", async () => {
110
135
  const { events, makeRequest } = createEvents();
111
136
 
@@ -121,4 +146,45 @@ describe("AssistantCloudEvents", () => {
121
146
  expect(options.body.events.length).toBeLessThanOrEqual(50);
122
147
  }
123
148
  });
149
+
150
+ it.each([Number.NaN, Number.POSITIVE_INFINITY, Number.NEGATIVE_INFINITY])(
151
+ "drops props containing the non-finite number %s",
152
+ async (value) => {
153
+ const { events, makeRequest } = createEvents();
154
+
155
+ events.track({ kind: "message_sent", props: { value } });
156
+ events.dispose();
157
+
158
+ await vi.waitFor(() => expect(makeRequest).toHaveBeenCalledOnce());
159
+ expect(makeRequest).toHaveBeenCalledWith("/events", {
160
+ method: "POST",
161
+ body: { events: [{ kind: "message_sent" }] },
162
+ keepalive: true,
163
+ });
164
+ },
165
+ );
166
+
167
+ it("keeps finite numeric props", async () => {
168
+ const { events, makeRequest } = createEvents();
169
+
170
+ events.track({
171
+ kind: "message_sent",
172
+ props: { negative: -1.5, zero: 0, positive: 2.5 },
173
+ });
174
+ events.dispose();
175
+
176
+ await vi.waitFor(() => expect(makeRequest).toHaveBeenCalledOnce());
177
+ expect(makeRequest).toHaveBeenCalledWith("/events", {
178
+ method: "POST",
179
+ body: {
180
+ events: [
181
+ {
182
+ kind: "message_sent",
183
+ props: { negative: -1.5, zero: 0, positive: 2.5 },
184
+ },
185
+ ],
186
+ },
187
+ keepalive: true,
188
+ });
189
+ });
124
190
  });
@@ -118,6 +118,11 @@ export class AssistantCloudEvents {
118
118
 
119
119
  private async flushPending(): Promise<void> {
120
120
  while (this.buffer.length > 0) {
121
+ if (!this.isEnabled()) {
122
+ this.buffer = [];
123
+ return;
124
+ }
125
+
121
126
  const events = this.buffer.splice(0, MAX_BATCH_SIZE);
122
127
  try {
123
128
  await this.cloud.makeRequest("/events", {
@@ -171,6 +176,7 @@ const normalizeProps = (
171
176
  entries.some(
172
177
  ([, value]) =>
173
178
  (typeof value === "string" && value.length > 256) ||
179
+ (typeof value === "number" && !Number.isFinite(value)) ||
174
180
  (typeof value !== "string" &&
175
181
  typeof value !== "number" &&
176
182
  typeof value !== "boolean"),
@@ -68,6 +68,7 @@ export class AssistantCloudRuns {
68
68
  return {
69
69
  ...headers,
70
70
  Accept: "text/plain",
71
+ "Aui-Sdk": this.cloud.sdkHeader(),
71
72
  };
72
73
  },
73
74
  body: {