@copilotkit/runtime 1.55.3-canary.1776260990 → 1.56.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 (49) hide show
  1. package/dist/index.cjs +1 -1
  2. package/dist/index.d.cts +1 -1
  3. package/dist/index.d.mts +1 -1
  4. package/dist/index.mjs +1 -1
  5. package/dist/lib/index.cjs +1 -1
  6. package/dist/lib/index.d.cts +1 -1
  7. package/dist/lib/index.d.mts +1 -1
  8. package/dist/lib/index.mjs +1 -1
  9. package/dist/lib/integrations/shared.cjs +1 -1
  10. package/dist/lib/integrations/shared.d.cts +1 -1
  11. package/dist/lib/integrations/shared.d.mts +1 -1
  12. package/dist/lib/integrations/shared.mjs +1 -1
  13. package/dist/lib/runtime/copilot-runtime.cjs +1 -0
  14. package/dist/lib/runtime/copilot-runtime.cjs.map +1 -1
  15. package/dist/lib/runtime/copilot-runtime.d.cts +13 -1
  16. package/dist/lib/runtime/copilot-runtime.d.cts.map +1 -1
  17. package/dist/lib/runtime/copilot-runtime.d.mts +13 -1
  18. package/dist/lib/runtime/copilot-runtime.d.mts.map +1 -1
  19. package/dist/lib/runtime/copilot-runtime.mjs +1 -0
  20. package/dist/lib/runtime/copilot-runtime.mjs.map +1 -1
  21. package/dist/package.cjs +3 -3
  22. package/dist/package.mjs +3 -3
  23. package/dist/v2/runtime/core/runtime.cjs +12 -0
  24. package/dist/v2/runtime/core/runtime.cjs.map +1 -1
  25. package/dist/v2/runtime/core/runtime.d.cts +10 -1
  26. package/dist/v2/runtime/core/runtime.d.cts.map +1 -1
  27. package/dist/v2/runtime/core/runtime.d.mts +10 -1
  28. package/dist/v2/runtime/core/runtime.d.mts.map +1 -1
  29. package/dist/v2/runtime/core/runtime.mjs +13 -1
  30. package/dist/v2/runtime/core/runtime.mjs.map +1 -1
  31. package/dist/v2/runtime/handlers/handle-run.cjs +7 -1
  32. package/dist/v2/runtime/handlers/handle-run.cjs.map +1 -1
  33. package/dist/v2/runtime/handlers/handle-run.mjs +7 -1
  34. package/dist/v2/runtime/handlers/handle-run.mjs.map +1 -1
  35. package/dist/v2/runtime/handlers/shared/sse-response.cjs +40 -1
  36. package/dist/v2/runtime/handlers/shared/sse-response.cjs.map +1 -1
  37. package/dist/v2/runtime/handlers/shared/sse-response.mjs +40 -1
  38. package/dist/v2/runtime/handlers/shared/sse-response.mjs.map +1 -1
  39. package/dist/v2/runtime/handlers/sse/run.cjs +3 -1
  40. package/dist/v2/runtime/handlers/sse/run.cjs.map +1 -1
  41. package/dist/v2/runtime/handlers/sse/run.mjs +3 -1
  42. package/dist/v2/runtime/handlers/sse/run.mjs.map +1 -1
  43. package/package.json +4 -4
  44. package/src/lib/runtime/copilot-runtime.ts +15 -0
  45. package/src/v2/runtime/__tests__/debug-sse-response.test.ts +302 -0
  46. package/src/v2/runtime/core/runtime.ts +27 -0
  47. package/src/v2/runtime/handlers/handle-run.ts +15 -1
  48. package/src/v2/runtime/handlers/shared/sse-response.ts +69 -0
  49. package/src/v2/runtime/handlers/sse/run.ts +9 -0
@@ -1 +1 @@
1
- {"version":3,"file":"sse-response.cjs","names":["EventEncoder"],"sources":["../../../../../src/v2/runtime/handlers/shared/sse-response.ts"],"sourcesContent":["import { BaseEvent } from \"@ag-ui/client\";\nimport { EventEncoder } from \"@ag-ui/encoder\";\nimport { Observable, Subscription } from \"rxjs\";\nimport { telemetry } from \"../../telemetry\";\n\ninterface CreateSseEventResponseParams {\n request: Request;\n observableFactory: () =>\n | Promise<Observable<BaseEvent>>\n | Observable<BaseEvent>;\n}\n\nexport function createSseEventResponse({\n request,\n observableFactory,\n}: CreateSseEventResponseParams): Response {\n const stream = new TransformStream();\n const writer = stream.writable.getWriter();\n const encoder = new EventEncoder();\n let streamClosed = false;\n\n const closeStream = async () => {\n if (!streamClosed) {\n try {\n await writer.close();\n streamClosed = true;\n } catch {\n // Stream already closed.\n }\n }\n };\n\n const logError = (error: unknown) => {\n console.error(\"Error running agent:\", error);\n console.error(\n \"Error stack:\",\n error instanceof Error ? error.stack : \"No stack trace\",\n );\n console.error(\"Error details:\", {\n name: error instanceof Error ? error.name : \"Unknown\",\n message: error instanceof Error ? error.message : String(error),\n cause: error instanceof Error ? error.cause : undefined,\n });\n };\n\n let subscription: Subscription | undefined;\n\n (async () => {\n const observable = await observableFactory();\n\n telemetry.capture(\"oss.runtime.agent_execution_stream_started\", {});\n\n subscription = observable.subscribe({\n next: async (event) => {\n if (!request.signal.aborted && !streamClosed) {\n try {\n await writer.write(encoder.encode(event));\n } catch (error) {\n if (error instanceof Error && error.name === \"AbortError\") {\n streamClosed = true;\n }\n }\n }\n },\n error: async (error) => {\n telemetry.capture(\"oss.runtime.agent_execution_stream_errored\", {\n error: error instanceof Error ? error.message : String(error),\n });\n logError(error);\n await closeStream();\n },\n complete: async () => {\n telemetry.capture(\"oss.runtime.agent_execution_stream_ended\", {});\n await closeStream();\n },\n });\n\n // If the client disconnected before the subscription was created,\n // unsubscribe immediately to avoid leaking the observable.\n if (request.signal.aborted) {\n subscription.unsubscribe();\n }\n })().catch(async (error) => {\n logError(error);\n await closeStream();\n });\n\n request.signal.addEventListener(\"abort\", () => {\n subscription?.unsubscribe();\n });\n\n return new Response(stream.readable, {\n status: 200,\n headers: {\n \"Content-Type\": \"text/event-stream\",\n \"Cache-Control\": \"no-cache\",\n Connection: \"keep-alive\",\n },\n });\n}\n"],"mappings":";;;;;;AAYA,SAAgB,uBAAuB,EACrC,SACA,qBACyC;CACzC,MAAM,SAAS,IAAI,iBAAiB;CACpC,MAAM,SAAS,OAAO,SAAS,WAAW;CAC1C,MAAM,UAAU,IAAIA,6BAAc;CAClC,IAAI,eAAe;CAEnB,MAAM,cAAc,YAAY;AAC9B,MAAI,CAAC,aACH,KAAI;AACF,SAAM,OAAO,OAAO;AACpB,kBAAe;UACT;;CAMZ,MAAM,YAAY,UAAmB;AACnC,UAAQ,MAAM,wBAAwB,MAAM;AAC5C,UAAQ,MACN,gBACA,iBAAiB,QAAQ,MAAM,QAAQ,iBACxC;AACD,UAAQ,MAAM,kBAAkB;GAC9B,MAAM,iBAAiB,QAAQ,MAAM,OAAO;GAC5C,SAAS,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;GAC/D,OAAO,iBAAiB,QAAQ,MAAM,QAAQ;GAC/C,CAAC;;CAGJ,IAAI;AAEJ,EAAC,YAAY;EACX,MAAM,aAAa,MAAM,mBAAmB;AAE5C,mCAAU,QAAQ,8CAA8C,EAAE,CAAC;AAEnE,iBAAe,WAAW,UAAU;GAClC,MAAM,OAAO,UAAU;AACrB,QAAI,CAAC,QAAQ,OAAO,WAAW,CAAC,aAC9B,KAAI;AACF,WAAM,OAAO,MAAM,QAAQ,OAAO,MAAM,CAAC;aAClC,OAAO;AACd,SAAI,iBAAiB,SAAS,MAAM,SAAS,aAC3C,gBAAe;;;GAKvB,OAAO,OAAO,UAAU;AACtB,qCAAU,QAAQ,8CAA8C,EAC9D,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM,EAC9D,CAAC;AACF,aAAS,MAAM;AACf,UAAM,aAAa;;GAErB,UAAU,YAAY;AACpB,qCAAU,QAAQ,4CAA4C,EAAE,CAAC;AACjE,UAAM,aAAa;;GAEtB,CAAC;AAIF,MAAI,QAAQ,OAAO,QACjB,cAAa,aAAa;KAE1B,CAAC,MAAM,OAAO,UAAU;AAC1B,WAAS,MAAM;AACf,QAAM,aAAa;GACnB;AAEF,SAAQ,OAAO,iBAAiB,eAAe;AAC7C,gBAAc,aAAa;GAC3B;AAEF,QAAO,IAAI,SAAS,OAAO,UAAU;EACnC,QAAQ;EACR,SAAS;GACP,gBAAgB;GAChB,iBAAiB;GACjB,YAAY;GACb;EACF,CAAC"}
1
+ {"version":3,"file":"sse-response.cjs","names":["EventEncoder","createLogger"],"sources":["../../../../../src/v2/runtime/handlers/shared/sse-response.ts"],"sourcesContent":["import { BaseEvent } from \"@ag-ui/client\";\nimport { EventEncoder } from \"@ag-ui/encoder\";\nimport { Observable, Subscription } from \"rxjs\";\nimport { ResolvedDebugConfig } from \"@copilotkit/shared\";\nimport {\n createLogger,\n type CopilotRuntimeLogger,\n} from \"../../../../lib/logger\";\nimport { telemetry } from \"../../telemetry\";\n\ninterface CreateSseEventResponseParams {\n request: Request;\n observableFactory: () =>\n | Promise<Observable<BaseEvent>>\n | Observable<BaseEvent>;\n debug?: ResolvedDebugConfig;\n /** Pre-created logger instance to avoid creating a new pino logger per request. */\n logger?: CopilotRuntimeLogger;\n}\n\nexport function createSseEventResponse({\n request,\n observableFactory,\n debug,\n logger,\n}: CreateSseEventResponseParams): Response {\n const stream = new TransformStream();\n const writer = stream.writable.getWriter();\n const encoder = new EventEncoder();\n let streamClosed = false;\n\n const debugLogger = debug?.enabled\n ? (logger ??\n createLogger({ level: \"debug\", component: \"copilotkit-debug\" }))\n : undefined;\n\n const closeStream = async () => {\n if (!streamClosed) {\n try {\n await writer.close();\n streamClosed = true;\n } catch {\n // Stream already closed.\n }\n }\n };\n\n const logError = (error: unknown) => {\n console.error(\"Error running agent:\", error);\n console.error(\n \"Error stack:\",\n error instanceof Error ? error.stack : \"No stack trace\",\n );\n console.error(\"Error details:\", {\n name: error instanceof Error ? error.name : \"Unknown\",\n message: error instanceof Error ? error.message : String(error),\n cause: error instanceof Error ? error.cause : undefined,\n });\n };\n\n let subscription: Subscription | undefined;\n\n (async () => {\n const observable = await observableFactory();\n\n telemetry.capture(\"oss.runtime.agent_execution_stream_started\", {});\n\n if (debug?.lifecycle) {\n debugLogger!.debug(\"SSE stream opened\");\n }\n\n let eventCount = 0;\n let loggedEventCount = 0;\n\n subscription = observable.subscribe({\n next: async (event) => {\n if (!request.signal.aborted && !streamClosed) {\n try {\n eventCount++;\n if (debug?.events) {\n loggedEventCount++;\n if (debug.verbose) {\n debugLogger!.debug({ event }, \"Event emitted\");\n } else {\n debugLogger!.debug(\n { type: event.type, ...summarizeEvent(event) },\n \"Event emitted\",\n );\n }\n }\n await writer.write(encoder.encode(event));\n } catch (error) {\n if (error instanceof Error && error.name === \"AbortError\") {\n streamClosed = true;\n }\n }\n }\n },\n error: async (error) => {\n telemetry.capture(\"oss.runtime.agent_execution_stream_errored\", {\n error: error instanceof Error ? error.message : String(error),\n });\n if (debug?.lifecycle) {\n debugLogger!.debug(\n { error: error instanceof Error ? error.message : String(error) },\n \"SSE stream errored\",\n );\n }\n logError(error);\n await closeStream();\n },\n complete: async () => {\n telemetry.capture(\"oss.runtime.agent_execution_stream_ended\", {});\n if (debug?.lifecycle) {\n debugLogger!.debug(\n { eventCount, loggedEventCount },\n \"SSE stream completed\",\n );\n }\n await closeStream();\n },\n });\n\n // If the client disconnected before the subscription was created,\n // unsubscribe immediately to avoid leaking the observable.\n if (request.signal.aborted) {\n subscription.unsubscribe();\n }\n })().catch(async (error) => {\n logError(error);\n await closeStream();\n });\n\n request.signal.addEventListener(\"abort\", () => {\n subscription?.unsubscribe();\n });\n\n return new Response(stream.readable, {\n status: 200,\n headers: {\n \"Content-Type\": \"text/event-stream\",\n \"Cache-Control\": \"no-cache\",\n Connection: \"keep-alive\",\n },\n });\n}\n\nfunction summarizeEvent(event: BaseEvent): Record<string, unknown> {\n const e = event as any;\n const summary: Record<string, unknown> = {};\n\n if (e.messageId) summary.messageId = e.messageId;\n if (e.toolCallId) summary.toolCallId = e.toolCallId;\n if (e.toolCallName) summary.toolCallName = e.toolCallName;\n if (e.role) summary.role = e.role;\n if (e.delta != null && typeof e.delta === \"string\")\n summary.deltaLength = e.delta.length;\n if (e.snapshot && typeof e.snapshot === \"object\")\n summary.snapshotKeys = Object.keys(e.snapshot);\n if (e.delta && Array.isArray(e.delta))\n summary.operationCount = e.delta.length;\n if (e.threadId) summary.threadId = e.threadId;\n if (e.runId) summary.runId = e.runId;\n if (e.message) summary.message = e.message;\n if (e.code) summary.code = e.code;\n if (e.stepName) summary.stepName = e.stepName;\n\n return summary;\n}\n"],"mappings":"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"}
@@ -1,13 +1,18 @@
1
1
  import "reflect-metadata";
2
+ import { createLogger } from "../../../../lib/logger.mjs";
2
3
  import telemetry from "../../telemetry/telemetry-client.mjs";
3
4
  import { EventEncoder } from "@ag-ui/encoder";
4
5
 
5
6
  //#region src/v2/runtime/handlers/shared/sse-response.ts
6
- function createSseEventResponse({ request, observableFactory }) {
7
+ function createSseEventResponse({ request, observableFactory, debug, logger }) {
7
8
  const stream = new TransformStream();
8
9
  const writer = stream.writable.getWriter();
9
10
  const encoder = new EventEncoder();
10
11
  let streamClosed = false;
12
+ const debugLogger = debug?.enabled ? logger ?? createLogger({
13
+ level: "debug",
14
+ component: "copilotkit-debug"
15
+ }) : void 0;
11
16
  const closeStream = async () => {
12
17
  if (!streamClosed) try {
13
18
  await writer.close();
@@ -27,9 +32,21 @@ function createSseEventResponse({ request, observableFactory }) {
27
32
  (async () => {
28
33
  const observable = await observableFactory();
29
34
  telemetry.capture("oss.runtime.agent_execution_stream_started", {});
35
+ if (debug?.lifecycle) debugLogger.debug("SSE stream opened");
36
+ let eventCount = 0;
37
+ let loggedEventCount = 0;
30
38
  subscription = observable.subscribe({
31
39
  next: async (event) => {
32
40
  if (!request.signal.aborted && !streamClosed) try {
41
+ eventCount++;
42
+ if (debug?.events) {
43
+ loggedEventCount++;
44
+ if (debug.verbose) debugLogger.debug({ event }, "Event emitted");
45
+ else debugLogger.debug({
46
+ type: event.type,
47
+ ...summarizeEvent(event)
48
+ }, "Event emitted");
49
+ }
33
50
  await writer.write(encoder.encode(event));
34
51
  } catch (error) {
35
52
  if (error instanceof Error && error.name === "AbortError") streamClosed = true;
@@ -37,11 +54,16 @@ function createSseEventResponse({ request, observableFactory }) {
37
54
  },
38
55
  error: async (error) => {
39
56
  telemetry.capture("oss.runtime.agent_execution_stream_errored", { error: error instanceof Error ? error.message : String(error) });
57
+ if (debug?.lifecycle) debugLogger.debug({ error: error instanceof Error ? error.message : String(error) }, "SSE stream errored");
40
58
  logError(error);
41
59
  await closeStream();
42
60
  },
43
61
  complete: async () => {
44
62
  telemetry.capture("oss.runtime.agent_execution_stream_ended", {});
63
+ if (debug?.lifecycle) debugLogger.debug({
64
+ eventCount,
65
+ loggedEventCount
66
+ }, "SSE stream completed");
45
67
  await closeStream();
46
68
  }
47
69
  });
@@ -62,6 +84,23 @@ function createSseEventResponse({ request, observableFactory }) {
62
84
  }
63
85
  });
64
86
  }
87
+ function summarizeEvent(event) {
88
+ const e = event;
89
+ const summary = {};
90
+ if (e.messageId) summary.messageId = e.messageId;
91
+ if (e.toolCallId) summary.toolCallId = e.toolCallId;
92
+ if (e.toolCallName) summary.toolCallName = e.toolCallName;
93
+ if (e.role) summary.role = e.role;
94
+ if (e.delta != null && typeof e.delta === "string") summary.deltaLength = e.delta.length;
95
+ if (e.snapshot && typeof e.snapshot === "object") summary.snapshotKeys = Object.keys(e.snapshot);
96
+ if (e.delta && Array.isArray(e.delta)) summary.operationCount = e.delta.length;
97
+ if (e.threadId) summary.threadId = e.threadId;
98
+ if (e.runId) summary.runId = e.runId;
99
+ if (e.message) summary.message = e.message;
100
+ if (e.code) summary.code = e.code;
101
+ if (e.stepName) summary.stepName = e.stepName;
102
+ return summary;
103
+ }
65
104
 
66
105
  //#endregion
67
106
  export { createSseEventResponse };
@@ -1 +1 @@
1
- {"version":3,"file":"sse-response.mjs","names":[],"sources":["../../../../../src/v2/runtime/handlers/shared/sse-response.ts"],"sourcesContent":["import { BaseEvent } from \"@ag-ui/client\";\nimport { EventEncoder } from \"@ag-ui/encoder\";\nimport { Observable, Subscription } from \"rxjs\";\nimport { telemetry } from \"../../telemetry\";\n\ninterface CreateSseEventResponseParams {\n request: Request;\n observableFactory: () =>\n | Promise<Observable<BaseEvent>>\n | Observable<BaseEvent>;\n}\n\nexport function createSseEventResponse({\n request,\n observableFactory,\n}: CreateSseEventResponseParams): Response {\n const stream = new TransformStream();\n const writer = stream.writable.getWriter();\n const encoder = new EventEncoder();\n let streamClosed = false;\n\n const closeStream = async () => {\n if (!streamClosed) {\n try {\n await writer.close();\n streamClosed = true;\n } catch {\n // Stream already closed.\n }\n }\n };\n\n const logError = (error: unknown) => {\n console.error(\"Error running agent:\", error);\n console.error(\n \"Error stack:\",\n error instanceof Error ? error.stack : \"No stack trace\",\n );\n console.error(\"Error details:\", {\n name: error instanceof Error ? error.name : \"Unknown\",\n message: error instanceof Error ? error.message : String(error),\n cause: error instanceof Error ? error.cause : undefined,\n });\n };\n\n let subscription: Subscription | undefined;\n\n (async () => {\n const observable = await observableFactory();\n\n telemetry.capture(\"oss.runtime.agent_execution_stream_started\", {});\n\n subscription = observable.subscribe({\n next: async (event) => {\n if (!request.signal.aborted && !streamClosed) {\n try {\n await writer.write(encoder.encode(event));\n } catch (error) {\n if (error instanceof Error && error.name === \"AbortError\") {\n streamClosed = true;\n }\n }\n }\n },\n error: async (error) => {\n telemetry.capture(\"oss.runtime.agent_execution_stream_errored\", {\n error: error instanceof Error ? error.message : String(error),\n });\n logError(error);\n await closeStream();\n },\n complete: async () => {\n telemetry.capture(\"oss.runtime.agent_execution_stream_ended\", {});\n await closeStream();\n },\n });\n\n // If the client disconnected before the subscription was created,\n // unsubscribe immediately to avoid leaking the observable.\n if (request.signal.aborted) {\n subscription.unsubscribe();\n }\n })().catch(async (error) => {\n logError(error);\n await closeStream();\n });\n\n request.signal.addEventListener(\"abort\", () => {\n subscription?.unsubscribe();\n });\n\n return new Response(stream.readable, {\n status: 200,\n headers: {\n \"Content-Type\": \"text/event-stream\",\n \"Cache-Control\": \"no-cache\",\n Connection: \"keep-alive\",\n },\n });\n}\n"],"mappings":";;;;;AAYA,SAAgB,uBAAuB,EACrC,SACA,qBACyC;CACzC,MAAM,SAAS,IAAI,iBAAiB;CACpC,MAAM,SAAS,OAAO,SAAS,WAAW;CAC1C,MAAM,UAAU,IAAI,cAAc;CAClC,IAAI,eAAe;CAEnB,MAAM,cAAc,YAAY;AAC9B,MAAI,CAAC,aACH,KAAI;AACF,SAAM,OAAO,OAAO;AACpB,kBAAe;UACT;;CAMZ,MAAM,YAAY,UAAmB;AACnC,UAAQ,MAAM,wBAAwB,MAAM;AAC5C,UAAQ,MACN,gBACA,iBAAiB,QAAQ,MAAM,QAAQ,iBACxC;AACD,UAAQ,MAAM,kBAAkB;GAC9B,MAAM,iBAAiB,QAAQ,MAAM,OAAO;GAC5C,SAAS,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;GAC/D,OAAO,iBAAiB,QAAQ,MAAM,QAAQ;GAC/C,CAAC;;CAGJ,IAAI;AAEJ,EAAC,YAAY;EACX,MAAM,aAAa,MAAM,mBAAmB;AAE5C,YAAU,QAAQ,8CAA8C,EAAE,CAAC;AAEnE,iBAAe,WAAW,UAAU;GAClC,MAAM,OAAO,UAAU;AACrB,QAAI,CAAC,QAAQ,OAAO,WAAW,CAAC,aAC9B,KAAI;AACF,WAAM,OAAO,MAAM,QAAQ,OAAO,MAAM,CAAC;aAClC,OAAO;AACd,SAAI,iBAAiB,SAAS,MAAM,SAAS,aAC3C,gBAAe;;;GAKvB,OAAO,OAAO,UAAU;AACtB,cAAU,QAAQ,8CAA8C,EAC9D,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM,EAC9D,CAAC;AACF,aAAS,MAAM;AACf,UAAM,aAAa;;GAErB,UAAU,YAAY;AACpB,cAAU,QAAQ,4CAA4C,EAAE,CAAC;AACjE,UAAM,aAAa;;GAEtB,CAAC;AAIF,MAAI,QAAQ,OAAO,QACjB,cAAa,aAAa;KAE1B,CAAC,MAAM,OAAO,UAAU;AAC1B,WAAS,MAAM;AACf,QAAM,aAAa;GACnB;AAEF,SAAQ,OAAO,iBAAiB,eAAe;AAC7C,gBAAc,aAAa;GAC3B;AAEF,QAAO,IAAI,SAAS,OAAO,UAAU;EACnC,QAAQ;EACR,SAAS;GACP,gBAAgB;GAChB,iBAAiB;GACjB,YAAY;GACb;EACF,CAAC"}
1
+ {"version":3,"file":"sse-response.mjs","names":[],"sources":["../../../../../src/v2/runtime/handlers/shared/sse-response.ts"],"sourcesContent":["import { BaseEvent } from \"@ag-ui/client\";\nimport { EventEncoder } from \"@ag-ui/encoder\";\nimport { Observable, Subscription } from \"rxjs\";\nimport { ResolvedDebugConfig } from \"@copilotkit/shared\";\nimport {\n createLogger,\n type CopilotRuntimeLogger,\n} from \"../../../../lib/logger\";\nimport { telemetry } from \"../../telemetry\";\n\ninterface CreateSseEventResponseParams {\n request: Request;\n observableFactory: () =>\n | Promise<Observable<BaseEvent>>\n | Observable<BaseEvent>;\n debug?: ResolvedDebugConfig;\n /** Pre-created logger instance to avoid creating a new pino logger per request. */\n logger?: CopilotRuntimeLogger;\n}\n\nexport function createSseEventResponse({\n request,\n observableFactory,\n debug,\n logger,\n}: CreateSseEventResponseParams): Response {\n const stream = new TransformStream();\n const writer = stream.writable.getWriter();\n const encoder = new EventEncoder();\n let streamClosed = false;\n\n const debugLogger = debug?.enabled\n ? (logger ??\n createLogger({ level: \"debug\", component: \"copilotkit-debug\" }))\n : undefined;\n\n const closeStream = async () => {\n if (!streamClosed) {\n try {\n await writer.close();\n streamClosed = true;\n } catch {\n // Stream already closed.\n }\n }\n };\n\n const logError = (error: unknown) => {\n console.error(\"Error running agent:\", error);\n console.error(\n \"Error stack:\",\n error instanceof Error ? error.stack : \"No stack trace\",\n );\n console.error(\"Error details:\", {\n name: error instanceof Error ? error.name : \"Unknown\",\n message: error instanceof Error ? error.message : String(error),\n cause: error instanceof Error ? error.cause : undefined,\n });\n };\n\n let subscription: Subscription | undefined;\n\n (async () => {\n const observable = await observableFactory();\n\n telemetry.capture(\"oss.runtime.agent_execution_stream_started\", {});\n\n if (debug?.lifecycle) {\n debugLogger!.debug(\"SSE stream opened\");\n }\n\n let eventCount = 0;\n let loggedEventCount = 0;\n\n subscription = observable.subscribe({\n next: async (event) => {\n if (!request.signal.aborted && !streamClosed) {\n try {\n eventCount++;\n if (debug?.events) {\n loggedEventCount++;\n if (debug.verbose) {\n debugLogger!.debug({ event }, \"Event emitted\");\n } else {\n debugLogger!.debug(\n { type: event.type, ...summarizeEvent(event) },\n \"Event emitted\",\n );\n }\n }\n await writer.write(encoder.encode(event));\n } catch (error) {\n if (error instanceof Error && error.name === \"AbortError\") {\n streamClosed = true;\n }\n }\n }\n },\n error: async (error) => {\n telemetry.capture(\"oss.runtime.agent_execution_stream_errored\", {\n error: error instanceof Error ? error.message : String(error),\n });\n if (debug?.lifecycle) {\n debugLogger!.debug(\n { error: error instanceof Error ? error.message : String(error) },\n \"SSE stream errored\",\n );\n }\n logError(error);\n await closeStream();\n },\n complete: async () => {\n telemetry.capture(\"oss.runtime.agent_execution_stream_ended\", {});\n if (debug?.lifecycle) {\n debugLogger!.debug(\n { eventCount, loggedEventCount },\n \"SSE stream completed\",\n );\n }\n await closeStream();\n },\n });\n\n // If the client disconnected before the subscription was created,\n // unsubscribe immediately to avoid leaking the observable.\n if (request.signal.aborted) {\n subscription.unsubscribe();\n }\n })().catch(async (error) => {\n logError(error);\n await closeStream();\n });\n\n request.signal.addEventListener(\"abort\", () => {\n subscription?.unsubscribe();\n });\n\n return new Response(stream.readable, {\n status: 200,\n headers: {\n \"Content-Type\": \"text/event-stream\",\n \"Cache-Control\": \"no-cache\",\n Connection: \"keep-alive\",\n },\n });\n}\n\nfunction summarizeEvent(event: BaseEvent): Record<string, unknown> {\n const e = event as any;\n const summary: Record<string, unknown> = {};\n\n if (e.messageId) summary.messageId = e.messageId;\n if (e.toolCallId) summary.toolCallId = e.toolCallId;\n if (e.toolCallName) summary.toolCallName = e.toolCallName;\n if (e.role) summary.role = e.role;\n if (e.delta != null && typeof e.delta === \"string\")\n summary.deltaLength = e.delta.length;\n if (e.snapshot && typeof e.snapshot === \"object\")\n summary.snapshotKeys = Object.keys(e.snapshot);\n if (e.delta && Array.isArray(e.delta))\n summary.operationCount = e.delta.length;\n if (e.threadId) summary.threadId = e.threadId;\n if (e.runId) summary.runId = e.runId;\n if (e.message) summary.message = e.message;\n if (e.code) summary.code = e.code;\n if (e.stepName) summary.stepName = e.stepName;\n\n return summary;\n}\n"],"mappings":"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"}
@@ -2,9 +2,11 @@ require("reflect-metadata");
2
2
  const require_sse_response = require('../shared/sse-response.cjs');
3
3
 
4
4
  //#region src/v2/runtime/handlers/sse/run.ts
5
- function handleSseRun({ runtime, request, agent, input }) {
5
+ function handleSseRun({ runtime, request, agent, input, debug, logger }) {
6
6
  return require_sse_response.createSseEventResponse({
7
7
  request,
8
+ debug,
9
+ logger,
8
10
  observableFactory: () => runtime.runner.run({
9
11
  threadId: input.threadId,
10
12
  agent,
@@ -1 +1 @@
1
- {"version":3,"file":"run.cjs","names":["createSseEventResponse"],"sources":["../../../../../src/v2/runtime/handlers/sse/run.ts"],"sourcesContent":["import { AbstractAgent, RunAgentInput } from \"@ag-ui/client\";\nimport { CopilotRuntimeLike } from \"../../core/runtime\";\nimport { createSseEventResponse } from \"../shared/sse-response\";\n\ninterface HandleSseRunParams {\n runtime: CopilotRuntimeLike;\n request: Request;\n agent: AbstractAgent;\n input: RunAgentInput;\n}\n\nexport function handleSseRun({\n runtime,\n request,\n agent,\n input,\n}: HandleSseRunParams): Response {\n return createSseEventResponse({\n request,\n observableFactory: () =>\n runtime.runner.run({\n threadId: input.threadId,\n agent,\n input,\n }),\n });\n}\n"],"mappings":";;;;AAWA,SAAgB,aAAa,EAC3B,SACA,SACA,OACA,SAC+B;AAC/B,QAAOA,4CAAuB;EAC5B;EACA,yBACE,QAAQ,OAAO,IAAI;GACjB,UAAU,MAAM;GAChB;GACA;GACD,CAAC;EACL,CAAC"}
1
+ {"version":3,"file":"run.cjs","names":["createSseEventResponse"],"sources":["../../../../../src/v2/runtime/handlers/sse/run.ts"],"sourcesContent":["import { AbstractAgent, RunAgentInput } from \"@ag-ui/client\";\nimport { ResolvedDebugConfig } from \"@copilotkit/shared\";\nimport { type CopilotRuntimeLogger } from \"../../../../lib/logger\";\nimport { CopilotRuntimeLike } from \"../../core/runtime\";\nimport { createSseEventResponse } from \"../shared/sse-response\";\n\ninterface HandleSseRunParams {\n runtime: CopilotRuntimeLike;\n request: Request;\n agent: AbstractAgent;\n input: RunAgentInput;\n debug?: ResolvedDebugConfig;\n /** Pre-created logger instance to avoid creating a new pino logger per request. */\n logger?: CopilotRuntimeLogger;\n}\n\nexport function handleSseRun({\n runtime,\n request,\n agent,\n input,\n debug,\n logger,\n}: HandleSseRunParams): Response {\n return createSseEventResponse({\n request,\n debug,\n logger,\n observableFactory: () =>\n runtime.runner.run({\n threadId: input.threadId,\n agent,\n input,\n }),\n });\n}\n"],"mappings":";;;;AAgBA,SAAgB,aAAa,EAC3B,SACA,SACA,OACA,OACA,OACA,UAC+B;AAC/B,QAAOA,4CAAuB;EAC5B;EACA;EACA;EACA,yBACE,QAAQ,OAAO,IAAI;GACjB,UAAU,MAAM;GAChB;GACA;GACD,CAAC;EACL,CAAC"}
@@ -2,9 +2,11 @@ import "reflect-metadata";
2
2
  import { createSseEventResponse } from "../shared/sse-response.mjs";
3
3
 
4
4
  //#region src/v2/runtime/handlers/sse/run.ts
5
- function handleSseRun({ runtime, request, agent, input }) {
5
+ function handleSseRun({ runtime, request, agent, input, debug, logger }) {
6
6
  return createSseEventResponse({
7
7
  request,
8
+ debug,
9
+ logger,
8
10
  observableFactory: () => runtime.runner.run({
9
11
  threadId: input.threadId,
10
12
  agent,
@@ -1 +1 @@
1
- {"version":3,"file":"run.mjs","names":[],"sources":["../../../../../src/v2/runtime/handlers/sse/run.ts"],"sourcesContent":["import { AbstractAgent, RunAgentInput } from \"@ag-ui/client\";\nimport { CopilotRuntimeLike } from \"../../core/runtime\";\nimport { createSseEventResponse } from \"../shared/sse-response\";\n\ninterface HandleSseRunParams {\n runtime: CopilotRuntimeLike;\n request: Request;\n agent: AbstractAgent;\n input: RunAgentInput;\n}\n\nexport function handleSseRun({\n runtime,\n request,\n agent,\n input,\n}: HandleSseRunParams): Response {\n return createSseEventResponse({\n request,\n observableFactory: () =>\n runtime.runner.run({\n threadId: input.threadId,\n agent,\n input,\n }),\n });\n}\n"],"mappings":";;;;AAWA,SAAgB,aAAa,EAC3B,SACA,SACA,OACA,SAC+B;AAC/B,QAAO,uBAAuB;EAC5B;EACA,yBACE,QAAQ,OAAO,IAAI;GACjB,UAAU,MAAM;GAChB;GACA;GACD,CAAC;EACL,CAAC"}
1
+ {"version":3,"file":"run.mjs","names":[],"sources":["../../../../../src/v2/runtime/handlers/sse/run.ts"],"sourcesContent":["import { AbstractAgent, RunAgentInput } from \"@ag-ui/client\";\nimport { ResolvedDebugConfig } from \"@copilotkit/shared\";\nimport { type CopilotRuntimeLogger } from \"../../../../lib/logger\";\nimport { CopilotRuntimeLike } from \"../../core/runtime\";\nimport { createSseEventResponse } from \"../shared/sse-response\";\n\ninterface HandleSseRunParams {\n runtime: CopilotRuntimeLike;\n request: Request;\n agent: AbstractAgent;\n input: RunAgentInput;\n debug?: ResolvedDebugConfig;\n /** Pre-created logger instance to avoid creating a new pino logger per request. */\n logger?: CopilotRuntimeLogger;\n}\n\nexport function handleSseRun({\n runtime,\n request,\n agent,\n input,\n debug,\n logger,\n}: HandleSseRunParams): Response {\n return createSseEventResponse({\n request,\n debug,\n logger,\n observableFactory: () =>\n runtime.runner.run({\n threadId: input.threadId,\n agent,\n input,\n }),\n });\n}\n"],"mappings":";;;;AAgBA,SAAgB,aAAa,EAC3B,SACA,SACA,OACA,OACA,OACA,UAC+B;AAC/B,QAAO,uBAAuB;EAC5B;EACA;EACA;EACA,yBACE,QAAQ,OAAO,IAAI;GACjB,UAAU,MAAM;GAChB;GACA;GACD,CAAC;EACL,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@copilotkit/runtime",
3
- "version": "1.55.3-canary.1776260990",
3
+ "version": "1.56.0",
4
4
  "private": false,
5
5
  "keywords": [
6
6
  "ai",
@@ -76,11 +76,11 @@
76
76
  "access": "public"
77
77
  },
78
78
  "dependencies": {
79
- "@ag-ui/a2ui-middleware": "0.0.4",
79
+ "@ag-ui/a2ui-middleware": "0.0.5",
80
80
  "@ag-ui/client": "0.0.52",
81
81
  "@ag-ui/core": "0.0.52",
82
82
  "@ag-ui/encoder": "0.0.52",
83
- "@ag-ui/langgraph": "0.0.27",
83
+ "@ag-ui/langgraph": "0.0.29",
84
84
  "@ag-ui/mcp-apps-middleware": "0.0.3",
85
85
  "@ai-sdk/anthropic": "^3.0.49",
86
86
  "@ai-sdk/google": "^3.0.33",
@@ -115,7 +115,7 @@
115
115
  "uuid": "^10.0.0",
116
116
  "ws": "^8.18.0",
117
117
  "zod": "^3.23.3",
118
- "@copilotkit/shared": "1.55.3-canary.1776260990"
118
+ "@copilotkit/shared": "1.56.0"
119
119
  },
120
120
  "devDependencies": {
121
121
  "@copilotkit/aimock": "^1.10.0",
@@ -23,6 +23,7 @@ import {
23
23
  getZodParameters,
24
24
  type PartialBy,
25
25
  isTelemetryDisabled,
26
+ type DebugConfig,
26
27
  } from "@copilotkit/shared";
27
28
  import type { RunAgentInput } from "@ag-ui/core";
28
29
  import { aguiToGQL } from "../../graphql/message-conversion/agui-to-gql";
@@ -292,6 +293,19 @@ export interface CopilotRuntimeConstructorParams_BASE<
292
293
 
293
294
  onStopGeneration?: OnStopGenerationHandler;
294
295
 
296
+ /**
297
+ * Enable debug logging for the runtime event pipeline.
298
+ * Pass `true` for full output, or an object for granular control:
299
+ *
300
+ * ```ts
301
+ * const runtime = new CopilotRuntime({
302
+ * debug: true,
303
+ * // or: debug: { events: true, lifecycle: true, verbose: false }
304
+ * });
305
+ * ```
306
+ */
307
+ debug?: DebugConfig;
308
+
295
309
  // /** Optional transcription service for audio processing. */
296
310
  // transcriptionService?: CopilotRuntimeOptionsVNext["transcriptionService"];
297
311
  // /** Optional *before* middleware – callback function or webhook URL. */
@@ -377,6 +391,7 @@ export class CopilotRuntime<const T extends Parameter[] | [] = []> {
377
391
  agents: mergedAgents,
378
392
  runner,
379
393
  licenseToken: params?.licenseToken,
394
+ debug: params?.debug,
380
395
  // TODO: add support for transcriptionService from CopilotRuntimeOptionsVNext once it is ready
381
396
  // transcriptionService: params?.transcriptionService,
382
397
 
@@ -0,0 +1,302 @@
1
+ import { describe, it, expect, vi, beforeEach } from "vitest";
2
+ import { Observable, of } from "rxjs";
3
+ import { BaseEvent, EventType } from "@ag-ui/client";
4
+ import type { ResolvedDebugConfig } from "@copilotkit/shared";
5
+
6
+ const mockDebug = vi.fn();
7
+
8
+ vi.mock("pino", () => ({
9
+ default: vi.fn(() => ({
10
+ child: vi.fn(() => ({ debug: mockDebug })),
11
+ debug: mockDebug,
12
+ })),
13
+ }));
14
+
15
+ vi.mock("pino-pretty", () => ({ default: vi.fn() }));
16
+
17
+ vi.mock("../../telemetry", () => ({
18
+ telemetry: { capture: vi.fn() },
19
+ }));
20
+
21
+ import pino from "pino";
22
+ import { createSseEventResponse } from "../handlers/shared/sse-response";
23
+
24
+ function createTestObservable(events: BaseEvent[]): Observable<BaseEvent> {
25
+ return of(...events);
26
+ }
27
+
28
+ function createMockRequest(): Request {
29
+ return new Request("https://example.com/agent/test/run", {
30
+ method: "POST",
31
+ });
32
+ }
33
+
34
+ async function drainResponse(response: Response): Promise<void> {
35
+ const reader = response.body!.getReader();
36
+ while (true) {
37
+ const { done } = await reader.read();
38
+ if (done) break;
39
+ }
40
+ }
41
+
42
+ describe("createSseEventResponse debug logging", () => {
43
+ beforeEach(() => {
44
+ mockDebug.mockClear();
45
+ });
46
+
47
+ it("does not log when debug is undefined", async () => {
48
+ const response = createSseEventResponse({
49
+ request: createMockRequest(),
50
+ observableFactory: () => createTestObservable([]),
51
+ });
52
+
53
+ await drainResponse(response);
54
+
55
+ expect(mockDebug).not.toHaveBeenCalled();
56
+ });
57
+
58
+ it("logs lifecycle message on stream open", async () => {
59
+ const debug: ResolvedDebugConfig = {
60
+ enabled: true,
61
+ events: false,
62
+ lifecycle: true,
63
+ verbose: false,
64
+ };
65
+
66
+ const event: BaseEvent = {
67
+ type: EventType.RUN_STARTED,
68
+ threadId: "t-1",
69
+ runId: "r-1",
70
+ } as BaseEvent;
71
+
72
+ const response = createSseEventResponse({
73
+ request: createMockRequest(),
74
+ observableFactory: () => createTestObservable([event]),
75
+ debug,
76
+ });
77
+
78
+ await drainResponse(response);
79
+
80
+ expect(mockDebug).toHaveBeenCalledWith("SSE stream opened");
81
+ });
82
+
83
+ it("logs lifecycle completion with total eventCount even when event logging is off", async () => {
84
+ const debug: ResolvedDebugConfig = {
85
+ enabled: true,
86
+ events: false,
87
+ lifecycle: true,
88
+ verbose: false,
89
+ };
90
+
91
+ const events: BaseEvent[] = [
92
+ {
93
+ type: EventType.RUN_STARTED,
94
+ threadId: "t-1",
95
+ runId: "r-1",
96
+ } as BaseEvent,
97
+ {
98
+ type: EventType.RUN_FINISHED,
99
+ threadId: "t-1",
100
+ runId: "r-1",
101
+ } as BaseEvent,
102
+ ];
103
+
104
+ const response = createSseEventResponse({
105
+ request: createMockRequest(),
106
+ observableFactory: () => createTestObservable(events),
107
+ debug,
108
+ });
109
+
110
+ await drainResponse(response);
111
+
112
+ expect(mockDebug).toHaveBeenCalledWith(
113
+ { eventCount: 2, loggedEventCount: 0 },
114
+ "SSE stream completed",
115
+ );
116
+ });
117
+
118
+ it("logs lifecycle completion with matching eventCount and loggedEventCount when events enabled", async () => {
119
+ const debug: ResolvedDebugConfig = {
120
+ enabled: true,
121
+ events: true,
122
+ lifecycle: true,
123
+ verbose: false,
124
+ };
125
+
126
+ const events: BaseEvent[] = [
127
+ {
128
+ type: EventType.RUN_STARTED,
129
+ threadId: "t-1",
130
+ runId: "r-1",
131
+ } as BaseEvent,
132
+ {
133
+ type: EventType.RUN_FINISHED,
134
+ threadId: "t-1",
135
+ runId: "r-1",
136
+ } as BaseEvent,
137
+ ];
138
+
139
+ const response = createSseEventResponse({
140
+ request: createMockRequest(),
141
+ observableFactory: () => createTestObservable(events),
142
+ debug,
143
+ });
144
+
145
+ await drainResponse(response);
146
+
147
+ expect(mockDebug).toHaveBeenCalledWith(
148
+ { eventCount: 2, loggedEventCount: 2 },
149
+ "SSE stream completed",
150
+ );
151
+ });
152
+
153
+ it("logs events in summary mode when verbose is false", async () => {
154
+ const debug: ResolvedDebugConfig = {
155
+ enabled: true,
156
+ events: true,
157
+ lifecycle: false,
158
+ verbose: false,
159
+ };
160
+
161
+ const event: BaseEvent = {
162
+ type: EventType.TEXT_MESSAGE_START,
163
+ messageId: "msg-1",
164
+ role: "assistant",
165
+ } as BaseEvent;
166
+
167
+ const response = createSseEventResponse({
168
+ request: createMockRequest(),
169
+ observableFactory: () => createTestObservable([event]),
170
+ debug,
171
+ });
172
+
173
+ await drainResponse(response);
174
+
175
+ const eventEmittedCalls = mockDebug.mock.calls.filter(
176
+ (call) => call[call.length - 1] === "Event emitted",
177
+ );
178
+ expect(eventEmittedCalls.length).toBeGreaterThanOrEqual(1);
179
+
180
+ const [summaryArg] = eventEmittedCalls[0];
181
+ expect(summaryArg).toHaveProperty("type", EventType.TEXT_MESSAGE_START);
182
+ expect(summaryArg).toHaveProperty("messageId", "msg-1");
183
+ // In summary mode the full event object is not passed directly
184
+ expect(summaryArg).not.toHaveProperty("event");
185
+ });
186
+
187
+ it("logs events in verbose mode with full event object", async () => {
188
+ const debug: ResolvedDebugConfig = {
189
+ enabled: true,
190
+ events: true,
191
+ lifecycle: false,
192
+ verbose: true,
193
+ };
194
+
195
+ const event: BaseEvent = {
196
+ type: EventType.TEXT_MESSAGE_START,
197
+ messageId: "msg-1",
198
+ role: "assistant",
199
+ } as BaseEvent;
200
+
201
+ const response = createSseEventResponse({
202
+ request: createMockRequest(),
203
+ observableFactory: () => createTestObservable([event]),
204
+ debug,
205
+ });
206
+
207
+ await drainResponse(response);
208
+
209
+ const eventEmittedCalls = mockDebug.mock.calls.filter(
210
+ (call) => call[call.length - 1] === "Event emitted",
211
+ );
212
+ expect(eventEmittedCalls.length).toBeGreaterThanOrEqual(1);
213
+
214
+ const [verboseArg] = eventEmittedCalls[0];
215
+ expect(verboseArg).toHaveProperty("event");
216
+ expect(verboseArg.event).toEqual(event);
217
+ });
218
+
219
+ it("uses a pre-created logger instead of calling createLogger when one is provided", async () => {
220
+ const debug: ResolvedDebugConfig = {
221
+ enabled: true,
222
+ events: false,
223
+ lifecycle: true,
224
+ verbose: false,
225
+ };
226
+
227
+ const externalDebug = vi.fn();
228
+ const externalLogger = { debug: externalDebug } as any;
229
+
230
+ // Clear the pino mock call count so we can assert it was NOT called again
231
+ const pinoMock = pino as unknown as ReturnType<typeof vi.fn>;
232
+ pinoMock.mockClear();
233
+
234
+ const event: BaseEvent = {
235
+ type: EventType.RUN_STARTED,
236
+ threadId: "t-1",
237
+ runId: "r-1",
238
+ } as BaseEvent;
239
+
240
+ const response = createSseEventResponse({
241
+ request: createMockRequest(),
242
+ observableFactory: () => createTestObservable([event]),
243
+ debug,
244
+ logger: externalLogger,
245
+ });
246
+
247
+ await drainResponse(response);
248
+
249
+ // The external logger should have been used for lifecycle messages
250
+ expect(externalDebug).toHaveBeenCalledWith("SSE stream opened");
251
+ expect(externalDebug).toHaveBeenCalledWith(
252
+ { eventCount: 1, loggedEventCount: 0 },
253
+ "SSE stream completed",
254
+ );
255
+
256
+ // pino should NOT have been called – the pre-created logger was reused
257
+ expect(pinoMock).not.toHaveBeenCalled();
258
+ });
259
+
260
+ it("does not log events when events is disabled", async () => {
261
+ const debug: ResolvedDebugConfig = {
262
+ enabled: true,
263
+ events: false,
264
+ lifecycle: true,
265
+ verbose: false,
266
+ };
267
+
268
+ const events: BaseEvent[] = [
269
+ {
270
+ type: EventType.TEXT_MESSAGE_START,
271
+ messageId: "msg-1",
272
+ role: "assistant",
273
+ } as BaseEvent,
274
+ {
275
+ type: EventType.RUN_FINISHED,
276
+ threadId: "t-1",
277
+ runId: "r-1",
278
+ } as BaseEvent,
279
+ ];
280
+
281
+ const response = createSseEventResponse({
282
+ request: createMockRequest(),
283
+ observableFactory: () => createTestObservable(events),
284
+ debug,
285
+ });
286
+
287
+ await drainResponse(response);
288
+
289
+ const eventEmittedCalls = mockDebug.mock.calls.filter(
290
+ (call) => call[call.length - 1] === "Event emitted",
291
+ );
292
+ expect(eventEmittedCalls).toHaveLength(0);
293
+
294
+ // Only lifecycle calls should be present
295
+ const lifecycleCalls = mockDebug.mock.calls.filter(
296
+ (call) =>
297
+ call[call.length - 1] === "SSE stream opened" ||
298
+ call[call.length - 1] === "SSE stream completed",
299
+ );
300
+ expect(lifecycleCalls.length).toBeGreaterThanOrEqual(1);
301
+ });
302
+ });
@@ -9,6 +9,11 @@ import {
9
9
  createLicenseChecker,
10
10
  type LicenseChecker,
11
11
  } from "@copilotkit/license-verifier";
12
+ import {
13
+ type ResolvedDebugConfig,
14
+ resolveDebugConfig,
15
+ type DebugConfig,
16
+ } from "@copilotkit/shared";
12
17
  import { AbstractAgent } from "@ag-ui/client";
13
18
  import type { MCPClientConfig } from "@ag-ui/mcp-apps-middleware";
14
19
  import { A2UIMiddlewareConfig } from "@ag-ui/a2ui-middleware";
@@ -17,6 +22,7 @@ import type {
17
22
  BeforeRequestMiddleware,
18
23
  AfterRequestMiddleware,
19
24
  } from "./middleware";
25
+ import { createLogger, type CopilotRuntimeLogger } from "../../../lib/logger";
20
26
  import { TranscriptionService } from "../transcription-service/transcription-service";
21
27
  import { AgentRunner } from "../runner/agent-runner";
22
28
  import { InMemoryAgentRunner } from "../runner/in-memory";
@@ -128,6 +134,8 @@ interface BaseCopilotRuntimeOptions extends CopilotRuntimeMiddlewares {
128
134
  afterRequestMiddleware?: AfterRequestMiddleware;
129
135
  /** Signed license token for server-side feature verification. Falls back to COPILOTKIT_LICENSE_TOKEN env var. */
130
136
  licenseToken?: string;
137
+ /** Enable debug logging for the event pipeline. */
138
+ debug?: DebugConfig;
131
139
  }
132
140
 
133
141
  export interface CopilotRuntimeUser {
@@ -181,6 +189,8 @@ export interface CopilotRuntimeLike {
181
189
  identifyUser?: IdentifyUserCallback;
182
190
  mode: RuntimeMode;
183
191
  licenseChecker?: LicenseChecker;
192
+ debug: ResolvedDebugConfig;
193
+ debugLogger?: CopilotRuntimeLogger;
184
194
  }
185
195
 
186
196
  export interface CopilotSseRuntimeLike extends CopilotRuntimeLike {
@@ -208,6 +218,8 @@ abstract class BaseCopilotRuntime implements CopilotRuntimeLike {
208
218
  public mcpApps: CopilotRuntimeOptions["mcpApps"];
209
219
  public openGenerativeUI: CopilotRuntimeOptions["openGenerativeUI"];
210
220
  public licenseChecker?: LicenseChecker;
221
+ public debug: ResolvedDebugConfig;
222
+ public debugLogger?: CopilotRuntimeLogger;
211
223
 
212
224
  abstract readonly intelligence?: CopilotKitIntelligence;
213
225
  abstract readonly mode: RuntimeMode;
@@ -231,6 +243,13 @@ abstract class BaseCopilotRuntime implements CopilotRuntimeLike {
231
243
  this.mcpApps = mcpApps;
232
244
  this.openGenerativeUI = openGenerativeUI;
233
245
  this.runner = runner;
246
+ this.debug = resolveDebugConfig(options.debug);
247
+ if (this.debug.enabled) {
248
+ this.debugLogger = createLogger({
249
+ level: "debug",
250
+ component: "copilotkit-debug",
251
+ });
252
+ }
234
253
  }
235
254
  }
236
255
 
@@ -387,4 +406,12 @@ export class CopilotRuntime implements CopilotRuntimeLike {
387
406
  get licenseChecker() {
388
407
  return this.delegate.licenseChecker;
389
408
  }
409
+
410
+ get debug(): ResolvedDebugConfig {
411
+ return this.delegate.debug;
412
+ }
413
+
414
+ get debugLogger(): CopilotRuntimeLogger | undefined {
415
+ return this.delegate.debugLogger;
416
+ }
390
417
  }
@@ -55,6 +55,13 @@ export async function handleRunAgent({
55
55
  agent.setState(input.state);
56
56
  agent.threadId = input.threadId;
57
57
 
58
+ if (runtime.debug?.lifecycle && runtime.debugLogger) {
59
+ runtime.debugLogger.debug(
60
+ { agentName: agentId, threadId: input.threadId },
61
+ "Agent run started",
62
+ );
63
+ }
64
+
58
65
  if (isIntelligenceRuntime(runtime)) {
59
66
  return handleIntelligenceRun({
60
67
  runtime,
@@ -65,7 +72,14 @@ export async function handleRunAgent({
65
72
  });
66
73
  }
67
74
 
68
- return handleSseRun({ runtime, request, agent, input });
75
+ return handleSseRun({
76
+ runtime,
77
+ request,
78
+ agent,
79
+ input,
80
+ debug: runtime.debug,
81
+ logger: runtime.debugLogger,
82
+ });
69
83
  } catch (error) {
70
84
  console.error("Error running agent:", error);
71
85
  console.error(