@copilotkit/runtime 1.50.0-beta.1 → 1.50.0-beta.11

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 (132) hide show
  1. package/CHANGELOG.md +85 -0
  2. package/dist/chunk-2OZAGFV3.mjs +43 -0
  3. package/dist/chunk-2OZAGFV3.mjs.map +1 -0
  4. package/dist/chunk-62NE5S6M.mjs +226 -0
  5. package/dist/chunk-62NE5S6M.mjs.map +1 -0
  6. package/dist/chunk-6XRUR5UK.mjs +1 -0
  7. package/dist/chunk-6XRUR5UK.mjs.map +1 -0
  8. package/dist/chunk-AMUJQ6IR.mjs +50 -0
  9. package/dist/chunk-AMUJQ6IR.mjs.map +1 -0
  10. package/dist/chunk-BJEYMRDD.mjs +25 -0
  11. package/dist/chunk-BJEYMRDD.mjs.map +1 -0
  12. package/dist/chunk-DZV4ZIAR.mjs +3063 -0
  13. package/dist/chunk-DZV4ZIAR.mjs.map +1 -0
  14. package/dist/chunk-FHD4JECV.mjs +33 -0
  15. package/dist/chunk-FHD4JECV.mjs.map +1 -0
  16. package/dist/chunk-FMU55SEU.mjs +25 -0
  17. package/dist/chunk-FMU55SEU.mjs.map +1 -0
  18. package/dist/chunk-OWIGJONH.mjs +275 -0
  19. package/dist/chunk-OWIGJONH.mjs.map +1 -0
  20. package/dist/chunk-SBCOROE4.mjs +1112 -0
  21. package/dist/chunk-SBCOROE4.mjs.map +1 -0
  22. package/dist/chunk-TTUAEJLD.mjs +617 -0
  23. package/dist/chunk-TTUAEJLD.mjs.map +1 -0
  24. package/dist/chunk-XWBDEXDA.mjs +153 -0
  25. package/dist/chunk-XWBDEXDA.mjs.map +1 -0
  26. package/dist/chunk-Z752VE75.mjs +74 -0
  27. package/dist/chunk-Z752VE75.mjs.map +1 -0
  28. package/dist/graphql/message-conversion/index.d.ts +18 -0
  29. package/dist/graphql/message-conversion/index.js +725 -0
  30. package/dist/graphql/message-conversion/index.js.map +1 -0
  31. package/dist/graphql/message-conversion/index.mjs +245 -0
  32. package/dist/graphql/message-conversion/index.mjs.map +1 -0
  33. package/dist/graphql/types/base/index.d.ts +6 -0
  34. package/dist/graphql/types/base/index.js +63 -0
  35. package/dist/graphql/types/base/index.js.map +1 -0
  36. package/dist/graphql/types/base/index.mjs +8 -0
  37. package/dist/graphql/types/base/index.mjs.map +1 -0
  38. package/dist/graphql/types/converted/index.d.ts +2 -0
  39. package/dist/graphql/types/converted/index.js +294 -0
  40. package/dist/graphql/types/converted/index.js.map +1 -0
  41. package/dist/graphql/types/converted/index.mjs +20 -0
  42. package/dist/graphql/types/converted/index.mjs.map +1 -0
  43. package/dist/groq-adapter-50bc6e4a.d.ts +326 -0
  44. package/dist/index-adbd78f1.d.ts +154 -0
  45. package/dist/index.d.ts +136 -287
  46. package/dist/index.js +414 -293
  47. package/dist/index.js.map +1 -1
  48. package/dist/index.mjs +407 -283
  49. package/dist/index.mjs.map +1 -1
  50. package/dist/langgraph.d.ts +284 -0
  51. package/dist/langgraph.js +211 -0
  52. package/dist/langgraph.js.map +1 -0
  53. package/dist/langgraph.mjs +206 -0
  54. package/dist/langgraph.mjs.map +1 -0
  55. package/dist/langserve-74a52292.d.ts +242 -0
  56. package/dist/lib/cloud/index.d.ts +6 -0
  57. package/dist/lib/cloud/index.js +18 -0
  58. package/dist/lib/cloud/index.js.map +1 -0
  59. package/dist/lib/cloud/index.mjs +1 -0
  60. package/dist/lib/cloud/index.mjs.map +1 -0
  61. package/dist/lib/index.d.ts +266 -0
  62. package/dist/lib/index.js +4944 -0
  63. package/dist/lib/index.js.map +1 -0
  64. package/dist/lib/index.mjs +74 -0
  65. package/dist/lib/index.mjs.map +1 -0
  66. package/dist/lib/integrations/index.d.ts +28 -0
  67. package/dist/lib/integrations/index.js +3024 -0
  68. package/dist/lib/integrations/index.js.map +1 -0
  69. package/dist/lib/integrations/index.mjs +36 -0
  70. package/dist/lib/integrations/index.mjs.map +1 -0
  71. package/dist/lib/integrations/nest/index.d.ts +16 -0
  72. package/dist/lib/integrations/nest/index.js +2937 -0
  73. package/dist/lib/integrations/nest/index.js.map +1 -0
  74. package/dist/lib/integrations/nest/index.mjs +13 -0
  75. package/dist/lib/integrations/nest/index.mjs.map +1 -0
  76. package/dist/lib/integrations/node-express/index.d.ts +16 -0
  77. package/dist/lib/integrations/node-express/index.js +2937 -0
  78. package/dist/lib/integrations/node-express/index.js.map +1 -0
  79. package/dist/lib/integrations/node-express/index.mjs +13 -0
  80. package/dist/lib/integrations/node-express/index.mjs.map +1 -0
  81. package/dist/lib/integrations/node-http/index.d.ts +16 -0
  82. package/dist/lib/integrations/node-http/index.js +2923 -0
  83. package/dist/lib/integrations/node-http/index.js.map +1 -0
  84. package/dist/lib/integrations/node-http/index.mjs +12 -0
  85. package/dist/lib/integrations/node-http/index.mjs.map +1 -0
  86. package/dist/service-adapters/index.d.ts +166 -0
  87. package/dist/service-adapters/index.js +1800 -0
  88. package/dist/service-adapters/index.js.map +1 -0
  89. package/dist/service-adapters/index.mjs +36 -0
  90. package/dist/service-adapters/index.mjs.map +1 -0
  91. package/dist/service-adapters/shared/index.d.ts +9 -0
  92. package/dist/service-adapters/shared/index.js +72 -0
  93. package/dist/service-adapters/shared/index.js.map +1 -0
  94. package/dist/service-adapters/shared/index.mjs +8 -0
  95. package/dist/service-adapters/shared/index.mjs.map +1 -0
  96. package/dist/shared-f6d43ef8.d.ts +446 -0
  97. package/dist/utils/index.d.ts +65 -0
  98. package/dist/utils/index.js +175 -0
  99. package/dist/utils/index.js.map +1 -0
  100. package/dist/utils/index.mjs +12 -0
  101. package/dist/utils/index.mjs.map +1 -0
  102. package/dist/v2/index.d.ts +1 -0
  103. package/dist/v2/index.js +7 -0
  104. package/dist/v2/index.js.map +1 -1
  105. package/dist/v2/index.mjs +1 -0
  106. package/dist/v2/index.mjs.map +1 -1
  107. package/package.json +56 -18
  108. package/src/graphql/message-conversion/agui-to-gql.test.ts +2 -2
  109. package/src/graphql/message-conversion/gql-to-agui.test.ts +30 -28
  110. package/src/graphql/message-conversion/roundtrip-conversion.test.ts +8 -8
  111. package/src/langgraph.ts +1 -0
  112. package/src/lib/index.ts +42 -1
  113. package/src/lib/integrations/nextjs/app-router.ts +3 -1
  114. package/src/lib/integrations/node-http/index.ts +132 -11
  115. package/src/lib/integrations/shared.ts +2 -2
  116. package/src/lib/runtime/agent-integrations/{langgraph.agent.ts → langgraph/agent.ts} +5 -30
  117. package/src/lib/runtime/agent-integrations/langgraph/consts.ts +34 -0
  118. package/src/lib/runtime/agent-integrations/langgraph/index.ts +2 -0
  119. package/src/lib/runtime/copilot-runtime.ts +86 -69
  120. package/src/lib/runtime/telemetry-agent-runner.ts +134 -0
  121. package/src/service-adapters/anthropic/anthropic-adapter.ts +16 -3
  122. package/src/service-adapters/bedrock/bedrock-adapter.ts +4 -1
  123. package/src/service-adapters/experimental/ollama/ollama-adapter.ts +2 -1
  124. package/src/service-adapters/google/google-genai-adapter.ts +9 -4
  125. package/src/service-adapters/groq/groq-adapter.ts +16 -3
  126. package/src/service-adapters/langchain/langchain-adapter.ts +5 -3
  127. package/src/service-adapters/langchain/langserve.ts +2 -1
  128. package/src/service-adapters/openai/openai-adapter.ts +17 -3
  129. package/src/service-adapters/openai/openai-assistant-adapter.ts +26 -11
  130. package/src/service-adapters/unify/unify-adapter.ts +3 -1
  131. package/src/v2/index.ts +1 -0
  132. package/tsup.config.ts +5 -2
@@ -4,6 +4,8 @@ import { createCopilotEndpointSingleRoute } from "@copilotkitnext/runtime";
4
4
  import { IncomingMessage, ServerResponse } from "http";
5
5
  import { Readable } from "node:stream";
6
6
 
7
+ type IncomingWithBody = IncomingMessage & { body?: unknown; complete?: boolean };
8
+
7
9
  export function readableStreamToNodeStream(webStream: ReadableStream): Readable {
8
10
  const reader = webStream.getReader();
9
11
 
@@ -24,7 +26,10 @@ export function readableStreamToNodeStream(webStream: ReadableStream): Readable
24
26
  }
25
27
 
26
28
  function getFullUrl(req: IncomingMessage): string {
27
- const path = req.url || "/";
29
+ const expressPath =
30
+ (req as any).originalUrl ??
31
+ ((req as any).baseUrl ? `${(req as any).baseUrl}${req.url ?? ""}` : undefined);
32
+ const path = expressPath || req.url || "/";
28
33
  const host =
29
34
  (req.headers["x-forwarded-host"] as string) || (req.headers.host as string) || "localhost";
30
35
  const proto =
@@ -34,6 +39,61 @@ function getFullUrl(req: IncomingMessage): string {
34
39
  return `${proto}://${host}${path}`;
35
40
  }
36
41
 
42
+ function toHeaders(rawHeaders: IncomingMessage["headers"]): Headers {
43
+ const headers = new Headers();
44
+
45
+ for (const [key, value] of Object.entries(rawHeaders)) {
46
+ if (value === undefined) continue;
47
+
48
+ if (Array.isArray(value)) {
49
+ value.forEach((entry) => headers.append(key, entry));
50
+ continue;
51
+ }
52
+
53
+ headers.append(key, value);
54
+ }
55
+
56
+ return headers;
57
+ }
58
+
59
+ function isStreamConsumed(req: IncomingWithBody): boolean {
60
+ const readableState = (req as any)._readableState;
61
+
62
+ return Boolean(
63
+ req.readableEnded || req.complete || readableState?.ended || readableState?.endEmitted,
64
+ );
65
+ }
66
+
67
+ function synthesizeBodyFromParsedBody(
68
+ parsedBody: unknown,
69
+ headers: Headers,
70
+ ): { body: BodyInit | null; contentType?: string } {
71
+ if (parsedBody === null || parsedBody === undefined) {
72
+ return { body: null };
73
+ }
74
+
75
+ if (parsedBody instanceof Buffer || parsedBody instanceof Uint8Array) {
76
+ return { body: parsedBody };
77
+ }
78
+
79
+ if (typeof parsedBody === "string") {
80
+ return { body: parsedBody, contentType: headers.get("content-type") ?? "text/plain" };
81
+ }
82
+
83
+ return {
84
+ body: JSON.stringify(parsedBody),
85
+ contentType: "application/json",
86
+ };
87
+ }
88
+
89
+ function isDisturbedOrLockedError(error: unknown): boolean {
90
+ return (
91
+ error instanceof TypeError &&
92
+ typeof error.message === "string" &&
93
+ (error.message.includes("disturbed") || error.message.includes("locked"))
94
+ );
95
+ }
96
+
37
97
  export function copilotRuntimeNodeHttpEndpoint(options: CreateCopilotRuntimeServerOptions) {
38
98
  const commonConfig = getCommonConfig(options);
39
99
 
@@ -55,26 +115,87 @@ export function copilotRuntimeNodeHttpEndpoint(options: CreateCopilotRuntimeServ
55
115
  logger.debug("Creating Node HTTP endpoint");
56
116
 
57
117
  const serviceAdapter = options.serviceAdapter;
58
- options.runtime.handleServiceAdapter(serviceAdapter);
118
+ if (serviceAdapter) {
119
+ options.runtime.handleServiceAdapter(serviceAdapter);
120
+ }
59
121
 
60
122
  const honoApp = createCopilotEndpointSingleRoute({
61
123
  runtime: options.runtime.instance,
62
124
  basePath: options.baseUrl ?? options.endpoint,
63
125
  });
64
126
 
65
- return async function handler(req: IncomingMessage, res: ServerResponse) {
127
+ return async function handler(req: IncomingWithBody, res: ServerResponse) {
66
128
  const url = getFullUrl(req);
67
129
  const hasBody = req.method !== "GET" && req.method !== "HEAD";
68
130
 
69
- const request = new Request(url, {
70
- method: req.method,
71
- headers: req.headers as any,
72
- body: hasBody ? (req as any) : undefined,
73
- // Node/undici extension
74
- duplex: hasBody ? "half" : undefined,
75
- } as any);
131
+ const baseHeaders = toHeaders(req.headers);
132
+ const parsedBody = req.body;
133
+
134
+ const streamConsumed = isStreamConsumed(req) || parsedBody !== undefined;
135
+ const canStream = hasBody && !streamConsumed;
76
136
 
77
- const response = await honoApp.fetch(request);
137
+ let requestBody: BodyInit | null | undefined = undefined;
138
+ let useDuplex = false;
139
+
140
+ if (hasBody && canStream) {
141
+ requestBody = req as unknown as BodyInit;
142
+ useDuplex = true;
143
+ }
144
+
145
+ if (hasBody && streamConsumed) {
146
+ if (parsedBody !== undefined) {
147
+ const synthesized = synthesizeBodyFromParsedBody(parsedBody, baseHeaders);
148
+ requestBody = synthesized.body ?? undefined;
149
+ baseHeaders.delete("content-length");
150
+
151
+ if (synthesized.contentType) {
152
+ baseHeaders.set("content-type", synthesized.contentType);
153
+ }
154
+
155
+ logger.debug("Request stream already consumed; using parsed req.body to rebuild request.");
156
+ } else {
157
+ logger.warn("Request stream consumed with no available body; sending empty payload.");
158
+ requestBody = undefined;
159
+ }
160
+ }
161
+
162
+ const buildRequest = (body: BodyInit | null | undefined, headers: Headers, duplex: boolean) =>
163
+ new Request(url, {
164
+ method: req.method,
165
+ headers,
166
+ body,
167
+ duplex: duplex ? "half" : undefined,
168
+ } as RequestInit);
169
+
170
+ let response: Response;
171
+ try {
172
+ response = await honoApp.fetch(buildRequest(requestBody, baseHeaders, useDuplex));
173
+ } catch (error) {
174
+ if (isDisturbedOrLockedError(error) && hasBody) {
175
+ logger.warn(
176
+ "Encountered disturbed/locked request body; rebuilding request using parsed body or empty payload.",
177
+ );
178
+
179
+ const fallbackHeaders = new Headers(baseHeaders);
180
+ let fallbackBody: BodyInit | null | undefined;
181
+
182
+ if (parsedBody !== undefined) {
183
+ const synthesized = synthesizeBodyFromParsedBody(parsedBody, fallbackHeaders);
184
+ fallbackBody = synthesized.body ?? undefined;
185
+ fallbackHeaders.delete("content-length");
186
+
187
+ if (synthesized.contentType) {
188
+ fallbackHeaders.set("content-type", synthesized.contentType);
189
+ }
190
+ } else {
191
+ fallbackBody = undefined;
192
+ }
193
+
194
+ response = await honoApp.fetch(buildRequest(fallbackBody, fallbackHeaders, false));
195
+ } else {
196
+ throw error;
197
+ }
198
+ }
78
199
 
79
200
  res.statusCode = response.status;
80
201
  response.headers.forEach((value, key) => {
@@ -34,8 +34,8 @@ export type GraphQLContext = YogaInitialContext & {
34
34
  };
35
35
 
36
36
  export interface CreateCopilotRuntimeServerOptions {
37
- runtime: CopilotRuntime;
38
- serviceAdapter: CopilotServiceAdapter;
37
+ runtime: CopilotRuntime<any>;
38
+ serviceAdapter?: CopilotServiceAdapter;
39
39
  endpoint: string;
40
40
  baseUrl?: string;
41
41
  cloud?: CopilotCloudOptions;
@@ -1,16 +1,5 @@
1
- import {
2
- RunAgentInput,
3
- EventType,
4
- CustomEvent,
5
- TextMessageStartEvent,
6
- TextMessageContentEvent,
7
- TextMessageEndEvent,
8
- ToolCallStartEvent,
9
- ToolCallArgsEvent,
10
- ToolCallEndEvent,
11
- } from "@ag-ui/client";
12
1
  import { map } from "rxjs";
13
- import { LangGraphEventTypes } from "../../../agents/langgraph/events";
2
+ import { LangGraphEventTypes } from "../../../../agents/langgraph/events";
14
3
  import { RawEvent } from "@ag-ui/core";
15
4
  import {
16
5
  LangGraphAgent as AGUILangGraphAgent,
@@ -31,25 +20,11 @@ interface CopilotKitStateEnrichment {
31
20
  };
32
21
  }
33
22
 
34
- export interface PredictStateTool {
35
- tool: string;
36
- state_key: string;
37
- tool_argument: string;
38
- }
39
-
40
- export type TextMessageEvents =
41
- | TextMessageStartEvent
42
- | TextMessageContentEvent
43
- | TextMessageEndEvent;
23
+ import { RunAgentInput, EventType, CustomEvent } from "@ag-ui/client";
44
24
 
45
- export type ToolCallEvents = ToolCallStartEvent | ToolCallArgsEvent | ToolCallEndEvent;
46
-
47
- export enum CustomEventNames {
48
- CopilotKitManuallyEmitMessage = "copilotkit_manually_emit_message",
49
- CopilotKitManuallyEmitToolCall = "copilotkit_manually_emit_tool_call",
50
- CopilotKitManuallyEmitIntermediateState = "copilotkit_manually_emit_intermediate_state",
51
- CopilotKitExit = "copilotkit_exit",
52
- }
25
+ // Import and re-export from separate file to maintain API compatibility
26
+ import { CustomEventNames, TextMessageEvents, ToolCallEvents, PredictStateTool } from "./consts";
27
+ export { CustomEventNames };
53
28
 
54
29
  export class LangGraphAgent extends AGUILangGraphAgent {
55
30
  constructor(config: LangGraphAgentConfig) {
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Constants for LangGraph integration.
3
+ * This file is separate from langgraph.agent.ts to avoid pulling in @ag-ui/langgraph
4
+ * when only these constants are needed.
5
+ */
6
+
7
+ import {
8
+ TextMessageStartEvent,
9
+ TextMessageContentEvent,
10
+ TextMessageEndEvent,
11
+ ToolCallStartEvent,
12
+ ToolCallArgsEvent,
13
+ ToolCallEndEvent,
14
+ } from "@ag-ui/client";
15
+
16
+ export type TextMessageEvents =
17
+ | TextMessageStartEvent
18
+ | TextMessageContentEvent
19
+ | TextMessageEndEvent;
20
+
21
+ export type ToolCallEvents = ToolCallStartEvent | ToolCallArgsEvent | ToolCallEndEvent;
22
+
23
+ export enum CustomEventNames {
24
+ CopilotKitManuallyEmitMessage = "copilotkit_manually_emit_message",
25
+ CopilotKitManuallyEmitToolCall = "copilotkit_manually_emit_tool_call",
26
+ CopilotKitManuallyEmitIntermediateState = "copilotkit_manually_emit_intermediate_state",
27
+ CopilotKitExit = "copilotkit_exit",
28
+ }
29
+
30
+ export interface PredictStateTool {
31
+ tool: string;
32
+ state_key: string;
33
+ tool_argument: string;
34
+ }
@@ -0,0 +1,2 @@
1
+ export * from "./consts";
2
+ export * from "./agent";
@@ -13,65 +13,50 @@
13
13
  */
14
14
 
15
15
  import {
16
- Action,
17
- CopilotErrorHandler,
16
+ type Action,
17
+ type CopilotErrorHandler,
18
18
  CopilotKitMisuseError,
19
- MaybePromise,
20
- NonEmptyRecord,
21
- Parameter,
19
+ type MaybePromise,
20
+ type NonEmptyRecord,
21
+ type Parameter,
22
22
  readBody,
23
23
  getZodParameters,
24
- PartialBy,
24
+ type PartialBy,
25
+ isTelemetryDisabled,
25
26
  } from "@copilotkit/shared";
26
- import { type RunAgentInput } from "@ag-ui/core";
27
+ import type { RunAgentInput } from "@ag-ui/core";
27
28
  import { aguiToGQL } from "../../graphql/message-conversion/agui-to-gql";
28
- import { CopilotServiceAdapter, RemoteChainParameters } from "../../service-adapters";
29
+ import type { CopilotServiceAdapter, RemoteChainParameters } from "../../service-adapters";
29
30
  import {
30
31
  CopilotRuntime as CopilotRuntimeVNext,
31
- CopilotRuntimeOptions,
32
- CopilotRuntimeOptions as CopilotRuntimeOptionsVNext,
33
- InMemoryAgentRunner as InMemoryAgentRunnerVNext,
32
+ type CopilotRuntimeOptions,
33
+ type CopilotRuntimeOptions as CopilotRuntimeOptionsVNext,
34
+ InMemoryAgentRunner,
34
35
  } from "@copilotkitnext/runtime";
36
+ import { TelemetryAgentRunner } from "./telemetry-agent-runner";
37
+ import telemetry from "../telemetry-client";
35
38
 
36
- import { MessageInput } from "../../graphql/inputs/message.input";
37
- import { ActionInput } from "../../graphql/inputs/action.input";
38
- import { RuntimeEventSource } from "../../service-adapters/events";
39
- import { Message } from "../../graphql/types/converted";
40
- import { ForwardedParametersInput } from "../../graphql/inputs/forwarded-parameters.input";
39
+ import type { MessageInput } from "../../graphql/inputs/message.input";
40
+ import type { Message } from "../../graphql/types/converted";
41
41
 
42
42
  import {
43
43
  EndpointType,
44
- EndpointDefinition,
45
- CopilotKitEndpoint,
46
- LangGraphPlatformEndpoint,
44
+ type EndpointDefinition,
45
+ type CopilotKitEndpoint,
46
+ type LangGraphPlatformEndpoint,
47
47
  } from "./types";
48
48
 
49
- import { GraphQLContext } from "../integrations/shared";
50
- import { AgentSessionInput } from "../../graphql/inputs/agent-session.input";
51
- import { AgentStateInput } from "../../graphql/inputs/agent-state.input";
52
- import { Agent } from "../../graphql/types/agents-response.type";
53
- import { ExtensionsInput } from "../../graphql/inputs/extensions.input";
54
- import { ExtensionsResponse } from "../../graphql/types/extensions-response.type";
55
- import { MetaEventInput } from "../../graphql/inputs/meta-event.input";
56
- import {
57
- CopilotObservabilityConfig,
58
- LLMRequestData,
59
- LLMResponseData,
60
- LLMErrorData,
61
- } from "../observability";
62
- import { AbstractAgent } from "@ag-ui/client";
49
+ import type { CopilotObservabilityConfig, LLMRequestData, LLMResponseData } from "../observability";
50
+ import type { AbstractAgent } from "@ag-ui/client";
63
51
 
64
52
  // +++ MCP Imports +++
65
53
  import {
66
- MCPClient,
67
- MCPEndpointConfig,
68
- MCPTool,
54
+ type MCPClient,
55
+ type MCPEndpointConfig,
56
+ type MCPTool,
69
57
  extractParametersFromSchema,
70
- convertMCPToolsToActions,
71
- generateMcpToolInstructions,
72
58
  } from "./mcp-tools-utils";
73
- import { LangGraphAgent } from "./agent-integrations/langgraph.agent";
74
- import { BasicAgent, BasicAgentConfiguration } from "@copilotkitnext/agent";
59
+ import { BasicAgent, type BasicAgentConfiguration } from "@copilotkitnext/agent";
75
60
  // Define the function type alias here or import if defined elsewhere
76
61
  type CreateMCPClientFunction = (config: MCPEndpointConfig) => Promise<MCPClient>;
77
62
 
@@ -312,8 +297,8 @@ interface CopilotRuntimeConstructorParams<T extends Parameter[] | [] = []>
312
297
  /**
313
298
  * Central runtime object passed to all request handlers.
314
299
  */
315
- export class CopilotRuntime {
316
- params?: CopilotRuntimeConstructorParams;
300
+ export class CopilotRuntime<const T extends Parameter[] | [] = []> {
301
+ params?: CopilotRuntimeConstructorParams<T>;
317
302
  private observability?: CopilotObservabilityConfig;
318
303
  // Cache MCP tools per endpoint to avoid re-fetching repeatedly
319
304
  private mcpToolsCache: Map<string, BasicAgentConfiguration["tools"]> = new Map();
@@ -321,12 +306,24 @@ export class CopilotRuntime {
321
306
  private _instance: CopilotRuntimeVNext;
322
307
 
323
308
  constructor(
324
- params?: CopilotRuntimeConstructorParams & PartialBy<CopilotRuntimeOptions, "agents">,
309
+ params?: CopilotRuntimeConstructorParams<T> & PartialBy<CopilotRuntimeOptions, "agents">,
325
310
  ) {
326
311
  const agents = params?.agents ?? {};
312
+ const endpointAgents = this.assignEndpointsToAgents(params?.remoteEndpoints ?? []);
313
+
314
+ // Determine the base runner (user-provided or default)
315
+ const baseRunner = params?.runner ?? new InMemoryAgentRunner();
316
+
317
+ // Wrap with TelemetryAgentRunner unless telemetry is disabled
318
+ // This ensures we always capture agent execution telemetry when enabled,
319
+ // even if the user provides their own custom runner
320
+ const runner = isTelemetryDisabled()
321
+ ? baseRunner
322
+ : new TelemetryAgentRunner({ runner: baseRunner });
323
+
327
324
  this.runtimeArgs = {
328
- agents: { ...this.assignEndpointsToAgents(params?.remoteEndpoints ?? []), ...agents },
329
- runner: params?.runner ?? new InMemoryAgentRunnerVNext(),
325
+ agents: { ...endpointAgents, ...agents },
326
+ runner,
330
327
  // TODO: add support for transcriptionService from CopilotRuntimeOptionsVNext once it is ready
331
328
  // transcriptionService: params?.transcriptionService,
332
329
 
@@ -345,28 +342,23 @@ export class CopilotRuntime {
345
342
  return this._instance;
346
343
  }
347
344
 
348
- private assignEndpointsToAgents(endpoints: CopilotRuntimeConstructorParams["remoteEndpoints"]) {
349
- return endpoints.reduce((acc, endpoint) => {
350
- if (resolveEndpointType(endpoint) == EndpointType.LangGraphPlatform) {
351
- let lgAgents = {};
352
- const lgEndpoint = endpoint as LangGraphPlatformEndpoint;
353
- lgEndpoint.agents.forEach((agent) => {
354
- const graphId = agent.assistantId ?? agent.name;
355
- lgAgents[graphId] = new LangGraphAgent({
356
- deploymentUrl: lgEndpoint.deploymentUrl,
357
- langsmithApiKey: lgEndpoint.langsmithApiKey,
358
- graphId,
359
- });
360
- });
345
+ private assignEndpointsToAgents(
346
+ endpoints: CopilotRuntimeConstructorParams<T>["remoteEndpoints"],
347
+ ): Record<string, AbstractAgent> {
348
+ let result: Record<string, AbstractAgent> = {};
361
349
 
362
- return {
363
- ...acc,
364
- ...lgAgents,
365
- };
366
- }
350
+ if (
351
+ endpoints.some((endpoint) => resolveEndpointType(endpoint) == EndpointType.LangGraphPlatform)
352
+ ) {
353
+ throw new CopilotKitMisuseError({
354
+ message:
355
+ "LangGraphPlatformEndpoint in remoteEndpoints is deprecated. " +
356
+ 'Please use the "agents" option instead with LangGraphAgent from "@copilotkit/runtime/langgraph". ' +
357
+ 'Example: agents: { myAgent: new LangGraphAgent({ deploymentUrl: "...", graphId: "..." }) }',
358
+ });
359
+ }
367
360
 
368
- return acc;
369
- }, {});
361
+ return result;
370
362
  }
371
363
 
372
364
  handleServiceAdapter(serviceAdapter: CopilotServiceAdapter) {
@@ -393,7 +385,7 @@ export class CopilotRuntime {
393
385
  });
394
386
  }
395
387
 
396
- if (this.params.actions?.length) {
388
+ if (this.params.actions) {
397
389
  const mcpTools = await this.getToolsFromMCP();
398
390
  agentsList = this.assignToolsToAgents(agents, [
399
391
  ...this.getToolsFromActions(this.params.actions),
@@ -423,6 +415,7 @@ export class CopilotRuntime {
423
415
  name: action.name,
424
416
  description: action.description || "",
425
417
  parameters: zodSchema,
418
+ execute: () => Promise.resolve(),
426
419
  };
427
420
  });
428
421
  }
@@ -454,9 +447,33 @@ export class CopilotRuntime {
454
447
  }
455
448
 
456
449
  private createOnBeforeRequestHandler(
457
- params?: CopilotRuntimeConstructorParams & PartialBy<CopilotRuntimeOptions, "agents">,
450
+ params?: CopilotRuntimeConstructorParams<T> & PartialBy<CopilotRuntimeOptions, "agents">,
458
451
  ) {
459
452
  return async (hookParams: BeforeRequestMiddlewareFnParameters[0]) => {
453
+ const { request } = hookParams;
454
+
455
+ // Capture telemetry for copilot request creation
456
+ const publicApiKey = request.headers.get("x-copilotcloud-public-api-key");
457
+ const body = (await readBody(request)) as RunAgentInput;
458
+ const forwardedProps = body.forwardedProps as
459
+ | {
460
+ cloud?: { guardrails?: unknown };
461
+ metadata?: { requestType?: string };
462
+ }
463
+ | undefined;
464
+
465
+ // Get cloud base URL from environment or default
466
+ const cloudBaseUrl =
467
+ process.env.COPILOT_CLOUD_BASE_URL || "https://api.cloud.copilotkit.ai";
468
+
469
+ telemetry.capture("oss.runtime.copilot_request_created", {
470
+ "cloud.guardrails.enabled": forwardedProps?.cloud?.guardrails !== undefined,
471
+ requestType: forwardedProps?.metadata?.requestType ?? "unknown",
472
+ "cloud.api_key_provided": !!publicApiKey,
473
+ ...(publicApiKey ? { "cloud.public_api_key": publicApiKey } : {}),
474
+ "cloud.base_url": cloudBaseUrl,
475
+ });
476
+
460
477
  // TODO: get public api key and run with expected data
461
478
  // if (this.observability?.enabled && this.params.publicApiKey) {
462
479
  // this.logObservabilityBeforeRequest()
@@ -467,7 +484,6 @@ export class CopilotRuntime {
467
484
 
468
485
  if (params?.middleware?.onBeforeRequest) {
469
486
  const { request, runtime, path } = hookParams;
470
- const body = (await readBody(request)) as RunAgentInput;
471
487
  const gqlMessages = (aguiToGQL(body.messages) as Message[]).reduce(
472
488
  (acc, msg) => {
473
489
  if ("role" in msg && msg.role === "user") {
@@ -492,7 +508,7 @@ export class CopilotRuntime {
492
508
  }
493
509
 
494
510
  private createOnAfterRequestHandler(
495
- params?: CopilotRuntimeConstructorParams & PartialBy<CopilotRuntimeOptions, "agents">,
511
+ params?: CopilotRuntimeConstructorParams<T> & PartialBy<CopilotRuntimeOptions, "agents">,
496
512
  ) {
497
513
  return async (hookParams: AfterRequestMiddlewareFnParameters[0]) => {
498
514
  // TODO: get public api key and run with expected data
@@ -636,6 +652,7 @@ export class CopilotRuntime {
636
652
  name: toolName,
637
653
  description: tool.description || `MCP tool: ${toolName} (from ${endpointUrl})`,
638
654
  parameters: zodSchema,
655
+ execute: () => Promise.resolve(),
639
656
  };
640
657
  },
641
658
  );
@@ -0,0 +1,134 @@
1
+ /**
2
+ * TelemetryAgentRunner - A wrapper around AgentRunner that adds telemetry
3
+ * for agent execution streams.
4
+ *
5
+ * This captures the following telemetry events:
6
+ * - oss.runtime.agent_execution_stream_started - when an agent execution starts
7
+ * - oss.runtime.agent_execution_stream_ended - when an agent execution completes
8
+ * - oss.runtime.agent_execution_stream_errored - when an agent execution fails
9
+ */
10
+
11
+ import { type AgentRunner, InMemoryAgentRunner } from "@copilotkitnext/runtime";
12
+ import { createHash } from "node:crypto";
13
+ import { tap, catchError, finalize } from "rxjs";
14
+ import telemetry from "../telemetry-client";
15
+ import type { AgentExecutionResponseInfo } from "@copilotkit/shared/src/telemetry/events";
16
+
17
+ /**
18
+ * Configuration options for TelemetryAgentRunner
19
+ */
20
+ export interface TelemetryAgentRunnerConfig {
21
+ /**
22
+ * The underlying runner to delegate to
23
+ * If not provided, defaults to InMemoryAgentRunner
24
+ */
25
+ runner?: AgentRunner;
26
+
27
+ /**
28
+ * Optional LangSmith API key (will be hashed for telemetry)
29
+ */
30
+ langsmithApiKey?: string;
31
+ }
32
+
33
+ /**
34
+ * An AgentRunner wrapper that adds telemetry tracking for agent executions.
35
+ *
36
+ * Usage:
37
+ * ```ts
38
+ * const runtime = new CopilotRuntime({
39
+ * runner: new TelemetryAgentRunner(),
40
+ * // or with custom runner:
41
+ * runner: new TelemetryAgentRunner({ runner: customRunner }),
42
+ * });
43
+ * ```
44
+ */
45
+ export class TelemetryAgentRunner implements AgentRunner {
46
+ private readonly _runner: AgentRunner;
47
+ private readonly hashedLgcKey: string | undefined;
48
+
49
+ constructor(config?: TelemetryAgentRunnerConfig) {
50
+ this._runner = config?.runner ?? new InMemoryAgentRunner();
51
+ this.hashedLgcKey = config?.langsmithApiKey
52
+ ? createHash("sha256").update(config.langsmithApiKey).digest("hex")
53
+ : undefined;
54
+ }
55
+
56
+ /**
57
+ * Runs an agent with telemetry tracking.
58
+ * Wraps the underlying runner's Observable stream with telemetry events.
59
+ */
60
+ run(
61
+ ...args: Parameters<AgentRunner["run"]>
62
+ ): ReturnType<AgentRunner["run"]> {
63
+ const streamInfo: AgentExecutionResponseInfo = {
64
+ hashedLgcKey: this.hashedLgcKey,
65
+ };
66
+ let streamErrored = false;
67
+
68
+ // Capture stream started event
69
+ telemetry.capture("oss.runtime.agent_execution_stream_started", {
70
+ hashedLgcKey: this.hashedLgcKey,
71
+ });
72
+
73
+ // Delegate to the underlying runner and wrap with telemetry
74
+ return this._runner.run(...args).pipe(
75
+ // Extract metadata from events if available
76
+ tap((event) => {
77
+ // Try to extract provider/model info from raw events
78
+ const rawEvent = (event as { rawEvent?: { metadata?: Record<string, unknown>; data?: Record<string, unknown> } }).rawEvent;
79
+ if (rawEvent?.data) {
80
+ const data = rawEvent.data as { output?: { model?: string } };
81
+ if (data?.output?.model) {
82
+ streamInfo.model = data.output.model;
83
+ streamInfo.provider = data.output.model;
84
+ }
85
+ }
86
+ if (rawEvent?.metadata) {
87
+ const metadata = rawEvent.metadata as { langgraph_host?: string; langgraph_version?: string };
88
+ if (metadata?.langgraph_host) {
89
+ streamInfo.langGraphHost = metadata.langgraph_host;
90
+ }
91
+ if (metadata?.langgraph_version) {
92
+ streamInfo.langGraphVersion = metadata.langgraph_version;
93
+ }
94
+ }
95
+ }),
96
+ catchError((error) => {
97
+ // Capture stream error event
98
+ streamErrored = true;
99
+ telemetry.capture("oss.runtime.agent_execution_stream_errored", {
100
+ ...streamInfo,
101
+ error: error instanceof Error ? error.message : String(error),
102
+ });
103
+ throw error;
104
+ }),
105
+ finalize(() => {
106
+ // Capture stream ended event (only if not errored)
107
+ if (!streamErrored) {
108
+ telemetry.capture("oss.runtime.agent_execution_stream_ended", streamInfo);
109
+ }
110
+ }),
111
+ );
112
+ }
113
+
114
+ /**
115
+ * Delegates to the underlying runner's connect method
116
+ */
117
+ connect(...args: Parameters<AgentRunner["connect"]>): ReturnType<AgentRunner["connect"]> {
118
+ return this._runner.connect(...args);
119
+ }
120
+
121
+ /**
122
+ * Delegates to the underlying runner's isRunning method
123
+ */
124
+ isRunning(...args: Parameters<AgentRunner["isRunning"]>): ReturnType<AgentRunner["isRunning"]> {
125
+ return this._runner.isRunning(...args);
126
+ }
127
+
128
+ /**
129
+ * Delegates to the underlying runner's stop method
130
+ */
131
+ stop(...args: Parameters<AgentRunner["stop"]>): ReturnType<AgentRunner["stop"]> {
132
+ return this._runner.stop(...args);
133
+ }
134
+ }