assistant-cloud 0.1.43 → 0.2.0

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 (81) hide show
  1. package/README.md +40 -0
  2. package/dist/AssistantCloud.d.ts +4 -0
  3. package/dist/AssistantCloud.d.ts.map +1 -1
  4. package/dist/AssistantCloud.js +11 -5
  5. package/dist/AssistantCloud.js.map +1 -1
  6. package/dist/AssistantCloudAPI.d.ts +17 -1
  7. package/dist/AssistantCloudAPI.d.ts.map +1 -1
  8. package/dist/AssistantCloudAPI.js +20 -9
  9. package/dist/AssistantCloudAPI.js.map +1 -1
  10. package/dist/AssistantCloudAuthStrategy.d.ts +4 -1
  11. package/dist/AssistantCloudAuthStrategy.d.ts.map +1 -1
  12. package/dist/AssistantCloudAuthStrategy.js +11 -1
  13. package/dist/AssistantCloudAuthStrategy.js.map +1 -1
  14. package/dist/AssistantCloudEvents.d.ts +32 -0
  15. package/dist/AssistantCloudEvents.d.ts.map +1 -0
  16. package/dist/AssistantCloudEvents.js +115 -0
  17. package/dist/AssistantCloudEvents.js.map +1 -0
  18. package/dist/AssistantCloudFiles.d.ts +12 -1
  19. package/dist/AssistantCloudFiles.d.ts.map +1 -1
  20. package/dist/AssistantCloudFiles.js +13 -1
  21. package/dist/AssistantCloudFiles.js.map +1 -1
  22. package/dist/AssistantCloudRuns.d.ts +11 -0
  23. package/dist/AssistantCloudRuns.d.ts.map +1 -1
  24. package/dist/AssistantCloudRuns.js.map +1 -1
  25. package/dist/AssistantCloudScores.d.ts +27 -0
  26. package/dist/AssistantCloudScores.d.ts.map +1 -0
  27. package/dist/AssistantCloudScores.js +30 -0
  28. package/dist/AssistantCloudScores.js.map +1 -0
  29. package/dist/AssistantCloudThreadMessages.d.ts +11 -1
  30. package/dist/AssistantCloudThreadMessages.d.ts.map +1 -1
  31. package/dist/AssistantCloudThreadMessages.js +12 -1
  32. package/dist/AssistantCloudThreadMessages.js.map +1 -1
  33. package/dist/AssistantCloudThreads.d.ts +8 -0
  34. package/dist/AssistantCloudThreads.d.ts.map +1 -1
  35. package/dist/AssistantCloudThreads.js +9 -1
  36. package/dist/AssistantCloudThreads.js.map +1 -1
  37. package/dist/CloudMessagePersistence.d.ts +4 -0
  38. package/dist/CloudMessagePersistence.d.ts.map +1 -1
  39. package/dist/CloudMessagePersistence.js +38 -11
  40. package/dist/CloudMessagePersistence.js.map +1 -1
  41. package/dist/cloudResponse.d.ts +4 -1
  42. package/dist/cloudResponse.d.ts.map +1 -1
  43. package/dist/cloudResponse.js +14 -1
  44. package/dist/cloudResponse.js.map +1 -1
  45. package/dist/index.d.ts +7 -3
  46. package/dist/index.js +5 -2
  47. package/dist/runTelemetry.d.ts +70 -7
  48. package/dist/runTelemetry.d.ts.map +1 -1
  49. package/dist/runTelemetry.js +144 -1
  50. package/dist/runTelemetry.js.map +1 -1
  51. package/dist/telemetry/index.d.ts +32 -0
  52. package/dist/telemetry/index.d.ts.map +1 -0
  53. package/dist/telemetry/index.js +62 -0
  54. package/dist/telemetry/index.js.map +1 -0
  55. package/package.json +28 -4
  56. package/src/AssistantCloud.ts +20 -8
  57. package/src/AssistantCloudAPI.ts +42 -10
  58. package/src/AssistantCloudAuthStrategy.ts +13 -0
  59. package/src/AssistantCloudEvents.test.ts +124 -0
  60. package/src/AssistantCloudEvents.ts +185 -0
  61. package/src/AssistantCloudFiles.test.ts +59 -0
  62. package/src/AssistantCloudFiles.ts +39 -0
  63. package/src/AssistantCloudRuns.ts +12 -1
  64. package/src/AssistantCloudScores.test.ts +113 -0
  65. package/src/AssistantCloudScores.ts +61 -0
  66. package/src/AssistantCloudThreadMessages.test.ts +51 -0
  67. package/src/AssistantCloudThreadMessages.ts +33 -0
  68. package/src/AssistantCloudThreads.test.ts +21 -0
  69. package/src/AssistantCloudThreads.ts +21 -0
  70. package/src/CloudMessagePersistence.ts +48 -16
  71. package/src/cloudResponse.test.ts +34 -1
  72. package/src/cloudResponse.ts +30 -0
  73. package/src/index.ts +24 -1
  74. package/src/runTelemetry.test.ts +325 -0
  75. package/src/runTelemetry.ts +257 -6
  76. package/src/telemetry/index.test.ts +252 -0
  77. package/src/telemetry/index.ts +98 -0
  78. package/src/tests/AssistantCloud.test.ts +22 -0
  79. package/src/tests/AssistantCloudAPI.test.ts +44 -4
  80. package/src/tests/AssistantCloudAuthStrategy.test.ts +71 -2
  81. package/src/tests/CloudMessagePersistence.test.ts +115 -0
@@ -1,4 +1,5 @@
1
1
  import { SamplingCallData } from "./instrumentMcpSampling.js";
2
+ import { AssistantCloudRunReport } from "./AssistantCloudRuns.js";
2
3
  //#region src/runTelemetry.d.ts
3
4
  type AssistantCloudRunReportToolCall = {
4
5
  tool_name: string;
@@ -10,6 +11,28 @@ type AssistantCloudRunReportToolCall = {
10
11
  end_ms?: number;
11
12
  sampling_calls?: SamplingCallData[];
12
13
  };
14
+ type RunReportOutcome = "aborted" | "disconnected" | "length" | "content_filter";
15
+ /**
16
+ * Maps a finish event to the report status and outcome. `fallbackStatus`
17
+ * applies when the event carries neither a finish reason nor a failure flag.
18
+ */
19
+ declare function deriveRunOutcome(input: {
20
+ finishReason?: string | undefined;
21
+ isAbort?: boolean | undefined;
22
+ isDisconnect?: boolean | undefined;
23
+ isError?: boolean | undefined;
24
+ }, fallbackStatus?: "completed" | "incomplete"): {
25
+ status: "completed" | "incomplete" | "error";
26
+ outcome?: RunReportOutcome;
27
+ };
28
+ /**
29
+ * Reads the message and code the runs endpoint stores for a failed run. The
30
+ * code is the error's `code` when it has one, else its class name.
31
+ */
32
+ declare function describeRunError(error: unknown): {
33
+ error?: string;
34
+ errorCode?: string;
35
+ };
13
36
  /**
14
37
  * Clamps a string to the size the runs endpoint accepts for a single span
15
38
  * field.
@@ -33,15 +56,23 @@ type RunTelemetryToolCallInit = {
33
56
  * base64 image and audio payloads that would otherwise dominate the report.
34
57
  */
35
58
  declare function createRunTelemetryToolCall(init: RunTelemetryToolCallInit): AssistantCloudRunReportToolCall;
59
+ /**
60
+ * Resolves the model ID a run reports, in the order an app can supply it: an
61
+ * explicit `modelId`, the `custom` bag, then the per-step `response.modelId`
62
+ * that a `messageMetadata` callback copies off the AI SDK's finish-step part.
63
+ * The AI SDK puts no model ID on a UI message part, so message metadata is the
64
+ * only channel one arrives on.
65
+ */
66
+ declare function extractRunTelemetryModelId(metadata: Record<string, unknown> | undefined): string | undefined;
36
67
  type RunTelemetryUsage = {
37
- inputTokens?: number;
38
- outputTokens?: number;
39
- reasoningTokens?: number;
40
- cachedInputTokens?: number;
68
+ inputTokens?: number | undefined;
69
+ outputTokens?: number | undefined;
70
+ reasoningTokens?: number | undefined;
71
+ cachedInputTokens?: number | undefined;
41
72
  };
42
73
  type RunTelemetryUsageInit = RunTelemetryUsage & {
43
- promptTokens?: number;
44
- completionTokens?: number;
74
+ promptTokens?: number | undefined;
75
+ completionTokens?: number | undefined;
45
76
  inputTokenDetails?: {
46
77
  cacheReadTokens?: number;
47
78
  };
@@ -49,6 +80,38 @@ type RunTelemetryUsageInit = RunTelemetryUsage & {
49
80
  reasoningTokens?: number;
50
81
  };
51
82
  };
83
+ type RunReportStepInit = {
84
+ usage?: RunTelemetryUsageInit | undefined;
85
+ toolCalls?: AssistantCloudRunReportToolCall[] | undefined;
86
+ startMs?: number | undefined;
87
+ endMs?: number | undefined;
88
+ finishReason?: string | undefined;
89
+ };
90
+ type RunReportInit = {
91
+ threadId: string;
92
+ status: AssistantCloudRunReport["status"];
93
+ outcome?: RunReportOutcome | undefined;
94
+ errorCode?: string | undefined;
95
+ error?: string | undefined;
96
+ messageId?: string | undefined;
97
+ traceId?: string | undefined;
98
+ modelId?: string | undefined;
99
+ provider?: string | undefined;
100
+ usage?: RunTelemetryUsageInit | undefined;
101
+ steps?: RunReportStepInit[] | undefined;
102
+ totalSteps?: number | undefined;
103
+ toolCalls?: AssistantCloudRunReportToolCall[] | undefined;
104
+ durationMs?: number | undefined;
105
+ firstTokenMs?: number | undefined;
106
+ outputText?: string | undefined;
107
+ metadata?: Record<string, unknown> | undefined;
108
+ telemetry?: {
109
+ environment?: string | undefined;
110
+ release?: string | undefined;
111
+ tags?: readonly string[] | undefined;
112
+ };
113
+ };
114
+ declare function createRunReport(init: RunReportInit): AssistantCloudRunReport;
52
115
  /**
53
116
  * Resolves the token counts a provider reports under any of the names the AI
54
117
  * SDK has used: the current top-level ones, the legacy prompt/completion pair,
@@ -57,5 +120,5 @@ type RunTelemetryUsageInit = RunTelemetryUsage & {
57
120
  */
58
121
  declare function normalizeRunTelemetryUsage(usage: RunTelemetryUsageInit): RunTelemetryUsage | undefined;
59
122
  //#endregion
60
- export { AssistantCloudRunReportToolCall, RunTelemetryToolCallInit, RunTelemetryUsage, RunTelemetryUsageInit, createRunTelemetryToolCall, normalizeRunTelemetryUsage, truncateRunTelemetryText };
123
+ export { AssistantCloudRunReportToolCall, RunReportInit, RunReportOutcome, RunReportStepInit, RunTelemetryToolCallInit, RunTelemetryUsage, RunTelemetryUsageInit, createRunReport, createRunTelemetryToolCall, deriveRunOutcome, describeRunError, extractRunTelemetryModelId, normalizeRunTelemetryUsage, truncateRunTelemetryText };
61
124
  //# sourceMappingURL=runTelemetry.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"runTelemetry.d.ts","names":[],"sources":["../src/runTelemetry.ts"],"mappings":";;KAMY;EACV;EACA;EACA;EACA;EACA;EACA;EACA;EACA,iBAAiB;;;;;;iBAOH,yBAAyB;KAwC7B;EACV;EACA;EACA;;;;;EAKA;EACA;EACA;;;;;;;iBAQc,2BACd,MAAM,2BACL;KAgBS;EACV;EACA;EACA;EACA;;KAGU,wBAAwB;EAClC;EACA;EACA;IAAsB;;EACtB;IAAuB;;;;;;;;;iBAST,2BACd,OAAO,wBACN"}
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"}
@@ -2,6 +2,54 @@
2
2
  const MAX_TELEMETRY_TEXT_LENGTH = 5e4;
3
3
  const BASE64_PATTERN = /^[A-Za-z0-9+/]{100,}={0,2}$/;
4
4
  /**
5
+ * Maps a finish event to the report status and outcome. `fallbackStatus`
6
+ * applies when the event carries neither a finish reason nor a failure flag.
7
+ */
8
+ function deriveRunOutcome(input, fallbackStatus = "completed") {
9
+ if (input.isError) return { status: "error" };
10
+ if (input.isAbort) return {
11
+ status: "incomplete",
12
+ outcome: "aborted"
13
+ };
14
+ if (input.isDisconnect) return {
15
+ status: "incomplete",
16
+ outcome: "disconnected"
17
+ };
18
+ switch (input.finishReason) {
19
+ case "length": return {
20
+ status: "incomplete",
21
+ outcome: "length"
22
+ };
23
+ case "content-filter":
24
+ case "content_filter": return {
25
+ status: "incomplete",
26
+ outcome: "content_filter"
27
+ };
28
+ case "cancelled": return {
29
+ status: "incomplete",
30
+ outcome: "aborted"
31
+ };
32
+ case "error": return { status: "error" };
33
+ default: return { status: input.finishReason === void 0 ? fallbackStatus : "completed" };
34
+ }
35
+ }
36
+ const MAX_RUN_ERROR_CODE_LENGTH = 64;
37
+ const MAX_RUN_ERROR_LENGTH = 2048;
38
+ /**
39
+ * Reads the message and code the runs endpoint stores for a failed run. The
40
+ * code is the error's `code` when it has one, else its class name.
41
+ */
42
+ function describeRunError(error) {
43
+ if (error == null) return {};
44
+ const record = typeof error === "object" ? error : void 0;
45
+ const message = typeof error === "string" ? error : typeof record?.message === "string" ? record.message : void 0;
46
+ const code = typeof record?.code === "string" ? record.code : typeof record?.name === "string" && record.name !== "Error" ? record.name : void 0;
47
+ return {
48
+ ...message ? { error: message.slice(0, MAX_RUN_ERROR_LENGTH) } : void 0,
49
+ ...code ? { errorCode: code.slice(0, MAX_RUN_ERROR_CODE_LENGTH) } : void 0
50
+ };
51
+ }
52
+ /**
5
53
  * Clamps a string to the size the runs endpoint accepts for a single span
6
54
  * field.
7
55
  */
@@ -58,6 +106,101 @@ function createRunTelemetryToolCall(init) {
58
106
  return call;
59
107
  }
60
108
  /**
109
+ * Resolves the model ID a run reports, in the order an app can supply it: an
110
+ * explicit `modelId`, the `custom` bag, then the per-step `response.modelId`
111
+ * that a `messageMetadata` callback copies off the AI SDK's finish-step part.
112
+ * The AI SDK puts no model ID on a UI message part, so message metadata is the
113
+ * only channel one arrives on.
114
+ */
115
+ function extractRunTelemetryModelId(metadata) {
116
+ if (!metadata) return void 0;
117
+ if (typeof metadata.modelId === "string") return metadata.modelId;
118
+ const custom = metadata.custom;
119
+ if (typeof custom?.modelId === "string") return custom.modelId;
120
+ const steps = metadata.steps;
121
+ if (!Array.isArray(steps)) return void 0;
122
+ for (const step of steps) {
123
+ if (!step || typeof step !== "object") continue;
124
+ const response = step.response;
125
+ if (!response || typeof response !== "object") continue;
126
+ const modelId = response.modelId;
127
+ if (typeof modelId === "string") return modelId;
128
+ }
129
+ }
130
+ function assignUsage(report, usage) {
131
+ if (!usage) return;
132
+ const normalized = normalizeRunTelemetryUsage(usage);
133
+ if (!normalized) return;
134
+ if (normalized.inputTokens !== void 0) report.input_tokens = normalized.inputTokens;
135
+ if (normalized.outputTokens !== void 0) report.output_tokens = normalized.outputTokens;
136
+ if (normalized.reasoningTokens !== void 0) report.reasoning_tokens = normalized.reasoningTokens;
137
+ if (normalized.cachedInputTokens !== void 0) report.cached_input_tokens = normalized.cachedInputTokens;
138
+ }
139
+ function createRunReportStep(init) {
140
+ const step = {};
141
+ const usage = init.usage ? normalizeRunTelemetryUsage(init.usage) : void 0;
142
+ if (usage?.inputTokens !== void 0) step.input_tokens = usage.inputTokens;
143
+ if (usage?.outputTokens !== void 0) step.output_tokens = usage.outputTokens;
144
+ if (usage?.reasoningTokens !== void 0) step.reasoning_tokens = usage.reasoningTokens;
145
+ if (usage?.cachedInputTokens !== void 0) step.cached_input_tokens = usage.cachedInputTokens;
146
+ if (init.toolCalls !== void 0) step.tool_calls = init.toolCalls;
147
+ if (init.startMs !== void 0) step.start_ms = init.startMs;
148
+ if (init.endMs !== void 0) step.end_ms = init.endMs;
149
+ if (init.finishReason !== void 0) step.finish_reason = init.finishReason.slice(0, 32);
150
+ return step;
151
+ }
152
+ function normalizeRunReportTags(tags) {
153
+ if (!tags) return void 0;
154
+ const result = [];
155
+ const seen = /* @__PURE__ */ new Set();
156
+ for (const tag of tags) {
157
+ const normalized = tag.trim().slice(0, 64).trim();
158
+ if (!normalized || seen.has(normalized)) continue;
159
+ seen.add(normalized);
160
+ result.push(normalized);
161
+ if (result.length === 20) break;
162
+ }
163
+ return result.length > 0 ? result : void 0;
164
+ }
165
+ function normalizeRunReportMilliseconds(value) {
166
+ if (value === void 0 || !Number.isFinite(value)) return void 0;
167
+ return Math.max(0, Math.round(value));
168
+ }
169
+ function createRunReport(init) {
170
+ const report = {
171
+ thread_id: init.threadId,
172
+ status: init.status
173
+ };
174
+ const traceId = init.traceId?.toLowerCase();
175
+ if (traceId && /^[0-9a-f]{32}$/.test(traceId)) report.trace_id = traceId;
176
+ if (init.outcome !== void 0) report.outcome_type = init.outcome;
177
+ if (init.errorCode !== void 0) report.error_code = init.errorCode;
178
+ if (init.error !== void 0) report.error = init.error;
179
+ if (init.messageId !== void 0) report.message_id = init.messageId;
180
+ if (init.modelId !== void 0) report.model_id = init.modelId;
181
+ if (init.provider !== void 0) {
182
+ report.provider = init.provider;
183
+ report.provider_type = init.provider;
184
+ }
185
+ assignUsage(report, init.usage);
186
+ if (init.steps !== void 0) {
187
+ report.steps = init.steps.map(createRunReportStep);
188
+ report.total_steps = init.steps.length;
189
+ } else if (init.totalSteps !== void 0) report.total_steps = init.totalSteps;
190
+ if (init.toolCalls !== void 0) report.tool_calls = init.toolCalls;
191
+ const durationMs = normalizeRunReportMilliseconds(init.durationMs);
192
+ if (durationMs !== void 0) report.duration_ms = durationMs;
193
+ const firstTokenMs = normalizeRunReportMilliseconds(init.firstTokenMs);
194
+ if (firstTokenMs !== void 0) report.first_token_ms = firstTokenMs;
195
+ if (init.outputText !== void 0) report.output_text = truncateRunTelemetryText(init.outputText);
196
+ if (init.metadata !== void 0) report.metadata = init.metadata;
197
+ if (init.telemetry?.environment !== void 0) report.environment = init.telemetry.environment;
198
+ if (init.telemetry?.release !== void 0) report.release = init.telemetry.release;
199
+ const tags = normalizeRunReportTags(init.telemetry?.tags);
200
+ if (tags !== void 0) report.tags = tags;
201
+ return report;
202
+ }
203
+ /**
61
204
  * Resolves the token counts a provider reports under any of the names the AI
62
205
  * SDK has used: the current top-level ones, the legacy prompt/completion pair,
63
206
  * and the v7 token detail objects. Returns undefined when no count is present,
@@ -77,6 +220,6 @@ function normalizeRunTelemetryUsage(usage) {
77
220
  };
78
221
  }
79
222
  //#endregion
80
- export { createRunTelemetryToolCall, normalizeRunTelemetryUsage, truncateRunTelemetryText };
223
+ export { createRunReport, createRunTelemetryToolCall, deriveRunOutcome, describeRunError, extractRunTelemetryModelId, normalizeRunTelemetryUsage, truncateRunTelemetryText };
81
224
 
82
225
  //# sourceMappingURL=runTelemetry.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"runTelemetry.js","names":[],"sources":["../src/runTelemetry.ts"],"sourcesContent":["import type { SamplingCallData } from \"./instrumentMcpSampling\";\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\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\nexport type RunTelemetryUsage = {\n inputTokens?: number;\n outputTokens?: number;\n reasoningTokens?: number;\n cachedInputTokens?: number;\n};\n\nexport type RunTelemetryUsageInit = RunTelemetryUsage & {\n promptTokens?: number;\n completionTokens?: number;\n inputTokenDetails?: { cacheReadTokens?: number };\n outputTokenDetails?: { reasoningTokens?: number };\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":";AAEA,MAAM,4BAA4B;AAElC,MAAM,iBAAiB;;;;;AAiBvB,SAAgB,yBAAyB,OAAuB;CAC9D,IAAI,MAAM,UAAU,2BAA2B,OAAO;CACtD,OAAO,MAAM,MAAM,GAAG,yBAAyB;AACjD;AAEA,SAAS,cAAc,OAAoC;CACzD,IAAI,SAAS,MAAM,OAAO,KAAA;CAC1B,IAAI;EACF,OAAO,yBAAyB,KAAK,UAAU,KAAK,CAAC;CACvD,QAAQ;EACN;CACF;AACF;AAEA,SAAS,mBAAmB,OAAoC;CAC9D,IAAI,SAAS,MAAM,OAAO,KAAA;CAC1B,IAAI;EACF,MAAM,SAAS,OAAO,UAAU,WAAW,KAAK,MAAM,KAAK,IAAI;EAC/D,IAAI,MAAM,QAAQ,MAAM,GAAG;GACzB,MAAM,aAAa,OAAO,KAAK,SAAS;IACtC,IAAI,QAAQ,OAAO,SAAS,YAAY,KAAK,MAExC;UAAA,KAAK,SAAS,WAAW,KAAK,SAAS,YACxC,OAAO,KAAK,SAAS,YACrB,eAAe,KAAK,KAAK,KAAK,MAAM,GAAG,GAAG,CAAC,GAC3C;MACA,MAAM,UAAW,KAAK,KAAK,SAAS,IAAK,IAAI,KAAA,CAAM,QAAQ,CAAC;MAC5D,OAAO;OAAE,GAAG;OAAM,MAAM,IAAI,KAAK,KAAK,IAAI,OAAO;MAAK;KACxD;;IAEF,OAAO;GACT,CAAC;GACD,OAAO,yBAAyB,KAAK,UAAU,UAAU,CAAC;EAC5D;CACF,QAAQ,CAER;CACA,OAAO,cAAc,KAAK;AAC5B;;;;;;AAoBA,SAAgB,2BACd,MACiC;CACjC,MAAM,EAAE,UAAU,YAAY,MAAM,UAAU,QAAQ,eAAe;CACrE,MAAM,OAAwC;EAC5C,WAAW;EACX,cAAc;CAChB;CACA,MAAM,WACJ,YAAY,OAAO,yBAAyB,QAAQ,IAAI,cAAc,IAAI;CAC5E,IAAI,aAAa,KAAA,GAAW,KAAK,YAAY;CAC7C,MAAM,aACJ,eAAe,QAAQ,mBAAmB,MAAM,IAAI,cAAc,MAAM;CAC1E,IAAI,eAAe,KAAA,GAAW,KAAK,cAAc;CACjD,IAAI,YAAY,KAAK,cAAc;CACnC,OAAO;AACT;;;;;;;AAsBA,SAAgB,2BACd,OAC+B;CAC/B,MAAM,cAAc,MAAM,eAAe,MAAM;CAC/C,MAAM,eAAe,MAAM,gBAAgB,MAAM;CAEjD,MAAM,kBACJ,MAAM,mBAAmB,MAAM,oBAAoB;CACrD,MAAM,oBACJ,MAAM,qBAAqB,MAAM,mBAAmB;CAEtD,IACE,eAAe,QACf,gBAAgB,QAChB,mBAAmB,QACnB,qBAAqB,MAErB;CAGF,OAAO;EACL,GAAI,eAAe,OAAO,EAAE,YAAY,IAAI,KAAA;EAC5C,GAAI,gBAAgB,OAAO,EAAE,aAAa,IAAI,KAAA;EAC9C,GAAI,mBAAmB,OAAO,EAAE,gBAAgB,IAAI,KAAA;EACpD,GAAI,qBAAqB,OAAO,EAAE,kBAAkB,IAAI,KAAA;CAC1D;AACF"}
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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"}
@@ -0,0 +1,32 @@
1
+ import { ReadableSpan, SpanExporter, SpanProcessor } from "@opentelemetry/sdk-trace-base";
2
+ //#region src/telemetry/index.d.ts
3
+ type AssistantCloudTraceExportOptions = {
4
+ apiKey: string;
5
+ baseUrl?: string;
6
+ headers?: Record<string, string>;
7
+ };
8
+ type AssistantCloudSpanProcessorOptions = {
9
+ filter?: (span: ReadableSpan) => boolean;
10
+ };
11
+ declare function assistantCloudTraceExportOptions({ apiKey, baseUrl, headers }: AssistantCloudTraceExportOptions): {
12
+ url: string;
13
+ headers: Record<string, string>;
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(): {
19
+ traceId?: string;
20
+ };
21
+ declare function withAssistantCloudTraceMetadata<Part extends {
22
+ type: string;
23
+ } = {
24
+ type: string;
25
+ }>(messageMetadata?: (options: {
26
+ part: Part;
27
+ }) => Record<string, unknown> | undefined): (options: {
28
+ part: Part;
29
+ }) => Record<string, unknown> | undefined;
30
+ //#endregion
31
+ export { AssistantCloudSpanProcessorOptions, AssistantCloudTraceExportOptions, assistantCloudTraceExportOptions, assistantCloudTraceMetadata, createAssistantCloudSpanProcessor, createAssistantCloudTraceExporter, isAssistantCloudSpan, withAssistantCloudTraceMetadata };
32
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,62 @@
1
+ import { trace } from "@opentelemetry/api";
2
+ import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
3
+ import { BatchSpanProcessor } from "@opentelemetry/sdk-trace-base";
4
+ //#region src/telemetry/index.ts
5
+ const DEFAULT_BACKEND_BASE_URL = "https://backend.assistant-api.com";
6
+ const AI_SPAN_PREFIXES = [
7
+ "gen_ai.",
8
+ "ai.",
9
+ "llm."
10
+ ];
11
+ function assistantCloudTraceExportOptions({ apiKey, baseUrl = DEFAULT_BACKEND_BASE_URL, headers }) {
12
+ if (!apiKey) throw new Error("An Assistant Cloud API key is required");
13
+ return {
14
+ url: `${trimTrailingSlashes(baseUrl)}/v1/traces`,
15
+ headers: {
16
+ Authorization: `Bearer ${apiKey}`,
17
+ ...headers
18
+ }
19
+ };
20
+ }
21
+ function trimTrailingSlashes(url) {
22
+ let end = url.length;
23
+ while (end > 0 && url[end - 1] === "/") end--;
24
+ return url.slice(0, end);
25
+ }
26
+ function createAssistantCloudTraceExporter(options) {
27
+ return new OTLPTraceExporter(assistantCloudTraceExportOptions(options));
28
+ }
29
+ function isAssistantCloudSpan(span) {
30
+ return typeof span.attributes["gen_ai.operation.name"] === "string" || AI_SPAN_PREFIXES.some((prefix) => span.name.startsWith(prefix));
31
+ }
32
+ function createAssistantCloudSpanProcessor(exporter, options = {}) {
33
+ const processor = new BatchSpanProcessor(exporter);
34
+ const filter = options.filter ?? isAssistantCloudSpan;
35
+ return {
36
+ onStart: (span, parentContext) => processor.onStart(span, parentContext),
37
+ onEnd: (span) => {
38
+ if (filter(span)) processor.onEnd(span);
39
+ },
40
+ forceFlush: () => processor.forceFlush(),
41
+ shutdown: () => processor.shutdown()
42
+ };
43
+ }
44
+ function assistantCloudTraceMetadata() {
45
+ const spanContext = trace.getActiveSpan()?.spanContext();
46
+ return spanContext && trace.isSpanContextValid(spanContext) ? { traceId: spanContext.traceId } : {};
47
+ }
48
+ function withAssistantCloudTraceMetadata(messageMetadata) {
49
+ return (options) => {
50
+ const metadata = messageMetadata?.(options);
51
+ if (options.part.type !== "start") return metadata;
52
+ const trace = assistantCloudTraceMetadata();
53
+ return trace.traceId === void 0 ? metadata : {
54
+ ...metadata,
55
+ ...trace
56
+ };
57
+ };
58
+ }
59
+ //#endregion
60
+ export { assistantCloudTraceExportOptions, assistantCloudTraceMetadata, createAssistantCloudSpanProcessor, createAssistantCloudTraceExporter, isAssistantCloudSpan, withAssistantCloudTraceMetadata };
61
+
62
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/telemetry/index.ts"],"sourcesContent":["import { trace } from \"@opentelemetry/api\";\nimport { OTLPTraceExporter } from \"@opentelemetry/exporter-trace-otlp-http\";\nimport {\n BatchSpanProcessor,\n type ReadableSpan,\n type SpanExporter,\n type SpanProcessor,\n} from \"@opentelemetry/sdk-trace-base\";\n\nconst DEFAULT_BACKEND_BASE_URL = \"https://backend.assistant-api.com\";\nconst AI_SPAN_PREFIXES = [\"gen_ai.\", \"ai.\", \"llm.\"];\n\nexport type AssistantCloudTraceExportOptions = {\n apiKey: string;\n baseUrl?: string;\n headers?: Record<string, string>;\n};\n\nexport type AssistantCloudSpanProcessorOptions = {\n filter?: (span: ReadableSpan) => boolean;\n};\n\nexport function assistantCloudTraceExportOptions({\n apiKey,\n baseUrl = DEFAULT_BACKEND_BASE_URL,\n headers,\n}: AssistantCloudTraceExportOptions): {\n url: string;\n headers: Record<string, string>;\n} {\n if (!apiKey) throw new Error(\"An Assistant Cloud API key is required\");\n\n return {\n url: `${trimTrailingSlashes(baseUrl)}/v1/traces`,\n headers: {\n Authorization: `Bearer ${apiKey}`,\n ...headers,\n },\n };\n}\n\nfunction trimTrailingSlashes(url: string): string {\n let end = url.length;\n while (end > 0 && url[end - 1] === \"/\") end--;\n return url.slice(0, end);\n}\n\nexport function createAssistantCloudTraceExporter(\n options: AssistantCloudTraceExportOptions,\n): SpanExporter {\n return new OTLPTraceExporter(assistantCloudTraceExportOptions(options));\n}\n\nexport function isAssistantCloudSpan(span: ReadableSpan): boolean {\n return (\n typeof span.attributes[\"gen_ai.operation.name\"] === \"string\" ||\n AI_SPAN_PREFIXES.some((prefix) => span.name.startsWith(prefix))\n );\n}\n\nexport function createAssistantCloudSpanProcessor(\n exporter: SpanExporter,\n options: AssistantCloudSpanProcessorOptions = {},\n): SpanProcessor {\n const processor = new BatchSpanProcessor(exporter);\n const filter = options.filter ?? isAssistantCloudSpan;\n\n return {\n onStart: (span, parentContext) => processor.onStart(span, parentContext),\n onEnd: (span) => {\n if (filter(span)) processor.onEnd(span);\n },\n forceFlush: () => processor.forceFlush(),\n shutdown: () => processor.shutdown(),\n };\n}\n\nexport function assistantCloudTraceMetadata(): { traceId?: string } {\n const spanContext = trace.getActiveSpan()?.spanContext();\n return spanContext && trace.isSpanContextValid(spanContext)\n ? { traceId: spanContext.traceId }\n : {};\n}\n\nexport function withAssistantCloudTraceMetadata<\n Part extends { type: string } = { type: string },\n>(\n messageMetadata?: (options: {\n part: Part;\n }) => Record<string, unknown> | undefined,\n): (options: { part: Part }) => Record<string, unknown> | undefined {\n return (options) => {\n const metadata = messageMetadata?.(options);\n if (options.part.type !== \"start\") return metadata;\n const trace = assistantCloudTraceMetadata();\n return trace.traceId === undefined ? metadata : { ...metadata, ...trace };\n };\n}\n"],"mappings":";;;;AASA,MAAM,2BAA2B;AACjC,MAAM,mBAAmB;CAAC;CAAW;CAAO;AAAM;AAYlD,SAAgB,iCAAiC,EAC/C,QACA,UAAU,0BACV,WAIA;CACA,IAAI,CAAC,QAAQ,MAAM,IAAI,MAAM,wCAAwC;CAErE,OAAO;EACL,KAAK,GAAG,oBAAoB,OAAO,EAAE;EACrC,SAAS;GACP,eAAe,UAAU;GACzB,GAAG;EACL;CACF;AACF;AAEA,SAAS,oBAAoB,KAAqB;CAChD,IAAI,MAAM,IAAI;CACd,OAAO,MAAM,KAAK,IAAI,MAAM,OAAO,KAAK;CACxC,OAAO,IAAI,MAAM,GAAG,GAAG;AACzB;AAEA,SAAgB,kCACd,SACc;CACd,OAAO,IAAI,kBAAkB,iCAAiC,OAAO,CAAC;AACxE;AAEA,SAAgB,qBAAqB,MAA6B;CAChE,OACE,OAAO,KAAK,WAAW,6BAA6B,YACpD,iBAAiB,MAAM,WAAW,KAAK,KAAK,WAAW,MAAM,CAAC;AAElE;AAEA,SAAgB,kCACd,UACA,UAA8C,CAAC,GAChC;CACf,MAAM,YAAY,IAAI,mBAAmB,QAAQ;CACjD,MAAM,SAAS,QAAQ,UAAU;CAEjC,OAAO;EACL,UAAU,MAAM,kBAAkB,UAAU,QAAQ,MAAM,aAAa;EACvE,QAAQ,SAAS;GACf,IAAI,OAAO,IAAI,GAAG,UAAU,MAAM,IAAI;EACxC;EACA,kBAAkB,UAAU,WAAW;EACvC,gBAAgB,UAAU,SAAS;CACrC;AACF;AAEA,SAAgB,8BAAoD;CAClE,MAAM,cAAc,MAAM,cAAc,CAAC,EAAE,YAAY;CACvD,OAAO,eAAe,MAAM,mBAAmB,WAAW,IACtD,EAAE,SAAS,YAAY,QAAQ,IAC/B,CAAC;AACP;AAEA,SAAgB,gCAGd,iBAGkE;CAClE,QAAQ,YAAY;EAClB,MAAM,WAAW,kBAAkB,OAAO;EAC1C,IAAI,QAAQ,KAAK,SAAS,SAAS,OAAO;EAC1C,MAAM,QAAQ,4BAA4B;EAC1C,OAAO,MAAM,YAAY,KAAA,IAAY,WAAW;GAAE,GAAG;GAAU,GAAG;EAAM;CAC1E;AACF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "assistant-cloud",
3
- "version": "0.1.43",
3
+ "version": "0.2.0",
4
4
  "description": "Cloud integration for assistant-ui",
5
5
  "keywords": [
6
6
  "assistant",
@@ -16,6 +16,10 @@
16
16
  ".": {
17
17
  "types": "./dist/index.d.ts",
18
18
  "default": "./dist/index.js"
19
+ },
20
+ "./telemetry": {
21
+ "types": "./dist/telemetry/index.d.ts",
22
+ "default": "./dist/telemetry/index.js"
19
23
  }
20
24
  },
21
25
  "main": "./dist/index.js",
@@ -27,11 +31,31 @@
27
31
  ],
28
32
  "sideEffects": false,
29
33
  "dependencies": {
30
- "assistant-stream": "^0.3.41"
34
+ "assistant-stream": "^0.3.42"
35
+ },
36
+ "peerDependencies": {
37
+ "@opentelemetry/api": "^1.9.0",
38
+ "@opentelemetry/exporter-trace-otlp-http": ">=0.200.0",
39
+ "@opentelemetry/sdk-trace-base": "^2.1.0"
40
+ },
41
+ "peerDependenciesMeta": {
42
+ "@opentelemetry/api": {
43
+ "optional": true
44
+ },
45
+ "@opentelemetry/exporter-trace-otlp-http": {
46
+ "optional": true
47
+ },
48
+ "@opentelemetry/sdk-trace-base": {
49
+ "optional": true
50
+ }
31
51
  },
32
52
  "devDependencies": {
33
- "@assistant-ui/x-buildutils": "0.0.25",
34
- "@types/node": "^26.4.0",
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",
58
+ "jsdom": "^30.0.1",
35
59
  "vitest": "^4.1.11"
36
60
  },
37
61
  "publishConfig": {
@@ -8,6 +8,8 @@ import { AssistantCloudProjects } from "./AssistantCloudProjects";
8
8
  import { AssistantCloudRuns } from "./AssistantCloudRuns";
9
9
  import { AssistantCloudThreads } from "./AssistantCloudThreads";
10
10
  import { AssistantCloudFiles } from "./AssistantCloudFiles";
11
+ import { AssistantCloudEvents } from "./AssistantCloudEvents";
12
+ import { AssistantCloudScores } from "./AssistantCloudScores";
11
13
 
12
14
  export class AssistantCloud {
13
15
  public readonly threads;
@@ -15,10 +17,23 @@ export class AssistantCloud {
15
17
  public readonly auth;
16
18
  public readonly runs;
17
19
  public readonly files;
20
+ public readonly events;
21
+ public readonly scores;
18
22
  public readonly telemetry: AssistantCloudTelemetryConfig;
19
23
 
20
24
  constructor(config: AssistantCloudConfig) {
21
25
  const api = new AssistantCloudAPI(config);
26
+ const t = config.telemetry;
27
+ this.telemetry =
28
+ t === false
29
+ ? { enabled: false }
30
+ : t === true || t === undefined
31
+ ? { enabled: true }
32
+ : {
33
+ ...t,
34
+ enabled: t.enabled !== false,
35
+ };
36
+
22
37
  this.threads = new AssistantCloudThreads(api);
23
38
  this.projects = new AssistantCloudProjects(api);
24
39
  this.auth = {
@@ -26,13 +41,10 @@ export class AssistantCloud {
26
41
  };
27
42
  this.runs = new AssistantCloudRuns(api);
28
43
  this.files = new AssistantCloudFiles(api);
29
-
30
- const t = config.telemetry;
31
- this.telemetry =
32
- t === false
33
- ? { enabled: false }
34
- : t === true || t === undefined
35
- ? { enabled: true }
36
- : { ...t, enabled: t.enabled !== false };
44
+ this.events = new AssistantCloudEvents(
45
+ api,
46
+ () => this.telemetry.enabled !== false && this.telemetry.events !== false,
47
+ );
48
+ this.scores = new AssistantCloudScores(api);
37
49
  }
38
50
  }
@@ -3,11 +3,25 @@ import {
3
3
  AssistantCloudJWTAuthStrategy,
4
4
  AssistantCloudAPIKeyAuthStrategy,
5
5
  AssistantCloudAnonymousAuthStrategy,
6
+ normalizeBaseUrl,
6
7
  } from "./AssistantCloudAuthStrategy";
7
8
  import type { AssistantCloudRunReport } from "./AssistantCloudRuns";
8
9
 
9
10
  export type AssistantCloudTelemetryConfig = {
11
+ /**
12
+ * Enables Assistant Cloud telemetry. Defaults to `true`. Set to `false` to
13
+ * disable both run reports and engagement events.
14
+ */
10
15
  enabled?: boolean;
16
+ /**
17
+ * Enables Assistant Cloud engagement events. Defaults to `true` when
18
+ * telemetry is enabled. Set to `false` to keep run reports while disabling
19
+ * engagement events.
20
+ */
21
+ events?: boolean;
22
+ release?: string;
23
+ environment?: string;
24
+ tags?: string[];
11
25
  /**
12
26
  * Called before each telemetry report is sent.
13
27
  * Return a modified report to enrich it (e.g. add `model_id`),
@@ -50,10 +64,19 @@ export type AssistantCloudConfig = (
50
64
 
51
65
  export class CloudAPIError extends Error {
52
66
  public readonly status: number;
53
-
54
- constructor(message: string, status: number) {
67
+ public readonly code?: string;
68
+ public readonly details?: Record<string, unknown>;
69
+
70
+ constructor(
71
+ message: string,
72
+ status: number,
73
+ code?: string,
74
+ details?: Record<string, unknown>,
75
+ ) {
55
76
  super(message);
56
77
  this.status = status;
78
+ if (code !== undefined) this.code = code;
79
+ if (details !== undefined) this.details = details;
57
80
  this.name = "CloudAPIError";
58
81
  }
59
82
  }
@@ -63,11 +86,7 @@ type MakeRequestOptions = {
63
86
  headers?: Record<string, string> | undefined;
64
87
  query?: Record<string, string | number | boolean> | undefined;
65
88
  body?: object | undefined;
66
- };
67
-
68
- const normalizeBaseUrl = (baseUrl: string) => {
69
- if (!baseUrl || !baseUrl.endsWith("/")) return baseUrl;
70
- return baseUrl.slice(0, -1);
89
+ keepalive?: boolean | undefined;
71
90
  };
72
91
 
73
92
  export class AssistantCloudAPI {
@@ -133,6 +152,7 @@ export class AssistantCloudAPI {
133
152
  method: options.method ?? "GET",
134
153
  headers,
135
154
  body: options.body ? JSON.stringify(options.body) : null,
155
+ ...(options.keepalive ? { keepalive: true } : {}),
136
156
  });
137
157
 
138
158
  this._auth.readAuthHeaders(response.headers);
@@ -140,15 +160,27 @@ export class AssistantCloudAPI {
140
160
  if (!response.ok) {
141
161
  const text = await response.text();
142
162
  let message: string | undefined;
163
+ let code: string | undefined;
164
+ let details: Record<string, unknown> | undefined;
143
165
  try {
144
- const body = JSON.parse(text);
145
- if (typeof body?.message === "string" && body.message.length > 0) {
146
- message = body.message;
166
+ const body = JSON.parse(text) as unknown;
167
+ if (typeof body === "object" && body !== null && !Array.isArray(body)) {
168
+ const record = body as Record<string, unknown>;
169
+ if (typeof record.message === "string" && record.message.length > 0) {
170
+ message = record.message;
171
+ }
172
+ if (typeof record.error === "string") {
173
+ code = record.error;
174
+ details = { ...record };
175
+ delete details.error;
176
+ }
147
177
  }
148
178
  } catch {}
149
179
  throw new CloudAPIError(
150
180
  message ?? `Request failed with status ${response.status}, ${text}`,
151
181
  response.status,
182
+ code,
183
+ details,
152
184
  );
153
185
  }
154
186