@copilotkit/runtime 1.9.2-next.3 → 1.9.2-next.4

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 (41) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/dist/{chunk-XDBXF3Q6.mjs → chunk-5FNEYAWH.mjs} +2 -2
  3. package/dist/{chunk-N24X5I3C.mjs → chunk-6W7TKZNY.mjs} +2 -2
  4. package/dist/{chunk-MIPAKFI5.mjs → chunk-CJ44W4R2.mjs} +2 -2
  5. package/dist/{chunk-WFYPJXWX.mjs → chunk-NK2XARWT.mjs} +2 -2
  6. package/dist/{chunk-GSYE3DGY.mjs → chunk-ZUF3A5TW.mjs} +197 -16
  7. package/dist/chunk-ZUF3A5TW.mjs.map +1 -0
  8. package/dist/index.d.ts +1 -1
  9. package/dist/index.js +196 -15
  10. package/dist/index.js.map +1 -1
  11. package/dist/index.mjs +5 -5
  12. package/dist/lib/index.d.ts +1 -1
  13. package/dist/lib/index.js +196 -15
  14. package/dist/lib/index.js.map +1 -1
  15. package/dist/lib/index.mjs +5 -5
  16. package/dist/lib/integrations/index.d.ts +2 -2
  17. package/dist/lib/integrations/index.js +19 -10
  18. package/dist/lib/integrations/index.js.map +1 -1
  19. package/dist/lib/integrations/index.mjs +4 -4
  20. package/dist/lib/integrations/nest/index.d.ts +1 -1
  21. package/dist/lib/integrations/nest/index.js +19 -10
  22. package/dist/lib/integrations/nest/index.js.map +1 -1
  23. package/dist/lib/integrations/nest/index.mjs +2 -2
  24. package/dist/lib/integrations/node-express/index.d.ts +1 -1
  25. package/dist/lib/integrations/node-express/index.js +19 -10
  26. package/dist/lib/integrations/node-express/index.js.map +1 -1
  27. package/dist/lib/integrations/node-express/index.mjs +2 -2
  28. package/dist/lib/integrations/node-http/index.d.ts +1 -1
  29. package/dist/lib/integrations/node-http/index.js +19 -10
  30. package/dist/lib/integrations/node-http/index.js.map +1 -1
  31. package/dist/lib/integrations/node-http/index.mjs +1 -1
  32. package/dist/{shared-e272b15a.d.ts → shared-ba062831.d.ts} +35 -1
  33. package/package.json +2 -2
  34. package/src/graphql/resolvers/copilot.resolver.ts +24 -5
  35. package/src/lib/runtime/__tests__/copilot-runtime-trace.test.ts +169 -0
  36. package/src/lib/runtime/copilot-runtime.ts +254 -6
  37. package/dist/chunk-GSYE3DGY.mjs.map +0 -1
  38. /package/dist/{chunk-XDBXF3Q6.mjs.map → chunk-5FNEYAWH.mjs.map} +0 -0
  39. /package/dist/{chunk-N24X5I3C.mjs.map → chunk-6W7TKZNY.mjs.map} +0 -0
  40. /package/dist/{chunk-MIPAKFI5.mjs.map → chunk-CJ44W4R2.mjs.map} +0 -0
  41. /package/dist/{chunk-WFYPJXWX.mjs.map → chunk-NK2XARWT.mjs.map} +0 -0
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  copilotRuntimeNodeHttpEndpoint
3
- } from "../../../chunk-GSYE3DGY.mjs";
3
+ } from "../../../chunk-ZUF3A5TW.mjs";
4
4
  import "../../../chunk-IIXJVVTV.mjs";
5
5
  import "../../../chunk-5BIEM2UU.mjs";
6
6
  import "../../../chunk-SHBDMA63.mjs";
@@ -1,7 +1,7 @@
1
1
  import * as graphql from 'graphql';
2
2
  import * as pino from 'pino';
3
3
  import { YogaInitialContext, createYoga } from 'graphql-yoga';
4
- import { Parameter, Action } from '@copilotkit/shared';
4
+ import { Parameter, Action, CopilotTraceHandler } from '@copilotkit/shared';
5
5
  import { b as CopilotServiceAdapter, A as ActionInput, d as AgentSessionInput, e as AgentStateInput, F as ForwardedParametersInput, E as ExtensionsInput, R as RemoteChainParameters, f as RuntimeEventSource, g as ExtensionsResponse } from './langserve-4a5c9217.js';
6
6
  import { M as MessageInput, a as Message } from './index-d4614f9b.js';
7
7
  import { CopilotCloudOptions } from './lib/cloud/index.js';
@@ -338,6 +338,24 @@ interface CopilotRuntimeConstructorParams<T extends Parameter[] | [] = []> {
338
338
  * ```
339
339
  */
340
340
  createMCPClient?: CreateMCPClientFunction;
341
+ /**
342
+ * Optional trace handler for comprehensive debugging and observability.
343
+ *
344
+ * **Requires publicApiKey**: Tracing only works when requests include a valid publicApiKey.
345
+ * This is a premium CopilotKit Cloud feature.
346
+ *
347
+ * @param traceEvent - Structured trace event with rich debugging context
348
+ *
349
+ * @example
350
+ * ```typescript
351
+ * const runtime = new CopilotRuntime({
352
+ * onTrace: (traceEvent) => {
353
+ * debugDashboard.capture(traceEvent);
354
+ * }
355
+ * });
356
+ * ```
357
+ */
358
+ onTrace?: CopilotTraceHandler;
341
359
  }
342
360
  declare class CopilotRuntime<const T extends Parameter[] | [] = []> {
343
361
  actions: ActionsConfiguration<T>;
@@ -349,6 +367,8 @@ declare class CopilotRuntime<const T extends Parameter[] | [] = []> {
349
367
  private delegateAgentProcessingToServiceAdapter;
350
368
  private observability?;
351
369
  private availableAgents;
370
+ private onTrace?;
371
+ private hasWarnedAboutTracing;
352
372
  private readonly mcpServersConfig?;
353
373
  private mcpActionCache;
354
374
  private readonly createMCPClientImpl?;
@@ -361,6 +381,20 @@ declare class CopilotRuntime<const T extends Parameter[] | [] = []> {
361
381
  private getServerSideActions;
362
382
  private detectProvider;
363
383
  private convertStreamingErrorToStructured;
384
+ private trace;
385
+ /**
386
+ * Public method to trace GraphQL validation errors
387
+ * This allows the GraphQL resolver to send validation errors through the trace system
388
+ */
389
+ traceGraphQLError(error: {
390
+ message: string;
391
+ code: string;
392
+ type: string;
393
+ }, context: {
394
+ operation: string;
395
+ cloudConfigPresent: boolean;
396
+ guardrailsEnabled: boolean;
397
+ }): Promise<void>;
364
398
  }
365
399
  declare function flattenToolCallsNoDuplicates(toolsByPriority: ActionInput[]): ActionInput[];
366
400
  declare function copilotKitEndpoint(config: Omit<CopilotKitEndpoint, "type">): CopilotKitEndpoint;
package/package.json CHANGED
@@ -9,7 +9,7 @@
9
9
  "publishConfig": {
10
10
  "access": "public"
11
11
  },
12
- "version": "1.9.2-next.3",
12
+ "version": "1.9.2-next.4",
13
13
  "sideEffects": false,
14
14
  "main": "./dist/index.js",
15
15
  "module": "./dist/index.mjs",
@@ -66,7 +66,7 @@
66
66
  "rxjs": "7.8.1",
67
67
  "type-graphql": "2.0.0-rc.1",
68
68
  "zod": "^3.23.3",
69
- "@copilotkit/shared": "1.9.2-next.3"
69
+ "@copilotkit/shared": "1.9.2-next.4"
70
70
  },
71
71
  "peerDependencies": {
72
72
  "@ag-ui/client": ">=0.0.28",
@@ -174,15 +174,34 @@ export class CopilotResolver {
174
174
  let copilotCloudPublicApiKey: string | null = null;
175
175
  let copilotCloudBaseUrl: string;
176
176
 
177
+ // Extract publicApiKey from headers for both cloud and non-cloud requests
178
+ // This enables onTrace functionality regardless of cloud configuration
179
+ const publicApiKeyFromHeaders = ctx.request.headers.get("x-copilotcloud-public-api-key");
180
+ if (publicApiKeyFromHeaders) {
181
+ copilotCloudPublicApiKey = publicApiKeyFromHeaders;
182
+ }
183
+
177
184
  if (data.cloud) {
178
185
  logger = logger.child({ cloud: true });
179
186
  logger.debug("Cloud configuration provided, checking for public API key in headers");
180
- const key = ctx.request.headers.get("x-copilotcloud-public-api-key");
181
- if (key) {
182
- logger.debug("Public API key found in headers");
183
- copilotCloudPublicApiKey = key;
184
- } else {
187
+
188
+ if (!copilotCloudPublicApiKey) {
185
189
  logger.error("Public API key not found in headers");
190
+
191
+ // Trace the validation error for debugging visibility
192
+ await copilotRuntime.traceGraphQLError(
193
+ {
194
+ message: "X-CopilotCloud-Public-API-Key header is required",
195
+ code: "MISSING_PUBLIC_API_KEY",
196
+ type: "GraphQLError",
197
+ },
198
+ {
199
+ operation: "generateCopilotResponse",
200
+ cloudConfigPresent: Boolean(data.cloud),
201
+ guardrailsEnabled: Boolean(data.cloud?.guardrails),
202
+ },
203
+ );
204
+
186
205
  throw new GraphQLError("X-CopilotCloud-Public-API-Key header is required");
187
206
  }
188
207
 
@@ -0,0 +1,169 @@
1
+ import { CopilotTraceEvent, CopilotRequestContext, CopilotTraceHandler } from "@copilotkit/shared";
2
+
3
+ describe("CopilotRuntime onTrace types", () => {
4
+ it("should have correct CopilotTraceEvent type structure", () => {
5
+ const traceEvent: CopilotTraceEvent = {
6
+ type: "error",
7
+ timestamp: Date.now(),
8
+ context: {
9
+ threadId: "test-123",
10
+ source: "runtime",
11
+ request: {
12
+ operation: "test-operation",
13
+ startTime: Date.now(),
14
+ },
15
+ technical: {},
16
+ metadata: {},
17
+ },
18
+ error: new Error("Test error"),
19
+ };
20
+
21
+ expect(traceEvent.type).toBe("error");
22
+ expect(traceEvent.timestamp).toBeGreaterThan(0);
23
+ expect(traceEvent.context.threadId).toBe("test-123");
24
+ expect(traceEvent.error).toBeInstanceOf(Error);
25
+ });
26
+
27
+ it("should have correct CopilotRequestContext type structure", () => {
28
+ const context: CopilotRequestContext = {
29
+ threadId: "test-thread-456",
30
+ runId: "test-run-789",
31
+ source: "runtime",
32
+ request: {
33
+ operation: "processRuntimeRequest",
34
+ method: "POST",
35
+ url: "http://localhost:3000/api/copilotkit",
36
+ startTime: Date.now(),
37
+ },
38
+ response: {
39
+ status: 200,
40
+ endTime: Date.now(),
41
+ latency: 1200,
42
+ },
43
+ agent: {
44
+ name: "test-agent",
45
+ nodeName: "test-node",
46
+ state: { step: 1 },
47
+ },
48
+ messages: {
49
+ input: [],
50
+ output: [],
51
+ messageCount: 2,
52
+ },
53
+ technical: {
54
+ userAgent: "Mozilla/5.0...",
55
+ host: "localhost:3000",
56
+ environment: "test",
57
+ version: "1.0.0",
58
+ stackTrace: "Error: Test\n at test.js:1:1",
59
+ },
60
+ performance: {
61
+ requestDuration: 1200,
62
+ streamingDuration: 800,
63
+ actionExecutionTime: 400,
64
+ memoryUsage: 45.2,
65
+ },
66
+ metadata: {
67
+ testFlag: true,
68
+ version: "1.0.0",
69
+ },
70
+ };
71
+
72
+ expect(context.threadId).toBe("test-thread-456");
73
+ expect(context.agent?.name).toBe("test-agent");
74
+ expect(context.messages?.messageCount).toBe(2);
75
+ expect(context.technical?.stackTrace).toContain("Error: Test");
76
+ expect(context.metadata?.testFlag).toBe(true);
77
+ });
78
+
79
+ it("should support all trace event types", () => {
80
+ const eventTypes: CopilotTraceEvent["type"][] = [
81
+ "error",
82
+ "request",
83
+ "response",
84
+ "agent_state",
85
+ "action",
86
+ "message",
87
+ "performance",
88
+ ];
89
+
90
+ eventTypes.forEach((type) => {
91
+ const event: CopilotTraceEvent = {
92
+ type,
93
+ timestamp: Date.now(),
94
+ context: {
95
+ threadId: `test-${type}`,
96
+ source: "runtime",
97
+ request: {
98
+ operation: "test",
99
+ startTime: Date.now(),
100
+ },
101
+ technical: {},
102
+ metadata: {},
103
+ },
104
+ };
105
+
106
+ expect(event.type).toBe(type);
107
+ });
108
+ });
109
+
110
+ describe("publicApiKey gating logic", () => {
111
+ type ShouldTrace = (onTrace?: CopilotTraceHandler, publicApiKey?: string) => boolean;
112
+
113
+ const shouldTrace: ShouldTrace = (onTrace, publicApiKey) => {
114
+ return Boolean(onTrace && publicApiKey);
115
+ };
116
+
117
+ it("should return true when both onTrace and publicApiKey are provided", () => {
118
+ const onTrace = jest.fn();
119
+ const result = shouldTrace(onTrace, "valid-api-key");
120
+ expect(result).toBe(true);
121
+ });
122
+
123
+ it("should return false when onTrace is missing", () => {
124
+ const result = shouldTrace(undefined, "valid-api-key");
125
+ expect(result).toBe(false);
126
+ });
127
+
128
+ it("should return false when publicApiKey is missing", () => {
129
+ const onTrace = jest.fn();
130
+ const result = shouldTrace(onTrace, undefined);
131
+ expect(result).toBe(false);
132
+ });
133
+
134
+ it("should return false when publicApiKey is empty string", () => {
135
+ const onTrace = jest.fn();
136
+ const result = shouldTrace(onTrace, "");
137
+ expect(result).toBe(false);
138
+ });
139
+
140
+ it("should return false when both are missing", () => {
141
+ const result = shouldTrace(undefined, undefined);
142
+ expect(result).toBe(false);
143
+ });
144
+
145
+ it("should extract publicApiKey from headers for both cloud and non-cloud requests", () => {
146
+ // Test the logic we just fixed in the GraphQL resolver
147
+ const mockHeaders = new Map([["x-copilotcloud-public-api-key", "test-key-123"]]);
148
+
149
+ // Simulate header extraction logic
150
+ const extractPublicApiKey = (headers: Map<string, string>, hasCloudConfig: boolean) => {
151
+ const publicApiKeyFromHeaders = headers.get("x-copilotcloud-public-api-key");
152
+ return publicApiKeyFromHeaders || null;
153
+ };
154
+
155
+ // Should work for cloud requests
156
+ const cloudKey = extractPublicApiKey(mockHeaders, true);
157
+ expect(cloudKey).toBe("test-key-123");
158
+
159
+ // Should also work for non-cloud requests (this was the bug)
160
+ const nonCloudKey = extractPublicApiKey(mockHeaders, false);
161
+ expect(nonCloudKey).toBe("test-key-123");
162
+
163
+ // Both should enable tracing when onTrace is present
164
+ const onTrace = jest.fn();
165
+ expect(shouldTrace(onTrace, cloudKey)).toBe(true);
166
+ expect(shouldTrace(onTrace, nonCloudKey)).toBe(true);
167
+ });
168
+ });
169
+ });
@@ -25,6 +25,9 @@ import {
25
25
  CopilotKitMisuseError,
26
26
  CopilotKitErrorCode,
27
27
  CopilotKitLowLevelError,
28
+ CopilotTraceHandler,
29
+ CopilotTraceEvent,
30
+ CopilotRequestContext,
28
31
  } from "@copilotkit/shared";
29
32
  import {
30
33
  CopilotServiceAdapter,
@@ -274,6 +277,25 @@ export interface CopilotRuntimeConstructorParams<T extends Parameter[] | [] = []
274
277
  * ```
275
278
  */
276
279
  createMCPClient?: CreateMCPClientFunction;
280
+
281
+ /**
282
+ * Optional trace handler for comprehensive debugging and observability.
283
+ *
284
+ * **Requires publicApiKey**: Tracing only works when requests include a valid publicApiKey.
285
+ * This is a premium CopilotKit Cloud feature.
286
+ *
287
+ * @param traceEvent - Structured trace event with rich debugging context
288
+ *
289
+ * @example
290
+ * ```typescript
291
+ * const runtime = new CopilotRuntime({
292
+ * onTrace: (traceEvent) => {
293
+ * debugDashboard.capture(traceEvent);
294
+ * }
295
+ * });
296
+ * ```
297
+ */
298
+ onTrace?: CopilotTraceHandler;
277
299
  }
278
300
 
279
301
  export class CopilotRuntime<const T extends Parameter[] | [] = []> {
@@ -286,6 +308,8 @@ export class CopilotRuntime<const T extends Parameter[] | [] = []> {
286
308
  private delegateAgentProcessingToServiceAdapter: boolean;
287
309
  private observability?: CopilotObservabilityConfig;
288
310
  private availableAgents: Pick<AgentWithEndpoint, "name" | "id">[];
311
+ private onTrace?: CopilotTraceHandler;
312
+ private hasWarnedAboutTracing = false;
289
313
 
290
314
  // +++ MCP Properties +++
291
315
  private readonly mcpServersConfig?: MCPEndpointConfig[];
@@ -320,6 +344,7 @@ export class CopilotRuntime<const T extends Parameter[] | [] = []> {
320
344
  params?.delegateAgentProcessingToServiceAdapter || false;
321
345
  this.observability = params?.observability_c;
322
346
  this.agents = params?.agents ?? {};
347
+ this.onTrace = params?.onTrace;
323
348
  // +++ MCP Initialization +++
324
349
  this.mcpServersConfig = params?.mcpServers;
325
350
  this.createMCPClientImpl = params?.createMCPClient;
@@ -452,6 +477,32 @@ export class CopilotRuntime<const T extends Parameter[] | [] = []> {
452
477
  // For storing streamed chunks if progressive logging is enabled
453
478
  const streamedChunks: any[] = [];
454
479
 
480
+ // Trace request start
481
+ await this.trace(
482
+ "request",
483
+ {
484
+ threadId,
485
+ runId,
486
+ source: "runtime",
487
+ request: {
488
+ operation: "processRuntimeRequest",
489
+ method: "POST",
490
+ url: url,
491
+ startTime: requestStartTime,
492
+ },
493
+ agent: agentSession ? { name: agentSession.agentName } : undefined,
494
+ messages: {
495
+ input: rawMessages,
496
+ messageCount: rawMessages.length,
497
+ },
498
+ technical: {
499
+ environment: process.env.NODE_ENV,
500
+ },
501
+ },
502
+ undefined,
503
+ publicApiKey,
504
+ );
505
+
455
506
  try {
456
507
  if (
457
508
  Object.keys(this.agents).length &&
@@ -675,13 +726,43 @@ please use an LLM adapter instead.`,
675
726
  }
676
727
  }
677
728
 
729
+ let structuredError: CopilotKitError;
730
+
678
731
  if (error instanceof CopilotKitError) {
679
- throw error;
732
+ structuredError = error;
733
+ } else {
734
+ // Convert non-CopilotKitErrors to structured errors
735
+ console.error("Error getting response:", error);
736
+ structuredError = this.convertStreamingErrorToStructured(error);
680
737
  }
681
738
 
682
- // Convert non-CopilotKitErrors to structured errors
683
- console.error("Error getting response:", error);
684
- const structuredError = this.convertStreamingErrorToStructured(error);
739
+ // Trace the error
740
+ await this.trace(
741
+ "error",
742
+ {
743
+ threadId,
744
+ runId,
745
+ source: "runtime",
746
+ request: {
747
+ operation: "processRuntimeRequest",
748
+ method: "POST",
749
+ url: url,
750
+ startTime: requestStartTime,
751
+ },
752
+ response: {
753
+ endTime: Date.now(),
754
+ latency: Date.now() - requestStartTime,
755
+ },
756
+ agent: agentSession ? { name: agentSession.agentName } : undefined,
757
+ technical: {
758
+ environment: process.env.NODE_ENV,
759
+ stackTrace: error instanceof Error ? error.stack : undefined,
760
+ },
761
+ },
762
+ structuredError,
763
+ publicApiKey,
764
+ );
765
+
685
766
  throw structuredError;
686
767
  }
687
768
  }
@@ -884,6 +965,33 @@ please use an LLM adapter instead.`,
884
965
  // for backwards compatibility, deal with the case when no threadId is provided
885
966
  const threadId = threadIdFromRequest ?? agentSession.threadId;
886
967
 
968
+ // Trace agent request start
969
+ await this.trace(
970
+ "agent_state",
971
+ {
972
+ threadId,
973
+ source: "agent",
974
+ request: {
975
+ operation: "processAgentRequest",
976
+ method: "POST",
977
+ startTime: requestStartTime,
978
+ },
979
+ agent: {
980
+ name: agentName,
981
+ nodeName: nodeName,
982
+ },
983
+ messages: {
984
+ input: rawMessages,
985
+ messageCount: rawMessages.length,
986
+ },
987
+ technical: {
988
+ environment: process.env.NODE_ENV,
989
+ },
990
+ },
991
+ undefined,
992
+ publicApiKey,
993
+ );
994
+
887
995
  const serverSideActions = await this.getServerSideActions(request);
888
996
 
889
997
  const messages = convertGqlInputToMessages(rawMessages);
@@ -1011,7 +1119,7 @@ please use an LLM adapter instead.`,
1011
1119
  eventSource.stream(async (eventStream$) => {
1012
1120
  from(stream).subscribe({
1013
1121
  next: (event) => eventStream$.next(event),
1014
- error: (err) => {
1122
+ error: async (err) => {
1015
1123
  console.error("Error in stream", err);
1016
1124
 
1017
1125
  // Log error with observability if enabled
@@ -1037,6 +1145,35 @@ please use an LLM adapter instead.`,
1037
1145
 
1038
1146
  // Convert network termination errors to structured errors
1039
1147
  const structuredError = this.convertStreamingErrorToStructured(err);
1148
+
1149
+ // Trace streaming errors
1150
+ await this.trace(
1151
+ "error",
1152
+ {
1153
+ threadId,
1154
+ source: "agent",
1155
+ request: {
1156
+ operation: "processAgentRequest",
1157
+ method: "POST",
1158
+ startTime: requestStartTime,
1159
+ },
1160
+ response: {
1161
+ endTime: Date.now(),
1162
+ latency: Date.now() - requestStartTime,
1163
+ },
1164
+ agent: {
1165
+ name: agentName,
1166
+ nodeName: nodeName,
1167
+ },
1168
+ technical: {
1169
+ environment: process.env.NODE_ENV,
1170
+ stackTrace: err instanceof Error ? err.stack : undefined,
1171
+ },
1172
+ },
1173
+ structuredError,
1174
+ publicApiKey,
1175
+ );
1176
+
1040
1177
  eventStream$.error(structuredError);
1041
1178
  eventStream$.complete();
1042
1179
  },
@@ -1114,8 +1251,44 @@ please use an LLM adapter instead.`,
1114
1251
  }
1115
1252
  }
1116
1253
 
1254
+ // Ensure error is structured
1255
+ let structuredError: CopilotKitError;
1256
+ if (error instanceof CopilotKitError) {
1257
+ structuredError = error;
1258
+ } else {
1259
+ structuredError = this.convertStreamingErrorToStructured(error);
1260
+ }
1261
+
1262
+ // Trace the agent error
1263
+ await this.trace(
1264
+ "error",
1265
+ {
1266
+ threadId,
1267
+ source: "agent",
1268
+ request: {
1269
+ operation: "processAgentRequest",
1270
+ method: "POST",
1271
+ startTime: requestStartTime,
1272
+ },
1273
+ response: {
1274
+ endTime: Date.now(),
1275
+ latency: Date.now() - requestStartTime,
1276
+ },
1277
+ agent: {
1278
+ name: agentName,
1279
+ nodeName: nodeName,
1280
+ },
1281
+ technical: {
1282
+ environment: process.env.NODE_ENV,
1283
+ stackTrace: error instanceof Error ? error.stack : undefined,
1284
+ },
1285
+ },
1286
+ structuredError,
1287
+ publicApiKey,
1288
+ );
1289
+
1117
1290
  console.error("Error getting response:", error);
1118
- throw error;
1291
+ throw structuredError;
1119
1292
  }
1120
1293
  }
1121
1294
 
@@ -1278,6 +1451,81 @@ please use an LLM adapter instead.`,
1278
1451
  code: CopilotKitErrorCode.UNKNOWN,
1279
1452
  });
1280
1453
  }
1454
+
1455
+ private async trace(
1456
+ type: CopilotTraceEvent["type"],
1457
+ context: CopilotRequestContext,
1458
+ error?: any,
1459
+ publicApiKey?: string,
1460
+ ): Promise<void> {
1461
+ if (!this.onTrace) return;
1462
+
1463
+ if (!publicApiKey) {
1464
+ if (!this.hasWarnedAboutTracing) {
1465
+ console.warn(
1466
+ "CopilotKit: onTrace handler provided but requires publicApiKey for tracing to work. " +
1467
+ "This is a CopilotKit Cloud feature. See: https://docs.copilotkit.ai/cloud",
1468
+ );
1469
+ this.hasWarnedAboutTracing = true;
1470
+ }
1471
+ return;
1472
+ }
1473
+
1474
+ try {
1475
+ const traceEvent: CopilotTraceEvent = {
1476
+ type,
1477
+ timestamp: Date.now(),
1478
+ context,
1479
+ ...(error && { error }),
1480
+ };
1481
+
1482
+ await this.onTrace(traceEvent);
1483
+ } catch (traceError) {
1484
+ // Don't let trace errors break the main flow
1485
+ console.error("Error in onTrace handler:", traceError);
1486
+ }
1487
+ }
1488
+
1489
+ /**
1490
+ * Public method to trace GraphQL validation errors
1491
+ * This allows the GraphQL resolver to send validation errors through the trace system
1492
+ */
1493
+ public async traceGraphQLError(
1494
+ error: { message: string; code: string; type: string },
1495
+ context: {
1496
+ operation: string;
1497
+ cloudConfigPresent: boolean;
1498
+ guardrailsEnabled: boolean;
1499
+ },
1500
+ ): Promise<void> {
1501
+ if (!this.onTrace) return;
1502
+
1503
+ try {
1504
+ await this.onTrace({
1505
+ type: "error",
1506
+ timestamp: Date.now(),
1507
+ context: {
1508
+ source: "runtime",
1509
+ request: {
1510
+ operation: context.operation,
1511
+ startTime: Date.now(),
1512
+ },
1513
+ technical: {
1514
+ environment: process.env.NODE_ENV,
1515
+ },
1516
+ metadata: {
1517
+ errorType: "GraphQLValidationError",
1518
+ cloudConfigPresent: context.cloudConfigPresent,
1519
+ guardrailsEnabled: context.guardrailsEnabled,
1520
+ },
1521
+ },
1522
+ error,
1523
+ });
1524
+ } catch (traceError) {
1525
+ // Don't let trace errors break the main flow
1526
+ console.error("Error in onTrace handler:", traceError);
1527
+ }
1528
+ }
1281
1529
  }
1282
1530
 
1283
1531
  export function flattenToolCallsNoDuplicates(toolsByPriority: ActionInput[]): ActionInput[] {