@copilotkit/runtime 1.9.2-next.0 → 1.9.2-next.10

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 (84) hide show
  1. package/CHANGELOG.md +74 -0
  2. package/dist/{chunk-O6KXX5R5.mjs → chunk-5OK4GLKL.mjs} +19 -2
  3. package/dist/chunk-5OK4GLKL.mjs.map +1 -0
  4. package/dist/{chunk-44FYLJJJ.mjs → chunk-6RUTA76W.mjs} +2 -2
  5. package/dist/chunk-AMUJQ6IR.mjs +50 -0
  6. package/dist/chunk-AMUJQ6IR.mjs.map +1 -0
  7. package/dist/{chunk-IIXJVVTV.mjs → chunk-GS7DO47Q.mjs} +155 -78
  8. package/dist/chunk-GS7DO47Q.mjs.map +1 -0
  9. package/dist/{chunk-YMIOUUPV.mjs → chunk-PMIAGZGS.mjs} +2374 -1855
  10. package/dist/chunk-PMIAGZGS.mjs.map +1 -0
  11. package/dist/{chunk-ESXPDYNT.mjs → chunk-TOBFVWZU.mjs} +2 -2
  12. package/dist/{chunk-YXL4PSJM.mjs → chunk-VBXBFZEL.mjs} +2 -2
  13. package/dist/{chunk-5BIEM2UU.mjs → chunk-XWBDEXDA.mjs} +4 -3
  14. package/dist/{chunk-5BIEM2UU.mjs.map → chunk-XWBDEXDA.mjs.map} +1 -1
  15. package/dist/{groq-adapter-25a2bd35.d.ts → groq-adapter-172a2ca4.d.ts} +1 -1
  16. package/dist/index.d.ts +4 -3
  17. package/dist/index.js +3765 -3108
  18. package/dist/index.js.map +1 -1
  19. package/dist/index.mjs +12 -8
  20. package/dist/index.mjs.map +1 -1
  21. package/dist/lib/index.d.ts +7 -133
  22. package/dist/lib/index.js +3357 -2743
  23. package/dist/lib/index.js.map +1 -1
  24. package/dist/lib/index.mjs +9 -8
  25. package/dist/lib/integrations/index.d.ts +3 -3
  26. package/dist/lib/integrations/index.js +162 -98
  27. package/dist/lib/integrations/index.js.map +1 -1
  28. package/dist/lib/integrations/index.mjs +7 -6
  29. package/dist/lib/integrations/nest/index.d.ts +2 -2
  30. package/dist/lib/integrations/nest/index.js +162 -98
  31. package/dist/lib/integrations/nest/index.js.map +1 -1
  32. package/dist/lib/integrations/nest/index.mjs +5 -4
  33. package/dist/lib/integrations/node-express/index.d.ts +2 -2
  34. package/dist/lib/integrations/node-express/index.js +162 -98
  35. package/dist/lib/integrations/node-express/index.js.map +1 -1
  36. package/dist/lib/integrations/node-express/index.mjs +5 -4
  37. package/dist/lib/integrations/node-http/index.d.ts +2 -2
  38. package/dist/lib/integrations/node-http/index.js +162 -98
  39. package/dist/lib/integrations/node-http/index.js.map +1 -1
  40. package/dist/lib/integrations/node-http/index.mjs +4 -3
  41. package/dist/service-adapters/index.d.ts +6 -4
  42. package/dist/service-adapters/index.js +225 -107
  43. package/dist/service-adapters/index.js.map +1 -1
  44. package/dist/service-adapters/index.mjs +6 -2
  45. package/dist/service-adapters/shared/index.d.ts +9 -0
  46. package/dist/service-adapters/shared/index.js +72 -0
  47. package/dist/service-adapters/shared/index.js.map +1 -0
  48. package/dist/service-adapters/shared/index.mjs +8 -0
  49. package/dist/service-adapters/shared/index.mjs.map +1 -0
  50. package/dist/{shared-e272b15a.d.ts → shared-bd953ebf.d.ts} +45 -5
  51. package/dist/utils/index.d.ts +17 -1
  52. package/dist/utils/index.js +3 -2
  53. package/dist/utils/index.js.map +1 -1
  54. package/dist/utils/index.mjs +1 -1
  55. package/package.json +4 -4
  56. package/src/agents/langgraph/event-source.ts +36 -38
  57. package/src/agents/langgraph/events.ts +19 -1
  58. package/src/graphql/resolvers/copilot.resolver.ts +108 -45
  59. package/src/graphql/resolvers/state.resolver.ts +3 -3
  60. package/src/lib/error-messages.ts +200 -0
  61. package/src/lib/integrations/shared.ts +43 -0
  62. package/src/lib/runtime/__tests__/copilot-runtime-trace.test.ts +169 -0
  63. package/src/lib/runtime/copilot-runtime.ts +420 -83
  64. package/src/lib/runtime/langgraph/langgraph-agent.ts +12 -0
  65. package/src/lib/runtime/remote-action-constructors.ts +28 -3
  66. package/src/lib/runtime/remote-lg-action.ts +130 -40
  67. package/src/lib/streaming.ts +125 -36
  68. package/src/service-adapters/anthropic/anthropic-adapter.ts +67 -8
  69. package/src/service-adapters/anthropic/utils.ts +3 -8
  70. package/src/service-adapters/events.ts +37 -81
  71. package/src/service-adapters/groq/groq-adapter.ts +66 -56
  72. package/src/service-adapters/index.ts +1 -0
  73. package/src/service-adapters/openai/openai-adapter.ts +18 -3
  74. package/src/service-adapters/shared/error-utils.ts +61 -0
  75. package/src/service-adapters/shared/index.ts +1 -0
  76. package/src/utils/failed-response-status-reasons.ts +23 -1
  77. package/tests/service-adapters/anthropic/anthropic-adapter.test.ts +172 -387
  78. package/dist/chunk-IIXJVVTV.mjs.map +0 -1
  79. package/dist/chunk-O6KXX5R5.mjs.map +0 -1
  80. package/dist/chunk-YMIOUUPV.mjs.map +0 -1
  81. package/dist/{chunk-44FYLJJJ.mjs.map → chunk-6RUTA76W.mjs.map} +0 -0
  82. package/dist/{chunk-ESXPDYNT.mjs.map → chunk-TOBFVWZU.mjs.map} +0 -0
  83. package/dist/{chunk-YXL4PSJM.mjs.map → chunk-VBXBFZEL.mjs.map} +0 -0
  84. package/dist/{langserve-4a5c9217.d.ts → langserve-fc5cac89.d.ts} +7 -7
@@ -25,6 +25,10 @@ import {
25
25
  CopilotKitMisuseError,
26
26
  CopilotKitErrorCode,
27
27
  CopilotKitLowLevelError,
28
+ CopilotTraceHandler,
29
+ CopilotTraceEvent,
30
+ CopilotRequestContext,
31
+ ensureStructuredError,
28
32
  } from "@copilotkit/shared";
29
33
  import {
30
34
  CopilotServiceAdapter,
@@ -62,7 +66,7 @@ import { ExtensionsInput } from "../../graphql/inputs/extensions.input";
62
66
  import { ExtensionsResponse } from "../../graphql/types/extensions-response.type";
63
67
  import { LoadAgentStateResponse } from "../../graphql/types/load-agent-state-response.type";
64
68
  import { Client as LangGraphClient } from "@langchain/langgraph-sdk";
65
- import { langchainMessagesToCopilotKit } from "./remote-lg-action";
69
+ import { langchainMessagesToCopilotKit, isUserConfigurationError } from "./remote-lg-action";
66
70
  import { MetaEventInput } from "../../graphql/inputs/meta-event.input";
67
71
  import {
68
72
  CopilotObservabilityConfig,
@@ -81,10 +85,13 @@ import {
81
85
  convertMCPToolsToActions,
82
86
  generateMcpToolInstructions,
83
87
  } from "./mcp-tools-utils";
88
+ import { LangGraphAgent } from "./langgraph/langgraph-agent";
84
89
  // Define the function type alias here or import if defined elsewhere
85
90
  type CreateMCPClientFunction = (config: MCPEndpointConfig) => Promise<MCPClient>;
86
91
  // --- MCP Imports ---
87
92
 
93
+ import { generateHelpfulErrorMessage } from "../streaming";
94
+
88
95
  export interface CopilotRuntimeRequest {
89
96
  serviceAdapter: CopilotServiceAdapter;
90
97
  messages: MessageInput[];
@@ -274,6 +281,25 @@ export interface CopilotRuntimeConstructorParams<T extends Parameter[] | [] = []
274
281
  * ```
275
282
  */
276
283
  createMCPClient?: CreateMCPClientFunction;
284
+
285
+ /**
286
+ * Optional trace handler for comprehensive debugging and observability.
287
+ *
288
+ * **Requires publicApiKey**: Tracing only works when requests include a valid publicApiKey.
289
+ * This is a premium CopilotKit Cloud feature.
290
+ *
291
+ * @param traceEvent - Structured trace event with rich debugging context
292
+ *
293
+ * @example
294
+ * ```typescript
295
+ * const runtime = new CopilotRuntime({
296
+ * onTrace: (traceEvent) => {
297
+ * debugDashboard.capture(traceEvent);
298
+ * }
299
+ * });
300
+ * ```
301
+ */
302
+ onTrace?: CopilotTraceHandler;
277
303
  }
278
304
 
279
305
  export class CopilotRuntime<const T extends Parameter[] | [] = []> {
@@ -286,6 +312,8 @@ export class CopilotRuntime<const T extends Parameter[] | [] = []> {
286
312
  private delegateAgentProcessingToServiceAdapter: boolean;
287
313
  private observability?: CopilotObservabilityConfig;
288
314
  private availableAgents: Pick<AgentWithEndpoint, "name" | "id">[];
315
+ private onTrace?: CopilotTraceHandler;
316
+ private hasWarnedAboutTracing = false;
289
317
 
290
318
  // +++ MCP Properties +++
291
319
  private readonly mcpServersConfig?: MCPEndpointConfig[];
@@ -303,7 +331,21 @@ export class CopilotRuntime<const T extends Parameter[] | [] = []> {
303
331
  params?.remoteEndpoints.some((e) => e.type === EndpointType.LangGraphPlatform)
304
332
  ) {
305
333
  console.warn("Actions set in runtime instance will not be available for the agent");
334
+ console.warn(
335
+ `LangGraph Platform remote endpoints are deprecated in favor of the "agents" property`,
336
+ );
306
337
  }
338
+
339
+ // TODO: finalize
340
+ // if (
341
+ // params?.agents &&
342
+ // Object.values(params.agents).some((agent) => {
343
+ // return agent instanceof AguiLangGraphAgent && !(agent instanceof LangGraphAgent);
344
+ // })
345
+ // ) {
346
+ // console.warn('LangGraph Agent class should be imported from @copilotkit/runtime. ')
347
+ // }
348
+
307
349
  this.actions = params?.actions || [];
308
350
  this.availableAgents = [];
309
351
 
@@ -320,6 +362,7 @@ export class CopilotRuntime<const T extends Parameter[] | [] = []> {
320
362
  params?.delegateAgentProcessingToServiceAdapter || false;
321
363
  this.observability = params?.observability_c;
322
364
  this.agents = params?.agents ?? {};
365
+ this.onTrace = params?.onTrace;
323
366
  // +++ MCP Initialization +++
324
367
  this.mcpServersConfig = params?.mcpServers;
325
368
  this.createMCPClientImpl = params?.createMCPClient;
@@ -452,7 +495,41 @@ export class CopilotRuntime<const T extends Parameter[] | [] = []> {
452
495
  // For storing streamed chunks if progressive logging is enabled
453
496
  const streamedChunks: any[] = [];
454
497
 
498
+ // Trace request start
499
+ await this.trace(
500
+ "request",
501
+ {
502
+ threadId,
503
+ runId,
504
+ source: "runtime",
505
+ request: {
506
+ operation: "processRuntimeRequest",
507
+ method: "POST",
508
+ url: url,
509
+ startTime: requestStartTime,
510
+ },
511
+ agent: agentSession ? { name: agentSession.agentName } : undefined,
512
+ messages: {
513
+ input: rawMessages,
514
+ messageCount: rawMessages.length,
515
+ },
516
+ technical: {
517
+ environment: process.env.NODE_ENV,
518
+ },
519
+ },
520
+ undefined,
521
+ publicApiKey,
522
+ );
523
+
455
524
  try {
525
+ if (
526
+ Object.keys(this.agents).length &&
527
+ agentSession?.agentName &&
528
+ !this.delegateAgentProcessingToServiceAdapter
529
+ ) {
530
+ this.agents = { [agentSession.agentName]: this.agents[agentSession.agentName] };
531
+ }
532
+
456
533
  if (agentSession && !this.delegateAgentProcessingToServiceAdapter) {
457
534
  return await this.processAgentRequest(request);
458
535
  }
@@ -667,17 +744,62 @@ please use an LLM adapter instead.`,
667
744
  }
668
745
  }
669
746
 
747
+ let structuredError: CopilotKitError;
748
+
670
749
  if (error instanceof CopilotKitError) {
671
- throw error;
750
+ structuredError = error;
751
+ } else {
752
+ // Convert non-CopilotKitErrors to structured errors, but preserve already structured ones
753
+ structuredError = ensureStructuredError(error, (err) =>
754
+ this.convertStreamingErrorToStructured(err),
755
+ );
672
756
  }
673
757
 
674
- // Convert non-CopilotKitErrors to structured errors
675
- console.error("Error getting response:", error);
676
- const structuredError = this.convertStreamingErrorToStructured(error);
758
+ // Trace the error
759
+ await this.trace(
760
+ "error",
761
+ {
762
+ threadId,
763
+ runId,
764
+ source: "runtime",
765
+ request: {
766
+ operation: "processRuntimeRequest",
767
+ method: "POST",
768
+ url: url,
769
+ startTime: requestStartTime,
770
+ },
771
+ response: {
772
+ endTime: Date.now(),
773
+ latency: Date.now() - requestStartTime,
774
+ },
775
+ agent: agentSession ? { name: agentSession.agentName } : undefined,
776
+ technical: {
777
+ environment: process.env.NODE_ENV,
778
+ stackTrace: error instanceof Error ? error.stack : undefined,
779
+ },
780
+ },
781
+ structuredError,
782
+ publicApiKey,
783
+ );
784
+
677
785
  throw structuredError;
678
786
  }
679
787
  }
680
788
 
789
+ async getAllAgents(graphqlContext: GraphQLContext): Promise<(AgentWithEndpoint | Agent)[]> {
790
+ const [agentsWithEndpoints, aguiAgents] = await Promise.all([
791
+ this.discoverAgentsFromEndpoints(graphqlContext),
792
+ this.discoverAgentsFromAgui(),
793
+ ]);
794
+
795
+ this.availableAgents = [...agentsWithEndpoints, ...aguiAgents].map((a) => ({
796
+ name: a.name,
797
+ id: a.id,
798
+ }));
799
+
800
+ return [...agentsWithEndpoints, ...aguiAgents];
801
+ }
802
+
681
803
  async discoverAgentsFromEndpoints(graphqlContext: GraphQLContext): Promise<AgentWithEndpoint[]> {
682
804
  const agents: Promise<AgentWithEndpoint[]> = this.remoteEndpointDefinitions.reduce(
683
805
  async (acc: Promise<Agent[]>, endpoint) => {
@@ -759,7 +881,41 @@ please use an LLM adapter instead.`,
759
881
  },
760
882
  Promise.resolve([]),
761
883
  );
762
- this.availableAgents = ((await agents) ?? []).map((a) => ({ name: a.name, id: a.id }));
884
+
885
+ return agents;
886
+ }
887
+
888
+ async discoverAgentsFromAgui(): Promise<AgentWithEndpoint[]> {
889
+ const agents: Promise<AgentWithEndpoint[]> = Object.values(this.agents ?? []).reduce(
890
+ async (acc: Promise<Agent[]>, agent: LangGraphAgent) => {
891
+ const agents = await acc;
892
+
893
+ const client = agent.client;
894
+ let data: Array<{ assistant_id: string; graph_id: string }> | { detail: string } = [];
895
+ try {
896
+ data = await client.assistants.search();
897
+
898
+ if (data && "detail" in data && (data.detail as string).toLowerCase() === "not found") {
899
+ throw new CopilotKitAgentDiscoveryError({ availableAgents: this.availableAgents });
900
+ }
901
+ } catch (e) {
902
+ throw new CopilotKitMisuseError({
903
+ message: `
904
+ Failed to find or contact agent ${agent.graphId}.
905
+ Make sure the LangGraph API is running and the agent is defined in langgraph.json
906
+
907
+ See more: https://docs.copilotkit.ai/troubleshooting/common-issues`,
908
+ });
909
+ }
910
+ const endpointAgents = data.map((entry) => ({
911
+ name: entry.graph_id,
912
+ id: entry.assistant_id,
913
+ description: "",
914
+ }));
915
+ return [...agents, ...endpointAgents];
916
+ },
917
+ Promise.resolve([]),
918
+ );
763
919
 
764
920
  return agents;
765
921
  }
@@ -769,50 +925,18 @@ please use an LLM adapter instead.`,
769
925
  threadId: string,
770
926
  agentName: string,
771
927
  ): Promise<LoadAgentStateResponse> {
772
- const agentsWithEndpoints = await this.discoverAgentsFromEndpoints(graphqlContext);
928
+ const agents = await this.getAllAgents(graphqlContext);
773
929
 
774
- const agentWithEndpoint = agentsWithEndpoints.find((agent) => agent.name === agentName);
775
- if (!agentWithEndpoint) {
930
+ const agent = agents.find((agent) => agent.name === agentName);
931
+ if (!agent) {
776
932
  throw new Error("Agent not found");
777
933
  }
778
934
 
779
- if (agentWithEndpoint.endpoint.type === EndpointType.LangGraphPlatform) {
780
- const propertyHeaders = graphqlContext.properties.authorization
781
- ? { authorization: `Bearer ${graphqlContext.properties.authorization}` }
782
- : null;
783
-
784
- const client = new LangGraphClient({
785
- apiUrl: agentWithEndpoint.endpoint.deploymentUrl,
786
- apiKey: agentWithEndpoint.endpoint.langsmithApiKey,
787
- defaultHeaders: { ...propertyHeaders },
788
- });
789
- let state: any = {};
790
- try {
791
- state = (await client.threads.getState(threadId)).values as any;
792
- } catch (error) {}
793
-
794
- if (Object.keys(state).length === 0) {
795
- return {
796
- threadId: threadId || "",
797
- threadExists: false,
798
- state: JSON.stringify({}),
799
- messages: JSON.stringify([]),
800
- };
801
- } else {
802
- const { messages, ...stateWithoutMessages } = state;
803
- const copilotkitMessages = langchainMessagesToCopilotKit(messages);
804
- return {
805
- threadId: threadId || "",
806
- threadExists: true,
807
- state: JSON.stringify(stateWithoutMessages),
808
- messages: JSON.stringify(copilotkitMessages),
809
- };
810
- }
811
- } else if (
812
- agentWithEndpoint.endpoint.type === EndpointType.CopilotKit ||
813
- !("type" in agentWithEndpoint.endpoint)
935
+ if (
936
+ "endpoint" in agent &&
937
+ (agent.endpoint.type === EndpointType.CopilotKit || !("type" in agent.endpoint))
814
938
  ) {
815
- const cpkEndpoint = agentWithEndpoint.endpoint as CopilotKitEndpoint;
939
+ const cpkEndpoint = agent.endpoint as CopilotKitEndpoint;
816
940
  const fetchUrl = `${cpkEndpoint.url}/agents/state`;
817
941
  try {
818
942
  const response = await fetchWithRetry(fetchUrl, {
@@ -828,10 +952,24 @@ please use an LLM adapter instead.`,
828
952
  if (response.status === 404) {
829
953
  throw new CopilotKitApiDiscoveryError({ url: fetchUrl });
830
954
  }
955
+
956
+ // Extract semantic error information from response body
957
+ let errorMessage = `HTTP ${response.status} error`;
958
+ try {
959
+ const errorBody = await response.text();
960
+ const parsedError = JSON.parse(errorBody);
961
+ if (parsedError.error && typeof parsedError.error === "string") {
962
+ errorMessage = parsedError.error;
963
+ }
964
+ } catch {
965
+ // If parsing fails, fall back to generic message
966
+ }
967
+
831
968
  throw new ResolvedCopilotKitError({
832
969
  status: response.status,
833
970
  url: fetchUrl,
834
971
  isRemoteEndpoint: true,
972
+ message: errorMessage,
835
973
  });
836
974
  }
837
975
 
@@ -848,9 +986,64 @@ please use an LLM adapter instead.`,
848
986
  }
849
987
  throw new CopilotKitLowLevelError({ error, url: fetchUrl });
850
988
  }
989
+ }
990
+
991
+ const propertyHeaders = graphqlContext.properties.authorization
992
+ ? { authorization: `Bearer ${graphqlContext.properties.authorization}` }
993
+ : null;
994
+
995
+ let client: LangGraphClient;
996
+ if ("endpoint" in agent && agent.endpoint.type === EndpointType.LangGraphPlatform) {
997
+ client = new LangGraphClient({
998
+ apiUrl: agent.endpoint.deploymentUrl,
999
+ apiKey: agent.endpoint.langsmithApiKey,
1000
+ defaultHeaders: { ...propertyHeaders },
1001
+ });
851
1002
  } else {
852
- throw new Error(`Unknown endpoint type: ${(agentWithEndpoint.endpoint as any).type}`);
1003
+ const aguiAgent = graphqlContext._copilotkit.runtime.agents[agent.name] as LangGraphAgent;
1004
+ if (!aguiAgent) {
1005
+ throw new Error(`Agent: ${agent.name} could not be resolved`);
1006
+ }
1007
+ // @ts-expect-error -- both clients are the same
1008
+ client = aguiAgent.client;
853
1009
  }
1010
+ let state: any = {};
1011
+ try {
1012
+ state = (await client.threads.getState(threadId)).values as any;
1013
+ } catch (error) {
1014
+ // All errors from agent state loading are user configuration issues
1015
+ const errorMessage = error instanceof Error ? error.message : String(error);
1016
+
1017
+ // Log user configuration errors at debug level to reduce noise
1018
+ console.debug(`Agent '${agentName}' configuration issue: ${errorMessage}`);
1019
+
1020
+ // Throw a configuration error - all agent state loading failures are user setup issues
1021
+ throw new ResolvedCopilotKitError({
1022
+ status: 400,
1023
+ message: `Agent '${agentName}' failed to execute: ${errorMessage}`,
1024
+ code: CopilotKitErrorCode.CONFIGURATION_ERROR,
1025
+ });
1026
+ }
1027
+
1028
+ if (Object.keys(state).length === 0) {
1029
+ return {
1030
+ threadId: threadId || "",
1031
+ threadExists: false,
1032
+ state: JSON.stringify({}),
1033
+ messages: JSON.stringify([]),
1034
+ };
1035
+ } else {
1036
+ const { messages, ...stateWithoutMessages } = state;
1037
+ const copilotkitMessages = langchainMessagesToCopilotKit(messages);
1038
+ return {
1039
+ threadId: threadId || "",
1040
+ threadExists: true,
1041
+ state: JSON.stringify(stateWithoutMessages),
1042
+ messages: JSON.stringify(copilotkitMessages),
1043
+ };
1044
+ }
1045
+
1046
+ throw new Error(`Agent: ${agent.name} could not be resolved`);
854
1047
  }
855
1048
 
856
1049
  private async processAgentRequest(
@@ -876,6 +1069,33 @@ please use an LLM adapter instead.`,
876
1069
  // for backwards compatibility, deal with the case when no threadId is provided
877
1070
  const threadId = threadIdFromRequest ?? agentSession.threadId;
878
1071
 
1072
+ // Trace agent request start
1073
+ await this.trace(
1074
+ "agent_state",
1075
+ {
1076
+ threadId,
1077
+ source: "agent",
1078
+ request: {
1079
+ operation: "processAgentRequest",
1080
+ method: "POST",
1081
+ startTime: requestStartTime,
1082
+ },
1083
+ agent: {
1084
+ name: agentName,
1085
+ nodeName: nodeName,
1086
+ },
1087
+ messages: {
1088
+ input: rawMessages,
1089
+ messageCount: rawMessages.length,
1090
+ },
1091
+ technical: {
1092
+ environment: process.env.NODE_ENV,
1093
+ },
1094
+ },
1095
+ undefined,
1096
+ publicApiKey,
1097
+ );
1098
+
879
1099
  const serverSideActions = await this.getServerSideActions(request);
880
1100
 
881
1101
  const messages = convertGqlInputToMessages(rawMessages);
@@ -1003,9 +1223,7 @@ please use an LLM adapter instead.`,
1003
1223
  eventSource.stream(async (eventStream$) => {
1004
1224
  from(stream).subscribe({
1005
1225
  next: (event) => eventStream$.next(event),
1006
- error: (err) => {
1007
- console.error("Error in stream", err);
1008
-
1226
+ error: async (err) => {
1009
1227
  // Log error with observability if enabled
1010
1228
  if (this.observability?.enabled && publicApiKey) {
1011
1229
  try {
@@ -1027,8 +1245,39 @@ please use an LLM adapter instead.`,
1027
1245
  }
1028
1246
  }
1029
1247
 
1030
- // Convert network termination errors to structured errors
1031
- const structuredError = this.convertStreamingErrorToStructured(err);
1248
+ // Preserve structured CopilotKit errors, only convert unstructured errors
1249
+ const structuredError = ensureStructuredError(err, (error) =>
1250
+ this.convertStreamingErrorToStructured(error),
1251
+ );
1252
+
1253
+ // Trace streaming errors
1254
+ await this.trace(
1255
+ "error",
1256
+ {
1257
+ threadId,
1258
+ source: "agent",
1259
+ request: {
1260
+ operation: "processAgentRequest",
1261
+ method: "POST",
1262
+ startTime: requestStartTime,
1263
+ },
1264
+ response: {
1265
+ endTime: Date.now(),
1266
+ latency: Date.now() - requestStartTime,
1267
+ },
1268
+ agent: {
1269
+ name: agentName,
1270
+ nodeName: nodeName,
1271
+ },
1272
+ technical: {
1273
+ environment: process.env.NODE_ENV,
1274
+ stackTrace: err instanceof Error ? err.stack : undefined,
1275
+ },
1276
+ },
1277
+ structuredError,
1278
+ publicApiKey,
1279
+ );
1280
+
1032
1281
  eventStream$.error(structuredError);
1033
1282
  eventStream$.complete();
1034
1283
  },
@@ -1106,8 +1355,41 @@ please use an LLM adapter instead.`,
1106
1355
  }
1107
1356
  }
1108
1357
 
1358
+ // Ensure error is structured
1359
+ const structuredError = ensureStructuredError(error, (err) =>
1360
+ this.convertStreamingErrorToStructured(err),
1361
+ );
1362
+
1363
+ // Trace the agent error
1364
+ await this.trace(
1365
+ "error",
1366
+ {
1367
+ threadId,
1368
+ source: "agent",
1369
+ request: {
1370
+ operation: "processAgentRequest",
1371
+ method: "POST",
1372
+ startTime: requestStartTime,
1373
+ },
1374
+ response: {
1375
+ endTime: Date.now(),
1376
+ latency: Date.now() - requestStartTime,
1377
+ },
1378
+ agent: {
1379
+ name: agentName,
1380
+ nodeName: nodeName,
1381
+ },
1382
+ technical: {
1383
+ environment: process.env.NODE_ENV,
1384
+ stackTrace: error instanceof Error ? error.stack : undefined,
1385
+ },
1386
+ },
1387
+ structuredError,
1388
+ publicApiKey,
1389
+ );
1390
+
1109
1391
  console.error("Error getting response:", error);
1110
- throw error;
1392
+ throw structuredError;
1111
1393
  }
1112
1394
  }
1113
1395
 
@@ -1223,53 +1505,108 @@ please use an LLM adapter instead.`,
1223
1505
  }
1224
1506
 
1225
1507
  private convertStreamingErrorToStructured(error: any): CopilotKitError {
1226
- // Handle network termination errors
1227
- if (
1228
- error?.message?.includes("terminated") ||
1229
- error?.cause?.code === "UND_ERR_SOCKET" ||
1230
- error?.message?.includes("other side closed") ||
1231
- error?.code === "UND_ERR_SOCKET"
1232
- ) {
1233
- return new CopilotKitError({
1234
- message:
1235
- "Connection to agent was unexpectedly terminated. This may be due to the agent service being restarted or network issues. Please try again.",
1236
- code: CopilotKitErrorCode.NETWORK_ERROR,
1237
- });
1238
- }
1508
+ // Determine a more helpful error message based on context
1509
+ let helpfulMessage = generateHelpfulErrorMessage(error, "agent streaming connection");
1239
1510
 
1240
- // Handle other network-related errors
1511
+ // For network-related errors, use CopilotKitLowLevelError to preserve the original error
1241
1512
  if (
1242
1513
  error?.message?.includes("fetch failed") ||
1243
1514
  error?.message?.includes("ECONNREFUSED") ||
1244
1515
  error?.message?.includes("ENOTFOUND") ||
1245
- error?.message?.includes("ETIMEDOUT")
1516
+ error?.message?.includes("ETIMEDOUT") ||
1517
+ error?.message?.includes("terminated") ||
1518
+ error?.cause?.code === "UND_ERR_SOCKET" ||
1519
+ error?.message?.includes("other side closed") ||
1520
+ error?.code === "UND_ERR_SOCKET"
1246
1521
  ) {
1247
1522
  return new CopilotKitLowLevelError({
1248
1523
  error: error instanceof Error ? error : new Error(String(error)),
1249
1524
  url: "agent streaming connection",
1250
- message:
1251
- "Network error occurred during agent streaming. Please check your connection and try again.",
1252
- });
1253
- }
1254
-
1255
- // Handle abort/cancellation errors (these are usually normal)
1256
- if (
1257
- error?.message?.includes("aborted") ||
1258
- error?.message?.includes("canceled") ||
1259
- error?.message?.includes("signal is aborted")
1260
- ) {
1261
- return new CopilotKitError({
1262
- message: "Agent request was cancelled",
1263
- code: CopilotKitErrorCode.UNKNOWN,
1525
+ message: helpfulMessage,
1264
1526
  });
1265
1527
  }
1266
1528
 
1267
- // Default: convert unknown streaming errors
1529
+ // For all other errors, preserve the raw error in a basic CopilotKitError
1268
1530
  return new CopilotKitError({
1269
- message: `Agent streaming error: ${error?.message || String(error)}`,
1531
+ message: helpfulMessage,
1270
1532
  code: CopilotKitErrorCode.UNKNOWN,
1271
1533
  });
1272
1534
  }
1535
+
1536
+ private async trace(
1537
+ type: CopilotTraceEvent["type"],
1538
+ context: CopilotRequestContext,
1539
+ error?: any,
1540
+ publicApiKey?: string,
1541
+ ): Promise<void> {
1542
+ if (!this.onTrace) return;
1543
+
1544
+ // Just check if publicApiKey is defined (regardless of validity)
1545
+ if (!publicApiKey) {
1546
+ if (!this.hasWarnedAboutTracing) {
1547
+ console.warn(
1548
+ "CopilotKit: onTrace handler provided but requires publicApiKey to be defined for tracing to work.",
1549
+ );
1550
+ this.hasWarnedAboutTracing = true;
1551
+ }
1552
+ return;
1553
+ }
1554
+
1555
+ try {
1556
+ const traceEvent: CopilotTraceEvent = {
1557
+ type,
1558
+ timestamp: Date.now(),
1559
+ context,
1560
+ ...(error && { error }),
1561
+ };
1562
+
1563
+ await this.onTrace(traceEvent);
1564
+ } catch (traceError) {
1565
+ // Don't let trace errors break the main flow
1566
+ console.error("Error in onTrace handler:", traceError);
1567
+ }
1568
+ }
1569
+
1570
+ /**
1571
+ * Public method to trace GraphQL validation errors
1572
+ * This allows the GraphQL resolver to send validation errors through the trace system
1573
+ */
1574
+ public async traceGraphQLError(
1575
+ error: { message: string; code: string; type: string },
1576
+ context: {
1577
+ operation: string;
1578
+ cloudConfigPresent: boolean;
1579
+ guardrailsEnabled: boolean;
1580
+ },
1581
+ ): Promise<void> {
1582
+ if (!this.onTrace) return;
1583
+
1584
+ try {
1585
+ await this.onTrace({
1586
+ type: "error",
1587
+ timestamp: Date.now(),
1588
+ context: {
1589
+ source: "runtime",
1590
+ request: {
1591
+ operation: context.operation,
1592
+ startTime: Date.now(),
1593
+ },
1594
+ technical: {
1595
+ environment: process.env.NODE_ENV,
1596
+ },
1597
+ metadata: {
1598
+ errorType: "GraphQLValidationError",
1599
+ cloudConfigPresent: context.cloudConfigPresent,
1600
+ guardrailsEnabled: context.guardrailsEnabled,
1601
+ },
1602
+ },
1603
+ error,
1604
+ });
1605
+ } catch (traceError) {
1606
+ // Don't let trace errors break the main flow
1607
+ console.error("Error in onTrace handler:", traceError);
1608
+ }
1609
+ }
1273
1610
  }
1274
1611
 
1275
1612
  export function flattenToolCallsNoDuplicates(toolsByPriority: ActionInput[]): ActionInput[] {
@@ -16,6 +16,7 @@ import {
16
16
  LangGraphAgent as AGUILangGraphAgent,
17
17
  type LangGraphAgentConfig,
18
18
  ProcessedEvents,
19
+ SchemaKeys,
19
20
  } from "@ag-ui/langgraph";
20
21
  import { Message as LangGraphMessage } from "@langchain/langgraph-sdk/dist/types.messages";
21
22
 
@@ -180,6 +181,7 @@ export class LangGraphAgent extends AGUILangGraphAgent {
180
181
  messages,
181
182
  tools,
182
183
  );
184
+
183
185
  return {
184
186
  ...rest,
185
187
  copilotkit: {
@@ -187,4 +189,14 @@ export class LangGraphAgent extends AGUILangGraphAgent {
187
189
  },
188
190
  };
189
191
  }
192
+
193
+ async getSchemaKeys(): Promise<SchemaKeys> {
194
+ const CONSTANT_KEYS = ["copilotkit"];
195
+ const schemaKeys = await super.getSchemaKeys();
196
+ return {
197
+ config: schemaKeys.config,
198
+ input: schemaKeys.input ? [...schemaKeys.input, ...CONSTANT_KEYS] : null,
199
+ output: schemaKeys.output ? [...schemaKeys.output, ...CONSTANT_KEYS] : null,
200
+ };
201
+ }
190
202
  }