@copilotkit/runtime 1.62.2 → 1.63.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_virtual/_rolldown/runtime.mjs +15 -1
- package/dist/agent/converters/aisdk.d.cts +1 -1
- package/dist/agent/converters/tanstack.d.cts +1 -1
- package/dist/agent/index.d.cts +1 -1
- package/dist/channels/dist/index.d.cts +10 -0
- package/dist/channels/dist/index.d.cts.map +1 -0
- package/dist/channels/dist/index.d.mts +10 -0
- package/dist/channels/dist/index.d.mts.map +1 -0
- package/dist/graphql/inputs/action.input.d.cts +1 -1
- package/dist/graphql/inputs/agent-session.input.d.cts +1 -1
- package/dist/graphql/inputs/agent-state.input.d.cts +1 -1
- package/dist/graphql/inputs/extensions.input.d.cts +1 -1
- package/dist/graphql/inputs/forwarded-parameters.input.d.cts +1 -1
- package/dist/graphql/inputs/message.input.d.cts +1 -1
- package/dist/graphql/types/base/index.d.cts +1 -1
- package/dist/graphql/types/converted/index.d.cts +1 -1
- package/dist/graphql/types/copilot-response.type.d.cts +1 -1
- package/dist/graphql/types/enums.d.cts +1 -1
- package/dist/graphql/types/extensions-response.type.d.cts +1 -1
- package/dist/graphql/types/message-status.type.d.cts +1 -1
- package/dist/graphql/types/response-status.type.d.cts +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/langgraph.d.cts +1 -1
- package/dist/lib/cloud/index.d.cts +1 -1
- package/dist/lib/index.d.cts +1 -1
- package/dist/lib/integrations/index.d.cts +1 -1
- package/dist/lib/integrations/nest/index.d.cts +1 -1
- package/dist/lib/integrations/nextjs/app-router.d.cts +1 -1
- package/dist/lib/integrations/nextjs/pages-router.d.cts +1 -1
- package/dist/lib/integrations/node-express/index.d.cts +1 -1
- package/dist/lib/integrations/node-http/index.d.cts +1 -1
- package/dist/lib/integrations/shared.d.cts +1 -1
- package/dist/lib/logger.d.cts +1 -1
- package/dist/lib/observability.d.cts +1 -1
- package/dist/lib/runtime/agent-integrations/langgraph/agent.d.cts +1 -1
- package/dist/lib/runtime/agent-integrations/langgraph/consts.d.cts +1 -1
- package/dist/lib/runtime/agent-integrations/langgraph/index.d.cts +1 -1
- package/dist/lib/runtime/copilot-runtime.d.cts +1 -1
- package/dist/lib/runtime/mcp-tools-utils.d.cts +1 -1
- package/dist/lib/runtime/telemetry-agent-runner.d.cts +1 -1
- package/dist/lib/runtime/types.d.cts +1 -1
- package/dist/lib/telemetry-client.mjs +1 -1
- package/dist/package.cjs +5 -1
- package/dist/{package.mjs → runtime/package.mjs} +6 -2
- package/dist/runtime/package.mjs.map +1 -0
- package/dist/service-adapters/anthropic/anthropic-adapter.d.cts +1 -1
- package/dist/service-adapters/bedrock/bedrock-adapter.d.cts +1 -1
- package/dist/service-adapters/empty/empty-adapter.d.cts +1 -1
- package/dist/service-adapters/events.d.cts +1 -1
- package/dist/service-adapters/experimental/ollama/ollama-adapter.d.cts +1 -1
- package/dist/service-adapters/google/google-genai-adapter.d.cts +1 -1
- package/dist/service-adapters/groq/groq-adapter.d.cts +1 -1
- package/dist/service-adapters/index.d.cts +1 -1
- package/dist/service-adapters/langchain/langchain-adapter.d.cts +1 -1
- package/dist/service-adapters/langchain/langserve.d.cts +1 -1
- package/dist/service-adapters/langchain/types.d.cts +1 -1
- package/dist/service-adapters/openai/openai-adapter.d.cts +1 -1
- package/dist/service-adapters/openai/openai-assistant-adapter.d.cts +1 -1
- package/dist/service-adapters/service-adapter.d.cts +1 -1
- package/dist/service-adapters/shared/error-utils.d.cts +1 -1
- package/dist/service-adapters/shared/sdk-client-utils.d.cts +1 -1
- package/dist/service-adapters/unify/unify-adapter.d.cts +1 -1
- package/dist/utils/failed-response-status-reasons.d.cts +1 -1
- package/dist/v2/express.d.cts +3 -3
- package/dist/v2/express.d.mts +2 -2
- package/dist/v2/hono.d.cts +3 -3
- package/dist/v2/hono.d.mts +2 -2
- package/dist/v2/index.d.cts +7 -6
- package/dist/v2/index.d.mts +6 -5
- package/dist/v2/node.d.cts +1 -1
- package/dist/v2/runtime/core/channel-activation-config.cjs +93 -0
- package/dist/v2/runtime/core/channel-activation-config.cjs.map +1 -0
- package/dist/v2/runtime/core/channel-activation-config.d.cts +34 -0
- package/dist/v2/runtime/core/channel-activation-config.d.cts.map +1 -0
- package/dist/v2/runtime/core/channel-activation-config.d.mts +34 -0
- package/dist/v2/runtime/core/channel-activation-config.d.mts.map +1 -0
- package/dist/v2/runtime/core/channel-activation-config.mjs +91 -0
- package/dist/v2/runtime/core/channel-activation-config.mjs.map +1 -0
- package/dist/v2/runtime/core/channel-manager.cjs +444 -0
- package/dist/v2/runtime/core/channel-manager.cjs.map +1 -0
- package/dist/v2/runtime/core/channel-manager.d.cts +90 -0
- package/dist/v2/runtime/core/channel-manager.d.cts.map +1 -0
- package/dist/v2/runtime/core/channel-manager.d.mts +90 -0
- package/dist/v2/runtime/core/channel-manager.d.mts.map +1 -0
- package/dist/v2/runtime/core/channel-manager.mjs +443 -0
- package/dist/v2/runtime/core/channel-manager.mjs.map +1 -0
- package/dist/v2/runtime/core/debug-event-bus.d.cts +1 -1
- package/dist/v2/runtime/core/fetch-cors.d.cts +1 -1
- package/dist/v2/runtime/core/fetch-handler.cjs +98 -2
- package/dist/v2/runtime/core/fetch-handler.cjs.map +1 -1
- package/dist/v2/runtime/core/fetch-handler.d.cts +60 -4
- package/dist/v2/runtime/core/fetch-handler.d.cts.map +1 -1
- package/dist/v2/runtime/core/fetch-handler.d.mts +59 -3
- package/dist/v2/runtime/core/fetch-handler.d.mts.map +1 -1
- package/dist/v2/runtime/core/fetch-handler.mjs +98 -2
- package/dist/v2/runtime/core/fetch-handler.mjs.map +1 -1
- package/dist/v2/runtime/core/fetch-router.cjs +18 -0
- package/dist/v2/runtime/core/fetch-router.cjs.map +1 -1
- package/dist/v2/runtime/core/fetch-router.mjs +18 -0
- package/dist/v2/runtime/core/fetch-router.mjs.map +1 -1
- package/dist/v2/runtime/core/hooks.cjs.map +1 -1
- package/dist/v2/runtime/core/hooks.d.cts +11 -1
- package/dist/v2/runtime/core/hooks.d.cts.map +1 -1
- package/dist/v2/runtime/core/hooks.d.mts +10 -0
- package/dist/v2/runtime/core/hooks.d.mts.map +1 -1
- package/dist/v2/runtime/core/hooks.mjs.map +1 -1
- package/dist/v2/runtime/core/middleware-sse-parser.d.cts +1 -1
- package/dist/v2/runtime/core/middleware.d.cts +1 -1
- package/dist/v2/runtime/core/runtime.cjs +29 -1
- package/dist/v2/runtime/core/runtime.cjs.map +1 -1
- package/dist/v2/runtime/core/runtime.d.cts +95 -26
- package/dist/v2/runtime/core/runtime.d.cts.map +1 -1
- package/dist/v2/runtime/core/runtime.d.mts +94 -25
- package/dist/v2/runtime/core/runtime.d.mts.map +1 -1
- package/dist/v2/runtime/core/runtime.mjs +30 -2
- package/dist/v2/runtime/core/runtime.mjs.map +1 -1
- package/dist/v2/runtime/endpoints/express-single.d.cts +1 -1
- package/dist/v2/runtime/endpoints/express.cjs +10 -5
- package/dist/v2/runtime/endpoints/express.cjs.map +1 -1
- package/dist/v2/runtime/endpoints/express.d.cts +30 -4
- package/dist/v2/runtime/endpoints/express.d.cts.map +1 -1
- package/dist/v2/runtime/endpoints/express.d.mts +29 -3
- package/dist/v2/runtime/endpoints/express.d.mts.map +1 -1
- package/dist/v2/runtime/endpoints/express.mjs +10 -5
- package/dist/v2/runtime/endpoints/express.mjs.map +1 -1
- package/dist/v2/runtime/endpoints/hono-single.d.cts +1 -1
- package/dist/v2/runtime/endpoints/hono.cjs +7 -3
- package/dist/v2/runtime/endpoints/hono.cjs.map +1 -1
- package/dist/v2/runtime/endpoints/hono.d.cts +30 -16
- package/dist/v2/runtime/endpoints/hono.d.cts.map +1 -1
- package/dist/v2/runtime/endpoints/hono.d.mts +29 -15
- package/dist/v2/runtime/endpoints/hono.d.mts.map +1 -1
- package/dist/v2/runtime/endpoints/hono.mjs +7 -3
- package/dist/v2/runtime/endpoints/hono.mjs.map +1 -1
- package/dist/v2/runtime/endpoints/index.d.cts +3 -3
- package/dist/v2/runtime/endpoints/index.d.mts +2 -2
- package/dist/v2/runtime/endpoints/node-fetch-handler.d.cts +1 -1
- package/dist/v2/runtime/endpoints/node.cjs +12 -1
- package/dist/v2/runtime/endpoints/node.cjs.map +1 -1
- package/dist/v2/runtime/endpoints/node.d.cts +22 -2
- package/dist/v2/runtime/endpoints/node.d.cts.map +1 -1
- package/dist/v2/runtime/endpoints/node.d.mts +21 -1
- package/dist/v2/runtime/endpoints/node.d.mts.map +1 -1
- package/dist/v2/runtime/endpoints/node.mjs +12 -1
- package/dist/v2/runtime/endpoints/node.mjs.map +1 -1
- package/dist/v2/runtime/endpoints/single-route-helpers.cjs +1 -0
- package/dist/v2/runtime/endpoints/single-route-helpers.cjs.map +1 -1
- package/dist/v2/runtime/endpoints/single-route-helpers.mjs +1 -0
- package/dist/v2/runtime/endpoints/single-route-helpers.mjs.map +1 -1
- package/dist/v2/runtime/express.d.cts +2 -2
- package/dist/v2/runtime/express.d.mts +1 -1
- package/dist/v2/runtime/handlers/get-runtime-info.cjs +1 -0
- package/dist/v2/runtime/handlers/get-runtime-info.cjs.map +1 -1
- package/dist/v2/runtime/handlers/get-runtime-info.mjs +1 -0
- package/dist/v2/runtime/handlers/get-runtime-info.mjs.map +1 -1
- package/dist/v2/runtime/handlers/handle-connect.cjs +2 -1
- package/dist/v2/runtime/handlers/handle-connect.cjs.map +1 -1
- package/dist/v2/runtime/handlers/handle-connect.mjs +2 -1
- package/dist/v2/runtime/handlers/handle-connect.mjs.map +1 -1
- package/dist/v2/runtime/handlers/handle-suggest.cjs +83 -0
- package/dist/v2/runtime/handlers/handle-suggest.cjs.map +1 -0
- package/dist/v2/runtime/handlers/handle-suggest.mjs +82 -0
- package/dist/v2/runtime/handlers/handle-suggest.mjs.map +1 -0
- package/dist/v2/runtime/handlers/header-utils.cjs +169 -9
- package/dist/v2/runtime/handlers/header-utils.cjs.map +1 -1
- package/dist/v2/runtime/handlers/header-utils.d.cts +54 -0
- package/dist/v2/runtime/handlers/header-utils.d.cts.map +1 -0
- package/dist/v2/runtime/handlers/header-utils.d.mts +54 -0
- package/dist/v2/runtime/handlers/header-utils.d.mts.map +1 -0
- package/dist/v2/runtime/handlers/header-utils.mjs +168 -9
- package/dist/v2/runtime/handlers/header-utils.mjs.map +1 -1
- package/dist/v2/runtime/handlers/intelligence/memories.cjs +209 -0
- package/dist/v2/runtime/handlers/intelligence/memories.cjs.map +1 -0
- package/dist/v2/runtime/handlers/intelligence/memories.mjs +204 -0
- package/dist/v2/runtime/handlers/intelligence/memories.mjs.map +1 -0
- package/dist/v2/runtime/handlers/shared/agent-utils.cjs +18 -5
- package/dist/v2/runtime/handlers/shared/agent-utils.cjs.map +1 -1
- package/dist/v2/runtime/handlers/shared/agent-utils.mjs +18 -5
- package/dist/v2/runtime/handlers/shared/agent-utils.mjs.map +1 -1
- package/dist/v2/runtime/handlers/shared/sse-response.cjs +4 -4
- package/dist/v2/runtime/handlers/shared/sse-response.cjs.map +1 -1
- package/dist/v2/runtime/handlers/shared/sse-response.mjs +4 -4
- package/dist/v2/runtime/handlers/shared/sse-response.mjs.map +1 -1
- package/dist/v2/runtime/handlers/sse/connect.cjs +2 -2
- package/dist/v2/runtime/handlers/sse/connect.cjs.map +1 -1
- package/dist/v2/runtime/handlers/sse/connect.mjs +3 -3
- package/dist/v2/runtime/handlers/sse/connect.mjs.map +1 -1
- package/dist/v2/runtime/hono.d.cts +2 -2
- package/dist/v2/runtime/hono.d.mts +1 -1
- package/dist/v2/runtime/index.d.cts +6 -5
- package/dist/v2/runtime/index.d.cts.map +1 -1
- package/dist/v2/runtime/index.d.mts +5 -4
- package/dist/v2/runtime/index.d.mts.map +1 -1
- package/dist/v2/runtime/intelligence-platform/client.cjs +71 -2
- package/dist/v2/runtime/intelligence-platform/client.cjs.map +1 -1
- package/dist/v2/runtime/intelligence-platform/client.d.cts +113 -1
- package/dist/v2/runtime/intelligence-platform/client.d.cts.map +1 -1
- package/dist/v2/runtime/intelligence-platform/client.d.mts +112 -0
- package/dist/v2/runtime/intelligence-platform/client.d.mts.map +1 -1
- package/dist/v2/runtime/intelligence-platform/client.mjs +71 -2
- package/dist/v2/runtime/intelligence-platform/client.mjs.map +1 -1
- package/dist/v2/runtime/node.d.cts +1 -1
- package/dist/v2/runtime/runner/agent-runner.d.cts +1 -1
- package/dist/v2/runtime/runner/in-memory.cjs +35 -19
- package/dist/v2/runtime/runner/in-memory.cjs.map +1 -1
- package/dist/v2/runtime/runner/in-memory.d.cts +13 -1
- package/dist/v2/runtime/runner/in-memory.d.cts.map +1 -1
- package/dist/v2/runtime/runner/in-memory.d.mts +12 -0
- package/dist/v2/runtime/runner/in-memory.d.mts.map +1 -1
- package/dist/v2/runtime/runner/in-memory.mjs +35 -19
- package/dist/v2/runtime/runner/in-memory.mjs.map +1 -1
- package/dist/v2/runtime/runner/index.d.cts +1 -1
- package/dist/v2/runtime/runner/intelligence.d.cts +1 -1
- package/dist/v2/runtime/telemetry/telemetry-client.mjs +1 -1
- package/dist/v2/runtime/transcription-service/transcription-service.d.cts +1 -1
- package/package.json +9 -3
- package/dist/package.mjs.map +0 -1
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import "reflect-metadata";
|
|
2
|
-
import { CopilotExpressEndpointParams, createCopilotExpressHandler } from "./endpoints/express.mjs";
|
|
2
|
+
import { CopilotExpressEndpointParams, CopilotExpressRouter, createCopilotExpressHandler } from "./endpoints/express.mjs";
|
|
3
3
|
import { createCopilotEndpointSingleRouteExpress } from "./endpoints/express-single.mjs";
|
|
@@ -38,6 +38,7 @@ async function handleGetRuntimeInfo({ runtime, request, threadEndpointsEnabled =
|
|
|
38
38
|
audioFileTranscriptionEnabled: !!runtime.transcriptionService,
|
|
39
39
|
mode: runtime.mode,
|
|
40
40
|
threadEndpoints: resolveThreadEndpointInfo(runtime, threadEndpointsEnabled),
|
|
41
|
+
suggestions: true,
|
|
41
42
|
...require_runtime.isIntelligenceRuntime(runtime) ? { intelligence: { wsUrl: runtime.intelligence.ɵgetClientWsUrl() } } : {},
|
|
42
43
|
a2uiEnabled: require_runtime.isA2UIEnabled(runtime.a2ui),
|
|
43
44
|
...require_runtime.isA2UIEnabled(runtime.a2ui) ? { a2ui: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"get-runtime-info.cjs","names":["resolveAgents","VERSION","isIntelligenceRuntime","isA2UIEnabled","isTelemetryDisabled","supportsLocalThreadEndpoints"],"sources":["../../../../src/v2/runtime/handlers/get-runtime-info.ts"],"sourcesContent":["import type { AgentCapabilities } from \"@ag-ui/core\";\nimport type { CopilotRuntimeLike } from \"../core/runtime\";\nimport {\n isA2UIEnabled,\n isIntelligenceRuntime,\n resolveAgents,\n} from \"../core/runtime\";\nimport type {\n AgentDescription,\n RuntimeInfo,\n ThreadEndpointRuntimeInfo,\n} from \"@copilotkit/shared\";\nimport type { RuntimeLicenseStatus } from \"@copilotkit/shared\";\nimport { VERSION } from \"../core/runtime\";\nimport { isTelemetryDisabled } from \"../telemetry/telemetry-client\";\nimport { supportsLocalThreadEndpoints } from \"../runner/agent-runner\";\n\nfunction resolveLicenseStatus(\n runtime: CopilotRuntimeLike,\n): RuntimeLicenseStatus {\n if (!runtime.licenseChecker) return \"none\";\n const status = runtime.licenseChecker.getStatus();\n if (status.warningSeverity === \"none\") return \"valid\";\n if (status.error === \"expired\") return \"expired\";\n if (status.warningSeverity === \"warning\") return \"expiring\";\n if (status.error) return \"invalid\";\n if (status.warningSeverity === \"info\") return \"none\";\n return \"unknown\";\n}\n\ninterface HandleGetRuntimeInfoParameters {\n runtime: CopilotRuntimeLike;\n request: Request;\n threadEndpointsEnabled?: boolean;\n}\n\nexport async function handleGetRuntimeInfo({\n runtime,\n request,\n threadEndpointsEnabled = true,\n}: HandleGetRuntimeInfoParameters) {\n try {\n const agents = await resolveAgents(runtime.agents, request);\n\n const agentEntries = await Promise.all(\n Object.entries(agents).map(async ([name, agent]) => {\n let capabilities: AgentCapabilities | undefined;\n try {\n capabilities = agent.getCapabilities\n ? await agent.getCapabilities()\n : undefined;\n } catch (error) {\n // Per-agent isolation: a single agent failing to report capabilities\n // must not take down the entire /info endpoint.\n console.warn(\n `Failed to fetch capabilities for agent \"${name}\":`,\n error instanceof Error ? error.message : error,\n );\n capabilities = undefined;\n }\n\n const description: AgentDescription = {\n name,\n description: agent.description,\n className: agent.constructor.name,\n ...(capabilities ? { capabilities } : {}),\n };\n\n return [name, description] as const;\n }),\n );\n\n const agentsDict: Record<string, AgentDescription> =\n Object.fromEntries(agentEntries);\n\n const runtimeInfo: RuntimeInfo = {\n version: VERSION,\n agents: agentsDict,\n audioFileTranscriptionEnabled: !!runtime.transcriptionService,\n mode: runtime.mode,\n threadEndpoints: resolveThreadEndpointInfo(\n runtime,\n threadEndpointsEnabled,\n ),\n ...(isIntelligenceRuntime(runtime)\n ? {\n intelligence: {\n wsUrl: runtime.intelligence.ɵgetClientWsUrl(),\n },\n }\n : {}),\n // Legacy flat flag, kept for older clients. The `a2ui` object below is\n // the source of truth: it preserves the per-agent scoping that this\n // boolean discards (see CopilotKit/CopilotKit#5369). Both go through the\n // shared isA2UIEnabled() predicate so an explicit `enabled: false`\n // disables a2ui here exactly as it does on the run path.\n a2uiEnabled: isA2UIEnabled(runtime.a2ui),\n ...(isA2UIEnabled(runtime.a2ui)\n ? {\n a2ui: {\n enabled: true,\n ...(runtime.a2ui.agents ? { agents: runtime.a2ui.agents } : {}),\n },\n }\n : {}),\n openGenerativeUIEnabled: !!runtime.openGenerativeUI,\n ...(isIntelligenceRuntime(runtime)\n ? { licenseStatus: resolveLicenseStatus(runtime) }\n : {}),\n telemetryDisabled: isTelemetryDisabled(),\n };\n\n return new Response(JSON.stringify(runtimeInfo), {\n status: 200,\n headers: { \"Content-Type\": \"application/json\" },\n });\n } catch (error) {\n return new Response(\n JSON.stringify({\n error: \"Failed to retrieve runtime information\",\n message: error instanceof Error ? error.message : \"Unknown error\",\n }),\n {\n status: 500,\n headers: { \"Content-Type\": \"application/json\" },\n },\n );\n }\n}\n\nfunction resolveThreadEndpointInfo(\n runtime: CopilotRuntimeLike,\n threadEndpointsEnabled: boolean,\n): ThreadEndpointRuntimeInfo {\n const hasRestThreadBackend =\n isIntelligenceRuntime(runtime) ||\n supportsLocalThreadEndpoints(runtime.runner);\n const restEndpointsAvailable = threadEndpointsEnabled && hasRestThreadBackend;\n const managedThreadMetadata =\n threadEndpointsEnabled && isIntelligenceRuntime(runtime);\n\n return {\n list: restEndpointsAvailable,\n inspect: restEndpointsAvailable,\n mutations: managedThreadMetadata,\n realtimeMetadata: managedThreadMetadata,\n };\n}\n"],"mappings":";;;;;;AAiBA,SAAS,qBACP,SACsB;AACtB,KAAI,CAAC,QAAQ,eAAgB,QAAO;CACpC,MAAM,SAAS,QAAQ,eAAe,WAAW;AACjD,KAAI,OAAO,oBAAoB,OAAQ,QAAO;AAC9C,KAAI,OAAO,UAAU,UAAW,QAAO;AACvC,KAAI,OAAO,oBAAoB,UAAW,QAAO;AACjD,KAAI,OAAO,MAAO,QAAO;AACzB,KAAI,OAAO,oBAAoB,OAAQ,QAAO;AAC9C,QAAO;;AAST,eAAsB,qBAAqB,EACzC,SACA,SACA,yBAAyB,QACQ;AACjC,KAAI;EACF,MAAM,SAAS,MAAMA,8BAAc,QAAQ,QAAQ,QAAQ;EAE3D,MAAM,eAAe,MAAM,QAAQ,IACjC,OAAO,QAAQ,OAAO,CAAC,IAAI,OAAO,CAAC,MAAM,WAAW;GAClD,IAAI;AACJ,OAAI;AACF,mBAAe,MAAM,kBACjB,MAAM,MAAM,iBAAiB,GAC7B;YACG,OAAO;AAGd,YAAQ,KACN,2CAA2C,KAAK,KAChD,iBAAiB,QAAQ,MAAM,UAAU,MAC1C;AACD,mBAAe;;AAUjB,UAAO,CAAC,MAP8B;IACpC;IACA,aAAa,MAAM;IACnB,WAAW,MAAM,YAAY;IAC7B,GAAI,eAAe,EAAE,cAAc,GAAG,EAAE;IACzC,CAEyB;IAC1B,CACH;EAKD,MAAM,cAA2B;GAC/B,SAASC;GACT,QAJA,OAAO,YAAY,aAAa;GAKhC,+BAA+B,CAAC,CAAC,QAAQ;GACzC,MAAM,QAAQ;GACd,iBAAiB,0BACf,SACA,uBACD;
|
|
1
|
+
{"version":3,"file":"get-runtime-info.cjs","names":["resolveAgents","VERSION","isIntelligenceRuntime","isA2UIEnabled","isTelemetryDisabled","supportsLocalThreadEndpoints"],"sources":["../../../../src/v2/runtime/handlers/get-runtime-info.ts"],"sourcesContent":["import type { AgentCapabilities } from \"@ag-ui/core\";\nimport type { CopilotRuntimeLike } from \"../core/runtime\";\nimport {\n isA2UIEnabled,\n isIntelligenceRuntime,\n resolveAgents,\n} from \"../core/runtime\";\nimport type {\n AgentDescription,\n RuntimeInfo,\n ThreadEndpointRuntimeInfo,\n} from \"@copilotkit/shared\";\nimport type { RuntimeLicenseStatus } from \"@copilotkit/shared\";\nimport { VERSION } from \"../core/runtime\";\nimport { isTelemetryDisabled } from \"../telemetry/telemetry-client\";\nimport { supportsLocalThreadEndpoints } from \"../runner/agent-runner\";\n\nfunction resolveLicenseStatus(\n runtime: CopilotRuntimeLike,\n): RuntimeLicenseStatus {\n if (!runtime.licenseChecker) return \"none\";\n const status = runtime.licenseChecker.getStatus();\n if (status.warningSeverity === \"none\") return \"valid\";\n if (status.error === \"expired\") return \"expired\";\n if (status.warningSeverity === \"warning\") return \"expiring\";\n if (status.error) return \"invalid\";\n if (status.warningSeverity === \"info\") return \"none\";\n return \"unknown\";\n}\n\ninterface HandleGetRuntimeInfoParameters {\n runtime: CopilotRuntimeLike;\n request: Request;\n threadEndpointsEnabled?: boolean;\n}\n\nexport async function handleGetRuntimeInfo({\n runtime,\n request,\n threadEndpointsEnabled = true,\n}: HandleGetRuntimeInfoParameters) {\n try {\n const agents = await resolveAgents(runtime.agents, request);\n\n const agentEntries = await Promise.all(\n Object.entries(agents).map(async ([name, agent]) => {\n let capabilities: AgentCapabilities | undefined;\n try {\n capabilities = agent.getCapabilities\n ? await agent.getCapabilities()\n : undefined;\n } catch (error) {\n // Per-agent isolation: a single agent failing to report capabilities\n // must not take down the entire /info endpoint.\n console.warn(\n `Failed to fetch capabilities for agent \"${name}\":`,\n error instanceof Error ? error.message : error,\n );\n capabilities = undefined;\n }\n\n const description: AgentDescription = {\n name,\n description: agent.description,\n className: agent.constructor.name,\n ...(capabilities ? { capabilities } : {}),\n };\n\n return [name, description] as const;\n }),\n );\n\n const agentsDict: Record<string, AgentDescription> =\n Object.fromEntries(agentEntries);\n\n const runtimeInfo: RuntimeInfo = {\n version: VERSION,\n agents: agentsDict,\n audioFileTranscriptionEnabled: !!runtime.transcriptionService,\n mode: runtime.mode,\n threadEndpoints: resolveThreadEndpointInfo(\n runtime,\n threadEndpointsEnabled,\n ),\n // Advertised unconditionally. Multi-route runtimes expose the dedicated\n // POST /agent/:agentId/suggest path; single-route clients fall back to a\n // client-side run (they don't construct the single-route envelope for\n // suggest). The flag lets multi-route clients detect the stateless path.\n suggestions: true,\n ...(isIntelligenceRuntime(runtime)\n ? {\n intelligence: {\n wsUrl: runtime.intelligence.ɵgetClientWsUrl(),\n },\n }\n : {}),\n // Legacy flat flag, kept for older clients. The `a2ui` object below is\n // the source of truth: it preserves the per-agent scoping that this\n // boolean discards (see CopilotKit/CopilotKit#5369). Both go through the\n // shared isA2UIEnabled() predicate so an explicit `enabled: false`\n // disables a2ui here exactly as it does on the run path.\n a2uiEnabled: isA2UIEnabled(runtime.a2ui),\n ...(isA2UIEnabled(runtime.a2ui)\n ? {\n a2ui: {\n enabled: true,\n ...(runtime.a2ui.agents ? { agents: runtime.a2ui.agents } : {}),\n },\n }\n : {}),\n openGenerativeUIEnabled: !!runtime.openGenerativeUI,\n ...(isIntelligenceRuntime(runtime)\n ? { licenseStatus: resolveLicenseStatus(runtime) }\n : {}),\n telemetryDisabled: isTelemetryDisabled(),\n };\n\n return new Response(JSON.stringify(runtimeInfo), {\n status: 200,\n headers: { \"Content-Type\": \"application/json\" },\n });\n } catch (error) {\n return new Response(\n JSON.stringify({\n error: \"Failed to retrieve runtime information\",\n message: error instanceof Error ? error.message : \"Unknown error\",\n }),\n {\n status: 500,\n headers: { \"Content-Type\": \"application/json\" },\n },\n );\n }\n}\n\nfunction resolveThreadEndpointInfo(\n runtime: CopilotRuntimeLike,\n threadEndpointsEnabled: boolean,\n): ThreadEndpointRuntimeInfo {\n const hasRestThreadBackend =\n isIntelligenceRuntime(runtime) ||\n supportsLocalThreadEndpoints(runtime.runner);\n const restEndpointsAvailable = threadEndpointsEnabled && hasRestThreadBackend;\n const managedThreadMetadata =\n threadEndpointsEnabled && isIntelligenceRuntime(runtime);\n\n return {\n list: restEndpointsAvailable,\n inspect: restEndpointsAvailable,\n mutations: managedThreadMetadata,\n realtimeMetadata: managedThreadMetadata,\n };\n}\n"],"mappings":";;;;;;AAiBA,SAAS,qBACP,SACsB;AACtB,KAAI,CAAC,QAAQ,eAAgB,QAAO;CACpC,MAAM,SAAS,QAAQ,eAAe,WAAW;AACjD,KAAI,OAAO,oBAAoB,OAAQ,QAAO;AAC9C,KAAI,OAAO,UAAU,UAAW,QAAO;AACvC,KAAI,OAAO,oBAAoB,UAAW,QAAO;AACjD,KAAI,OAAO,MAAO,QAAO;AACzB,KAAI,OAAO,oBAAoB,OAAQ,QAAO;AAC9C,QAAO;;AAST,eAAsB,qBAAqB,EACzC,SACA,SACA,yBAAyB,QACQ;AACjC,KAAI;EACF,MAAM,SAAS,MAAMA,8BAAc,QAAQ,QAAQ,QAAQ;EAE3D,MAAM,eAAe,MAAM,QAAQ,IACjC,OAAO,QAAQ,OAAO,CAAC,IAAI,OAAO,CAAC,MAAM,WAAW;GAClD,IAAI;AACJ,OAAI;AACF,mBAAe,MAAM,kBACjB,MAAM,MAAM,iBAAiB,GAC7B;YACG,OAAO;AAGd,YAAQ,KACN,2CAA2C,KAAK,KAChD,iBAAiB,QAAQ,MAAM,UAAU,MAC1C;AACD,mBAAe;;AAUjB,UAAO,CAAC,MAP8B;IACpC;IACA,aAAa,MAAM;IACnB,WAAW,MAAM,YAAY;IAC7B,GAAI,eAAe,EAAE,cAAc,GAAG,EAAE;IACzC,CAEyB;IAC1B,CACH;EAKD,MAAM,cAA2B;GAC/B,SAASC;GACT,QAJA,OAAO,YAAY,aAAa;GAKhC,+BAA+B,CAAC,CAAC,QAAQ;GACzC,MAAM,QAAQ;GACd,iBAAiB,0BACf,SACA,uBACD;GAKD,aAAa;GACb,GAAIC,sCAAsB,QAAQ,GAC9B,EACE,cAAc,EACZ,OAAO,QAAQ,aAAa,iBAAiB,EAC9C,EACF,GACD,EAAE;GAMN,aAAaC,8BAAc,QAAQ,KAAK;GACxC,GAAIA,8BAAc,QAAQ,KAAK,GAC3B,EACE,MAAM;IACJ,SAAS;IACT,GAAI,QAAQ,KAAK,SAAS,EAAE,QAAQ,QAAQ,KAAK,QAAQ,GAAG,EAAE;IAC/D,EACF,GACD,EAAE;GACN,yBAAyB,CAAC,CAAC,QAAQ;GACnC,GAAID,sCAAsB,QAAQ,GAC9B,EAAE,eAAe,qBAAqB,QAAQ,EAAE,GAChD,EAAE;GACN,mBAAmBE,8CAAqB;GACzC;AAED,SAAO,IAAI,SAAS,KAAK,UAAU,YAAY,EAAE;GAC/C,QAAQ;GACR,SAAS,EAAE,gBAAgB,oBAAoB;GAChD,CAAC;UACK,OAAO;AACd,SAAO,IAAI,SACT,KAAK,UAAU;GACb,OAAO;GACP,SAAS,iBAAiB,QAAQ,MAAM,UAAU;GACnD,CAAC,EACF;GACE,QAAQ;GACR,SAAS,EAAE,gBAAgB,oBAAoB;GAChD,CACF;;;AAIL,SAAS,0BACP,SACA,wBAC2B;CAC3B,MAAM,uBACJF,sCAAsB,QAAQ,IAC9BG,kDAA6B,QAAQ,OAAO;CAC9C,MAAM,yBAAyB,0BAA0B;CACzD,MAAM,wBACJ,0BAA0BH,sCAAsB,QAAQ;AAE1D,QAAO;EACL,MAAM;EACN,SAAS;EACT,WAAW;EACX,kBAAkB;EACnB"}
|
|
@@ -38,6 +38,7 @@ async function handleGetRuntimeInfo({ runtime, request, threadEndpointsEnabled =
|
|
|
38
38
|
audioFileTranscriptionEnabled: !!runtime.transcriptionService,
|
|
39
39
|
mode: runtime.mode,
|
|
40
40
|
threadEndpoints: resolveThreadEndpointInfo(runtime, threadEndpointsEnabled),
|
|
41
|
+
suggestions: true,
|
|
41
42
|
...isIntelligenceRuntime(runtime) ? { intelligence: { wsUrl: runtime.intelligence.ɵgetClientWsUrl() } } : {},
|
|
42
43
|
a2uiEnabled: isA2UIEnabled(runtime.a2ui),
|
|
43
44
|
...isA2UIEnabled(runtime.a2ui) ? { a2ui: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"get-runtime-info.mjs","names":[],"sources":["../../../../src/v2/runtime/handlers/get-runtime-info.ts"],"sourcesContent":["import type { AgentCapabilities } from \"@ag-ui/core\";\nimport type { CopilotRuntimeLike } from \"../core/runtime\";\nimport {\n isA2UIEnabled,\n isIntelligenceRuntime,\n resolveAgents,\n} from \"../core/runtime\";\nimport type {\n AgentDescription,\n RuntimeInfo,\n ThreadEndpointRuntimeInfo,\n} from \"@copilotkit/shared\";\nimport type { RuntimeLicenseStatus } from \"@copilotkit/shared\";\nimport { VERSION } from \"../core/runtime\";\nimport { isTelemetryDisabled } from \"../telemetry/telemetry-client\";\nimport { supportsLocalThreadEndpoints } from \"../runner/agent-runner\";\n\nfunction resolveLicenseStatus(\n runtime: CopilotRuntimeLike,\n): RuntimeLicenseStatus {\n if (!runtime.licenseChecker) return \"none\";\n const status = runtime.licenseChecker.getStatus();\n if (status.warningSeverity === \"none\") return \"valid\";\n if (status.error === \"expired\") return \"expired\";\n if (status.warningSeverity === \"warning\") return \"expiring\";\n if (status.error) return \"invalid\";\n if (status.warningSeverity === \"info\") return \"none\";\n return \"unknown\";\n}\n\ninterface HandleGetRuntimeInfoParameters {\n runtime: CopilotRuntimeLike;\n request: Request;\n threadEndpointsEnabled?: boolean;\n}\n\nexport async function handleGetRuntimeInfo({\n runtime,\n request,\n threadEndpointsEnabled = true,\n}: HandleGetRuntimeInfoParameters) {\n try {\n const agents = await resolveAgents(runtime.agents, request);\n\n const agentEntries = await Promise.all(\n Object.entries(agents).map(async ([name, agent]) => {\n let capabilities: AgentCapabilities | undefined;\n try {\n capabilities = agent.getCapabilities\n ? await agent.getCapabilities()\n : undefined;\n } catch (error) {\n // Per-agent isolation: a single agent failing to report capabilities\n // must not take down the entire /info endpoint.\n console.warn(\n `Failed to fetch capabilities for agent \"${name}\":`,\n error instanceof Error ? error.message : error,\n );\n capabilities = undefined;\n }\n\n const description: AgentDescription = {\n name,\n description: agent.description,\n className: agent.constructor.name,\n ...(capabilities ? { capabilities } : {}),\n };\n\n return [name, description] as const;\n }),\n );\n\n const agentsDict: Record<string, AgentDescription> =\n Object.fromEntries(agentEntries);\n\n const runtimeInfo: RuntimeInfo = {\n version: VERSION,\n agents: agentsDict,\n audioFileTranscriptionEnabled: !!runtime.transcriptionService,\n mode: runtime.mode,\n threadEndpoints: resolveThreadEndpointInfo(\n runtime,\n threadEndpointsEnabled,\n ),\n ...(isIntelligenceRuntime(runtime)\n ? {\n intelligence: {\n wsUrl: runtime.intelligence.ɵgetClientWsUrl(),\n },\n }\n : {}),\n // Legacy flat flag, kept for older clients. The `a2ui` object below is\n // the source of truth: it preserves the per-agent scoping that this\n // boolean discards (see CopilotKit/CopilotKit#5369). Both go through the\n // shared isA2UIEnabled() predicate so an explicit `enabled: false`\n // disables a2ui here exactly as it does on the run path.\n a2uiEnabled: isA2UIEnabled(runtime.a2ui),\n ...(isA2UIEnabled(runtime.a2ui)\n ? {\n a2ui: {\n enabled: true,\n ...(runtime.a2ui.agents ? { agents: runtime.a2ui.agents } : {}),\n },\n }\n : {}),\n openGenerativeUIEnabled: !!runtime.openGenerativeUI,\n ...(isIntelligenceRuntime(runtime)\n ? { licenseStatus: resolveLicenseStatus(runtime) }\n : {}),\n telemetryDisabled: isTelemetryDisabled(),\n };\n\n return new Response(JSON.stringify(runtimeInfo), {\n status: 200,\n headers: { \"Content-Type\": \"application/json\" },\n });\n } catch (error) {\n return new Response(\n JSON.stringify({\n error: \"Failed to retrieve runtime information\",\n message: error instanceof Error ? error.message : \"Unknown error\",\n }),\n {\n status: 500,\n headers: { \"Content-Type\": \"application/json\" },\n },\n );\n }\n}\n\nfunction resolveThreadEndpointInfo(\n runtime: CopilotRuntimeLike,\n threadEndpointsEnabled: boolean,\n): ThreadEndpointRuntimeInfo {\n const hasRestThreadBackend =\n isIntelligenceRuntime(runtime) ||\n supportsLocalThreadEndpoints(runtime.runner);\n const restEndpointsAvailable = threadEndpointsEnabled && hasRestThreadBackend;\n const managedThreadMetadata =\n threadEndpointsEnabled && isIntelligenceRuntime(runtime);\n\n return {\n list: restEndpointsAvailable,\n inspect: restEndpointsAvailable,\n mutations: managedThreadMetadata,\n realtimeMetadata: managedThreadMetadata,\n };\n}\n"],"mappings":";;;;;;AAiBA,SAAS,qBACP,SACsB;AACtB,KAAI,CAAC,QAAQ,eAAgB,QAAO;CACpC,MAAM,SAAS,QAAQ,eAAe,WAAW;AACjD,KAAI,OAAO,oBAAoB,OAAQ,QAAO;AAC9C,KAAI,OAAO,UAAU,UAAW,QAAO;AACvC,KAAI,OAAO,oBAAoB,UAAW,QAAO;AACjD,KAAI,OAAO,MAAO,QAAO;AACzB,KAAI,OAAO,oBAAoB,OAAQ,QAAO;AAC9C,QAAO;;AAST,eAAsB,qBAAqB,EACzC,SACA,SACA,yBAAyB,QACQ;AACjC,KAAI;EACF,MAAM,SAAS,MAAM,cAAc,QAAQ,QAAQ,QAAQ;EAE3D,MAAM,eAAe,MAAM,QAAQ,IACjC,OAAO,QAAQ,OAAO,CAAC,IAAI,OAAO,CAAC,MAAM,WAAW;GAClD,IAAI;AACJ,OAAI;AACF,mBAAe,MAAM,kBACjB,MAAM,MAAM,iBAAiB,GAC7B;YACG,OAAO;AAGd,YAAQ,KACN,2CAA2C,KAAK,KAChD,iBAAiB,QAAQ,MAAM,UAAU,MAC1C;AACD,mBAAe;;AAUjB,UAAO,CAAC,MAP8B;IACpC;IACA,aAAa,MAAM;IACnB,WAAW,MAAM,YAAY;IAC7B,GAAI,eAAe,EAAE,cAAc,GAAG,EAAE;IACzC,CAEyB;IAC1B,CACH;EAKD,MAAM,cAA2B;GAC/B,SAAS;GACT,QAJA,OAAO,YAAY,aAAa;GAKhC,+BAA+B,CAAC,CAAC,QAAQ;GACzC,MAAM,QAAQ;GACd,iBAAiB,0BACf,SACA,uBACD;
|
|
1
|
+
{"version":3,"file":"get-runtime-info.mjs","names":[],"sources":["../../../../src/v2/runtime/handlers/get-runtime-info.ts"],"sourcesContent":["import type { AgentCapabilities } from \"@ag-ui/core\";\nimport type { CopilotRuntimeLike } from \"../core/runtime\";\nimport {\n isA2UIEnabled,\n isIntelligenceRuntime,\n resolveAgents,\n} from \"../core/runtime\";\nimport type {\n AgentDescription,\n RuntimeInfo,\n ThreadEndpointRuntimeInfo,\n} from \"@copilotkit/shared\";\nimport type { RuntimeLicenseStatus } from \"@copilotkit/shared\";\nimport { VERSION } from \"../core/runtime\";\nimport { isTelemetryDisabled } from \"../telemetry/telemetry-client\";\nimport { supportsLocalThreadEndpoints } from \"../runner/agent-runner\";\n\nfunction resolveLicenseStatus(\n runtime: CopilotRuntimeLike,\n): RuntimeLicenseStatus {\n if (!runtime.licenseChecker) return \"none\";\n const status = runtime.licenseChecker.getStatus();\n if (status.warningSeverity === \"none\") return \"valid\";\n if (status.error === \"expired\") return \"expired\";\n if (status.warningSeverity === \"warning\") return \"expiring\";\n if (status.error) return \"invalid\";\n if (status.warningSeverity === \"info\") return \"none\";\n return \"unknown\";\n}\n\ninterface HandleGetRuntimeInfoParameters {\n runtime: CopilotRuntimeLike;\n request: Request;\n threadEndpointsEnabled?: boolean;\n}\n\nexport async function handleGetRuntimeInfo({\n runtime,\n request,\n threadEndpointsEnabled = true,\n}: HandleGetRuntimeInfoParameters) {\n try {\n const agents = await resolveAgents(runtime.agents, request);\n\n const agentEntries = await Promise.all(\n Object.entries(agents).map(async ([name, agent]) => {\n let capabilities: AgentCapabilities | undefined;\n try {\n capabilities = agent.getCapabilities\n ? await agent.getCapabilities()\n : undefined;\n } catch (error) {\n // Per-agent isolation: a single agent failing to report capabilities\n // must not take down the entire /info endpoint.\n console.warn(\n `Failed to fetch capabilities for agent \"${name}\":`,\n error instanceof Error ? error.message : error,\n );\n capabilities = undefined;\n }\n\n const description: AgentDescription = {\n name,\n description: agent.description,\n className: agent.constructor.name,\n ...(capabilities ? { capabilities } : {}),\n };\n\n return [name, description] as const;\n }),\n );\n\n const agentsDict: Record<string, AgentDescription> =\n Object.fromEntries(agentEntries);\n\n const runtimeInfo: RuntimeInfo = {\n version: VERSION,\n agents: agentsDict,\n audioFileTranscriptionEnabled: !!runtime.transcriptionService,\n mode: runtime.mode,\n threadEndpoints: resolveThreadEndpointInfo(\n runtime,\n threadEndpointsEnabled,\n ),\n // Advertised unconditionally. Multi-route runtimes expose the dedicated\n // POST /agent/:agentId/suggest path; single-route clients fall back to a\n // client-side run (they don't construct the single-route envelope for\n // suggest). The flag lets multi-route clients detect the stateless path.\n suggestions: true,\n ...(isIntelligenceRuntime(runtime)\n ? {\n intelligence: {\n wsUrl: runtime.intelligence.ɵgetClientWsUrl(),\n },\n }\n : {}),\n // Legacy flat flag, kept for older clients. The `a2ui` object below is\n // the source of truth: it preserves the per-agent scoping that this\n // boolean discards (see CopilotKit/CopilotKit#5369). Both go through the\n // shared isA2UIEnabled() predicate so an explicit `enabled: false`\n // disables a2ui here exactly as it does on the run path.\n a2uiEnabled: isA2UIEnabled(runtime.a2ui),\n ...(isA2UIEnabled(runtime.a2ui)\n ? {\n a2ui: {\n enabled: true,\n ...(runtime.a2ui.agents ? { agents: runtime.a2ui.agents } : {}),\n },\n }\n : {}),\n openGenerativeUIEnabled: !!runtime.openGenerativeUI,\n ...(isIntelligenceRuntime(runtime)\n ? { licenseStatus: resolveLicenseStatus(runtime) }\n : {}),\n telemetryDisabled: isTelemetryDisabled(),\n };\n\n return new Response(JSON.stringify(runtimeInfo), {\n status: 200,\n headers: { \"Content-Type\": \"application/json\" },\n });\n } catch (error) {\n return new Response(\n JSON.stringify({\n error: \"Failed to retrieve runtime information\",\n message: error instanceof Error ? error.message : \"Unknown error\",\n }),\n {\n status: 500,\n headers: { \"Content-Type\": \"application/json\" },\n },\n );\n }\n}\n\nfunction resolveThreadEndpointInfo(\n runtime: CopilotRuntimeLike,\n threadEndpointsEnabled: boolean,\n): ThreadEndpointRuntimeInfo {\n const hasRestThreadBackend =\n isIntelligenceRuntime(runtime) ||\n supportsLocalThreadEndpoints(runtime.runner);\n const restEndpointsAvailable = threadEndpointsEnabled && hasRestThreadBackend;\n const managedThreadMetadata =\n threadEndpointsEnabled && isIntelligenceRuntime(runtime);\n\n return {\n list: restEndpointsAvailable,\n inspect: restEndpointsAvailable,\n mutations: managedThreadMetadata,\n realtimeMetadata: managedThreadMetadata,\n };\n}\n"],"mappings":";;;;;;AAiBA,SAAS,qBACP,SACsB;AACtB,KAAI,CAAC,QAAQ,eAAgB,QAAO;CACpC,MAAM,SAAS,QAAQ,eAAe,WAAW;AACjD,KAAI,OAAO,oBAAoB,OAAQ,QAAO;AAC9C,KAAI,OAAO,UAAU,UAAW,QAAO;AACvC,KAAI,OAAO,oBAAoB,UAAW,QAAO;AACjD,KAAI,OAAO,MAAO,QAAO;AACzB,KAAI,OAAO,oBAAoB,OAAQ,QAAO;AAC9C,QAAO;;AAST,eAAsB,qBAAqB,EACzC,SACA,SACA,yBAAyB,QACQ;AACjC,KAAI;EACF,MAAM,SAAS,MAAM,cAAc,QAAQ,QAAQ,QAAQ;EAE3D,MAAM,eAAe,MAAM,QAAQ,IACjC,OAAO,QAAQ,OAAO,CAAC,IAAI,OAAO,CAAC,MAAM,WAAW;GAClD,IAAI;AACJ,OAAI;AACF,mBAAe,MAAM,kBACjB,MAAM,MAAM,iBAAiB,GAC7B;YACG,OAAO;AAGd,YAAQ,KACN,2CAA2C,KAAK,KAChD,iBAAiB,QAAQ,MAAM,UAAU,MAC1C;AACD,mBAAe;;AAUjB,UAAO,CAAC,MAP8B;IACpC;IACA,aAAa,MAAM;IACnB,WAAW,MAAM,YAAY;IAC7B,GAAI,eAAe,EAAE,cAAc,GAAG,EAAE;IACzC,CAEyB;IAC1B,CACH;EAKD,MAAM,cAA2B;GAC/B,SAAS;GACT,QAJA,OAAO,YAAY,aAAa;GAKhC,+BAA+B,CAAC,CAAC,QAAQ;GACzC,MAAM,QAAQ;GACd,iBAAiB,0BACf,SACA,uBACD;GAKD,aAAa;GACb,GAAI,sBAAsB,QAAQ,GAC9B,EACE,cAAc,EACZ,OAAO,QAAQ,aAAa,iBAAiB,EAC9C,EACF,GACD,EAAE;GAMN,aAAa,cAAc,QAAQ,KAAK;GACxC,GAAI,cAAc,QAAQ,KAAK,GAC3B,EACE,MAAM;IACJ,SAAS;IACT,GAAI,QAAQ,KAAK,SAAS,EAAE,QAAQ,QAAQ,KAAK,QAAQ,GAAG,EAAE;IAC/D,EACF,GACD,EAAE;GACN,yBAAyB,CAAC,CAAC,QAAQ;GACnC,GAAI,sBAAsB,QAAQ,GAC9B,EAAE,eAAe,qBAAqB,QAAQ,EAAE,GAChD,EAAE;GACN,mBAAmB,qBAAqB;GACzC;AAED,SAAO,IAAI,SAAS,KAAK,UAAU,YAAY,EAAE;GAC/C,QAAQ;GACR,SAAS,EAAE,gBAAgB,oBAAoB;GAChD,CAAC;UACK,OAAO;AACd,SAAO,IAAI,SACT,KAAK,UAAU;GACb,OAAO;GACP,SAAS,iBAAiB,QAAQ,MAAM,UAAU;GACnD,CAAC,EACF;GACE,QAAQ;GACR,SAAS,EAAE,gBAAgB,oBAAoB;GAChD,CACF;;;AAIL,SAAS,0BACP,SACA,wBAC2B;CAC3B,MAAM,uBACJ,sBAAsB,QAAQ,IAC9B,6BAA6B,QAAQ,OAAO;CAC9C,MAAM,yBAAyB,0BAA0B;CACzD,MAAM,wBACJ,0BAA0B,sBAAsB,QAAQ;AAE1D,QAAO;EACL,MAAM;EACN,SAAS;EACT,WAAW;EACX,kBAAkB;EACnB"}
|
|
@@ -28,7 +28,8 @@ async function handleConnectAgent({ runtime, request, agentId }) {
|
|
|
28
28
|
runtime,
|
|
29
29
|
request,
|
|
30
30
|
agentId,
|
|
31
|
-
threadId: connectRequest.input.threadId
|
|
31
|
+
threadId: connectRequest.input.threadId,
|
|
32
|
+
agent
|
|
32
33
|
});
|
|
33
34
|
} catch (error) {
|
|
34
35
|
console.error("Error running agent:", error);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handle-connect.cjs","names":["cloneAgentForRequest","parseConnectRequest","isIntelligenceRuntime","handleIntelligenceConnect","handleSseConnect"],"sources":["../../../../src/v2/runtime/handlers/handle-connect.ts"],"sourcesContent":["import { handleIntelligenceConnect } from \"./intelligence/connect\";\nimport { handleSseConnect } from \"./sse/connect\";\nimport { isIntelligenceRuntime } from \"../core/runtime\";\nimport { telemetry } from \"../telemetry\";\nimport {
|
|
1
|
+
{"version":3,"file":"handle-connect.cjs","names":["cloneAgentForRequest","parseConnectRequest","isIntelligenceRuntime","handleIntelligenceConnect","handleSseConnect"],"sources":["../../../../src/v2/runtime/handlers/handle-connect.ts"],"sourcesContent":["import { handleIntelligenceConnect } from \"./intelligence/connect\";\nimport { handleSseConnect } from \"./sse/connect\";\nimport { isIntelligenceRuntime } from \"../core/runtime\";\nimport { telemetry } from \"../telemetry\";\nimport type { RunAgentParameters as ConnectAgentParameters } from \"./shared/agent-utils\";\nimport {\n parseConnectRequest,\n cloneAgentForRequest,\n} from \"./shared/agent-utils\";\n\nexport async function handleConnectAgent({\n runtime,\n request,\n agentId,\n}: ConnectAgentParameters) {\n telemetry.capture(\"oss.runtime.copilot_request_created\", {\n \"cloud.guardrails.enabled\": false,\n requestType: \"connect\",\n \"cloud.api_key_provided\": !!request.headers.get(\n \"x-copilotcloud-public-api-key\",\n ),\n ...(request.headers.get(\"x-copilotcloud-public-api-key\")\n ? {\n \"cloud.public_api_key\": request.headers.get(\n \"x-copilotcloud-public-api-key\",\n )!,\n }\n : {}),\n });\n\n try {\n // Runs on BOTH branches deliberately: this is the only place the connect\n // path validates that `agentId` exists, returning a 404 `Response` for an\n // unknown agent. The intelligence branch below never re-checks the id (it\n // hands `agentId` straight to `ɵconnectThread`), so this clone/resolve must\n // happen before the branch split or unknown agents would slip through. The\n // SSE branch additionally uses the returned clone's `agent.headers`.\n const agent = await cloneAgentForRequest(runtime, agentId, request);\n if (agent instanceof Response) {\n return agent;\n }\n\n const connectRequest = await parseConnectRequest(request);\n if (connectRequest instanceof Response) {\n return connectRequest;\n }\n\n if (isIntelligenceRuntime(runtime)) {\n return handleIntelligenceConnect({\n runtime,\n request,\n agentId,\n threadId: connectRequest.input.threadId,\n });\n }\n\n return handleSseConnect({\n runtime,\n request,\n agentId,\n threadId: connectRequest.input.threadId,\n // Pass the per-request clone so its server-configured `agent.headers` are\n // folded into the merged header set threaded into `runner.connect`. That\n // merge is forward-looking plumbing — no shipped runner consumes\n // connect-path headers yet; see the note in `sse/connect.ts`.\n agent,\n });\n } catch (error) {\n console.error(\"Error running agent:\", error);\n console.error(\n \"Error stack:\",\n error instanceof Error ? error.stack : \"No stack trace\",\n );\n console.error(\"Error details:\", {\n name: error instanceof Error ? error.name : \"Unknown\",\n message: error instanceof Error ? error.message : String(error),\n cause: error instanceof Error ? error.cause : undefined,\n });\n\n return new Response(\n JSON.stringify({\n error: \"Failed to run agent\",\n message: error instanceof Error ? error.message : \"Unknown error\",\n }),\n {\n status: 500,\n headers: { \"Content-Type\": \"application/json\" },\n },\n );\n }\n}\n"],"mappings":";;;;;;;;AAUA,eAAsB,mBAAmB,EACvC,SACA,SACA,WACyB;AACzB,kCAAU,QAAQ,uCAAuC;EACvD,4BAA4B;EAC5B,aAAa;EACb,0BAA0B,CAAC,CAAC,QAAQ,QAAQ,IAC1C,gCACD;EACD,GAAI,QAAQ,QAAQ,IAAI,gCAAgC,GACpD,EACE,wBAAwB,QAAQ,QAAQ,IACtC,gCACD,EACF,GACD,EAAE;EACP,CAAC;AAEF,KAAI;EAOF,MAAM,QAAQ,MAAMA,yCAAqB,SAAS,SAAS,QAAQ;AACnE,MAAI,iBAAiB,SACnB,QAAO;EAGT,MAAM,iBAAiB,MAAMC,wCAAoB,QAAQ;AACzD,MAAI,0BAA0B,SAC5B,QAAO;AAGT,MAAIC,sCAAsB,QAAQ,CAChC,QAAOC,0CAA0B;GAC/B;GACA;GACA;GACA,UAAU,eAAe,MAAM;GAChC,CAAC;AAGJ,SAAOC,mCAAiB;GACtB;GACA;GACA;GACA,UAAU,eAAe,MAAM;GAK/B;GACD,CAAC;UACK,OAAO;AACd,UAAQ,MAAM,wBAAwB,MAAM;AAC5C,UAAQ,MACN,gBACA,iBAAiB,QAAQ,MAAM,QAAQ,iBACxC;AACD,UAAQ,MAAM,kBAAkB;GAC9B,MAAM,iBAAiB,QAAQ,MAAM,OAAO;GAC5C,SAAS,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;GAC/D,OAAO,iBAAiB,QAAQ,MAAM,QAAQ;GAC/C,CAAC;AAEF,SAAO,IAAI,SACT,KAAK,UAAU;GACb,OAAO;GACP,SAAS,iBAAiB,QAAQ,MAAM,UAAU;GACnD,CAAC,EACF;GACE,QAAQ;GACR,SAAS,EAAE,gBAAgB,oBAAoB;GAChD,CACF"}
|
|
@@ -28,7 +28,8 @@ async function handleConnectAgent({ runtime, request, agentId }) {
|
|
|
28
28
|
runtime,
|
|
29
29
|
request,
|
|
30
30
|
agentId,
|
|
31
|
-
threadId: connectRequest.input.threadId
|
|
31
|
+
threadId: connectRequest.input.threadId,
|
|
32
|
+
agent
|
|
32
33
|
});
|
|
33
34
|
} catch (error) {
|
|
34
35
|
console.error("Error running agent:", error);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handle-connect.mjs","names":[],"sources":["../../../../src/v2/runtime/handlers/handle-connect.ts"],"sourcesContent":["import { handleIntelligenceConnect } from \"./intelligence/connect\";\nimport { handleSseConnect } from \"./sse/connect\";\nimport { isIntelligenceRuntime } from \"../core/runtime\";\nimport { telemetry } from \"../telemetry\";\nimport {
|
|
1
|
+
{"version":3,"file":"handle-connect.mjs","names":[],"sources":["../../../../src/v2/runtime/handlers/handle-connect.ts"],"sourcesContent":["import { handleIntelligenceConnect } from \"./intelligence/connect\";\nimport { handleSseConnect } from \"./sse/connect\";\nimport { isIntelligenceRuntime } from \"../core/runtime\";\nimport { telemetry } from \"../telemetry\";\nimport type { RunAgentParameters as ConnectAgentParameters } from \"./shared/agent-utils\";\nimport {\n parseConnectRequest,\n cloneAgentForRequest,\n} from \"./shared/agent-utils\";\n\nexport async function handleConnectAgent({\n runtime,\n request,\n agentId,\n}: ConnectAgentParameters) {\n telemetry.capture(\"oss.runtime.copilot_request_created\", {\n \"cloud.guardrails.enabled\": false,\n requestType: \"connect\",\n \"cloud.api_key_provided\": !!request.headers.get(\n \"x-copilotcloud-public-api-key\",\n ),\n ...(request.headers.get(\"x-copilotcloud-public-api-key\")\n ? {\n \"cloud.public_api_key\": request.headers.get(\n \"x-copilotcloud-public-api-key\",\n )!,\n }\n : {}),\n });\n\n try {\n // Runs on BOTH branches deliberately: this is the only place the connect\n // path validates that `agentId` exists, returning a 404 `Response` for an\n // unknown agent. The intelligence branch below never re-checks the id (it\n // hands `agentId` straight to `ɵconnectThread`), so this clone/resolve must\n // happen before the branch split or unknown agents would slip through. The\n // SSE branch additionally uses the returned clone's `agent.headers`.\n const agent = await cloneAgentForRequest(runtime, agentId, request);\n if (agent instanceof Response) {\n return agent;\n }\n\n const connectRequest = await parseConnectRequest(request);\n if (connectRequest instanceof Response) {\n return connectRequest;\n }\n\n if (isIntelligenceRuntime(runtime)) {\n return handleIntelligenceConnect({\n runtime,\n request,\n agentId,\n threadId: connectRequest.input.threadId,\n });\n }\n\n return handleSseConnect({\n runtime,\n request,\n agentId,\n threadId: connectRequest.input.threadId,\n // Pass the per-request clone so its server-configured `agent.headers` are\n // folded into the merged header set threaded into `runner.connect`. That\n // merge is forward-looking plumbing — no shipped runner consumes\n // connect-path headers yet; see the note in `sse/connect.ts`.\n agent,\n });\n } catch (error) {\n console.error(\"Error running agent:\", error);\n console.error(\n \"Error stack:\",\n error instanceof Error ? error.stack : \"No stack trace\",\n );\n console.error(\"Error details:\", {\n name: error instanceof Error ? error.name : \"Unknown\",\n message: error instanceof Error ? error.message : String(error),\n cause: error instanceof Error ? error.cause : undefined,\n });\n\n return new Response(\n JSON.stringify({\n error: \"Failed to run agent\",\n message: error instanceof Error ? error.message : \"Unknown error\",\n }),\n {\n status: 500,\n headers: { \"Content-Type\": \"application/json\" },\n },\n );\n }\n}\n"],"mappings":";;;;;;;;AAUA,eAAsB,mBAAmB,EACvC,SACA,SACA,WACyB;AACzB,WAAU,QAAQ,uCAAuC;EACvD,4BAA4B;EAC5B,aAAa;EACb,0BAA0B,CAAC,CAAC,QAAQ,QAAQ,IAC1C,gCACD;EACD,GAAI,QAAQ,QAAQ,IAAI,gCAAgC,GACpD,EACE,wBAAwB,QAAQ,QAAQ,IACtC,gCACD,EACF,GACD,EAAE;EACP,CAAC;AAEF,KAAI;EAOF,MAAM,QAAQ,MAAM,qBAAqB,SAAS,SAAS,QAAQ;AACnE,MAAI,iBAAiB,SACnB,QAAO;EAGT,MAAM,iBAAiB,MAAM,oBAAoB,QAAQ;AACzD,MAAI,0BAA0B,SAC5B,QAAO;AAGT,MAAI,sBAAsB,QAAQ,CAChC,QAAO,0BAA0B;GAC/B;GACA;GACA;GACA,UAAU,eAAe,MAAM;GAChC,CAAC;AAGJ,SAAO,iBAAiB;GACtB;GACA;GACA;GACA,UAAU,eAAe,MAAM;GAK/B;GACD,CAAC;UACK,OAAO;AACd,UAAQ,MAAM,wBAAwB,MAAM;AAC5C,UAAQ,MACN,gBACA,iBAAiB,QAAQ,MAAM,QAAQ,iBACxC;AACD,UAAQ,MAAM,kBAAkB;GAC9B,MAAM,iBAAiB,QAAQ,MAAM,OAAO;GAC5C,SAAS,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;GAC/D,OAAO,iBAAiB,QAAQ,MAAM,QAAQ;GAC/C,CAAC;AAEF,SAAO,IAAI,SACT,KAAK,UAAU;GACb,OAAO;GACP,SAAS,iBAAiB,QAAQ,MAAM,UAAU;GACnD,CAAC,EACF;GACE,QAAQ;GACR,SAAS,EAAE,gBAAgB,oBAAoB;GAChD,CACF"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
require("reflect-metadata");
|
|
2
|
+
const require_runtime = require('../../../_virtual/_rolldown/runtime.cjs');
|
|
3
|
+
const require_header_utils = require('./header-utils.cjs');
|
|
4
|
+
const require_agent_utils = require('./shared/agent-utils.cjs');
|
|
5
|
+
const require_sse_response = require('./shared/sse-response.cjs');
|
|
6
|
+
let _copilotkit_shared = require("@copilotkit/shared");
|
|
7
|
+
let rxjs = require("rxjs");
|
|
8
|
+
|
|
9
|
+
//#region src/v2/runtime/handlers/handle-suggest.ts
|
|
10
|
+
/**
|
|
11
|
+
* Stateless suggestion run.
|
|
12
|
+
*
|
|
13
|
+
* Executes the provider agent **directly** and streams its AG-UI events back as
|
|
14
|
+
* SSE — the same wire format as `/agent/:id/run`, so the client consumes it with
|
|
15
|
+
* the stock `HttpAgent` transport and chips fill in as the provider emits them.
|
|
16
|
+
* It deliberately does not go through `runtime.runner`: the runner's
|
|
17
|
+
* `run()` writes to a module-level store keyed by threadId (backing the SSE
|
|
18
|
+
* runtime's local thread endpoints), which would leak the throwaway suggestion
|
|
19
|
+
* thread. This handler runs the agent's event pipeline **without** that
|
|
20
|
+
* persistence — no thread, lock, gateway, name-gen, or run telemetry — so
|
|
21
|
+
* dynamic suggestions are side-effect-free in every runtime mode.
|
|
22
|
+
*
|
|
23
|
+
* The only per-request configuration it applies is forwarding the request's
|
|
24
|
+
* allowlisted headers (`authorization` + `x-*`) onto the agent clone. It does
|
|
25
|
+
* **not** attach any request middleware — no A2UI, no MCPApps, no
|
|
26
|
+
* OpenGenerativeUI, no Intelligence enterprise-learning tools. The **client**
|
|
27
|
+
* (the core suggestion engine) forces `toolChoice: copilotkitSuggest` in the
|
|
28
|
+
* request body; this handler does not set tool choice itself and relies on it
|
|
29
|
+
* being present in the incoming `input`. Given that forced tool choice, any
|
|
30
|
+
* middleware-injected tools are dead weight, and MCPApps setup can incur a
|
|
31
|
+
* `listTools` network round-trip per suggestion under `available: "always"` — a
|
|
32
|
+
* side effect this path must never pay.
|
|
33
|
+
*
|
|
34
|
+
* When the client aborts the HTTP request (via its `AbortController`), the
|
|
35
|
+
* server-side run is cancelled best-effort so an aborted suggestion does not
|
|
36
|
+
* keep running a provider call to completion.
|
|
37
|
+
*
|
|
38
|
+
* @param params - The runtime, request, and resolved agent id.
|
|
39
|
+
* @returns A `text/event-stream` `Response` of the provider agent's AG-UI events
|
|
40
|
+
* on success, or the resolution `Response` (e.g. 404/400) when agent/body
|
|
41
|
+
* resolution fails before streaming begins.
|
|
42
|
+
*/
|
|
43
|
+
async function handleSuggestAgent({ runtime, request, agentId }) {
|
|
44
|
+
const agent = await require_agent_utils.cloneAgentForRequest(runtime, agentId, request);
|
|
45
|
+
if (agent instanceof Response) return agent;
|
|
46
|
+
agent.agentId = agentId;
|
|
47
|
+
const input = await require_agent_utils.parseRunRequest(request);
|
|
48
|
+
if (input instanceof Response) return input;
|
|
49
|
+
const headerCarryingAgent = agent;
|
|
50
|
+
headerCarryingAgent.headers = require_header_utils.mergeForwardableHeaders(headerCarryingAgent.headers, request, runtime.forwardHeadersPolicy ?? require_header_utils.resolveForwardHeadersPolicy(void 0));
|
|
51
|
+
agent.setMessages(input.messages);
|
|
52
|
+
agent.setState(input.state);
|
|
53
|
+
agent.threadId = input.threadId;
|
|
54
|
+
return require_sse_response.createSseEventResponse({
|
|
55
|
+
request,
|
|
56
|
+
agentId,
|
|
57
|
+
captureTelemetry: false,
|
|
58
|
+
observableFactory: () => new rxjs.Observable((subscriber) => {
|
|
59
|
+
const collected = [];
|
|
60
|
+
let settled = false;
|
|
61
|
+
agent.runAgent(input, { onEvent: ({ event }) => {
|
|
62
|
+
collected.push(event);
|
|
63
|
+
subscriber.next(event);
|
|
64
|
+
} }).then(() => {
|
|
65
|
+
for (const event of (0, _copilotkit_shared.finalizeRunEvents)(collected, { stopRequested: false })) subscriber.next(event);
|
|
66
|
+
settled = true;
|
|
67
|
+
subscriber.complete();
|
|
68
|
+
}).catch((error) => {
|
|
69
|
+
settled = true;
|
|
70
|
+
subscriber.error(error);
|
|
71
|
+
});
|
|
72
|
+
return () => {
|
|
73
|
+
if (!settled && typeof agent.abortRun === "function") try {
|
|
74
|
+
agent.abortRun();
|
|
75
|
+
} catch {}
|
|
76
|
+
};
|
|
77
|
+
})
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
//#endregion
|
|
82
|
+
exports.handleSuggestAgent = handleSuggestAgent;
|
|
83
|
+
//# sourceMappingURL=handle-suggest.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handle-suggest.cjs","names":["cloneAgentForRequest","parseRunRequest","mergeForwardableHeaders","resolveForwardHeadersPolicy","createSseEventResponse","Observable"],"sources":["../../../../src/v2/runtime/handlers/handle-suggest.ts"],"sourcesContent":["import type { AbstractAgent, BaseEvent } from \"@ag-ui/client\";\nimport { finalizeRunEvents } from \"@copilotkit/shared\";\nimport { Observable } from \"rxjs\";\nimport type { CopilotRuntimeLike } from \"../core/runtime\";\nimport {\n mergeForwardableHeaders,\n resolveForwardHeadersPolicy,\n} from \"./header-utils\";\nimport { cloneAgentForRequest, parseRunRequest } from \"./shared/agent-utils\";\nimport { createSseEventResponse } from \"./shared/sse-response\";\n\n/**\n * Parameters for {@link handleSuggestAgent}.\n */\ninterface SuggestAgentParameters {\n /** The runtime whose registry the agent is resolved from. */\n runtime: CopilotRuntimeLike;\n /** The incoming `POST /agent/:agentId/suggest` request. */\n request: Request;\n /** The route-resolved agent id to run. */\n agentId: string;\n}\n\n/**\n * Stateless suggestion run.\n *\n * Executes the provider agent **directly** and streams its AG-UI events back as\n * SSE — the same wire format as `/agent/:id/run`, so the client consumes it with\n * the stock `HttpAgent` transport and chips fill in as the provider emits them.\n * It deliberately does not go through `runtime.runner`: the runner's\n * `run()` writes to a module-level store keyed by threadId (backing the SSE\n * runtime's local thread endpoints), which would leak the throwaway suggestion\n * thread. This handler runs the agent's event pipeline **without** that\n * persistence — no thread, lock, gateway, name-gen, or run telemetry — so\n * dynamic suggestions are side-effect-free in every runtime mode.\n *\n * The only per-request configuration it applies is forwarding the request's\n * allowlisted headers (`authorization` + `x-*`) onto the agent clone. It does\n * **not** attach any request middleware — no A2UI, no MCPApps, no\n * OpenGenerativeUI, no Intelligence enterprise-learning tools. The **client**\n * (the core suggestion engine) forces `toolChoice: copilotkitSuggest` in the\n * request body; this handler does not set tool choice itself and relies on it\n * being present in the incoming `input`. Given that forced tool choice, any\n * middleware-injected tools are dead weight, and MCPApps setup can incur a\n * `listTools` network round-trip per suggestion under `available: \"always\"` — a\n * side effect this path must never pay.\n *\n * When the client aborts the HTTP request (via its `AbortController`), the\n * server-side run is cancelled best-effort so an aborted suggestion does not\n * keep running a provider call to completion.\n *\n * @param params - The runtime, request, and resolved agent id.\n * @returns A `text/event-stream` `Response` of the provider agent's AG-UI events\n * on success, or the resolution `Response` (e.g. 404/400) when agent/body\n * resolution fails before streaming begins.\n */\nexport async function handleSuggestAgent({\n runtime,\n request,\n agentId,\n}: SuggestAgentParameters): Promise<Response> {\n const agent = await cloneAgentForRequest(runtime, agentId, request);\n if (agent instanceof Response) {\n return agent;\n }\n\n // Carry the registry key onto the clone so any framework relying on it during\n // the run sees the correct agentId.\n agent.agentId = agentId;\n\n const input = await parseRunRequest(request);\n if (input instanceof Response) {\n return input;\n }\n\n // Forward eligible inbound headers onto the clone under the runtime's resolved\n // forwarding policy (same helper the run handler uses: `authorization` /\n // custom `x-*`, infra/proxy/platform headers denylisted, server-set headers\n // winning on collision — #5712). Unlike a full agent run, the suggest path\n // intentionally attaches no middleware (see the handler docblock): the forced\n // `copilotkitSuggest` tool choice makes any middleware-injected tools dead\n // weight, and MCPApps setup can trigger a `listTools` network round-trip we\n // must never incur per suggestion.\n //\n // `AbstractAgent` doesn't declare `headers` on its base type (concrete\n // framework agents add it), so narrow to the header-carrying shape rather\n // than casting to `any`.\n const headerCarryingAgent = agent as AbstractAgent & {\n headers?: Record<string, string>;\n };\n headerCarryingAgent.headers = mergeForwardableHeaders(\n headerCarryingAgent.headers,\n request,\n runtime.forwardHeadersPolicy ?? resolveForwardHeadersPolicy(undefined),\n );\n\n agent.setMessages(input.messages);\n agent.setState(input.state);\n agent.threadId = input.threadId;\n\n // Stream the provider agent's events over SSE without persistence. This is\n // the runner's event pipeline (`agent.runAgent({ onEvent })` + terminal-event\n // finalization) minus the `GLOBAL_STORE` writes that would leak a thread.\n // `captureTelemetry: false` keeps suggestions out of run telemetry; omitting\n // `debugEventBus` keeps them out of the inspector's run trace.\n return createSseEventResponse({\n request,\n agentId,\n captureTelemetry: false,\n observableFactory: () =>\n new Observable<BaseEvent>((subscriber) => {\n // Collected so `finalizeRunEvents` can append any missing terminal\n // events (e.g. an unclosed message/tool call, or a `RUN_FINISHED`) —\n // the same closure the runner applies — so the client sees a\n // well-formed AG-UI sequence.\n const collected: BaseEvent[] = [];\n let settled = false;\n\n void agent\n .runAgent(input, {\n onEvent: ({ event }) => {\n collected.push(event);\n subscriber.next(event);\n },\n })\n .then(() => {\n for (const event of finalizeRunEvents(collected, {\n stopRequested: false,\n })) {\n subscriber.next(event);\n }\n settled = true;\n subscriber.complete();\n })\n .catch((error: unknown) => {\n settled = true;\n subscriber.error(error);\n });\n\n // Teardown fires when the response stream is torn down (client abort /\n // disconnect). Cancel the still-running provider call best-effort.\n return () => {\n if (!settled && typeof agent.abortRun === \"function\") {\n try {\n agent.abortRun();\n } catch {\n // best-effort — nothing actionable if aborting the run fails.\n }\n }\n };\n }),\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwDA,eAAsB,mBAAmB,EACvC,SACA,SACA,WAC4C;CAC5C,MAAM,QAAQ,MAAMA,yCAAqB,SAAS,SAAS,QAAQ;AACnE,KAAI,iBAAiB,SACnB,QAAO;AAKT,OAAM,UAAU;CAEhB,MAAM,QAAQ,MAAMC,oCAAgB,QAAQ;AAC5C,KAAI,iBAAiB,SACnB,QAAO;CAeT,MAAM,sBAAsB;AAG5B,qBAAoB,UAAUC,6CAC5B,oBAAoB,SACpB,SACA,QAAQ,wBAAwBC,iDAA4B,OAAU,CACvE;AAED,OAAM,YAAY,MAAM,SAAS;AACjC,OAAM,SAAS,MAAM,MAAM;AAC3B,OAAM,WAAW,MAAM;AAOvB,QAAOC,4CAAuB;EAC5B;EACA;EACA,kBAAkB;EAClB,yBACE,IAAIC,iBAAuB,eAAe;GAKxC,MAAM,YAAyB,EAAE;GACjC,IAAI,UAAU;AAEd,GAAK,MACF,SAAS,OAAO,EACf,UAAU,EAAE,YAAY;AACtB,cAAU,KAAK,MAAM;AACrB,eAAW,KAAK,MAAM;MAEzB,CAAC,CACD,WAAW;AACV,SAAK,MAAM,mDAA2B,WAAW,EAC/C,eAAe,OAChB,CAAC,CACA,YAAW,KAAK,MAAM;AAExB,cAAU;AACV,eAAW,UAAU;KACrB,CACD,OAAO,UAAmB;AACzB,cAAU;AACV,eAAW,MAAM,MAAM;KACvB;AAIJ,gBAAa;AACX,QAAI,CAAC,WAAW,OAAO,MAAM,aAAa,WACxC,KAAI;AACF,WAAM,UAAU;YACV;;IAKZ;EACL,CAAC"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import "reflect-metadata";
|
|
2
|
+
import { mergeForwardableHeaders, resolveForwardHeadersPolicy } from "./header-utils.mjs";
|
|
3
|
+
import { cloneAgentForRequest, parseRunRequest } from "./shared/agent-utils.mjs";
|
|
4
|
+
import { createSseEventResponse } from "./shared/sse-response.mjs";
|
|
5
|
+
import { finalizeRunEvents } from "@copilotkit/shared";
|
|
6
|
+
import { Observable } from "rxjs";
|
|
7
|
+
|
|
8
|
+
//#region src/v2/runtime/handlers/handle-suggest.ts
|
|
9
|
+
/**
|
|
10
|
+
* Stateless suggestion run.
|
|
11
|
+
*
|
|
12
|
+
* Executes the provider agent **directly** and streams its AG-UI events back as
|
|
13
|
+
* SSE — the same wire format as `/agent/:id/run`, so the client consumes it with
|
|
14
|
+
* the stock `HttpAgent` transport and chips fill in as the provider emits them.
|
|
15
|
+
* It deliberately does not go through `runtime.runner`: the runner's
|
|
16
|
+
* `run()` writes to a module-level store keyed by threadId (backing the SSE
|
|
17
|
+
* runtime's local thread endpoints), which would leak the throwaway suggestion
|
|
18
|
+
* thread. This handler runs the agent's event pipeline **without** that
|
|
19
|
+
* persistence — no thread, lock, gateway, name-gen, or run telemetry — so
|
|
20
|
+
* dynamic suggestions are side-effect-free in every runtime mode.
|
|
21
|
+
*
|
|
22
|
+
* The only per-request configuration it applies is forwarding the request's
|
|
23
|
+
* allowlisted headers (`authorization` + `x-*`) onto the agent clone. It does
|
|
24
|
+
* **not** attach any request middleware — no A2UI, no MCPApps, no
|
|
25
|
+
* OpenGenerativeUI, no Intelligence enterprise-learning tools. The **client**
|
|
26
|
+
* (the core suggestion engine) forces `toolChoice: copilotkitSuggest` in the
|
|
27
|
+
* request body; this handler does not set tool choice itself and relies on it
|
|
28
|
+
* being present in the incoming `input`. Given that forced tool choice, any
|
|
29
|
+
* middleware-injected tools are dead weight, and MCPApps setup can incur a
|
|
30
|
+
* `listTools` network round-trip per suggestion under `available: "always"` — a
|
|
31
|
+
* side effect this path must never pay.
|
|
32
|
+
*
|
|
33
|
+
* When the client aborts the HTTP request (via its `AbortController`), the
|
|
34
|
+
* server-side run is cancelled best-effort so an aborted suggestion does not
|
|
35
|
+
* keep running a provider call to completion.
|
|
36
|
+
*
|
|
37
|
+
* @param params - The runtime, request, and resolved agent id.
|
|
38
|
+
* @returns A `text/event-stream` `Response` of the provider agent's AG-UI events
|
|
39
|
+
* on success, or the resolution `Response` (e.g. 404/400) when agent/body
|
|
40
|
+
* resolution fails before streaming begins.
|
|
41
|
+
*/
|
|
42
|
+
async function handleSuggestAgent({ runtime, request, agentId }) {
|
|
43
|
+
const agent = await cloneAgentForRequest(runtime, agentId, request);
|
|
44
|
+
if (agent instanceof Response) return agent;
|
|
45
|
+
agent.agentId = agentId;
|
|
46
|
+
const input = await parseRunRequest(request);
|
|
47
|
+
if (input instanceof Response) return input;
|
|
48
|
+
const headerCarryingAgent = agent;
|
|
49
|
+
headerCarryingAgent.headers = mergeForwardableHeaders(headerCarryingAgent.headers, request, runtime.forwardHeadersPolicy ?? resolveForwardHeadersPolicy(void 0));
|
|
50
|
+
agent.setMessages(input.messages);
|
|
51
|
+
agent.setState(input.state);
|
|
52
|
+
agent.threadId = input.threadId;
|
|
53
|
+
return createSseEventResponse({
|
|
54
|
+
request,
|
|
55
|
+
agentId,
|
|
56
|
+
captureTelemetry: false,
|
|
57
|
+
observableFactory: () => new Observable((subscriber) => {
|
|
58
|
+
const collected = [];
|
|
59
|
+
let settled = false;
|
|
60
|
+
agent.runAgent(input, { onEvent: ({ event }) => {
|
|
61
|
+
collected.push(event);
|
|
62
|
+
subscriber.next(event);
|
|
63
|
+
} }).then(() => {
|
|
64
|
+
for (const event of finalizeRunEvents(collected, { stopRequested: false })) subscriber.next(event);
|
|
65
|
+
settled = true;
|
|
66
|
+
subscriber.complete();
|
|
67
|
+
}).catch((error) => {
|
|
68
|
+
settled = true;
|
|
69
|
+
subscriber.error(error);
|
|
70
|
+
});
|
|
71
|
+
return () => {
|
|
72
|
+
if (!settled && typeof agent.abortRun === "function") try {
|
|
73
|
+
agent.abortRun();
|
|
74
|
+
} catch {}
|
|
75
|
+
};
|
|
76
|
+
})
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
//#endregion
|
|
81
|
+
export { handleSuggestAgent };
|
|
82
|
+
//# sourceMappingURL=handle-suggest.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handle-suggest.mjs","names":[],"sources":["../../../../src/v2/runtime/handlers/handle-suggest.ts"],"sourcesContent":["import type { AbstractAgent, BaseEvent } from \"@ag-ui/client\";\nimport { finalizeRunEvents } from \"@copilotkit/shared\";\nimport { Observable } from \"rxjs\";\nimport type { CopilotRuntimeLike } from \"../core/runtime\";\nimport {\n mergeForwardableHeaders,\n resolveForwardHeadersPolicy,\n} from \"./header-utils\";\nimport { cloneAgentForRequest, parseRunRequest } from \"./shared/agent-utils\";\nimport { createSseEventResponse } from \"./shared/sse-response\";\n\n/**\n * Parameters for {@link handleSuggestAgent}.\n */\ninterface SuggestAgentParameters {\n /** The runtime whose registry the agent is resolved from. */\n runtime: CopilotRuntimeLike;\n /** The incoming `POST /agent/:agentId/suggest` request. */\n request: Request;\n /** The route-resolved agent id to run. */\n agentId: string;\n}\n\n/**\n * Stateless suggestion run.\n *\n * Executes the provider agent **directly** and streams its AG-UI events back as\n * SSE — the same wire format as `/agent/:id/run`, so the client consumes it with\n * the stock `HttpAgent` transport and chips fill in as the provider emits them.\n * It deliberately does not go through `runtime.runner`: the runner's\n * `run()` writes to a module-level store keyed by threadId (backing the SSE\n * runtime's local thread endpoints), which would leak the throwaway suggestion\n * thread. This handler runs the agent's event pipeline **without** that\n * persistence — no thread, lock, gateway, name-gen, or run telemetry — so\n * dynamic suggestions are side-effect-free in every runtime mode.\n *\n * The only per-request configuration it applies is forwarding the request's\n * allowlisted headers (`authorization` + `x-*`) onto the agent clone. It does\n * **not** attach any request middleware — no A2UI, no MCPApps, no\n * OpenGenerativeUI, no Intelligence enterprise-learning tools. The **client**\n * (the core suggestion engine) forces `toolChoice: copilotkitSuggest` in the\n * request body; this handler does not set tool choice itself and relies on it\n * being present in the incoming `input`. Given that forced tool choice, any\n * middleware-injected tools are dead weight, and MCPApps setup can incur a\n * `listTools` network round-trip per suggestion under `available: \"always\"` — a\n * side effect this path must never pay.\n *\n * When the client aborts the HTTP request (via its `AbortController`), the\n * server-side run is cancelled best-effort so an aborted suggestion does not\n * keep running a provider call to completion.\n *\n * @param params - The runtime, request, and resolved agent id.\n * @returns A `text/event-stream` `Response` of the provider agent's AG-UI events\n * on success, or the resolution `Response` (e.g. 404/400) when agent/body\n * resolution fails before streaming begins.\n */\nexport async function handleSuggestAgent({\n runtime,\n request,\n agentId,\n}: SuggestAgentParameters): Promise<Response> {\n const agent = await cloneAgentForRequest(runtime, agentId, request);\n if (agent instanceof Response) {\n return agent;\n }\n\n // Carry the registry key onto the clone so any framework relying on it during\n // the run sees the correct agentId.\n agent.agentId = agentId;\n\n const input = await parseRunRequest(request);\n if (input instanceof Response) {\n return input;\n }\n\n // Forward eligible inbound headers onto the clone under the runtime's resolved\n // forwarding policy (same helper the run handler uses: `authorization` /\n // custom `x-*`, infra/proxy/platform headers denylisted, server-set headers\n // winning on collision — #5712). Unlike a full agent run, the suggest path\n // intentionally attaches no middleware (see the handler docblock): the forced\n // `copilotkitSuggest` tool choice makes any middleware-injected tools dead\n // weight, and MCPApps setup can trigger a `listTools` network round-trip we\n // must never incur per suggestion.\n //\n // `AbstractAgent` doesn't declare `headers` on its base type (concrete\n // framework agents add it), so narrow to the header-carrying shape rather\n // than casting to `any`.\n const headerCarryingAgent = agent as AbstractAgent & {\n headers?: Record<string, string>;\n };\n headerCarryingAgent.headers = mergeForwardableHeaders(\n headerCarryingAgent.headers,\n request,\n runtime.forwardHeadersPolicy ?? resolveForwardHeadersPolicy(undefined),\n );\n\n agent.setMessages(input.messages);\n agent.setState(input.state);\n agent.threadId = input.threadId;\n\n // Stream the provider agent's events over SSE without persistence. This is\n // the runner's event pipeline (`agent.runAgent({ onEvent })` + terminal-event\n // finalization) minus the `GLOBAL_STORE` writes that would leak a thread.\n // `captureTelemetry: false` keeps suggestions out of run telemetry; omitting\n // `debugEventBus` keeps them out of the inspector's run trace.\n return createSseEventResponse({\n request,\n agentId,\n captureTelemetry: false,\n observableFactory: () =>\n new Observable<BaseEvent>((subscriber) => {\n // Collected so `finalizeRunEvents` can append any missing terminal\n // events (e.g. an unclosed message/tool call, or a `RUN_FINISHED`) —\n // the same closure the runner applies — so the client sees a\n // well-formed AG-UI sequence.\n const collected: BaseEvent[] = [];\n let settled = false;\n\n void agent\n .runAgent(input, {\n onEvent: ({ event }) => {\n collected.push(event);\n subscriber.next(event);\n },\n })\n .then(() => {\n for (const event of finalizeRunEvents(collected, {\n stopRequested: false,\n })) {\n subscriber.next(event);\n }\n settled = true;\n subscriber.complete();\n })\n .catch((error: unknown) => {\n settled = true;\n subscriber.error(error);\n });\n\n // Teardown fires when the response stream is torn down (client abort /\n // disconnect). Cancel the still-running provider call best-effort.\n return () => {\n if (!settled && typeof agent.abortRun === \"function\") {\n try {\n agent.abortRun();\n } catch {\n // best-effort — nothing actionable if aborting the run fails.\n }\n }\n };\n }),\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwDA,eAAsB,mBAAmB,EACvC,SACA,SACA,WAC4C;CAC5C,MAAM,QAAQ,MAAM,qBAAqB,SAAS,SAAS,QAAQ;AACnE,KAAI,iBAAiB,SACnB,QAAO;AAKT,OAAM,UAAU;CAEhB,MAAM,QAAQ,MAAM,gBAAgB,QAAQ;AAC5C,KAAI,iBAAiB,SACnB,QAAO;CAeT,MAAM,sBAAsB;AAG5B,qBAAoB,UAAU,wBAC5B,oBAAoB,SACpB,SACA,QAAQ,wBAAwB,4BAA4B,OAAU,CACvE;AAED,OAAM,YAAY,MAAM,SAAS;AACjC,OAAM,SAAS,MAAM,MAAM;AAC3B,OAAM,WAAW,MAAM;AAOvB,QAAO,uBAAuB;EAC5B;EACA;EACA,kBAAkB;EAClB,yBACE,IAAI,YAAuB,eAAe;GAKxC,MAAM,YAAyB,EAAE;GACjC,IAAI,UAAU;AAEd,GAAK,MACF,SAAS,OAAO,EACf,UAAU,EAAE,YAAY;AACtB,cAAU,KAAK,MAAM;AACrB,eAAW,KAAK,MAAM;MAEzB,CAAC,CACD,WAAW;AACV,SAAK,MAAM,SAAS,kBAAkB,WAAW,EAC/C,eAAe,OAChB,CAAC,CACA,YAAW,KAAK,MAAM;AAExB,cAAU;AACV,eAAW,UAAU;KACrB,CACD,OAAO,UAAmB;AACzB,cAAU;AACV,eAAW,MAAM,MAAM;KACvB;AAIJ,gBAAa;AACX,QAAI,CAAC,WAAW,OAAO,MAAM,aAAa,WACxC,KAAI;AACF,WAAM,UAAU;YACV;;IAKZ;EACL,CAAC"}
|
|
@@ -2,25 +2,185 @@ require("reflect-metadata");
|
|
|
2
2
|
|
|
3
3
|
//#region src/v2/runtime/handlers/header-utils.ts
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
5
|
+
* Exact header names (lowercased) stripped from forwarding by default.
|
|
6
|
+
*
|
|
7
|
+
* These are infrastructure/proxy/platform artifacts that no legitimate agent
|
|
8
|
+
* integration depends on receiving *forwarded from the inbound edge* — the
|
|
9
|
+
* inbound request has already traversed a browser, CDN/edge, load balancer, and
|
|
10
|
+
* hosting platform, each of which stamps its own `x-*` headers. Forwarding them
|
|
11
|
+
* verbatim to an arbitrary configured agent URL leaks client topology and, in
|
|
12
|
+
* the Copilot Cloud case, a platform credential (#5712).
|
|
13
|
+
*
|
|
14
|
+
* `x-amz-cf-id` and `x-copilotcloud-public-api-key` are also covered by the
|
|
15
|
+
* `x-amz-` / `x-copilotcloud-` prefixes below; the exact entries are kept
|
|
16
|
+
* intentionally as documentation anchors for the highest-severity headers
|
|
17
|
+
* (notably the platform API key), not as drift/oversight.
|
|
7
18
|
*/
|
|
8
|
-
|
|
19
|
+
const DEFAULT_DENY_HEADER_NAMES = new Set([
|
|
20
|
+
"x-forwarded-for",
|
|
21
|
+
"x-forwarded-proto",
|
|
22
|
+
"x-forwarded-host",
|
|
23
|
+
"x-forwarded-port",
|
|
24
|
+
"x-forwarded-server",
|
|
25
|
+
"x-real-ip",
|
|
26
|
+
"x-amzn-trace-id",
|
|
27
|
+
"x-amz-cf-id",
|
|
28
|
+
"x-cloud-trace-context",
|
|
29
|
+
"x-cache",
|
|
30
|
+
"x-served-by",
|
|
31
|
+
"x-request-id",
|
|
32
|
+
"x-copilotcloud-public-api-key"
|
|
33
|
+
]);
|
|
34
|
+
/**
|
|
35
|
+
* Header-name prefixes (lowercased) stripped from forwarding by default.
|
|
36
|
+
*
|
|
37
|
+
* Prefix matching covers the well-known platform/CDN families so a new member
|
|
38
|
+
* of a family (e.g. a future `x-vercel-foo`) is denied without a constant edit.
|
|
39
|
+
*/
|
|
40
|
+
const DEFAULT_DENY_HEADER_PREFIXES = [
|
|
41
|
+
"x-amz-",
|
|
42
|
+
"x-azure-",
|
|
43
|
+
"x-fastly-",
|
|
44
|
+
"x-vercel-",
|
|
45
|
+
"x-middleware-",
|
|
46
|
+
"x-copilotcloud-"
|
|
47
|
+
];
|
|
48
|
+
/**
|
|
49
|
+
* Normalizes a public {@link ForwardHeadersConfig} (or `undefined`) into a
|
|
50
|
+
* fully-resolved {@link ResolvedForwardHeadersPolicy}.
|
|
51
|
+
*
|
|
52
|
+
* - `useDefaultDenylist` defaults to `true` (the built-in denylist is active
|
|
53
|
+
* on upgrade); pass `false` to restore the previous wide-open behavior.
|
|
54
|
+
* - `deny` / `denyPrefixes` extend (do not replace) the defaults.
|
|
55
|
+
* - `allow` activates allowlist mode only when it has at least one non-empty
|
|
56
|
+
* entry after normalization.
|
|
57
|
+
*
|
|
58
|
+
* All names/prefixes are trimmed, lowercased, and stripped of empty/
|
|
59
|
+
* whitespace-only entries before use. Trimming/lowercasing keeps matching a
|
|
60
|
+
* plain set/prefix check against the lowercased inbound keys; dropping empties
|
|
61
|
+
* is a safety guard: a stray `denyPrefixes: [""]` would make `startsWith("")`
|
|
62
|
+
* true for every header (silently denying ALL forwarding). Because empties are
|
|
63
|
+
* dropped BEFORE the allowlist-mode decision, an `allow: [""]` / `allow: [" "]`
|
|
64
|
+
* normalizes to an empty set and does NOT switch on allowlist mode — the runtime
|
|
65
|
+
* stays in denylist mode. Allowlist mode activates only when `allow` has at
|
|
66
|
+
* least one non-empty entry; these empty/whitespace-only entries are integrator
|
|
67
|
+
* typos, not intent, so we filter them.
|
|
68
|
+
*/
|
|
69
|
+
function normalizeHeaderEntries(entries) {
|
|
70
|
+
return (entries ?? []).map((entry) => entry.trim().toLowerCase()).filter((entry) => entry.length > 0);
|
|
71
|
+
}
|
|
72
|
+
function resolveForwardHeadersPolicy(config) {
|
|
73
|
+
const denyNames = new Set(normalizeHeaderEntries(config?.deny));
|
|
74
|
+
const denyPrefixes = normalizeHeaderEntries(config?.denyPrefixes);
|
|
75
|
+
const allowEntries = normalizeHeaderEntries(config?.allow);
|
|
76
|
+
const allow = allowEntries.length > 0 ? new Set(allowEntries) : void 0;
|
|
77
|
+
return {
|
|
78
|
+
useDefaultDenylist: config?.useDefaultDenylist ?? true,
|
|
79
|
+
denyNames,
|
|
80
|
+
denyPrefixes,
|
|
81
|
+
allow
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* True iff the (already-lowercased) header name matches the integrator's OWN
|
|
86
|
+
* `deny` / `denyPrefixes`. This is the authoritative subtractive check: it is
|
|
87
|
+
* consulted in BOTH allowlist and denylist mode. It deliberately does NOT
|
|
88
|
+
* include the built-in {@link DEFAULT_DENY_HEADER_NAMES} /
|
|
89
|
+
* {@link DEFAULT_DENY_HEADER_PREFIXES} — an explicit `allow` opts the integrator
|
|
90
|
+
* back into a default-denied header on purpose, so only their own `deny`
|
|
91
|
+
* subtracts from an allowlist.
|
|
92
|
+
*/
|
|
93
|
+
function matchesIntegratorDeny(lower, policy) {
|
|
94
|
+
if (policy.denyNames.has(lower)) return true;
|
|
95
|
+
return policy.denyPrefixes.some((prefix) => lower.startsWith(prefix));
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Determines if a header should be forwarded under the given resolved policy.
|
|
99
|
+
*
|
|
100
|
+
* The integrator's `deny` / `denyPrefixes` ALWAYS strip, including in allowlist
|
|
101
|
+
* mode: `allow` selects the candidate set, `deny` removes from it. A header the
|
|
102
|
+
* integrator lists in BOTH `allow` and `deny` is NOT forwarded — deny is
|
|
103
|
+
* authoritative so a security-motivated `deny` can never be silently defeated
|
|
104
|
+
* by an overlapping `allow` (the footgun this hardens against).
|
|
105
|
+
*
|
|
106
|
+
* Modes:
|
|
107
|
+
* - Allowlist (`policy.allow` set): forward iff the name is in `allow` AND is
|
|
108
|
+
* NOT matched by the integrator's `deny` / `denyPrefixes`. Nothing else
|
|
109
|
+
* forwards — not even the usual `authorization` / `x-*` eligibility.
|
|
110
|
+
* - Denylist (default): base eligibility is `authorization` or any `x-*`, then
|
|
111
|
+
* the built-in denylist (when enabled) and the integrator's own
|
|
112
|
+
* names/prefixes strip from that set.
|
|
113
|
+
*
|
|
114
|
+
* Note: the built-in default denylist applies ONLY in denylist mode; an
|
|
115
|
+
* explicit `allow` is treated as the integrator deliberately opting back into
|
|
116
|
+
* those headers, so only their OWN `deny` subtracts in allowlist mode.
|
|
117
|
+
*/
|
|
118
|
+
function shouldForwardHeader(headerName, policy) {
|
|
9
119
|
const lower = headerName.toLowerCase();
|
|
10
|
-
return lower
|
|
120
|
+
if (policy.allow) return policy.allow.has(lower) && !matchesIntegratorDeny(lower, policy);
|
|
121
|
+
if (!(lower === "authorization" || lower.startsWith("x-"))) return false;
|
|
122
|
+
if (policy.useDefaultDenylist) {
|
|
123
|
+
if (DEFAULT_DENY_HEADER_NAMES.has(lower)) return false;
|
|
124
|
+
if (DEFAULT_DENY_HEADER_PREFIXES.some((prefix) => lower.startsWith(prefix))) return false;
|
|
125
|
+
}
|
|
126
|
+
if (matchesIntegratorDeny(lower, policy)) return false;
|
|
127
|
+
return true;
|
|
11
128
|
}
|
|
12
129
|
/**
|
|
13
|
-
* Extracts headers that should be forwarded from a Request object
|
|
14
|
-
*
|
|
130
|
+
* Extracts headers that should be forwarded from a Request object, applying the
|
|
131
|
+
* resolved forwarding policy. Keys are normalized to the lowercased form the
|
|
132
|
+
* `Headers` iterator yields.
|
|
15
133
|
*/
|
|
16
|
-
function extractForwardableHeaders(request) {
|
|
134
|
+
function extractForwardableHeaders(request, policy) {
|
|
17
135
|
const forwardableHeaders = {};
|
|
18
136
|
request.headers.forEach((value, key) => {
|
|
19
|
-
if (shouldForwardHeader(key)) forwardableHeaders[key] = value;
|
|
137
|
+
if (shouldForwardHeader(key, policy)) forwardableHeaders[key] = value;
|
|
20
138
|
});
|
|
21
139
|
return forwardableHeaders;
|
|
22
140
|
}
|
|
141
|
+
/**
|
|
142
|
+
* Merges forwardable inbound request headers onto the headers a server
|
|
143
|
+
* explicitly configured on an agent, letting the SERVER-CONFIGURED headers WIN
|
|
144
|
+
* on collision — a server-set service-to-service token (e.g. an IAM bearer)
|
|
145
|
+
* must never be silently overridden by a browser/edge/platform-injected inbound
|
|
146
|
+
* header (#5712).
|
|
147
|
+
*
|
|
148
|
+
* The collision check is case-insensitive: `extractForwardableHeaders`
|
|
149
|
+
* normalizes inbound keys to lowercase (`authorization`) while the server
|
|
150
|
+
* typically configures canonical casing (`Authorization`). A plain object
|
|
151
|
+
* spread would treat those as distinct keys and emit BOTH — which downstream
|
|
152
|
+
* (undici) comma-joins into a single invalid "multiple JWTs" value. So we drop
|
|
153
|
+
* any forwarded header the agent already sets, matched case-insensitively, and
|
|
154
|
+
* let non-colliding inbound headers pass through unchanged.
|
|
155
|
+
*
|
|
156
|
+
* The same comma-join hazard exists if the SERVER CONFIG ITSELF contains two
|
|
157
|
+
* case-variants of one header (e.g. both `Authorization` and `authorization`
|
|
158
|
+
* in `agent.headers`). A plain `{ ...serverHeaders }` spread would keep both,
|
|
159
|
+
* so we additionally collapse server-self case-collisions to a SINGLE entry,
|
|
160
|
+
* FIRST-OCCURRENCE WINS: the first key seen (in `Object.keys` order) keeps its
|
|
161
|
+
* exact casing and value, and any later case-variant of that name is dropped.
|
|
162
|
+
* Server-wins-over-inbound and case-insensitive inbound suppression are
|
|
163
|
+
* otherwise unchanged.
|
|
164
|
+
*
|
|
165
|
+
* Breadth (which inbound headers are eligible to forward at all) is decided by
|
|
166
|
+
* `policy` upstream in `extractForwardableHeaders` → `shouldForwardHeader`; the
|
|
167
|
+
* merge never re-widens or re-narrows the set.
|
|
168
|
+
*/
|
|
169
|
+
function mergeForwardableHeaders(serverHeaders, request, policy) {
|
|
170
|
+
const base = serverHeaders ?? {};
|
|
171
|
+
const merged = {};
|
|
172
|
+
const serverHeaderNames = /* @__PURE__ */ new Set();
|
|
173
|
+
for (const [name, value] of Object.entries(base)) {
|
|
174
|
+
const lower = name.toLowerCase();
|
|
175
|
+
if (serverHeaderNames.has(lower)) continue;
|
|
176
|
+
serverHeaderNames.add(lower);
|
|
177
|
+
merged[name] = value;
|
|
178
|
+
}
|
|
179
|
+
for (const [name, value] of Object.entries(extractForwardableHeaders(request, policy))) if (!serverHeaderNames.has(name.toLowerCase())) merged[name] = value;
|
|
180
|
+
return merged;
|
|
181
|
+
}
|
|
23
182
|
|
|
24
183
|
//#endregion
|
|
25
|
-
exports.
|
|
184
|
+
exports.mergeForwardableHeaders = mergeForwardableHeaders;
|
|
185
|
+
exports.resolveForwardHeadersPolicy = resolveForwardHeadersPolicy;
|
|
26
186
|
//# sourceMappingURL=header-utils.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"header-utils.cjs","names":[],"sources":["../../../../src/v2/runtime/handlers/header-utils.ts"],"sourcesContent":["/**\n * Determines if a header should be forwarded based on the allowlist.\n * Forwards: authorization header and all x-* custom headers.\n */\nexport function shouldForwardHeader(headerName: string): boolean {\n const lower = headerName.toLowerCase();\n return lower === \"authorization\" || lower.startsWith(\"x-\");\n}\n\n/**\n * Extracts headers that should be forwarded from a Request object.\n * Forwards only authorization and x-* headers.\n */\nexport function extractForwardableHeaders(\n request: Request,\n): Record<string, string> {\n const forwardableHeaders: Record<string, string> = {};\n request.headers.forEach((value, key) => {\n if (shouldForwardHeader(key)) {\n forwardableHeaders[key] = value;\n }\n });\n return forwardableHeaders;\n}\n"],"mappings":";;;;;;;AAIA,SAAgB,oBAAoB,YAA6B;CAC/D,MAAM,QAAQ,WAAW,aAAa;AACtC,QAAO,UAAU,mBAAmB,MAAM,WAAW,KAAK;;;;;;AAO5D,SAAgB,0BACd,SACwB;CACxB,MAAM,qBAA6C,EAAE;AACrD,SAAQ,QAAQ,SAAS,OAAO,QAAQ;AACtC,MAAI,oBAAoB,IAAI,CAC1B,oBAAmB,OAAO;GAE5B;AACF,QAAO"}
|
|
1
|
+
{"version":3,"file":"header-utils.cjs","names":[],"sources":["../../../../src/v2/runtime/handlers/header-utils.ts"],"sourcesContent":["/**\n * Exact header names (lowercased) stripped from forwarding by default.\n *\n * These are infrastructure/proxy/platform artifacts that no legitimate agent\n * integration depends on receiving *forwarded from the inbound edge* — the\n * inbound request has already traversed a browser, CDN/edge, load balancer, and\n * hosting platform, each of which stamps its own `x-*` headers. Forwarding them\n * verbatim to an arbitrary configured agent URL leaks client topology and, in\n * the Copilot Cloud case, a platform credential (#5712).\n *\n * `x-amz-cf-id` and `x-copilotcloud-public-api-key` are also covered by the\n * `x-amz-` / `x-copilotcloud-` prefixes below; the exact entries are kept\n * intentionally as documentation anchors for the highest-severity headers\n * (notably the platform API key), not as drift/oversight.\n */\nexport const DEFAULT_DENY_HEADER_NAMES: ReadonlySet<string> = new Set([\n // Hop-by-hop / proxy topology\n \"x-forwarded-for\",\n \"x-forwarded-proto\",\n \"x-forwarded-host\",\n \"x-forwarded-port\",\n \"x-forwarded-server\",\n \"x-real-ip\",\n // Cloud / CDN tracing + infra\n \"x-amzn-trace-id\",\n \"x-amz-cf-id\",\n \"x-cloud-trace-context\",\n \"x-cache\",\n \"x-served-by\",\n \"x-request-id\",\n // CopilotKit platform credentials/identifiers\n \"x-copilotcloud-public-api-key\",\n]);\n\n/**\n * Header-name prefixes (lowercased) stripped from forwarding by default.\n *\n * Prefix matching covers the well-known platform/CDN families so a new member\n * of a family (e.g. a future `x-vercel-foo`) is denied without a constant edit.\n */\nexport const DEFAULT_DENY_HEADER_PREFIXES: readonly string[] = [\n \"x-amz-\", // AWS\n \"x-azure-\", // Azure Front Door\n \"x-fastly-\", // Fastly\n \"x-vercel-\", // Vercel\n \"x-middleware-\", // Next.js\n \"x-copilotcloud-\", // CopilotKit platform-internal\n];\n\n/**\n * Fully-resolved inbound-header forwarding policy read by the call sites.\n *\n * Distinct from the public `ForwardHeadersConfig` option an integrator passes:\n * the runtime resolves that option ONCE in its constructor into this shape\n * (lowercasing names/prefixes, defaulting `useDefaultDenylist`, building the\n * `allow` set) so the predicate stays branch-simple and the policy can never be\n * re-resolved divergently at a call site. See `resolveForwardHeadersPolicy`.\n */\nexport interface ResolvedForwardHeadersPolicy {\n /** When true, the built-in infra/platform denylist is active. */\n useDefaultDenylist: boolean;\n /** Extra exact names to strip (lowercased). */\n denyNames: ReadonlySet<string>;\n /** Extra prefixes to strip (lowercased). */\n denyPrefixes: readonly string[];\n /**\n * If set, allowlist mode: ONLY these (lowercased) names are candidates to\n * forward — and `denyNames` / `denyPrefixes` still subtract from them.\n */\n allow?: ReadonlySet<string>;\n}\n\n/**\n * Public, integrator-facing config for inbound-header forwarding. Resolved into\n * a {@link ResolvedForwardHeadersPolicy} by {@link resolveForwardHeadersPolicy}.\n */\nexport interface ForwardHeadersConfig {\n /** Strip the built-in infra/platform denylist. @default true */\n useDefaultDenylist?: boolean;\n /** Additional exact header names to strip (case-insensitive). */\n deny?: string[];\n /** Additional header-name prefixes to strip (case-insensitive). */\n denyPrefixes?: string[];\n /**\n * If set (with at least one non-empty entry), switch to allowlist mode: ONLY\n * these headers are candidates to forward, overriding the default `x-*` /\n * `authorization` eligibility (case-insensitive). `deny` / `denyPrefixes`\n * still apply and subtract from this set — a header listed in both `allow` and\n * `deny` is NOT forwarded.\n *\n * Footgun: in allowlist mode the built-in DEFAULT denylist (and\n * `useDefaultDenylist`) is BYPASSED — only your `allow` set, minus your own\n * `deny` / `denyPrefixes`, is forwarded. Do NOT allow-list protected/platform\n * headers (e.g. `x-copilotcloud-public-api-key`, `x-forwarded-*`) unless you\n * truly intend to forward them, since the default protection does not apply\n * here.\n */\n allow?: string[];\n}\n\n/**\n * Normalizes a public {@link ForwardHeadersConfig} (or `undefined`) into a\n * fully-resolved {@link ResolvedForwardHeadersPolicy}.\n *\n * - `useDefaultDenylist` defaults to `true` (the built-in denylist is active\n * on upgrade); pass `false` to restore the previous wide-open behavior.\n * - `deny` / `denyPrefixes` extend (do not replace) the defaults.\n * - `allow` activates allowlist mode only when it has at least one non-empty\n * entry after normalization.\n *\n * All names/prefixes are trimmed, lowercased, and stripped of empty/\n * whitespace-only entries before use. Trimming/lowercasing keeps matching a\n * plain set/prefix check against the lowercased inbound keys; dropping empties\n * is a safety guard: a stray `denyPrefixes: [\"\"]` would make `startsWith(\"\")`\n * true for every header (silently denying ALL forwarding). Because empties are\n * dropped BEFORE the allowlist-mode decision, an `allow: [\"\"]` / `allow: [\" \"]`\n * normalizes to an empty set and does NOT switch on allowlist mode — the runtime\n * stays in denylist mode. Allowlist mode activates only when `allow` has at\n * least one non-empty entry; these empty/whitespace-only entries are integrator\n * typos, not intent, so we filter them.\n */\nfunction normalizeHeaderEntries(entries: string[] | undefined): string[] {\n return (entries ?? [])\n .map((entry) => entry.trim().toLowerCase())\n .filter((entry) => entry.length > 0);\n}\n\nexport function resolveForwardHeadersPolicy(\n config: ForwardHeadersConfig | undefined,\n): ResolvedForwardHeadersPolicy {\n const denyNames = new Set<string>(normalizeHeaderEntries(config?.deny));\n const denyPrefixes = normalizeHeaderEntries(config?.denyPrefixes);\n const allowEntries = normalizeHeaderEntries(config?.allow);\n const allow =\n allowEntries.length > 0 ? new Set<string>(allowEntries) : undefined;\n\n return {\n useDefaultDenylist: config?.useDefaultDenylist ?? true,\n denyNames,\n denyPrefixes,\n allow,\n };\n}\n\n/**\n * True iff the (already-lowercased) header name matches the integrator's OWN\n * `deny` / `denyPrefixes`. This is the authoritative subtractive check: it is\n * consulted in BOTH allowlist and denylist mode. It deliberately does NOT\n * include the built-in {@link DEFAULT_DENY_HEADER_NAMES} /\n * {@link DEFAULT_DENY_HEADER_PREFIXES} — an explicit `allow` opts the integrator\n * back into a default-denied header on purpose, so only their own `deny`\n * subtracts from an allowlist.\n */\nfunction matchesIntegratorDeny(\n lower: string,\n policy: ResolvedForwardHeadersPolicy,\n): boolean {\n if (policy.denyNames.has(lower)) return true;\n return policy.denyPrefixes.some((prefix) => lower.startsWith(prefix));\n}\n\n/**\n * Determines if a header should be forwarded under the given resolved policy.\n *\n * The integrator's `deny` / `denyPrefixes` ALWAYS strip, including in allowlist\n * mode: `allow` selects the candidate set, `deny` removes from it. A header the\n * integrator lists in BOTH `allow` and `deny` is NOT forwarded — deny is\n * authoritative so a security-motivated `deny` can never be silently defeated\n * by an overlapping `allow` (the footgun this hardens against).\n *\n * Modes:\n * - Allowlist (`policy.allow` set): forward iff the name is in `allow` AND is\n * NOT matched by the integrator's `deny` / `denyPrefixes`. Nothing else\n * forwards — not even the usual `authorization` / `x-*` eligibility.\n * - Denylist (default): base eligibility is `authorization` or any `x-*`, then\n * the built-in denylist (when enabled) and the integrator's own\n * names/prefixes strip from that set.\n *\n * Note: the built-in default denylist applies ONLY in denylist mode; an\n * explicit `allow` is treated as the integrator deliberately opting back into\n * those headers, so only their OWN `deny` subtracts in allowlist mode.\n */\nexport function shouldForwardHeader(\n headerName: string,\n policy: ResolvedForwardHeadersPolicy,\n): boolean {\n const lower = headerName.toLowerCase();\n\n // Allowlist mode: forward iff explicitly allowed AND not subtracted by the\n // integrator's own deny/denyPrefixes. Nothing else forwards — not even the\n // usual `authorization` / `x-*` eligibility.\n if (policy.allow) {\n return policy.allow.has(lower) && !matchesIntegratorDeny(lower, policy);\n }\n\n // Base eligibility (unchanged): authorization + any x-*.\n const eligible = lower === \"authorization\" || lower.startsWith(\"x-\");\n if (!eligible) return false;\n\n // Built-in denylist (default-on): strip known infra/platform headers.\n if (policy.useDefaultDenylist) {\n if (DEFAULT_DENY_HEADER_NAMES.has(lower)) return false;\n if (\n DEFAULT_DENY_HEADER_PREFIXES.some((prefix) => lower.startsWith(prefix))\n ) {\n return false;\n }\n }\n\n // Integrator-supplied additions extend the denylist regardless of the default.\n if (matchesIntegratorDeny(lower, policy)) return false;\n\n return true;\n}\n\n/**\n * Extracts headers that should be forwarded from a Request object, applying the\n * resolved forwarding policy. Keys are normalized to the lowercased form the\n * `Headers` iterator yields.\n */\nexport function extractForwardableHeaders(\n request: Request,\n policy: ResolvedForwardHeadersPolicy,\n): Record<string, string> {\n const forwardableHeaders: Record<string, string> = {};\n request.headers.forEach((value, key) => {\n if (shouldForwardHeader(key, policy)) {\n forwardableHeaders[key] = value;\n }\n });\n return forwardableHeaders;\n}\n\n/**\n * Merges forwardable inbound request headers onto the headers a server\n * explicitly configured on an agent, letting the SERVER-CONFIGURED headers WIN\n * on collision — a server-set service-to-service token (e.g. an IAM bearer)\n * must never be silently overridden by a browser/edge/platform-injected inbound\n * header (#5712).\n *\n * The collision check is case-insensitive: `extractForwardableHeaders`\n * normalizes inbound keys to lowercase (`authorization`) while the server\n * typically configures canonical casing (`Authorization`). A plain object\n * spread would treat those as distinct keys and emit BOTH — which downstream\n * (undici) comma-joins into a single invalid \"multiple JWTs\" value. So we drop\n * any forwarded header the agent already sets, matched case-insensitively, and\n * let non-colliding inbound headers pass through unchanged.\n *\n * The same comma-join hazard exists if the SERVER CONFIG ITSELF contains two\n * case-variants of one header (e.g. both `Authorization` and `authorization`\n * in `agent.headers`). A plain `{ ...serverHeaders }` spread would keep both,\n * so we additionally collapse server-self case-collisions to a SINGLE entry,\n * FIRST-OCCURRENCE WINS: the first key seen (in `Object.keys` order) keeps its\n * exact casing and value, and any later case-variant of that name is dropped.\n * Server-wins-over-inbound and case-insensitive inbound suppression are\n * otherwise unchanged.\n *\n * Breadth (which inbound headers are eligible to forward at all) is decided by\n * `policy` upstream in `extractForwardableHeaders` → `shouldForwardHeader`; the\n * merge never re-widens or re-narrows the set.\n */\nexport function mergeForwardableHeaders(\n serverHeaders: Record<string, string> | undefined,\n request: Request,\n policy: ResolvedForwardHeadersPolicy,\n): Record<string, string> {\n const base = serverHeaders ?? {};\n const merged: Record<string, string> = {};\n const serverHeaderNames = new Set<string>();\n // Collapse server-self case-collisions: first occurrence wins, later\n // case-variants of the same name are dropped.\n for (const [name, value] of Object.entries(base)) {\n const lower = name.toLowerCase();\n if (serverHeaderNames.has(lower)) {\n continue;\n }\n serverHeaderNames.add(lower);\n merged[name] = value;\n }\n for (const [name, value] of Object.entries(\n extractForwardableHeaders(request, policy),\n )) {\n if (!serverHeaderNames.has(name.toLowerCase())) {\n merged[name] = value;\n }\n }\n return merged;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAeA,MAAa,4BAAiD,IAAI,IAAI;CAEpE;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CACA;CAEA;CACD,CAAC;;;;;;;AAQF,MAAa,+BAAkD;CAC7D;CACA;CACA;CACA;CACA;CACA;CACD;;;;;;;;;;;;;;;;;;;;;;AA0ED,SAAS,uBAAuB,SAAyC;AACvE,SAAQ,WAAW,EAAE,EAClB,KAAK,UAAU,MAAM,MAAM,CAAC,aAAa,CAAC,CAC1C,QAAQ,UAAU,MAAM,SAAS,EAAE;;AAGxC,SAAgB,4BACd,QAC8B;CAC9B,MAAM,YAAY,IAAI,IAAY,uBAAuB,QAAQ,KAAK,CAAC;CACvE,MAAM,eAAe,uBAAuB,QAAQ,aAAa;CACjE,MAAM,eAAe,uBAAuB,QAAQ,MAAM;CAC1D,MAAM,QACJ,aAAa,SAAS,IAAI,IAAI,IAAY,aAAa,GAAG;AAE5D,QAAO;EACL,oBAAoB,QAAQ,sBAAsB;EAClD;EACA;EACA;EACD;;;;;;;;;;;AAYH,SAAS,sBACP,OACA,QACS;AACT,KAAI,OAAO,UAAU,IAAI,MAAM,CAAE,QAAO;AACxC,QAAO,OAAO,aAAa,MAAM,WAAW,MAAM,WAAW,OAAO,CAAC;;;;;;;;;;;;;;;;;;;;;;;AAwBvE,SAAgB,oBACd,YACA,QACS;CACT,MAAM,QAAQ,WAAW,aAAa;AAKtC,KAAI,OAAO,MACT,QAAO,OAAO,MAAM,IAAI,MAAM,IAAI,CAAC,sBAAsB,OAAO,OAAO;AAKzE,KAAI,EADa,UAAU,mBAAmB,MAAM,WAAW,KAAK,EACrD,QAAO;AAGtB,KAAI,OAAO,oBAAoB;AAC7B,MAAI,0BAA0B,IAAI,MAAM,CAAE,QAAO;AACjD,MACE,6BAA6B,MAAM,WAAW,MAAM,WAAW,OAAO,CAAC,CAEvE,QAAO;;AAKX,KAAI,sBAAsB,OAAO,OAAO,CAAE,QAAO;AAEjD,QAAO;;;;;;;AAQT,SAAgB,0BACd,SACA,QACwB;CACxB,MAAM,qBAA6C,EAAE;AACrD,SAAQ,QAAQ,SAAS,OAAO,QAAQ;AACtC,MAAI,oBAAoB,KAAK,OAAO,CAClC,oBAAmB,OAAO;GAE5B;AACF,QAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+BT,SAAgB,wBACd,eACA,SACA,QACwB;CACxB,MAAM,OAAO,iBAAiB,EAAE;CAChC,MAAM,SAAiC,EAAE;CACzC,MAAM,oCAAoB,IAAI,KAAa;AAG3C,MAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAQ,KAAK,EAAE;EAChD,MAAM,QAAQ,KAAK,aAAa;AAChC,MAAI,kBAAkB,IAAI,MAAM,CAC9B;AAEF,oBAAkB,IAAI,MAAM;AAC5B,SAAO,QAAQ;;AAEjB,MAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QACjC,0BAA0B,SAAS,OAAO,CAC3C,CACC,KAAI,CAAC,kBAAkB,IAAI,KAAK,aAAa,CAAC,CAC5C,QAAO,QAAQ;AAGnB,QAAO"}
|