@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
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
require("reflect-metadata");
|
|
2
|
+
//#region src/v2/runtime/handlers/header-utils.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* Fully-resolved inbound-header forwarding policy read by the call sites.
|
|
5
|
+
*
|
|
6
|
+
* Distinct from the public `ForwardHeadersConfig` option an integrator passes:
|
|
7
|
+
* the runtime resolves that option ONCE in its constructor into this shape
|
|
8
|
+
* (lowercasing names/prefixes, defaulting `useDefaultDenylist`, building the
|
|
9
|
+
* `allow` set) so the predicate stays branch-simple and the policy can never be
|
|
10
|
+
* re-resolved divergently at a call site. See `resolveForwardHeadersPolicy`.
|
|
11
|
+
*/
|
|
12
|
+
interface ResolvedForwardHeadersPolicy {
|
|
13
|
+
/** When true, the built-in infra/platform denylist is active. */
|
|
14
|
+
useDefaultDenylist: boolean;
|
|
15
|
+
/** Extra exact names to strip (lowercased). */
|
|
16
|
+
denyNames: ReadonlySet<string>;
|
|
17
|
+
/** Extra prefixes to strip (lowercased). */
|
|
18
|
+
denyPrefixes: readonly string[];
|
|
19
|
+
/**
|
|
20
|
+
* If set, allowlist mode: ONLY these (lowercased) names are candidates to
|
|
21
|
+
* forward — and `denyNames` / `denyPrefixes` still subtract from them.
|
|
22
|
+
*/
|
|
23
|
+
allow?: ReadonlySet<string>;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Public, integrator-facing config for inbound-header forwarding. Resolved into
|
|
27
|
+
* a {@link ResolvedForwardHeadersPolicy} by {@link resolveForwardHeadersPolicy}.
|
|
28
|
+
*/
|
|
29
|
+
interface ForwardHeadersConfig {
|
|
30
|
+
/** Strip the built-in infra/platform denylist. @default true */
|
|
31
|
+
useDefaultDenylist?: boolean;
|
|
32
|
+
/** Additional exact header names to strip (case-insensitive). */
|
|
33
|
+
deny?: string[];
|
|
34
|
+
/** Additional header-name prefixes to strip (case-insensitive). */
|
|
35
|
+
denyPrefixes?: string[];
|
|
36
|
+
/**
|
|
37
|
+
* If set (with at least one non-empty entry), switch to allowlist mode: ONLY
|
|
38
|
+
* these headers are candidates to forward, overriding the default `x-*` /
|
|
39
|
+
* `authorization` eligibility (case-insensitive). `deny` / `denyPrefixes`
|
|
40
|
+
* still apply and subtract from this set — a header listed in both `allow` and
|
|
41
|
+
* `deny` is NOT forwarded.
|
|
42
|
+
*
|
|
43
|
+
* Footgun: in allowlist mode the built-in DEFAULT denylist (and
|
|
44
|
+
* `useDefaultDenylist`) is BYPASSED — only your `allow` set, minus your own
|
|
45
|
+
* `deny` / `denyPrefixes`, is forwarded. Do NOT allow-list protected/platform
|
|
46
|
+
* headers (e.g. `x-copilotcloud-public-api-key`, `x-forwarded-*`) unless you
|
|
47
|
+
* truly intend to forward them, since the default protection does not apply
|
|
48
|
+
* here.
|
|
49
|
+
*/
|
|
50
|
+
allow?: string[];
|
|
51
|
+
}
|
|
52
|
+
//#endregion
|
|
53
|
+
export { ForwardHeadersConfig, ResolvedForwardHeadersPolicy };
|
|
54
|
+
//# sourceMappingURL=header-utils.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"header-utils.d.cts","names":[],"sources":["../../../../src/v2/runtime/handlers/header-utils.ts"],"mappings":";;;;;;;;;;;UA0DiB,4BAAA;;EAEf,kBAAA;;EAEA,SAAA,EAAW,WAAA;;EAEX,YAAA;;;;;EAKA,KAAA,GAAQ,WAAA;AAAA;;;;;UAOO,oBAAA;;EAEf,kBAAA;;EAEA,IAAA;;EAEA,YAAA;;;;;;;;;;;;;;;EAeA,KAAA;AAAA"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import "reflect-metadata";
|
|
2
|
+
//#region src/v2/runtime/handlers/header-utils.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* Fully-resolved inbound-header forwarding policy read by the call sites.
|
|
5
|
+
*
|
|
6
|
+
* Distinct from the public `ForwardHeadersConfig` option an integrator passes:
|
|
7
|
+
* the runtime resolves that option ONCE in its constructor into this shape
|
|
8
|
+
* (lowercasing names/prefixes, defaulting `useDefaultDenylist`, building the
|
|
9
|
+
* `allow` set) so the predicate stays branch-simple and the policy can never be
|
|
10
|
+
* re-resolved divergently at a call site. See `resolveForwardHeadersPolicy`.
|
|
11
|
+
*/
|
|
12
|
+
interface ResolvedForwardHeadersPolicy {
|
|
13
|
+
/** When true, the built-in infra/platform denylist is active. */
|
|
14
|
+
useDefaultDenylist: boolean;
|
|
15
|
+
/** Extra exact names to strip (lowercased). */
|
|
16
|
+
denyNames: ReadonlySet<string>;
|
|
17
|
+
/** Extra prefixes to strip (lowercased). */
|
|
18
|
+
denyPrefixes: readonly string[];
|
|
19
|
+
/**
|
|
20
|
+
* If set, allowlist mode: ONLY these (lowercased) names are candidates to
|
|
21
|
+
* forward — and `denyNames` / `denyPrefixes` still subtract from them.
|
|
22
|
+
*/
|
|
23
|
+
allow?: ReadonlySet<string>;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Public, integrator-facing config for inbound-header forwarding. Resolved into
|
|
27
|
+
* a {@link ResolvedForwardHeadersPolicy} by {@link resolveForwardHeadersPolicy}.
|
|
28
|
+
*/
|
|
29
|
+
interface ForwardHeadersConfig {
|
|
30
|
+
/** Strip the built-in infra/platform denylist. @default true */
|
|
31
|
+
useDefaultDenylist?: boolean;
|
|
32
|
+
/** Additional exact header names to strip (case-insensitive). */
|
|
33
|
+
deny?: string[];
|
|
34
|
+
/** Additional header-name prefixes to strip (case-insensitive). */
|
|
35
|
+
denyPrefixes?: string[];
|
|
36
|
+
/**
|
|
37
|
+
* If set (with at least one non-empty entry), switch to allowlist mode: ONLY
|
|
38
|
+
* these headers are candidates to forward, overriding the default `x-*` /
|
|
39
|
+
* `authorization` eligibility (case-insensitive). `deny` / `denyPrefixes`
|
|
40
|
+
* still apply and subtract from this set — a header listed in both `allow` and
|
|
41
|
+
* `deny` is NOT forwarded.
|
|
42
|
+
*
|
|
43
|
+
* Footgun: in allowlist mode the built-in DEFAULT denylist (and
|
|
44
|
+
* `useDefaultDenylist`) is BYPASSED — only your `allow` set, minus your own
|
|
45
|
+
* `deny` / `denyPrefixes`, is forwarded. Do NOT allow-list protected/platform
|
|
46
|
+
* headers (e.g. `x-copilotcloud-public-api-key`, `x-forwarded-*`) unless you
|
|
47
|
+
* truly intend to forward them, since the default protection does not apply
|
|
48
|
+
* here.
|
|
49
|
+
*/
|
|
50
|
+
allow?: string[];
|
|
51
|
+
}
|
|
52
|
+
//#endregion
|
|
53
|
+
export { ForwardHeadersConfig, ResolvedForwardHeadersPolicy };
|
|
54
|
+
//# sourceMappingURL=header-utils.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"header-utils.d.mts","names":[],"sources":["../../../../src/v2/runtime/handlers/header-utils.ts"],"mappings":";;;;;;;;;;;UA0DiB,4BAAA;;EAEf,kBAAA;;EAEA,SAAA,EAAW,WAAA;;EAEX,YAAA;;;;;EAKA,KAAA,GAAQ,WAAA;AAAA;;;;;UAOO,oBAAA;;EAEf,kBAAA;;EAEA,IAAA;;EAEA,YAAA;;;;;;;;;;;;;;;EAeA,KAAA;AAAA"}
|
|
@@ -1,25 +1,184 @@
|
|
|
1
1
|
import "reflect-metadata";
|
|
2
2
|
//#region src/v2/runtime/handlers/header-utils.ts
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Exact header names (lowercased) stripped from forwarding by default.
|
|
5
|
+
*
|
|
6
|
+
* These are infrastructure/proxy/platform artifacts that no legitimate agent
|
|
7
|
+
* integration depends on receiving *forwarded from the inbound edge* — the
|
|
8
|
+
* inbound request has already traversed a browser, CDN/edge, load balancer, and
|
|
9
|
+
* hosting platform, each of which stamps its own `x-*` headers. Forwarding them
|
|
10
|
+
* verbatim to an arbitrary configured agent URL leaks client topology and, in
|
|
11
|
+
* the Copilot Cloud case, a platform credential (#5712).
|
|
12
|
+
*
|
|
13
|
+
* `x-amz-cf-id` and `x-copilotcloud-public-api-key` are also covered by the
|
|
14
|
+
* `x-amz-` / `x-copilotcloud-` prefixes below; the exact entries are kept
|
|
15
|
+
* intentionally as documentation anchors for the highest-severity headers
|
|
16
|
+
* (notably the platform API key), not as drift/oversight.
|
|
6
17
|
*/
|
|
7
|
-
|
|
18
|
+
const DEFAULT_DENY_HEADER_NAMES = new Set([
|
|
19
|
+
"x-forwarded-for",
|
|
20
|
+
"x-forwarded-proto",
|
|
21
|
+
"x-forwarded-host",
|
|
22
|
+
"x-forwarded-port",
|
|
23
|
+
"x-forwarded-server",
|
|
24
|
+
"x-real-ip",
|
|
25
|
+
"x-amzn-trace-id",
|
|
26
|
+
"x-amz-cf-id",
|
|
27
|
+
"x-cloud-trace-context",
|
|
28
|
+
"x-cache",
|
|
29
|
+
"x-served-by",
|
|
30
|
+
"x-request-id",
|
|
31
|
+
"x-copilotcloud-public-api-key"
|
|
32
|
+
]);
|
|
33
|
+
/**
|
|
34
|
+
* Header-name prefixes (lowercased) stripped from forwarding by default.
|
|
35
|
+
*
|
|
36
|
+
* Prefix matching covers the well-known platform/CDN families so a new member
|
|
37
|
+
* of a family (e.g. a future `x-vercel-foo`) is denied without a constant edit.
|
|
38
|
+
*/
|
|
39
|
+
const DEFAULT_DENY_HEADER_PREFIXES = [
|
|
40
|
+
"x-amz-",
|
|
41
|
+
"x-azure-",
|
|
42
|
+
"x-fastly-",
|
|
43
|
+
"x-vercel-",
|
|
44
|
+
"x-middleware-",
|
|
45
|
+
"x-copilotcloud-"
|
|
46
|
+
];
|
|
47
|
+
/**
|
|
48
|
+
* Normalizes a public {@link ForwardHeadersConfig} (or `undefined`) into a
|
|
49
|
+
* fully-resolved {@link ResolvedForwardHeadersPolicy}.
|
|
50
|
+
*
|
|
51
|
+
* - `useDefaultDenylist` defaults to `true` (the built-in denylist is active
|
|
52
|
+
* on upgrade); pass `false` to restore the previous wide-open behavior.
|
|
53
|
+
* - `deny` / `denyPrefixes` extend (do not replace) the defaults.
|
|
54
|
+
* - `allow` activates allowlist mode only when it has at least one non-empty
|
|
55
|
+
* entry after normalization.
|
|
56
|
+
*
|
|
57
|
+
* All names/prefixes are trimmed, lowercased, and stripped of empty/
|
|
58
|
+
* whitespace-only entries before use. Trimming/lowercasing keeps matching a
|
|
59
|
+
* plain set/prefix check against the lowercased inbound keys; dropping empties
|
|
60
|
+
* is a safety guard: a stray `denyPrefixes: [""]` would make `startsWith("")`
|
|
61
|
+
* true for every header (silently denying ALL forwarding). Because empties are
|
|
62
|
+
* dropped BEFORE the allowlist-mode decision, an `allow: [""]` / `allow: [" "]`
|
|
63
|
+
* normalizes to an empty set and does NOT switch on allowlist mode — the runtime
|
|
64
|
+
* stays in denylist mode. Allowlist mode activates only when `allow` has at
|
|
65
|
+
* least one non-empty entry; these empty/whitespace-only entries are integrator
|
|
66
|
+
* typos, not intent, so we filter them.
|
|
67
|
+
*/
|
|
68
|
+
function normalizeHeaderEntries(entries) {
|
|
69
|
+
return (entries ?? []).map((entry) => entry.trim().toLowerCase()).filter((entry) => entry.length > 0);
|
|
70
|
+
}
|
|
71
|
+
function resolveForwardHeadersPolicy(config) {
|
|
72
|
+
const denyNames = new Set(normalizeHeaderEntries(config?.deny));
|
|
73
|
+
const denyPrefixes = normalizeHeaderEntries(config?.denyPrefixes);
|
|
74
|
+
const allowEntries = normalizeHeaderEntries(config?.allow);
|
|
75
|
+
const allow = allowEntries.length > 0 ? new Set(allowEntries) : void 0;
|
|
76
|
+
return {
|
|
77
|
+
useDefaultDenylist: config?.useDefaultDenylist ?? true,
|
|
78
|
+
denyNames,
|
|
79
|
+
denyPrefixes,
|
|
80
|
+
allow
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* True iff the (already-lowercased) header name matches the integrator's OWN
|
|
85
|
+
* `deny` / `denyPrefixes`. This is the authoritative subtractive check: it is
|
|
86
|
+
* consulted in BOTH allowlist and denylist mode. It deliberately does NOT
|
|
87
|
+
* include the built-in {@link DEFAULT_DENY_HEADER_NAMES} /
|
|
88
|
+
* {@link DEFAULT_DENY_HEADER_PREFIXES} — an explicit `allow` opts the integrator
|
|
89
|
+
* back into a default-denied header on purpose, so only their own `deny`
|
|
90
|
+
* subtracts from an allowlist.
|
|
91
|
+
*/
|
|
92
|
+
function matchesIntegratorDeny(lower, policy) {
|
|
93
|
+
if (policy.denyNames.has(lower)) return true;
|
|
94
|
+
return policy.denyPrefixes.some((prefix) => lower.startsWith(prefix));
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Determines if a header should be forwarded under the given resolved policy.
|
|
98
|
+
*
|
|
99
|
+
* The integrator's `deny` / `denyPrefixes` ALWAYS strip, including in allowlist
|
|
100
|
+
* mode: `allow` selects the candidate set, `deny` removes from it. A header the
|
|
101
|
+
* integrator lists in BOTH `allow` and `deny` is NOT forwarded — deny is
|
|
102
|
+
* authoritative so a security-motivated `deny` can never be silently defeated
|
|
103
|
+
* by an overlapping `allow` (the footgun this hardens against).
|
|
104
|
+
*
|
|
105
|
+
* Modes:
|
|
106
|
+
* - Allowlist (`policy.allow` set): forward iff the name is in `allow` AND is
|
|
107
|
+
* NOT matched by the integrator's `deny` / `denyPrefixes`. Nothing else
|
|
108
|
+
* forwards — not even the usual `authorization` / `x-*` eligibility.
|
|
109
|
+
* - Denylist (default): base eligibility is `authorization` or any `x-*`, then
|
|
110
|
+
* the built-in denylist (when enabled) and the integrator's own
|
|
111
|
+
* names/prefixes strip from that set.
|
|
112
|
+
*
|
|
113
|
+
* Note: the built-in default denylist applies ONLY in denylist mode; an
|
|
114
|
+
* explicit `allow` is treated as the integrator deliberately opting back into
|
|
115
|
+
* those headers, so only their OWN `deny` subtracts in allowlist mode.
|
|
116
|
+
*/
|
|
117
|
+
function shouldForwardHeader(headerName, policy) {
|
|
8
118
|
const lower = headerName.toLowerCase();
|
|
9
|
-
return lower
|
|
119
|
+
if (policy.allow) return policy.allow.has(lower) && !matchesIntegratorDeny(lower, policy);
|
|
120
|
+
if (!(lower === "authorization" || lower.startsWith("x-"))) return false;
|
|
121
|
+
if (policy.useDefaultDenylist) {
|
|
122
|
+
if (DEFAULT_DENY_HEADER_NAMES.has(lower)) return false;
|
|
123
|
+
if (DEFAULT_DENY_HEADER_PREFIXES.some((prefix) => lower.startsWith(prefix))) return false;
|
|
124
|
+
}
|
|
125
|
+
if (matchesIntegratorDeny(lower, policy)) return false;
|
|
126
|
+
return true;
|
|
10
127
|
}
|
|
11
128
|
/**
|
|
12
|
-
* Extracts headers that should be forwarded from a Request object
|
|
13
|
-
*
|
|
129
|
+
* Extracts headers that should be forwarded from a Request object, applying the
|
|
130
|
+
* resolved forwarding policy. Keys are normalized to the lowercased form the
|
|
131
|
+
* `Headers` iterator yields.
|
|
14
132
|
*/
|
|
15
|
-
function extractForwardableHeaders(request) {
|
|
133
|
+
function extractForwardableHeaders(request, policy) {
|
|
16
134
|
const forwardableHeaders = {};
|
|
17
135
|
request.headers.forEach((value, key) => {
|
|
18
|
-
if (shouldForwardHeader(key)) forwardableHeaders[key] = value;
|
|
136
|
+
if (shouldForwardHeader(key, policy)) forwardableHeaders[key] = value;
|
|
19
137
|
});
|
|
20
138
|
return forwardableHeaders;
|
|
21
139
|
}
|
|
140
|
+
/**
|
|
141
|
+
* Merges forwardable inbound request headers onto the headers a server
|
|
142
|
+
* explicitly configured on an agent, letting the SERVER-CONFIGURED headers WIN
|
|
143
|
+
* on collision — a server-set service-to-service token (e.g. an IAM bearer)
|
|
144
|
+
* must never be silently overridden by a browser/edge/platform-injected inbound
|
|
145
|
+
* header (#5712).
|
|
146
|
+
*
|
|
147
|
+
* The collision check is case-insensitive: `extractForwardableHeaders`
|
|
148
|
+
* normalizes inbound keys to lowercase (`authorization`) while the server
|
|
149
|
+
* typically configures canonical casing (`Authorization`). A plain object
|
|
150
|
+
* spread would treat those as distinct keys and emit BOTH — which downstream
|
|
151
|
+
* (undici) comma-joins into a single invalid "multiple JWTs" value. So we drop
|
|
152
|
+
* any forwarded header the agent already sets, matched case-insensitively, and
|
|
153
|
+
* let non-colliding inbound headers pass through unchanged.
|
|
154
|
+
*
|
|
155
|
+
* The same comma-join hazard exists if the SERVER CONFIG ITSELF contains two
|
|
156
|
+
* case-variants of one header (e.g. both `Authorization` and `authorization`
|
|
157
|
+
* in `agent.headers`). A plain `{ ...serverHeaders }` spread would keep both,
|
|
158
|
+
* so we additionally collapse server-self case-collisions to a SINGLE entry,
|
|
159
|
+
* FIRST-OCCURRENCE WINS: the first key seen (in `Object.keys` order) keeps its
|
|
160
|
+
* exact casing and value, and any later case-variant of that name is dropped.
|
|
161
|
+
* Server-wins-over-inbound and case-insensitive inbound suppression are
|
|
162
|
+
* otherwise unchanged.
|
|
163
|
+
*
|
|
164
|
+
* Breadth (which inbound headers are eligible to forward at all) is decided by
|
|
165
|
+
* `policy` upstream in `extractForwardableHeaders` → `shouldForwardHeader`; the
|
|
166
|
+
* merge never re-widens or re-narrows the set.
|
|
167
|
+
*/
|
|
168
|
+
function mergeForwardableHeaders(serverHeaders, request, policy) {
|
|
169
|
+
const base = serverHeaders ?? {};
|
|
170
|
+
const merged = {};
|
|
171
|
+
const serverHeaderNames = /* @__PURE__ */ new Set();
|
|
172
|
+
for (const [name, value] of Object.entries(base)) {
|
|
173
|
+
const lower = name.toLowerCase();
|
|
174
|
+
if (serverHeaderNames.has(lower)) continue;
|
|
175
|
+
serverHeaderNames.add(lower);
|
|
176
|
+
merged[name] = value;
|
|
177
|
+
}
|
|
178
|
+
for (const [name, value] of Object.entries(extractForwardableHeaders(request, policy))) if (!serverHeaderNames.has(name.toLowerCase())) merged[name] = value;
|
|
179
|
+
return merged;
|
|
180
|
+
}
|
|
22
181
|
|
|
23
182
|
//#endregion
|
|
24
|
-
export {
|
|
183
|
+
export { mergeForwardableHeaders, resolveForwardHeadersPolicy };
|
|
25
184
|
//# sourceMappingURL=header-utils.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"header-utils.mjs","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.mjs","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"}
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
require("reflect-metadata");
|
|
2
|
+
const require_runtime = require('../../../../_virtual/_rolldown/runtime.cjs');
|
|
3
|
+
const require_runtime$1 = require('../../core/runtime.cjs');
|
|
4
|
+
const require_client = require('../../intelligence-platform/client.cjs');
|
|
5
|
+
const require_json_response = require('../shared/json-response.cjs');
|
|
6
|
+
const require_resolve_intelligence_user = require('../shared/resolve-intelligence-user.cjs');
|
|
7
|
+
let _copilotkit_shared = require("@copilotkit/shared");
|
|
8
|
+
|
|
9
|
+
//#region src/v2/runtime/handlers/intelligence/memories.ts
|
|
10
|
+
const MISSING_INTELLIGENCE_MESSAGE = "Missing CopilotKitIntelligence configuration. Memory operations require a CopilotKitIntelligence instance to be provided in CopilotRuntime options.";
|
|
11
|
+
/** Allowed `kind` vocabulary the platform's memory endpoints accept. */
|
|
12
|
+
const MEMORY_KINDS = new Set([
|
|
13
|
+
"topical",
|
|
14
|
+
"episodic",
|
|
15
|
+
"operational"
|
|
16
|
+
]);
|
|
17
|
+
/** Allowed `scope` vocabulary the platform's memory endpoints accept. */
|
|
18
|
+
const MEMORY_SCOPES = new Set(["user", "project"]);
|
|
19
|
+
/**
|
|
20
|
+
* Maps a thrown error to a `Response`.
|
|
21
|
+
*
|
|
22
|
+
* For a {@link PlatformRequestError}, forward only client-actionable **4xx**
|
|
23
|
+
* statuses verbatim (e.g. 404 missing/wrong-scope memory, 409 conflict, 422
|
|
24
|
+
* unprocessable) so a `useMemories` consumer can branch on them — a flat 500
|
|
25
|
+
* would erase that distinction. A platform **5xx** (or any non-4xx / malformed
|
|
26
|
+
* status) means the runtime is healthy but its dependency failed, so it surfaces
|
|
27
|
+
* as `502 Bad Gateway` rather than echoing the upstream status as if the runtime
|
|
28
|
+
* itself broke — and this also avoids a `new Response(..., { status })`
|
|
29
|
+
* `RangeError` on an out-of-range status. Non-platform throws stay 500.
|
|
30
|
+
*/
|
|
31
|
+
function memoryErrorResponse(error, message) {
|
|
32
|
+
if (error instanceof require_client.PlatformRequestError) {
|
|
33
|
+
const { status } = error;
|
|
34
|
+
if (Number.isInteger(status) && status >= 400 && status <= 499) return require_json_response.errorResponse(message, status);
|
|
35
|
+
return require_json_response.errorResponse(message, 502);
|
|
36
|
+
}
|
|
37
|
+
return require_json_response.errorResponse(message, 500);
|
|
38
|
+
}
|
|
39
|
+
async function parseJsonBody(request) {
|
|
40
|
+
try {
|
|
41
|
+
return await request.json();
|
|
42
|
+
} catch (error) {
|
|
43
|
+
_copilotkit_shared.logger.error({ err: error }, "Malformed JSON in memory request body");
|
|
44
|
+
return require_json_response.errorResponse("Invalid request body", 400);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Extracts and validates the create/supersede body fields the platform's
|
|
49
|
+
* memory endpoints require. Returns a `Response` (400) on invalid input.
|
|
50
|
+
*/
|
|
51
|
+
function parseMemoryBody(body) {
|
|
52
|
+
const { content, kind, scope, sourceThreadIds } = body;
|
|
53
|
+
if (typeof content !== "string" || typeof kind !== "string") return require_json_response.errorResponse("Memory requires string `content` and `kind`", 400);
|
|
54
|
+
if (!MEMORY_KINDS.has(kind)) return require_json_response.errorResponse("Memory `kind` must be one of: topical, episodic, operational", 400);
|
|
55
|
+
if (scope !== void 0 && typeof scope !== "string") return require_json_response.errorResponse("Memory `scope` must be a string when provided", 400);
|
|
56
|
+
if (typeof scope === "string" && !MEMORY_SCOPES.has(scope)) return require_json_response.errorResponse("Memory `scope` must be one of: user, project", 400);
|
|
57
|
+
if (sourceThreadIds !== void 0 && (!Array.isArray(sourceThreadIds) || !sourceThreadIds.every((id) => typeof id === "string"))) return require_json_response.errorResponse("Memory `sourceThreadIds` must be an array of strings when provided", 400);
|
|
58
|
+
return {
|
|
59
|
+
content,
|
|
60
|
+
kind,
|
|
61
|
+
...typeof scope === "string" ? { scope } : {},
|
|
62
|
+
...Array.isArray(sourceThreadIds) ? { sourceThreadIds } : {}
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Lists the resolved user's long-term memories via the Intelligence platform.
|
|
67
|
+
*
|
|
68
|
+
* Mirrors {@link handleListThreads}: requires a `CopilotKitIntelligence`
|
|
69
|
+
* runtime, resolves the user with `identifyUser` (never trusting a
|
|
70
|
+
* client-supplied id), and proxies to the platform's `GET /api/memories`
|
|
71
|
+
* with the project API key + resolved user. The `?includeInvalidated=true`
|
|
72
|
+
* query is forwarded so callers can opt into retired rows. The response is
|
|
73
|
+
* the platform's `{ memories }` envelope, which the client memory store
|
|
74
|
+
* consumes directly.
|
|
75
|
+
*/
|
|
76
|
+
async function handleListMemories({ runtime, request }) {
|
|
77
|
+
if (require_runtime$1.isIntelligenceRuntime(runtime)) try {
|
|
78
|
+
const includeInvalidated = new URL(request.url).searchParams.get("includeInvalidated") === "true";
|
|
79
|
+
const user = await require_resolve_intelligence_user.resolveIntelligenceUser({
|
|
80
|
+
runtime,
|
|
81
|
+
request
|
|
82
|
+
});
|
|
83
|
+
if (require_json_response.isHandlerResponse(user)) return user;
|
|
84
|
+
const data = await runtime.intelligence.listMemories({
|
|
85
|
+
userId: user.id,
|
|
86
|
+
...includeInvalidated ? { includeInvalidated: true } : {}
|
|
87
|
+
});
|
|
88
|
+
if (data == null || typeof data !== "object" || !Array.isArray(data.memories)) {
|
|
89
|
+
_copilotkit_shared.logger.error({ data }, "listMemories: platform returned a response without a `memories` array");
|
|
90
|
+
return require_json_response.errorResponse("Memory platform returned an invalid list response", 502);
|
|
91
|
+
}
|
|
92
|
+
return Response.json(data);
|
|
93
|
+
} catch (error) {
|
|
94
|
+
_copilotkit_shared.logger.error({ err: error }, "Error listing memories");
|
|
95
|
+
return memoryErrorResponse(error, "Failed to list memories");
|
|
96
|
+
}
|
|
97
|
+
return require_json_response.errorResponse(MISSING_INTELLIGENCE_MESSAGE, 422);
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Mints memory-realtime join credentials (platform `POST
|
|
101
|
+
* /api/memories/subscribe`). Mirrors {@link handleSubscribeToThreads}: requires
|
|
102
|
+
* a `CopilotKitIntelligence` runtime and resolves the user with `identifyUser`
|
|
103
|
+
* (never a client-supplied id). Returns `{ joinToken, joinCode }` — memory needs
|
|
104
|
+
* the `joinCode` here (unlike threads, where it rides the thread-list response)
|
|
105
|
+
* because the client builds the `user_meta:memories:<joinCode>` channel topic
|
|
106
|
+
* from it.
|
|
107
|
+
*/
|
|
108
|
+
async function handleSubscribeToMemories({ runtime, request }) {
|
|
109
|
+
if (require_runtime$1.isIntelligenceRuntime(runtime)) try {
|
|
110
|
+
const user = await require_resolve_intelligence_user.resolveIntelligenceUser({
|
|
111
|
+
runtime,
|
|
112
|
+
request
|
|
113
|
+
});
|
|
114
|
+
if (require_json_response.isHandlerResponse(user)) return user;
|
|
115
|
+
const credentials = await runtime.intelligence.ɵsubscribeToMemories({ userId: user.id });
|
|
116
|
+
return Response.json({
|
|
117
|
+
joinToken: credentials.joinToken,
|
|
118
|
+
joinCode: credentials.joinCode
|
|
119
|
+
});
|
|
120
|
+
} catch (error) {
|
|
121
|
+
_copilotkit_shared.logger.error({ err: error }, "Error subscribing to memories");
|
|
122
|
+
return memoryErrorResponse(error, "Failed to subscribe to memories");
|
|
123
|
+
}
|
|
124
|
+
return require_json_response.errorResponse(MISSING_INTELLIGENCE_MESSAGE, 422);
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Creates a memory for the resolved user (platform `POST /api/memories`).
|
|
128
|
+
* Identity comes from `identifyUser`, never the request body. Returns 201
|
|
129
|
+
* with the stored memory (the client store applies it server-authoritatively).
|
|
130
|
+
*/
|
|
131
|
+
async function handleCreateMemory({ runtime, request }) {
|
|
132
|
+
if (!require_runtime$1.isIntelligenceRuntime(runtime)) return require_json_response.errorResponse(MISSING_INTELLIGENCE_MESSAGE, 422);
|
|
133
|
+
try {
|
|
134
|
+
const body = await parseJsonBody(request);
|
|
135
|
+
if (require_json_response.isHandlerResponse(body)) return body;
|
|
136
|
+
const fields = parseMemoryBody(body);
|
|
137
|
+
if (require_json_response.isHandlerResponse(fields)) return fields;
|
|
138
|
+
const user = await require_resolve_intelligence_user.resolveIntelligenceUser({
|
|
139
|
+
runtime,
|
|
140
|
+
request
|
|
141
|
+
});
|
|
142
|
+
if (require_json_response.isHandlerResponse(user)) return user;
|
|
143
|
+
const data = await runtime.intelligence.createMemory({
|
|
144
|
+
userId: user.id,
|
|
145
|
+
...fields
|
|
146
|
+
});
|
|
147
|
+
return Response.json(data, { status: 201 });
|
|
148
|
+
} catch (error) {
|
|
149
|
+
_copilotkit_shared.logger.error({ err: error }, "Error creating memory");
|
|
150
|
+
return memoryErrorResponse(error, "Failed to create memory");
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Supersedes a memory (platform `PATCH /api/memories/:id`): retires `:id` and
|
|
155
|
+
* inserts the new content atomically; the response carries `retiredId`.
|
|
156
|
+
*/
|
|
157
|
+
async function handleUpdateMemory({ runtime, request, memoryId }) {
|
|
158
|
+
if (!require_runtime$1.isIntelligenceRuntime(runtime)) return require_json_response.errorResponse(MISSING_INTELLIGENCE_MESSAGE, 422);
|
|
159
|
+
try {
|
|
160
|
+
const body = await parseJsonBody(request);
|
|
161
|
+
if (require_json_response.isHandlerResponse(body)) return body;
|
|
162
|
+
const fields = parseMemoryBody(body);
|
|
163
|
+
if (require_json_response.isHandlerResponse(fields)) return fields;
|
|
164
|
+
const user = await require_resolve_intelligence_user.resolveIntelligenceUser({
|
|
165
|
+
runtime,
|
|
166
|
+
request
|
|
167
|
+
});
|
|
168
|
+
if (require_json_response.isHandlerResponse(user)) return user;
|
|
169
|
+
const data = await runtime.intelligence.updateMemory({
|
|
170
|
+
userId: user.id,
|
|
171
|
+
id: memoryId,
|
|
172
|
+
...fields
|
|
173
|
+
});
|
|
174
|
+
return Response.json(data);
|
|
175
|
+
} catch (error) {
|
|
176
|
+
_copilotkit_shared.logger.error({ err: error }, "Error updating memory");
|
|
177
|
+
return memoryErrorResponse(error, "Failed to update memory");
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Retires (forgets) a memory (platform `DELETE /api/memories/:id`). Non-lossy
|
|
182
|
+
* on the platform side; returns 204.
|
|
183
|
+
*/
|
|
184
|
+
async function handleRemoveMemory({ runtime, request, memoryId }) {
|
|
185
|
+
if (!require_runtime$1.isIntelligenceRuntime(runtime)) return require_json_response.errorResponse(MISSING_INTELLIGENCE_MESSAGE, 422);
|
|
186
|
+
try {
|
|
187
|
+
const user = await require_resolve_intelligence_user.resolveIntelligenceUser({
|
|
188
|
+
runtime,
|
|
189
|
+
request
|
|
190
|
+
});
|
|
191
|
+
if (require_json_response.isHandlerResponse(user)) return user;
|
|
192
|
+
await runtime.intelligence.removeMemory({
|
|
193
|
+
userId: user.id,
|
|
194
|
+
id: memoryId
|
|
195
|
+
});
|
|
196
|
+
return new Response(null, { status: 204 });
|
|
197
|
+
} catch (error) {
|
|
198
|
+
_copilotkit_shared.logger.error({ err: error }, "Error removing memory");
|
|
199
|
+
return memoryErrorResponse(error, "Failed to remove memory");
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
//#endregion
|
|
204
|
+
exports.handleCreateMemory = handleCreateMemory;
|
|
205
|
+
exports.handleListMemories = handleListMemories;
|
|
206
|
+
exports.handleRemoveMemory = handleRemoveMemory;
|
|
207
|
+
exports.handleSubscribeToMemories = handleSubscribeToMemories;
|
|
208
|
+
exports.handleUpdateMemory = handleUpdateMemory;
|
|
209
|
+
//# sourceMappingURL=memories.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memories.cjs","names":["PlatformRequestError","errorResponse","isIntelligenceRuntime","resolveIntelligenceUser","isHandlerResponse"],"sources":["../../../../../src/v2/runtime/handlers/intelligence/memories.ts"],"sourcesContent":["import type { CopilotRuntimeLike } from \"../../core/runtime\";\nimport { isIntelligenceRuntime } from \"../../core/runtime\";\nimport { logger } from \"@copilotkit/shared\";\nimport { errorResponse, isHandlerResponse } from \"../shared/json-response\";\nimport { resolveIntelligenceUser } from \"../shared/resolve-intelligence-user\";\nimport { PlatformRequestError } from \"../../intelligence-platform/client\";\n\ninterface MemoriesHandlerParams {\n runtime: CopilotRuntimeLike;\n request: Request;\n}\n\ninterface MemoryMutationParams extends MemoriesHandlerParams {\n memoryId: string;\n}\n\nconst MISSING_INTELLIGENCE_MESSAGE =\n \"Missing CopilotKitIntelligence configuration. Memory operations require a CopilotKitIntelligence instance to be provided in CopilotRuntime options.\";\n\n/** Allowed `kind` vocabulary the platform's memory endpoints accept. */\nconst MEMORY_KINDS: ReadonlySet<string> = new Set([\n \"topical\",\n \"episodic\",\n \"operational\",\n]);\n/** Allowed `scope` vocabulary the platform's memory endpoints accept. */\nconst MEMORY_SCOPES: ReadonlySet<string> = new Set([\"user\", \"project\"]);\n\n/**\n * Maps a thrown error to a `Response`.\n *\n * For a {@link PlatformRequestError}, forward only client-actionable **4xx**\n * statuses verbatim (e.g. 404 missing/wrong-scope memory, 409 conflict, 422\n * unprocessable) so a `useMemories` consumer can branch on them — a flat 500\n * would erase that distinction. A platform **5xx** (or any non-4xx / malformed\n * status) means the runtime is healthy but its dependency failed, so it surfaces\n * as `502 Bad Gateway` rather than echoing the upstream status as if the runtime\n * itself broke — and this also avoids a `new Response(..., { status })`\n * `RangeError` on an out-of-range status. Non-platform throws stay 500.\n */\nfunction memoryErrorResponse(error: unknown, message: string): Response {\n if (error instanceof PlatformRequestError) {\n const { status } = error;\n if (Number.isInteger(status) && status >= 400 && status <= 499) {\n return errorResponse(message, status);\n }\n return errorResponse(message, 502);\n }\n return errorResponse(message, 500);\n}\n\nasync function parseJsonBody(\n request: Request,\n): Promise<Record<string, unknown> | Response> {\n try {\n return (await request.json()) as Record<string, unknown>;\n } catch (error) {\n logger.error({ err: error }, \"Malformed JSON in memory request body\");\n return errorResponse(\"Invalid request body\", 400);\n }\n}\n\n/**\n * Extracts and validates the create/supersede body fields the platform's\n * memory endpoints require. Returns a `Response` (400) on invalid input.\n */\nfunction parseMemoryBody(body: Record<string, unknown>):\n | {\n content: string;\n kind: string;\n scope?: string;\n sourceThreadIds?: string[];\n }\n | Response {\n const { content, kind, scope, sourceThreadIds } = body;\n if (typeof content !== \"string\" || typeof kind !== \"string\") {\n return errorResponse(\"Memory requires string `content` and `kind`\", 400);\n }\n // `kind` must be one of the platform's known kinds. Reject an out-of-vocabulary\n // value here rather than forwarding it for the platform to reject.\n if (!MEMORY_KINDS.has(kind)) {\n return errorResponse(\n \"Memory `kind` must be one of: topical, episodic, operational\",\n 400,\n );\n }\n // `scope` is optional: when omitted the platform applies its default\n // (`\"user\"`). Only reject a present-but-wrong-typed scope.\n if (scope !== undefined && typeof scope !== \"string\") {\n return errorResponse(\"Memory `scope` must be a string when provided\", 400);\n }\n // When `scope` is present, it must be one of the known scopes.\n if (typeof scope === \"string\" && !MEMORY_SCOPES.has(scope)) {\n return errorResponse(\"Memory `scope` must be one of: user, project\", 400);\n }\n // `sourceThreadIds` is optional, but when present it must be a string array.\n // Validate every element so non-string ids are not forwarded to the platform.\n if (\n sourceThreadIds !== undefined &&\n (!Array.isArray(sourceThreadIds) ||\n !sourceThreadIds.every((id) => typeof id === \"string\"))\n ) {\n return errorResponse(\n \"Memory `sourceThreadIds` must be an array of strings when provided\",\n 400,\n );\n }\n return {\n content,\n kind,\n ...(typeof scope === \"string\" ? { scope } : {}),\n ...(Array.isArray(sourceThreadIds)\n ? { sourceThreadIds: sourceThreadIds as string[] }\n : {}),\n // `sourceThreadIds` elements are validated as strings above; the cast is safe.\n };\n}\n\n/**\n * Lists the resolved user's long-term memories via the Intelligence platform.\n *\n * Mirrors {@link handleListThreads}: requires a `CopilotKitIntelligence`\n * runtime, resolves the user with `identifyUser` (never trusting a\n * client-supplied id), and proxies to the platform's `GET /api/memories`\n * with the project API key + resolved user. The `?includeInvalidated=true`\n * query is forwarded so callers can opt into retired rows. The response is\n * the platform's `{ memories }` envelope, which the client memory store\n * consumes directly.\n */\nexport async function handleListMemories({\n runtime,\n request,\n}: MemoriesHandlerParams): Promise<Response> {\n if (isIntelligenceRuntime(runtime)) {\n try {\n const url = new URL(request.url);\n const includeInvalidated =\n url.searchParams.get(\"includeInvalidated\") === \"true\";\n\n const user = await resolveIntelligenceUser({ runtime, request });\n if (isHandlerResponse(user)) return user;\n\n const data = await runtime.intelligence.listMemories({\n userId: user.id,\n ...(includeInvalidated ? { includeInvalidated: true } : {}),\n });\n\n // The client memory store consumes the `{ memories: [...] }` envelope\n // directly. Assert the shape before forwarding so a platform contract\n // violation surfaces as a clear 502 (the runtime is healthy but its\n // dependency returned the wrong shape) instead of a 200 the client will\n // choke on.\n if (\n data == null ||\n typeof data !== \"object\" ||\n !Array.isArray((data as { memories?: unknown }).memories)\n ) {\n logger.error(\n { data },\n \"listMemories: platform returned a response without a `memories` array\",\n );\n return errorResponse(\n \"Memory platform returned an invalid list response\",\n 502,\n );\n }\n\n return Response.json(data);\n } catch (error) {\n logger.error({ err: error }, \"Error listing memories\");\n return memoryErrorResponse(error, \"Failed to list memories\");\n }\n }\n\n return errorResponse(MISSING_INTELLIGENCE_MESSAGE, 422);\n}\n\n/**\n * Mints memory-realtime join credentials (platform `POST\n * /api/memories/subscribe`). Mirrors {@link handleSubscribeToThreads}: requires\n * a `CopilotKitIntelligence` runtime and resolves the user with `identifyUser`\n * (never a client-supplied id). Returns `{ joinToken, joinCode }` — memory needs\n * the `joinCode` here (unlike threads, where it rides the thread-list response)\n * because the client builds the `user_meta:memories:<joinCode>` channel topic\n * from it.\n */\nexport async function handleSubscribeToMemories({\n runtime,\n request,\n}: MemoriesHandlerParams): Promise<Response> {\n if (isIntelligenceRuntime(runtime)) {\n try {\n const user = await resolveIntelligenceUser({ runtime, request });\n if (isHandlerResponse(user)) return user;\n\n const credentials = await runtime.intelligence.ɵsubscribeToMemories({\n userId: user.id,\n });\n\n return Response.json({\n joinToken: credentials.joinToken,\n joinCode: credentials.joinCode,\n });\n } catch (error) {\n logger.error({ err: error }, \"Error subscribing to memories\");\n return memoryErrorResponse(error, \"Failed to subscribe to memories\");\n }\n }\n\n return errorResponse(MISSING_INTELLIGENCE_MESSAGE, 422);\n}\n\n/**\n * Creates a memory for the resolved user (platform `POST /api/memories`).\n * Identity comes from `identifyUser`, never the request body. Returns 201\n * with the stored memory (the client store applies it server-authoritatively).\n */\nexport async function handleCreateMemory({\n runtime,\n request,\n}: MemoriesHandlerParams): Promise<Response> {\n if (!isIntelligenceRuntime(runtime)) {\n return errorResponse(MISSING_INTELLIGENCE_MESSAGE, 422);\n }\n try {\n const body = await parseJsonBody(request);\n if (isHandlerResponse(body)) return body;\n const fields = parseMemoryBody(body);\n if (isHandlerResponse(fields)) return fields;\n\n const user = await resolveIntelligenceUser({ runtime, request });\n if (isHandlerResponse(user)) return user;\n\n const data = await runtime.intelligence.createMemory({\n userId: user.id,\n ...fields,\n });\n return Response.json(data, { status: 201 });\n } catch (error) {\n logger.error({ err: error }, \"Error creating memory\");\n return memoryErrorResponse(error, \"Failed to create memory\");\n }\n}\n\n/**\n * Supersedes a memory (platform `PATCH /api/memories/:id`): retires `:id` and\n * inserts the new content atomically; the response carries `retiredId`.\n */\nexport async function handleUpdateMemory({\n runtime,\n request,\n memoryId,\n}: MemoryMutationParams): Promise<Response> {\n if (!isIntelligenceRuntime(runtime)) {\n return errorResponse(MISSING_INTELLIGENCE_MESSAGE, 422);\n }\n try {\n const body = await parseJsonBody(request);\n if (isHandlerResponse(body)) return body;\n const fields = parseMemoryBody(body);\n if (isHandlerResponse(fields)) return fields;\n\n const user = await resolveIntelligenceUser({ runtime, request });\n if (isHandlerResponse(user)) return user;\n\n const data = await runtime.intelligence.updateMemory({\n userId: user.id,\n id: memoryId,\n ...fields,\n });\n return Response.json(data);\n } catch (error) {\n logger.error({ err: error }, \"Error updating memory\");\n return memoryErrorResponse(error, \"Failed to update memory\");\n }\n}\n\n/**\n * Retires (forgets) a memory (platform `DELETE /api/memories/:id`). Non-lossy\n * on the platform side; returns 204.\n */\nexport async function handleRemoveMemory({\n runtime,\n request,\n memoryId,\n}: MemoryMutationParams): Promise<Response> {\n if (!isIntelligenceRuntime(runtime)) {\n return errorResponse(MISSING_INTELLIGENCE_MESSAGE, 422);\n }\n try {\n const user = await resolveIntelligenceUser({ runtime, request });\n if (isHandlerResponse(user)) return user;\n\n await runtime.intelligence.removeMemory({ userId: user.id, id: memoryId });\n return new Response(null, { status: 204 });\n } catch (error) {\n logger.error({ err: error }, \"Error removing memory\");\n return memoryErrorResponse(error, \"Failed to remove memory\");\n }\n}\n"],"mappings":"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"}
|