@copilotkit/runtime 1.56.4 → 1.56.5

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.
@@ -1 +1 @@
1
- {"version":3,"file":"agent.mjs","names":["LangGraphAgent","AGUILangGraphAgent"],"sources":["../../../../../src/lib/runtime/agent-integrations/langgraph/agent.ts"],"sourcesContent":["import { map, Observable } from \"rxjs\";\nimport { LangGraphEventTypes } from \"../../../../agents/langgraph/events\";\nimport { BaseEvent, RawEvent } from \"@ag-ui/core\";\nimport {\n LangGraphAgent as AGUILangGraphAgent,\n LangGraphHttpAgent,\n type LangGraphAgentConfig,\n ProcessedEvents,\n SchemaKeys,\n type State,\n StateEnrichment,\n} from \"@ag-ui/langgraph\";\nimport { Message as LangGraphMessage } from \"@langchain/langgraph-sdk/dist/types.messages\";\nimport { ThreadState } from \"@langchain/langgraph-sdk\";\n\ninterface CopilotKitStateEnrichment {\n copilotkit: {\n actions: StateEnrichment[\"ag-ui\"][\"tools\"];\n context: StateEnrichment[\"ag-ui\"][\"context\"];\n };\n}\n\nimport { RunAgentInput, EventType, CustomEvent } from \"@ag-ui/client\";\n\n// Import and re-export from separate file to maintain API compatibility\nimport {\n CustomEventNames,\n TextMessageEvents,\n ToolCallEvents,\n PredictStateTool,\n} from \"./consts\";\nexport { CustomEventNames };\n\nexport class LangGraphAgent extends AGUILangGraphAgent {\n constructor(config: LangGraphAgentConfig) {\n super(config);\n }\n\n dispatchEvent(event: ProcessedEvents) {\n if (event.type === EventType.CUSTOM) {\n // const event = processedEvent as unknown as CustomEvent;\n const customEvent = event as unknown as CustomEvent;\n\n if (customEvent.name === CustomEventNames.CopilotKitManuallyEmitMessage) {\n this.subscriber.next({\n type: EventType.TEXT_MESSAGE_START,\n role: \"assistant\",\n messageId: customEvent.value.message_id,\n rawEvent: event,\n });\n this.subscriber.next({\n type: EventType.TEXT_MESSAGE_CONTENT,\n messageId: customEvent.value.message_id,\n delta: customEvent.value.message,\n rawEvent: event,\n });\n this.subscriber.next({\n type: EventType.TEXT_MESSAGE_END,\n messageId: customEvent.value.message_id,\n rawEvent: event,\n });\n return true;\n }\n\n if (\n customEvent.name === CustomEventNames.CopilotKitManuallyEmitToolCall\n ) {\n this.subscriber.next({\n type: EventType.TOOL_CALL_START,\n toolCallId: customEvent.value.id,\n toolCallName: customEvent.value.name,\n parentMessageId: customEvent.value.id,\n rawEvent: event,\n });\n this.subscriber.next({\n type: EventType.TOOL_CALL_ARGS,\n toolCallId: customEvent.value.id,\n delta: customEvent.value.args,\n rawEvent: event,\n });\n this.subscriber.next({\n type: EventType.TOOL_CALL_END,\n toolCallId: customEvent.value.id,\n rawEvent: event,\n });\n return true;\n }\n\n if (\n customEvent.name ===\n CustomEventNames.CopilotKitManuallyEmitIntermediateState\n ) {\n this.activeRun.manuallyEmittedState = customEvent.value;\n this.dispatchEvent({\n type: EventType.STATE_SNAPSHOT,\n snapshot: this.getStateSnapshot({\n values: this.activeRun.manuallyEmittedState,\n } as ThreadState<State>),\n rawEvent: event,\n });\n return true;\n }\n\n if (customEvent.name === CustomEventNames.CopilotKitExit) {\n this.subscriber.next({\n type: EventType.CUSTOM,\n name: \"Exit\",\n value: true,\n });\n return true;\n }\n }\n\n // Intercept all text message and tool call events and check if should disable\n const rawEvent = (event as ToolCallEvents | TextMessageEvents).rawEvent;\n if (!rawEvent) {\n this.subscriber.next(event);\n return true;\n }\n\n const isMessageEvent =\n event.type === EventType.TEXT_MESSAGE_START ||\n event.type === EventType.TEXT_MESSAGE_CONTENT ||\n event.type === EventType.TEXT_MESSAGE_END;\n const isToolEvent =\n event.type === EventType.TOOL_CALL_START ||\n event.type === EventType.TOOL_CALL_ARGS ||\n event.type === EventType.TOOL_CALL_END;\n if (\"copilotkit:emit-tool-calls\" in (rawEvent.metadata || {})) {\n if (\n rawEvent.metadata[\"copilotkit:emit-tool-calls\"] === false &&\n isToolEvent\n ) {\n return false;\n }\n }\n if (\"copilotkit:emit-messages\" in (rawEvent.metadata || {})) {\n if (\n rawEvent.metadata[\"copilotkit:emit-messages\"] === false &&\n isMessageEvent\n ) {\n // Clean up tracked message state to prevent stale records from\n // leaking into subsequent nodes that have emit-messages enabled.\n if (this.activeRun?.id) {\n this.messagesInProcess[this.activeRun.id] = null;\n }\n return false;\n }\n }\n\n this.subscriber.next(event);\n return true;\n }\n\n // @ts-ignore\n run(input: RunAgentInput): Observable<BaseEvent> {\n return super.run(input).pipe(\n map((processedEvent) => {\n // Turn raw event into emit state snapshot from tool call event\n if (processedEvent.type === EventType.RAW) {\n // Get the LangGraph event from the AGUI event.\n const event =\n (processedEvent as RawEvent).event ??\n (processedEvent as RawEvent).rawEvent;\n\n const eventType = event.event;\n const toolCallData = event.data?.chunk?.tool_call_chunks?.[0];\n const toolCallUsedToPredictState = event.metadata?.[\n \"copilotkit:emit-intermediate-state\"\n ]?.some(\n (predictStateTool: PredictStateTool) =>\n predictStateTool.tool === toolCallData?.name,\n );\n\n if (\n eventType === LangGraphEventTypes.OnChatModelStream &&\n toolCallUsedToPredictState\n ) {\n return {\n type: EventType.CUSTOM,\n name: \"PredictState\",\n value: event.metadata[\"copilotkit:emit-intermediate-state\"],\n };\n }\n }\n\n return processedEvent;\n }),\n );\n }\n\n langGraphDefaultMergeState(\n state: State,\n messages: LangGraphMessage[],\n input: RunAgentInput,\n ): State<StateEnrichment & CopilotKitStateEnrichment> {\n const aguiMergedState = super.langGraphDefaultMergeState(\n state,\n messages,\n input,\n );\n const { tools: returnedTools, \"ag-ui\": agui } = aguiMergedState;\n // tolerate undefined and de-duplicate by stable key (id | name | key)\n const rawCombinedTools = [\n ...((returnedTools as any[]) ?? []),\n ...((agui?.tools as any[]) ?? []),\n ];\n const combinedTools = Array.from(\n new Map(\n rawCombinedTools.map((t: any) => [\n t?.id ?? t?.name ?? t?.key ?? JSON.stringify(t),\n t,\n ]),\n ).values(),\n );\n\n return {\n ...aguiMergedState,\n copilotkit: {\n actions: combinedTools,\n context: agui?.context ?? [],\n },\n };\n }\n\n async getSchemaKeys(): Promise<SchemaKeys> {\n const CONSTANT_KEYS = [\"copilotkit\"];\n const schemaKeys = await super.getSchemaKeys();\n return {\n config: schemaKeys.config,\n input: schemaKeys.input ? [...schemaKeys.input, ...CONSTANT_KEYS] : null,\n output: schemaKeys.output\n ? [...schemaKeys.output, ...CONSTANT_KEYS]\n : null,\n context: schemaKeys.context\n ? [...schemaKeys.context, ...CONSTANT_KEYS]\n : null,\n };\n }\n}\n\nexport { LangGraphHttpAgent };\n"],"mappings":"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"}
1
+ {"version":3,"file":"agent.mjs","names":["LangGraphAgent","AGUILangGraphAgent"],"sources":["../../../../../src/lib/runtime/agent-integrations/langgraph/agent.ts"],"sourcesContent":["import { map, Observable } from \"rxjs\";\nimport { LangGraphEventTypes } from \"../../../../agents/langgraph/events\";\nimport { BaseEvent, RawEvent } from \"@ag-ui/core\";\nimport {\n LangGraphAgent as AGUILangGraphAgent,\n LangGraphHttpAgent,\n type LangGraphAgentConfig,\n ProcessedEvents,\n SchemaKeys,\n type State,\n StateEnrichment,\n} from \"@ag-ui/langgraph\";\nimport { Message as LangGraphMessage } from \"@langchain/langgraph-sdk/dist/types.messages\";\nimport { ThreadState } from \"@langchain/langgraph-sdk\";\n\ninterface CopilotKitStateEnrichment {\n copilotkit: {\n actions: StateEnrichment[\"ag-ui\"][\"tools\"];\n context: StateEnrichment[\"ag-ui\"][\"context\"];\n };\n}\n\nimport { RunAgentInput, EventType, CustomEvent } from \"@ag-ui/client\";\n\n// Import and re-export from separate file to maintain API compatibility\nimport {\n CustomEventNames,\n TextMessageEvents,\n ToolCallEvents,\n PredictStateTool,\n} from \"./consts\";\nexport { CustomEventNames };\n\nexport class LangGraphAgent extends AGUILangGraphAgent {\n constructor(config: LangGraphAgentConfig) {\n super(config);\n }\n\n dispatchEvent(event: ProcessedEvents) {\n if (event.type === EventType.CUSTOM) {\n // const event = processedEvent as unknown as CustomEvent;\n const customEvent = event as unknown as CustomEvent;\n\n if (customEvent.name === CustomEventNames.CopilotKitManuallyEmitMessage) {\n this.subscriber.next({\n type: EventType.TEXT_MESSAGE_START,\n role: \"assistant\",\n messageId: customEvent.value.message_id,\n rawEvent: event,\n });\n this.subscriber.next({\n type: EventType.TEXT_MESSAGE_CONTENT,\n messageId: customEvent.value.message_id,\n delta: customEvent.value.message,\n rawEvent: event,\n });\n this.subscriber.next({\n type: EventType.TEXT_MESSAGE_END,\n messageId: customEvent.value.message_id,\n rawEvent: event,\n });\n return true;\n }\n\n if (\n customEvent.name === CustomEventNames.CopilotKitManuallyEmitToolCall\n ) {\n this.subscriber.next({\n type: EventType.TOOL_CALL_START,\n toolCallId: customEvent.value.id,\n toolCallName: customEvent.value.name,\n parentMessageId: customEvent.value.id,\n rawEvent: event,\n });\n this.subscriber.next({\n type: EventType.TOOL_CALL_ARGS,\n toolCallId: customEvent.value.id,\n delta: customEvent.value.args,\n rawEvent: event,\n });\n this.subscriber.next({\n type: EventType.TOOL_CALL_END,\n toolCallId: customEvent.value.id,\n rawEvent: event,\n });\n return true;\n }\n\n if (\n customEvent.name ===\n CustomEventNames.CopilotKitManuallyEmitIntermediateState\n ) {\n this.activeRun.manuallyEmittedState = customEvent.value;\n this.dispatchEvent({\n type: EventType.STATE_SNAPSHOT,\n snapshot: this.getStateSnapshot({\n values: this.activeRun.manuallyEmittedState,\n } as ThreadState<State>),\n rawEvent: event,\n });\n return true;\n }\n\n if (customEvent.name === CustomEventNames.CopilotKitExit) {\n this.subscriber.next({\n type: EventType.CUSTOM,\n name: \"Exit\",\n value: true,\n });\n return true;\n }\n }\n\n // Intercept all text message and tool call events and check if should disable\n const rawEvent = (event as ToolCallEvents | TextMessageEvents).rawEvent;\n if (!rawEvent) {\n this.subscriber.next(event);\n return true;\n }\n\n const isMessageEvent =\n event.type === EventType.TEXT_MESSAGE_START ||\n event.type === EventType.TEXT_MESSAGE_CONTENT ||\n event.type === EventType.TEXT_MESSAGE_END;\n const isToolEvent =\n event.type === EventType.TOOL_CALL_START ||\n event.type === EventType.TOOL_CALL_ARGS ||\n event.type === EventType.TOOL_CALL_END;\n if (\"copilotkit:emit-tool-calls\" in (rawEvent.metadata || {})) {\n if (\n rawEvent.metadata[\"copilotkit:emit-tool-calls\"] === false &&\n isToolEvent\n ) {\n return false;\n }\n }\n if (\"copilotkit:emit-messages\" in (rawEvent.metadata || {})) {\n if (\n rawEvent.metadata[\"copilotkit:emit-messages\"] === false &&\n isMessageEvent\n ) {\n // Clean up tracked message state to prevent stale records from\n // leaking into subsequent nodes that have emit-messages enabled.\n if (this.activeRun?.id) {\n this.messagesInProcess[this.activeRun.id] = null;\n }\n return false;\n }\n }\n\n this.subscriber.next(event);\n return true;\n }\n\n // @ts-ignore\n run(input: RunAgentInput): Observable<BaseEvent> {\n const enrichedInput = {\n ...input,\n forwardedProps: {\n ...input.forwardedProps,\n streamSubgraphs: input.forwardedProps?.streamSubgraphs ?? true,\n },\n };\n return super.run(enrichedInput).pipe(\n map((processedEvent) => {\n // Turn raw event into emit state snapshot from tool call event\n if (processedEvent.type === EventType.RAW) {\n // Get the LangGraph event from the AGUI event.\n const event =\n (processedEvent as RawEvent).event ??\n (processedEvent as RawEvent).rawEvent;\n\n const eventType = event.event;\n const toolCallData = event.data?.chunk?.tool_call_chunks?.[0];\n const toolCallUsedToPredictState = event.metadata?.[\n \"copilotkit:emit-intermediate-state\"\n ]?.some(\n (predictStateTool: PredictStateTool) =>\n predictStateTool.tool === toolCallData?.name,\n );\n\n if (\n eventType === LangGraphEventTypes.OnChatModelStream &&\n toolCallUsedToPredictState\n ) {\n return {\n type: EventType.CUSTOM,\n name: \"PredictState\",\n value: event.metadata[\"copilotkit:emit-intermediate-state\"],\n };\n }\n }\n\n return processedEvent;\n }),\n );\n }\n\n langGraphDefaultMergeState(\n state: State,\n messages: LangGraphMessage[],\n input: RunAgentInput,\n ): State<StateEnrichment & CopilotKitStateEnrichment> {\n const aguiMergedState = super.langGraphDefaultMergeState(\n state,\n messages,\n input,\n );\n const { tools: returnedTools, \"ag-ui\": agui } = aguiMergedState;\n // tolerate undefined and de-duplicate by stable key (id | name | key)\n const rawCombinedTools = [\n ...((returnedTools as any[]) ?? []),\n ...((agui?.tools as any[]) ?? []),\n ];\n const combinedTools = Array.from(\n new Map(\n rawCombinedTools.map((t: any) => [\n t?.id ?? t?.name ?? t?.key ?? JSON.stringify(t),\n t,\n ]),\n ).values(),\n );\n\n return {\n ...aguiMergedState,\n copilotkit: {\n actions: combinedTools,\n context: agui?.context ?? [],\n },\n };\n }\n\n async getSchemaKeys(): Promise<SchemaKeys> {\n const CONSTANT_KEYS = [\"copilotkit\"];\n const schemaKeys = await super.getSchemaKeys();\n return {\n config: schemaKeys.config,\n input: schemaKeys.input ? [...schemaKeys.input, ...CONSTANT_KEYS] : null,\n output: schemaKeys.output\n ? [...schemaKeys.output, ...CONSTANT_KEYS]\n : null,\n context: schemaKeys.context\n ? [...schemaKeys.context, ...CONSTANT_KEYS]\n : null,\n };\n }\n}\n\nexport { LangGraphHttpAgent };\n"],"mappings":"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"}
package/dist/package.cjs CHANGED
@@ -5,7 +5,7 @@ const require_runtime = require('./_virtual/_rolldown/runtime.cjs');
5
5
  var require_package = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
6
6
  module.exports = {
7
7
  "name": "@copilotkit/runtime",
8
- "version": "1.56.4",
8
+ "version": "1.56.5",
9
9
  "private": false,
10
10
  "keywords": [
11
11
  "ai",
@@ -82,10 +82,10 @@ var require_package = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, m
82
82
  },
83
83
  "dependencies": {
84
84
  "@ag-ui/a2ui-middleware": "0.0.5",
85
- "@ag-ui/client": "0.0.52",
86
- "@ag-ui/core": "0.0.52",
87
- "@ag-ui/encoder": "0.0.52",
88
- "@ag-ui/langgraph": "0.0.29",
85
+ "@ag-ui/client": "0.0.53",
86
+ "@ag-ui/core": "0.0.53",
87
+ "@ag-ui/encoder": "0.0.53",
88
+ "@ag-ui/langgraph": "0.0.31",
89
89
  "@ag-ui/mcp-apps-middleware": "0.0.3",
90
90
  "@ai-sdk/anthropic": "^3.0.49",
91
91
  "@ai-sdk/google": "^3.0.33",
@@ -123,7 +123,7 @@ var require_package = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, m
123
123
  "zod": "^3.23.3"
124
124
  },
125
125
  "devDependencies": {
126
- "@copilotkit/aimock": "^1.10.0",
126
+ "@copilotkit/aimock": "latest",
127
127
  "@swc/core": "1.5.28",
128
128
  "@types/cors": "^2.8.17",
129
129
  "@types/express": "^4.17.21",
package/dist/package.mjs CHANGED
@@ -5,7 +5,7 @@ import { __commonJSMin } from "./_virtual/_rolldown/runtime.mjs";
5
5
  var require_package = /* @__PURE__ */ __commonJSMin(((exports, module) => {
6
6
  module.exports = {
7
7
  "name": "@copilotkit/runtime",
8
- "version": "1.56.4",
8
+ "version": "1.56.5",
9
9
  "private": false,
10
10
  "keywords": [
11
11
  "ai",
@@ -82,10 +82,10 @@ var require_package = /* @__PURE__ */ __commonJSMin(((exports, module) => {
82
82
  },
83
83
  "dependencies": {
84
84
  "@ag-ui/a2ui-middleware": "0.0.5",
85
- "@ag-ui/client": "0.0.52",
86
- "@ag-ui/core": "0.0.52",
87
- "@ag-ui/encoder": "0.0.52",
88
- "@ag-ui/langgraph": "0.0.29",
85
+ "@ag-ui/client": "0.0.53",
86
+ "@ag-ui/core": "0.0.53",
87
+ "@ag-ui/encoder": "0.0.53",
88
+ "@ag-ui/langgraph": "0.0.31",
89
89
  "@ag-ui/mcp-apps-middleware": "0.0.3",
90
90
  "@ai-sdk/anthropic": "^3.0.49",
91
91
  "@ai-sdk/google": "^3.0.33",
@@ -123,7 +123,7 @@ var require_package = /* @__PURE__ */ __commonJSMin(((exports, module) => {
123
123
  "zod": "^3.23.3"
124
124
  },
125
125
  "devDependencies": {
126
- "@copilotkit/aimock": "^1.10.0",
126
+ "@copilotkit/aimock": "latest",
127
127
  "@swc/core": "1.5.28",
128
128
  "@types/cors": "^2.8.17",
129
129
  "@types/express": "^4.17.21",
@@ -44,13 +44,13 @@ function createCopilotExpressHandler({ runtime, basePath, mode = "multi-route",
44
44
  if (mode === "single-route") {
45
45
  router.post(normalizedBase, expressHandler);
46
46
  router.options(normalizedBase, expressHandler);
47
- } else if (normalizedBase === "/") router.all("*", expressHandler);
48
- else {
49
- router.all(`${normalizedBase}/*`, expressHandler);
50
- router.all(normalizedBase, expressHandler);
51
- }
47
+ } else if (normalizedBase === "/") router.all(/.*/, expressHandler);
48
+ else router.all(new RegExp(`^${escapeRegExp(normalizedBase)}(\\/.*)?$`), expressHandler);
52
49
  return router;
53
50
  }
51
+ function escapeRegExp(s) {
52
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
53
+ }
54
54
  function normalizeBasePath(path) {
55
55
  if (!path) throw new Error("basePath must be provided for Express endpoint");
56
56
  if (!path.startsWith("/")) return `/${path}`;
@@ -1 +1 @@
1
- {"version":3,"file":"express.cjs","names":["createExpressNodeHandler","createCopilotRuntimeHandler"],"sources":["../../../../src/v2/runtime/endpoints/express.ts"],"sourcesContent":["import express from \"express\";\nimport type {\n Request as ExpressRequest,\n Response as ExpressResponse,\n NextFunction,\n Router,\n} from \"express\";\nimport cors from \"cors\";\nimport type { CorsOptions } from \"cors\";\nimport type { CopilotRuntimeLike } from \"../core/runtime\";\nimport { createCopilotRuntimeHandler } from \"../core/fetch-handler\";\nimport { createExpressNodeHandler } from \"./express-fetch-bridge\";\nimport type { CopilotRuntimeHooks } from \"../core/hooks\";\n\nexport interface CopilotExpressEndpointParams {\n runtime: CopilotRuntimeLike;\n basePath: string;\n\n /**\n * Endpoint mode.\n * - `\"multi-route\"` (default): separate routes for each operation\n * - `\"single-route\"`: single POST endpoint with JSON envelope dispatch\n */\n mode?: \"multi-route\" | \"single-route\";\n\n /**\n * CORS configuration for the Express router.\n * - `true` (default): permissive CORS (`origin: \"*\"`, all methods, all headers).\n * - `false`: no CORS middleware is applied — handle it yourself.\n * - object: passed directly to the Express `cors()` middleware.\n */\n cors?: boolean | CorsOptions;\n\n /**\n * Lifecycle hooks for request processing.\n */\n hooks?: CopilotRuntimeHooks;\n}\n\n/**\n * Creates an Express router that serves the CopilotKit runtime.\n *\n * In **multi-route** mode (default) the router exposes:\n * - `GET {basePath}/info` — runtime info\n * - `POST {basePath}/agent/:agentId/run` — start an agent run\n * - `POST {basePath}/agent/:agentId/connect` — connect to an agent run\n * - `POST {basePath}/agent/:agentId/stop/:threadId` — stop an agent run\n * - `POST {basePath}/transcribe` — transcribe audio\n *\n * In **single-route** mode a single `POST {basePath}` endpoint accepts a JSON\n * envelope `{ method, params, body }` and dispatches to the appropriate handler.\n *\n * @example\n * ```typescript\n * import express from \"express\";\n * import { CopilotRuntime } from \"@copilotkit/runtime/v2\";\n * import { createCopilotExpressHandler } from \"@copilotkit/runtime/v2/express\";\n *\n * const runtime = new CopilotRuntime({\n * agents: { default: new BuiltInAgent({ model: \"openai/gpt-4o-mini\" }) },\n * });\n *\n * const app = express();\n * app.use(createCopilotExpressHandler({\n * runtime,\n * basePath: \"/api/copilotkit\",\n * cors: true,\n * }));\n * app.listen(4000);\n * ```\n *\n * @example Single-route mode with lifecycle hooks\n * ```typescript\n * app.use(createCopilotExpressHandler({\n * runtime,\n * basePath: \"/api/copilotkit\",\n * mode: \"single-route\",\n * hooks: {\n * onRequest: ({ request }) => {\n * if (!request.headers.get(\"authorization\")) {\n * throw new Response(\"Unauthorized\", { status: 401 });\n * }\n * },\n * },\n * }));\n * ```\n */\n/** @deprecated Use `createCopilotExpressHandler` instead. */\nexport { createCopilotExpressHandler as createCopilotEndpointExpress };\n\nexport function createCopilotExpressHandler({\n runtime,\n basePath,\n mode = \"multi-route\",\n cors: corsOption = true,\n hooks,\n}: CopilotExpressEndpointParams): Router {\n const normalizedBase = normalizeBasePath(basePath);\n\n const handler = createCopilotRuntimeHandler({\n runtime,\n basePath: normalizedBase,\n mode,\n cors: false, // CORS is handled at the Express middleware layer\n hooks,\n });\n\n const nodeHandler = createExpressNodeHandler(handler);\n\n const expressHandler = async (\n req: ExpressRequest,\n res: ExpressResponse,\n next: NextFunction,\n ) => {\n try {\n await nodeHandler(req, res);\n } catch (err) {\n next(err);\n }\n };\n\n const router = express.Router();\n\n // CORS middleware\n if (corsOption) {\n const corsConfig: CorsOptions =\n corsOption === true\n ? {\n origin: \"*\",\n methods: [\n \"GET\",\n \"HEAD\",\n \"PUT\",\n \"POST\",\n \"DELETE\",\n \"PATCH\",\n \"OPTIONS\",\n ],\n allowedHeaders: [\"*\"],\n }\n : corsOption;\n router.use(cors(corsConfig));\n }\n\n // Route mounting\n if (mode === \"single-route\") {\n router.post(normalizedBase, expressHandler);\n router.options(normalizedBase, expressHandler);\n } else if (normalizedBase === \"/\") {\n router.all(\"*\", expressHandler);\n } else {\n router.all(`${normalizedBase}/*`, expressHandler);\n router.all(normalizedBase, expressHandler);\n }\n\n return router;\n}\n\nfunction normalizeBasePath(path: string): string {\n if (!path) {\n throw new Error(\"basePath must be provided for Express endpoint\");\n }\n\n if (!path.startsWith(\"/\")) {\n return `/${path}`;\n }\n\n if (path.length > 1 && path.endsWith(\"/\")) {\n return path.slice(0, -1);\n }\n\n return path;\n}\n"],"mappings":";;;;;;;;;;AA0FA,SAAgB,4BAA4B,EAC1C,SACA,UACA,OAAO,eACP,MAAM,aAAa,MACnB,SACuC;CACvC,MAAM,iBAAiB,kBAAkB,SAAS;CAUlD,MAAM,cAAcA,sDARJC,kDAA4B;EAC1C;EACA,UAAU;EACV;EACA,MAAM;EACN;EACD,CAAC,CAEmD;CAErD,MAAM,iBAAiB,OACrB,KACA,KACA,SACG;AACH,MAAI;AACF,SAAM,YAAY,KAAK,IAAI;WACpB,KAAK;AACZ,QAAK,IAAI;;;CAIb,MAAM,SAAS,gBAAQ,QAAQ;AAG/B,KAAI,YAAY;EACd,MAAM,aACJ,eAAe,OACX;GACE,QAAQ;GACR,SAAS;IACP;IACA;IACA;IACA;IACA;IACA;IACA;IACD;GACD,gBAAgB,CAAC,IAAI;GACtB,GACD;AACN,SAAO,sBAAS,WAAW,CAAC;;AAI9B,KAAI,SAAS,gBAAgB;AAC3B,SAAO,KAAK,gBAAgB,eAAe;AAC3C,SAAO,QAAQ,gBAAgB,eAAe;YACrC,mBAAmB,IAC5B,QAAO,IAAI,KAAK,eAAe;MAC1B;AACL,SAAO,IAAI,GAAG,eAAe,KAAK,eAAe;AACjD,SAAO,IAAI,gBAAgB,eAAe;;AAG5C,QAAO;;AAGT,SAAS,kBAAkB,MAAsB;AAC/C,KAAI,CAAC,KACH,OAAM,IAAI,MAAM,iDAAiD;AAGnE,KAAI,CAAC,KAAK,WAAW,IAAI,CACvB,QAAO,IAAI;AAGb,KAAI,KAAK,SAAS,KAAK,KAAK,SAAS,IAAI,CACvC,QAAO,KAAK,MAAM,GAAG,GAAG;AAG1B,QAAO"}
1
+ {"version":3,"file":"express.cjs","names":["createExpressNodeHandler","createCopilotRuntimeHandler"],"sources":["../../../../src/v2/runtime/endpoints/express.ts"],"sourcesContent":["import express from \"express\";\nimport type {\n Request as ExpressRequest,\n Response as ExpressResponse,\n NextFunction,\n Router,\n} from \"express\";\nimport cors from \"cors\";\nimport type { CorsOptions } from \"cors\";\nimport type { CopilotRuntimeLike } from \"../core/runtime\";\nimport { createCopilotRuntimeHandler } from \"../core/fetch-handler\";\nimport { createExpressNodeHandler } from \"./express-fetch-bridge\";\nimport type { CopilotRuntimeHooks } from \"../core/hooks\";\n\nexport interface CopilotExpressEndpointParams {\n runtime: CopilotRuntimeLike;\n basePath: string;\n\n /**\n * Endpoint mode.\n * - `\"multi-route\"` (default): separate routes for each operation\n * - `\"single-route\"`: single POST endpoint with JSON envelope dispatch\n */\n mode?: \"multi-route\" | \"single-route\";\n\n /**\n * CORS configuration for the Express router.\n * - `true` (default): permissive CORS (`origin: \"*\"`, all methods, all headers).\n * - `false`: no CORS middleware is applied — handle it yourself.\n * - object: passed directly to the Express `cors()` middleware.\n */\n cors?: boolean | CorsOptions;\n\n /**\n * Lifecycle hooks for request processing.\n */\n hooks?: CopilotRuntimeHooks;\n}\n\n/**\n * Creates an Express router that serves the CopilotKit runtime.\n *\n * In **multi-route** mode (default) the router exposes:\n * - `GET {basePath}/info` — runtime info\n * - `POST {basePath}/agent/:agentId/run` — start an agent run\n * - `POST {basePath}/agent/:agentId/connect` — connect to an agent run\n * - `POST {basePath}/agent/:agentId/stop/:threadId` — stop an agent run\n * - `POST {basePath}/transcribe` — transcribe audio\n *\n * In **single-route** mode a single `POST {basePath}` endpoint accepts a JSON\n * envelope `{ method, params, body }` and dispatches to the appropriate handler.\n *\n * @example\n * ```typescript\n * import express from \"express\";\n * import { CopilotRuntime } from \"@copilotkit/runtime/v2\";\n * import { createCopilotExpressHandler } from \"@copilotkit/runtime/v2/express\";\n *\n * const runtime = new CopilotRuntime({\n * agents: { default: new BuiltInAgent({ model: \"openai/gpt-4o-mini\" }) },\n * });\n *\n * const app = express();\n * app.use(createCopilotExpressHandler({\n * runtime,\n * basePath: \"/api/copilotkit\",\n * cors: true,\n * }));\n * app.listen(4000);\n * ```\n *\n * @example Single-route mode with lifecycle hooks\n * ```typescript\n * app.use(createCopilotExpressHandler({\n * runtime,\n * basePath: \"/api/copilotkit\",\n * mode: \"single-route\",\n * hooks: {\n * onRequest: ({ request }) => {\n * if (!request.headers.get(\"authorization\")) {\n * throw new Response(\"Unauthorized\", { status: 401 });\n * }\n * },\n * },\n * }));\n * ```\n */\n/** @deprecated Use `createCopilotExpressHandler` instead. */\nexport { createCopilotExpressHandler as createCopilotEndpointExpress };\n\nexport function createCopilotExpressHandler({\n runtime,\n basePath,\n mode = \"multi-route\",\n cors: corsOption = true,\n hooks,\n}: CopilotExpressEndpointParams): Router {\n const normalizedBase = normalizeBasePath(basePath);\n\n const handler = createCopilotRuntimeHandler({\n runtime,\n basePath: normalizedBase,\n mode,\n cors: false, // CORS is handled at the Express middleware layer\n hooks,\n });\n\n const nodeHandler = createExpressNodeHandler(handler);\n\n const expressHandler = async (\n req: ExpressRequest,\n res: ExpressResponse,\n next: NextFunction,\n ) => {\n try {\n await nodeHandler(req, res);\n } catch (err) {\n next(err);\n }\n };\n\n const router = express.Router();\n\n // CORS middleware\n if (corsOption) {\n const corsConfig: CorsOptions =\n corsOption === true\n ? {\n origin: \"*\",\n methods: [\n \"GET\",\n \"HEAD\",\n \"PUT\",\n \"POST\",\n \"DELETE\",\n \"PATCH\",\n \"OPTIONS\",\n ],\n allowedHeaders: [\"*\"],\n }\n : corsOption;\n router.use(cors(corsConfig));\n }\n\n // Route mounting\n if (mode === \"single-route\") {\n router.post(normalizedBase, expressHandler);\n router.options(normalizedBase, expressHandler);\n } else if (normalizedBase === \"/\") {\n router.all(/.*/, expressHandler);\n } else {\n router.all(\n new RegExp(`^${escapeRegExp(normalizedBase)}(\\\\/.*)?$`),\n expressHandler,\n );\n }\n\n return router;\n}\n\nfunction escapeRegExp(s: string): string {\n return s.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\nfunction normalizeBasePath(path: string): string {\n if (!path) {\n throw new Error(\"basePath must be provided for Express endpoint\");\n }\n\n if (!path.startsWith(\"/\")) {\n return `/${path}`;\n }\n\n if (path.length > 1 && path.endsWith(\"/\")) {\n return path.slice(0, -1);\n }\n\n return path;\n}\n"],"mappings":";;;;;;;;;;AA0FA,SAAgB,4BAA4B,EAC1C,SACA,UACA,OAAO,eACP,MAAM,aAAa,MACnB,SACuC;CACvC,MAAM,iBAAiB,kBAAkB,SAAS;CAUlD,MAAM,cAAcA,sDARJC,kDAA4B;EAC1C;EACA,UAAU;EACV;EACA,MAAM;EACN;EACD,CAAC,CAEmD;CAErD,MAAM,iBAAiB,OACrB,KACA,KACA,SACG;AACH,MAAI;AACF,SAAM,YAAY,KAAK,IAAI;WACpB,KAAK;AACZ,QAAK,IAAI;;;CAIb,MAAM,SAAS,gBAAQ,QAAQ;AAG/B,KAAI,YAAY;EACd,MAAM,aACJ,eAAe,OACX;GACE,QAAQ;GACR,SAAS;IACP;IACA;IACA;IACA;IACA;IACA;IACA;IACD;GACD,gBAAgB,CAAC,IAAI;GACtB,GACD;AACN,SAAO,sBAAS,WAAW,CAAC;;AAI9B,KAAI,SAAS,gBAAgB;AAC3B,SAAO,KAAK,gBAAgB,eAAe;AAC3C,SAAO,QAAQ,gBAAgB,eAAe;YACrC,mBAAmB,IAC5B,QAAO,IAAI,MAAM,eAAe;KAEhC,QAAO,IACL,IAAI,OAAO,IAAI,aAAa,eAAe,CAAC,WAAW,EACvD,eACD;AAGH,QAAO;;AAGT,SAAS,aAAa,GAAmB;AACvC,QAAO,EAAE,QAAQ,uBAAuB,OAAO;;AAGjD,SAAS,kBAAkB,MAAsB;AAC/C,KAAI,CAAC,KACH,OAAM,IAAI,MAAM,iDAAiD;AAGnE,KAAI,CAAC,KAAK,WAAW,IAAI,CACvB,QAAO,IAAI;AAGb,KAAI,KAAK,SAAS,KAAK,KAAK,SAAS,IAAI,CACvC,QAAO,KAAK,MAAM,GAAG,GAAG;AAG1B,QAAO"}
@@ -41,13 +41,13 @@ function createCopilotExpressHandler({ runtime, basePath, mode = "multi-route",
41
41
  if (mode === "single-route") {
42
42
  router.post(normalizedBase, expressHandler);
43
43
  router.options(normalizedBase, expressHandler);
44
- } else if (normalizedBase === "/") router.all("*", expressHandler);
45
- else {
46
- router.all(`${normalizedBase}/*`, expressHandler);
47
- router.all(normalizedBase, expressHandler);
48
- }
44
+ } else if (normalizedBase === "/") router.all(/.*/, expressHandler);
45
+ else router.all(new RegExp(`^${escapeRegExp(normalizedBase)}(\\/.*)?$`), expressHandler);
49
46
  return router;
50
47
  }
48
+ function escapeRegExp(s) {
49
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
50
+ }
51
51
  function normalizeBasePath(path) {
52
52
  if (!path) throw new Error("basePath must be provided for Express endpoint");
53
53
  if (!path.startsWith("/")) return `/${path}`;
@@ -1 +1 @@
1
- {"version":3,"file":"express.mjs","names":[],"sources":["../../../../src/v2/runtime/endpoints/express.ts"],"sourcesContent":["import express from \"express\";\nimport type {\n Request as ExpressRequest,\n Response as ExpressResponse,\n NextFunction,\n Router,\n} from \"express\";\nimport cors from \"cors\";\nimport type { CorsOptions } from \"cors\";\nimport type { CopilotRuntimeLike } from \"../core/runtime\";\nimport { createCopilotRuntimeHandler } from \"../core/fetch-handler\";\nimport { createExpressNodeHandler } from \"./express-fetch-bridge\";\nimport type { CopilotRuntimeHooks } from \"../core/hooks\";\n\nexport interface CopilotExpressEndpointParams {\n runtime: CopilotRuntimeLike;\n basePath: string;\n\n /**\n * Endpoint mode.\n * - `\"multi-route\"` (default): separate routes for each operation\n * - `\"single-route\"`: single POST endpoint with JSON envelope dispatch\n */\n mode?: \"multi-route\" | \"single-route\";\n\n /**\n * CORS configuration for the Express router.\n * - `true` (default): permissive CORS (`origin: \"*\"`, all methods, all headers).\n * - `false`: no CORS middleware is applied — handle it yourself.\n * - object: passed directly to the Express `cors()` middleware.\n */\n cors?: boolean | CorsOptions;\n\n /**\n * Lifecycle hooks for request processing.\n */\n hooks?: CopilotRuntimeHooks;\n}\n\n/**\n * Creates an Express router that serves the CopilotKit runtime.\n *\n * In **multi-route** mode (default) the router exposes:\n * - `GET {basePath}/info` — runtime info\n * - `POST {basePath}/agent/:agentId/run` — start an agent run\n * - `POST {basePath}/agent/:agentId/connect` — connect to an agent run\n * - `POST {basePath}/agent/:agentId/stop/:threadId` — stop an agent run\n * - `POST {basePath}/transcribe` — transcribe audio\n *\n * In **single-route** mode a single `POST {basePath}` endpoint accepts a JSON\n * envelope `{ method, params, body }` and dispatches to the appropriate handler.\n *\n * @example\n * ```typescript\n * import express from \"express\";\n * import { CopilotRuntime } from \"@copilotkit/runtime/v2\";\n * import { createCopilotExpressHandler } from \"@copilotkit/runtime/v2/express\";\n *\n * const runtime = new CopilotRuntime({\n * agents: { default: new BuiltInAgent({ model: \"openai/gpt-4o-mini\" }) },\n * });\n *\n * const app = express();\n * app.use(createCopilotExpressHandler({\n * runtime,\n * basePath: \"/api/copilotkit\",\n * cors: true,\n * }));\n * app.listen(4000);\n * ```\n *\n * @example Single-route mode with lifecycle hooks\n * ```typescript\n * app.use(createCopilotExpressHandler({\n * runtime,\n * basePath: \"/api/copilotkit\",\n * mode: \"single-route\",\n * hooks: {\n * onRequest: ({ request }) => {\n * if (!request.headers.get(\"authorization\")) {\n * throw new Response(\"Unauthorized\", { status: 401 });\n * }\n * },\n * },\n * }));\n * ```\n */\n/** @deprecated Use `createCopilotExpressHandler` instead. */\nexport { createCopilotExpressHandler as createCopilotEndpointExpress };\n\nexport function createCopilotExpressHandler({\n runtime,\n basePath,\n mode = \"multi-route\",\n cors: corsOption = true,\n hooks,\n}: CopilotExpressEndpointParams): Router {\n const normalizedBase = normalizeBasePath(basePath);\n\n const handler = createCopilotRuntimeHandler({\n runtime,\n basePath: normalizedBase,\n mode,\n cors: false, // CORS is handled at the Express middleware layer\n hooks,\n });\n\n const nodeHandler = createExpressNodeHandler(handler);\n\n const expressHandler = async (\n req: ExpressRequest,\n res: ExpressResponse,\n next: NextFunction,\n ) => {\n try {\n await nodeHandler(req, res);\n } catch (err) {\n next(err);\n }\n };\n\n const router = express.Router();\n\n // CORS middleware\n if (corsOption) {\n const corsConfig: CorsOptions =\n corsOption === true\n ? {\n origin: \"*\",\n methods: [\n \"GET\",\n \"HEAD\",\n \"PUT\",\n \"POST\",\n \"DELETE\",\n \"PATCH\",\n \"OPTIONS\",\n ],\n allowedHeaders: [\"*\"],\n }\n : corsOption;\n router.use(cors(corsConfig));\n }\n\n // Route mounting\n if (mode === \"single-route\") {\n router.post(normalizedBase, expressHandler);\n router.options(normalizedBase, expressHandler);\n } else if (normalizedBase === \"/\") {\n router.all(\"*\", expressHandler);\n } else {\n router.all(`${normalizedBase}/*`, expressHandler);\n router.all(normalizedBase, expressHandler);\n }\n\n return router;\n}\n\nfunction normalizeBasePath(path: string): string {\n if (!path) {\n throw new Error(\"basePath must be provided for Express endpoint\");\n }\n\n if (!path.startsWith(\"/\")) {\n return `/${path}`;\n }\n\n if (path.length > 1 && path.endsWith(\"/\")) {\n return path.slice(0, -1);\n }\n\n return path;\n}\n"],"mappings":";;;;;;;AA0FA,SAAgB,4BAA4B,EAC1C,SACA,UACA,OAAO,eACP,MAAM,aAAa,MACnB,SACuC;CACvC,MAAM,iBAAiB,kBAAkB,SAAS;CAUlD,MAAM,cAAc,yBARJ,4BAA4B;EAC1C;EACA,UAAU;EACV;EACA,MAAM;EACN;EACD,CAAC,CAEmD;CAErD,MAAM,iBAAiB,OACrB,KACA,KACA,SACG;AACH,MAAI;AACF,SAAM,YAAY,KAAK,IAAI;WACpB,KAAK;AACZ,QAAK,IAAI;;;CAIb,MAAM,SAAS,QAAQ,QAAQ;AAG/B,KAAI,YAAY;EACd,MAAM,aACJ,eAAe,OACX;GACE,QAAQ;GACR,SAAS;IACP;IACA;IACA;IACA;IACA;IACA;IACA;IACD;GACD,gBAAgB,CAAC,IAAI;GACtB,GACD;AACN,SAAO,IAAI,KAAK,WAAW,CAAC;;AAI9B,KAAI,SAAS,gBAAgB;AAC3B,SAAO,KAAK,gBAAgB,eAAe;AAC3C,SAAO,QAAQ,gBAAgB,eAAe;YACrC,mBAAmB,IAC5B,QAAO,IAAI,KAAK,eAAe;MAC1B;AACL,SAAO,IAAI,GAAG,eAAe,KAAK,eAAe;AACjD,SAAO,IAAI,gBAAgB,eAAe;;AAG5C,QAAO;;AAGT,SAAS,kBAAkB,MAAsB;AAC/C,KAAI,CAAC,KACH,OAAM,IAAI,MAAM,iDAAiD;AAGnE,KAAI,CAAC,KAAK,WAAW,IAAI,CACvB,QAAO,IAAI;AAGb,KAAI,KAAK,SAAS,KAAK,KAAK,SAAS,IAAI,CACvC,QAAO,KAAK,MAAM,GAAG,GAAG;AAG1B,QAAO"}
1
+ {"version":3,"file":"express.mjs","names":[],"sources":["../../../../src/v2/runtime/endpoints/express.ts"],"sourcesContent":["import express from \"express\";\nimport type {\n Request as ExpressRequest,\n Response as ExpressResponse,\n NextFunction,\n Router,\n} from \"express\";\nimport cors from \"cors\";\nimport type { CorsOptions } from \"cors\";\nimport type { CopilotRuntimeLike } from \"../core/runtime\";\nimport { createCopilotRuntimeHandler } from \"../core/fetch-handler\";\nimport { createExpressNodeHandler } from \"./express-fetch-bridge\";\nimport type { CopilotRuntimeHooks } from \"../core/hooks\";\n\nexport interface CopilotExpressEndpointParams {\n runtime: CopilotRuntimeLike;\n basePath: string;\n\n /**\n * Endpoint mode.\n * - `\"multi-route\"` (default): separate routes for each operation\n * - `\"single-route\"`: single POST endpoint with JSON envelope dispatch\n */\n mode?: \"multi-route\" | \"single-route\";\n\n /**\n * CORS configuration for the Express router.\n * - `true` (default): permissive CORS (`origin: \"*\"`, all methods, all headers).\n * - `false`: no CORS middleware is applied — handle it yourself.\n * - object: passed directly to the Express `cors()` middleware.\n */\n cors?: boolean | CorsOptions;\n\n /**\n * Lifecycle hooks for request processing.\n */\n hooks?: CopilotRuntimeHooks;\n}\n\n/**\n * Creates an Express router that serves the CopilotKit runtime.\n *\n * In **multi-route** mode (default) the router exposes:\n * - `GET {basePath}/info` — runtime info\n * - `POST {basePath}/agent/:agentId/run` — start an agent run\n * - `POST {basePath}/agent/:agentId/connect` — connect to an agent run\n * - `POST {basePath}/agent/:agentId/stop/:threadId` — stop an agent run\n * - `POST {basePath}/transcribe` — transcribe audio\n *\n * In **single-route** mode a single `POST {basePath}` endpoint accepts a JSON\n * envelope `{ method, params, body }` and dispatches to the appropriate handler.\n *\n * @example\n * ```typescript\n * import express from \"express\";\n * import { CopilotRuntime } from \"@copilotkit/runtime/v2\";\n * import { createCopilotExpressHandler } from \"@copilotkit/runtime/v2/express\";\n *\n * const runtime = new CopilotRuntime({\n * agents: { default: new BuiltInAgent({ model: \"openai/gpt-4o-mini\" }) },\n * });\n *\n * const app = express();\n * app.use(createCopilotExpressHandler({\n * runtime,\n * basePath: \"/api/copilotkit\",\n * cors: true,\n * }));\n * app.listen(4000);\n * ```\n *\n * @example Single-route mode with lifecycle hooks\n * ```typescript\n * app.use(createCopilotExpressHandler({\n * runtime,\n * basePath: \"/api/copilotkit\",\n * mode: \"single-route\",\n * hooks: {\n * onRequest: ({ request }) => {\n * if (!request.headers.get(\"authorization\")) {\n * throw new Response(\"Unauthorized\", { status: 401 });\n * }\n * },\n * },\n * }));\n * ```\n */\n/** @deprecated Use `createCopilotExpressHandler` instead. */\nexport { createCopilotExpressHandler as createCopilotEndpointExpress };\n\nexport function createCopilotExpressHandler({\n runtime,\n basePath,\n mode = \"multi-route\",\n cors: corsOption = true,\n hooks,\n}: CopilotExpressEndpointParams): Router {\n const normalizedBase = normalizeBasePath(basePath);\n\n const handler = createCopilotRuntimeHandler({\n runtime,\n basePath: normalizedBase,\n mode,\n cors: false, // CORS is handled at the Express middleware layer\n hooks,\n });\n\n const nodeHandler = createExpressNodeHandler(handler);\n\n const expressHandler = async (\n req: ExpressRequest,\n res: ExpressResponse,\n next: NextFunction,\n ) => {\n try {\n await nodeHandler(req, res);\n } catch (err) {\n next(err);\n }\n };\n\n const router = express.Router();\n\n // CORS middleware\n if (corsOption) {\n const corsConfig: CorsOptions =\n corsOption === true\n ? {\n origin: \"*\",\n methods: [\n \"GET\",\n \"HEAD\",\n \"PUT\",\n \"POST\",\n \"DELETE\",\n \"PATCH\",\n \"OPTIONS\",\n ],\n allowedHeaders: [\"*\"],\n }\n : corsOption;\n router.use(cors(corsConfig));\n }\n\n // Route mounting\n if (mode === \"single-route\") {\n router.post(normalizedBase, expressHandler);\n router.options(normalizedBase, expressHandler);\n } else if (normalizedBase === \"/\") {\n router.all(/.*/, expressHandler);\n } else {\n router.all(\n new RegExp(`^${escapeRegExp(normalizedBase)}(\\\\/.*)?$`),\n expressHandler,\n );\n }\n\n return router;\n}\n\nfunction escapeRegExp(s: string): string {\n return s.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\nfunction normalizeBasePath(path: string): string {\n if (!path) {\n throw new Error(\"basePath must be provided for Express endpoint\");\n }\n\n if (!path.startsWith(\"/\")) {\n return `/${path}`;\n }\n\n if (path.length > 1 && path.endsWith(\"/\")) {\n return path.slice(0, -1);\n }\n\n return path;\n}\n"],"mappings":";;;;;;;AA0FA,SAAgB,4BAA4B,EAC1C,SACA,UACA,OAAO,eACP,MAAM,aAAa,MACnB,SACuC;CACvC,MAAM,iBAAiB,kBAAkB,SAAS;CAUlD,MAAM,cAAc,yBARJ,4BAA4B;EAC1C;EACA,UAAU;EACV;EACA,MAAM;EACN;EACD,CAAC,CAEmD;CAErD,MAAM,iBAAiB,OACrB,KACA,KACA,SACG;AACH,MAAI;AACF,SAAM,YAAY,KAAK,IAAI;WACpB,KAAK;AACZ,QAAK,IAAI;;;CAIb,MAAM,SAAS,QAAQ,QAAQ;AAG/B,KAAI,YAAY;EACd,MAAM,aACJ,eAAe,OACX;GACE,QAAQ;GACR,SAAS;IACP;IACA;IACA;IACA;IACA;IACA;IACA;IACD;GACD,gBAAgB,CAAC,IAAI;GACtB,GACD;AACN,SAAO,IAAI,KAAK,WAAW,CAAC;;AAI9B,KAAI,SAAS,gBAAgB;AAC3B,SAAO,KAAK,gBAAgB,eAAe;AAC3C,SAAO,QAAQ,gBAAgB,eAAe;YACrC,mBAAmB,IAC5B,QAAO,IAAI,MAAM,eAAe;KAEhC,QAAO,IACL,IAAI,OAAO,IAAI,aAAa,eAAe,CAAC,WAAW,EACvD,eACD;AAGH,QAAO;;AAGT,SAAS,aAAa,GAAmB;AACvC,QAAO,EAAE,QAAQ,uBAAuB,OAAO;;AAGjD,SAAS,kBAAkB,MAAsB;AAC/C,KAAI,CAAC,KACH,OAAM,IAAI,MAAM,iDAAiD;AAGnE,KAAI,CAAC,KAAK,WAAW,IAAI,CACvB,QAAO,IAAI;AAGb,KAAI,KAAK,SAAS,KAAK,KAAK,SAAS,IAAI,CACvC,QAAO,KAAK,MAAM,GAAG,GAAG;AAG1B,QAAO"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@copilotkit/runtime",
3
- "version": "1.56.4",
3
+ "version": "1.56.5",
4
4
  "private": false,
5
5
  "keywords": [
6
6
  "ai",
@@ -77,10 +77,10 @@
77
77
  },
78
78
  "dependencies": {
79
79
  "@ag-ui/a2ui-middleware": "0.0.5",
80
- "@ag-ui/client": "0.0.52",
81
- "@ag-ui/core": "0.0.52",
82
- "@ag-ui/encoder": "0.0.52",
83
- "@ag-ui/langgraph": "0.0.29",
80
+ "@ag-ui/client": "0.0.53",
81
+ "@ag-ui/core": "0.0.53",
82
+ "@ag-ui/encoder": "0.0.53",
83
+ "@ag-ui/langgraph": "0.0.31",
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,10 +115,10 @@
115
115
  "uuid": "^10.0.0",
116
116
  "ws": "^8.18.0",
117
117
  "zod": "^3.23.3",
118
- "@copilotkit/shared": "1.56.4"
118
+ "@copilotkit/shared": "1.56.5"
119
119
  },
120
120
  "devDependencies": {
121
- "@copilotkit/aimock": "^1.10.0",
121
+ "@copilotkit/aimock": "latest",
122
122
  "@swc/core": "1.5.28",
123
123
  "@types/cors": "^2.8.17",
124
124
  "@types/express": "^4.17.21",
@@ -5,7 +5,8 @@
5
5
  * BuiltInAgent-specific factories, mock stream builders, and assertion helpers.
6
6
  */
7
7
 
8
- import { EventType, type BaseEvent, type RunAgentInput } from "@ag-ui/client";
8
+ import { EventType } from "@ag-ui/client";
9
+ import type { BaseEvent, RunAgentInput } from "@ag-ui/client";
9
10
  import type { Observable } from "rxjs";
10
11
  import { BuiltInAgent } from "../index";
11
12
  import type { AgentFactoryContext, BuiltInAgentFactoryConfig } from "../index";
@@ -85,6 +86,35 @@ export function tanstackToolCallEnd(toolCallId: string) {
85
86
  return { type: "TOOL_CALL_END", toolCallId } as const;
86
87
  }
87
88
 
89
+ /** TanStack tool-call result chunk. `content` is what the tool's `execute()` returned. */
90
+ export function tanstackToolCallResult(toolCallId: string, content: unknown) {
91
+ return { type: "TOOL_CALL_RESULT", toolCallId, content } as const;
92
+ }
93
+
94
+ /** TanStack reasoning lifecycle chunk builders */
95
+ export function tanstackReasoningStart(messageId: string) {
96
+ return { type: "REASONING_START", messageId } as const;
97
+ }
98
+ export function tanstackReasoningMessageStart(messageId: string) {
99
+ return {
100
+ type: "REASONING_MESSAGE_START",
101
+ messageId,
102
+ role: "reasoning",
103
+ } as const;
104
+ }
105
+ export function tanstackReasoningMessageContent(
106
+ messageId: string,
107
+ delta: string,
108
+ ) {
109
+ return { type: "REASONING_MESSAGE_CONTENT", messageId, delta } as const;
110
+ }
111
+ export function tanstackReasoningMessageEnd(messageId: string) {
112
+ return { type: "REASONING_MESSAGE_END", messageId } as const;
113
+ }
114
+ export function tanstackReasoningEnd(messageId: string) {
115
+ return { type: "REASONING_END", messageId } as const;
116
+ }
117
+
88
118
  // ---------------------------------------------------------------------------
89
119
  // Mock async iterable builders
90
120
  // ---------------------------------------------------------------------------
@@ -11,6 +11,12 @@ import {
11
11
  tanstackToolCallStart,
12
12
  tanstackToolCallArgs,
13
13
  tanstackToolCallEnd,
14
+ tanstackToolCallResult,
15
+ tanstackReasoningStart,
16
+ tanstackReasoningMessageStart,
17
+ tanstackReasoningMessageContent,
18
+ tanstackReasoningMessageEnd,
19
+ tanstackReasoningEnd,
14
20
  } from "./agent-test-helpers";
15
21
 
16
22
  describe("TanStack AI converter (via Agent)", () => {
@@ -312,3 +318,277 @@ describe("TanStack AI converter (via Agent)", () => {
312
318
  });
313
319
  });
314
320
  });
321
+
322
+ describe("TanStack AI converter — state tools", () => {
323
+ it("emits STATE_SNAPSHOT before TOOL_CALL_RESULT for AGUISendStateSnapshot", async () => {
324
+ const snapshot = { counter: 5, items: ["x", "y"] };
325
+ const agent = createAgent("tanstack", [
326
+ tanstackToolCallStart("call1", "AGUISendStateSnapshot"),
327
+ tanstackToolCallEnd("call1"),
328
+ tanstackToolCallResult("call1", { success: true, snapshot }),
329
+ ]);
330
+ const events = await collectEvents(agent.run(createDefaultInput()));
331
+
332
+ expectLifecycleWrapped(events);
333
+
334
+ const snapshotIdx = events.findIndex(
335
+ (e) => e.type === EventType.STATE_SNAPSHOT,
336
+ );
337
+ const resultIdx = events.findIndex(
338
+ (e) => e.type === EventType.TOOL_CALL_RESULT,
339
+ );
340
+
341
+ expect(snapshotIdx).toBeGreaterThanOrEqual(0);
342
+ expect(resultIdx).toBeGreaterThanOrEqual(0);
343
+ expect(snapshotIdx).toBeLessThan(resultIdx);
344
+ expect(eventField<unknown>(events[snapshotIdx], "snapshot")).toEqual(
345
+ snapshot,
346
+ );
347
+ });
348
+
349
+ it("emits STATE_DELTA before TOOL_CALL_RESULT for AGUISendStateDelta", async () => {
350
+ const delta = [{ op: "replace", path: "/counter", value: 7 }];
351
+ const agent = createAgent("tanstack", [
352
+ tanstackToolCallStart("call1", "AGUISendStateDelta"),
353
+ tanstackToolCallEnd("call1"),
354
+ tanstackToolCallResult("call1", { success: true, delta }),
355
+ ]);
356
+ const events = await collectEvents(agent.run(createDefaultInput()));
357
+
358
+ expectLifecycleWrapped(events);
359
+
360
+ const deltaIdx = events.findIndex((e) => e.type === EventType.STATE_DELTA);
361
+ const resultIdx = events.findIndex(
362
+ (e) => e.type === EventType.TOOL_CALL_RESULT,
363
+ );
364
+
365
+ expect(deltaIdx).toBeGreaterThanOrEqual(0);
366
+ expect(deltaIdx).toBeLessThan(resultIdx);
367
+ expect(eventField<unknown>(events[deltaIdx], "delta")).toEqual(delta);
368
+ });
369
+
370
+ it("emits STATE_SNAPSHOT when payload arrives in raw.result instead of raw.content", async () => {
371
+ // Regression: serialization fell back to raw.result (`?? raw.result ?? null`)
372
+ // but state-tool detection only inspected raw.content, so STATE_* events
373
+ // were silently dropped if upstream used `result` for the state-tool body.
374
+ // See the "TOOL_CALL_RESULT with object content serializes to JSON" test
375
+ // above which already exercises the `result` field on a non-state tool.
376
+ const snapshot = { counter: 99 };
377
+ const agent = createAgent("tanstack", [
378
+ tanstackToolCallStart("call1", "AGUISendStateSnapshot"),
379
+ tanstackToolCallEnd("call1"),
380
+ {
381
+ type: "TOOL_CALL_RESULT",
382
+ toolCallId: "call1",
383
+ result: { success: true, snapshot },
384
+ },
385
+ ]);
386
+ const events = await collectEvents(agent.run(createDefaultInput()));
387
+
388
+ expectLifecycleWrapped(events);
389
+
390
+ const snapshotIdx = events.findIndex(
391
+ (e) => e.type === EventType.STATE_SNAPSHOT,
392
+ );
393
+ expect(snapshotIdx).toBeGreaterThanOrEqual(0);
394
+ expect(eventField<unknown>(events[snapshotIdx], "snapshot")).toEqual(
395
+ snapshot,
396
+ );
397
+ });
398
+
399
+ it("does NOT emit STATE_* for non-state tool results", async () => {
400
+ const agent = createAgent("tanstack", [
401
+ tanstackToolCallStart("call1", "getWeather"),
402
+ tanstackToolCallEnd("call1"),
403
+ tanstackToolCallResult("call1", {
404
+ snapshot: { spoofed: true },
405
+ delta: [{ op: "x" }],
406
+ }),
407
+ ]);
408
+ const events = await collectEvents(agent.run(createDefaultInput()));
409
+
410
+ expectLifecycleWrapped(events);
411
+
412
+ expect(
413
+ events.find(
414
+ (e) =>
415
+ e.type === EventType.STATE_SNAPSHOT ||
416
+ e.type === EventType.STATE_DELTA,
417
+ ),
418
+ ).toBeUndefined();
419
+ expect(
420
+ events.find((e) => e.type === EventType.TOOL_CALL_RESULT),
421
+ ).toBeDefined();
422
+ });
423
+ });
424
+
425
+ describe("TanStack AI converter — reasoning", () => {
426
+ it("emits the full REASONING lifecycle for reasoning chunks", async () => {
427
+ const agent = createAgent("tanstack", [
428
+ tanstackReasoningStart("r1"),
429
+ tanstackReasoningMessageStart("r1"),
430
+ tanstackReasoningMessageContent("r1", "thinking"),
431
+ tanstackReasoningMessageEnd("r1"),
432
+ tanstackReasoningEnd("r1"),
433
+ ]);
434
+ const events = await collectEvents(agent.run(createDefaultInput()));
435
+
436
+ expectLifecycleWrapped(events);
437
+
438
+ // Strip the lifecycle wrap and inspect the inner sequence.
439
+ const inner = events.slice(1, -1).map((e) => e.type);
440
+ expect(inner).toEqual([
441
+ EventType.REASONING_START,
442
+ EventType.REASONING_MESSAGE_START,
443
+ EventType.REASONING_MESSAGE_CONTENT,
444
+ EventType.REASONING_MESSAGE_END,
445
+ EventType.REASONING_END,
446
+ ]);
447
+ });
448
+
449
+ it("auto-closes an open reasoning lifecycle when text starts", async () => {
450
+ const agent = createAgent("tanstack", [
451
+ tanstackReasoningStart("r1"),
452
+ tanstackReasoningMessageStart("r1"),
453
+ tanstackReasoningMessageContent("r1", "thinking"),
454
+ // No REASONING_MESSAGE_END / REASONING_END before text
455
+ tanstackTextChunk("Hi"),
456
+ ]);
457
+ const events = await collectEvents(agent.run(createDefaultInput()));
458
+ const types = events.map((e) => e.type);
459
+
460
+ const reasonEndIdx = types.indexOf(EventType.REASONING_END);
461
+ const reasonMsgEndIdx = types.indexOf(EventType.REASONING_MESSAGE_END);
462
+ const textIdx = types.indexOf(EventType.TEXT_MESSAGE_CHUNK);
463
+
464
+ expect(reasonMsgEndIdx).toBeGreaterThan(-1);
465
+ expect(reasonEndIdx).toBeGreaterThan(-1);
466
+ expect(reasonEndIdx).toBeLessThan(textIdx);
467
+ });
468
+
469
+ it("auto-closes an open reasoning lifecycle when a tool call starts", async () => {
470
+ const agent = createAgent("tanstack", [
471
+ tanstackReasoningStart("r1"),
472
+ tanstackReasoningMessageStart("r1"),
473
+ tanstackReasoningMessageContent("r1", "..."),
474
+ tanstackToolCallStart("t1", "x"),
475
+ ]);
476
+ const events = await collectEvents(agent.run(createDefaultInput()));
477
+ const types = events.map((e) => e.type);
478
+
479
+ expect(types).toContain(EventType.REASONING_MESSAGE_END);
480
+ expect(types).toContain(EventType.REASONING_END);
481
+ expect(types.indexOf(EventType.REASONING_END)).toBeLessThan(
482
+ types.indexOf(EventType.TOOL_CALL_START),
483
+ );
484
+ });
485
+
486
+ it("auto-closes when the stream ends without explicit reasoning end", async () => {
487
+ const agent = createAgent("tanstack", [
488
+ tanstackReasoningStart("r1"),
489
+ tanstackReasoningMessageStart("r1"),
490
+ tanstackReasoningMessageContent("r1", "x"),
491
+ ]);
492
+ const events = await collectEvents(agent.run(createDefaultInput()));
493
+ const types = events.map((e) => e.type);
494
+
495
+ expect(types).toContain(EventType.REASONING_MESSAGE_END);
496
+ expect(types).toContain(EventType.REASONING_END);
497
+ });
498
+
499
+ it("emits REASONING_MESSAGE_END before REASONING_END when upstream sends END with message still open", async () => {
500
+ // Regression: if the converter received REASONING_END while a message
501
+ // was still open, it cleared run-open and emitted END only — leaving
502
+ // message-open true. The next non-reasoning chunk then triggered
503
+ // closeReasoningIfOpen, which emitted MSG_END AFTER END (wrong order).
504
+ // Fix: REASONING_END handler closes any open message first.
505
+ const agent = createAgent("tanstack", [
506
+ tanstackReasoningStart("r1"),
507
+ tanstackReasoningMessageStart("r1"),
508
+ tanstackReasoningMessageContent("r1", "thinking"),
509
+ // Upstream skips MSG_END and goes straight to END
510
+ tanstackReasoningEnd("r1"),
511
+ tanstackTextChunk("Hi"),
512
+ ]);
513
+ const events = await collectEvents(agent.run(createDefaultInput()));
514
+ const types = events.map((e) => e.type);
515
+
516
+ const msgEndIdx = types.indexOf(EventType.REASONING_MESSAGE_END);
517
+ const endIdx = types.indexOf(EventType.REASONING_END);
518
+
519
+ expect(msgEndIdx).toBeGreaterThan(-1);
520
+ expect(endIdx).toBeGreaterThan(-1);
521
+ expect(msgEndIdx).toBeLessThan(endIdx);
522
+ // No duplicate MSG_END or END from auto-close on the text chunk
523
+ expect(
524
+ types.filter((t) => t === EventType.REASONING_MESSAGE_END),
525
+ ).toHaveLength(1);
526
+ expect(types.filter((t) => t === EventType.REASONING_END)).toHaveLength(1);
527
+ });
528
+
529
+ it("auto-closes prior reasoning run when a new REASONING_START arrives without END", async () => {
530
+ // Regression: REASONING_START used to overwrite reasoningMessageId
531
+ // unconditionally, orphaning the prior run's MSG_END / END.
532
+ // Fix: REASONING_START handler calls closeReasoningIfOpen() first.
533
+ const agent = createAgent("tanstack", [
534
+ tanstackReasoningStart("r1"),
535
+ tanstackReasoningMessageStart("r1"),
536
+ tanstackReasoningMessageContent("r1", "first"),
537
+ // Second START with no intervening END
538
+ tanstackReasoningStart("r2"),
539
+ tanstackReasoningMessageStart("r2"),
540
+ tanstackReasoningMessageContent("r2", "second"),
541
+ tanstackReasoningMessageEnd("r2"),
542
+ tanstackReasoningEnd("r2"),
543
+ ]);
544
+ const events = await collectEvents(agent.run(createDefaultInput()));
545
+ const types = events.map((e) => e.type);
546
+
547
+ // Two complete START → MSG_START → ... → MSG_END → END sequences
548
+ expect(types.filter((t) => t === EventType.REASONING_START)).toHaveLength(
549
+ 2,
550
+ );
551
+ expect(types.filter((t) => t === EventType.REASONING_END)).toHaveLength(2);
552
+ expect(
553
+ types.filter((t) => t === EventType.REASONING_MESSAGE_END),
554
+ ).toHaveLength(2);
555
+
556
+ // First START's prior message gets closed BEFORE the second START
557
+ const firstEndIdx = types.indexOf(EventType.REASONING_END);
558
+ const secondStartIdx = types.indexOf(
559
+ EventType.REASONING_START,
560
+ firstEndIdx + 1,
561
+ );
562
+ expect(firstEndIdx).toBeLessThan(secondStartIdx);
563
+ });
564
+
565
+ it("does NOT duplicate REASONING_MESSAGE_END when upstream emits it explicitly before text", async () => {
566
+ // Regression: a single isInReasoning flag conflated message-open with
567
+ // run-open, so closeReasoningIfOpen on TEXT_MESSAGE_CONTENT emitted a
568
+ // second REASONING_MESSAGE_END after upstream's own. Track message-open
569
+ // and run-open separately so closeReasoningIfOpen owes only what's still
570
+ // open.
571
+ const agent = createAgent("tanstack", [
572
+ tanstackReasoningStart("r1"),
573
+ tanstackReasoningMessageStart("r1"),
574
+ tanstackReasoningMessageContent("r1", "thinking"),
575
+ tanstackReasoningMessageEnd("r1"),
576
+ // No explicit REASONING_END before text — closeReasoningIfOpen should
577
+ // emit REASONING_END but NOT a second REASONING_MESSAGE_END.
578
+ tanstackTextChunk("Hi"),
579
+ ]);
580
+ const events = await collectEvents(agent.run(createDefaultInput()));
581
+ const types = events.map((e) => e.type);
582
+
583
+ const msgEndCount = types.filter(
584
+ (t) => t === EventType.REASONING_MESSAGE_END,
585
+ ).length;
586
+ const endCount = types.filter((t) => t === EventType.REASONING_END).length;
587
+
588
+ expect(msgEndCount).toBe(1);
589
+ expect(endCount).toBe(1);
590
+ expect(types.indexOf(EventType.REASONING_END)).toBeLessThan(
591
+ types.indexOf(EventType.TEXT_MESSAGE_CHUNK),
592
+ );
593
+ });
594
+ });