@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 • 89.1 kB
Source Map (JSON)
{"version":3,"file":"channel-manager.cjs","names":["MCPMiddleware","INTELLIGENCE_MEMORY_GRANT_HEADER","INTELLIGENCE_USER_ID_HEADER","AbstractAgent","EMPTY","resolveLearningContainerId","EventType","deriveChannelActivationConfig","ChannelConfigError","telemetry"],"sources":["../../../../src/v2/runtime/core/channel-manager.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport {\n ChannelConfigError,\n deriveChannelActivationConfig,\n} from \"./channel-activation-config\";\nimport type { ChannelActivationConfig } from \"./channel-activation-config\";\nimport type { CopilotKitIntelligence } from \"../intelligence-platform\";\nimport { telemetry } from \"../telemetry\";\nimport type { AnalyticsEvents } from \"../telemetry\";\nimport { AbstractAgent, EventType } from \"@ag-ui/client\";\nimport type {\n AgentSubscriber,\n BaseEvent,\n Message,\n RunAgentParameters,\n RunAgentResult,\n} from \"@ag-ui/client\";\nimport { EMPTY } from \"rxjs\";\nimport { MCPMiddleware } from \"@ag-ui/mcp-middleware\";\nimport type { AgentRunner } from \"../runner/agent-runner\";\nimport {\n INTELLIGENCE_MEMORY_GRANT_HEADER,\n INTELLIGENCE_USER_ID_HEADER,\n} from \"../intelligence-platform/client\";\n// Type-only: @copilotkit/channels is pure-ESM, so a value import would break this\n// package's CJS output (see `core/runtime.ts` and `channel-activation-config.ts`\n// for the same constraint).\nimport type {\n Channel,\n ReplyContinuationOptions,\n ResolvedChannelMemory,\n} from \"@copilotkit/channels\";\nimport type { CopilotRuntimeLearningConfig } from \"./learning\";\nimport { resolveLearningContainerId } from \"./learning\";\n\n/**\n * Lifecycle status of a single Channel activation, or of the manager overall.\n *\n * - `connecting`: activation in flight, not yet settled.\n * - `online`: activation resolved, the managed session can currently send, AND\n * the gateway did not report the Channel as missing a managed provider. A drop\n * moves the Channel to `reconnecting` (not `online`); a successful rejoin\n * restores `online`.\n * - `setup_required`: the Channel is declared but has no managed provider yet —\n * a valid degraded state, not a failure. Reached when the gateway reports the\n * provider as unattached/disabled/undeclared on the control join reply (see\n * {@link ChannelLegs}), or when the activation engine throws a\n * `SETUP_REQUIRED` error.\n *\n * NOTE: between the 2026-07-29 realtime-boundary cutover and the introduction\n * of {@link ChannelLegs}, this state had NO producer — the engine stopped\n * classifying it and nothing else set it, so a Channel with no Slack app at\n * all reported `online`. Do not reintroduce a code path that describes\n * `setup_required` without one that can actually emit it.\n * - `reconnecting`: the managed session dropped and Phoenix is retrying — not\n * currently sendable. The manager does NOT re-activate (reconnection is\n * delegated to the Phoenix connection layer); it only reflects the health the\n * session reports via its `onStateChange` observer.\n * - `stopped`: {@link ChannelManager.stop} has torn the Channel down.\n * - `error`: activation rejected with a non-setup error, or a previously-online\n * control link gave up reconnecting after its bounded reconnect window.\n *\n * A Channel may carry developer-supplied direct adapters alongside the managed\n * Intelligence adapter. The managed engine owns the shared Channel lifecycle;\n * each adapter still receives only its own ingress and sends only its own\n * provider output.\n */\nexport type ChannelStatus =\n | \"connecting\"\n | \"online\"\n | \"setup_required\"\n | \"reconnecting\"\n | \"stopped\"\n | \"error\";\n\n/**\n * Managed provider attachment state for one Channel, as reported by the gateway\n * on the control join reply.\n *\n * `unknown` is this package's own value for \"the gateway did not tell us\" — a\n * gateway predating the provider-state contract, one whose lookup failed, or a\n * non-gateway handle. It must never be read as \"no provider attached\".\n */\nexport type ChannelProviderLeg =\n | \"attached\"\n | \"unhealthy\"\n | \"not_attached\"\n | \"disabled\"\n | \"channel_not_declared\"\n | \"unknown\";\n\n/**\n * The two independent things that have to be true for a managed Channel to\n * work, reported separately so a caller can assert the one it cares about.\n *\n * `status` is the fold of the two and matches this Channel's entry in\n * {@link ChannelsControl.status}'s `channels` map.\n *\n * The legs exist because they are genuinely separable: the control socket can be\n * joined and sendable while no Slack/Teams app is bound to the Channel at all.\n * Before they were split, `overall: \"online\"` proved only the socket, and\n * onboarding guidance used it to certify end-to-end success.\n */\nexport interface ChannelLegs {\n /** Fold of {@link transport} and {@link provider}. */\n status: ChannelStatus;\n /** Runtime ⇄ Gateway control socket for this Channel. */\n transport: ChannelStatus;\n /** Whether a managed provider is bound to this Channel. */\n provider: ChannelProviderLeg;\n}\n\n/**\n * Recognised provider states, used to validate what crosses the seam.\n *\n * Deliberately duplicates `PROVIDER_STATES` in\n * `@copilotkit/channels-intelligence`'s `realtime-gateway.ts` rather than\n * importing it: this package must not take a static dependency on\n * channels-intelligence (it is reached only through a dynamic import), so the\n * `providerStates` seam is duck-typed as `Record<string, string>`.\n *\n * A state added there needs adding here too, plus a `case` in\n * {@link foldChannelLegs}. Until both land it fails OPEN — an unrecognised state\n * becomes `unknown` and the Channel keeps its transport-derived status, rather\n * than being wrongly certified or condemned.\n */\nconst PROVIDER_LEGS: ReadonlySet<string> = new Set<ChannelProviderLeg>([\n \"attached\",\n \"unhealthy\",\n \"not_attached\",\n \"disabled\",\n \"channel_not_declared\",\n]);\n\n/**\n * Fold a Channel's transport and provider legs into its single status.\n *\n * The transport leg dominates whenever it is not `online`: while the control\n * socket is connecting, retrying, stopped, or failed, whatever the gateway last\n * said about the provider is stale or irrelevant — the Channel cannot serve a\n * turn either way, and reporting `setup_required` for a Channel that is actually\n * mid-reconnect would hide the outage.\n *\n * Once the transport is `online` the provider leg decides, which is the whole\n * point of the split: a joined socket with no provider bound is\n * `setup_required`, not `online`.\n *\n * `unknown` keeps the transport-derived answer. That is what makes an older\n * gateway (or a gateway whose lookup failed) behave exactly as it did before\n * provider states existed, instead of turning every Channel into\n * `setup_required`.\n *\n * @param transport - Control-socket status for the Channel.\n * @param provider - Reported provider attachment state.\n * @returns The folded Channel status.\n */\nexport function foldChannelLegs(\n transport: ChannelStatus,\n provider: ChannelProviderLeg,\n): ChannelStatus {\n if (transport !== \"online\") {\n return transport;\n }\n switch (provider) {\n case \"attached\":\n case \"unknown\":\n return \"online\";\n case \"unhealthy\":\n return \"error\";\n case \"not_attached\":\n case \"disabled\":\n case \"channel_not_declared\":\n return \"setup_required\";\n }\n}\n\n/**\n * The lifecycle control surface a Channel host uses to drive and observe\n * managed Channel activation.\n */\nexport interface ChannelsControl {\n /**\n * Resolve once every declared Channel has settled its ACTIVATION — that is,\n * each Channel either activated or failed to. Rejects if any Channel failed to\n * activate, or — when `timeoutMs` is given — if the whole set has not settled\n * in time.\n *\n * Readiness is about activation, NOT about provider health: a Channel whose\n * transport joined but whose provider leg is `unhealthy` folds to a status of\n * `error` (see {@link foldChannelLegs}) while its activation settled normally.\n * So `ready()` resolving and `status().overall === \"error\"` can both be true at\n * once, by design — provider attachment is the Gateway's answer to a question\n * asked after activation, and it can change at any later rejoin. Assert\n * end-to-end reachability with {@link ChannelsControl.status}, not here.\n */\n ready(opts?: { timeoutMs?: number }): Promise<void>;\n /**\n * Snapshot the overall status, the per-Channel status map, and the per-Channel\n * transport/provider legs.\n *\n * `overall === \"online\"` does NOT by itself prove a Channel can receive\n * provider traffic unless the provider leg is `attached`: read `detail` when\n * you need to assert that a Channel is genuinely reachable from Slack/Teams,\n * because a `provider` of `unknown` leaves `status` transport-derived.\n */\n status(): {\n overall: ChannelStatus;\n channels: Record<string, ChannelStatus>;\n detail: Record<string, ChannelLegs>;\n };\n /** Tear down every activated Channel. Idempotent. */\n stop(): Promise<void>;\n}\n\ninterface ChannelRunErrorDetails {\n readonly category: \"validation\";\n readonly provider: \"slack\" | \"teams\";\n readonly operation: string;\n readonly effectKind: string;\n readonly providerCode: \"invalid_arguments\" | \"invalid_blocks\";\n readonly validationMessages: readonly string[];\n readonly retryable: false;\n readonly deliveryId: string;\n}\n\n/**\n * Signals that a declared Channel cannot be activated because no managed\n * provider exists for it yet. The engine throws this (or any error whose\n * `code === \"SETUP_REQUIRED\"`) to move a Channel to `setup_required` rather\n * than `error` — a declared-but-unprovisioned Channel is a valid degraded\n * state, not a failure.\n */\nexport class ChannelSetupRequiredError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"ChannelSetupRequiredError\";\n }\n}\n\n/**\n * The activation engine: given a resolved {@link ChannelActivationConfig} and\n * the declared {@link Channel}, bring the Channel online and return its handle.\n * Injected in tests (a fake engine); defaults to the Realtime Gateway launcher.\n */\nexport type ActivateChannelEngine = (\n config: ChannelActivationConfig,\n channel: Channel,\n) => Promise<ChannelsHandle>;\n\n/**\n * Minimal structural view of the `@copilotkit/channels-intelligence`\n * `ChannelsHandle`. Declared locally (not imported) because the runtime is a\n * CJS package that must not take a static dependency on the pure-ESM\n * channels-intelligence package — the default engine reaches its launcher\n * through a dynamic `import()` instead. The manager only ever needs `stop()`.\n */\nexport interface ChannelsHandle {\n /** Activation metadata declared to Intelligence. Unused by the manager. */\n metadata: unknown;\n /** Stop the underlying Channel(s) and release transports. */\n stop(): Promise<void>;\n /**\n * Optional seam: register a callback the handle fires when its managed\n * session drops. Retained as a per-episode drop breadcrumb; the manager drives\n * status from {@link ChannelsHandle.onStateChange} instead. Present on the\n * Realtime Gateway launcher handle; optional for non-gateway/test handles.\n */\n onClose?(cb: () => void): void;\n /**\n * Optional seam: register a connection-health observer the handle fires as its\n * managed session moves between `online` (sendable), `reconnecting` (dropped,\n * Phoenix retrying), and `gave_up` (dead after the bounded reconnect window).\n * The manager uses this to keep {@link ChannelManager.status} honest — it does\n * NOT re-activate on a drop (reconnection is delegated to the Phoenix\n * connection layer; see {@link ChannelManager}). Optional so non-gateway or\n * test handles that do not implement it are always invoked as\n * `handle.onStateChange?.(cb)`.\n */\n onStateChange?(\n cb: (\n state: \"online\" | \"reconnecting\" | \"gave_up\",\n detail?: { reason?: string; code?: string },\n ) => void,\n ): void;\n /**\n * Optional seam: managed provider attachment state per declared Channel, as\n * reported on the newest gateway control join reply.\n *\n * A getter, so each read reflects the current join reply — the gateway's join\n * hooks re-fire on every auto-rejoin, so a Channel provisioned while the\n * runtime was disconnected is picked up without re-activating.\n *\n * `undefined` (or an absent method) means \"not reported\", NOT \"no provider\".\n * A gateway predating this contract, a gateway whose database read failed, and\n * a non-gateway/test handle all land here, and all must fall back to\n * transport-only status rather than claim a Channel is unprovisioned.\n */\n providerStates?(): Readonly<Record<string, string>> | undefined;\n}\n\n/** Constructor arguments for {@link ChannelManager}. */\nexport interface ChannelManagerArgs {\n /** The Intelligence runtime client the activation config is derived from. */\n intelligence: CopilotKitIntelligence;\n /** The declared framework Channels to activate. */\n channels: Channel[];\n /** Standard runtime AgentRunner used by managed Channel executions. */\n runner?: AgentRunner;\n /** Selects one stable Learning Container ID for each Channel run. */\n learning?: CopilotRuntimeLearningConfig;\n /** Standard thread-lock TTL forwarded to Channel AgentRunner heartbeats. */\n lockTtlSeconds?: number;\n /** Standard thread-lock heartbeat cadence used by Channel AgentRunner calls. */\n lockHeartbeatIntervalSeconds?: number;\n /** Must match web Intelligence runs so channel + HTTP share the same lock key. */\n lockKeyPrefix?: string;\n /**\n * Activation engine. Defaults to a wrapper over the channels-intelligence\n * Realtime Gateway launcher (`startChannelsOverRealtimeGateway`), reached via\n * dynamic import so this CJS package keeps no static ESM dependency.\n */\n activateChannel?: ActivateChannelEngine;\n /** Mint a runtime instance id per Channel. Defaults to `rti_{uuid-no-dashes}`. */\n mintRuntimeInstanceId?: () => string;\n /** Diagnostic sink. Forwarded to the launcher/transport when the default\n * activation engine is used, so transport-level drops surface in the managed\n * path (not just activation-level events). */\n log?: (msg: string, meta?: unknown) => void;\n /**\n * Initial delay (ms) before a \"still down\" log while a managed session is\n * disconnected. Later reminders back off exponentially to a 15-minute cap,\n * keeping a prolonged outage visible without flooding logs. Injectable so\n * tests can use a shorter first delay. Default 30000.\n */\n reconnectLogIntervalMs?: number;\n /** Per-handle deadline (ms) for `handle.stop()` during {@link ChannelManager.stop}\n * so a wedged stop can't hang SIGTERM shutdown. Default 5000. */\n stopHandleTimeoutMs?: number;\n}\n\n/** Per-Channel mutable activation entry tracked by the manager. */\ninterface ChannelEntry {\n status: ChannelStatus;\n /** Resolves on `online`/`setup_required`; rejects on `error`. Awaited by `ready`. */\n readonly settled: Promise<void>;\n handle?: ChannelsHandle;\n /**\n * Whether {@link ChannelManager.stopEntry} has already stopped `handle`. Gates\n * the single-stop guarantee: the success settle handler and `stop()` can both\n * reach the same entry in the same tick, but the handle is torn down at most\n * once.\n */\n handleStopped: boolean;\n /** Epoch ms this outage episode began; unset while the session is healthy. */\n downSince?: number;\n /** Cause of THIS outage episode, replayed on each \"still down\" reminder. */\n downCause?: string;\n /** Next \"still down\" logger for this outage; cleared on recovery/teardown. */\n reconnectLogTimer?: ReturnType<typeof setTimeout>;\n /** Delay before the next reminder; doubles after each emitted reminder. */\n reconnectLogDelayMs?: number;\n /** Next retry after a transient initial activation failure. */\n activationRetryTimer?: ReturnType<typeof setTimeout>;\n /** Delay before the next activation retry; doubles after each failed attempt. */\n activationRetryDelayMs?: number;\n /** Reject the retry wrapper when teardown cancels a pending retry. */\n cancelActivationRetry?: () => void;\n}\n\n/**\n * Runtime installs this pure-ESM package as a direct dependency, but the\n * specifier must stay non-literal so it never becomes a static dependency of\n * the runtime's CJS build. The packed-consumer contract is enforced by\n * `scripts/release/verify-runtime-package.ts`.\n */\nconst CHANNELS_INTELLIGENCE_SPECIFIER = \"@copilotkit/channels-intelligence\";\n\n/**\n * Structural view of the `@copilotkit/channels-intelligence` module surface the\n * default engine consumes. Declared locally (not imported) for the same\n * CJS/ESM-boundary reason the {@link ChannelsHandle} view is.\n */\nexport interface ChannelsIntelligenceModule {\n startChannelsOverRealtimeGateway: (\n channels: Channel[],\n opts: {\n wsUrl: string;\n apiKey: string;\n scope: { projectId: number; channelName: string };\n runtimeInstanceId: string;\n /** Optional per-Channel override for managed tool-call visibility. */\n showToolStatus?: boolean;\n /** Optional per-Channel tuning for continuation messages on long replies. */\n replyContinuation?: ReplyContinuationOptions;\n /** Intelligence app-api HTTP base URL, forwarded to the transport so the\n * managed realtime path enables file/history parity (HTTP-only) — OSS-476. */\n appApiBaseUrl?: string;\n /** Diagnostic sink forwarded to the launcher/transport so transport-level\n * drop diagnostics (e.g. a version-skew missing-leaseToken outage) are not\n * silent in the managed path. */\n log?: (msg: string, meta?: unknown) => void;\n runCanonical(args: {\n agent: AbstractAgent;\n deliveryId: string;\n signal?: AbortSignal;\n threadId: string;\n runId: string;\n userId: string;\n agentId: string;\n tools: readonly {\n name: string;\n description: string;\n parameters: Record<string, unknown>;\n }[];\n context: readonly { description: string; value: string }[];\n persistedInputMessages: Message[];\n execute(\n subscriber: AgentSubscriber,\n canonicalRun?: { threadId: string; runId: string },\n ): Promise<{\n iterations: number;\n interrupted: boolean;\n deliveryError?: unknown;\n }>;\n }): Promise<{\n iterations: number;\n interrupted: boolean;\n deliveryError?: unknown;\n }>;\n loadHistory(args: {\n deliveryId: string;\n threadId: string;\n appUserId: string;\n }): Promise<Message[]>;\n },\n ) => Promise<ChannelsHandle>;\n}\n\n/**\n * Default engine: wrap the channels-intelligence Realtime Gateway launcher.\n *\n * The module is reached through an injectable importer that defaults to a\n * dynamic `import()` of a non-literal specifier, so the pure-ESM\n * `@copilotkit/channels-intelligence` never becomes a static dependency of this\n * CJS package (mirrors the runtime's other channels seams). The `import`\n * seam is a parameter purely so this function's config→opts mapping and its\n * module-not-found / generic-error branches are unit-testable WITHOUT the real\n * package installed; production always uses the default importer.\n *\n * Passes NO `org`/`channelId` — the launcher's realtime scope treats them as\n * optional.\n *\n * @param config - Resolved activation config for the Channel.\n * @param channel - The Channel to activate.\n * @param importChannelsIntelligence - Test seam; loads the channels-intelligence\n * module. Defaults to a dynamic import of the real package.\n * @param log - Optional diagnostic sink forwarded to the launcher/transport so\n * transport-level drop diagnostics are not silent in the managed path.\n * @returns The launcher's {@link ChannelsHandle}.\n */\nexport async function defaultActivateChannel(\n config: ChannelActivationConfig,\n channel: Channel,\n importChannelsIntelligence: () => Promise<ChannelsIntelligenceModule> = () =>\n import(\n CHANNELS_INTELLIGENCE_SPECIFIER\n ) as Promise<ChannelsIntelligenceModule>,\n log?: (msg: string, meta?: unknown) => void,\n services?: {\n runner: AgentRunner;\n intelligence: CopilotKitIntelligence;\n lockTtlSeconds?: number;\n lockHeartbeatIntervalSeconds?: number;\n lockKeyPrefix?: string;\n learning?: CopilotRuntimeLearningConfig;\n },\n): Promise<ChannelsHandle> {\n let mod: ChannelsIntelligenceModule;\n try {\n mod = await importChannelsIntelligence();\n } catch (err) {\n if (isModuleNotFound(err)) {\n throw new Error(\n \"Managed Channels require '@copilotkit/channels-intelligence' to be installed. Add it to your app's dependencies.\",\n { cause: err },\n );\n }\n throw err;\n }\n if (!services) {\n throw new Error(\n \"Managed Channels require the runtime AgentRunner and Intelligence client\",\n );\n }\n return mod.startChannelsOverRealtimeGateway([channel], {\n wsUrl: config.wsUrl,\n apiKey: config.apiKey,\n scope: { projectId: config.projectId, channelName: config.channelName },\n runtimeInstanceId: config.runtimeInstanceId,\n ...(config.showToolStatus !== undefined\n ? { showToolStatus: config.showToolStatus }\n : {}),\n ...(config.replyContinuation !== undefined\n ? { replyContinuation: config.replyContinuation }\n : {}),\n // Forward the app-api HTTP base URL so the transport wires file/history\n // (HTTP-only) on the NORMAL managed path — without this, Channels started by\n // the CopilotRuntime handler run with no history/file support (OSS-476).\n appApiBaseUrl: config.apiUrl,\n // Forward the manager's diagnostic sink down to the launcher/transport so a\n // transport-level drop (e.g. a version-skew missing-leaseToken outage) is\n // observable in the managed path, not just activation-level events.\n ...(log ? { log } : {}),\n runCanonical: (args) =>\n runCanonicalChannelAgent(\n services.runner,\n services.intelligence,\n services.lockTtlSeconds ?? 20,\n services.lockHeartbeatIntervalSeconds ?? 15,\n args,\n services.lockKeyPrefix,\n services.learning,\n ),\n loadHistory: async ({ deliveryId, threadId, appUserId }) => {\n const history = await services.intelligence.getThreadMessages({\n threadId,\n userId: appUserId,\n channelDeliveryId: deliveryId,\n });\n return Promise.all(\n history.messages.map((message) =>\n toAgentMessage(message, services.intelligence),\n ),\n );\n },\n });\n}\n\ninterface CanonicalRunArgs {\n agent: AbstractAgent;\n deliveryId: string;\n signal?: AbortSignal;\n threadId: string;\n runId: string;\n userId: string;\n memory?: ResolvedChannelMemory;\n agentId: string;\n tools: readonly {\n name: string;\n description: string;\n parameters: Record<string, unknown>;\n }[];\n context: readonly { description: string; value: string }[];\n persistedInputMessages: Message[];\n execute(\n subscriber: AgentSubscriber,\n canonicalRun?: { threadId: string; runId: string },\n ): Promise<{\n iterations: number;\n interrupted: boolean;\n deliveryError?: unknown;\n }>;\n}\n\n/** Attach grant-scoped Intelligence Memory tools to one isolated Channel agent. */\nexport function attachChannelMemory(\n agent: AbstractAgent,\n intelligence: CopilotKitIntelligence,\n memory: ResolvedChannelMemory | undefined,\n): void {\n if (!memory) return;\n const middlewareAgent = agent as AbstractAgent & {\n use?: (middleware: unknown) => void;\n };\n if (typeof middlewareAgent.use !== \"function\") {\n const error = new Error(\n \"Channel Memory requires an agent with middleware support\",\n ) as Error & { code?: string };\n error.name = \"ChannelMemoryAgentUnsupportedError\";\n error.code = \"channel_memory_agent_unsupported\";\n throw error;\n }\n middlewareAgent.use(\n new MCPMiddleware([\n {\n type: \"http\",\n url: `${intelligence.ɵgetApiUrl()}/mcp`,\n serverId: \"intelligence\",\n headers: {\n Authorization: `Bearer ${intelligence.ɵgetApiKey()}`,\n [INTELLIGENCE_MEMORY_GRANT_HEADER]: JSON.stringify(memory.grant),\n ...(memory.user\n ? { [INTELLIGENCE_USER_ID_HEADER]: memory.user.id }\n : {}),\n },\n },\n ]),\n );\n}\n\n/** One outer agent that lets the standard runner own the whole local tool loop. */\nclass ChannelOuterAgent extends AbstractAgent {\n constructor(\n private readonly inner: AbstractAgent,\n private readonly canonicalThreadId: string,\n private readonly executeLoop: CanonicalRunArgs[\"execute\"],\n ) {\n super({\n threadId: inner.threadId,\n initialMessages: inner.messages,\n initialState: inner.state,\n ...(inner.agentId ? { agentId: inner.agentId } : {}),\n });\n }\n\n run(): ReturnType<AbstractAgent[\"run\"]> {\n return EMPTY;\n }\n\n override async runAgent(\n parameters?: RunAgentParameters,\n subscriber?: AgentSubscriber,\n ): Promise<RunAgentResult> {\n if (!parameters?.runId) {\n throw new Error(\"Canonical Channel run requires a runId\");\n }\n const result = await this.executeLoop(subscriber ?? {}, {\n threadId: this.canonicalThreadId,\n runId: parameters.runId,\n });\n return { result, newMessages: [] };\n }\n\n override abortRun(): void {\n this.inner.abortRun();\n }\n}\n\n/** Drive one public Channel run through the runtime's existing AgentRunner. */\nasync function runCanonicalChannelAgent(\n runner: AgentRunner,\n intelligence: CopilotKitIntelligence,\n lockTtlSeconds: number,\n lockHeartbeatIntervalSeconds: number,\n args: CanonicalRunArgs,\n lockKeyPrefix?: string,\n learning?: CopilotRuntimeLearningConfig,\n): Promise<{\n iterations: number;\n interrupted: boolean;\n deliveryError?: unknown;\n}> {\n const learningContainerId = await resolveLearningContainerId(learning, {\n surface: \"channel\",\n threadId: args.threadId,\n runId: args.runId,\n agentId: args.agentId,\n userId: args.userId,\n deliveryId: args.deliveryId,\n });\n const lock = await intelligence.ɵacquireThreadLock({\n threadId: args.threadId,\n runId: args.runId,\n userId: args.userId,\n agentId: args.agentId,\n channelDeliveryId: args.deliveryId,\n ...(learningContainerId !== undefined ? { learningContainerId } : {}),\n ttlSeconds: lockTtlSeconds,\n ...(lockKeyPrefix !== undefined ? { lockKeyPrefix } : {}),\n });\n const canonicalThreadId = lock.threadId;\n const canonicalRunId = lock.runId;\n let result = { iterations: 0, interrupted: false };\n attachChannelMemory(args.agent, intelligence, args.memory);\n const outer = new ChannelOuterAgent(\n args.agent,\n canonicalThreadId,\n async (subscriber, canonicalRun) => {\n result = await args.execute(subscriber, canonicalRun);\n return result;\n },\n );\n let stopPromise: Promise<boolean | undefined> | undefined;\n let heartbeatError: unknown;\n let heartbeatTimer: ReturnType<typeof setInterval> | undefined;\n const stopCanonicalRun = (): void => {\n stopPromise ??= Promise.resolve()\n .then(() =>\n runner.stop({\n threadId: canonicalThreadId,\n runId: canonicalRunId,\n }),\n )\n .catch(() => false);\n };\n const abortCanonicalRun = (): void => {\n try {\n args.agent.abortRun();\n } catch {\n // The exact runner stop remains the authoritative cancellation path.\n }\n stopCanonicalRun();\n };\n args.signal?.addEventListener(\"abort\", abortCanonicalRun, { once: true });\n heartbeatTimer = setInterval(() => {\n intelligence\n .ɵrenewThreadLock({\n threadId: canonicalThreadId,\n runId: canonicalRunId,\n ttlSeconds: lockTtlSeconds,\n ...(lockKeyPrefix !== undefined ? { lockKeyPrefix } : {}),\n })\n .catch((error: unknown) => {\n if (heartbeatTimer === undefined) return;\n clearInterval(heartbeatTimer);\n heartbeatTimer = undefined;\n heartbeatError = error;\n try {\n args.agent.abortRun();\n } catch {\n // The runner stop below remains the authoritative cancellation path.\n }\n stopCanonicalRun();\n });\n }, lockHeartbeatIntervalSeconds * 1_000);\n heartbeatTimer.unref?.();\n\n try {\n await new Promise<void>((resolve, reject) => {\n let terminalError:\n | (Error & {\n code?: string;\n category?: string;\n provider?: string;\n operation?: string;\n effectKind?: string;\n providerCode?: string;\n validationMessages?: readonly string[];\n retryable?: boolean;\n deliveryId?: string;\n details?: unknown;\n })\n | undefined;\n const stream = runner.run({\n threadId: canonicalThreadId,\n agent: outer,\n input: {\n threadId: canonicalThreadId,\n runId: canonicalRunId,\n messages: args.agent.messages,\n state: args.agent.state,\n tools: [...args.tools],\n context: [...args.context],\n forwardedProps: undefined,\n },\n persistedInputMessages: args.persistedInputMessages,\n });\n stream.subscribe({\n next: (event: BaseEvent) => {\n if (event.type !== EventType.RUN_ERROR || terminalError) return;\n const message =\n \"message\" in event && typeof event.message === \"string\"\n ? event.message\n : \"Canonical Channel agent run failed\";\n const details = safeChannelRunErrorDetails(event);\n terminalError = new Error(\n message,\n details ? { cause: details } : undefined,\n );\n terminalError.name = \"ChannelCanonicalRunError\";\n if (\n \"code\" in event &&\n typeof event.code === \"string\" &&\n event.code.length > 0\n ) {\n terminalError.code = event.code;\n }\n if (details) {\n terminalError.category = details.category;\n terminalError.provider = details.provider;\n terminalError.operation = details.operation;\n terminalError.effectKind = details.effectKind;\n terminalError.providerCode = details.providerCode;\n terminalError.validationMessages = details.validationMessages;\n terminalError.retryable = details.retryable;\n terminalError.deliveryId = details.deliveryId;\n terminalError.details = details;\n }\n },\n error: reject,\n complete: () => {\n if (terminalError) {\n reject(terminalError);\n } else {\n resolve();\n }\n },\n });\n if (args.signal?.aborted) {\n abortCanonicalRun();\n }\n });\n } finally {\n args.signal?.removeEventListener(\"abort\", abortCanonicalRun);\n if (heartbeatTimer !== undefined) {\n clearInterval(heartbeatTimer);\n heartbeatTimer = undefined;\n }\n // Always release the product thread lock from the Runtime side. Gateway\n // may also release on terminal AG-UI ingestion; cleanup is idempotent and\n // covers runner paths that never stream terminal events (or lose them).\n await intelligence\n .ɵcleanupThreadLock({\n threadId: canonicalThreadId,\n runId: canonicalRunId,\n })\n .catch(() => undefined);\n }\n\n if (heartbeatError !== undefined) {\n await stopPromise;\n throw heartbeatError;\n }\n return result;\n}\n\nfunction safeChannelRunErrorDetails(\n event: BaseEvent,\n): ChannelRunErrorDetails | undefined {\n if (\n !(\"details\" in event) ||\n typeof event.details !== \"object\" ||\n event.details === null ||\n Array.isArray(event.details)\n ) {\n return undefined;\n }\n const details = event.details as Record<string, unknown>;\n const allowed = new Set([\n \"category\",\n \"provider\",\n \"operation\",\n \"effectKind\",\n \"providerCode\",\n \"validationMessages\",\n \"retryable\",\n \"deliveryId\",\n ]);\n if (\n !Object.keys(details).every((field) => allowed.has(field)) ||\n details.category !== \"validation\" ||\n (details.provider !== \"slack\" && details.provider !== \"teams\") ||\n !boundedString(details.operation, 80) ||\n !boundedString(details.effectKind, 80) ||\n (details.providerCode !== \"invalid_arguments\" &&\n details.providerCode !== \"invalid_blocks\") ||\n details.retryable !== false ||\n !boundedString(details.deliveryId, 512) ||\n !Array.isArray(details.validationMessages) ||\n details.validationMessages.length > 5 ||\n !details.validationMessages.every(\n (validationMessage) =>\n typeof validationMessage === \"string\" &&\n validationMessage.length <= 256 &&\n validationMessage.startsWith(\"invalid field at /\"),\n )\n ) {\n return undefined;\n }\n return details as unknown as ChannelRunErrorDetails;\n}\n\nfunction boundedString(value: unknown, maxLength: number): value is string {\n return (\n typeof value === \"string\" && value.length > 0 && value.length <= maxLength\n );\n}\n\n/** Convert canonical Intelligence history into AG-UI messages. */\nasync function toAgentMessage(\n message: {\n id: string;\n role: string;\n activityType?: string;\n content?: unknown;\n toolCalls?: Array<{ id: string; name: string; args: string }>;\n toolCallId?: string;\n },\n intelligence: CopilotKitIntelligence,\n): Promise<Message> {\n const content = await hydrateManagedContent(message.content, intelligence);\n return {\n id: message.id,\n role: message.role as Message[\"role\"],\n content: content ?? \"\",\n ...(message.activityType ? { activityType: message.activityType } : {}),\n ...(message.toolCalls\n ? {\n toolCalls: message.toolCalls.map((call) => ({\n id: call.id,\n type: \"function\" as const,\n function: { name: call.name, arguments: call.args },\n })),\n }\n : {}),\n ...(message.toolCallId ? { toolCallId: message.toolCallId } : {}),\n } as Message;\n}\n\n/** Resolves managed asset references only at the authorized Runtime boundary. */\nasync function hydrateManagedContent(\n content: unknown,\n intelligence: CopilotKitIntelligence,\n): Promise<unknown> {\n if (Array.isArray(content)) {\n return Promise.all(\n content.map(async (part) => {\n if (\n typeof part !== \"object\" ||\n part === null ||\n !(\"source\" in part) ||\n typeof part.source !== \"object\" ||\n part.source === null ||\n !(\"value\" in part.source) ||\n typeof part.source.value !== \"string\" ||\n !part.source.value.startsWith(\"cpki-asset://\")\n ) {\n return part;\n }\n const assetId = part.source.value.slice(\"cpki-asset://\".length);\n const asset = await intelligence.ɵgetManagedChannelAsset(assetId);\n return {\n ...part,\n source: {\n type: \"data\",\n value: Buffer.from(asset.bytes).toString(\"base64\"),\n mimeType:\n asset.mimeType ??\n (\"mimeType\" in part.source &&\n typeof part.source.mimeType === \"string\"\n ? part.source.mimeType\n : \"application/octet-stream\"),\n },\n };\n }),\n );\n }\n\n if (\n typeof content === \"object\" &&\n content !== null &&\n \"assetId\" in content &&\n typeof content.assetId === \"string\"\n ) {\n const asset = await intelligence.ɵgetManagedChannelAsset(content.assetId);\n return {\n ...content,\n source: {\n type: \"data\",\n value: Buffer.from(asset.bytes).toString(\"base64\"),\n mimeType:\n asset.mimeType ??\n (\"mimeType\" in content && typeof content.mimeType === \"string\"\n ? content.mimeType\n : \"application/octet-stream\"),\n },\n };\n }\n\n return content;\n}\n\n/** Whether `err` signals a missing managed provider rather than a hard failure. */\nfunction isSetupRequired(err: unknown): boolean {\n return (\n err instanceof ChannelSetupRequiredError ||\n (typeof err === \"object\" &&\n err !== null &&\n (err as { code?: unknown }).code === \"SETUP_REQUIRED\")\n );\n}\n\n/** Whether a failed initial activation can recover without new configuration. */\nfunction isRetryableActivationError(err: unknown): boolean {\n if (typeof err !== \"object\" || err === null) {\n return false;\n }\n const value = err as { code?: unknown; retryable?: unknown };\n return (\n (value.code === \"GATEWAY_UNREACHABLE\" ||\n value.code === \"GATEWAY_JOIN_FAILED\") &&\n value.retryable === true\n );\n}\n\n/**\n * Whether `err` is a Node/runtime module-resolution failure — i.e. the error\n * a dynamic `import()` throws when the target package is not installed.\n * Exported so the friendly-error path in {@link defaultActivateChannel} can be\n * unit-tested without forcing a real failing import.\n */\nexport function isModuleNotFound(err: unknown): boolean {\n if (typeof err !== \"object\" || err === null) {\n return false;\n }\n const code = (err as { code?: unknown }).code;\n return code === \"ERR_MODULE_NOT_FOUND\" || code === \"MODULE_NOT_FOUND\";\n}\n\n/** Default deadline (ms) for a single `handle.stop()` during teardown. */\nconst DEFAULT_STOP_HANDLE_TIMEOUT_MS = 5_000;\n\n/** First delay (ms) before logging that a dropped session is still down. */\nconst DEFAULT_RECONNECT_LOG_INTERVAL_MS = 30_000;\n\n/** Longest delay (ms) between reminders during one continuous outage. */\nconst DEFAULT_RECONNECT_LOG_MAX_INTERVAL_MS = 15 * 60_000;\n\n/** First delay (ms) before retrying a transient initial activation failure. */\nconst DEFAULT_ACTIVATION_RETRY_DELAY_MS = 1_000;\n\n/** Longest delay (ms) between transient initial activation attempts. */\nconst DEFAULT_ACTIVATION_RETRY_MAX_DELAY_MS = 30_000;\n\n/**\n * Reject with `timeoutMessage` after `timeoutMs` if `inner` has not settled,\n * otherwise pass `inner` through. When `timeoutMs` is undefined, `inner` is\n * returned unchanged. The timer is `unref`'d so a pending deadline never keeps\n * the process alive, and `inner` always has a settle handler attached, so a\n * timed-out promise that later settles never surfaces as unhandled.\n */\nfunction withTimeout<T>(\n inner: Promise<T>,\n timeoutMs: number | undefined,\n timeoutMessage: string,\n): Promise<T> {\n if (timeoutMs === undefined) {\n return inner;\n }\n return new Promise<T>((resolve, reject) => {\n const timer = setTimeout(\n () => reject(new Error(timeoutMessage)),\n timeoutMs,\n );\n (timer as unknown as { unref?: () => void }).unref?.();\n inner.then(\n (value) => {\n clearTimeout(timer);\n resolve(value);\n },\n (err) => {\n clearTimeout(timer);\n reject(err);\n },\n );\n });\n}\n\n/**\n * Drives Channel activation for an Intelligence runtime: lazily activates each\n * declared Channel through the managed engine, tracks per-Channel lifecycle\n * status, exposes readiness, and tears everything down. Existing direct\n * adapters remain on the Channel; the launcher attaches the one managed\n * adapter before starting the combined adapter array.\n *\n * Activation is lazy and idempotent — constructing the manager does nothing;\n * {@link activate} starts it and a second call is a no-op. Activation throws\n * SYNCHRONOUSLY (a {@link ChannelConfigError}) only for a misconfiguration it\n * can detect up front — a duplicate or missing Channel name. Every OTHER\n * permanent activation failure is recorded as the Channel's status (`error`,\n * or `setup_required` for a missing provider) and surfaced through\n * {@link status} and {@link ready} rather than thrown. A retryable initial\n * gateway outage stays unsettled and retries until it connects or the manager\n * stops.\n *\n * Established-session reconnection is delegated to the Phoenix connection\n * layer that backs the launcher. When a managed control socket drops, Phoenix's\n * `Socket` reconnects and rejoins with the same Runtime declaration. The manager\n * never re-activates an already-started Channel. It does retry a transient\n * INITIAL gateway activation failure: that happens before the launcher adds or\n * starts the managed adapter, so a later attempt is safe.\n *\n * It DOES, however, reflect real connection health through the session's\n * `onStateChange` observer so {@link ChannelManager.status} stays honest rather\n * than reporting `online` forever after a drop: a drop moves the Channel to\n * `reconnecting`, a successful rejoin restores `online`, and a bounded give-up\n * (Phoenix would otherwise retry forever) moves it to `error`.\n */\nexport class ChannelManager implements ChannelsControl {\n private readonly intelligence: CopilotKitIntelligence;\n private readonly runner?: AgentRunner;\n private readonly learning?: CopilotRuntimeLearningConfig;\n private readonly lockTtlSeconds: number;\n private readonly lockHeartbeatIntervalSeconds: number;\n private readonly lockKeyPrefix?: string;\n private readonly channels: Channel[];\n private readonly activateChannel: ActivateChannelEngine;\n private readonly mintRuntimeInstanceId: () => string;\n private readonly log?: (msg: string, meta?: unknown) => void;\n private readonly stopHandleTimeoutMs: number;\n private readonly reconnectLogIntervalMs: number;\n\n private readonly entries = new Map<string, ChannelEntry>();\n private activated = false;\n private stopped = false;\n\n /** @param args - See {@link ChannelManagerArgs}. */\n constructor(args: ChannelManagerArgs) {\n this.intelligence = args.intelligence;\n this.runner = args.runner;\n this.learning = args.learning;\n this.lockTtlSeconds = args.lockTtlSeconds ?? 20;\n this.lockHeartbeatIntervalSeconds = args.lockHeartbeatIntervalSeconds ?? 15;\n this.lockKeyPrefix = args.lockKeyPrefix;\n this.channels = args.channels;\n this.log = args.log;\n // When using the default engine, forward the manager's log DOWN to the\n // launcher/transport (via defaultActivateChannel's log param) so a\n // transport-level drop is observable in the managed path. `this.log` is read\n // lazily at activation time, so this closure always sees the assigned sink.\n this.activateChannel =\n args.activateChannel ??\n ((config, channel) =>\n defaultActivateChannel(\n config,\n channel,\n undefined,\n this.log,\n this.runner\n ? {\n runner: this.runner,\n intelligence: this.intelligence,\n ...(this.learning !== undefined\n ? { learning: this.learning }\n : {}),\n lockTtlSeconds: this.lockTtlSeconds,\n lockHeartbeatIntervalSeconds: this.lockHeartbeatIntervalSeconds,\n ...(this.lockKeyPrefix !== undefined\n ? { lockKeyPrefix: this.lockKeyPrefix }\n : {}),\n }\n : undefined,\n ));\n this.mintRuntimeInstanceId =\n args.mintRuntimeInstanceId ??\n (() => `rti_${randomUUID().replace(/-/g, \"\")}`);\n this.stopHandleTimeoutMs =\n args.stopHandleTimeoutMs ?? DEFAULT_STOP_HANDLE_TIMEOUT_MS;\n this.reconnectLogIntervalMs =\n args.reconnectLogIntervalMs ?? DEFAULT_RECONNECT_LOG_INTERVAL_MS;\n }\n\n /**\n * Start activation of every declared Channel (lazy + idempotent). Mints a\n * distinct runtime instance id per Channel, derives its activation config,\n * and calls the engine. Transient gateway failures retry with exponential\n * backoff; other outcomes transition to `online`/`setup_required`/`error`.\n */\n activate(): void {\n // Short-circuit on BOTH latches: `activated` makes activation idempotent,\n // and `stopped` prevents a post-`stop()` activate() from opening transports\n // on a dead manager. (A late activation self-heals via the post-settle guard,\n // but never starting it is cheaper and clearer.)\n if (this.activated || this.stopped) {\n return;\n }\n // Reject duplicate Channel names BEFORE kicking off any engine call. The\n // manager keys `entries` by name, so a duplicate would let the second\n // activation's entry silently overwrite the first — leaking the first\n // Channel's control link out of status()/ready()/stop(). Fail loud here so\n // nothing is ever activated in that state.\n this.assertUniqueChannelNames();\n this.activated = true;\n\n // Every declared Channel gets the managed adapter. Any developer-supplied\n // direct adapters stay in the same adapter array and are started by the\n // launcher's single `channel.ɵruntime.start()` call.\n for (const channel of this.channels) {\n channel.ɵruntime.enableIntelligenceMemory();\n const name = channel.name!;\n const runtimeInstanceId = this.mintRuntimeInstanceId();\n\n let resolveSettled!: () => void;\n let rejectSettled!: (err: unknown) => void;\n const settled = new Promise<void>((resolve, reject) => {\n resolveSettled = resolve;\n rejectSettled = reject;\n });\n // ready() awaits `settled`; if nothing ever handles a rejection there,\n // Node reports an unhandled rejection. Attach a no-op catch so the\n // promise is always considered handled — ready() still sees the reason.\n settled.catch(() => {});\n\n // The deferred activation callbacks capture `entry` and run only after\n // the literal has fully initialized, so referencing it there is safe.\n const entry: ChannelEntry = {\n status: \"connecting\",\n handle: undefined,\n handleStopped: false,\n settled,\n };\n\n // Invoke the engine synchronously so activation is observably started the\n // moment activate() returns. Only a typed transient gateway failure is\n // retried; config errors stay on the existing terminal path.\n let activation: Promise<ChannelsHandle>;\n try {\n const config = deriveChannelActivationConfig({\n intelligence: this.intelligence,\n channel,\n runtimeInstanceId,\n });\n activation = this.activateWithRetry(config, channel, name, entry);\n } catch (err) {\n activation = Promise.reject(err);\n }\n\n // Anchor the settle handlers. Both branches route every teardown through\n // the idempotent `stopEntry`, so a late settle can never resurrect a\n // `stopped` entry and a handle is torn down at most once. The handlers\n // only mutate state (never throw), so the trailing no-op catch just keeps\n // the chain from surfacing as an unhandled rejection.\n activation\n .then(\n async (handle) => {\n entry.handle = handle;\n if (this.stopped) {\n // stop() ran before this activation settled, so it could not tear\n // down a handle that did not exist yet. Release it now (idempotent)\n // and keep the Channel `stopped`.\n await this.stopEntry(entry);\n resolveSettled();\n return;\n }\n entry.status = \"online\";\n this.registerConnectionObserver(name, entry);\n resolveSettled();\n },\n async (err: unknown) => {\n if (this.stopped) {\n // A rejection that arrives AFTER stop() must NOT resurrect the\n // entry into `error`/`setup_required`: the Channel is already\n // being torn down. Keep it `stopped` and resolve `settled` so a\n // subsequent ready() does not reject on a stopped Channel.\n await this.stopEntry(entry);\n resolveSettled();\n return;\n }\n if (isSetupRequired(err)) {\n const hasDirectAdapter = channel.adapters.some(\n (adapter) => !adapter.__intelligenceChannel,\n );\n if (hasDirectAdapter) {\n try {\n // Managed setup may be incomplete while a developer-owned\n // transport is fully configured. Keep that transport alive;\n // a later runtime restart can attach the managed adapter once\n // Intelligence setup is complete.\n await channel.ɵruntime.start();\n entry.handle = {\n metadata: {},\n stop: () => channel.ɵruntime.stop(),\n };\n if (this.stopped) {\n await this.stopEntry(entry);\n resolveSettled();\n return;\n }\n } catch (directError) {\n if (this.stopped) {\n await this.stopEntry(entry);\n resolveSettled();\n return;\n }\n entry.status = \"error\";\n this.log?.(\n `channel \"${name}\" failed to start its direct adapters while managed setup is incomplete`,\n directError,\n );\n rejectSettled(directError);\n return;\n }\n }\n entry.status = \"setup_required\";\n this.log?.(`channel \"${name}\" requires setup`, err);\n resolveSettled();\n } else {\n entry.status = \"error\";\n this.log?.(`channel \"${name}\" failed to activate`, err);\n rejectSettled(err);\n }\n },\n )\n .catch(() => {});\n\n this.entries.set(name, entry);\n }\n }\n\n /**\n * Retry only transient failures from the pre-adapter gateway connection.\n * Permanent errors reject on the first attempt; teardown cancels a pending\n * timer while preserving the existing late-settle handling for in-flight work.\n */\n private activateWithRetry(\n config: ChannelActivationConfig,\n channel: Channel,\n name: string,\n entry: ChannelEntry,\n ): Promise<ChannelsHandle> {\n return new Promise<ChannelsHandle>((resolve, reject) => {\n const attempt = (): void => {\n let activation: Promise<ChannelsHandle>;\n try {\n activation = this.activateChannel(config, channel);\n } catch (err) {\n activation = Promise.reject(err);\n }\n activation.then(\n (handle) => {\n this.clearActivationRetry(entry);\n resolve(handle);\n },\n (err: unknown) => {\n if (this.stopped || !isRetryableActivationError(err)) {\n this.clearActivationRetry(entry);\n reject(err);\n return;\n }\n\n const delayMs =\n entry.activationRetryDelayMs ?? DEFAULT_ACTIVATION_RETRY_DELAY_MS;\n entry.status = \"reconnecting\";\n entry.activationRetryDelayMs = Math.min(\n delayMs * 2,\n DEFAULT_ACTIVATION_RETRY_MAX_DELAY_MS,\n );\n this.log?.(\n `channel \"${name}\" failed to activate; retrying in ${delayMs}ms`,\n err,\n );\n const timer = setTimeout(() => {\n entry.activationRetryTimer = undefined;\n entry.cancelActivationRetry = undefined;\n if (this.stopped || entry.status === \"stopped\") {\n reject(err);\n return;\n }\n entry.status = \"connecting\";\n attempt();\n }, delayMs);\n (timer as unknown as { unref?: () => void }).unref?.();\n entry.activationRetryTimer = timer;\n entry.cancelActivationRetry = () => {\n this.clearActivationRetry(entry);\n reject(err);\n };\n },\n );\n };\n\n attempt();\n });\n }\n\n /**\n * Throw if two declared Channels share a `name`. `entries` is keyed by name,\n * so a duplicate would overwrite the first Channel's entry and leak its live\n * session. Called at the very start of {@link activate}, before any engine\n * call, so a misconfiguration fails loud instead of silently.\n *\n * @throws {ChannelConfigError} If any Channel is missing a name, or if any\n * name appears more than once.\n */\n private assertUniqueChannelNames(): void {\n const seen = new Set<string>();\n for (const channel of this.channels) {\n const name = channel.name;\n // Check for a missing/empty name FIRST: `channel.name!` on a nameless\n // Channel keys as the string \"undefined\", which would otherwise report a\n // spurious duplicate for two nameless Channels before the accurate\n // missing-name error. Fail with the precise error instead.\n if (!name) {\n throw new ChannelConfigError(\n \"A managed Channel is missing a `name` — every declared Channel must \" +\n \"have a unique, non-empty name (pass createChannel({ name })).\",\n );\n }\n if (seen.has(name)) {\n throw new ChannelConfigError(\n `Duplicate managed Channel name \"${name}\" — every declared Channel ` +\n `must have a unique name.`,\n );\n }\n seen.add(name);\n }\n }\n\n /**\n * Resolve when every declared Channel has settled to\n * `online`/`setup_required` through its managed activation.\n *\n * Activates lazily if not already started — so a first call rejects with the\n * same {@link ChannelConfigError} as the synchronous throw from\n * {@link activate} for an up-front misconfiguration (duplicate/missing Channel\n * names). Once activation has been kicked off, all OTHER failures are surfaced\n * here instead: this rejects with an `AggregateError` if any Channel settled\n * to `error` OR — when `timeoutMs` is given — did not settle in time. The\n * `timeoutMs` deadline is applied PER CHANNEL, so the aggregate carries each\n * failed Channel's real reason AND a named timeout for each Channel still\n * hanging: a genuine activation error is never masked by a sibling that hangs\n * (a pre-fix set-wide timeout discarded the real reason in that case).\n *\n * A STOPPED manager short-circuits and resolves: a Channel that settled to\n * `error` BEFORE {@link stop} already rejected its `settled` promise, so\n * awaiting it here would throw an `AggregateError` even though\n * {@link status}.overall is `\"stopped\"` — inconsistent with the case where the\n * Channel was still online at stop() (which resolves). A stopped manager has\n * nothing left to be ready for, so resolve uniformly.\n *\n * `ready()` is ONE-SHOT: it settles on the INITIAL activation outcome. Later\n * connection-health transitions (a live Channel dropping to `reconnecting`, or\n * giving up to `error`) are reported through {@link status} — where `online`\n * means currently-sendable — but do NOT re-arm or re-reject an already-settled\n * `ready()`.\n */\n async ready(opts?: { timeoutMs?: number }): Promise<void> {\n if (this.stopped) {\n return;\n }\n this.activate();\n const entries = [...this.entries.entries()];\n // Apply `timeoutMs` PER CHANNEL rather than to the whole set. A single\n // set-wide timeout wrapping `allSettled` would, when one channel settles to\n // `error` while a sibling hangs, reject with only a generic timeout and\n // DISCARD the erroring channel's real reason. Timing out each channel's\n // `settled` independently lets `allSettled` collect BOTH a hung channel's\n // named timeout AND a failed channel's real error into one AggregateError.\n const results = await Promise.allSettled(\n entries.map(([name, e]) =>\n withTimeout(\n e.settled,\n opts?.timeoutMs,\n `channel \"${name}\" did not settle within ${opts?.timeoutMs}ms`,\n ),\n ),\n );\n const errors = results\n .filter((r): r is PromiseRejectedResult => r.status === \"rejected\")\n .map((r) => r.reason);\n if (errors.length > 0) {\n throw new AggregateError(\n errors,\n `ChannelManager.ready: ${errors.length} channel(s) failed to activate or settle in time`,\n );\n }\n }\n\n /**\n * Snapshot status. Every declared Channel appears keyed by name in\n * `channels` after its combined adapter lifecycle starts.\n *\n * Each Channel's entry is the fold of its transport and provider legs (see\n * {@link foldChannelLegs}), and `detail` reports those legs separately so a\n * caller can assert the one it cares about.\n *\n * `overall` is folded over ALL declared Channels (see {@link computeOverall}),\n * by precedence `error` > `reconnecting` > `setup_required` > `connecting` >\n * `online`. `online` means every Channel can currently send AND none was\n * reported as missing its managed provider. `reconnecting` outranks\n * `setup_required` because a dropped-but-retrying Channel is an active outage,\n * louder than a steadily-degraded unprovisioned one. With no declared Channels\n * at all, `overall` is `online` (nothing is degraded); once every Channel has\n * been stopped, `overall` is `stopped`.\n *\n * `overall === \"online\"` is only end-to-end proof when every Channel's\n * `provider` leg is `attached`. A gateway that reports no provider state leaves\n * the legs `unknown` and `overall` transport-derived, exactly as before this\n * contract existed — so a caller that must be certain checks `detail`.\n */\n status(): {\n overall: ChannelStatus;\n channels: Record<string, ChannelStatus>;\n detail: Record<string, ChannelLegs>;\n } {\n const channels: Record<string, ChannelStatus> = {};\n const detail: Record<string, ChannelLegs> = {};\n for (const [name, entry] of this.entries) {\n // KNOWN LIMITATION, dead today: the legacy `ChannelSetupRequiredError` /\n // `code === \"SETUP_REQUIRED\"` path writes `setup_required` into\n // `entry.status`, which is reported verbatim as the transport leg — so such\n // a Channel reads `transport: \"setup_required\", provider: \"unknown\"`, a\n // provider condition parked on the transport leg. Nothing emits it (the\n // activation engine stopped throwing it at the 2026-07-29\n // realtime-boundary cutover) and the folded `status` is right either way,\n // so this is cosmetic. A future producer should report provider attachment\n // through the `providerStates` seam rather than `entry.status`.\n const transport = entry.status;\n const provider = this.providerLeg(name, entry);\n const status = foldChannelLegs(transport, provider);\n channels[name] = status;\n detail[name] = { status, transport, provider };\n }\n // A stopped manager is `stopped` regardless of whether it was ever activated.\n // stop() before activate() (e.g. SIGTERM during startup) leaves `entries`\n // empty, and the empty-set fold below returns `online` — a torn-down manager\n // must never read healthy. Short-circuit before that fold. (After a normal\n // activate→stop, every entry is already `stopped` and the fold agrees, so\n // this is also consistent with the populated case.)\n if (this.stopped) {\n return { overall: \"stopped\", channels, detail };\n }\n // Before activate() has run, `entries` is empty. Folding an empty set gives\n // `online` — correct for a manager that declares NO channels (nothing is\n // degraded), but a LIE for one that declares channels and simply has not\n // opened its socket yet: activation is lazy (deferred to the first\n // `ready()`), so a not-yet-activated manager must never read `online`.\n // Report `connecting` (\"not started\") for that case so `status()` is honest\n // before any `ready()`.\n if (!this.activated && this.channels.length > 0) {\n return { overall: \"connecting\", channels, detail };\n }\n return {\n overall: this.computeOverall(Object.values(channels)),\n channels,\n detail,\n };\n }\n\n /**\n * Read one Channel's provider leg from its handle.\n *\n * Defensive on every axis, because a wrong answer here silently changes what\n * `status()` certifies: a handle without the seam, a gateway that reported\n * nothing, a name the gateway did not mention, an unrecognised value, or a\n * throwing getter all yield `unknown` — which {@link foldChannelLegs} treats as\n * \"keep the transport-derived status\", i.e. pre-provider-state behaviour.\n *\n * @param name - The Channel name (map key).\n * @param entry - The Channel's activation entry.\n * @returns The provider leg, or `\"unknown\"` when it cannot be established.\n */\n private providerLeg(name: string, entry: ChannelEntry): ChannelProviderLeg {\n let states: Readonly<Record<string, string>> | undefined;\n try {\n states = entry.handle?.providerStates?.();\n } catch {\n // A misbehaving handle must never break a status snapshot.\n return \"unknown\";\n }\n const reported = states?.[name];\n return reported !== undefined && PROVIDER_LEGS.has(reported)\n ? (reported as ChannelProviderLeg)\n : \"unknown\";\n }\n\n /**\n * Fold per-Channel statuses into a single overall status (see {@link status}).\n *\n * Every declared Channel participates. Statuses are ranked\n * `error` > `reconnecting` > `setup_required` > `connecting` > `online`, so a\n * genuine failure still dominates a healthy sibling.\n * The empty-input case (no declared Channels at all) stays `online` (nothing is\n * degraded).\n */\n private computeOverall(values: ChannelStatus[]): ChannelStatus {\n if (values.length === 0) {\n return \"online\";\n }\n if (values.every((v) => v === \"stopped\")) {\n return \"stopped\";\n }\n if (values.includes(\"error\")) {\n return \"error\";\n }\n if (values.includes(\"reconnecting\")) {\n return \"reconnecting\";\n }\n if (values.includes(\"setup_required\")) {\n return \"setup_required\";\n }\n if (values.includes(\"connecting\")) {\n return \"connecting\";\n }\n return \"online\";\n }\n\n /**\n * Wire the Channel's connection-health observer (if the handle exposes the\n * optional `onStateChange` seam) so {@link ChannelManager.status} reflects real\n * health instead of reporting `online` forever after a drop:\n *\n * - `reconnecting` → status `reconnecting` (dropped, Phoenix retrying);\n * - `online` → status `online` (rejoined, sendable again);\n * - `gave_up` → status `error` (dead after the bounded reconnect window).\n *\n * Makes NO re-activation — reconnection is delegated to the Phoenix connection\n * layer (see {@link ChannelManager}), which auto-rejoins under the persistent\n * adapter. A STOPPED manager (or an already-stopped entry) ignores late\n * connection events, so a drop that fires after {@link ChannelManager.stop}\n * never resurrects the Channel out of `stopped`.\n *\n * @param name - The Channel name (map key).\n * @param entry - The Channel's activation entry.\n */\n private registerConnectionObserver(name: string, entry: ChannelEntry): void {\n entry.handle?.onStateChange?.((state, detail) => {\n // A stopped manager (or a stopped entry) ignores late connection events.\n if (this.stopped || entry.status === \"stopped\") {\n return;\n }\n const cause = detail?.reason ?? detail?.code;\n const because = cause !== undefined ? ` — ${cause}` : \"\";\n if (state === \"reconnecting\") {\n entry.status = \"reconnecting\";\n entry.downSince ??= Date.now();\n // Remembered for the repeat line below: an operator reading a reminder\n // hours into an outage should not have to find the first line to learn\n // the cause.\n if (cause !== undefined) entry.downCause = cause;\n this.log?.(\n `channel \"${name}\" managed session dropped; reconnecting (Phoenix auto-rejoin)${because}`,\n );\n this.captureChannelTelemetry(\"oss.runtime.channel_session_dropped\", {\n ...(detail?.reason !== undefined ? { reason: detail.reason } : {}),\n ...(detail?.code !== undefined ? { code: detail.code } : {}),\n });\n this.startReconnectLog(name, entry);\n } else if (state === \"online\") {\n entry.status = \"online\";\n this.clearReconnectLog(entry);\n // Read BEFORE clearing `downSince`, and only when an outage was\n // actually in progress — a session may report `online` with no\n // preceding drop, which is not a recovery.\n const downSince = entry.downSince;\n entry.downSince = undefined;\n entry.downCause = undefined;\n this.log?.(`channel \"${name}\" managed session back online`);\n if (downSince !== undefined) {\n this.captureChannelTelemetry(\n \"oss.runtime.channel_session_recovered\",\n { downForMs: Date.now() - downSince },\n );\n }\n } else if (state === \"gave_up\") {\n // `error` here means \"not sendable\", NOT \"dead\": Phoenix keeps retrying\n // underneath and a successful rejoin restores `online`. Say so, or the\n // line reads as terminal (OSS-670). The repeat keeps running.\n entry.status = \"error\";\n this.log?.(\n `channel \"${name}\" managed session gave up reconnecting after ${this.downFor(entry)}; ` +\n `marking error (still retrying — a successful rejoin restores online)${because}`,\n );\n }\n });\n }\n\n /**\n * Report a Channel connection event. A managed session that drops is\n * otherwise invisible outside the host process — the `log` seam reaches only\n * whoever reads that process's stdout, which for a self-hosted runtime is\n * nobody who can act on it.\n *\n * Fire-and-forget, and failures are swallowed: telemetry must never break a\n * live session. Same contract as `fireInstanceCreatedTelemetry`. The `try`\n * also covers a `capture` that throws synchronously or returns no promise.\n */\n private captureChannelTelemetry<\n K extends\n | \"oss.runtime.channel_session_dropped\"\n | \"oss.runtime.channel_session_recovered\",\n >(event: K, props: AnalyticsEvents[K]): void {\n try {\n void telemetry.capture(event, props).catch(() => {});\n } catch {\n // Swallow — a telemetry transport must not take the session with it.\n }\n }\n\n /** Rendered downtime for this outage episode (`\"45s\"`), or `\"unknown\"`. */\n private downFor(entry: ChannelEntry): string {\n return entry.downSince === undefined\n ? \"unknown\"\n : `${Math.round((Date.now() - entry.downSince) / 1000)}s`;\n }\n\n /**\n * Repeat a \"still down\" line for as long as this outage lasts, with an\n * exponential delay capped at 15 minutes. Runs THROUGH `gave_up` on purpose:\n * that transition is where the old behavior went quiet, and an operator\n * watching a silent process cannot tell a dead bot from an idle one.\n */\n private startReconnectLog(name: string, entry: ChannelEntry): void {\n if (entry.reconnectLogTimer !== undefined) return;\n\n const delayMs = entry.reconnectLogDelayMs ?? this.reconnectLogIntervalMs;\n const timer = setTimeout(() => {\n entry.reconnectLogTimer = undefined;\n if (this.stopped || entry.status === \"stopped\") {\n this.clearReconnectLog(entry);\n return;\n }\n this.log?.(\n `channel \"${name}\" managed session still down after ${this.downFor(entry)}; Phoenix is retrying` +\n (entry.downCause !== undefined ? ` — ${entry.downCause}` : \"\"),\n );\n entry.reconnectLogDelayMs = Math.min(\n delayMs * 2,\n Math.max(\n this.reconnectLogIntervalMs,\n DEFAULT_RECONNECT_LOG_MAX_INTERVAL_MS,\n ),\n );\n this.startReconnectLog(name, entry);\n }, delayMs);\n (timer as unknown as { unref?: () => void }).unref?.();\n entry.reconnectLogTimer = timer;\n }\n\n /** Stop this entry's \"still down\" repeat, if one is running. */\n private clearReconnectLog(entry: ChannelEntry): void {\n if (entry.reconnectLogTimer !== undefined) {\n clearTimeout(entry.reconnectLogTimer);\n entry.reconnectLogTimer = undefined;\n }\n entry.reconnectLogDelayMs = undefined;\n }\n\n /** Cancel a pending transient activation retry and reset its backoff. */\n private clearActivationRetry(entry: ChannelEntry): void {\n if (entry.activationRetryTimer !== undefined) {\n clearTimeout(entry.activationRetryTimer);\n entry.activationRetryTimer = undefined;\n }\n entry.activationRetryDelayMs = undefined;\n entry.cancelActivationRetry = undefined;\n }\n\n /** Cancel a scheduled activation retry and settle its wrapper. */\n private cancelActivationRetry(entry: ChannelEntry): void {\n const cancel = entry.cancelActivationRetry;\n if (cancel) {\n cancel();\n } else {\n this.clearActivationRetry(entry);\n }\n }\n\n /**\n * Drive a single entry to its terminal `stopped` state, tearing down its\n * handle AT MOST ONCE. Idempotent: it always sets `status = \"stopped\"`, and\n * only calls `handle.stop()` on the first invocation that sees a live,\n * not-yet-stopped handle (gated by {@link ChannelEntry.handleStopped}).\n *\n * This is the ONE guarded teardown path shared by both `stop()` and the\n * post-settle guard in {@link activate}. Because the\n * guard is per-entry and idempotent, a handle assigned in the same tick as\n * `stop()` is stopped exactly once even when both callers reach the entry, and a\n * late settle can never resurrect a `stopped` entry. The activation handle\n * releases the gateway session and stops the Channel's combined adapter array.\n *\n * `handle.stop()` failures are logged (via {@link ChannelManager.log}) but NOT\n * rethrown: the real launcher's `stop()` rethrows after `session.disconnect()`,\n * and teardown must still complete for every other entry. The call is wrapped\n * in `Promise.resolve().then(...)` so a foreign/injected handle whose `stop()`\n * throws SYNCHRONOUSLY (before any promise is created) is caught by the same\n * `.catch` — otherwise the sync throw would escape, skip `resolveSettled()` in\n * the fulfilled-then-stopped branch of {@link activate}, and hang `settled`.\n *\n * An entry with no handle yet (a still-`connecting` Channel whose transport has\n * not come up) is only marked `stopped`: there is nothing to tear down, and the\n * post-settle guard releases the transport if it arrives after `stop()`.\n *\n * A WEDGED `handle.stop()` (one that never settles) is bounded by\n * {@link ChannelManagerArgs.stopHandleTimeoutMs}: after the deadline the call\n * is logged and abandoned so it can't hang `stop()` — and thus SIGTERM\n * shutdown — forever.\n *\n * @param entry - The Channel entry to stop.\n */\n private async stopEntry(entry: ChannelEntry): Promise<void> {\n entry.status = \"stopped\";\n // An unref'd interval would not hold the process open, but a stopped\n // manager must not keep logging about a session it no longer owns.\n this.clearReconnectLog(entry);\n this.cancelActivationRetry(entry);\n if (entry.handle && !entry.handleStopped) {\n entry.handleStopped = true;\n const handle = entry.handle;\n // Bound handle.stop(): a wedged stop() (e.g. a socket.disconnect that\n // never returns) must not hang teardown — and thus SIGTERM shutdown —\n // forever. On timeout, log and abandon it (the call keeps running with a\n // settle handler attached inside withTimeout, so it never surfaces as an\n // unhandled rejection) so every OTHER entry still reaches `stopped`. The\n // `Promise.resolve().then(...)` wrap also routes a SYNCHRONOUS throw from\n // a foreign handle through the same timeout+catch.\n await withTimeout(\n Promise.resolve().then(() => handle.stop()),\n this.stopHandleTimeoutMs,\n `channel handle stop() timed out after ${this.stopHandleTimeoutMs}ms during teardown`,\n ).catch((err: unknown) =>\n this.log?.(\"channel handle stop() failed during teardown\", err),\n );\n }\n }\n\n /**\n * Stop every activated Channel exactly once and mark all statuses `stopped`.\n * Idempotent — a second call is a no-op.\n *\n * Resolves promptly: {@link stopEntry} stops only the handles that already\n * exist and never blocks on activations that have not settled. A hung connect\n * (which `ready({ timeoutMs })` tolerates) has no handle to stop yet, and\n * awaiting it here would hang teardown — and thus SIGTERM shutdown — forever.\n * Any handle that arrives after this point is torn down by the post-settle\n * guard in {@link activate}, which routes through the same idempotent\n * {@link stopEntry}, so nothing leaks and nothing double-stops.\n *\n * Teardown is resilient to a throwing `handle.stop()`: `Promise.allSettled`\n * over the per-entry `stopEntry` calls guarantees one rejection can't abort\n * the rest, so every entry reaches `stopped` and `stop()` always resolves.\n * It is equally resilient to a WEDGED `handle.stop()` that never settles: each\n * is bounded by {@link ChannelManagerArgs.stopHandleTimeoutMs} inside\n * {@link stopEntry}, so a single hung handle can't hang SIGTERM shutdown.\n */\n async stop(): Promise<void> {\n if (this.stopped) {\n return;\n }\n this.stopped = true;\n\n const entries = [...this.entries.values()];\n await Promise.allSettled(entries.map((entry) => this.stopEntry(entry)));\n }\n}\n"],"mappings":"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"}