@copilotkit/runtime 1.9.2-next.7 → 1.9.2-next.9

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 (78) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/{chunk-IIXJVVTV.mjs → chunk-4JBKY7XT.mjs} +109 -75
  3. package/dist/chunk-4JBKY7XT.mjs.map +1 -0
  4. package/dist/{chunk-LSB3QZHS.mjs → chunk-5YGKE5SN.mjs} +2 -2
  5. package/dist/{chunk-2PQGAIVB.mjs → chunk-ALZ5H3VD.mjs} +2 -2
  6. package/dist/chunk-AMUJQ6IR.mjs +50 -0
  7. package/dist/chunk-AMUJQ6IR.mjs.map +1 -0
  8. package/dist/{chunk-2UAJCT5X.mjs → chunk-SMDVD4VG.mjs} +2 -2
  9. package/dist/{chunk-GW6ERFKI.mjs → chunk-UUXRYAB4.mjs} +2 -2
  10. package/dist/{chunk-5BIEM2UU.mjs → chunk-XWBDEXDA.mjs} +4 -3
  11. package/dist/{chunk-5BIEM2UU.mjs.map → chunk-XWBDEXDA.mjs.map} +1 -1
  12. package/dist/{chunk-HB4D2KJC.mjs → chunk-Z5GYTKMD.mjs} +2345 -2066
  13. package/dist/chunk-Z5GYTKMD.mjs.map +1 -0
  14. package/dist/{groq-adapter-25a2bd35.d.ts → groq-adapter-172a2ca4.d.ts} +1 -1
  15. package/dist/index.d.ts +4 -3
  16. package/dist/index.js +3713 -3356
  17. package/dist/index.js.map +1 -1
  18. package/dist/index.mjs +12 -8
  19. package/dist/index.mjs.map +1 -1
  20. package/dist/lib/index.d.ts +3 -3
  21. package/dist/lib/index.js +2981 -2624
  22. package/dist/lib/index.js.map +1 -1
  23. package/dist/lib/index.mjs +9 -8
  24. package/dist/lib/integrations/index.d.ts +3 -3
  25. package/dist/lib/integrations/index.js +141 -86
  26. package/dist/lib/integrations/index.js.map +1 -1
  27. package/dist/lib/integrations/index.mjs +7 -6
  28. package/dist/lib/integrations/nest/index.d.ts +2 -2
  29. package/dist/lib/integrations/nest/index.js +141 -86
  30. package/dist/lib/integrations/nest/index.js.map +1 -1
  31. package/dist/lib/integrations/nest/index.mjs +5 -4
  32. package/dist/lib/integrations/node-express/index.d.ts +2 -2
  33. package/dist/lib/integrations/node-express/index.js +141 -86
  34. package/dist/lib/integrations/node-express/index.js.map +1 -1
  35. package/dist/lib/integrations/node-express/index.mjs +5 -4
  36. package/dist/lib/integrations/node-http/index.d.ts +2 -2
  37. package/dist/lib/integrations/node-http/index.js +141 -86
  38. package/dist/lib/integrations/node-http/index.js.map +1 -1
  39. package/dist/lib/integrations/node-http/index.mjs +4 -3
  40. package/dist/service-adapters/index.d.ts +5 -4
  41. package/dist/service-adapters/index.js +179 -104
  42. package/dist/service-adapters/index.js.map +1 -1
  43. package/dist/service-adapters/index.mjs +6 -2
  44. package/dist/service-adapters/shared/index.d.ts +9 -0
  45. package/dist/service-adapters/shared/index.js +72 -0
  46. package/dist/service-adapters/shared/index.js.map +1 -0
  47. package/dist/service-adapters/shared/index.mjs +8 -0
  48. package/dist/service-adapters/shared/index.mjs.map +1 -0
  49. package/dist/{shared-941d59dc.d.ts → shared-bd953ebf.d.ts} +8 -4
  50. package/dist/utils/index.d.ts +17 -1
  51. package/dist/utils/index.js +3 -2
  52. package/dist/utils/index.js.map +1 -1
  53. package/dist/utils/index.mjs +1 -1
  54. package/package.json +2 -2
  55. package/src/agents/langgraph/event-source.ts +36 -38
  56. package/src/agents/langgraph/events.ts +19 -1
  57. package/src/graphql/resolvers/copilot.resolver.ts +84 -40
  58. package/src/lib/error-messages.ts +200 -0
  59. package/src/lib/integrations/shared.ts +43 -0
  60. package/src/lib/runtime/copilot-runtime.ts +56 -48
  61. package/src/lib/runtime/remote-action-constructors.ts +28 -3
  62. package/src/lib/runtime/remote-lg-action.ts +130 -40
  63. package/src/lib/streaming.ts +125 -36
  64. package/src/service-adapters/anthropic/anthropic-adapter.ts +3 -4
  65. package/src/service-adapters/events.ts +37 -81
  66. package/src/service-adapters/groq/groq-adapter.ts +66 -56
  67. package/src/service-adapters/index.ts +1 -0
  68. package/src/service-adapters/openai/openai-adapter.ts +18 -3
  69. package/src/service-adapters/shared/error-utils.ts +61 -0
  70. package/src/service-adapters/shared/index.ts +1 -0
  71. package/src/utils/failed-response-status-reasons.ts +23 -1
  72. package/dist/chunk-HB4D2KJC.mjs.map +0 -1
  73. package/dist/chunk-IIXJVVTV.mjs.map +0 -1
  74. package/dist/{chunk-LSB3QZHS.mjs.map → chunk-5YGKE5SN.mjs.map} +0 -0
  75. package/dist/{chunk-2PQGAIVB.mjs.map → chunk-ALZ5H3VD.mjs.map} +0 -0
  76. package/dist/{chunk-2UAJCT5X.mjs.map → chunk-SMDVD4VG.mjs.map} +0 -0
  77. package/dist/{chunk-GW6ERFKI.mjs.map → chunk-UUXRYAB4.mjs.map} +0 -0
  78. package/dist/{langserve-4a5c9217.d.ts → langserve-fc5cac89.d.ts} +7 -7
@@ -2,7 +2,9 @@ import {
2
2
  Client as LangGraphClient,
3
3
  EventsStreamEvent,
4
4
  GraphSchema,
5
+ Interrupt,
5
6
  StreamMode,
7
+ ThreadState,
6
8
  } from "@langchain/langgraph-sdk";
7
9
  import { createHash } from "node:crypto";
8
10
  import { isValidUUID, randomUUID } from "@copilotkit/shared";
@@ -17,9 +19,28 @@ import { CustomEventNames, LangGraphEventTypes } from "../../agents/langgraph/ev
17
19
  import telemetry from "../telemetry-client";
18
20
  import { MetaEventInput } from "../../graphql/inputs/meta-event.input";
19
21
  import { MetaEventName } from "../../graphql/types/meta-events.type";
20
- import { parseJson, CopilotKitMisuseError } from "@copilotkit/shared";
22
+ import {
23
+ parseJson,
24
+ CopilotKitMisuseError,
25
+ CopilotKitLowLevelError,
26
+ CopilotKitError,
27
+ } from "@copilotkit/shared";
21
28
  import { RemoveMessage } from "@langchain/core/messages";
22
29
  import { RETRY_CONFIG, isRetryableError, sleep, calculateDelay } from "./retry-utils";
30
+ import { generateHelpfulErrorMessage } from "../streaming";
31
+
32
+ // Utility to determine if an error is a user configuration issue vs system error
33
+ export function isUserConfigurationError(error: any): boolean {
34
+ return (
35
+ (error instanceof CopilotKitError || error instanceof CopilotKitLowLevelError) &&
36
+ (error.code === "NETWORK_ERROR" ||
37
+ error.code === "AUTHENTICATION_ERROR" ||
38
+ error.statusCode === 401 ||
39
+ error.statusCode === 403 ||
40
+ error.message?.toLowerCase().includes("authentication") ||
41
+ error.message?.toLowerCase().includes("api key"))
42
+ );
43
+ }
23
44
 
24
45
  type State = Record<string, any>;
25
46
 
@@ -84,8 +105,6 @@ type SchemaKeys = {
84
105
  config: string[] | null;
85
106
  } | null;
86
107
 
87
- let activeInterruptEvent = false;
88
-
89
108
  export async function execute(args: ExecutionArgs): Promise<ReadableStream<Uint8Array>> {
90
109
  return new ReadableStream({
91
110
  async start(controller) {
@@ -128,6 +147,15 @@ export async function execute(args: ExecutionArgs): Promise<ReadableStream<Uint8
128
147
  See more: https://docs.copilotkit.ai/troubleshooting/common-issues`,
129
148
  });
130
149
  } else {
150
+ // Preserve already structured CopilotKit errors with semantic information
151
+ if (
152
+ lastError instanceof CopilotKitError ||
153
+ lastError instanceof CopilotKitLowLevelError ||
154
+ (lastError instanceof Error && lastError.name && lastError.name.includes("CopilotKit"))
155
+ ) {
156
+ throw lastError; // Re-throw to preserve semantic information and visibility settings
157
+ }
158
+
131
159
  throw new CopilotKitMisuseError({
132
160
  message: `
133
161
  The LangGraph client threw unhandled error ${lastError}.
@@ -188,7 +216,7 @@ async function streamEvents(controller: ReadableStreamDefaultController, args: E
188
216
  await client.threads.create({ threadId });
189
217
  }
190
218
 
191
- let agentState = { values: {} };
219
+ let agentState = { values: {} } as ThreadState;
192
220
  if (wasInitiatedWithExistingThread) {
193
221
  agentState = await client.threads.getState(threadId);
194
222
  }
@@ -218,16 +246,14 @@ async function streamEvents(controller: ReadableStreamDefaultController, args: E
218
246
  const lgInterruptMetaEvent = metaEvents?.find(
219
247
  (ev) => ev.name === MetaEventName.LangGraphInterruptEvent,
220
248
  );
221
- if (activeInterruptEvent && !lgInterruptMetaEvent) {
222
- // state.messages includes only messages that were not processed by the agent, which are the interrupt messages
223
- payload.command = { resume: state.messages };
224
- }
249
+
225
250
  if (lgInterruptMetaEvent?.response) {
226
251
  let response = lgInterruptMetaEvent.response;
227
252
  payload.command = { resume: parseJson(response, response) };
228
253
  }
229
254
 
230
- if (mode === "continue" && !activeInterruptEvent) {
255
+ const interrupts = (agentState.tasks?.[0]?.interrupts ?? []) as Interrupt[];
256
+ if (mode === "continue" && !interrupts.length) {
231
257
  await client.threads.updateState(threadId, { values: state, asNode: nodeName });
232
258
  }
233
259
 
@@ -311,17 +337,30 @@ async function streamEvents(controller: ReadableStreamDefaultController, args: E
311
337
  let shouldExit = false;
312
338
  let externalRunId = null;
313
339
 
314
- const streamResponse = client.runs.stream(threadId, assistantId, payload);
315
-
316
340
  const emit = (message: string) => controller.enqueue(new TextEncoder().encode(message));
317
341
 
342
+ // If there are still outstanding unresolved interrupts, we must force resolution of them before moving forward
343
+ if (interrupts?.length && !payload.command?.resume) {
344
+ // If the interrupt is "by message" we assume the upcoming user message is a resoluton for the interrupt
345
+ if (!lgInterruptMetaEvent) {
346
+ // state.messages includes only messages that were not processed by the agent, which are the interrupt messages
347
+ payload.command = { resume: state.messages };
348
+ } else {
349
+ interrupts.forEach((interrupt) => {
350
+ emitInterrupt(interrupt.value, emit);
351
+ });
352
+ return Promise.resolve();
353
+ }
354
+ }
355
+
356
+ const streamResponse = client.runs.stream(threadId, assistantId, payload);
357
+
318
358
  let latestStateValues = {};
319
359
  let updatedState = state;
320
360
  // If a manual emittance happens, it is the ultimate source of truth of state, unless a node has exited.
321
361
  // Therefore, this value should either hold null, or the only edition of state that should be used.
322
362
  let manuallyEmittedState = null;
323
363
 
324
- activeInterruptEvent = false;
325
364
  try {
326
365
  telemetry.capture("oss.runtime.agent_execution_stream_started", {
327
366
  hashedLgcKey: streamInfo.hashedLgcKey,
@@ -330,7 +369,43 @@ async function streamEvents(controller: ReadableStreamDefaultController, args: E
330
369
  if (!["events", "values", "error", "updates"].includes(streamResponseChunk.event)) continue;
331
370
 
332
371
  if (streamResponseChunk.event === "error") {
333
- throw new Error(`Error event thrown: ${streamResponseChunk.data.message}`);
372
+ const errorData = streamResponseChunk.data;
373
+
374
+ // Check if this is a structured error from our Python agent
375
+ if (errorData && typeof errorData === "object" && "error_details" in errorData) {
376
+ const errorDetails = (errorData as any).error_details;
377
+
378
+ // Create a structured error with preserved semantic information
379
+ const preservedError = new CopilotKitLowLevelError({
380
+ error: new Error(errorDetails.message),
381
+ url: "langgraph platform agent",
382
+ message: `${errorDetails.type}: ${errorDetails.message}`,
383
+ });
384
+
385
+ // Add additional error context
386
+ if (errorDetails.status_code) {
387
+ (preservedError as any).statusCode = errorDetails.status_code;
388
+ }
389
+ if (errorDetails.response_data) {
390
+ (preservedError as any).responseData = errorDetails.response_data;
391
+ }
392
+ (preservedError as any).agentName = errorDetails.agent_name;
393
+ (preservedError as any).originalErrorType = errorDetails.type;
394
+
395
+ throw preservedError;
396
+ }
397
+
398
+ // Fallback for generic error messages
399
+ const helpfulMessage = generateHelpfulErrorMessage(
400
+ new Error(errorData.message),
401
+ "LangGraph Platform agent",
402
+ );
403
+
404
+ throw new CopilotKitLowLevelError({
405
+ error: new Error(errorData.message),
406
+ url: "langgraph platform agent",
407
+ message: helpfulMessage,
408
+ });
334
409
  }
335
410
 
336
411
  // Force event type, as data is not properly defined on the LG side.
@@ -345,33 +420,8 @@ async function streamEvents(controller: ReadableStreamDefaultController, args: E
345
420
 
346
421
  const interruptEvents = chunk.data.__interrupt__;
347
422
  if (interruptEvents?.length) {
348
- activeInterruptEvent = true;
349
423
  const interruptValue = interruptEvents?.[0].value;
350
- if (
351
- typeof interruptValue != "string" &&
352
- "__copilotkit_interrupt_value__" in interruptValue
353
- ) {
354
- const evValue = interruptValue.__copilotkit_interrupt_value__;
355
- emit(
356
- JSON.stringify({
357
- event: LangGraphEventTypes.OnCopilotKitInterrupt,
358
- data: {
359
- value: typeof evValue === "string" ? evValue : JSON.stringify(evValue),
360
- messages: langchainMessagesToCopilotKit(interruptValue.__copilotkit_messages__),
361
- },
362
- }) + "\n",
363
- );
364
- } else {
365
- emit(
366
- JSON.stringify({
367
- event: LangGraphEventTypes.OnInterrupt,
368
- value:
369
- typeof interruptValue === "string"
370
- ? interruptValue
371
- : JSON.stringify(interruptValue),
372
- }) + "\n",
373
- );
374
- }
424
+ emitInterrupt(interruptValue, emit);
375
425
  continue;
376
426
  }
377
427
  if (streamResponseChunk.event === "updates") continue;
@@ -521,11 +571,29 @@ async function streamEvents(controller: ReadableStreamDefaultController, args: E
521
571
 
522
572
  return Promise.resolve();
523
573
  } catch (e) {
524
- logger.error(e);
574
+ // Distinguish between user errors and system errors for logging
575
+ if (isUserConfigurationError(e)) {
576
+ // Log user errors at debug level to reduce noise
577
+ logger.debug({ error: e.message, code: e.code }, "User configuration error");
578
+ } else {
579
+ // Log actual system errors at error level
580
+ logger.error(e);
581
+ }
582
+
525
583
  telemetry.capture("oss.runtime.agent_execution_stream_errored", {
526
584
  ...streamInfo,
527
585
  error: e.message,
528
586
  });
587
+
588
+ // Re-throw CopilotKit errors so they can be handled properly at higher levels
589
+ if (
590
+ e instanceof CopilotKitError ||
591
+ e instanceof CopilotKitLowLevelError ||
592
+ (e instanceof Error && e.name && e.name.includes("CopilotKit"))
593
+ ) {
594
+ throw e;
595
+ }
596
+
529
597
  return Promise.resolve();
530
598
  }
531
599
  }
@@ -909,3 +977,25 @@ function getSchemaKeys(graphSchema: GraphSchema): SchemaKeys {
909
977
  function filterObjectBySchemaKeys(obj: Record<string, any>, schemaKeys: string[]) {
910
978
  return Object.fromEntries(Object.entries(obj).filter(([key]) => schemaKeys.includes(key)));
911
979
  }
980
+
981
+ function emitInterrupt(interruptValue: any, emit: (data: string) => void) {
982
+ if (typeof interruptValue != "string" && "__copilotkit_interrupt_value__" in interruptValue) {
983
+ const evValue = interruptValue.__copilotkit_interrupt_value__;
984
+ emit(
985
+ JSON.stringify({
986
+ event: LangGraphEventTypes.OnCopilotKitInterrupt,
987
+ data: {
988
+ value: typeof evValue === "string" ? evValue : JSON.stringify(evValue),
989
+ messages: langchainMessagesToCopilotKit(interruptValue.__copilotkit_messages__),
990
+ },
991
+ }) + "\n",
992
+ );
993
+ } else {
994
+ emit(
995
+ JSON.stringify({
996
+ event: LangGraphEventTypes.OnInterrupt,
997
+ value: typeof interruptValue === "string" ? interruptValue : JSON.stringify(interruptValue),
998
+ }) + "\n",
999
+ );
1000
+ }
1001
+ }
@@ -1,5 +1,11 @@
1
1
  import { ReplaySubject } from "rxjs";
2
- import { CopilotKitLowLevelError, CopilotKitError, CopilotKitErrorCode } from "@copilotkit/shared";
2
+ import {
3
+ CopilotKitLowLevelError,
4
+ CopilotKitError,
5
+ CopilotKitErrorCode,
6
+ ensureStructuredError,
7
+ } from "@copilotkit/shared";
8
+ import { errorConfig, getFallbackMessage } from "./error-messages";
3
9
 
4
10
  export async function writeJsonLineResponseToEventStream<T>(
5
11
  response: ReadableStream<Uint8Array>,
@@ -52,10 +58,8 @@ export async function writeJsonLineResponseToEventStream<T>(
52
58
  }
53
59
  }
54
60
  } catch (error) {
55
- console.error("Error in stream", error);
56
-
57
- // Convert network termination errors to structured errors
58
- const structuredError = convertStreamingErrorToStructured(error);
61
+ // Preserve already structured CopilotKit errors, only convert unstructured errors
62
+ const structuredError = ensureStructuredError(error, convertStreamingErrorToStructured);
59
63
  eventStream$.error(structuredError);
60
64
  return;
61
65
  }
@@ -63,50 +67,135 @@ export async function writeJsonLineResponseToEventStream<T>(
63
67
  }
64
68
 
65
69
  function convertStreamingErrorToStructured(error: any): CopilotKitError {
66
- // Handle network termination errors
67
- if (
68
- error?.message?.includes("terminated") ||
69
- error?.cause?.code === "UND_ERR_SOCKET" ||
70
- error?.message?.includes("other side closed") ||
71
- error?.code === "UND_ERR_SOCKET"
72
- ) {
73
- return new CopilotKitError({
74
- message:
75
- "Connection to agent was unexpectedly terminated. This is likely due to the agent service being down or experiencing issues. Please check your agent logs and try again.",
76
- code: CopilotKitErrorCode.NETWORK_ERROR,
77
- });
78
- }
70
+ // Determine a more helpful error message based on context
71
+ let helpfulMessage = generateHelpfulErrorMessage(error);
79
72
 
80
- // Handle other network-related errors
73
+ // For network-related errors, use CopilotKitLowLevelError to preserve the original error
81
74
  if (
82
75
  error?.message?.includes("fetch failed") ||
83
76
  error?.message?.includes("ECONNREFUSED") ||
84
77
  error?.message?.includes("ENOTFOUND") ||
85
- error?.message?.includes("ETIMEDOUT")
78
+ error?.message?.includes("ETIMEDOUT") ||
79
+ error?.message?.includes("terminated") ||
80
+ error?.cause?.code === "UND_ERR_SOCKET" ||
81
+ error?.message?.includes("other side closed") ||
82
+ error?.code === "UND_ERR_SOCKET"
86
83
  ) {
87
84
  return new CopilotKitLowLevelError({
88
85
  error: error instanceof Error ? error : new Error(String(error)),
89
86
  url: "streaming connection",
90
- message:
91
- "Network error occurred during streaming. Please check your connection and try again.",
92
- });
93
- }
94
-
95
- // Handle abort/cancellation errors (these are usually normal)
96
- if (
97
- error?.message?.includes("aborted") ||
98
- error?.message?.includes("canceled") ||
99
- error?.message?.includes("signal is aborted")
100
- ) {
101
- return new CopilotKitError({
102
- message: "Request was cancelled",
103
- code: CopilotKitErrorCode.UNKNOWN,
87
+ message: helpfulMessage,
104
88
  });
105
89
  }
106
90
 
107
- // Default: convert unknown streaming errors
91
+ // For all other errors, preserve the raw error in a basic CopilotKitError
108
92
  return new CopilotKitError({
109
- message: `Streaming error: ${error?.message || String(error)}`,
93
+ message: helpfulMessage,
110
94
  code: CopilotKitErrorCode.UNKNOWN,
111
95
  });
112
96
  }
97
+
98
+ /**
99
+ * Generates a helpful error message based on error patterns and context
100
+ */
101
+ export function generateHelpfulErrorMessage(error: any, context: string = "connection"): string {
102
+ const baseMessage = error?.message || String(error);
103
+
104
+ // Check for preserved error information from Python agent
105
+ const originalErrorType = error?.originalErrorType || error?.extensions?.originalErrorType;
106
+ const statusCode = error?.statusCode || error?.extensions?.statusCode;
107
+ const responseData = error?.responseData || error?.extensions?.responseData;
108
+
109
+ // First, try to match by original error type if available (more specific)
110
+ if (originalErrorType) {
111
+ const typeConfig = errorConfig.errorPatterns[originalErrorType];
112
+ if (typeConfig) {
113
+ return typeConfig.message.replace("{context}", context);
114
+ }
115
+ }
116
+
117
+ // Check for specific error patterns from configuration
118
+ for (const [pattern, config] of Object.entries(errorConfig.errorPatterns)) {
119
+ const shouldMatch =
120
+ baseMessage?.includes(pattern) ||
121
+ error?.cause?.code === pattern ||
122
+ error?.code === pattern ||
123
+ statusCode === parseInt(pattern) ||
124
+ (pattern === "other_side_closed" && baseMessage?.includes("other side closed")) ||
125
+ (pattern === "fetch_failed" && baseMessage?.includes("fetch failed")) ||
126
+ (responseData && JSON.stringify(responseData).includes(pattern));
127
+
128
+ if (shouldMatch) {
129
+ // Replace {context} placeholder with actual context
130
+ return config.message.replace("{context}", context);
131
+ }
132
+ }
133
+
134
+ // Try to match by category for fallback messages
135
+ if (isNetworkError(error)) {
136
+ return getFallbackMessage("network");
137
+ }
138
+
139
+ if (isConnectionError(error)) {
140
+ return getFallbackMessage("connection");
141
+ }
142
+
143
+ if (isAuthenticationError(error)) {
144
+ return getFallbackMessage("authentication");
145
+ }
146
+
147
+ // Default fallback
148
+ return getFallbackMessage("default");
149
+ }
150
+
151
+ /**
152
+ * Determines if an error is network-related
153
+ */
154
+ function isNetworkError(error: any): boolean {
155
+ const networkPatterns = ["ECONNREFUSED", "ENOTFOUND", "ETIMEDOUT", "fetch_failed"];
156
+ return networkPatterns.some(
157
+ (pattern) =>
158
+ error?.message?.includes(pattern) ||
159
+ error?.cause?.code === pattern ||
160
+ error?.code === pattern,
161
+ );
162
+ }
163
+
164
+ /**
165
+ * Determines if an error is connection-related
166
+ */
167
+ function isConnectionError(error: any): boolean {
168
+ const connectionPatterns = ["terminated", "UND_ERR_SOCKET", "other side closed"];
169
+ return connectionPatterns.some(
170
+ (pattern) =>
171
+ error?.message?.includes(pattern) ||
172
+ error?.cause?.code === pattern ||
173
+ error?.code === pattern,
174
+ );
175
+ }
176
+
177
+ /**
178
+ * Determines if an error is authentication-related
179
+ */
180
+ function isAuthenticationError(error: any): boolean {
181
+ const authPatterns = [
182
+ "401",
183
+ "api key",
184
+ "unauthorized",
185
+ "authentication",
186
+ "AuthenticationError",
187
+ "PermissionDeniedError",
188
+ ];
189
+ const baseMessage = error?.message || String(error);
190
+ const originalErrorType = error?.originalErrorType || error?.extensions?.originalErrorType;
191
+ const statusCode = error?.statusCode || error?.extensions?.statusCode;
192
+
193
+ return authPatterns.some(
194
+ (pattern) =>
195
+ baseMessage?.toLowerCase().includes(pattern.toLowerCase()) ||
196
+ originalErrorType === pattern ||
197
+ statusCode === 401 ||
198
+ error?.status === 401 ||
199
+ error?.statusCode === 401,
200
+ );
201
+ }
@@ -29,6 +29,7 @@ import {
29
29
  } from "./utils";
30
30
 
31
31
  import { randomId, randomUUID } from "@copilotkit/shared";
32
+ import { convertServiceAdapterError } from "../shared";
32
33
 
33
34
  const DEFAULT_MODEL = "claude-3-5-sonnet-latest";
34
35
 
@@ -220,15 +221,13 @@ export class AnthropicAdapter implements CopilotServiceAdapter {
220
221
  }
221
222
  }
222
223
  } catch (error) {
223
- console.error("[Anthropic] Error processing stream:", error);
224
- throw error;
224
+ throw convertServiceAdapterError(error, "Anthropic");
225
225
  }
226
226
 
227
227
  eventStream$.complete();
228
228
  });
229
229
  } catch (error) {
230
- console.error("[Anthropic] Error during API call:", error);
231
- throw error;
230
+ throw convertServiceAdapterError(error, "Anthropic");
232
231
  }
233
232
 
234
233
  return {
@@ -1,37 +1,32 @@
1
1
  import {
2
2
  Action,
3
- randomId,
4
3
  CopilotKitError,
5
4
  CopilotKitErrorCode,
6
5
  CopilotKitLowLevelError,
6
+ ensureStructuredError,
7
+ randomId,
8
+ Severity,
7
9
  } from "@copilotkit/shared";
10
+ import { plainToInstance } from "class-transformer";
8
11
  import {
9
- of,
12
+ catchError,
10
13
  concat,
11
- scan,
12
14
  concatMap,
13
- ReplaySubject,
14
- Subject,
15
+ EMPTY,
15
16
  firstValueFrom,
16
17
  from,
17
- catchError,
18
- EMPTY,
19
- BehaviorSubject,
18
+ of,
19
+ ReplaySubject,
20
+ scan,
21
+ Subject,
20
22
  } from "rxjs";
21
- import { streamLangChainResponse } from "./langchain/utils";
23
+ import { ActionInput } from "../graphql/inputs/action.input";
24
+ import { ActionExecutionMessage, ResultMessage, TextMessage } from "../graphql/types/converted";
22
25
  import { GuardrailsResult } from "../graphql/types/guardrails-result.type";
23
- import telemetry from "../lib/telemetry-client";
24
26
  import { isRemoteAgentAction } from "../lib/runtime/remote-actions";
25
- import { ActionInput } from "../graphql/inputs/action.input";
26
- import {
27
- ActionExecutionMessage,
28
- ResultMessage,
29
- TextMessage,
30
- Message,
31
- } from "../graphql/types/converted";
32
- import { plainToInstance } from "class-transformer";
33
- import { MessageRole } from "../graphql/types/enums";
34
- import { parseJson, tryMap } from "@copilotkit/shared";
27
+ import { generateHelpfulErrorMessage } from "../lib/streaming";
28
+ import telemetry from "../lib/telemetry-client";
29
+ import { streamLangChainResponse } from "./langchain/utils";
35
30
 
36
31
  export enum RuntimeEventTypes {
37
32
  TextMessageStart = "TextMessageStart",
@@ -273,10 +268,8 @@ export class RuntimeEventSource {
273
268
  threadId: string;
274
269
  }) {
275
270
  this.callback(this.eventStream$).catch((error) => {
276
- console.error("Error in event source callback", error);
277
-
278
- // Convert streaming errors to structured errors
279
- const structuredError = convertStreamingErrorToStructured(error);
271
+ // Convert streaming errors to structured errors, but preserve already structured ones
272
+ const structuredError = ensureStructuredError(error, convertStreamingErrorToStructured);
280
273
  this.eventStream$.error(structuredError);
281
274
  this.eventStream$.complete();
282
275
  });
@@ -330,17 +323,16 @@ export class RuntimeEventSource {
330
323
  eventWithState.actionExecutionId,
331
324
  actionInputsWithoutAgents,
332
325
  threadId,
333
- ).catch((error) => {
334
- console.error(error);
335
- });
326
+ ).catch((error) => {});
336
327
 
337
328
  telemetry.capture("oss.runtime.server_action_executed", {});
338
329
  return concat(of(eventWithState.event!), toolCallEventStream$).pipe(
339
330
  catchError((error) => {
340
- console.error("Error in tool call stream", error);
341
-
342
- // Convert streaming errors to structured errors and send as action result
343
- const structuredError = convertStreamingErrorToStructured(error);
331
+ // Convert streaming errors to structured errors and send as action result, but preserve already structured ones
332
+ const structuredError = ensureStructuredError(
333
+ error,
334
+ convertStreamingErrorToStructured,
335
+ );
344
336
  toolCallEventStream$.sendActionExecutionResult({
345
337
  actionExecutionId: eventWithState.actionExecutionId!,
346
338
  actionName: eventWithState.action!.name,
@@ -436,10 +428,8 @@ async function executeAction(
436
428
  from(stream).subscribe({
437
429
  next: (event) => eventStream$.next(event),
438
430
  error: (err) => {
439
- console.error("Error in stream", err);
440
-
441
- // Convert streaming errors to structured errors
442
- const structuredError = convertStreamingErrorToStructured(err);
431
+ // Preserve already structured CopilotKit errors, only convert unstructured errors
432
+ const structuredError = ensureStructuredError(err, convertStreamingErrorToStructured);
443
433
  eventStream$.sendActionExecutionResult({
444
434
  actionExecutionId,
445
435
  actionName: action.name,
@@ -480,65 +470,31 @@ async function executeAction(
480
470
  }
481
471
 
482
472
  function convertStreamingErrorToStructured(error: any): CopilotKitError {
483
- // Handle network termination errors
484
- if (
485
- error?.message?.includes("terminated") ||
486
- error?.cause?.code === "UND_ERR_SOCKET" ||
487
- error?.message?.includes("other side closed") ||
488
- error?.code === "UND_ERR_SOCKET"
489
- ) {
490
- return new CopilotKitError({
491
- message:
492
- "Connection to agent was unexpectedly terminated. This may be due to the agent service being restarted or network issues. Please try again.",
493
- code: CopilotKitErrorCode.NETWORK_ERROR,
494
- });
495
- }
473
+ // Determine a more helpful error message based on context
474
+ let helpfulMessage = generateHelpfulErrorMessage(error, "event streaming connection");
496
475
 
497
- // Handle other network-related errors
476
+ // For network-related errors, use CopilotKitLowLevelError to preserve the original error
498
477
  if (
499
478
  error?.message?.includes("fetch failed") ||
500
479
  error?.message?.includes("ECONNREFUSED") ||
501
480
  error?.message?.includes("ENOTFOUND") ||
502
- error?.message?.includes("ETIMEDOUT")
481
+ error?.message?.includes("ETIMEDOUT") ||
482
+ error?.message?.includes("terminated") ||
483
+ error?.cause?.code === "UND_ERR_SOCKET" ||
484
+ error?.message?.includes("other side closed") ||
485
+ error?.code === "UND_ERR_SOCKET"
503
486
  ) {
504
487
  return new CopilotKitLowLevelError({
505
488
  error: error instanceof Error ? error : new Error(String(error)),
506
489
  url: "event streaming connection",
507
- message:
508
- "Network error occurred during event streaming. Please check your connection and try again.",
509
- });
510
- }
511
-
512
- // Handle abort/cancellation errors (these are usually normal)
513
- if (
514
- error?.message?.includes("aborted") ||
515
- error?.message?.includes("canceled") ||
516
- error?.message?.includes("signal is aborted")
517
- ) {
518
- return new CopilotKitError({
519
- message: "Request was cancelled",
520
- code: CopilotKitErrorCode.UNKNOWN,
521
- });
522
- }
523
-
524
- // Handle API key errors (authentication/authorization issues)
525
- const errorMessage = error?.message || String(error);
526
- if (
527
- errorMessage.includes("401") ||
528
- errorMessage.toLowerCase().includes("api key") ||
529
- errorMessage.toLowerCase().includes("unauthorized") ||
530
- errorMessage.toLowerCase().includes("authentication") ||
531
- errorMessage.toLowerCase().includes("incorrect api key")
532
- ) {
533
- return new CopilotKitError({
534
- message: `Event streaming error: ${errorMessage}`,
535
- code: CopilotKitErrorCode.MISSING_PUBLIC_API_KEY_ERROR,
490
+ message: helpfulMessage,
536
491
  });
537
492
  }
538
493
 
539
- // Default: convert unknown streaming errors
494
+ // For all other errors, preserve the raw error in a basic CopilotKitError
540
495
  return new CopilotKitError({
541
- message: `Event streaming error: ${errorMessage}`,
496
+ message: helpfulMessage,
542
497
  code: CopilotKitErrorCode.UNKNOWN,
498
+ severity: Severity.CRITICAL,
543
499
  });
544
500
  }