@copilotkit/runtime 1.55.0-next.9 → 1.55.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/CHANGELOG.md +24 -2
- package/dist/agent/index.cjs +101 -12
- package/dist/agent/index.cjs.map +1 -1
- package/dist/agent/index.d.cts.map +1 -1
- package/dist/agent/index.d.mts.map +1 -1
- package/dist/agent/index.mjs +102 -13
- package/dist/agent/index.mjs.map +1 -1
- package/dist/lib/runtime/agent-integrations/langgraph/agent.cjs.map +1 -1
- package/dist/lib/runtime/agent-integrations/langgraph/agent.d.cts +4 -841
- package/dist/lib/runtime/agent-integrations/langgraph/agent.d.cts.map +1 -1
- package/dist/lib/runtime/agent-integrations/langgraph/agent.d.mts +4 -841
- package/dist/lib/runtime/agent-integrations/langgraph/agent.d.mts.map +1 -1
- package/dist/lib/runtime/agent-integrations/langgraph/agent.mjs.map +1 -1
- package/dist/lib/runtime/copilot-runtime.cjs +3 -2
- package/dist/lib/runtime/copilot-runtime.cjs.map +1 -1
- package/dist/lib/runtime/copilot-runtime.d.cts +1 -1
- package/dist/lib/runtime/copilot-runtime.d.cts.map +1 -1
- package/dist/lib/runtime/copilot-runtime.d.mts +3 -3
- package/dist/lib/runtime/copilot-runtime.d.mts.map +1 -1
- package/dist/lib/runtime/copilot-runtime.mjs +3 -2
- package/dist/lib/runtime/copilot-runtime.mjs.map +1 -1
- package/dist/package.cjs +70 -47
- package/dist/package.mjs +70 -47
- package/dist/v2/express.cjs +8 -0
- package/dist/v2/express.d.cts +5 -0
- package/dist/v2/express.d.mts +5 -0
- package/dist/v2/express.mjs +5 -0
- package/dist/v2/hono.cjs +9 -0
- package/dist/v2/hono.d.cts +5 -0
- package/dist/v2/hono.d.mts +5 -0
- package/dist/v2/hono.mjs +5 -0
- package/dist/v2/index.cjs +8 -3
- package/dist/v2/index.d.cts +8 -5
- package/dist/v2/index.d.mts +8 -5
- package/dist/v2/index.mjs +5 -4
- package/dist/v2/node.cjs +8 -0
- package/dist/v2/node.d.cts +5 -0
- package/dist/v2/node.d.mts +5 -0
- package/dist/v2/node.mjs +5 -0
- package/dist/v2/runtime/core/fetch-cors.cjs +72 -0
- package/dist/v2/runtime/core/fetch-cors.cjs.map +1 -0
- package/dist/v2/runtime/core/fetch-cors.d.cts +20 -0
- package/dist/v2/runtime/core/fetch-cors.d.cts.map +1 -0
- package/dist/v2/runtime/core/fetch-cors.d.mts +20 -0
- package/dist/v2/runtime/core/fetch-cors.d.mts.map +1 -0
- package/dist/v2/runtime/core/fetch-cors.mjs +70 -0
- package/dist/v2/runtime/core/fetch-cors.mjs.map +1 -0
- package/dist/v2/runtime/core/fetch-handler.cjs +232 -0
- package/dist/v2/runtime/core/fetch-handler.cjs.map +1 -0
- package/dist/v2/runtime/core/fetch-handler.d.cts +40 -0
- package/dist/v2/runtime/core/fetch-handler.d.cts.map +1 -0
- package/dist/v2/runtime/core/fetch-handler.d.mts +40 -0
- package/dist/v2/runtime/core/fetch-handler.d.mts.map +1 -0
- package/dist/v2/runtime/core/fetch-handler.mjs +231 -0
- package/dist/v2/runtime/core/fetch-handler.mjs.map +1 -0
- package/dist/v2/runtime/core/fetch-router.cjs +68 -0
- package/dist/v2/runtime/core/fetch-router.cjs.map +1 -0
- package/dist/v2/runtime/core/fetch-router.mjs +67 -0
- package/dist/v2/runtime/core/fetch-router.mjs.map +1 -0
- package/dist/v2/runtime/core/hooks.cjs +29 -0
- package/dist/v2/runtime/core/hooks.cjs.map +1 -0
- package/dist/v2/runtime/core/hooks.d.cts +78 -0
- package/dist/v2/runtime/core/hooks.d.cts.map +1 -0
- package/dist/v2/runtime/core/hooks.d.mts +78 -0
- package/dist/v2/runtime/core/hooks.d.mts.map +1 -0
- package/dist/v2/runtime/core/hooks.mjs +25 -0
- package/dist/v2/runtime/core/hooks.mjs.map +1 -0
- package/dist/v2/runtime/{middleware-sse-parser.cjs → core/middleware-sse-parser.cjs} +2 -2
- package/dist/v2/runtime/core/middleware-sse-parser.cjs.map +1 -0
- package/dist/v2/runtime/{middleware-sse-parser.d.cts → core/middleware-sse-parser.d.cts} +1 -1
- package/dist/v2/runtime/core/middleware-sse-parser.d.cts.map +1 -0
- package/dist/v2/runtime/{middleware-sse-parser.d.mts → core/middleware-sse-parser.d.mts} +1 -1
- package/dist/v2/runtime/core/middleware-sse-parser.d.mts.map +1 -0
- package/dist/v2/runtime/{middleware-sse-parser.mjs → core/middleware-sse-parser.mjs} +1 -1
- package/dist/v2/runtime/core/middleware-sse-parser.mjs.map +1 -0
- package/dist/v2/runtime/{middleware.cjs → core/middleware.cjs} +2 -2
- package/dist/v2/runtime/core/middleware.cjs.map +1 -0
- package/dist/v2/runtime/{middleware.d.cts → core/middleware.d.cts} +1 -1
- package/dist/v2/runtime/core/middleware.d.cts.map +1 -0
- package/dist/v2/runtime/{middleware.d.mts → core/middleware.d.mts} +1 -1
- package/dist/v2/runtime/core/middleware.d.mts.map +1 -0
- package/dist/v2/runtime/{middleware.mjs → core/middleware.mjs} +1 -1
- package/dist/v2/runtime/core/middleware.mjs.map +1 -0
- package/dist/v2/runtime/{runtime.cjs → core/runtime.cjs} +35 -10
- package/dist/v2/runtime/core/runtime.cjs.map +1 -0
- package/dist/v2/runtime/{runtime.d.cts → core/runtime.d.cts} +41 -7
- package/dist/v2/runtime/core/runtime.d.cts.map +1 -0
- package/dist/v2/runtime/{runtime.d.mts → core/runtime.d.mts} +42 -8
- package/dist/v2/runtime/core/runtime.d.mts.map +1 -0
- package/dist/v2/runtime/{runtime.mjs → core/runtime.mjs} +36 -11
- package/dist/v2/runtime/core/runtime.mjs.map +1 -0
- package/dist/v2/runtime/endpoints/express-fetch-bridge.cjs +83 -0
- package/dist/v2/runtime/endpoints/express-fetch-bridge.cjs.map +1 -0
- package/dist/v2/runtime/endpoints/express-fetch-bridge.mjs +82 -0
- package/dist/v2/runtime/endpoints/express-fetch-bridge.mjs.map +1 -0
- package/dist/v2/runtime/endpoints/express-single.cjs +35 -181
- package/dist/v2/runtime/endpoints/express-single.cjs.map +1 -1
- package/dist/v2/runtime/endpoints/express-single.d.cts +35 -2
- package/dist/v2/runtime/endpoints/express-single.d.cts.map +1 -1
- package/dist/v2/runtime/endpoints/express-single.d.mts +35 -2
- package/dist/v2/runtime/endpoints/express-single.d.mts.map +1 -1
- package/dist/v2/runtime/endpoints/express-single.mjs +35 -178
- package/dist/v2/runtime/endpoints/express-single.mjs.map +1 -1
- package/dist/v2/runtime/endpoints/express.cjs +41 -195
- package/dist/v2/runtime/endpoints/express.cjs.map +1 -1
- package/dist/v2/runtime/endpoints/express.d.cts +26 -4
- package/dist/v2/runtime/endpoints/express.d.cts.map +1 -1
- package/dist/v2/runtime/endpoints/express.d.mts +26 -4
- package/dist/v2/runtime/endpoints/express.d.mts.map +1 -1
- package/dist/v2/runtime/endpoints/express.mjs +41 -195
- package/dist/v2/runtime/endpoints/express.mjs.map +1 -1
- package/dist/v2/runtime/endpoints/hono-single.cjs +11 -123
- package/dist/v2/runtime/endpoints/hono-single.cjs.map +1 -1
- package/dist/v2/runtime/endpoints/hono-single.d.cts +14 -11
- package/dist/v2/runtime/endpoints/hono-single.d.cts.map +1 -1
- package/dist/v2/runtime/endpoints/hono-single.d.mts +14 -11
- package/dist/v2/runtime/endpoints/hono-single.d.mts.map +1 -1
- package/dist/v2/runtime/endpoints/hono-single.mjs +11 -123
- package/dist/v2/runtime/endpoints/hono-single.mjs.map +1 -1
- package/dist/v2/runtime/endpoints/hono.cjs +23 -237
- package/dist/v2/runtime/endpoints/hono.cjs.map +1 -1
- package/dist/v2/runtime/endpoints/hono.d.cts +29 -120
- package/dist/v2/runtime/endpoints/hono.d.cts.map +1 -1
- package/dist/v2/runtime/endpoints/hono.d.mts +29 -120
- package/dist/v2/runtime/endpoints/hono.d.mts.map +1 -1
- package/dist/v2/runtime/endpoints/hono.mjs +22 -238
- package/dist/v2/runtime/endpoints/hono.mjs.map +1 -1
- package/dist/v2/runtime/endpoints/index.d.cts +2 -2
- package/dist/v2/runtime/endpoints/index.d.mts +2 -2
- package/dist/v2/runtime/endpoints/node-fetch-handler.cjs +26 -0
- package/dist/v2/runtime/endpoints/node-fetch-handler.cjs.map +1 -0
- package/dist/v2/runtime/endpoints/node-fetch-handler.d.cts +12 -0
- package/dist/v2/runtime/endpoints/node-fetch-handler.d.cts.map +1 -0
- package/dist/v2/runtime/endpoints/node-fetch-handler.d.mts +12 -0
- package/dist/v2/runtime/endpoints/node-fetch-handler.d.mts.map +1 -0
- package/dist/v2/runtime/endpoints/node-fetch-handler.mjs +24 -0
- package/dist/v2/runtime/endpoints/node-fetch-handler.mjs.map +1 -0
- package/dist/v2/runtime/endpoints/node.cjs +30 -0
- package/dist/v2/runtime/endpoints/node.cjs.map +1 -0
- package/dist/v2/runtime/endpoints/node.d.cts +27 -0
- package/dist/v2/runtime/endpoints/node.d.cts.map +1 -0
- package/dist/v2/runtime/endpoints/node.d.mts +27 -0
- package/dist/v2/runtime/endpoints/node.d.mts.map +1 -0
- package/dist/v2/runtime/endpoints/node.mjs +30 -0
- package/dist/v2/runtime/endpoints/node.mjs.map +1 -0
- package/dist/v2/runtime/express.d.cts +3 -0
- package/dist/v2/runtime/express.d.mts +3 -0
- package/dist/v2/runtime/handlers/get-runtime-info.cjs +2 -1
- package/dist/v2/runtime/handlers/get-runtime-info.cjs.map +1 -1
- package/dist/v2/runtime/handlers/get-runtime-info.mjs +2 -1
- package/dist/v2/runtime/handlers/get-runtime-info.mjs.map +1 -1
- package/dist/v2/runtime/handlers/handle-connect.cjs +6 -3
- package/dist/v2/runtime/handlers/handle-connect.cjs.map +1 -1
- package/dist/v2/runtime/handlers/handle-connect.mjs +6 -3
- package/dist/v2/runtime/handlers/handle-connect.mjs.map +1 -1
- package/dist/v2/runtime/handlers/handle-run.cjs +6 -3
- package/dist/v2/runtime/handlers/handle-run.cjs.map +1 -1
- package/dist/v2/runtime/handlers/handle-run.mjs +6 -3
- package/dist/v2/runtime/handlers/handle-run.mjs.map +1 -1
- package/dist/v2/runtime/handlers/handle-stop.cjs.map +1 -1
- package/dist/v2/runtime/handlers/handle-stop.mjs.map +1 -1
- package/dist/v2/runtime/handlers/handle-transcribe.cjs.map +1 -1
- package/dist/v2/runtime/handlers/handle-transcribe.mjs.map +1 -1
- package/dist/v2/runtime/handlers/intelligence/connect.cjs.map +1 -1
- package/dist/v2/runtime/handlers/intelligence/connect.mjs.map +1 -1
- package/dist/v2/runtime/handlers/intelligence/run.cjs +22 -1
- package/dist/v2/runtime/handlers/intelligence/run.cjs.map +1 -1
- package/dist/v2/runtime/handlers/intelligence/run.mjs +22 -1
- package/dist/v2/runtime/handlers/intelligence/run.mjs.map +1 -1
- package/dist/v2/runtime/handlers/intelligence/thread-names.cjs +1 -1
- package/dist/v2/runtime/handlers/intelligence/thread-names.cjs.map +1 -1
- package/dist/v2/runtime/handlers/intelligence/thread-names.mjs +1 -1
- package/dist/v2/runtime/handlers/intelligence/thread-names.mjs.map +1 -1
- package/dist/v2/runtime/handlers/shared/agent-utils.cjs +21 -6
- package/dist/v2/runtime/handlers/shared/agent-utils.cjs.map +1 -1
- package/dist/v2/runtime/handlers/shared/agent-utils.mjs +21 -6
- package/dist/v2/runtime/handlers/shared/agent-utils.mjs.map +1 -1
- package/dist/v2/runtime/handlers/shared/json-response.cjs +4 -1
- package/dist/v2/runtime/handlers/shared/json-response.cjs.map +1 -1
- package/dist/v2/runtime/handlers/shared/json-response.mjs +4 -1
- package/dist/v2/runtime/handlers/shared/json-response.mjs.map +1 -1
- package/dist/v2/runtime/handlers/shared/resolve-intelligence-user.cjs.map +1 -1
- package/dist/v2/runtime/handlers/shared/resolve-intelligence-user.mjs.map +1 -1
- package/dist/v2/runtime/handlers/sse/connect.cjs.map +1 -1
- package/dist/v2/runtime/handlers/sse/connect.mjs.map +1 -1
- package/dist/v2/runtime/handlers/sse/run.cjs.map +1 -1
- package/dist/v2/runtime/handlers/sse/run.mjs.map +1 -1
- package/dist/v2/runtime/hono.d.cts +3 -0
- package/dist/v2/runtime/hono.d.mts +3 -0
- package/dist/v2/runtime/index.d.cts +16 -4
- package/dist/v2/runtime/index.d.cts.map +1 -0
- package/dist/v2/runtime/index.d.mts +16 -4
- package/dist/v2/runtime/index.d.mts.map +1 -0
- package/dist/v2/runtime/intelligence-platform/client.cjs +10 -1
- package/dist/v2/runtime/intelligence-platform/client.cjs.map +1 -1
- package/dist/v2/runtime/intelligence-platform/client.d.cts +22 -0
- package/dist/v2/runtime/intelligence-platform/client.d.cts.map +1 -1
- package/dist/v2/runtime/intelligence-platform/client.d.mts +22 -0
- package/dist/v2/runtime/intelligence-platform/client.d.mts.map +1 -1
- package/dist/v2/runtime/intelligence-platform/client.mjs +10 -1
- package/dist/v2/runtime/intelligence-platform/client.mjs.map +1 -1
- package/dist/v2/runtime/node.d.cts +3 -0
- package/dist/v2/runtime/node.d.mts +3 -0
- package/dist/v2/runtime/open-generative-ui-middleware.cjs +282 -0
- package/dist/v2/runtime/open-generative-ui-middleware.cjs.map +1 -0
- package/dist/v2/runtime/open-generative-ui-middleware.mjs +280 -0
- package/dist/v2/runtime/open-generative-ui-middleware.mjs.map +1 -0
- package/dist/v2/runtime/runner/intelligence.cjs +4 -4
- package/dist/v2/runtime/runner/intelligence.cjs.map +1 -1
- package/dist/v2/runtime/runner/intelligence.d.cts +6 -2
- package/dist/v2/runtime/runner/intelligence.d.cts.map +1 -1
- package/dist/v2/runtime/runner/intelligence.d.mts +6 -2
- package/dist/v2/runtime/runner/intelligence.d.mts.map +1 -1
- package/dist/v2/runtime/runner/intelligence.mjs +4 -4
- package/dist/v2/runtime/runner/intelligence.mjs.map +1 -1
- package/dist/v2/runtime/telemetry/telemetry-client.cjs +37 -0
- package/dist/v2/runtime/telemetry/telemetry-client.cjs.map +1 -1
- package/dist/v2/runtime/telemetry/telemetry-client.mjs +36 -0
- package/dist/v2/runtime/telemetry/telemetry-client.mjs.map +1 -1
- package/dist/v2/runtime/telemetry/utils.cjs +15 -0
- package/dist/v2/runtime/telemetry/utils.cjs.map +1 -0
- package/dist/v2/runtime/telemetry/utils.mjs +14 -0
- package/dist/v2/runtime/telemetry/utils.mjs.map +1 -0
- package/package.json +81 -48
- package/src/agent/__tests__/multimodal.test.ts +176 -0
- package/src/agent/index.ts +130 -19
- package/src/lib/runtime/agent-integrations/langgraph/agent.ts +3 -3
- package/src/lib/runtime/copilot-runtime.ts +1 -0
- package/src/v2/express.ts +1 -0
- package/src/v2/hono.ts +1 -0
- package/src/v2/node.ts +1 -0
- package/src/v2/runtime/__tests__/backward-compat.test.ts +261 -0
- package/src/v2/runtime/__tests__/code-review-fixes.test.ts +500 -0
- package/src/v2/runtime/__tests__/cors-credentials.test.ts +2 -2
- package/src/v2/runtime/__tests__/express-adapter.test.ts +188 -0
- package/src/v2/runtime/__tests__/express-body-order.test.ts +1 -1
- package/src/v2/runtime/__tests__/express-fetch-bridge.test.ts +344 -0
- package/src/v2/runtime/__tests__/express-single-sse.test.ts +1 -1
- package/src/v2/runtime/__tests__/fetch-cors.test.ts +205 -0
- package/src/v2/runtime/__tests__/fetch-handler-validation.test.ts +372 -0
- package/src/v2/runtime/__tests__/fetch-handler.test.ts +456 -0
- package/src/v2/runtime/__tests__/fetch-router.test.ts +132 -0
- package/src/v2/runtime/__tests__/get-runtime-info.test.ts +4 -1
- package/src/v2/runtime/__tests__/handle-connect.test.ts +15 -13
- package/src/v2/runtime/__tests__/handle-run.test.ts +21 -17
- package/src/v2/runtime/__tests__/handle-threads.test.ts +1 -1
- package/src/v2/runtime/__tests__/handle-transcribe.test.ts +1 -1
- package/src/v2/runtime/__tests__/hono-adapter.test.ts +150 -0
- package/src/v2/runtime/__tests__/hooks-edge-cases.test.ts +457 -0
- package/src/v2/runtime/__tests__/hooks.test.ts +557 -0
- package/src/v2/runtime/__tests__/integration/bun/bun-servers.integration.test.ts +27 -0
- package/src/v2/runtime/__tests__/integration/bun/elysia-multi.ts +32 -0
- package/src/v2/runtime/__tests__/integration/bun/elysia-single.ts +33 -0
- package/src/v2/runtime/__tests__/integration/bun/hono-bun-multi.ts +25 -0
- package/src/v2/runtime/__tests__/integration/bun/hono-bun-single.ts +32 -0
- package/src/v2/runtime/__tests__/integration/helpers/create-test-runtime.ts +15 -0
- package/src/v2/runtime/__tests__/integration/helpers/sse-reader.ts +45 -0
- package/src/v2/runtime/__tests__/integration/helpers/test-agent.ts +58 -0
- package/src/v2/runtime/__tests__/integration/node-servers.integration.test.ts +39 -0
- package/src/v2/runtime/__tests__/integration/servers/express-multi.ts +35 -0
- package/src/v2/runtime/__tests__/integration/servers/express-single.ts +36 -0
- package/src/v2/runtime/__tests__/integration/servers/fetch-direct.ts +39 -0
- package/src/v2/runtime/__tests__/integration/servers/hono-multi.ts +30 -0
- package/src/v2/runtime/__tests__/integration/servers/hono-single.ts +37 -0
- package/src/v2/runtime/__tests__/integration/servers/node-multi.ts +45 -0
- package/src/v2/runtime/__tests__/integration/servers/node-single.ts +46 -0
- package/src/v2/runtime/__tests__/integration/servers/types.ts +18 -0
- package/src/v2/runtime/__tests__/integration/suites/multi-endpoint.suite.ts +358 -0
- package/src/v2/runtime/__tests__/integration/suites/single-endpoint.suite.ts +363 -0
- package/src/v2/runtime/__tests__/middleware-express.test.ts +1 -1
- package/src/v2/runtime/__tests__/middleware-single-express.test.ts +1 -1
- package/src/v2/runtime/__tests__/middleware-single.test.ts +1 -1
- package/src/v2/runtime/__tests__/middleware-sse-parser.test.ts +1 -1
- package/src/v2/runtime/__tests__/middleware.test.ts +1 -2
- package/src/v2/runtime/__tests__/node-fetch-handler.test.ts +157 -0
- package/src/v2/runtime/__tests__/open-generative-ui-middleware.e2e.test.ts +728 -0
- package/src/v2/runtime/__tests__/router-edge-cases.test.ts +217 -0
- package/src/v2/runtime/__tests__/routing-express.test.ts +1 -1
- package/src/v2/runtime/__tests__/routing-single-express.test.ts +1 -1
- package/src/v2/runtime/__tests__/routing-single.test.ts +1 -1
- package/src/v2/runtime/__tests__/routing.test.ts +1 -1
- package/src/v2/runtime/__tests__/runtime.test.ts +110 -1
- package/src/v2/runtime/__tests__/telemetry.test.ts +62 -1
- package/src/v2/runtime/core/fetch-cors.ts +136 -0
- package/src/v2/runtime/core/fetch-handler.ts +415 -0
- package/src/v2/runtime/core/fetch-router.ts +112 -0
- package/src/v2/runtime/core/hooks.ts +151 -0
- package/src/v2/runtime/{runtime.ts → core/runtime.ts} +79 -10
- package/src/v2/runtime/endpoints/express-fetch-bridge.ts +137 -0
- package/src/v2/runtime/endpoints/express-single.ts +42 -219
- package/src/v2/runtime/endpoints/express.ts +128 -230
- package/src/v2/runtime/endpoints/hono-single.ts +19 -171
- package/src/v2/runtime/endpoints/hono.ts +45 -270
- package/src/v2/runtime/endpoints/node-fetch-handler.ts +48 -0
- package/src/v2/runtime/endpoints/node.ts +28 -0
- package/src/v2/runtime/handlers/get-runtime-info.ts +3 -2
- package/src/v2/runtime/handlers/handle-connect.ts +7 -4
- package/src/v2/runtime/handlers/handle-run.ts +7 -4
- package/src/v2/runtime/handlers/handle-stop.ts +1 -1
- package/src/v2/runtime/handlers/handle-transcribe.ts +1 -1
- package/src/v2/runtime/handlers/intelligence/connect.ts +1 -1
- package/src/v2/runtime/handlers/intelligence/run.ts +31 -1
- package/src/v2/runtime/handlers/intelligence/thread-names.ts +2 -2
- package/src/v2/runtime/handlers/intelligence/threads.ts +1 -1
- package/src/v2/runtime/handlers/shared/agent-utils.ts +29 -10
- package/src/v2/runtime/handlers/shared/json-response.ts +4 -1
- package/src/v2/runtime/handlers/shared/resolve-intelligence-user.ts +1 -1
- package/src/v2/runtime/handlers/sse/connect.ts +1 -1
- package/src/v2/runtime/handlers/sse/run.ts +1 -1
- package/src/v2/runtime/hono.ts +2 -0
- package/src/v2/runtime/index.ts +27 -1
- package/src/v2/runtime/intelligence-platform/client.ts +50 -1
- package/src/v2/runtime/node.ts +6 -0
- package/src/v2/runtime/open-generative-ui-middleware.ts +373 -0
- package/src/v2/runtime/runner/intelligence.ts +14 -4
- package/src/v2/runtime/telemetry/telemetry-client.ts +56 -0
- package/src/v2/runtime/telemetry/utils.ts +15 -0
- package/tsdown.config.ts +8 -1
- package/vitest.config.mjs +2 -5
- package/.eslintrc.js +0 -7
- package/dist/v2/runtime/endpoints/express-utils.cjs +0 -119
- package/dist/v2/runtime/endpoints/express-utils.cjs.map +0 -1
- package/dist/v2/runtime/endpoints/express-utils.mjs +0 -117
- package/dist/v2/runtime/endpoints/express-utils.mjs.map +0 -1
- package/dist/v2/runtime/handlers/intelligence/threads.cjs +0 -159
- package/dist/v2/runtime/handlers/intelligence/threads.cjs.map +0 -1
- package/dist/v2/runtime/handlers/intelligence/threads.mjs +0 -154
- package/dist/v2/runtime/handlers/intelligence/threads.mjs.map +0 -1
- package/dist/v2/runtime/middleware-sse-parser.cjs.map +0 -1
- package/dist/v2/runtime/middleware-sse-parser.d.cts.map +0 -1
- package/dist/v2/runtime/middleware-sse-parser.d.mts.map +0 -1
- package/dist/v2/runtime/middleware-sse-parser.mjs.map +0 -1
- package/dist/v2/runtime/middleware.cjs.map +0 -1
- package/dist/v2/runtime/middleware.d.cts.map +0 -1
- package/dist/v2/runtime/middleware.d.mts.map +0 -1
- package/dist/v2/runtime/middleware.mjs.map +0 -1
- package/dist/v2/runtime/runtime.cjs.map +0 -1
- package/dist/v2/runtime/runtime.d.cts.map +0 -1
- package/dist/v2/runtime/runtime.d.mts.map +0 -1
- package/dist/v2/runtime/runtime.mjs.map +0 -1
- package/src/v2/runtime/__tests__/express-abort-signal.test.ts +0 -25
- package/src/v2/runtime/endpoints/express-utils.ts +0 -182
- package/src/v2/runtime/handler.ts +0 -3
- /package/src/v2/runtime/{middleware-sse-parser.ts → core/middleware-sse-parser.ts} +0 -0
- /package/src/v2/runtime/{middleware.ts → core/middleware.ts} +0 -0
|
@@ -0,0 +1,500 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Regression tests for code review fixes.
|
|
3
|
+
* Each describe block maps to a specific review item.
|
|
4
|
+
*/
|
|
5
|
+
import { describe, it, expect, vi } from "vitest";
|
|
6
|
+
import { createCopilotRuntimeHandler } from "../core/fetch-handler";
|
|
7
|
+
import { CopilotRuntime } from "../core/runtime";
|
|
8
|
+
import { matchRoute } from "../core/fetch-router";
|
|
9
|
+
import { handleCors, addCorsHeaders } from "../core/fetch-cors";
|
|
10
|
+
import type { CopilotCorsConfig } from "../core/fetch-cors";
|
|
11
|
+
import type { AbstractAgent } from "@ag-ui/client";
|
|
12
|
+
|
|
13
|
+
/* ------------------------------------------------------------------------------------------------
|
|
14
|
+
* Helpers
|
|
15
|
+
* --------------------------------------------------------------------------------------------- */
|
|
16
|
+
|
|
17
|
+
const createMockAgent = () => {
|
|
18
|
+
const agent: unknown = {
|
|
19
|
+
execute: vi.fn().mockResolvedValue({ events: [] }),
|
|
20
|
+
};
|
|
21
|
+
(agent as { clone: () => unknown }).clone = () => createMockAgent();
|
|
22
|
+
return agent as AbstractAgent;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const createRuntime = () =>
|
|
26
|
+
new CopilotRuntime({ agents: { default: createMockAgent() } });
|
|
27
|
+
|
|
28
|
+
const get = (url: string) => new Request(url, { method: "GET" });
|
|
29
|
+
|
|
30
|
+
const post = (url: string, body?: unknown) =>
|
|
31
|
+
new Request(url, {
|
|
32
|
+
method: "POST",
|
|
33
|
+
headers: { "Content-Type": "application/json" },
|
|
34
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
/* ------------------------------------------------------------------------------------------------
|
|
38
|
+
* Item 1: __cpk_methodCall mutation lost if hooks replace the request
|
|
39
|
+
*
|
|
40
|
+
* In single-route mode, the parsed method call must survive even if
|
|
41
|
+
* onBeforeHandler returns a new Request object.
|
|
42
|
+
* --------------------------------------------------------------------------------------------- */
|
|
43
|
+
|
|
44
|
+
describe("Item 1: methodCall preserved when hooks replace request", () => {
|
|
45
|
+
it("single-route dispatch works after onBeforeHandler replaces request", async () => {
|
|
46
|
+
const handler = createCopilotRuntimeHandler({
|
|
47
|
+
runtime: createRuntime(),
|
|
48
|
+
basePath: "/api",
|
|
49
|
+
mode: "single-route",
|
|
50
|
+
hooks: {
|
|
51
|
+
onBeforeHandler: ({ request }) => {
|
|
52
|
+
// Return a brand-new Request with only headers modified.
|
|
53
|
+
// This discards any properties stashed on the old request object.
|
|
54
|
+
return new Request(request, {
|
|
55
|
+
headers: new Headers([...request.headers, ["x-replaced", "true"]]),
|
|
56
|
+
});
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
const response = await handler(
|
|
62
|
+
post("http://localhost/api", { method: "info" }),
|
|
63
|
+
);
|
|
64
|
+
// Should succeed (200) rather than crash with undefined methodCall
|
|
65
|
+
expect(response.status).toBe(200);
|
|
66
|
+
const body = await response.json();
|
|
67
|
+
expect(body).toHaveProperty("version");
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it("single-route agent/run works after onBeforeHandler replaces request", async () => {
|
|
71
|
+
const handler = createCopilotRuntimeHandler({
|
|
72
|
+
runtime: createRuntime(),
|
|
73
|
+
basePath: "/api",
|
|
74
|
+
mode: "single-route",
|
|
75
|
+
hooks: {
|
|
76
|
+
onBeforeHandler: ({ request }) => {
|
|
77
|
+
return new Request(request.url, {
|
|
78
|
+
method: request.method,
|
|
79
|
+
headers: request.headers,
|
|
80
|
+
body: request.clone().body,
|
|
81
|
+
});
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
const response = await handler(
|
|
87
|
+
post("http://localhost/api", {
|
|
88
|
+
method: "agent/run",
|
|
89
|
+
params: { agentId: "default" },
|
|
90
|
+
body: { threadId: "t1", runId: "r1" },
|
|
91
|
+
}),
|
|
92
|
+
);
|
|
93
|
+
// Should not crash — the method call data is passed explicitly, not via request property
|
|
94
|
+
expect(response).toBeInstanceOf(Response);
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
/* ------------------------------------------------------------------------------------------------
|
|
99
|
+
* Item 2: credentials: true + wildcard origin silently produces invalid CORS
|
|
100
|
+
*
|
|
101
|
+
* When credentials is true and origin resolves to "*", we must auto-resolve
|
|
102
|
+
* to the request origin to comply with the Fetch spec.
|
|
103
|
+
* --------------------------------------------------------------------------------------------- */
|
|
104
|
+
|
|
105
|
+
describe("Item 2: credentials + wildcard CORS", () => {
|
|
106
|
+
it("auto-resolves wildcard to request origin when credentials enabled (preflight)", () => {
|
|
107
|
+
const request = new Request("http://localhost/api", {
|
|
108
|
+
method: "OPTIONS",
|
|
109
|
+
headers: { Origin: "https://app.example.com" },
|
|
110
|
+
});
|
|
111
|
+
const config: CopilotCorsConfig = { credentials: true };
|
|
112
|
+
const response = handleCors(request, config)!;
|
|
113
|
+
|
|
114
|
+
expect(response.headers.get("Access-Control-Allow-Origin")).toBe(
|
|
115
|
+
"https://app.example.com",
|
|
116
|
+
);
|
|
117
|
+
expect(response.headers.get("Access-Control-Allow-Credentials")).toBe(
|
|
118
|
+
"true",
|
|
119
|
+
);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it("auto-resolves wildcard to request origin in addCorsHeaders", () => {
|
|
123
|
+
const response = new Response("ok", { status: 200 });
|
|
124
|
+
const config: CopilotCorsConfig = { credentials: true };
|
|
125
|
+
const result = addCorsHeaders(response, config, "https://mysite.com");
|
|
126
|
+
|
|
127
|
+
expect(result.headers.get("Access-Control-Allow-Origin")).toBe(
|
|
128
|
+
"https://mysite.com",
|
|
129
|
+
);
|
|
130
|
+
expect(result.headers.get("Access-Control-Allow-Credentials")).toBe("true");
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it("skips CORS entirely when credentials enabled, wildcard origin, and no request origin", () => {
|
|
134
|
+
const request = new Request("http://localhost/api", {
|
|
135
|
+
method: "OPTIONS",
|
|
136
|
+
// No Origin header
|
|
137
|
+
});
|
|
138
|
+
const config: CopilotCorsConfig = { credentials: true };
|
|
139
|
+
const response = handleCors(request, config)!;
|
|
140
|
+
|
|
141
|
+
// Should not set any CORS origin since there's no request origin to reflect
|
|
142
|
+
expect(response.headers.get("Access-Control-Allow-Origin")).toBeNull();
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it("explicit origin string + credentials does not trigger auto-resolution", () => {
|
|
146
|
+
const request = new Request("http://localhost/api", {
|
|
147
|
+
method: "OPTIONS",
|
|
148
|
+
headers: { Origin: "https://app.example.com" },
|
|
149
|
+
});
|
|
150
|
+
const config: CopilotCorsConfig = {
|
|
151
|
+
origin: "https://specific.example.com",
|
|
152
|
+
credentials: true,
|
|
153
|
+
};
|
|
154
|
+
const response = handleCors(request, config)!;
|
|
155
|
+
|
|
156
|
+
expect(response.headers.get("Access-Control-Allow-Origin")).toBe(
|
|
157
|
+
"https://specific.example.com",
|
|
158
|
+
);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it("end-to-end: handler CORS with credentials does not produce wildcard + credentials", async () => {
|
|
162
|
+
const handler = createCopilotRuntimeHandler({
|
|
163
|
+
runtime: createRuntime(),
|
|
164
|
+
basePath: "/api",
|
|
165
|
+
cors: { credentials: true },
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
const response = await handler(
|
|
169
|
+
new Request("http://localhost/api/info", {
|
|
170
|
+
method: "GET",
|
|
171
|
+
headers: { Origin: "https://app.example.com" },
|
|
172
|
+
}),
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
const allowOrigin = response.headers.get("Access-Control-Allow-Origin");
|
|
176
|
+
const allowCreds = response.headers.get("Access-Control-Allow-Credentials");
|
|
177
|
+
|
|
178
|
+
// Must NEVER be "*" + "true" — that's the invalid combo
|
|
179
|
+
if (allowCreds === "true") {
|
|
180
|
+
expect(allowOrigin).not.toBe("*");
|
|
181
|
+
}
|
|
182
|
+
expect(allowOrigin).toBe("https://app.example.com");
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
/* ------------------------------------------------------------------------------------------------
|
|
187
|
+
* Item 3: runOnError can throw unhandled
|
|
188
|
+
*
|
|
189
|
+
* If the onError hook itself throws, the handler must catch it and return
|
|
190
|
+
* a 500 response rather than letting the promise reject.
|
|
191
|
+
* --------------------------------------------------------------------------------------------- */
|
|
192
|
+
|
|
193
|
+
describe("Item 3: onError hook throwing is caught", () => {
|
|
194
|
+
it("returns 500 when onError throws synchronously", async () => {
|
|
195
|
+
const handler = createCopilotRuntimeHandler({
|
|
196
|
+
runtime: createRuntime(),
|
|
197
|
+
basePath: "/api",
|
|
198
|
+
hooks: {
|
|
199
|
+
onRequest: () => {
|
|
200
|
+
throw new Error("trigger error");
|
|
201
|
+
},
|
|
202
|
+
onError: () => {
|
|
203
|
+
throw new Error("hook exploded");
|
|
204
|
+
},
|
|
205
|
+
},
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
// Must not reject — should return a Response
|
|
209
|
+
const response = await handler(get("http://localhost/api/info"));
|
|
210
|
+
expect(response.status).toBe(500);
|
|
211
|
+
const body = await response.json();
|
|
212
|
+
expect(body.error).toBe("internal_error");
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
it("returns 500 when onError throws asynchronously", async () => {
|
|
216
|
+
const handler = createCopilotRuntimeHandler({
|
|
217
|
+
runtime: createRuntime(),
|
|
218
|
+
basePath: "/api",
|
|
219
|
+
hooks: {
|
|
220
|
+
onRequest: () => {
|
|
221
|
+
throw new Error("trigger error");
|
|
222
|
+
},
|
|
223
|
+
onError: async () => {
|
|
224
|
+
await Promise.resolve();
|
|
225
|
+
throw new Error("async hook exploded");
|
|
226
|
+
},
|
|
227
|
+
},
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
const response = await handler(get("http://localhost/api/info"));
|
|
231
|
+
expect(response.status).toBe(500);
|
|
232
|
+
});
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
/* ------------------------------------------------------------------------------------------------
|
|
236
|
+
* Item 4: decodeURIComponent can throw URIError on malformed URLs
|
|
237
|
+
*
|
|
238
|
+
* Malformed percent-encoding in path segments must not crash with an
|
|
239
|
+
* uncaught URIError; the router should return null (no match).
|
|
240
|
+
* --------------------------------------------------------------------------------------------- */
|
|
241
|
+
|
|
242
|
+
describe("Item 4: malformed URI encoding handled gracefully", () => {
|
|
243
|
+
it("returns null for malformed agentId in /agent/:agentId/run", () => {
|
|
244
|
+
const result = matchRoute("/api/agent/%ZZ/run", "/api");
|
|
245
|
+
expect(result).toBeNull();
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
it("returns null for malformed agentId in /agent/:agentId/connect", () => {
|
|
249
|
+
const result = matchRoute("/api/agent/%ZZ/connect", "/api");
|
|
250
|
+
expect(result).toBeNull();
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
it("returns null for malformed agentId in /agent/:agentId/stop/:threadId", () => {
|
|
254
|
+
const result = matchRoute("/api/agent/%ZZ/stop/valid-thread", "/api");
|
|
255
|
+
expect(result).toBeNull();
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
it("returns null for malformed threadId in /agent/:agentId/stop/:threadId", () => {
|
|
259
|
+
const result = matchRoute("/api/agent/valid-agent/stop/%ZZ", "/api");
|
|
260
|
+
expect(result).toBeNull();
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
it("still decodes valid percent-encoding correctly", () => {
|
|
264
|
+
const result = matchRoute("/api/agent/hello%20world/run", "/api");
|
|
265
|
+
expect(result).toEqual({ method: "agent/run", agentId: "hello world" });
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
it("end-to-end: malformed URL returns 404", async () => {
|
|
269
|
+
const handler = createCopilotRuntimeHandler({
|
|
270
|
+
runtime: createRuntime(),
|
|
271
|
+
basePath: "/api",
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
const response = await handler(
|
|
275
|
+
post("http://localhost/api/agent/%ZZ/run", { threadId: "t1" }),
|
|
276
|
+
);
|
|
277
|
+
expect(response.status).toBe(404);
|
|
278
|
+
});
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
/* ------------------------------------------------------------------------------------------------
|
|
282
|
+
* Item 8: Missing Allow header on 405 responses
|
|
283
|
+
*
|
|
284
|
+
* RFC 9110 §15.5.6 requires 405 responses to include an Allow header.
|
|
285
|
+
* --------------------------------------------------------------------------------------------- */
|
|
286
|
+
|
|
287
|
+
describe("Item 8: Allow header on 405 responses", () => {
|
|
288
|
+
it("multi-route: 405 for POST to /info includes Allow: GET", async () => {
|
|
289
|
+
const handler = createCopilotRuntimeHandler({
|
|
290
|
+
runtime: createRuntime(),
|
|
291
|
+
basePath: "/api",
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
const response = await handler(post("http://localhost/api/info"));
|
|
295
|
+
expect(response.status).toBe(405);
|
|
296
|
+
expect(response.headers.get("Allow")).toBe("GET");
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
it("multi-route: 405 for GET to /agent/:id/run includes Allow: POST", async () => {
|
|
300
|
+
const handler = createCopilotRuntimeHandler({
|
|
301
|
+
runtime: createRuntime(),
|
|
302
|
+
basePath: "/api",
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
const response = await handler(
|
|
306
|
+
get("http://localhost/api/agent/default/run"),
|
|
307
|
+
);
|
|
308
|
+
expect(response.status).toBe(405);
|
|
309
|
+
expect(response.headers.get("Allow")).toBe("POST");
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
it("single-route: 405 for GET includes Allow: POST", async () => {
|
|
313
|
+
const handler = createCopilotRuntimeHandler({
|
|
314
|
+
runtime: createRuntime(),
|
|
315
|
+
basePath: "/api",
|
|
316
|
+
mode: "single-route",
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
const response = await handler(get("http://localhost/api"));
|
|
320
|
+
expect(response.status).toBe(405);
|
|
321
|
+
expect(response.headers.get("Allow")).toBe("POST");
|
|
322
|
+
});
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
/* ------------------------------------------------------------------------------------------------
|
|
326
|
+
* Item 9: Error messages not leaked to clients in 500 responses
|
|
327
|
+
*
|
|
328
|
+
* The 500 response body should contain { error: "internal_error" }
|
|
329
|
+
* without exposing error.message which could contain file paths, DB errors, etc.
|
|
330
|
+
* --------------------------------------------------------------------------------------------- */
|
|
331
|
+
|
|
332
|
+
describe("Item 9: error messages not leaked to clients", () => {
|
|
333
|
+
it("500 response does not include error message", async () => {
|
|
334
|
+
const handler = createCopilotRuntimeHandler({
|
|
335
|
+
runtime: createRuntime(),
|
|
336
|
+
basePath: "/api",
|
|
337
|
+
hooks: {
|
|
338
|
+
onRequest: () => {
|
|
339
|
+
throw new Error("secret: /etc/passwd contents here");
|
|
340
|
+
},
|
|
341
|
+
},
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
const response = await handler(get("http://localhost/api/info"));
|
|
345
|
+
expect(response.status).toBe(500);
|
|
346
|
+
const body = await response.json();
|
|
347
|
+
expect(body.error).toBe("internal_error");
|
|
348
|
+
expect(body.message).toBeUndefined();
|
|
349
|
+
expect(JSON.stringify(body)).not.toContain("passwd");
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
it("non-Error throws also produce clean 500", async () => {
|
|
353
|
+
const handler = createCopilotRuntimeHandler({
|
|
354
|
+
runtime: createRuntime(),
|
|
355
|
+
basePath: "/api",
|
|
356
|
+
hooks: {
|
|
357
|
+
onRequest: () => {
|
|
358
|
+
throw { secretData: "db_password_123" };
|
|
359
|
+
},
|
|
360
|
+
},
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
const response = await handler(get("http://localhost/api/info"));
|
|
364
|
+
expect(response.status).toBe(500);
|
|
365
|
+
const body = await response.json();
|
|
366
|
+
expect(body.error).toBe("internal_error");
|
|
367
|
+
expect(body.message).toBeUndefined();
|
|
368
|
+
expect(JSON.stringify(body)).not.toContain("db_password");
|
|
369
|
+
});
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
/* ------------------------------------------------------------------------------------------------
|
|
373
|
+
* Item 12: Missing Vary headers on preflight
|
|
374
|
+
*
|
|
375
|
+
* Preflight responses should include Vary: Access-Control-Request-Headers
|
|
376
|
+
* and Vary: Access-Control-Request-Method for CDN caching correctness.
|
|
377
|
+
* --------------------------------------------------------------------------------------------- */
|
|
378
|
+
|
|
379
|
+
describe("Item 12: Vary headers on preflight", () => {
|
|
380
|
+
it("preflight includes Vary for CORS request headers and method", () => {
|
|
381
|
+
const request = new Request("http://localhost/api", {
|
|
382
|
+
method: "OPTIONS",
|
|
383
|
+
headers: { Origin: "https://example.com" },
|
|
384
|
+
});
|
|
385
|
+
const response = handleCors(request, {})!;
|
|
386
|
+
|
|
387
|
+
const vary = response.headers.get("Vary") ?? "";
|
|
388
|
+
expect(vary).toContain("Access-Control-Request-Headers");
|
|
389
|
+
expect(vary).toContain("Access-Control-Request-Method");
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
it("preflight includes Vary: Origin when origin is not wildcard", () => {
|
|
393
|
+
const request = new Request("http://localhost/api", {
|
|
394
|
+
method: "OPTIONS",
|
|
395
|
+
headers: { Origin: "https://example.com" },
|
|
396
|
+
});
|
|
397
|
+
const response = handleCors(request, {
|
|
398
|
+
origin: "https://example.com",
|
|
399
|
+
})!;
|
|
400
|
+
|
|
401
|
+
const vary = response.headers.get("Vary") ?? "";
|
|
402
|
+
expect(vary).toContain("Origin");
|
|
403
|
+
expect(vary).toContain("Access-Control-Request-Headers");
|
|
404
|
+
expect(vary).toContain("Access-Control-Request-Method");
|
|
405
|
+
});
|
|
406
|
+
});
|
|
407
|
+
|
|
408
|
+
/* ------------------------------------------------------------------------------------------------
|
|
409
|
+
* Item 7: synthesizeBody null guard uses wrong comparison
|
|
410
|
+
*
|
|
411
|
+
* Tested indirectly through express-fetch-bridge.
|
|
412
|
+
* The synthesizeBody function should treat null bodies as empty (not serialize "null").
|
|
413
|
+
* This is already mitigated by hasPreParsedBody rejecting null, but we test the
|
|
414
|
+
* boundary to ensure the fix is correct.
|
|
415
|
+
* --------------------------------------------------------------------------------------------- */
|
|
416
|
+
|
|
417
|
+
describe("Item 7: synthesizeBody null guard", () => {
|
|
418
|
+
// Verify that null req.body causes hasPreParsedBody to return false,
|
|
419
|
+
// meaning the generic (stream-based) handler runs instead of trying to
|
|
420
|
+
// serialize null into the string "null".
|
|
421
|
+
it("null req.body is treated as no pre-parsed body (falls through to generic handler)", async () => {
|
|
422
|
+
const { createExpressNodeHandler } =
|
|
423
|
+
await import("../endpoints/express-fetch-bridge");
|
|
424
|
+
|
|
425
|
+
let handlerCalled = false;
|
|
426
|
+
const fetchHandler = async (_req: Request) => {
|
|
427
|
+
handlerCalled = true;
|
|
428
|
+
return new Response("ok", { status: 200 });
|
|
429
|
+
};
|
|
430
|
+
|
|
431
|
+
const nodeHandler = createExpressNodeHandler(fetchHandler);
|
|
432
|
+
|
|
433
|
+
// Use supertest-style approach: create a real HTTP server to exercise
|
|
434
|
+
// the full path. Since we can't easily mock Node streams, we verify
|
|
435
|
+
// the hasPreParsedBody logic through the express-fetch-bridge tests.
|
|
436
|
+
// Here we just assert the module loads and the handler is created correctly.
|
|
437
|
+
expect(typeof nodeHandler).toBe("function");
|
|
438
|
+
expect(handlerCalled).toBe(false);
|
|
439
|
+
});
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
/* ------------------------------------------------------------------------------------------------
|
|
443
|
+
* Item 10: After-request middleware does not double-consume response body
|
|
444
|
+
*
|
|
445
|
+
* The response passed to afterRequestMiddleware should be a clone, so
|
|
446
|
+
* reading its body doesn't affect the response sent to the client.
|
|
447
|
+
* --------------------------------------------------------------------------------------------- */
|
|
448
|
+
|
|
449
|
+
describe("Item 10: after-request middleware gets cloned response", () => {
|
|
450
|
+
it("response body is still readable after middleware runs", async () => {
|
|
451
|
+
const handler = createCopilotRuntimeHandler({
|
|
452
|
+
runtime: createRuntime(),
|
|
453
|
+
basePath: "/api",
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
// Simply call the handler and verify the response body is readable
|
|
457
|
+
const response = await handler(get("http://localhost/api/info"));
|
|
458
|
+
expect(response.status).toBe(200);
|
|
459
|
+
|
|
460
|
+
// The body should be consumable — if clone() wasn't used, the middleware
|
|
461
|
+
// might have consumed it first
|
|
462
|
+
const body = await response.json();
|
|
463
|
+
expect(body).toHaveProperty("version");
|
|
464
|
+
});
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
/* ------------------------------------------------------------------------------------------------
|
|
468
|
+
* Breaking Change: CopilotKitRequestHandler deprecated alias
|
|
469
|
+
*
|
|
470
|
+
* The CopilotKitRequestHandler type must still be importable for backward
|
|
471
|
+
* compatibility, even though it's deprecated.
|
|
472
|
+
* --------------------------------------------------------------------------------------------- */
|
|
473
|
+
|
|
474
|
+
describe("Breaking change: CopilotKitRequestHandler type alias exists", () => {
|
|
475
|
+
it("CopilotKitRequestHandler is exported from the package index", async () => {
|
|
476
|
+
// Dynamic import to check the type exists at runtime
|
|
477
|
+
const exports = await import("../index");
|
|
478
|
+
// TypeScript types don't exist at runtime, but we can verify the module
|
|
479
|
+
// loads without errors. The type check is compile-time.
|
|
480
|
+
expect(exports).toBeDefined();
|
|
481
|
+
});
|
|
482
|
+
});
|
|
483
|
+
|
|
484
|
+
/* ------------------------------------------------------------------------------------------------
|
|
485
|
+
* Item 13: Forward-reference code ordering in node-fetch-handler.ts
|
|
486
|
+
*
|
|
487
|
+
* createCopilotNodeHandler should be defined before createNodeFetchHandler
|
|
488
|
+
* references it. This is a code quality fix — no runtime behavior change.
|
|
489
|
+
* --------------------------------------------------------------------------------------------- */
|
|
490
|
+
|
|
491
|
+
describe("Item 13: node-fetch-handler exports are both available", () => {
|
|
492
|
+
it("both createCopilotNodeHandler and createNodeFetchHandler are exported", async () => {
|
|
493
|
+
const { createCopilotNodeHandler, createNodeFetchHandler } =
|
|
494
|
+
await import("../endpoints/node-fetch-handler");
|
|
495
|
+
expect(typeof createCopilotNodeHandler).toBe("function");
|
|
496
|
+
expect(typeof createNodeFetchHandler).toBe("function");
|
|
497
|
+
// The deprecated alias should reference the same function
|
|
498
|
+
expect(createNodeFetchHandler).toBe(createCopilotNodeHandler);
|
|
499
|
+
});
|
|
500
|
+
});
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { describe, it, expect
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
2
|
import {
|
|
3
3
|
createCopilotEndpoint,
|
|
4
4
|
createCopilotEndpointSingleRoute,
|
|
5
5
|
} from "../endpoints";
|
|
6
|
-
import { CopilotRuntime } from "../runtime";
|
|
6
|
+
import { CopilotRuntime } from "../core/runtime";
|
|
7
7
|
import type { AbstractAgent } from "@ag-ui/client";
|
|
8
8
|
|
|
9
9
|
const createMockRuntime = (options: { beforeRequestMiddleware?: any } = {}) => {
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import express from "express";
|
|
2
|
+
import request from "supertest";
|
|
3
|
+
import { describe, it, expect, vi } from "vitest";
|
|
4
|
+
import type { AbstractAgent } from "@ag-ui/client";
|
|
5
|
+
import { Observable, of } from "rxjs";
|
|
6
|
+
|
|
7
|
+
import { createCopilotEndpointExpress } from "../express";
|
|
8
|
+
import { createCopilotEndpointSingleRouteExpress } from "../endpoints/express-single";
|
|
9
|
+
import { CopilotRuntime } from "../core/runtime";
|
|
10
|
+
|
|
11
|
+
vi.mock("../handlers/handle-run", () => ({
|
|
12
|
+
handleRunAgent: vi
|
|
13
|
+
.fn()
|
|
14
|
+
.mockResolvedValue(new Response(null, { status: 200 })),
|
|
15
|
+
}));
|
|
16
|
+
|
|
17
|
+
vi.mock("../handlers/handle-connect", () => ({
|
|
18
|
+
handleConnectAgent: vi
|
|
19
|
+
.fn()
|
|
20
|
+
.mockResolvedValue(new Response(null, { status: 200 })),
|
|
21
|
+
}));
|
|
22
|
+
|
|
23
|
+
vi.mock("../handlers/handle-stop", () => ({
|
|
24
|
+
handleStopAgent: vi
|
|
25
|
+
.fn()
|
|
26
|
+
.mockResolvedValue(new Response(null, { status: 200 })),
|
|
27
|
+
}));
|
|
28
|
+
|
|
29
|
+
describe("Express adapter with hooks", () => {
|
|
30
|
+
const createMockRuntime = (opts?: Partial<CopilotRuntime>) => {
|
|
31
|
+
const createMockAgent = () => {
|
|
32
|
+
const agent: unknown = {
|
|
33
|
+
execute: async () => ({ events: [] }),
|
|
34
|
+
};
|
|
35
|
+
(agent as { clone: () => unknown }).clone = () => createMockAgent();
|
|
36
|
+
return agent as AbstractAgent;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const runner = {
|
|
40
|
+
run: () =>
|
|
41
|
+
new Observable((observer) => {
|
|
42
|
+
observer.next({});
|
|
43
|
+
observer.complete();
|
|
44
|
+
return () => undefined;
|
|
45
|
+
}),
|
|
46
|
+
connect: () => of({}),
|
|
47
|
+
stop: async () => true,
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
return new CopilotRuntime({
|
|
51
|
+
agents: {
|
|
52
|
+
default: createMockAgent(),
|
|
53
|
+
myAgent: createMockAgent(),
|
|
54
|
+
testAgent: createMockAgent(),
|
|
55
|
+
},
|
|
56
|
+
runner,
|
|
57
|
+
...opts,
|
|
58
|
+
});
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
describe("createCopilotEndpointExpress", () => {
|
|
62
|
+
const createApp = (
|
|
63
|
+
runtimeOpts?: Partial<CopilotRuntime>,
|
|
64
|
+
endpointOpts?: Partial<
|
|
65
|
+
Omit<
|
|
66
|
+
Parameters<typeof createCopilotEndpointExpress>[0],
|
|
67
|
+
"runtime" | "basePath"
|
|
68
|
+
>
|
|
69
|
+
>,
|
|
70
|
+
) => {
|
|
71
|
+
const runtime = createMockRuntime(runtimeOpts);
|
|
72
|
+
const app = express();
|
|
73
|
+
app.use(
|
|
74
|
+
createCopilotEndpointExpress({
|
|
75
|
+
runtime,
|
|
76
|
+
basePath: "/",
|
|
77
|
+
...endpointOpts,
|
|
78
|
+
}),
|
|
79
|
+
);
|
|
80
|
+
return { app, runtime };
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
it("routes GET /info correctly", async () => {
|
|
84
|
+
const { app } = createApp();
|
|
85
|
+
const response = await request(app).get("/info");
|
|
86
|
+
|
|
87
|
+
expect(response.status).toBe(200);
|
|
88
|
+
expect(response.body).toHaveProperty("version");
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it("routes POST /agent/:id/run correctly", async () => {
|
|
92
|
+
const { app } = createApp();
|
|
93
|
+
const response = await request(app)
|
|
94
|
+
.post("/agent/myAgent/run")
|
|
95
|
+
.set("Content-Type", "application/json")
|
|
96
|
+
.send({
|
|
97
|
+
messages: [],
|
|
98
|
+
state: {},
|
|
99
|
+
threadId: "thread-1",
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
expect(response.status).not.toBe(404);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it("forwards hooks — onRequest hook is called", async () => {
|
|
106
|
+
const onRequest = vi.fn();
|
|
107
|
+
const { app } = createApp(undefined, { hooks: { onRequest } });
|
|
108
|
+
|
|
109
|
+
await request(app).get("/info");
|
|
110
|
+
|
|
111
|
+
expect(onRequest).toHaveBeenCalledTimes(1);
|
|
112
|
+
expect(onRequest).toHaveBeenCalledWith(
|
|
113
|
+
expect.objectContaining({
|
|
114
|
+
request: expect.any(Request),
|
|
115
|
+
path: expect.stringContaining("/info"),
|
|
116
|
+
runtime: expect.any(CopilotRuntime),
|
|
117
|
+
}),
|
|
118
|
+
);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it("hooks can modify response headers via onResponse", async () => {
|
|
122
|
+
const { app } = createApp(undefined, {
|
|
123
|
+
hooks: {
|
|
124
|
+
onResponse: ({ response }) => {
|
|
125
|
+
const headers = new Headers(response.headers);
|
|
126
|
+
headers.set("x-custom-header", "hello-from-hook");
|
|
127
|
+
return new Response(response.body, {
|
|
128
|
+
status: response.status,
|
|
129
|
+
headers,
|
|
130
|
+
});
|
|
131
|
+
},
|
|
132
|
+
},
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
const response = await request(app).get("/info");
|
|
136
|
+
|
|
137
|
+
expect(response.status).toBe(200);
|
|
138
|
+
expect(response.headers["x-custom-header"]).toBe("hello-from-hook");
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it("CORS headers are present (Access-Control-Allow-Origin: *)", async () => {
|
|
142
|
+
const { app } = createApp(undefined, { cors: true });
|
|
143
|
+
const response = await request(app)
|
|
144
|
+
.options("/info")
|
|
145
|
+
.set("Origin", "https://example.com")
|
|
146
|
+
.set("Access-Control-Request-Method", "GET");
|
|
147
|
+
|
|
148
|
+
expect(response.headers["access-control-allow-origin"]).toBe("*");
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
describe("createCopilotEndpointSingleRouteExpress", () => {
|
|
153
|
+
const createSingleApp = (
|
|
154
|
+
hooks?: Parameters<
|
|
155
|
+
typeof createCopilotEndpointSingleRouteExpress
|
|
156
|
+
>[0]["hooks"],
|
|
157
|
+
) => {
|
|
158
|
+
const runtime = createMockRuntime();
|
|
159
|
+
const app = express();
|
|
160
|
+
app.use(
|
|
161
|
+
createCopilotEndpointSingleRouteExpress({
|
|
162
|
+
runtime,
|
|
163
|
+
basePath: "/rpc",
|
|
164
|
+
hooks,
|
|
165
|
+
}),
|
|
166
|
+
);
|
|
167
|
+
return { app, runtime };
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
it("dispatches single-route with method envelope", async () => {
|
|
171
|
+
const { app } = createSingleApp();
|
|
172
|
+
const response = await request(app)
|
|
173
|
+
.post("/rpc")
|
|
174
|
+
.set("Content-Type", "application/json")
|
|
175
|
+
.send({
|
|
176
|
+
method: "agent/run",
|
|
177
|
+
params: { agentId: "myAgent" },
|
|
178
|
+
body: {
|
|
179
|
+
messages: [],
|
|
180
|
+
state: {},
|
|
181
|
+
threadId: "thread-1",
|
|
182
|
+
},
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
expect(response.status).not.toBe(404);
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
});
|