@copilotkit/runtime 0.0.0-0.0.0-max-changeset-10101010101010-20260109191632

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 (122) hide show
  1. package/.eslintrc.js +7 -0
  2. package/CHANGELOG.md +2905 -0
  3. package/LICENSE +21 -0
  4. package/README.md +76 -0
  5. package/__snapshots__/schema/schema.graphql +371 -0
  6. package/dist/index.d.ts +1495 -0
  7. package/dist/index.js +5644 -0
  8. package/dist/index.js.map +1 -0
  9. package/dist/index.mjs +5601 -0
  10. package/dist/index.mjs.map +1 -0
  11. package/dist/langgraph.d.ts +284 -0
  12. package/dist/langgraph.js +211 -0
  13. package/dist/langgraph.js.map +1 -0
  14. package/dist/langgraph.mjs +206 -0
  15. package/dist/langgraph.mjs.map +1 -0
  16. package/dist/v2/index.d.ts +2 -0
  17. package/dist/v2/index.js +22 -0
  18. package/dist/v2/index.js.map +1 -0
  19. package/dist/v2/index.mjs +5 -0
  20. package/dist/v2/index.mjs.map +1 -0
  21. package/jest.config.js +10 -0
  22. package/package.json +143 -0
  23. package/scripts/generate-gql-schema.ts +13 -0
  24. package/src/agents/langgraph/event-source.ts +329 -0
  25. package/src/agents/langgraph/events.ts +377 -0
  26. package/src/graphql/inputs/action.input.ts +16 -0
  27. package/src/graphql/inputs/agent-session.input.ts +13 -0
  28. package/src/graphql/inputs/agent-state.input.ts +13 -0
  29. package/src/graphql/inputs/cloud-guardrails.input.ts +16 -0
  30. package/src/graphql/inputs/cloud.input.ts +8 -0
  31. package/src/graphql/inputs/context-property.input.ts +10 -0
  32. package/src/graphql/inputs/copilot-context.input.ts +10 -0
  33. package/src/graphql/inputs/custom-property.input.ts +15 -0
  34. package/src/graphql/inputs/extensions.input.ts +21 -0
  35. package/src/graphql/inputs/forwarded-parameters.input.ts +22 -0
  36. package/src/graphql/inputs/frontend.input.ts +14 -0
  37. package/src/graphql/inputs/generate-copilot-response.input.ts +59 -0
  38. package/src/graphql/inputs/load-agent-state.input.ts +10 -0
  39. package/src/graphql/inputs/message.input.ts +110 -0
  40. package/src/graphql/inputs/meta-event.input.ts +18 -0
  41. package/src/graphql/message-conversion/agui-to-gql.test.ts +1263 -0
  42. package/src/graphql/message-conversion/agui-to-gql.ts +333 -0
  43. package/src/graphql/message-conversion/gql-to-agui.test.ts +1580 -0
  44. package/src/graphql/message-conversion/gql-to-agui.ts +278 -0
  45. package/src/graphql/message-conversion/index.ts +2 -0
  46. package/src/graphql/message-conversion/roundtrip-conversion.test.ts +526 -0
  47. package/src/graphql/resolvers/copilot.resolver.ts +708 -0
  48. package/src/graphql/resolvers/state.resolver.ts +27 -0
  49. package/src/graphql/types/agents-response.type.ts +19 -0
  50. package/src/graphql/types/base/index.ts +10 -0
  51. package/src/graphql/types/converted/index.ts +176 -0
  52. package/src/graphql/types/copilot-response.type.ts +138 -0
  53. package/src/graphql/types/enums.ts +38 -0
  54. package/src/graphql/types/extensions-response.type.ts +23 -0
  55. package/src/graphql/types/guardrails-result.type.ts +20 -0
  56. package/src/graphql/types/load-agent-state-response.type.ts +17 -0
  57. package/src/graphql/types/message-status.type.ts +42 -0
  58. package/src/graphql/types/meta-events.type.ts +71 -0
  59. package/src/graphql/types/response-status.type.ts +66 -0
  60. package/src/index.ts +4 -0
  61. package/src/langgraph.ts +1 -0
  62. package/src/lib/cloud/index.ts +4 -0
  63. package/src/lib/error-messages.ts +200 -0
  64. package/src/lib/index.ts +52 -0
  65. package/src/lib/integrations/index.ts +6 -0
  66. package/src/lib/integrations/nest/index.ts +14 -0
  67. package/src/lib/integrations/nextjs/app-router.ts +38 -0
  68. package/src/lib/integrations/nextjs/pages-router.ts +39 -0
  69. package/src/lib/integrations/node-express/index.ts +14 -0
  70. package/src/lib/integrations/node-http/index.ts +143 -0
  71. package/src/lib/integrations/node-http/request-handler.ts +111 -0
  72. package/src/lib/integrations/shared.ts +161 -0
  73. package/src/lib/logger.ts +28 -0
  74. package/src/lib/observability.ts +160 -0
  75. package/src/lib/runtime/__tests__/copilot-runtime-error.test.ts +169 -0
  76. package/src/lib/runtime/__tests__/mcp-tools-utils.test.ts +464 -0
  77. package/src/lib/runtime/agent-integrations/langgraph/agent.ts +209 -0
  78. package/src/lib/runtime/agent-integrations/langgraph/consts.ts +34 -0
  79. package/src/lib/runtime/agent-integrations/langgraph/index.ts +2 -0
  80. package/src/lib/runtime/copilot-runtime.ts +710 -0
  81. package/src/lib/runtime/mcp-tools-utils.ts +254 -0
  82. package/src/lib/runtime/retry-utils.ts +96 -0
  83. package/src/lib/runtime/telemetry-agent-runner.ts +139 -0
  84. package/src/lib/runtime/types.ts +49 -0
  85. package/src/lib/runtime/utils.ts +87 -0
  86. package/src/lib/streaming.ts +202 -0
  87. package/src/lib/telemetry-client.ts +64 -0
  88. package/src/service-adapters/anthropic/anthropic-adapter.ts +452 -0
  89. package/src/service-adapters/anthropic/utils.ts +152 -0
  90. package/src/service-adapters/bedrock/bedrock-adapter.ts +73 -0
  91. package/src/service-adapters/conversion.ts +67 -0
  92. package/src/service-adapters/empty/empty-adapter.ts +38 -0
  93. package/src/service-adapters/events.ts +294 -0
  94. package/src/service-adapters/experimental/ollama/ollama-adapter.ts +84 -0
  95. package/src/service-adapters/google/google-genai-adapter.test.ts +104 -0
  96. package/src/service-adapters/google/google-genai-adapter.ts +88 -0
  97. package/src/service-adapters/groq/groq-adapter.ts +203 -0
  98. package/src/service-adapters/index.ts +18 -0
  99. package/src/service-adapters/langchain/langchain-adapter.ts +111 -0
  100. package/src/service-adapters/langchain/langserve.ts +88 -0
  101. package/src/service-adapters/langchain/types.ts +14 -0
  102. package/src/service-adapters/langchain/utils.ts +313 -0
  103. package/src/service-adapters/openai/openai-adapter.ts +283 -0
  104. package/src/service-adapters/openai/openai-assistant-adapter.ts +344 -0
  105. package/src/service-adapters/openai/utils.ts +199 -0
  106. package/src/service-adapters/service-adapter.ts +41 -0
  107. package/src/service-adapters/shared/error-utils.ts +61 -0
  108. package/src/service-adapters/shared/index.ts +1 -0
  109. package/src/service-adapters/unify/unify-adapter.ts +151 -0
  110. package/src/utils/failed-response-status-reasons.ts +70 -0
  111. package/src/utils/index.ts +1 -0
  112. package/src/v2/index.ts +3 -0
  113. package/tests/global.d.ts +13 -0
  114. package/tests/service-adapters/anthropic/allowlist-approach.test.ts +226 -0
  115. package/tests/service-adapters/anthropic/anthropic-adapter.test.ts +389 -0
  116. package/tests/service-adapters/openai/allowlist-approach.test.ts +238 -0
  117. package/tests/service-adapters/openai/openai-adapter.test.ts +301 -0
  118. package/tests/setup.jest.ts +21 -0
  119. package/tests/tsconfig.json +10 -0
  120. package/tsconfig.json +13 -0
  121. package/tsup.config.ts +20 -0
  122. package/typedoc.json +4 -0
@@ -0,0 +1,710 @@
1
+ /**
2
+ * <Callout type="info">
3
+ * This is the reference for the `CopilotRuntime` class. For more information and example code snippets, please see [Concept: Copilot Runtime](/concepts/copilot-runtime).
4
+ * </Callout>
5
+ *
6
+ * ## Usage
7
+ *
8
+ * ```tsx
9
+ * import { CopilotRuntime } from "@copilotkit/runtime";
10
+ *
11
+ * const copilotKit = new CopilotRuntime();
12
+ * ```
13
+ */
14
+
15
+ import {
16
+ type Action,
17
+ type CopilotErrorHandler,
18
+ CopilotKitMisuseError,
19
+ type MaybePromise,
20
+ type NonEmptyRecord,
21
+ type Parameter,
22
+ readBody,
23
+ getZodParameters,
24
+ type PartialBy,
25
+ isTelemetryDisabled,
26
+ } from "@copilotkit/shared";
27
+ import type { RunAgentInput } from "@ag-ui/core";
28
+ import { aguiToGQL } from "../../graphql/message-conversion/agui-to-gql";
29
+ import type { CopilotServiceAdapter, RemoteChainParameters } from "../../service-adapters";
30
+ import {
31
+ CopilotRuntime as CopilotRuntimeVNext,
32
+ type CopilotRuntimeOptions,
33
+ type CopilotRuntimeOptions as CopilotRuntimeOptionsVNext,
34
+ InMemoryAgentRunner,
35
+ } from "@copilotkitnext/runtime";
36
+ import { TelemetryAgentRunner } from "./telemetry-agent-runner";
37
+ import telemetry from "../telemetry-client";
38
+
39
+ import type { MessageInput } from "../../graphql/inputs/message.input";
40
+ import type { Message } from "../../graphql/types/converted";
41
+
42
+ import {
43
+ EndpointType,
44
+ type EndpointDefinition,
45
+ type CopilotKitEndpoint,
46
+ type LangGraphPlatformEndpoint,
47
+ } from "./types";
48
+
49
+ import type { CopilotObservabilityConfig, LLMRequestData, LLMResponseData } from "../observability";
50
+ import type { AbstractAgent } from "@ag-ui/client";
51
+
52
+ // +++ MCP Imports +++
53
+ import {
54
+ type MCPClient,
55
+ type MCPEndpointConfig,
56
+ type MCPTool,
57
+ extractParametersFromSchema,
58
+ } from "./mcp-tools-utils";
59
+ import { BuiltInAgent, type BuiltInAgentConfiguration } from "@copilotkitnext/agent";
60
+ // Define the function type alias here or import if defined elsewhere
61
+ type CreateMCPClientFunction = (config: MCPEndpointConfig) => Promise<MCPClient>;
62
+
63
+ type ActionsConfiguration<T extends Parameter[] | [] = []> =
64
+ | Action<T>[]
65
+ | ((ctx: { properties: any; url?: string }) => Action<T>[]);
66
+
67
+ interface OnBeforeRequestOptions {
68
+ threadId?: string;
69
+ runId?: string;
70
+ inputMessages: Message[];
71
+ properties: any;
72
+ url?: string;
73
+ }
74
+
75
+ type OnBeforeRequestHandler = (options: OnBeforeRequestOptions) => void | Promise<void>;
76
+
77
+ interface OnAfterRequestOptions {
78
+ threadId: string;
79
+ runId?: string;
80
+ inputMessages: Message[];
81
+ outputMessages: Message[];
82
+ properties: any;
83
+ url?: string;
84
+ }
85
+
86
+ type OnAfterRequestHandler = (options: OnAfterRequestOptions) => void | Promise<void>;
87
+
88
+ interface OnStopGenerationOptions {
89
+ threadId: string;
90
+ runId?: string;
91
+ url?: string;
92
+ agentName?: string;
93
+ lastMessage: MessageInput;
94
+ }
95
+ type OnStopGenerationHandler = (options: OnStopGenerationOptions) => void | Promise<void>;
96
+
97
+ interface Middleware {
98
+ /**
99
+ * A function that is called before the request is processed.
100
+ */
101
+ /**
102
+ * @deprecated This middleware hook is deprecated and will be removed in a future version.
103
+ * Use updated middleware integration methods in CopilotRuntimeVNext instead.
104
+ */
105
+ onBeforeRequest?: OnBeforeRequestHandler;
106
+
107
+ /**
108
+ * A function that is called after the request is processed.
109
+ */
110
+ /**
111
+ * @deprecated This middleware hook is deprecated and will be removed in a future version.
112
+ * Use updated middleware integration methods in CopilotRuntimeVNext instead.
113
+ */
114
+ onAfterRequest?: OnAfterRequestHandler;
115
+ }
116
+
117
+ export interface CopilotRuntimeConstructorParams_BASE<T extends Parameter[] | [] = []> {
118
+ /**
119
+ * Middleware to be used by the runtime.
120
+ *
121
+ * ```ts
122
+ * onBeforeRequest: (options: {
123
+ * threadId?: string;
124
+ * runId?: string;
125
+ * inputMessages: Message[];
126
+ * properties: any;
127
+ * }) => void | Promise<void>;
128
+ * ```
129
+ *
130
+ * ```ts
131
+ * onAfterRequest: (options: {
132
+ * threadId?: string;
133
+ * runId?: string;
134
+ * inputMessages: Message[];
135
+ * outputMessages: Message[];
136
+ * properties: any;
137
+ * }) => void | Promise<void>;
138
+ * ```
139
+ */
140
+ /**
141
+ * @deprecated This middleware hook is deprecated and will be removed in a future version.
142
+ * Use updated middleware integration methods in CopilotRuntimeVNext instead.
143
+ */
144
+ middleware?: Middleware;
145
+
146
+ /*
147
+ * A list of server side actions that can be executed. Will be ignored when remoteActions are set
148
+ */
149
+ actions?: ActionsConfiguration<T>;
150
+
151
+ /*
152
+ * Deprecated: Use `remoteEndpoints`.
153
+ */
154
+ remoteActions?: CopilotKitEndpoint[];
155
+
156
+ /*
157
+ * A list of remote actions that can be executed.
158
+ */
159
+ remoteEndpoints?: EndpointDefinition[];
160
+
161
+ /*
162
+ * An array of LangServer URLs.
163
+ */
164
+ langserve?: RemoteChainParameters[];
165
+
166
+ /*
167
+ * A map of agent names to AGUI agents.
168
+ * Example agent config:
169
+ * ```ts
170
+ * import { AbstractAgent } from "@ag-ui/client";
171
+ * // ...
172
+ * agents: {
173
+ * "support": new CustomerSupportAgent(),
174
+ * "technical": new TechnicalAgent()
175
+ * }
176
+ * ```
177
+ */
178
+ agents?: Record<string, AbstractAgent>;
179
+
180
+ /*
181
+ * Delegates agent state processing to the service adapter.
182
+ *
183
+ * When enabled, individual agent state requests will not be processed by the agent itself.
184
+ * Instead, all processing will be handled by the service adapter.
185
+ */
186
+ delegateAgentProcessingToServiceAdapter?: boolean;
187
+
188
+ /**
189
+ * Configuration for LLM request/response logging.
190
+ * Requires publicApiKey from CopilotKit component to be set:
191
+ *
192
+ * ```tsx
193
+ * <CopilotKit publicApiKey="ck_pub_..." />
194
+ * ```
195
+ *
196
+ * Example logging config:
197
+ * ```ts
198
+ * logging: {
199
+ * enabled: true, // Enable or disable logging
200
+ * progressive: true, // Set to false for buffered logging
201
+ * logger: {
202
+ * logRequest: (data) => langfuse.trace({ name: "LLM Request", input: data }),
203
+ * logResponse: (data) => langfuse.trace({ name: "LLM Response", output: data }),
204
+ * logError: (errorData) => langfuse.trace({ name: "LLM Error", metadata: errorData }),
205
+ * },
206
+ * }
207
+ * ```
208
+ */
209
+ observability_c?: CopilotObservabilityConfig;
210
+
211
+ /**
212
+ * Configuration for connecting to Model Context Protocol (MCP) servers.
213
+ * Allows fetching and using tools defined on external MCP-compliant servers.
214
+ * Requires providing the `createMCPClient` function during instantiation.
215
+ * @experimental
216
+ */
217
+ mcpServers?: MCPEndpointConfig[];
218
+
219
+ /**
220
+ * A function that creates an MCP client instance for a given endpoint configuration.
221
+ * This function is responsible for using the appropriate MCP client library
222
+ * (e.g., `@copilotkit/runtime`, `ai`) to establish a connection.
223
+ * Required if `mcpServers` is provided.
224
+ *
225
+ * ```typescript
226
+ * import { experimental_createMCPClient } from "ai"; // Import from vercel ai library
227
+ * // ...
228
+ * const runtime = new CopilotRuntime({
229
+ * mcpServers: [{ endpoint: "..." }],
230
+ * async createMCPClient(config) {
231
+ * return await experimental_createMCPClient({
232
+ * transport: {
233
+ * type: "sse",
234
+ * url: config.endpoint,
235
+ * headers: config.apiKey
236
+ * ? { Authorization: `Bearer ${config.apiKey}` }
237
+ * : undefined,
238
+ * },
239
+ * });
240
+ * }
241
+ * });
242
+ * ```
243
+ */
244
+ createMCPClient?: CreateMCPClientFunction;
245
+
246
+ /**
247
+ * Optional error handler for comprehensive debugging and observability.
248
+ *
249
+ * **Requires publicApiKey**: Error handling only works when requests include a valid publicApiKey.
250
+ * This is a premium Copilot Cloud feature.
251
+ *
252
+ * @param errorEvent - Structured error event with rich debugging context
253
+ *
254
+ * @example
255
+ * ```typescript
256
+ * const runtime = new CopilotRuntime({
257
+ * onError: (errorEvent) => {
258
+ * debugDashboard.capture(errorEvent);
259
+ * }
260
+ * });
261
+ * ```
262
+ */
263
+ onError?: CopilotErrorHandler;
264
+
265
+ onStopGeneration?: OnStopGenerationHandler;
266
+
267
+ // /** Optional transcription service for audio processing. */
268
+ // transcriptionService?: CopilotRuntimeOptionsVNext["transcriptionService"];
269
+ // /** Optional *before* middleware – callback function or webhook URL. */
270
+ // beforeRequestMiddleware?: CopilotRuntimeOptionsVNext["beforeRequestMiddleware"];
271
+ // /** Optional *after* middleware – callback function or webhook URL. */
272
+ // afterRequestMiddleware?: CopilotRuntimeOptionsVNext["afterRequestMiddleware"];
273
+ }
274
+
275
+ type BeforeRequestMiddleware = CopilotRuntimeOptionsVNext["beforeRequestMiddleware"];
276
+ type AfterRequestMiddleware = CopilotRuntimeOptionsVNext["afterRequestMiddleware"];
277
+ type BeforeRequestMiddlewareFn = Exclude<BeforeRequestMiddleware, string>;
278
+ type BeforeRequestMiddlewareFnParameters = Parameters<BeforeRequestMiddlewareFn>;
279
+ type BeforeRequestMiddlewareFnResult = ReturnType<BeforeRequestMiddlewareFn>;
280
+ type AfterRequestMiddlewareFn = Exclude<AfterRequestMiddleware, string>;
281
+ type AfterRequestMiddlewareFnParameters = Parameters<AfterRequestMiddlewareFn>;
282
+
283
+ interface CopilotRuntimeConstructorParams<T extends Parameter[] | [] = []>
284
+ extends Omit<CopilotRuntimeConstructorParams_BASE<T>, "agents">,
285
+ Omit<CopilotRuntimeOptionsVNext, "agents" | "transcriptionService"> {
286
+ /**
287
+ * TODO: un-omit `transcriptionService` above once it's supported
288
+ *
289
+ * This satisfies...
290
+ * – the optional constraint in `CopilotRuntimeConstructorParams_BASE`
291
+ * – the `MaybePromise<NonEmptyRecord<T>>` constraint in `CopilotRuntimeOptionsVNext`
292
+ * – the `Record<string, AbstractAgent>` constraint in `both
293
+ */
294
+ agents?: MaybePromise<NonEmptyRecord<Record<string, AbstractAgent>>>;
295
+ }
296
+
297
+ /**
298
+ * Central runtime object passed to all request handlers.
299
+ */
300
+ export class CopilotRuntime<const T extends Parameter[] | [] = []> {
301
+ params?: CopilotRuntimeConstructorParams<T>;
302
+ private observability?: CopilotObservabilityConfig;
303
+ // Cache MCP tools per endpoint to avoid re-fetching repeatedly
304
+ private mcpToolsCache: Map<string, BuiltInAgentConfiguration["tools"]> = new Map();
305
+ private runtimeArgs: CopilotRuntimeOptions;
306
+ private _instance: CopilotRuntimeVNext;
307
+
308
+ constructor(
309
+ params?: CopilotRuntimeConstructorParams<T> & PartialBy<CopilotRuntimeOptions, "agents">,
310
+ ) {
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
+
324
+ this.runtimeArgs = {
325
+ agents: { ...endpointAgents, ...agents },
326
+ runner,
327
+ // TODO: add support for transcriptionService from CopilotRuntimeOptionsVNext once it is ready
328
+ // transcriptionService: params?.transcriptionService,
329
+
330
+ beforeRequestMiddleware: this.createOnBeforeRequestHandler(params).bind(this),
331
+ afterRequestMiddleware: this.createOnAfterRequestHandler(params).bind(this),
332
+ };
333
+ this.params = params;
334
+ this.observability = params?.observability_c;
335
+ }
336
+
337
+ get instance() {
338
+ if (!this._instance) {
339
+ this._instance = new CopilotRuntimeVNext(this.runtimeArgs);
340
+ }
341
+
342
+ return this._instance;
343
+ }
344
+
345
+ private assignEndpointsToAgents(
346
+ endpoints: CopilotRuntimeConstructorParams<T>["remoteEndpoints"],
347
+ ): Record<string, AbstractAgent> {
348
+ let result: Record<string, AbstractAgent> = {};
349
+
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
+ }
360
+
361
+ return result;
362
+ }
363
+
364
+ handleServiceAdapter(serviceAdapter: CopilotServiceAdapter) {
365
+ this.runtimeArgs.agents = Promise.resolve(this.runtimeArgs.agents ?? {}).then(
366
+ async (agents) => {
367
+ let agentsList = agents;
368
+ const isAgentsListEmpty = !Object.keys(agents).length;
369
+ const hasServiceAdapter = Boolean(serviceAdapter);
370
+ const illegalServiceAdapterNames = ["EmptyAdapter"];
371
+ const serviceAdapterCanBeUsedForAgent = !illegalServiceAdapterNames.includes(
372
+ serviceAdapter.name,
373
+ );
374
+
375
+ if (isAgentsListEmpty && (!hasServiceAdapter || !serviceAdapterCanBeUsedForAgent)) {
376
+ throw new CopilotKitMisuseError({
377
+ message:
378
+ "No default agent provided. Please provide a default agent in the runtime config.",
379
+ });
380
+ }
381
+
382
+ if (isAgentsListEmpty) {
383
+ agentsList.default = new BuiltInAgent({
384
+ model: `${serviceAdapter.provider}/${serviceAdapter.model}`,
385
+ });
386
+ }
387
+
388
+ const actions = this.params?.actions;
389
+ if (actions) {
390
+ const mcpTools = await this.getToolsFromMCP();
391
+ agentsList = this.assignToolsToAgents(agents, [
392
+ ...this.getToolsFromActions(actions),
393
+ ...mcpTools,
394
+ ]);
395
+ }
396
+
397
+ return agentsList;
398
+ },
399
+ );
400
+ }
401
+
402
+ // Receive this.params.action and turn it into the AbstractAgent tools
403
+ private getToolsFromActions(
404
+ actions: ActionsConfiguration<any>,
405
+ ): BuiltInAgentConfiguration["tools"] {
406
+ // Resolve actions to an array (handle function case)
407
+ const actionsArray =
408
+ typeof actions === "function" ? actions({ properties: {}, url: undefined }) : actions;
409
+
410
+ // Convert each Action to a ToolDefinition
411
+ return actionsArray.map((action) => {
412
+ // Convert JSON schema to Zod schema
413
+ const zodSchema = getZodParameters(action.parameters || []);
414
+
415
+ return {
416
+ name: action.name,
417
+ description: action.description || "",
418
+ parameters: zodSchema,
419
+ execute: () => Promise.resolve(),
420
+ };
421
+ });
422
+ }
423
+
424
+ private assignToolsToAgents(
425
+ agents: Record<string, AbstractAgent>,
426
+ tools: BuiltInAgentConfiguration["tools"],
427
+ ): Record<string, AbstractAgent> {
428
+ if (!tools?.length) {
429
+ return agents;
430
+ }
431
+
432
+ const enrichedAgents: Record<string, AbstractAgent> = { ...agents };
433
+
434
+ for (const [agentId, agent] of Object.entries(enrichedAgents)) {
435
+ const existingConfig = (Reflect.get(agent, "config") ?? {}) as BuiltInAgentConfiguration;
436
+ const existingTools = existingConfig.tools ?? [];
437
+
438
+ const updatedConfig: BuiltInAgentConfiguration = {
439
+ ...existingConfig,
440
+ tools: [...existingTools, ...tools],
441
+ };
442
+
443
+ Reflect.set(agent, "config", updatedConfig);
444
+ enrichedAgents[agentId] = agent;
445
+ }
446
+
447
+ return enrichedAgents;
448
+ }
449
+
450
+ private createOnBeforeRequestHandler(
451
+ params?: CopilotRuntimeConstructorParams<T> & PartialBy<CopilotRuntimeOptions, "agents">,
452
+ ) {
453
+ return async (hookParams: BeforeRequestMiddlewareFnParameters[0]) => {
454
+ const { request } = hookParams;
455
+
456
+ // Capture telemetry for copilot request creation
457
+ const publicApiKey = request.headers.get("x-copilotcloud-public-api-key");
458
+ const body = (await readBody(request)) as RunAgentInput;
459
+ const forwardedProps = body.forwardedProps as
460
+ | {
461
+ cloud?: { guardrails?: unknown };
462
+ metadata?: { requestType?: string };
463
+ }
464
+ | undefined;
465
+
466
+ // Get cloud base URL from environment or default
467
+ const cloudBaseUrl = 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
+
477
+ // TODO: get public api key and run with expected data
478
+ // if (this.observability?.enabled && this.params.publicApiKey) {
479
+ // this.logObservabilityBeforeRequest()
480
+ // }
481
+
482
+ // TODO: replace hooksParams top argument type with BeforeRequestMiddlewareParameters when exported
483
+ params?.beforeRequestMiddleware?.(hookParams);
484
+
485
+ if (params?.middleware?.onBeforeRequest) {
486
+ const { request, runtime, path } = hookParams;
487
+ const gqlMessages = (aguiToGQL(body.messages) as Message[]).reduce(
488
+ (acc, msg) => {
489
+ if ("role" in msg && msg.role === "user") {
490
+ acc.inputMessages.push(msg);
491
+ } else {
492
+ acc.outputMessages.push(msg);
493
+ }
494
+ return acc;
495
+ },
496
+ { inputMessages: [] as Message[], outputMessages: [] as Message[] },
497
+ );
498
+ const { inputMessages, outputMessages } = gqlMessages;
499
+ params.middleware.onBeforeRequest({
500
+ threadId: body.threadId,
501
+ runId: body.runId,
502
+ inputMessages,
503
+ properties: body.forwardedProps,
504
+ url: request.url,
505
+ } satisfies OnBeforeRequestOptions);
506
+ }
507
+ };
508
+ }
509
+
510
+ private createOnAfterRequestHandler(
511
+ params?: CopilotRuntimeConstructorParams<T> & PartialBy<CopilotRuntimeOptions, "agents">,
512
+ ) {
513
+ return async (hookParams: AfterRequestMiddlewareFnParameters[0]) => {
514
+ // TODO: get public api key and run with expected data
515
+ // if (this.observability?.enabled && publicApiKey) {
516
+ // this.logObservabilityAfterRequest()
517
+ // }
518
+
519
+ // TODO: replace hooksParams top argument type with AfterRequestMiddlewareParameters when exported
520
+ params?.afterRequestMiddleware?.(hookParams);
521
+
522
+ if (params?.middleware?.onAfterRequest) {
523
+ // TODO: provide old expected params here when available
524
+ // @ts-expect-error -- missing arguments.
525
+ params.middleware.onAfterRequest({});
526
+ }
527
+ };
528
+ }
529
+
530
+ // Observability Methods
531
+
532
+ /**
533
+ * Log LLM request if observability is enabled
534
+ */
535
+ private async logObservabilityBeforeRequest(requestData: LLMRequestData): Promise<void> {
536
+ try {
537
+ await this.observability.hooks.handleRequest(requestData);
538
+ } catch (error) {
539
+ console.error("Error logging LLM request:", error);
540
+ }
541
+ }
542
+
543
+ /**
544
+ * Log final LLM response after request completes
545
+ */
546
+ private logObservabilityAfterRequest(
547
+ outputMessagesPromise: Promise<Message[]>,
548
+ baseData: {
549
+ threadId: string;
550
+ runId?: string;
551
+ model?: string;
552
+ provider?: string;
553
+ agentName?: string;
554
+ nodeName?: string;
555
+ },
556
+ streamedChunks: any[],
557
+ requestStartTime: number,
558
+ publicApiKey?: string,
559
+ ): void {
560
+ try {
561
+ outputMessagesPromise
562
+ .then((outputMessages) => {
563
+ const responseData: LLMResponseData = {
564
+ threadId: baseData.threadId,
565
+ runId: baseData.runId,
566
+ model: baseData.model,
567
+ // Use collected chunks for progressive mode or outputMessages for regular mode
568
+ output: this.observability.progressive ? streamedChunks : outputMessages,
569
+ latency: Date.now() - requestStartTime,
570
+ timestamp: Date.now(),
571
+ provider: baseData.provider,
572
+ isFinalResponse: true,
573
+ agentName: baseData.agentName,
574
+ nodeName: baseData.nodeName,
575
+ };
576
+
577
+ try {
578
+ this.observability.hooks.handleResponse(responseData);
579
+ } catch (logError) {
580
+ console.error("Error logging LLM response:", logError);
581
+ }
582
+ })
583
+ .catch((error) => {
584
+ console.error("Failed to get output messages for logging:", error);
585
+ });
586
+ } catch (error) {
587
+ console.error("Error setting up logging for LLM response:", error);
588
+ }
589
+ }
590
+
591
+ // Resolve MCP tools to BuiltInAgent tool definitions
592
+ // Optionally accepts request-scoped properties to merge request-provided mcpServers
593
+ private async getToolsFromMCP(options?: {
594
+ properties?: Record<string, unknown>;
595
+ }): Promise<BuiltInAgentConfiguration["tools"]> {
596
+ const runtimeMcpServers = (this.params?.mcpServers ?? []) as MCPEndpointConfig[];
597
+ const createMCPClient = this.params?.createMCPClient as CreateMCPClientFunction | undefined;
598
+
599
+ // If no runtime config and no request overrides, nothing to do
600
+ const requestMcpServers = ((
601
+ options?.properties as { mcpServers?: MCPEndpointConfig[] } | undefined
602
+ )?.mcpServers ??
603
+ (options?.properties as { mcpEndpoints?: MCPEndpointConfig[] } | undefined)?.mcpEndpoints ??
604
+ []) as MCPEndpointConfig[];
605
+
606
+ const hasAnyServers =
607
+ (runtimeMcpServers?.length ?? 0) > 0 || (requestMcpServers?.length ?? 0) > 0;
608
+ if (!hasAnyServers) {
609
+ return [];
610
+ }
611
+
612
+ if (!createMCPClient) {
613
+ // Mirror legacy behavior: when servers are provided without a factory, treat as misconfiguration
614
+ throw new CopilotKitMisuseError({
615
+ message:
616
+ "MCP Integration Error: `mcpServers` were provided, but the `createMCPClient` function was not passed to the CopilotRuntime constructor. Please provide an implementation for `createMCPClient`.",
617
+ });
618
+ }
619
+
620
+ // Merge and dedupe endpoints by URL; request-level overrides take precedence
621
+ const effectiveEndpoints = (() => {
622
+ const byUrl = new Map<string, MCPEndpointConfig>();
623
+ for (const ep of runtimeMcpServers) {
624
+ if (ep?.endpoint) byUrl.set(ep.endpoint, ep);
625
+ }
626
+ for (const ep of requestMcpServers) {
627
+ if (ep?.endpoint) byUrl.set(ep.endpoint, ep);
628
+ }
629
+ return Array.from(byUrl.values());
630
+ })();
631
+
632
+ const allTools: BuiltInAgentConfiguration["tools"] = [];
633
+
634
+ for (const config of effectiveEndpoints) {
635
+ const endpointUrl = config.endpoint;
636
+ // Return cached tool definitions when available
637
+ const cached = this.mcpToolsCache.get(endpointUrl);
638
+ if (cached) {
639
+ allTools.push(...cached);
640
+ continue;
641
+ }
642
+
643
+ try {
644
+ const client = await createMCPClient(config);
645
+ const toolsMap = await client.tools();
646
+
647
+ const toolDefs: BuiltInAgentConfiguration["tools"] = Object.entries(toolsMap).map(
648
+ ([toolName, tool]: [string, MCPTool]) => {
649
+ const params: Parameter[] = extractParametersFromSchema(tool);
650
+ const zodSchema = getZodParameters(params);
651
+ return {
652
+ name: toolName,
653
+ description: tool.description || `MCP tool: ${toolName} (from ${endpointUrl})`,
654
+ parameters: zodSchema,
655
+ execute: () => Promise.resolve(),
656
+ };
657
+ },
658
+ );
659
+
660
+ // Cache per endpoint and add to aggregate
661
+ this.mcpToolsCache.set(endpointUrl, toolDefs);
662
+ allTools.push(...toolDefs);
663
+ } catch (error) {
664
+ console.error(
665
+ `MCP: Failed to fetch tools from endpoint ${endpointUrl}. Skipping. Error:`,
666
+ error,
667
+ );
668
+ // Cache empty to prevent repeated attempts within lifecycle
669
+ this.mcpToolsCache.set(endpointUrl, []);
670
+ }
671
+ }
672
+
673
+ // Dedupe tools by name while preserving last-in wins (request overrides)
674
+ const dedupedByName = new Map<string, (typeof allTools)[number]>();
675
+ for (const tool of allTools) {
676
+ dedupedByName.set(tool.name, tool);
677
+ }
678
+
679
+ return Array.from(dedupedByName.values());
680
+ }
681
+ }
682
+
683
+ // The two functions below are "factory functions", meant to create the action objects that adhere to the expected interfaces
684
+ export function copilotKitEndpoint(config: Omit<CopilotKitEndpoint, "type">): CopilotKitEndpoint {
685
+ return {
686
+ ...config,
687
+ type: EndpointType.CopilotKit,
688
+ };
689
+ }
690
+
691
+ export function langGraphPlatformEndpoint(
692
+ config: Omit<LangGraphPlatformEndpoint, "type">,
693
+ ): LangGraphPlatformEndpoint {
694
+ return {
695
+ ...config,
696
+ type: EndpointType.LangGraphPlatform,
697
+ };
698
+ }
699
+
700
+ export function resolveEndpointType(endpoint: EndpointDefinition) {
701
+ if (!endpoint.type) {
702
+ if ("deploymentUrl" in endpoint && "agents" in endpoint) {
703
+ return EndpointType.LangGraphPlatform;
704
+ } else {
705
+ return EndpointType.CopilotKit;
706
+ }
707
+ }
708
+
709
+ return endpoint.type;
710
+ }