@copilotkit/runtime
Version:
<img src="https://github.com/user-attachments/assets/0a6b64d9-e193-4940-a3f6-60334ac34084" alt="banner" style="border-radius: 12px; border: 2px solid #d6d4fa;" />
1 lines • 8.97 kB
Source Map (JSON)
{"version":3,"file":"channel-activation-config.mjs","names":[],"sources":["../../../../src/v2/runtime/core/channel-activation-config.ts"],"sourcesContent":["import type { CopilotKitIntelligence } from \"../intelligence-platform\";\n// Type-only: @copilotkit/channels is pure-ESM, so a value import would break this\n// package's CJS output (see `runtime.ts` for the same constraint).\nimport type { Channel } from \"@copilotkit/channels\";\n\n/**\n * Error thrown when a Channel activation config cannot be derived — either the\n * Intelligence API key does not carry a project id in the expected\n * `cpk-{projectId}_...` format, or the {@link Channel} is missing a `name`.\n */\nexport class ChannelConfigError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"ChannelConfigError\";\n }\n}\n\n/**\n * Resolved configuration needed to activate a single Channel against a\n * running Intelligence runtime instance.\n */\nexport interface ChannelActivationConfig {\n /** Intelligence runner websocket URL the Channel connects to. */\n wsUrl: string;\n /** Intelligence API key used to authenticate the runner connection. */\n apiKey: string;\n /** Intelligence app-api HTTP base URL (`intelligence.ɵgetApiUrl()`), forwarded\n * to the transport so the managed realtime path enables file/history parity\n * (those are HTTP-only). Without it, Channels started by the normal runtime\n * handler run with no history and no file support (OSS-476). */\n apiUrl: string;\n /** Project id parsed from {@link apiKey}. */\n projectId: number;\n /** The Channel's declared name (`createChannel({ name })`). */\n channelName: string;\n /**\n * The managed provider this Channel declares to the Intelligence gateway on\n * join, resolved from the Channel's per-Channel `provider` (e.g. `\"slack\"` or\n * `\"teams\"`), defaulting to `\"slack\"`. Named `adapter` because that is the\n * field the gateway's join payload expects; the gateway resolves the actual\n * connection for the declared provider.\n */\n adapter: string;\n /** Identifier for the runtime instance activating this Channel. */\n runtimeInstanceId: string;\n}\n\n/** Matches the `cpk-{projectId}_...` Intelligence API key format. */\nconst API_KEY_PROJECT_ID_PATTERN = /^cpk-(\\d+)_/;\n\n/**\n * Parse the project id embedded in an Intelligence API key.\n *\n * Intelligence API keys are formatted `cpk-{projectId}_{rest}` — this\n * extracts and numerically parses the `{projectId}` segment.\n *\n * @param apiKey - The Intelligence API key to parse.\n * @returns The parsed, strictly-positive, safe-integer project id.\n * @throws {ChannelConfigError} If `apiKey` does not match the expected\n * `cpk-{projectId}_...` format — a wrong/missing prefix or an absent project\n * id segment all fail the same match — or if the parsed project id is not a\n * strictly-positive safe integer.\n */\nexport function parseProjectIdFromApiKey(apiKey: string): number {\n const match = API_KEY_PROJECT_ID_PATTERN.exec(apiKey);\n if (!match) {\n // The whole API key is a `cpk-…` secret — everything after the fixed `cpk-`\n // namespace is sensitive, so a fixed-width slice (e.g. `apiKey.slice(0, 8)`)\n // would echo secret bytes for a `cpk-_short_long`-shaped key. This message is\n // logged and surfaced through `ready()`'s AggregateError, so echo NONE of the\n // key value; name only the expected format to aid diagnosis.\n throw new ChannelConfigError(\n `Could not parse a project id from the Intelligence API key — expected the ` +\n `\"cpk-{projectId}_...\" format (the key value is omitted here to avoid ` +\n `leaking secret material).`,\n );\n }\n const projectId = Number(match[1]);\n // Validate the parser's OWN output: `cpk-0_...` matches the pattern but a\n // non-positive project id would otherwise fail deep inside the launcher's\n // `assertValidChannelRealtimeScope` (which requires a positive projectId).\n // A very long digit run also matches `\\d+` but loses precision (or overflows\n // to Infinity) once coerced to `Number`, so it must be rejected too even\n // though it is `> 0` — `Number.isSafeInteger` catches both. This is the\n // parser guarding its own contract, not a channel-name replica, so it\n // belongs here. Reuse the same redaction: never echo the key value.\n if (!Number.isSafeInteger(projectId) || projectId <= 0) {\n throw new ChannelConfigError(\n `Parsed an invalid project id (${projectId}) from the Intelligence API ` +\n `key — the project id in \"cpk-{projectId}_...\" must be a positive safe ` +\n `integer (the key value is omitted here to avoid leaking secret material).`,\n );\n }\n return projectId;\n}\n\n/**\n * Derive the {@link ChannelActivationConfig} needed to activate `channel`\n * against the given Intelligence runtime configuration.\n *\n * @param args.intelligence - The Intelligence runtime client to pull the\n * runner websocket URL and auth token from.\n * @param args.channel - The Channel being activated. Must have a `name`; its\n * per-Channel `provider` selects the managed adapter declared to the gateway.\n * @param args.runtimeInstanceId - Identifier for the activating runtime\n * instance, passed through unchanged.\n * @returns The resolved {@link ChannelActivationConfig}.\n * @throws {ChannelConfigError} If the Intelligence API key does not carry a\n * parseable, strictly-positive project id, or if `channel.name` is\n * missing/empty.\n *\n * The managed provider is a PER-CHANNEL choice read from `channel.provider`, so\n * one runtime can activate a Slack-backed Channel and a Teams-backed Channel\n * side by side. When `channel.provider` is unset the config adapter defaults to\n * `\"slack\"` — an explicit, documented default, not a silent global. The SDK\n * only DECLARES this provider to the Intelligence gateway on join; the gateway\n * resolves the actual connection and is the authority on which providers it\n * accepts (it accepts only `\"slack\"` today — Teams gateway support is tracked\n * in OSS-450).\n *\n * The Channel-name FORMAT rules (lowercase kebab-case, 3–64 chars) and the\n * reserved-name rule are NOT re-checked here. Their single source of truth is\n * the `@copilotkit/channels-intelligence` launcher\n * (`assertValidChannelRealtimeScope` + `assertValidChannelNames`), which\n * validates them at activation; a malformed name surfaces as a logged `error`\n * status via {@link ChannelManager.ready} rather than an up-front throw. An\n * empty/missing name is still rejected here because that is this config's own\n * precondition (it has no name to forward at all), not a downstream replica.\n */\nexport function deriveChannelActivationConfig(args: {\n intelligence: CopilotKitIntelligence;\n channel: Channel;\n runtimeInstanceId: string;\n}): ChannelActivationConfig {\n const { intelligence, channel, runtimeInstanceId } = args;\n\n if (!channel.name) {\n throw new ChannelConfigError(\n \"Channel is missing a `name` — pass createChannel({ name }) to activate it.\",\n );\n }\n\n const channelName = channel.name;\n\n const wsUrl = intelligence.ɵgetRunnerWsUrl();\n const apiUrl = intelligence.ɵgetApiUrl();\n const apiKey = intelligence.ɵgetRunnerAuthToken();\n const projectId = parseProjectIdFromApiKey(apiKey);\n\n // Resolve the managed adapter declared to the gateway from the Channel's OWN\n // `provider` — a per-Channel choice, not a manager-wide default. When\n // `provider` is unset the adapter is the documented default `\"slack\"`; set\n // `createChannel({ provider: \"teams\" })` to declare Teams instead. The value\n // is trimmed so a padded runtime value (one that bypassed the typed union)\n // resolves to its bare provider rather than being forwarded with whitespace,\n // and a blank/whitespace-only provider falls back to `\"slack\"` (`??` alone\n // would keep `\"\"`).\n const trimmedProvider = channel.provider?.trim();\n\n return {\n wsUrl,\n apiUrl,\n apiKey,\n projectId,\n channelName,\n adapter: trimmedProvider ? trimmedProvider : \"slack\",\n runtimeInstanceId,\n };\n}\n"],"mappings":";;;;;;;AAUA,IAAa,qBAAb,cAAwC,MAAM;CAC5C,YAAY,SAAiB;AAC3B,QAAM,QAAQ;AACd,OAAK,OAAO;;;;AAmChB,MAAM,6BAA6B;;;;;;;;;;;;;;AAenC,SAAgB,yBAAyB,QAAwB;CAC/D,MAAM,QAAQ,2BAA2B,KAAK,OAAO;AACrD,KAAI,CAAC,MAMH,OAAM,IAAI,mBACR,6KAGD;CAEH,MAAM,YAAY,OAAO,MAAM,GAAG;AASlC,KAAI,CAAC,OAAO,cAAc,UAAU,IAAI,aAAa,EACnD,OAAM,IAAI,mBACR,iCAAiC,UAAU,6KAG5C;AAEH,QAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoCT,SAAgB,8BAA8B,MAIlB;CAC1B,MAAM,EAAE,cAAc,SAAS,sBAAsB;AAErD,KAAI,CAAC,QAAQ,KACX,OAAM,IAAI,mBACR,6EACD;CAGH,MAAM,cAAc,QAAQ;CAE5B,MAAM,QAAQ,aAAa,iBAAiB;CAC5C,MAAM,SAAS,aAAa,YAAY;CACxC,MAAM,SAAS,aAAa,qBAAqB;CACjD,MAAM,YAAY,yBAAyB,OAAO;CAUlD,MAAM,kBAAkB,QAAQ,UAAU,MAAM;AAEhD,QAAO;EACL;EACA;EACA;EACA;EACA;EACA,SAAS,kBAAkB,kBAAkB;EAC7C;EACD"}